linux/drivers/video/fbdev/omap2/omapfb/dss/dispc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * linux/drivers/video/omap2/dss/dispc.c
   4 *
   5 * Copyright (C) 2009 Nokia Corporation
   6 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
   7 *
   8 * Some code and ideas taken from drivers/video/omap/ driver
   9 * by Imre Deak.
  10 */
  11
  12#define DSS_SUBSYS_NAME "DISPC"
  13
  14#include <linux/kernel.h>
  15#include <linux/dma-mapping.h>
  16#include <linux/vmalloc.h>
  17#include <linux/export.h>
  18#include <linux/clk.h>
  19#include <linux/io.h>
  20#include <linux/jiffies.h>
  21#include <linux/seq_file.h>
  22#include <linux/delay.h>
  23#include <linux/workqueue.h>
  24#include <linux/hardirq.h>
  25#include <linux/platform_device.h>
  26#include <linux/pm_runtime.h>
  27#include <linux/sizes.h>
  28#include <linux/mfd/syscon.h>
  29#include <linux/regmap.h>
  30#include <linux/of.h>
  31#include <linux/component.h>
  32
  33#include <video/omapfb_dss.h>
  34
  35#include "dss.h"
  36#include "dss_features.h"
  37#include "dispc.h"
  38
  39/* DISPC */
  40#define DISPC_SZ_REGS                   SZ_4K
  41
  42enum omap_burst_size {
  43        BURST_SIZE_X2 = 0,
  44        BURST_SIZE_X4 = 1,
  45        BURST_SIZE_X8 = 2,
  46};
  47
  48#define REG_GET(idx, start, end) \
  49        FLD_GET(dispc_read_reg(idx), start, end)
  50
  51#define REG_FLD_MOD(idx, val, start, end)                               \
  52        dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end))
  53
  54struct dispc_features {
  55        u8 sw_start;
  56        u8 fp_start;
  57        u8 bp_start;
  58        u16 sw_max;
  59        u16 vp_max;
  60        u16 hp_max;
  61        u8 mgr_width_start;
  62        u8 mgr_height_start;
  63        u16 mgr_width_max;
  64        u16 mgr_height_max;
  65        unsigned long max_lcd_pclk;
  66        unsigned long max_tv_pclk;
  67        int (*calc_scaling) (unsigned long pclk, unsigned long lclk,
  68                const struct omap_video_timings *mgr_timings,
  69                u16 width, u16 height, u16 out_width, u16 out_height,
  70                enum omap_color_mode color_mode, bool *five_taps,
  71                int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
  72                u16 pos_x, unsigned long *core_clk, bool mem_to_mem);
  73        unsigned long (*calc_core_clk) (unsigned long pclk,
  74                u16 width, u16 height, u16 out_width, u16 out_height,
  75                bool mem_to_mem);
  76        u8 num_fifos;
  77
  78        /* swap GFX & WB fifos */
  79        bool gfx_fifo_workaround:1;
  80
  81        /* no DISPC_IRQ_FRAMEDONETV on this SoC */
  82        bool no_framedone_tv:1;
  83
  84        /* revert to the OMAP4 mechanism of DISPC Smart Standby operation */
  85        bool mstandby_workaround:1;
  86
  87        bool set_max_preload:1;
  88
  89        /* PIXEL_INC is not added to the last pixel of a line */
  90        bool last_pixel_inc_missing:1;
  91
  92        /* POL_FREQ has ALIGN bit */
  93        bool supports_sync_align:1;
  94
  95        bool has_writeback:1;
  96};
  97
  98#define DISPC_MAX_NR_FIFOS 5
  99
 100static struct {
 101        struct platform_device *pdev;
 102        void __iomem    *base;
 103
 104        int irq;
 105        irq_handler_t user_handler;
 106        void *user_data;
 107
 108        unsigned long core_clk_rate;
 109        unsigned long tv_pclk_rate;
 110
 111        u32 fifo_size[DISPC_MAX_NR_FIFOS];
 112        /* maps which plane is using a fifo. fifo-id -> plane-id */
 113        int fifo_assignment[DISPC_MAX_NR_FIFOS];
 114
 115        bool            ctx_valid;
 116        u32             ctx[DISPC_SZ_REGS / sizeof(u32)];
 117
 118        const struct dispc_features *feat;
 119
 120        bool is_enabled;
 121
 122        struct regmap *syscon_pol;
 123        u32 syscon_pol_offset;
 124
 125        /* DISPC_CONTROL & DISPC_CONFIG lock*/
 126        spinlock_t control_lock;
 127} dispc;
 128
 129enum omap_color_component {
 130        /* used for all color formats for OMAP3 and earlier
 131         * and for RGB and Y color component on OMAP4
 132         */
 133        DISPC_COLOR_COMPONENT_RGB_Y             = 1 << 0,
 134        /* used for UV component for
 135         * OMAP_DSS_COLOR_YUV2, OMAP_DSS_COLOR_UYVY, OMAP_DSS_COLOR_NV12
 136         * color formats on OMAP4
 137         */
 138        DISPC_COLOR_COMPONENT_UV                = 1 << 1,
 139};
 140
 141enum mgr_reg_fields {
 142        DISPC_MGR_FLD_ENABLE,
 143        DISPC_MGR_FLD_STNTFT,
 144        DISPC_MGR_FLD_GO,
 145        DISPC_MGR_FLD_TFTDATALINES,
 146        DISPC_MGR_FLD_STALLMODE,
 147        DISPC_MGR_FLD_TCKENABLE,
 148        DISPC_MGR_FLD_TCKSELECTION,
 149        DISPC_MGR_FLD_CPR,
 150        DISPC_MGR_FLD_FIFOHANDCHECK,
 151        /* used to maintain a count of the above fields */
 152        DISPC_MGR_FLD_NUM,
 153};
 154
 155struct dispc_reg_field {
 156        u16 reg;
 157        u8 high;
 158        u8 low;
 159};
 160
 161static const struct {
 162        const char *name;
 163        u32 vsync_irq;
 164        u32 framedone_irq;
 165        u32 sync_lost_irq;
 166        struct dispc_reg_field reg_desc[DISPC_MGR_FLD_NUM];
 167} mgr_desc[] = {
 168        [OMAP_DSS_CHANNEL_LCD] = {
 169                .name           = "LCD",
 170                .vsync_irq      = DISPC_IRQ_VSYNC,
 171                .framedone_irq  = DISPC_IRQ_FRAMEDONE,
 172                .sync_lost_irq  = DISPC_IRQ_SYNC_LOST,
 173                .reg_desc       = {
 174                        [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL,  0,  0 },
 175                        [DISPC_MGR_FLD_STNTFT]          = { DISPC_CONTROL,  3,  3 },
 176                        [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL,  5,  5 },
 177                        [DISPC_MGR_FLD_TFTDATALINES]    = { DISPC_CONTROL,  9,  8 },
 178                        [DISPC_MGR_FLD_STALLMODE]       = { DISPC_CONTROL, 11, 11 },
 179                        [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG,  10, 10 },
 180                        [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG,  11, 11 },
 181                        [DISPC_MGR_FLD_CPR]             = { DISPC_CONFIG,  15, 15 },
 182                        [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG,  16, 16 },
 183                },
 184        },
 185        [OMAP_DSS_CHANNEL_DIGIT] = {
 186                .name           = "DIGIT",
 187                .vsync_irq      = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN,
 188                .framedone_irq  = DISPC_IRQ_FRAMEDONETV,
 189                .sync_lost_irq  = DISPC_IRQ_SYNC_LOST_DIGIT,
 190                .reg_desc       = {
 191                        [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL,  1,  1 },
 192                        [DISPC_MGR_FLD_STNTFT]          = { },
 193                        [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL,  6,  6 },
 194                        [DISPC_MGR_FLD_TFTDATALINES]    = { },
 195                        [DISPC_MGR_FLD_STALLMODE]       = { },
 196                        [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG,  12, 12 },
 197                        [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG,  13, 13 },
 198                        [DISPC_MGR_FLD_CPR]             = { },
 199                        [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG,  16, 16 },
 200                },
 201        },
 202        [OMAP_DSS_CHANNEL_LCD2] = {
 203                .name           = "LCD2",
 204                .vsync_irq      = DISPC_IRQ_VSYNC2,
 205                .framedone_irq  = DISPC_IRQ_FRAMEDONE2,
 206                .sync_lost_irq  = DISPC_IRQ_SYNC_LOST2,
 207                .reg_desc       = {
 208                        [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL2,  0,  0 },
 209                        [DISPC_MGR_FLD_STNTFT]          = { DISPC_CONTROL2,  3,  3 },
 210                        [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL2,  5,  5 },
 211                        [DISPC_MGR_FLD_TFTDATALINES]    = { DISPC_CONTROL2,  9,  8 },
 212                        [DISPC_MGR_FLD_STALLMODE]       = { DISPC_CONTROL2, 11, 11 },
 213                        [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG2,  10, 10 },
 214                        [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG2,  11, 11 },
 215                        [DISPC_MGR_FLD_CPR]             = { DISPC_CONFIG2,  15, 15 },
 216                        [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG2,  16, 16 },
 217                },
 218        },
 219        [OMAP_DSS_CHANNEL_LCD3] = {
 220                .name           = "LCD3",
 221                .vsync_irq      = DISPC_IRQ_VSYNC3,
 222                .framedone_irq  = DISPC_IRQ_FRAMEDONE3,
 223                .sync_lost_irq  = DISPC_IRQ_SYNC_LOST3,
 224                .reg_desc       = {
 225                        [DISPC_MGR_FLD_ENABLE]          = { DISPC_CONTROL3,  0,  0 },
 226                        [DISPC_MGR_FLD_STNTFT]          = { DISPC_CONTROL3,  3,  3 },
 227                        [DISPC_MGR_FLD_GO]              = { DISPC_CONTROL3,  5,  5 },
 228                        [DISPC_MGR_FLD_TFTDATALINES]    = { DISPC_CONTROL3,  9,  8 },
 229                        [DISPC_MGR_FLD_STALLMODE]       = { DISPC_CONTROL3, 11, 11 },
 230                        [DISPC_MGR_FLD_TCKENABLE]       = { DISPC_CONFIG3,  10, 10 },
 231                        [DISPC_MGR_FLD_TCKSELECTION]    = { DISPC_CONFIG3,  11, 11 },
 232                        [DISPC_MGR_FLD_CPR]             = { DISPC_CONFIG3,  15, 15 },
 233                        [DISPC_MGR_FLD_FIFOHANDCHECK]   = { DISPC_CONFIG3,  16, 16 },
 234                },
 235        },
 236};
 237
 238struct color_conv_coef {
 239        int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb;
 240        int full_range;
 241};
 242
 243static unsigned long dispc_fclk_rate(void);
 244static unsigned long dispc_core_clk_rate(void);
 245static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel);
 246static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel);
 247
 248static unsigned long dispc_plane_pclk_rate(enum omap_plane plane);
 249static unsigned long dispc_plane_lclk_rate(enum omap_plane plane);
 250
 251static inline void dispc_write_reg(const u16 idx, u32 val)
 252{
 253        __raw_writel(val, dispc.base + idx);
 254}
 255
 256static inline u32 dispc_read_reg(const u16 idx)
 257{
 258        return __raw_readl(dispc.base + idx);
 259}
 260
 261static u32 mgr_fld_read(enum omap_channel channel, enum mgr_reg_fields regfld)
 262{
 263        const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld];
 264        return REG_GET(rfld.reg, rfld.high, rfld.low);
 265}
 266
 267static void mgr_fld_write(enum omap_channel channel,
 268                                        enum mgr_reg_fields regfld, int val) {
 269        const struct dispc_reg_field rfld = mgr_desc[channel].reg_desc[regfld];
 270        const bool need_lock = rfld.reg == DISPC_CONTROL || rfld.reg == DISPC_CONFIG;
 271        unsigned long flags;
 272
 273        if (need_lock)
 274                spin_lock_irqsave(&dispc.control_lock, flags);
 275
 276        REG_FLD_MOD(rfld.reg, val, rfld.high, rfld.low);
 277
 278        if (need_lock)
 279                spin_unlock_irqrestore(&dispc.control_lock, flags);
 280}
 281
 282#define SR(reg) \
 283        dispc.ctx[DISPC_##reg / sizeof(u32)] = dispc_read_reg(DISPC_##reg)
 284#define RR(reg) \
 285        dispc_write_reg(DISPC_##reg, dispc.ctx[DISPC_##reg / sizeof(u32)])
 286
 287static void dispc_save_context(void)
 288{
 289        int i, j;
 290
 291        DSSDBG("dispc_save_context\n");
 292
 293        SR(IRQENABLE);
 294        SR(CONTROL);
 295        SR(CONFIG);
 296        SR(LINE_NUMBER);
 297        if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
 298                        dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
 299                SR(GLOBAL_ALPHA);
 300        if (dss_has_feature(FEAT_MGR_LCD2)) {
 301                SR(CONTROL2);
 302                SR(CONFIG2);
 303        }
 304        if (dss_has_feature(FEAT_MGR_LCD3)) {
 305                SR(CONTROL3);
 306                SR(CONFIG3);
 307        }
 308
 309        for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
 310                SR(DEFAULT_COLOR(i));
 311                SR(TRANS_COLOR(i));
 312                SR(SIZE_MGR(i));
 313                if (i == OMAP_DSS_CHANNEL_DIGIT)
 314                        continue;
 315                SR(TIMING_H(i));
 316                SR(TIMING_V(i));
 317                SR(POL_FREQ(i));
 318                SR(DIVISORo(i));
 319
 320                SR(DATA_CYCLE1(i));
 321                SR(DATA_CYCLE2(i));
 322                SR(DATA_CYCLE3(i));
 323
 324                if (dss_has_feature(FEAT_CPR)) {
 325                        SR(CPR_COEF_R(i));
 326                        SR(CPR_COEF_G(i));
 327                        SR(CPR_COEF_B(i));
 328                }
 329        }
 330
 331        for (i = 0; i < dss_feat_get_num_ovls(); i++) {
 332                SR(OVL_BA0(i));
 333                SR(OVL_BA1(i));
 334                SR(OVL_POSITION(i));
 335                SR(OVL_SIZE(i));
 336                SR(OVL_ATTRIBUTES(i));
 337                SR(OVL_FIFO_THRESHOLD(i));
 338                SR(OVL_ROW_INC(i));
 339                SR(OVL_PIXEL_INC(i));
 340                if (dss_has_feature(FEAT_PRELOAD))
 341                        SR(OVL_PRELOAD(i));
 342                if (i == OMAP_DSS_GFX) {
 343                        SR(OVL_WINDOW_SKIP(i));
 344                        SR(OVL_TABLE_BA(i));
 345                        continue;
 346                }
 347                SR(OVL_FIR(i));
 348                SR(OVL_PICTURE_SIZE(i));
 349                SR(OVL_ACCU0(i));
 350                SR(OVL_ACCU1(i));
 351
 352                for (j = 0; j < 8; j++)
 353                        SR(OVL_FIR_COEF_H(i, j));
 354
 355                for (j = 0; j < 8; j++)
 356                        SR(OVL_FIR_COEF_HV(i, j));
 357
 358                for (j = 0; j < 5; j++)
 359                        SR(OVL_CONV_COEF(i, j));
 360
 361                if (dss_has_feature(FEAT_FIR_COEF_V)) {
 362                        for (j = 0; j < 8; j++)
 363                                SR(OVL_FIR_COEF_V(i, j));
 364                }
 365
 366                if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
 367                        SR(OVL_BA0_UV(i));
 368                        SR(OVL_BA1_UV(i));
 369                        SR(OVL_FIR2(i));
 370                        SR(OVL_ACCU2_0(i));
 371                        SR(OVL_ACCU2_1(i));
 372
 373                        for (j = 0; j < 8; j++)
 374                                SR(OVL_FIR_COEF_H2(i, j));
 375
 376                        for (j = 0; j < 8; j++)
 377                                SR(OVL_FIR_COEF_HV2(i, j));
 378
 379                        for (j = 0; j < 8; j++)
 380                                SR(OVL_FIR_COEF_V2(i, j));
 381                }
 382                if (dss_has_feature(FEAT_ATTR2))
 383                        SR(OVL_ATTRIBUTES2(i));
 384        }
 385
 386        if (dss_has_feature(FEAT_CORE_CLK_DIV))
 387                SR(DIVISOR);
 388
 389        dispc.ctx_valid = true;
 390
 391        DSSDBG("context saved\n");
 392}
 393
 394static void dispc_restore_context(void)
 395{
 396        int i, j;
 397
 398        DSSDBG("dispc_restore_context\n");
 399
 400        if (!dispc.ctx_valid)
 401                return;
 402
 403        /*RR(IRQENABLE);*/
 404        /*RR(CONTROL);*/
 405        RR(CONFIG);
 406        RR(LINE_NUMBER);
 407        if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
 408                        dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
 409                RR(GLOBAL_ALPHA);
 410        if (dss_has_feature(FEAT_MGR_LCD2))
 411                RR(CONFIG2);
 412        if (dss_has_feature(FEAT_MGR_LCD3))
 413                RR(CONFIG3);
 414
 415        for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
 416                RR(DEFAULT_COLOR(i));
 417                RR(TRANS_COLOR(i));
 418                RR(SIZE_MGR(i));
 419                if (i == OMAP_DSS_CHANNEL_DIGIT)
 420                        continue;
 421                RR(TIMING_H(i));
 422                RR(TIMING_V(i));
 423                RR(POL_FREQ(i));
 424                RR(DIVISORo(i));
 425
 426                RR(DATA_CYCLE1(i));
 427                RR(DATA_CYCLE2(i));
 428                RR(DATA_CYCLE3(i));
 429
 430                if (dss_has_feature(FEAT_CPR)) {
 431                        RR(CPR_COEF_R(i));
 432                        RR(CPR_COEF_G(i));
 433                        RR(CPR_COEF_B(i));
 434                }
 435        }
 436
 437        for (i = 0; i < dss_feat_get_num_ovls(); i++) {
 438                RR(OVL_BA0(i));
 439                RR(OVL_BA1(i));
 440                RR(OVL_POSITION(i));
 441                RR(OVL_SIZE(i));
 442                RR(OVL_ATTRIBUTES(i));
 443                RR(OVL_FIFO_THRESHOLD(i));
 444                RR(OVL_ROW_INC(i));
 445                RR(OVL_PIXEL_INC(i));
 446                if (dss_has_feature(FEAT_PRELOAD))
 447                        RR(OVL_PRELOAD(i));
 448                if (i == OMAP_DSS_GFX) {
 449                        RR(OVL_WINDOW_SKIP(i));
 450                        RR(OVL_TABLE_BA(i));
 451                        continue;
 452                }
 453                RR(OVL_FIR(i));
 454                RR(OVL_PICTURE_SIZE(i));
 455                RR(OVL_ACCU0(i));
 456                RR(OVL_ACCU1(i));
 457
 458                for (j = 0; j < 8; j++)
 459                        RR(OVL_FIR_COEF_H(i, j));
 460
 461                for (j = 0; j < 8; j++)
 462                        RR(OVL_FIR_COEF_HV(i, j));
 463
 464                for (j = 0; j < 5; j++)
 465                        RR(OVL_CONV_COEF(i, j));
 466
 467                if (dss_has_feature(FEAT_FIR_COEF_V)) {
 468                        for (j = 0; j < 8; j++)
 469                                RR(OVL_FIR_COEF_V(i, j));
 470                }
 471
 472                if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
 473                        RR(OVL_BA0_UV(i));
 474                        RR(OVL_BA1_UV(i));
 475                        RR(OVL_FIR2(i));
 476                        RR(OVL_ACCU2_0(i));
 477                        RR(OVL_ACCU2_1(i));
 478
 479                        for (j = 0; j < 8; j++)
 480                                RR(OVL_FIR_COEF_H2(i, j));
 481
 482                        for (j = 0; j < 8; j++)
 483                                RR(OVL_FIR_COEF_HV2(i, j));
 484
 485                        for (j = 0; j < 8; j++)
 486                                RR(OVL_FIR_COEF_V2(i, j));
 487                }
 488                if (dss_has_feature(FEAT_ATTR2))
 489                        RR(OVL_ATTRIBUTES2(i));
 490        }
 491
 492        if (dss_has_feature(FEAT_CORE_CLK_DIV))
 493                RR(DIVISOR);
 494
 495        /* enable last, because LCD & DIGIT enable are here */
 496        RR(CONTROL);
 497        if (dss_has_feature(FEAT_MGR_LCD2))
 498                RR(CONTROL2);
 499        if (dss_has_feature(FEAT_MGR_LCD3))
 500                RR(CONTROL3);
 501        /* clear spurious SYNC_LOST_DIGIT interrupts */
 502        dispc_clear_irqstatus(DISPC_IRQ_SYNC_LOST_DIGIT);
 503
 504        /*
 505         * enable last so IRQs won't trigger before
 506         * the context is fully restored
 507         */
 508        RR(IRQENABLE);
 509
 510        DSSDBG("context restored\n");
 511}
 512
 513#undef SR
 514#undef RR
 515
 516int dispc_runtime_get(void)
 517{
 518        int r;
 519
 520        DSSDBG("dispc_runtime_get\n");
 521
 522        r = pm_runtime_get_sync(&dispc.pdev->dev);
 523        WARN_ON(r < 0);
 524        return r < 0 ? r : 0;
 525}
 526EXPORT_SYMBOL(dispc_runtime_get);
 527
 528void dispc_runtime_put(void)
 529{
 530        int r;
 531
 532        DSSDBG("dispc_runtime_put\n");
 533
 534        r = pm_runtime_put_sync(&dispc.pdev->dev);
 535        WARN_ON(r < 0 && r != -ENOSYS);
 536}
 537EXPORT_SYMBOL(dispc_runtime_put);
 538
 539u32 dispc_mgr_get_vsync_irq(enum omap_channel channel)
 540{
 541        return mgr_desc[channel].vsync_irq;
 542}
 543EXPORT_SYMBOL(dispc_mgr_get_vsync_irq);
 544
 545u32 dispc_mgr_get_framedone_irq(enum omap_channel channel)
 546{
 547        if (channel == OMAP_DSS_CHANNEL_DIGIT && dispc.feat->no_framedone_tv)
 548                return 0;
 549
 550        return mgr_desc[channel].framedone_irq;
 551}
 552EXPORT_SYMBOL(dispc_mgr_get_framedone_irq);
 553
 554u32 dispc_mgr_get_sync_lost_irq(enum omap_channel channel)
 555{
 556        return mgr_desc[channel].sync_lost_irq;
 557}
 558EXPORT_SYMBOL(dispc_mgr_get_sync_lost_irq);
 559
 560u32 dispc_wb_get_framedone_irq(void)
 561{
 562        return DISPC_IRQ_FRAMEDONEWB;
 563}
 564
 565bool dispc_mgr_go_busy(enum omap_channel channel)
 566{
 567        return mgr_fld_read(channel, DISPC_MGR_FLD_GO) == 1;
 568}
 569EXPORT_SYMBOL(dispc_mgr_go_busy);
 570
 571void dispc_mgr_go(enum omap_channel channel)
 572{
 573        WARN_ON(!dispc_mgr_is_enabled(channel));
 574        WARN_ON(dispc_mgr_go_busy(channel));
 575
 576        DSSDBG("GO %s\n", mgr_desc[channel].name);
 577
 578        mgr_fld_write(channel, DISPC_MGR_FLD_GO, 1);
 579}
 580EXPORT_SYMBOL(dispc_mgr_go);
 581
 582bool dispc_wb_go_busy(void)
 583{
 584        return REG_GET(DISPC_CONTROL2, 6, 6) == 1;
 585}
 586
 587void dispc_wb_go(void)
 588{
 589        enum omap_plane plane = OMAP_DSS_WB;
 590        bool enable, go;
 591
 592        enable = REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0) == 1;
 593
 594        if (!enable)
 595                return;
 596
 597        go = REG_GET(DISPC_CONTROL2, 6, 6) == 1;
 598        if (go) {
 599                DSSERR("GO bit not down for WB\n");
 600                return;
 601        }
 602
 603        REG_FLD_MOD(DISPC_CONTROL2, 1, 6, 6);
 604}
 605
 606static void dispc_ovl_write_firh_reg(enum omap_plane plane, int reg, u32 value)
 607{
 608        dispc_write_reg(DISPC_OVL_FIR_COEF_H(plane, reg), value);
 609}
 610
 611static void dispc_ovl_write_firhv_reg(enum omap_plane plane, int reg, u32 value)
 612{
 613        dispc_write_reg(DISPC_OVL_FIR_COEF_HV(plane, reg), value);
 614}
 615
 616static void dispc_ovl_write_firv_reg(enum omap_plane plane, int reg, u32 value)
 617{
 618        dispc_write_reg(DISPC_OVL_FIR_COEF_V(plane, reg), value);
 619}
 620
 621static void dispc_ovl_write_firh2_reg(enum omap_plane plane, int reg, u32 value)
 622{
 623        BUG_ON(plane == OMAP_DSS_GFX);
 624
 625        dispc_write_reg(DISPC_OVL_FIR_COEF_H2(plane, reg), value);
 626}
 627
 628static void dispc_ovl_write_firhv2_reg(enum omap_plane plane, int reg,
 629                u32 value)
 630{
 631        BUG_ON(plane == OMAP_DSS_GFX);
 632
 633        dispc_write_reg(DISPC_OVL_FIR_COEF_HV2(plane, reg), value);
 634}
 635
 636static void dispc_ovl_write_firv2_reg(enum omap_plane plane, int reg, u32 value)
 637{
 638        BUG_ON(plane == OMAP_DSS_GFX);
 639
 640        dispc_write_reg(DISPC_OVL_FIR_COEF_V2(plane, reg), value);
 641}
 642
 643static void dispc_ovl_set_scale_coef(enum omap_plane plane, int fir_hinc,
 644                                int fir_vinc, int five_taps,
 645                                enum omap_color_component color_comp)
 646{
 647        const struct dispc_coef *h_coef, *v_coef;
 648        int i;
 649
 650        h_coef = dispc_ovl_get_scale_coef(fir_hinc, true);
 651        v_coef = dispc_ovl_get_scale_coef(fir_vinc, five_taps);
 652
 653        for (i = 0; i < 8; i++) {
 654                u32 h, hv;
 655
 656                h = FLD_VAL(h_coef[i].hc0_vc00, 7, 0)
 657                        | FLD_VAL(h_coef[i].hc1_vc0, 15, 8)
 658                        | FLD_VAL(h_coef[i].hc2_vc1, 23, 16)
 659                        | FLD_VAL(h_coef[i].hc3_vc2, 31, 24);
 660                hv = FLD_VAL(h_coef[i].hc4_vc22, 7, 0)
 661                        | FLD_VAL(v_coef[i].hc1_vc0, 15, 8)
 662                        | FLD_VAL(v_coef[i].hc2_vc1, 23, 16)
 663                        | FLD_VAL(v_coef[i].hc3_vc2, 31, 24);
 664
 665                if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
 666                        dispc_ovl_write_firh_reg(plane, i, h);
 667                        dispc_ovl_write_firhv_reg(plane, i, hv);
 668                } else {
 669                        dispc_ovl_write_firh2_reg(plane, i, h);
 670                        dispc_ovl_write_firhv2_reg(plane, i, hv);
 671                }
 672
 673        }
 674
 675        if (five_taps) {
 676                for (i = 0; i < 8; i++) {
 677                        u32 v;
 678                        v = FLD_VAL(v_coef[i].hc0_vc00, 7, 0)
 679                                | FLD_VAL(v_coef[i].hc4_vc22, 15, 8);
 680                        if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y)
 681                                dispc_ovl_write_firv_reg(plane, i, v);
 682                        else
 683                                dispc_ovl_write_firv2_reg(plane, i, v);
 684                }
 685        }
 686}
 687
 688
 689static void dispc_ovl_write_color_conv_coef(enum omap_plane plane,
 690                const struct color_conv_coef *ct)
 691{
 692#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0))
 693
 694        dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 0), CVAL(ct->rcr, ct->ry));
 695        dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 1), CVAL(ct->gy,  ct->rcb));
 696        dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 2), CVAL(ct->gcb, ct->gcr));
 697        dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 3), CVAL(ct->bcr, ct->by));
 698        dispc_write_reg(DISPC_OVL_CONV_COEF(plane, 4), CVAL(0, ct->bcb));
 699
 700        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), ct->full_range, 11, 11);
 701
 702#undef CVAL
 703}
 704
 705static void dispc_setup_color_conv_coef(void)
 706{
 707        int i;
 708        int num_ovl = dss_feat_get_num_ovls();
 709        const struct color_conv_coef ctbl_bt601_5_ovl = {
 710                /* YUV -> RGB */
 711                298, 409, 0, 298, -208, -100, 298, 0, 517, 0,
 712        };
 713        const struct color_conv_coef ctbl_bt601_5_wb = {
 714                /* RGB -> YUV */
 715                66, 129, 25, 112, -94, -18, -38, -74, 112, 0,
 716        };
 717
 718        for (i = 1; i < num_ovl; i++)
 719                dispc_ovl_write_color_conv_coef(i, &ctbl_bt601_5_ovl);
 720
 721        if (dispc.feat->has_writeback)
 722                dispc_ovl_write_color_conv_coef(OMAP_DSS_WB, &ctbl_bt601_5_wb);
 723}
 724
 725static void dispc_ovl_set_ba0(enum omap_plane plane, u32 paddr)
 726{
 727        dispc_write_reg(DISPC_OVL_BA0(plane), paddr);
 728}
 729
 730static void dispc_ovl_set_ba1(enum omap_plane plane, u32 paddr)
 731{
 732        dispc_write_reg(DISPC_OVL_BA1(plane), paddr);
 733}
 734
 735static void dispc_ovl_set_ba0_uv(enum omap_plane plane, u32 paddr)
 736{
 737        dispc_write_reg(DISPC_OVL_BA0_UV(plane), paddr);
 738}
 739
 740static void dispc_ovl_set_ba1_uv(enum omap_plane plane, u32 paddr)
 741{
 742        dispc_write_reg(DISPC_OVL_BA1_UV(plane), paddr);
 743}
 744
 745static void dispc_ovl_set_pos(enum omap_plane plane,
 746                enum omap_overlay_caps caps, int x, int y)
 747{
 748        u32 val;
 749
 750        if ((caps & OMAP_DSS_OVL_CAP_POS) == 0)
 751                return;
 752
 753        val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0);
 754
 755        dispc_write_reg(DISPC_OVL_POSITION(plane), val);
 756}
 757
 758static void dispc_ovl_set_input_size(enum omap_plane plane, int width,
 759                int height)
 760{
 761        u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
 762
 763        if (plane == OMAP_DSS_GFX || plane == OMAP_DSS_WB)
 764                dispc_write_reg(DISPC_OVL_SIZE(plane), val);
 765        else
 766                dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
 767}
 768
 769static void dispc_ovl_set_output_size(enum omap_plane plane, int width,
 770                int height)
 771{
 772        u32 val;
 773
 774        BUG_ON(plane == OMAP_DSS_GFX);
 775
 776        val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0);
 777
 778        if (plane == OMAP_DSS_WB)
 779                dispc_write_reg(DISPC_OVL_PICTURE_SIZE(plane), val);
 780        else
 781                dispc_write_reg(DISPC_OVL_SIZE(plane), val);
 782}
 783
 784static void dispc_ovl_set_zorder(enum omap_plane plane,
 785                enum omap_overlay_caps caps, u8 zorder)
 786{
 787        if ((caps & OMAP_DSS_OVL_CAP_ZORDER) == 0)
 788                return;
 789
 790        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), zorder, 27, 26);
 791}
 792
 793static void dispc_ovl_enable_zorder_planes(void)
 794{
 795        int i;
 796
 797        if (!dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
 798                return;
 799
 800        for (i = 0; i < dss_feat_get_num_ovls(); i++)
 801                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(i), 1, 25, 25);
 802}
 803
 804static void dispc_ovl_set_pre_mult_alpha(enum omap_plane plane,
 805                enum omap_overlay_caps caps, bool enable)
 806{
 807        if ((caps & OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA) == 0)
 808                return;
 809
 810        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 28, 28);
 811}
 812
 813static void dispc_ovl_setup_global_alpha(enum omap_plane plane,
 814                enum omap_overlay_caps caps, u8 global_alpha)
 815{
 816        static const unsigned shifts[] = { 0, 8, 16, 24, };
 817        int shift;
 818
 819        if ((caps & OMAP_DSS_OVL_CAP_GLOBAL_ALPHA) == 0)
 820                return;
 821
 822        shift = shifts[plane];
 823        REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, shift + 7, shift);
 824}
 825
 826static void dispc_ovl_set_pix_inc(enum omap_plane plane, s32 inc)
 827{
 828        dispc_write_reg(DISPC_OVL_PIXEL_INC(plane), inc);
 829}
 830
 831static void dispc_ovl_set_row_inc(enum omap_plane plane, s32 inc)
 832{
 833        dispc_write_reg(DISPC_OVL_ROW_INC(plane), inc);
 834}
 835
 836static void dispc_ovl_set_color_mode(enum omap_plane plane,
 837                enum omap_color_mode color_mode)
 838{
 839        u32 m = 0;
 840        if (plane != OMAP_DSS_GFX) {
 841                switch (color_mode) {
 842                case OMAP_DSS_COLOR_NV12:
 843                        m = 0x0; break;
 844                case OMAP_DSS_COLOR_RGBX16:
 845                        m = 0x1; break;
 846                case OMAP_DSS_COLOR_RGBA16:
 847                        m = 0x2; break;
 848                case OMAP_DSS_COLOR_RGB12U:
 849                        m = 0x4; break;
 850                case OMAP_DSS_COLOR_ARGB16:
 851                        m = 0x5; break;
 852                case OMAP_DSS_COLOR_RGB16:
 853                        m = 0x6; break;
 854                case OMAP_DSS_COLOR_ARGB16_1555:
 855                        m = 0x7; break;
 856                case OMAP_DSS_COLOR_RGB24U:
 857                        m = 0x8; break;
 858                case OMAP_DSS_COLOR_RGB24P:
 859                        m = 0x9; break;
 860                case OMAP_DSS_COLOR_YUV2:
 861                        m = 0xa; break;
 862                case OMAP_DSS_COLOR_UYVY:
 863                        m = 0xb; break;
 864                case OMAP_DSS_COLOR_ARGB32:
 865                        m = 0xc; break;
 866                case OMAP_DSS_COLOR_RGBA32:
 867                        m = 0xd; break;
 868                case OMAP_DSS_COLOR_RGBX32:
 869                        m = 0xe; break;
 870                case OMAP_DSS_COLOR_XRGB16_1555:
 871                        m = 0xf; break;
 872                default:
 873                        BUG(); return;
 874                }
 875        } else {
 876                switch (color_mode) {
 877                case OMAP_DSS_COLOR_CLUT1:
 878                        m = 0x0; break;
 879                case OMAP_DSS_COLOR_CLUT2:
 880                        m = 0x1; break;
 881                case OMAP_DSS_COLOR_CLUT4:
 882                        m = 0x2; break;
 883                case OMAP_DSS_COLOR_CLUT8:
 884                        m = 0x3; break;
 885                case OMAP_DSS_COLOR_RGB12U:
 886                        m = 0x4; break;
 887                case OMAP_DSS_COLOR_ARGB16:
 888                        m = 0x5; break;
 889                case OMAP_DSS_COLOR_RGB16:
 890                        m = 0x6; break;
 891                case OMAP_DSS_COLOR_ARGB16_1555:
 892                        m = 0x7; break;
 893                case OMAP_DSS_COLOR_RGB24U:
 894                        m = 0x8; break;
 895                case OMAP_DSS_COLOR_RGB24P:
 896                        m = 0x9; break;
 897                case OMAP_DSS_COLOR_RGBX16:
 898                        m = 0xa; break;
 899                case OMAP_DSS_COLOR_RGBA16:
 900                        m = 0xb; break;
 901                case OMAP_DSS_COLOR_ARGB32:
 902                        m = 0xc; break;
 903                case OMAP_DSS_COLOR_RGBA32:
 904                        m = 0xd; break;
 905                case OMAP_DSS_COLOR_RGBX32:
 906                        m = 0xe; break;
 907                case OMAP_DSS_COLOR_XRGB16_1555:
 908                        m = 0xf; break;
 909                default:
 910                        BUG(); return;
 911                }
 912        }
 913
 914        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), m, 4, 1);
 915}
 916
 917static void dispc_ovl_configure_burst_type(enum omap_plane plane,
 918                enum omap_dss_rotation_type rotation_type)
 919{
 920        if (dss_has_feature(FEAT_BURST_2D) == 0)
 921                return;
 922
 923        if (rotation_type == OMAP_DSS_ROT_TILER)
 924                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 1, 29, 29);
 925        else
 926                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), 0, 29, 29);
 927}
 928
 929void dispc_ovl_set_channel_out(enum omap_plane plane, enum omap_channel channel)
 930{
 931        int shift;
 932        u32 val;
 933        int chan = 0, chan2 = 0;
 934
 935        switch (plane) {
 936        case OMAP_DSS_GFX:
 937                shift = 8;
 938                break;
 939        case OMAP_DSS_VIDEO1:
 940        case OMAP_DSS_VIDEO2:
 941        case OMAP_DSS_VIDEO3:
 942                shift = 16;
 943                break;
 944        default:
 945                BUG();
 946                return;
 947        }
 948
 949        val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
 950        if (dss_has_feature(FEAT_MGR_LCD2)) {
 951                switch (channel) {
 952                case OMAP_DSS_CHANNEL_LCD:
 953                        chan = 0;
 954                        chan2 = 0;
 955                        break;
 956                case OMAP_DSS_CHANNEL_DIGIT:
 957                        chan = 1;
 958                        chan2 = 0;
 959                        break;
 960                case OMAP_DSS_CHANNEL_LCD2:
 961                        chan = 0;
 962                        chan2 = 1;
 963                        break;
 964                case OMAP_DSS_CHANNEL_LCD3:
 965                        if (dss_has_feature(FEAT_MGR_LCD3)) {
 966                                chan = 0;
 967                                chan2 = 2;
 968                        } else {
 969                                BUG();
 970                                return;
 971                        }
 972                        break;
 973                case OMAP_DSS_CHANNEL_WB:
 974                        chan = 0;
 975                        chan2 = 3;
 976                        break;
 977                default:
 978                        BUG();
 979                        return;
 980                }
 981
 982                val = FLD_MOD(val, chan, shift, shift);
 983                val = FLD_MOD(val, chan2, 31, 30);
 984        } else {
 985                val = FLD_MOD(val, channel, shift, shift);
 986        }
 987        dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
 988}
 989EXPORT_SYMBOL(dispc_ovl_set_channel_out);
 990
 991static enum omap_channel dispc_ovl_get_channel_out(enum omap_plane plane)
 992{
 993        int shift;
 994        u32 val;
 995
 996        switch (plane) {
 997        case OMAP_DSS_GFX:
 998                shift = 8;
 999                break;
1000        case OMAP_DSS_VIDEO1:
1001        case OMAP_DSS_VIDEO2:
1002        case OMAP_DSS_VIDEO3:
1003                shift = 16;
1004                break;
1005        default:
1006                BUG();
1007                return 0;
1008        }
1009
1010        val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
1011
1012        if (FLD_GET(val, shift, shift) == 1)
1013                return OMAP_DSS_CHANNEL_DIGIT;
1014
1015        if (!dss_has_feature(FEAT_MGR_LCD2))
1016                return OMAP_DSS_CHANNEL_LCD;
1017
1018        switch (FLD_GET(val, 31, 30)) {
1019        case 0:
1020        default:
1021                return OMAP_DSS_CHANNEL_LCD;
1022        case 1:
1023                return OMAP_DSS_CHANNEL_LCD2;
1024        case 2:
1025                return OMAP_DSS_CHANNEL_LCD3;
1026        case 3:
1027                return OMAP_DSS_CHANNEL_WB;
1028        }
1029}
1030
1031void dispc_wb_set_channel_in(enum dss_writeback_channel channel)
1032{
1033        enum omap_plane plane = OMAP_DSS_WB;
1034
1035        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), channel, 18, 16);
1036}
1037
1038static void dispc_ovl_set_burst_size(enum omap_plane plane,
1039                enum omap_burst_size burst_size)
1040{
1041        static const unsigned shifts[] = { 6, 14, 14, 14, 14, };
1042        int shift;
1043
1044        shift = shifts[plane];
1045        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), burst_size, shift + 1, shift);
1046}
1047
1048static void dispc_configure_burst_sizes(void)
1049{
1050        int i;
1051        const int burst_size = BURST_SIZE_X8;
1052
1053        /* Configure burst size always to maximum size */
1054        for (i = 0; i < dss_feat_get_num_ovls(); ++i)
1055                dispc_ovl_set_burst_size(i, burst_size);
1056        if (dispc.feat->has_writeback)
1057                dispc_ovl_set_burst_size(OMAP_DSS_WB, burst_size);
1058}
1059
1060static u32 dispc_ovl_get_burst_size(enum omap_plane plane)
1061{
1062        unsigned unit = dss_feat_get_burst_size_unit();
1063        /* burst multiplier is always x8 (see dispc_configure_burst_sizes()) */
1064        return unit * 8;
1065}
1066
1067void dispc_enable_gamma_table(bool enable)
1068{
1069        /*
1070         * This is partially implemented to support only disabling of
1071         * the gamma table.
1072         */
1073        if (enable) {
1074                DSSWARN("Gamma table enabling for TV not yet supported");
1075                return;
1076        }
1077
1078        REG_FLD_MOD(DISPC_CONFIG, enable, 9, 9);
1079}
1080
1081static void dispc_mgr_enable_cpr(enum omap_channel channel, bool enable)
1082{
1083        if (channel == OMAP_DSS_CHANNEL_DIGIT)
1084                return;
1085
1086        mgr_fld_write(channel, DISPC_MGR_FLD_CPR, enable);
1087}
1088
1089static void dispc_mgr_set_cpr_coef(enum omap_channel channel,
1090                const struct omap_dss_cpr_coefs *coefs)
1091{
1092        u32 coef_r, coef_g, coef_b;
1093
1094        if (!dss_mgr_is_lcd(channel))
1095                return;
1096
1097        coef_r = FLD_VAL(coefs->rr, 31, 22) | FLD_VAL(coefs->rg, 20, 11) |
1098                FLD_VAL(coefs->rb, 9, 0);
1099        coef_g = FLD_VAL(coefs->gr, 31, 22) | FLD_VAL(coefs->gg, 20, 11) |
1100                FLD_VAL(coefs->gb, 9, 0);
1101        coef_b = FLD_VAL(coefs->br, 31, 22) | FLD_VAL(coefs->bg, 20, 11) |
1102                FLD_VAL(coefs->bb, 9, 0);
1103
1104        dispc_write_reg(DISPC_CPR_COEF_R(channel), coef_r);
1105        dispc_write_reg(DISPC_CPR_COEF_G(channel), coef_g);
1106        dispc_write_reg(DISPC_CPR_COEF_B(channel), coef_b);
1107}
1108
1109static void dispc_ovl_set_vid_color_conv(enum omap_plane plane, bool enable)
1110{
1111        u32 val;
1112
1113        BUG_ON(plane == OMAP_DSS_GFX);
1114
1115        val = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
1116        val = FLD_MOD(val, enable, 9, 9);
1117        dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), val);
1118}
1119
1120static void dispc_ovl_enable_replication(enum omap_plane plane,
1121                enum omap_overlay_caps caps, bool enable)
1122{
1123        static const unsigned shifts[] = { 5, 10, 10, 10 };
1124        int shift;
1125
1126        if ((caps & OMAP_DSS_OVL_CAP_REPLICATION) == 0)
1127                return;
1128
1129        shift = shifts[plane];
1130        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, shift, shift);
1131}
1132
1133static void dispc_mgr_set_size(enum omap_channel channel, u16 width,
1134                u16 height)
1135{
1136        u32 val;
1137
1138        val = FLD_VAL(height - 1, dispc.feat->mgr_height_start, 16) |
1139                FLD_VAL(width - 1, dispc.feat->mgr_width_start, 0);
1140
1141        dispc_write_reg(DISPC_SIZE_MGR(channel), val);
1142}
1143
1144static void dispc_init_fifos(void)
1145{
1146        u32 size;
1147        int fifo;
1148        u8 start, end;
1149        u32 unit;
1150        int i;
1151
1152        unit = dss_feat_get_buffer_size_unit();
1153
1154        dss_feat_get_reg_field(FEAT_REG_FIFOSIZE, &start, &end);
1155
1156        for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
1157                size = REG_GET(DISPC_OVL_FIFO_SIZE_STATUS(fifo), start, end);
1158                size *= unit;
1159                dispc.fifo_size[fifo] = size;
1160
1161                /*
1162                 * By default fifos are mapped directly to overlays, fifo 0 to
1163                 * ovl 0, fifo 1 to ovl 1, etc.
1164                 */
1165                dispc.fifo_assignment[fifo] = fifo;
1166        }
1167
1168        /*
1169         * The GFX fifo on OMAP4 is smaller than the other fifos. The small fifo
1170         * causes problems with certain use cases, like using the tiler in 2D
1171         * mode. The below hack swaps the fifos of GFX and WB planes, thus
1172         * giving GFX plane a larger fifo. WB but should work fine with a
1173         * smaller fifo.
1174         */
1175        if (dispc.feat->gfx_fifo_workaround) {
1176                u32 v;
1177
1178                v = dispc_read_reg(DISPC_GLOBAL_BUFFER);
1179
1180                v = FLD_MOD(v, 4, 2, 0); /* GFX BUF top to WB */
1181                v = FLD_MOD(v, 4, 5, 3); /* GFX BUF bottom to WB */
1182                v = FLD_MOD(v, 0, 26, 24); /* WB BUF top to GFX */
1183                v = FLD_MOD(v, 0, 29, 27); /* WB BUF bottom to GFX */
1184
1185                dispc_write_reg(DISPC_GLOBAL_BUFFER, v);
1186
1187                dispc.fifo_assignment[OMAP_DSS_GFX] = OMAP_DSS_WB;
1188                dispc.fifo_assignment[OMAP_DSS_WB] = OMAP_DSS_GFX;
1189        }
1190
1191        /*
1192         * Setup default fifo thresholds.
1193         */
1194        for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
1195                u32 low, high;
1196                const bool use_fifomerge = false;
1197                const bool manual_update = false;
1198
1199                dispc_ovl_compute_fifo_thresholds(i, &low, &high,
1200                        use_fifomerge, manual_update);
1201
1202                dispc_ovl_set_fifo_threshold(i, low, high);
1203        }
1204
1205        if (dispc.feat->has_writeback) {
1206                u32 low, high;
1207                const bool use_fifomerge = false;
1208                const bool manual_update = false;
1209
1210                dispc_ovl_compute_fifo_thresholds(OMAP_DSS_WB, &low, &high,
1211                        use_fifomerge, manual_update);
1212
1213                dispc_ovl_set_fifo_threshold(OMAP_DSS_WB, low, high);
1214        }
1215}
1216
1217static u32 dispc_ovl_get_fifo_size(enum omap_plane plane)
1218{
1219        int fifo;
1220        u32 size = 0;
1221
1222        for (fifo = 0; fifo < dispc.feat->num_fifos; ++fifo) {
1223                if (dispc.fifo_assignment[fifo] == plane)
1224                        size += dispc.fifo_size[fifo];
1225        }
1226
1227        return size;
1228}
1229
1230void dispc_ovl_set_fifo_threshold(enum omap_plane plane, u32 low, u32 high)
1231{
1232        u8 hi_start, hi_end, lo_start, lo_end;
1233        u32 unit;
1234
1235        unit = dss_feat_get_buffer_size_unit();
1236
1237        WARN_ON(low % unit != 0);
1238        WARN_ON(high % unit != 0);
1239
1240        low /= unit;
1241        high /= unit;
1242
1243        dss_feat_get_reg_field(FEAT_REG_FIFOHIGHTHRESHOLD, &hi_start, &hi_end);
1244        dss_feat_get_reg_field(FEAT_REG_FIFOLOWTHRESHOLD, &lo_start, &lo_end);
1245
1246        DSSDBG("fifo(%d) threshold (bytes), old %u/%u, new %u/%u\n",
1247                        plane,
1248                        REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
1249                                lo_start, lo_end) * unit,
1250                        REG_GET(DISPC_OVL_FIFO_THRESHOLD(plane),
1251                                hi_start, hi_end) * unit,
1252                        low * unit, high * unit);
1253
1254        dispc_write_reg(DISPC_OVL_FIFO_THRESHOLD(plane),
1255                        FLD_VAL(high, hi_start, hi_end) |
1256                        FLD_VAL(low, lo_start, lo_end));
1257
1258        /*
1259         * configure the preload to the pipeline's high threhold, if HT it's too
1260         * large for the preload field, set the threshold to the maximum value
1261         * that can be held by the preload register
1262         */
1263        if (dss_has_feature(FEAT_PRELOAD) && dispc.feat->set_max_preload &&
1264                        plane != OMAP_DSS_WB)
1265                dispc_write_reg(DISPC_OVL_PRELOAD(plane), min(high, 0xfffu));
1266}
1267
1268void dispc_enable_fifomerge(bool enable)
1269{
1270        if (!dss_has_feature(FEAT_FIFO_MERGE)) {
1271                WARN_ON(enable);
1272                return;
1273        }
1274
1275        DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled");
1276        REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14);
1277}
1278
1279void dispc_ovl_compute_fifo_thresholds(enum omap_plane plane,
1280                u32 *fifo_low, u32 *fifo_high, bool use_fifomerge,
1281                bool manual_update)
1282{
1283        /*
1284         * All sizes are in bytes. Both the buffer and burst are made of
1285         * buffer_units, and the fifo thresholds must be buffer_unit aligned.
1286         */
1287
1288        unsigned buf_unit = dss_feat_get_buffer_size_unit();
1289        unsigned ovl_fifo_size, total_fifo_size, burst_size;
1290        int i;
1291
1292        burst_size = dispc_ovl_get_burst_size(plane);
1293        ovl_fifo_size = dispc_ovl_get_fifo_size(plane);
1294
1295        if (use_fifomerge) {
1296                total_fifo_size = 0;
1297                for (i = 0; i < dss_feat_get_num_ovls(); ++i)
1298                        total_fifo_size += dispc_ovl_get_fifo_size(i);
1299        } else {
1300                total_fifo_size = ovl_fifo_size;
1301        }
1302
1303        /*
1304         * We use the same low threshold for both fifomerge and non-fifomerge
1305         * cases, but for fifomerge we calculate the high threshold using the
1306         * combined fifo size
1307         */
1308
1309        if (manual_update && dss_has_feature(FEAT_OMAP3_DSI_FIFO_BUG)) {
1310                *fifo_low = ovl_fifo_size - burst_size * 2;
1311                *fifo_high = total_fifo_size - burst_size;
1312        } else if (plane == OMAP_DSS_WB) {
1313                /*
1314                 * Most optimal configuration for writeback is to push out data
1315                 * to the interconnect the moment writeback pushes enough pixels
1316                 * in the FIFO to form a burst
1317                 */
1318                *fifo_low = 0;
1319                *fifo_high = burst_size;
1320        } else {
1321                *fifo_low = ovl_fifo_size - burst_size;
1322                *fifo_high = total_fifo_size - buf_unit;
1323        }
1324}
1325
1326static void dispc_ovl_set_mflag(enum omap_plane plane, bool enable)
1327{
1328        int bit;
1329
1330        if (plane == OMAP_DSS_GFX)
1331                bit = 14;
1332        else
1333                bit = 23;
1334
1335        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable, bit, bit);
1336}
1337
1338static void dispc_ovl_set_mflag_threshold(enum omap_plane plane,
1339        int low, int high)
1340{
1341        dispc_write_reg(DISPC_OVL_MFLAG_THRESHOLD(plane),
1342                FLD_VAL(high, 31, 16) | FLD_VAL(low, 15, 0));
1343}
1344
1345static void dispc_init_mflag(void)
1346{
1347        int i;
1348
1349        /*
1350         * HACK: NV12 color format and MFLAG seem to have problems working
1351         * together: using two displays, and having an NV12 overlay on one of
1352         * the displays will cause underflows/synclosts when MFLAG_CTRL=2.
1353         * Changing MFLAG thresholds and PRELOAD to certain values seem to
1354         * remove the errors, but there doesn't seem to be a clear logic on
1355         * which values work and which not.
1356         *
1357         * As a work-around, set force MFLAG to always on.
1358         */
1359        dispc_write_reg(DISPC_GLOBAL_MFLAG_ATTRIBUTE,
1360                (1 << 0) |      /* MFLAG_CTRL = force always on */
1361                (0 << 2));      /* MFLAG_START = disable */
1362
1363        for (i = 0; i < dss_feat_get_num_ovls(); ++i) {
1364                u32 size = dispc_ovl_get_fifo_size(i);
1365                u32 unit = dss_feat_get_buffer_size_unit();
1366                u32 low, high;
1367
1368                dispc_ovl_set_mflag(i, true);
1369
1370                /*
1371                 * Simulation team suggests below thesholds:
1372                 * HT = fifosize * 5 / 8;
1373                 * LT = fifosize * 4 / 8;
1374                 */
1375
1376                low = size * 4 / 8 / unit;
1377                high = size * 5 / 8 / unit;
1378
1379                dispc_ovl_set_mflag_threshold(i, low, high);
1380        }
1381
1382        if (dispc.feat->has_writeback) {
1383                u32 size = dispc_ovl_get_fifo_size(OMAP_DSS_WB);
1384                u32 unit = dss_feat_get_buffer_size_unit();
1385                u32 low, high;
1386
1387                dispc_ovl_set_mflag(OMAP_DSS_WB, true);
1388
1389                /*
1390                 * Simulation team suggests below thesholds:
1391                 * HT = fifosize * 5 / 8;
1392                 * LT = fifosize * 4 / 8;
1393                 */
1394
1395                low = size * 4 / 8 / unit;
1396                high = size * 5 / 8 / unit;
1397
1398                dispc_ovl_set_mflag_threshold(OMAP_DSS_WB, low, high);
1399        }
1400}
1401
1402static void dispc_ovl_set_fir(enum omap_plane plane,
1403                                int hinc, int vinc,
1404                                enum omap_color_component color_comp)
1405{
1406        u32 val;
1407
1408        if (color_comp == DISPC_COLOR_COMPONENT_RGB_Y) {
1409                u8 hinc_start, hinc_end, vinc_start, vinc_end;
1410
1411                dss_feat_get_reg_field(FEAT_REG_FIRHINC,
1412                                        &hinc_start, &hinc_end);
1413                dss_feat_get_reg_field(FEAT_REG_FIRVINC,
1414                                        &vinc_start, &vinc_end);
1415                val = FLD_VAL(vinc, vinc_start, vinc_end) |
1416                                FLD_VAL(hinc, hinc_start, hinc_end);
1417
1418                dispc_write_reg(DISPC_OVL_FIR(plane), val);
1419        } else {
1420                val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0);
1421                dispc_write_reg(DISPC_OVL_FIR2(plane), val);
1422        }
1423}
1424
1425static void dispc_ovl_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu)
1426{
1427        u32 val;
1428        u8 hor_start, hor_end, vert_start, vert_end;
1429
1430        dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end);
1431        dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end);
1432
1433        val = FLD_VAL(vaccu, vert_start, vert_end) |
1434                        FLD_VAL(haccu, hor_start, hor_end);
1435
1436        dispc_write_reg(DISPC_OVL_ACCU0(plane), val);
1437}
1438
1439static void dispc_ovl_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu)
1440{
1441        u32 val;
1442        u8 hor_start, hor_end, vert_start, vert_end;
1443
1444        dss_feat_get_reg_field(FEAT_REG_HORIZONTALACCU, &hor_start, &hor_end);
1445        dss_feat_get_reg_field(FEAT_REG_VERTICALACCU, &vert_start, &vert_end);
1446
1447        val = FLD_VAL(vaccu, vert_start, vert_end) |
1448                        FLD_VAL(haccu, hor_start, hor_end);
1449
1450        dispc_write_reg(DISPC_OVL_ACCU1(plane), val);
1451}
1452
1453static void dispc_ovl_set_vid_accu2_0(enum omap_plane plane, int haccu,
1454                int vaccu)
1455{
1456        u32 val;
1457
1458        val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0);
1459        dispc_write_reg(DISPC_OVL_ACCU2_0(plane), val);
1460}
1461
1462static void dispc_ovl_set_vid_accu2_1(enum omap_plane plane, int haccu,
1463                int vaccu)
1464{
1465        u32 val;
1466
1467        val = FLD_VAL(vaccu, 26, 16) | FLD_VAL(haccu, 10, 0);
1468        dispc_write_reg(DISPC_OVL_ACCU2_1(plane), val);
1469}
1470
1471static void dispc_ovl_set_scale_param(enum omap_plane plane,
1472                u16 orig_width, u16 orig_height,
1473                u16 out_width, u16 out_height,
1474                bool five_taps, u8 rotation,
1475                enum omap_color_component color_comp)
1476{
1477        int fir_hinc, fir_vinc;
1478
1479        fir_hinc = 1024 * orig_width / out_width;
1480        fir_vinc = 1024 * orig_height / out_height;
1481
1482        dispc_ovl_set_scale_coef(plane, fir_hinc, fir_vinc, five_taps,
1483                                color_comp);
1484        dispc_ovl_set_fir(plane, fir_hinc, fir_vinc, color_comp);
1485}
1486
1487static void dispc_ovl_set_accu_uv(enum omap_plane plane,
1488                u16 orig_width, u16 orig_height, u16 out_width, u16 out_height,
1489                bool ilace, enum omap_color_mode color_mode, u8 rotation)
1490{
1491        int h_accu2_0, h_accu2_1;
1492        int v_accu2_0, v_accu2_1;
1493        int chroma_hinc, chroma_vinc;
1494        int idx;
1495
1496        struct accu {
1497                s8 h0_m, h0_n;
1498                s8 h1_m, h1_n;
1499                s8 v0_m, v0_n;
1500                s8 v1_m, v1_n;
1501        };
1502
1503        const struct accu *accu_table;
1504        const struct accu *accu_val;
1505
1506        static const struct accu accu_nv12[4] = {
1507                {  0, 1,  0, 1 , -1, 2, 0, 1 },
1508                {  1, 2, -3, 4 ,  0, 1, 0, 1 },
1509                { -1, 1,  0, 1 , -1, 2, 0, 1 },
1510                { -1, 2, -1, 2 , -1, 1, 0, 1 },
1511        };
1512
1513        static const struct accu accu_nv12_ilace[4] = {
1514                {  0, 1,  0, 1 , -3, 4, -1, 4 },
1515                { -1, 4, -3, 4 ,  0, 1,  0, 1 },
1516                { -1, 1,  0, 1 , -1, 4, -3, 4 },
1517                { -3, 4, -3, 4 , -1, 1,  0, 1 },
1518        };
1519
1520        static const struct accu accu_yuv[4] = {
1521                {  0, 1, 0, 1,  0, 1, 0, 1 },
1522                {  0, 1, 0, 1,  0, 1, 0, 1 },
1523                { -1, 1, 0, 1,  0, 1, 0, 1 },
1524                {  0, 1, 0, 1, -1, 1, 0, 1 },
1525        };
1526
1527        switch (rotation) {
1528        case OMAP_DSS_ROT_0:
1529                idx = 0;
1530                break;
1531        case OMAP_DSS_ROT_90:
1532                idx = 1;
1533                break;
1534        case OMAP_DSS_ROT_180:
1535                idx = 2;
1536                break;
1537        case OMAP_DSS_ROT_270:
1538                idx = 3;
1539                break;
1540        default:
1541                BUG();
1542                return;
1543        }
1544
1545        switch (color_mode) {
1546        case OMAP_DSS_COLOR_NV12:
1547                if (ilace)
1548                        accu_table = accu_nv12_ilace;
1549                else
1550                        accu_table = accu_nv12;
1551                break;
1552        case OMAP_DSS_COLOR_YUV2:
1553        case OMAP_DSS_COLOR_UYVY:
1554                accu_table = accu_yuv;
1555                break;
1556        default:
1557                BUG();
1558                return;
1559        }
1560
1561        accu_val = &accu_table[idx];
1562
1563        chroma_hinc = 1024 * orig_width / out_width;
1564        chroma_vinc = 1024 * orig_height / out_height;
1565
1566        h_accu2_0 = (accu_val->h0_m * chroma_hinc / accu_val->h0_n) % 1024;
1567        h_accu2_1 = (accu_val->h1_m * chroma_hinc / accu_val->h1_n) % 1024;
1568        v_accu2_0 = (accu_val->v0_m * chroma_vinc / accu_val->v0_n) % 1024;
1569        v_accu2_1 = (accu_val->v1_m * chroma_vinc / accu_val->v1_n) % 1024;
1570
1571        dispc_ovl_set_vid_accu2_0(plane, h_accu2_0, v_accu2_0);
1572        dispc_ovl_set_vid_accu2_1(plane, h_accu2_1, v_accu2_1);
1573}
1574
1575static void dispc_ovl_set_scaling_common(enum omap_plane plane,
1576                u16 orig_width, u16 orig_height,
1577                u16 out_width, u16 out_height,
1578                bool ilace, bool five_taps,
1579                bool fieldmode, enum omap_color_mode color_mode,
1580                u8 rotation)
1581{
1582        int accu0 = 0;
1583        int accu1 = 0;
1584        u32 l;
1585
1586        dispc_ovl_set_scale_param(plane, orig_width, orig_height,
1587                                out_width, out_height, five_taps,
1588                                rotation, DISPC_COLOR_COMPONENT_RGB_Y);
1589        l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
1590
1591        /* RESIZEENABLE and VERTICALTAPS */
1592        l &= ~((0x3 << 5) | (0x1 << 21));
1593        l |= (orig_width != out_width) ? (1 << 5) : 0;
1594        l |= (orig_height != out_height) ? (1 << 6) : 0;
1595        l |= five_taps ? (1 << 21) : 0;
1596
1597        /* VRESIZECONF and HRESIZECONF */
1598        if (dss_has_feature(FEAT_RESIZECONF)) {
1599                l &= ~(0x3 << 7);
1600                l |= (orig_width <= out_width) ? 0 : (1 << 7);
1601                l |= (orig_height <= out_height) ? 0 : (1 << 8);
1602        }
1603
1604        /* LINEBUFFERSPLIT */
1605        if (dss_has_feature(FEAT_LINEBUFFERSPLIT)) {
1606                l &= ~(0x1 << 22);
1607                l |= five_taps ? (1 << 22) : 0;
1608        }
1609
1610        dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l);
1611
1612        /*
1613         * field 0 = even field = bottom field
1614         * field 1 = odd field = top field
1615         */
1616        if (ilace && !fieldmode) {
1617                accu1 = 0;
1618                accu0 = ((1024 * orig_height / out_height) / 2) & 0x3ff;
1619                if (accu0 >= 1024/2) {
1620                        accu1 = 1024/2;
1621                        accu0 -= accu1;
1622                }
1623        }
1624
1625        dispc_ovl_set_vid_accu0(plane, 0, accu0);
1626        dispc_ovl_set_vid_accu1(plane, 0, accu1);
1627}
1628
1629static void dispc_ovl_set_scaling_uv(enum omap_plane plane,
1630                u16 orig_width, u16 orig_height,
1631                u16 out_width, u16 out_height,
1632                bool ilace, bool five_taps,
1633                bool fieldmode, enum omap_color_mode color_mode,
1634                u8 rotation)
1635{
1636        int scale_x = out_width != orig_width;
1637        int scale_y = out_height != orig_height;
1638        bool chroma_upscale = plane != OMAP_DSS_WB ? true : false;
1639
1640        if (!dss_has_feature(FEAT_HANDLE_UV_SEPARATE))
1641                return;
1642        if ((color_mode != OMAP_DSS_COLOR_YUV2 &&
1643                        color_mode != OMAP_DSS_COLOR_UYVY &&
1644                        color_mode != OMAP_DSS_COLOR_NV12)) {
1645                /* reset chroma resampling for RGB formats  */
1646                if (plane != OMAP_DSS_WB)
1647                        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 8, 8);
1648                return;
1649        }
1650
1651        dispc_ovl_set_accu_uv(plane, orig_width, orig_height, out_width,
1652                        out_height, ilace, color_mode, rotation);
1653
1654        switch (color_mode) {
1655        case OMAP_DSS_COLOR_NV12:
1656                if (chroma_upscale) {
1657                        /* UV is subsampled by 2 horizontally and vertically */
1658                        orig_height >>= 1;
1659                        orig_width >>= 1;
1660                } else {
1661                        /* UV is downsampled by 2 horizontally and vertically */
1662                        orig_height <<= 1;
1663                        orig_width <<= 1;
1664                }
1665
1666                break;
1667        case OMAP_DSS_COLOR_YUV2:
1668        case OMAP_DSS_COLOR_UYVY:
1669                /* For YUV422 with 90/270 rotation, we don't upsample chroma */
1670                if (rotation == OMAP_DSS_ROT_0 ||
1671                                rotation == OMAP_DSS_ROT_180) {
1672                        if (chroma_upscale)
1673                                /* UV is subsampled by 2 horizontally */
1674                                orig_width >>= 1;
1675                        else
1676                                /* UV is downsampled by 2 horizontally */
1677                                orig_width <<= 1;
1678                }
1679
1680                /* must use FIR for YUV422 if rotated */
1681                if (rotation != OMAP_DSS_ROT_0)
1682                        scale_x = scale_y = true;
1683
1684                break;
1685        default:
1686                BUG();
1687                return;
1688        }
1689
1690        if (out_width != orig_width)
1691                scale_x = true;
1692        if (out_height != orig_height)
1693                scale_y = true;
1694
1695        dispc_ovl_set_scale_param(plane, orig_width, orig_height,
1696                        out_width, out_height, five_taps,
1697                                rotation, DISPC_COLOR_COMPONENT_UV);
1698
1699        if (plane != OMAP_DSS_WB)
1700                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane),
1701                        (scale_x || scale_y) ? 1 : 0, 8, 8);
1702
1703        /* set H scaling */
1704        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_x ? 1 : 0, 5, 5);
1705        /* set V scaling */
1706        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), scale_y ? 1 : 0, 6, 6);
1707}
1708
1709static void dispc_ovl_set_scaling(enum omap_plane plane,
1710                u16 orig_width, u16 orig_height,
1711                u16 out_width, u16 out_height,
1712                bool ilace, bool five_taps,
1713                bool fieldmode, enum omap_color_mode color_mode,
1714                u8 rotation)
1715{
1716        BUG_ON(plane == OMAP_DSS_GFX);
1717
1718        dispc_ovl_set_scaling_common(plane,
1719                        orig_width, orig_height,
1720                        out_width, out_height,
1721                        ilace, five_taps,
1722                        fieldmode, color_mode,
1723                        rotation);
1724
1725        dispc_ovl_set_scaling_uv(plane,
1726                orig_width, orig_height,
1727                out_width, out_height,
1728                ilace, five_taps,
1729                fieldmode, color_mode,
1730                rotation);
1731}
1732
1733static void dispc_ovl_set_rotation_attrs(enum omap_plane plane, u8 rotation,
1734                enum omap_dss_rotation_type rotation_type,
1735                bool mirroring, enum omap_color_mode color_mode)
1736{
1737        bool row_repeat = false;
1738        int vidrot = 0;
1739
1740        if (color_mode == OMAP_DSS_COLOR_YUV2 ||
1741                        color_mode == OMAP_DSS_COLOR_UYVY) {
1742
1743                if (mirroring) {
1744                        switch (rotation) {
1745                        case OMAP_DSS_ROT_0:
1746                                vidrot = 2;
1747                                break;
1748                        case OMAP_DSS_ROT_90:
1749                                vidrot = 1;
1750                                break;
1751                        case OMAP_DSS_ROT_180:
1752                                vidrot = 0;
1753                                break;
1754                        case OMAP_DSS_ROT_270:
1755                                vidrot = 3;
1756                                break;
1757                        }
1758                } else {
1759                        switch (rotation) {
1760                        case OMAP_DSS_ROT_0:
1761                                vidrot = 0;
1762                                break;
1763                        case OMAP_DSS_ROT_90:
1764                                vidrot = 1;
1765                                break;
1766                        case OMAP_DSS_ROT_180:
1767                                vidrot = 2;
1768                                break;
1769                        case OMAP_DSS_ROT_270:
1770                                vidrot = 3;
1771                                break;
1772                        }
1773                }
1774
1775                if (rotation == OMAP_DSS_ROT_90 || rotation == OMAP_DSS_ROT_270)
1776                        row_repeat = true;
1777                else
1778                        row_repeat = false;
1779        }
1780
1781        /*
1782         * OMAP4/5 Errata i631:
1783         * NV12 in 1D mode must use ROTATION=1. Otherwise DSS will fetch extra
1784         * rows beyond the framebuffer, which may cause OCP error.
1785         */
1786        if (color_mode == OMAP_DSS_COLOR_NV12 &&
1787                        rotation_type != OMAP_DSS_ROT_TILER)
1788                vidrot = 1;
1789
1790        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), vidrot, 13, 12);
1791        if (dss_has_feature(FEAT_ROWREPEATENABLE))
1792                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane),
1793                        row_repeat ? 1 : 0, 18, 18);
1794
1795        if (color_mode == OMAP_DSS_COLOR_NV12) {
1796                bool doublestride = (rotation_type == OMAP_DSS_ROT_TILER) &&
1797                                        (rotation == OMAP_DSS_ROT_0 ||
1798                                        rotation == OMAP_DSS_ROT_180);
1799                /* DOUBLESTRIDE */
1800                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), doublestride, 22, 22);
1801        }
1802
1803}
1804
1805static int color_mode_to_bpp(enum omap_color_mode color_mode)
1806{
1807        switch (color_mode) {
1808        case OMAP_DSS_COLOR_CLUT1:
1809                return 1;
1810        case OMAP_DSS_COLOR_CLUT2:
1811                return 2;
1812        case OMAP_DSS_COLOR_CLUT4:
1813                return 4;
1814        case OMAP_DSS_COLOR_CLUT8:
1815        case OMAP_DSS_COLOR_NV12:
1816                return 8;
1817        case OMAP_DSS_COLOR_RGB12U:
1818        case OMAP_DSS_COLOR_RGB16:
1819        case OMAP_DSS_COLOR_ARGB16:
1820        case OMAP_DSS_COLOR_YUV2:
1821        case OMAP_DSS_COLOR_UYVY:
1822        case OMAP_DSS_COLOR_RGBA16:
1823        case OMAP_DSS_COLOR_RGBX16:
1824        case OMAP_DSS_COLOR_ARGB16_1555:
1825        case OMAP_DSS_COLOR_XRGB16_1555:
1826                return 16;
1827        case OMAP_DSS_COLOR_RGB24P:
1828                return 24;
1829        case OMAP_DSS_COLOR_RGB24U:
1830        case OMAP_DSS_COLOR_ARGB32:
1831        case OMAP_DSS_COLOR_RGBA32:
1832        case OMAP_DSS_COLOR_RGBX32:
1833                return 32;
1834        default:
1835                BUG();
1836                return 0;
1837        }
1838}
1839
1840static s32 pixinc(int pixels, u8 ps)
1841{
1842        if (pixels == 1)
1843                return 1;
1844        else if (pixels > 1)
1845                return 1 + (pixels - 1) * ps;
1846        else if (pixels < 0)
1847                return 1 - (-pixels + 1) * ps;
1848        else
1849                BUG();
1850        return 0;
1851}
1852
1853static void calc_vrfb_rotation_offset(u8 rotation, bool mirror,
1854                u16 screen_width,
1855                u16 width, u16 height,
1856                enum omap_color_mode color_mode, bool fieldmode,
1857                unsigned int field_offset,
1858                unsigned *offset0, unsigned *offset1,
1859                s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim)
1860{
1861        u8 ps;
1862
1863        /* FIXME CLUT formats */
1864        switch (color_mode) {
1865        case OMAP_DSS_COLOR_CLUT1:
1866        case OMAP_DSS_COLOR_CLUT2:
1867        case OMAP_DSS_COLOR_CLUT4:
1868        case OMAP_DSS_COLOR_CLUT8:
1869                BUG();
1870                return;
1871        case OMAP_DSS_COLOR_YUV2:
1872        case OMAP_DSS_COLOR_UYVY:
1873                ps = 4;
1874                break;
1875        default:
1876                ps = color_mode_to_bpp(color_mode) / 8;
1877                break;
1878        }
1879
1880        DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
1881                        width, height);
1882
1883        /*
1884         * field 0 = even field = bottom field
1885         * field 1 = odd field = top field
1886         */
1887        switch (rotation + mirror * 4) {
1888        case OMAP_DSS_ROT_0:
1889        case OMAP_DSS_ROT_180:
1890                /*
1891                 * If the pixel format is YUV or UYVY divide the width
1892                 * of the image by 2 for 0 and 180 degree rotation.
1893                 */
1894                if (color_mode == OMAP_DSS_COLOR_YUV2 ||
1895                        color_mode == OMAP_DSS_COLOR_UYVY)
1896                        width = width >> 1;
1897                /* fall through */
1898        case OMAP_DSS_ROT_90:
1899        case OMAP_DSS_ROT_270:
1900                *offset1 = 0;
1901                if (field_offset)
1902                        *offset0 = field_offset * screen_width * ps;
1903                else
1904                        *offset0 = 0;
1905
1906                *row_inc = pixinc(1 +
1907                        (y_predecim * screen_width - x_predecim * width) +
1908                        (fieldmode ? screen_width : 0), ps);
1909                *pix_inc = pixinc(x_predecim, ps);
1910                break;
1911
1912        case OMAP_DSS_ROT_0 + 4:
1913        case OMAP_DSS_ROT_180 + 4:
1914                /* If the pixel format is YUV or UYVY divide the width
1915                 * of the image by 2  for 0 degree and 180 degree
1916                 */
1917                if (color_mode == OMAP_DSS_COLOR_YUV2 ||
1918                        color_mode == OMAP_DSS_COLOR_UYVY)
1919                        width = width >> 1;
1920                /* fall through */
1921        case OMAP_DSS_ROT_90 + 4:
1922        case OMAP_DSS_ROT_270 + 4:
1923                *offset1 = 0;
1924                if (field_offset)
1925                        *offset0 = field_offset * screen_width * ps;
1926                else
1927                        *offset0 = 0;
1928                *row_inc = pixinc(1 -
1929                        (y_predecim * screen_width + x_predecim * width) -
1930                        (fieldmode ? screen_width : 0), ps);
1931                *pix_inc = pixinc(x_predecim, ps);
1932                break;
1933
1934        default:
1935                BUG();
1936                return;
1937        }
1938}
1939
1940static void calc_dma_rotation_offset(u8 rotation, bool mirror,
1941                u16 screen_width,
1942                u16 width, u16 height,
1943                enum omap_color_mode color_mode, bool fieldmode,
1944                unsigned int field_offset,
1945                unsigned *offset0, unsigned *offset1,
1946                s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim)
1947{
1948        u8 ps;
1949        u16 fbw, fbh;
1950
1951        /* FIXME CLUT formats */
1952        switch (color_mode) {
1953        case OMAP_DSS_COLOR_CLUT1:
1954        case OMAP_DSS_COLOR_CLUT2:
1955        case OMAP_DSS_COLOR_CLUT4:
1956        case OMAP_DSS_COLOR_CLUT8:
1957                BUG();
1958                return;
1959        default:
1960                ps = color_mode_to_bpp(color_mode) / 8;
1961                break;
1962        }
1963
1964        DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width,
1965                        width, height);
1966
1967        /* width & height are overlay sizes, convert to fb sizes */
1968
1969        if (rotation == OMAP_DSS_ROT_0 || rotation == OMAP_DSS_ROT_180) {
1970                fbw = width;
1971                fbh = height;
1972        } else {
1973                fbw = height;
1974                fbh = width;
1975        }
1976
1977        /*
1978         * field 0 = even field = bottom field
1979         * field 1 = odd field = top field
1980         */
1981        switch (rotation + mirror * 4) {
1982        case OMAP_DSS_ROT_0:
1983                *offset1 = 0;
1984                if (field_offset)
1985                        *offset0 = *offset1 + field_offset * screen_width * ps;
1986                else
1987                        *offset0 = *offset1;
1988                *row_inc = pixinc(1 +
1989                        (y_predecim * screen_width - fbw * x_predecim) +
1990                        (fieldmode ? screen_width : 0), ps);
1991                if (color_mode == OMAP_DSS_COLOR_YUV2 ||
1992                        color_mode == OMAP_DSS_COLOR_UYVY)
1993                        *pix_inc = pixinc(x_predecim, 2 * ps);
1994                else
1995                        *pix_inc = pixinc(x_predecim, ps);
1996                break;
1997        case OMAP_DSS_ROT_90:
1998                *offset1 = screen_width * (fbh - 1) * ps;
1999                if (field_offset)
2000                        *offset0 = *offset1 + field_offset * ps;
2001                else
2002                        *offset0 = *offset1;
2003                *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) +
2004                                y_predecim + (fieldmode ? 1 : 0), ps);
2005                *pix_inc = pixinc(-x_predecim * screen_width, ps);
2006                break;
2007        case OMAP_DSS_ROT_180:
2008                *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
2009                if (field_offset)
2010                        *offset0 = *offset1 - field_offset * screen_width * ps;
2011                else
2012                        *offset0 = *offset1;
2013                *row_inc = pixinc(-1 -
2014                        (y_predecim * screen_width - fbw * x_predecim) -
2015                        (fieldmode ? screen_width : 0), ps);
2016                if (color_mode == OMAP_DSS_COLOR_YUV2 ||
2017                        color_mode == OMAP_DSS_COLOR_UYVY)
2018                        *pix_inc = pixinc(-x_predecim, 2 * ps);
2019                else
2020                        *pix_inc = pixinc(-x_predecim, ps);
2021                break;
2022        case OMAP_DSS_ROT_270:
2023                *offset1 = (fbw - 1) * ps;
2024                if (field_offset)
2025                        *offset0 = *offset1 - field_offset * ps;
2026                else
2027                        *offset0 = *offset1;
2028                *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) -
2029                                y_predecim - (fieldmode ? 1 : 0), ps);
2030                *pix_inc = pixinc(x_predecim * screen_width, ps);
2031                break;
2032
2033        /* mirroring */
2034        case OMAP_DSS_ROT_0 + 4:
2035                *offset1 = (fbw - 1) * ps;
2036                if (field_offset)
2037                        *offset0 = *offset1 + field_offset * screen_width * ps;
2038                else
2039                        *offset0 = *offset1;
2040                *row_inc = pixinc(y_predecim * screen_width * 2 - 1 +
2041                                (fieldmode ? screen_width : 0),
2042                                ps);
2043                if (color_mode == OMAP_DSS_COLOR_YUV2 ||
2044                        color_mode == OMAP_DSS_COLOR_UYVY)
2045                        *pix_inc = pixinc(-x_predecim, 2 * ps);
2046                else
2047                        *pix_inc = pixinc(-x_predecim, ps);
2048                break;
2049
2050        case OMAP_DSS_ROT_90 + 4:
2051                *offset1 = 0;
2052                if (field_offset)
2053                        *offset0 = *offset1 + field_offset * ps;
2054                else
2055                        *offset0 = *offset1;
2056                *row_inc = pixinc(-screen_width * (fbh * x_predecim - 1) +
2057                                y_predecim + (fieldmode ? 1 : 0),
2058                                ps);
2059                *pix_inc = pixinc(x_predecim * screen_width, ps);
2060                break;
2061
2062        case OMAP_DSS_ROT_180 + 4:
2063                *offset1 = screen_width * (fbh - 1) * ps;
2064                if (field_offset)
2065                        *offset0 = *offset1 - field_offset * screen_width * ps;
2066                else
2067                        *offset0 = *offset1;
2068                *row_inc = pixinc(1 - y_predecim * screen_width * 2 -
2069                                (fieldmode ? screen_width : 0),
2070                                ps);
2071                if (color_mode == OMAP_DSS_COLOR_YUV2 ||
2072                        color_mode == OMAP_DSS_COLOR_UYVY)
2073                        *pix_inc = pixinc(x_predecim, 2 * ps);
2074                else
2075                        *pix_inc = pixinc(x_predecim, ps);
2076                break;
2077
2078        case OMAP_DSS_ROT_270 + 4:
2079                *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps;
2080                if (field_offset)
2081                        *offset0 = *offset1 - field_offset * ps;
2082                else
2083                        *offset0 = *offset1;
2084                *row_inc = pixinc(screen_width * (fbh * x_predecim - 1) -
2085                                y_predecim - (fieldmode ? 1 : 0),
2086                                ps);
2087                *pix_inc = pixinc(-x_predecim * screen_width, ps);
2088                break;
2089
2090        default:
2091                BUG();
2092                return;
2093        }
2094}
2095
2096static void calc_tiler_rotation_offset(u16 screen_width, u16 width,
2097                enum omap_color_mode color_mode, bool fieldmode,
2098                unsigned int field_offset, unsigned *offset0, unsigned *offset1,
2099                s32 *row_inc, s32 *pix_inc, int x_predecim, int y_predecim)
2100{
2101        u8 ps;
2102
2103        switch (color_mode) {
2104        case OMAP_DSS_COLOR_CLUT1:
2105        case OMAP_DSS_COLOR_CLUT2:
2106        case OMAP_DSS_COLOR_CLUT4:
2107        case OMAP_DSS_COLOR_CLUT8:
2108                BUG();
2109                return;
2110        default:
2111                ps = color_mode_to_bpp(color_mode) / 8;
2112                break;
2113        }
2114
2115        DSSDBG("scrw %d, width %d\n", screen_width, width);
2116
2117        /*
2118         * field 0 = even field = bottom field
2119         * field 1 = odd field = top field
2120         */
2121        *offset1 = 0;
2122        if (field_offset)
2123                *offset0 = *offset1 + field_offset * screen_width * ps;
2124        else
2125                *offset0 = *offset1;
2126        *row_inc = pixinc(1 + (y_predecim * screen_width - width * x_predecim) +
2127                        (fieldmode ? screen_width : 0), ps);
2128        if (color_mode == OMAP_DSS_COLOR_YUV2 ||
2129                color_mode == OMAP_DSS_COLOR_UYVY)
2130                *pix_inc = pixinc(x_predecim, 2 * ps);
2131        else
2132                *pix_inc = pixinc(x_predecim, ps);
2133}
2134
2135/*
2136 * This function is used to avoid synclosts in OMAP3, because of some
2137 * undocumented horizontal position and timing related limitations.
2138 */
2139static int check_horiz_timing_omap3(unsigned long pclk, unsigned long lclk,
2140                const struct omap_video_timings *t, u16 pos_x,
2141                u16 width, u16 height, u16 out_width, u16 out_height,
2142                bool five_taps)
2143{
2144        const int ds = DIV_ROUND_UP(height, out_height);
2145        unsigned long nonactive;
2146        static const u8 limits[3] = { 8, 10, 20 };
2147        u64 val, blank;
2148        int i;
2149
2150        nonactive = t->x_res + t->hfp + t->hsw + t->hbp - out_width;
2151
2152        i = 0;
2153        if (out_height < height)
2154                i++;
2155        if (out_width < width)
2156                i++;
2157        blank = div_u64((u64)(t->hbp + t->hsw + t->hfp) * lclk, pclk);
2158        DSSDBG("blanking period + ppl = %llu (limit = %u)\n", blank, limits[i]);
2159        if (blank <= limits[i])
2160                return -EINVAL;
2161
2162        /* FIXME add checks for 3-tap filter once the limitations are known */
2163        if (!five_taps)
2164                return 0;
2165
2166        /*
2167         * Pixel data should be prepared before visible display point starts.
2168         * So, atleast DS-2 lines must have already been fetched by DISPC
2169         * during nonactive - pos_x period.
2170         */
2171        val = div_u64((u64)(nonactive - pos_x) * lclk, pclk);
2172        DSSDBG("(nonactive - pos_x) * pcd = %llu max(0, DS - 2) * width = %d\n",
2173                val, max(0, ds - 2) * width);
2174        if (val < max(0, ds - 2) * width)
2175                return -EINVAL;
2176
2177        /*
2178         * All lines need to be refilled during the nonactive period of which
2179         * only one line can be loaded during the active period. So, atleast
2180         * DS - 1 lines should be loaded during nonactive period.
2181         */
2182        val =  div_u64((u64)nonactive * lclk, pclk);
2183        DSSDBG("nonactive * pcd  = %llu, max(0, DS - 1) * width = %d\n",
2184                val, max(0, ds - 1) * width);
2185        if (val < max(0, ds - 1) * width)
2186                return -EINVAL;
2187
2188        return 0;
2189}
2190
2191static unsigned long calc_core_clk_five_taps(unsigned long pclk,
2192                const struct omap_video_timings *mgr_timings, u16 width,
2193                u16 height, u16 out_width, u16 out_height,
2194                enum omap_color_mode color_mode)
2195{
2196        u32 core_clk = 0;
2197        u64 tmp;
2198
2199        if (height <= out_height && width <= out_width)
2200                return (unsigned long) pclk;
2201
2202        if (height > out_height) {
2203                unsigned int ppl = mgr_timings->x_res;
2204
2205                tmp = (u64)pclk * height * out_width;
2206                do_div(tmp, 2 * out_height * ppl);
2207                core_clk = tmp;
2208
2209                if (height > 2 * out_height) {
2210                        if (ppl == out_width)
2211                                return 0;
2212
2213                        tmp = (u64)pclk * (height - 2 * out_height) * out_width;
2214                        do_div(tmp, 2 * out_height * (ppl - out_width));
2215                        core_clk = max_t(u32, core_clk, tmp);
2216                }
2217        }
2218
2219        if (width > out_width) {
2220                tmp = (u64)pclk * width;
2221                do_div(tmp, out_width);
2222                core_clk = max_t(u32, core_clk, tmp);
2223
2224                if (color_mode == OMAP_DSS_COLOR_RGB24U)
2225                        core_clk <<= 1;
2226        }
2227
2228        return core_clk;
2229}
2230
2231static unsigned long calc_core_clk_24xx(unsigned long pclk, u16 width,
2232                u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
2233{
2234        if (height > out_height && width > out_width)
2235                return pclk * 4;
2236        else
2237                return pclk * 2;
2238}
2239
2240static unsigned long calc_core_clk_34xx(unsigned long pclk, u16 width,
2241                u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
2242{
2243        unsigned int hf, vf;
2244
2245        /*
2246         * FIXME how to determine the 'A' factor
2247         * for the no downscaling case ?
2248         */
2249
2250        if (width > 3 * out_width)
2251                hf = 4;
2252        else if (width > 2 * out_width)
2253                hf = 3;
2254        else if (width > out_width)
2255                hf = 2;
2256        else
2257                hf = 1;
2258        if (height > out_height)
2259                vf = 2;
2260        else
2261                vf = 1;
2262
2263        return pclk * vf * hf;
2264}
2265
2266static unsigned long calc_core_clk_44xx(unsigned long pclk, u16 width,
2267                u16 height, u16 out_width, u16 out_height, bool mem_to_mem)
2268{
2269        /*
2270         * If the overlay/writeback is in mem to mem mode, there are no
2271         * downscaling limitations with respect to pixel clock, return 1 as
2272         * required core clock to represent that we have sufficient enough
2273         * core clock to do maximum downscaling
2274         */
2275        if (mem_to_mem)
2276                return 1;
2277
2278        if (width > out_width)
2279                return DIV_ROUND_UP(pclk, out_width) * width;
2280        else
2281                return pclk;
2282}
2283
2284static int dispc_ovl_calc_scaling_24xx(unsigned long pclk, unsigned long lclk,
2285                const struct omap_video_timings *mgr_timings,
2286                u16 width, u16 height, u16 out_width, u16 out_height,
2287                enum omap_color_mode color_mode, bool *five_taps,
2288                int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
2289                u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
2290{
2291        int error;
2292        u16 in_width, in_height;
2293        int min_factor = min(*decim_x, *decim_y);
2294        const int maxsinglelinewidth =
2295                        dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
2296
2297        *five_taps = false;
2298
2299        do {
2300                in_height = height / *decim_y;
2301                in_width = width / *decim_x;
2302                *core_clk = dispc.feat->calc_core_clk(pclk, in_width,
2303                                in_height, out_width, out_height, mem_to_mem);
2304                error = (in_width > maxsinglelinewidth || !*core_clk ||
2305                        *core_clk > dispc_core_clk_rate());
2306                if (error) {
2307                        if (*decim_x == *decim_y) {
2308                                *decim_x = min_factor;
2309                                ++*decim_y;
2310                        } else {
2311                                swap(*decim_x, *decim_y);
2312                                if (*decim_x < *decim_y)
2313                                        ++*decim_x;
2314                        }
2315                }
2316        } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
2317
2318        if (error) {
2319                DSSERR("failed to find scaling settings\n");
2320                return -EINVAL;
2321        }
2322
2323        if (in_width > maxsinglelinewidth) {
2324                DSSERR("Cannot scale max input width exceeded");
2325                return -EINVAL;
2326        }
2327        return 0;
2328}
2329
2330static int dispc_ovl_calc_scaling_34xx(unsigned long pclk, unsigned long lclk,
2331                const struct omap_video_timings *mgr_timings,
2332                u16 width, u16 height, u16 out_width, u16 out_height,
2333                enum omap_color_mode color_mode, bool *five_taps,
2334                int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
2335                u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
2336{
2337        int error;
2338        u16 in_width, in_height;
2339        const int maxsinglelinewidth =
2340                        dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
2341
2342        do {
2343                in_height = height / *decim_y;
2344                in_width = width / *decim_x;
2345                *five_taps = in_height > out_height;
2346
2347                if (in_width > maxsinglelinewidth)
2348                        if (in_height > out_height &&
2349                                                in_height < out_height * 2)
2350                                *five_taps = false;
2351again:
2352                if (*five_taps)
2353                        *core_clk = calc_core_clk_five_taps(pclk, mgr_timings,
2354                                                in_width, in_height, out_width,
2355                                                out_height, color_mode);
2356                else
2357                        *core_clk = dispc.feat->calc_core_clk(pclk, in_width,
2358                                        in_height, out_width, out_height,
2359                                        mem_to_mem);
2360
2361                error = check_horiz_timing_omap3(pclk, lclk, mgr_timings,
2362                                pos_x, in_width, in_height, out_width,
2363                                out_height, *five_taps);
2364                if (error && *five_taps) {
2365                        *five_taps = false;
2366                        goto again;
2367                }
2368
2369                error = (error || in_width > maxsinglelinewidth * 2 ||
2370                        (in_width > maxsinglelinewidth && *five_taps) ||
2371                        !*core_clk || *core_clk > dispc_core_clk_rate());
2372
2373                if (!error) {
2374                        /* verify that we're inside the limits of scaler */
2375                        if (in_width / 4 > out_width)
2376                                        error = 1;
2377
2378                        if (*five_taps) {
2379                                if (in_height / 4 > out_height)
2380                                        error = 1;
2381                        } else {
2382                                if (in_height / 2 > out_height)
2383                                        error = 1;
2384                        }
2385                }
2386
2387                if (error)
2388                        ++*decim_y;
2389        } while (*decim_x <= *x_predecim && *decim_y <= *y_predecim && error);
2390
2391        if (error) {
2392                DSSERR("failed to find scaling settings\n");
2393                return -EINVAL;
2394        }
2395
2396        if (check_horiz_timing_omap3(pclk, lclk, mgr_timings, pos_x, in_width,
2397                                in_height, out_width, out_height, *five_taps)) {
2398                        DSSERR("horizontal timing too tight\n");
2399                        return -EINVAL;
2400        }
2401
2402        if (in_width > (maxsinglelinewidth * 2)) {
2403                DSSERR("Cannot setup scaling");
2404                DSSERR("width exceeds maximum width possible");
2405                return -EINVAL;
2406        }
2407
2408        if (in_width > maxsinglelinewidth && *five_taps) {
2409                DSSERR("cannot setup scaling with five taps");
2410                return -EINVAL;
2411        }
2412        return 0;
2413}
2414
2415static int dispc_ovl_calc_scaling_44xx(unsigned long pclk, unsigned long lclk,
2416                const struct omap_video_timings *mgr_timings,
2417                u16 width, u16 height, u16 out_width, u16 out_height,
2418                enum omap_color_mode color_mode, bool *five_taps,
2419                int *x_predecim, int *y_predecim, int *decim_x, int *decim_y,
2420                u16 pos_x, unsigned long *core_clk, bool mem_to_mem)
2421{
2422        u16 in_width, in_width_max;
2423        int decim_x_min = *decim_x;
2424        u16 in_height = height / *decim_y;
2425        const int maxsinglelinewidth =
2426                                dss_feat_get_param_max(FEAT_PARAM_LINEWIDTH);
2427        const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
2428
2429        if (mem_to_mem) {
2430                in_width_max = out_width * maxdownscale;
2431        } else {
2432                in_width_max = dispc_core_clk_rate() /
2433                                        DIV_ROUND_UP(pclk, out_width);
2434        }
2435
2436        *decim_x = DIV_ROUND_UP(width, in_width_max);
2437
2438        *decim_x = *decim_x > decim_x_min ? *decim_x : decim_x_min;
2439        if (*decim_x > *x_predecim)
2440                return -EINVAL;
2441
2442        do {
2443                in_width = width / *decim_x;
2444        } while (*decim_x <= *x_predecim &&
2445                        in_width > maxsinglelinewidth && ++*decim_x);
2446
2447        if (in_width > maxsinglelinewidth) {
2448                DSSERR("Cannot scale width exceeds max line width");
2449                return -EINVAL;
2450        }
2451
2452        *core_clk = dispc.feat->calc_core_clk(pclk, in_width, in_height,
2453                                out_width, out_height, mem_to_mem);
2454        return 0;
2455}
2456
2457#define DIV_FRAC(dividend, divisor) \
2458        ((dividend) * 100 / (divisor) - ((dividend) / (divisor) * 100))
2459
2460static int dispc_ovl_calc_scaling(unsigned long pclk, unsigned long lclk,
2461                enum omap_overlay_caps caps,
2462                const struct omap_video_timings *mgr_timings,
2463                u16 width, u16 height, u16 out_width, u16 out_height,
2464                enum omap_color_mode color_mode, bool *five_taps,
2465                int *x_predecim, int *y_predecim, u16 pos_x,
2466                enum omap_dss_rotation_type rotation_type, bool mem_to_mem)
2467{
2468        const int maxdownscale = dss_feat_get_param_max(FEAT_PARAM_DOWNSCALE);
2469        const int max_decim_limit = 16;
2470        unsigned long core_clk = 0;
2471        int decim_x, decim_y, ret;
2472
2473        if (width == out_width && height == out_height)
2474                return 0;
2475
2476        if (!mem_to_mem && (pclk == 0 || mgr_timings->pixelclock == 0)) {
2477                DSSERR("cannot calculate scaling settings: pclk is zero\n");
2478                return -EINVAL;
2479        }
2480
2481        if ((caps & OMAP_DSS_OVL_CAP_SCALE) == 0)
2482                return -EINVAL;
2483
2484        if (mem_to_mem) {
2485                *x_predecim = *y_predecim = 1;
2486        } else {
2487                *x_predecim = max_decim_limit;
2488                *y_predecim = (rotation_type == OMAP_DSS_ROT_TILER &&
2489                                dss_has_feature(FEAT_BURST_2D)) ?
2490                                2 : max_decim_limit;
2491        }
2492
2493        if (color_mode == OMAP_DSS_COLOR_CLUT1 ||
2494            color_mode == OMAP_DSS_COLOR_CLUT2 ||
2495            color_mode == OMAP_DSS_COLOR_CLUT4 ||
2496            color_mode == OMAP_DSS_COLOR_CLUT8) {
2497                *x_predecim = 1;
2498                *y_predecim = 1;
2499                *five_taps = false;
2500                return 0;
2501        }
2502
2503        decim_x = DIV_ROUND_UP(DIV_ROUND_UP(width, out_width), maxdownscale);
2504        decim_y = DIV_ROUND_UP(DIV_ROUND_UP(height, out_height), maxdownscale);
2505
2506        if (decim_x > *x_predecim || out_width > width * 8)
2507                return -EINVAL;
2508
2509        if (decim_y > *y_predecim || out_height > height * 8)
2510                return -EINVAL;
2511
2512        ret = dispc.feat->calc_scaling(pclk, lclk, mgr_timings, width, height,
2513                out_width, out_height, color_mode, five_taps,
2514                x_predecim, y_predecim, &decim_x, &decim_y, pos_x, &core_clk,
2515                mem_to_mem);
2516        if (ret)
2517                return ret;
2518
2519        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",
2520                width, height,
2521                out_width, out_height,
2522                out_width / width, DIV_FRAC(out_width, width),
2523                out_height / height, DIV_FRAC(out_height, height),
2524
2525                decim_x, decim_y,
2526                width / decim_x, height / decim_y,
2527                out_width / (width / decim_x), DIV_FRAC(out_width, width / decim_x),
2528                out_height / (height / decim_y), DIV_FRAC(out_height, height / decim_y),
2529
2530                *five_taps ? 5 : 3,
2531                core_clk, dispc_core_clk_rate());
2532
2533        if (!core_clk || core_clk > dispc_core_clk_rate()) {
2534                DSSERR("failed to set up scaling, "
2535                        "required core clk rate = %lu Hz, "
2536                        "current core clk rate = %lu Hz\n",
2537                        core_clk, dispc_core_clk_rate());
2538                return -EINVAL;
2539        }
2540
2541        *x_predecim = decim_x;
2542        *y_predecim = decim_y;
2543        return 0;
2544}
2545
2546int dispc_ovl_check(enum omap_plane plane, enum omap_channel channel,
2547                const struct omap_overlay_info *oi,
2548                const struct omap_video_timings *timings,
2549                int *x_predecim, int *y_predecim)
2550{
2551        enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
2552        bool five_taps = true;
2553        bool fieldmode = false;
2554        u16 in_height = oi->height;
2555        u16 in_width = oi->width;
2556        bool ilace = timings->interlace;
2557        u16 out_width, out_height;
2558        int pos_x = oi->pos_x;
2559        unsigned long pclk = dispc_mgr_pclk_rate(channel);
2560        unsigned long lclk = dispc_mgr_lclk_rate(channel);
2561
2562        out_width = oi->out_width == 0 ? oi->width : oi->out_width;
2563        out_height = oi->out_height == 0 ? oi->height : oi->out_height;
2564
2565        if (ilace && oi->height == out_height)
2566                fieldmode = true;
2567
2568        if (ilace) {
2569                if (fieldmode)
2570                        in_height /= 2;
2571                out_height /= 2;
2572
2573                DSSDBG("adjusting for ilace: height %d, out_height %d\n",
2574                                in_height, out_height);
2575        }
2576
2577        if (!dss_feat_color_mode_supported(plane, oi->color_mode))
2578                return -EINVAL;
2579
2580        return dispc_ovl_calc_scaling(pclk, lclk, caps, timings, in_width,
2581                        in_height, out_width, out_height, oi->color_mode,
2582                        &five_taps, x_predecim, y_predecim, pos_x,
2583                        oi->rotation_type, false);
2584}
2585EXPORT_SYMBOL(dispc_ovl_check);
2586
2587static int dispc_ovl_setup_common(enum omap_plane plane,
2588                enum omap_overlay_caps caps, u32 paddr, u32 p_uv_addr,
2589                u16 screen_width, int pos_x, int pos_y, u16 width, u16 height,
2590                u16 out_width, u16 out_height, enum omap_color_mode color_mode,
2591                u8 rotation, bool mirror, u8 zorder, u8 pre_mult_alpha,
2592                u8 global_alpha, enum omap_dss_rotation_type rotation_type,
2593                bool replication, const struct omap_video_timings *mgr_timings,
2594                bool mem_to_mem)
2595{
2596        bool five_taps = true;
2597        bool fieldmode = false;
2598        int r, cconv = 0;
2599        unsigned offset0, offset1;
2600        s32 row_inc;
2601        s32 pix_inc;
2602        u16 frame_width, frame_height;
2603        unsigned int field_offset = 0;
2604        u16 in_height = height;
2605        u16 in_width = width;
2606        int x_predecim = 1, y_predecim = 1;
2607        bool ilace = mgr_timings->interlace;
2608        unsigned long pclk = dispc_plane_pclk_rate(plane);
2609        unsigned long lclk = dispc_plane_lclk_rate(plane);
2610
2611        if (paddr == 0 && rotation_type != OMAP_DSS_ROT_TILER)
2612                return -EINVAL;
2613
2614        switch (color_mode) {
2615        case OMAP_DSS_COLOR_YUV2:
2616        case OMAP_DSS_COLOR_UYVY:
2617        case OMAP_DSS_COLOR_NV12:
2618                if (in_width & 1) {
2619                        DSSERR("input width %d is not even for YUV format\n",
2620                                in_width);
2621                        return -EINVAL;
2622                }
2623                break;
2624
2625        default:
2626                break;
2627        }
2628
2629        out_width = out_width == 0 ? width : out_width;
2630        out_height = out_height == 0 ? height : out_height;
2631
2632        if (ilace && height == out_height)
2633                fieldmode = true;
2634
2635        if (ilace) {
2636                if (fieldmode)
2637                        in_height /= 2;
2638                pos_y /= 2;
2639                out_height /= 2;
2640
2641                DSSDBG("adjusting for ilace: height %d, pos_y %d, "
2642                        "out_height %d\n", in_height, pos_y,
2643                        out_height);
2644        }
2645
2646        if (!dss_feat_color_mode_supported(plane, color_mode))
2647                return -EINVAL;
2648
2649        r = dispc_ovl_calc_scaling(pclk, lclk, caps, mgr_timings, in_width,
2650                        in_height, out_width, out_height, color_mode,
2651                        &five_taps, &x_predecim, &y_predecim, pos_x,
2652                        rotation_type, mem_to_mem);
2653        if (r)
2654                return r;
2655
2656        in_width = in_width / x_predecim;
2657        in_height = in_height / y_predecim;
2658
2659        if (x_predecim > 1 || y_predecim > 1)
2660                DSSDBG("predecimation %d x %x, new input size %d x %d\n",
2661                        x_predecim, y_predecim, in_width, in_height);
2662
2663        switch (color_mode) {
2664        case OMAP_DSS_COLOR_YUV2:
2665        case OMAP_DSS_COLOR_UYVY:
2666        case OMAP_DSS_COLOR_NV12:
2667                if (in_width & 1) {
2668                        DSSDBG("predecimated input width is not even for YUV format\n");
2669                        DSSDBG("adjusting input width %d -> %d\n",
2670                                in_width, in_width & ~1);
2671
2672                        in_width &= ~1;
2673                }
2674                break;
2675
2676        default:
2677                break;
2678        }
2679
2680        if (color_mode == OMAP_DSS_COLOR_YUV2 ||
2681                        color_mode == OMAP_DSS_COLOR_UYVY ||
2682                        color_mode == OMAP_DSS_COLOR_NV12)
2683                cconv = 1;
2684
2685        if (ilace && !fieldmode) {
2686                /*
2687                 * when downscaling the bottom field may have to start several
2688                 * source lines below the top field. Unfortunately ACCUI
2689                 * registers will only hold the fractional part of the offset
2690                 * so the integer part must be added to the base address of the
2691                 * bottom field.
2692                 */
2693                if (!in_height || in_height == out_height)
2694                        field_offset = 0;
2695                else
2696                        field_offset = in_height / out_height / 2;
2697        }
2698
2699        /* Fields are independent but interleaved in memory. */
2700        if (fieldmode)
2701                field_offset = 1;
2702
2703        offset0 = 0;
2704        offset1 = 0;
2705        row_inc = 0;
2706        pix_inc = 0;
2707
2708        if (plane == OMAP_DSS_WB) {
2709                frame_width = out_width;
2710                frame_height = out_height;
2711        } else {
2712                frame_width = in_width;
2713                frame_height = height;
2714        }
2715
2716        if (rotation_type == OMAP_DSS_ROT_TILER)
2717                calc_tiler_rotation_offset(screen_width, frame_width,
2718                                color_mode, fieldmode, field_offset,
2719                                &offset0, &offset1, &row_inc, &pix_inc,
2720                                x_predecim, y_predecim);
2721        else if (rotation_type == OMAP_DSS_ROT_DMA)
2722                calc_dma_rotation_offset(rotation, mirror, screen_width,
2723                                frame_width, frame_height,
2724                                color_mode, fieldmode, field_offset,
2725                                &offset0, &offset1, &row_inc, &pix_inc,
2726                                x_predecim, y_predecim);
2727        else
2728                calc_vrfb_rotation_offset(rotation, mirror,
2729                                screen_width, frame_width, frame_height,
2730                                color_mode, fieldmode, field_offset,
2731                                &offset0, &offset1, &row_inc, &pix_inc,
2732                                x_predecim, y_predecim);
2733
2734        DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n",
2735                        offset0, offset1, row_inc, pix_inc);
2736
2737        dispc_ovl_set_color_mode(plane, color_mode);
2738
2739        dispc_ovl_configure_burst_type(plane, rotation_type);
2740
2741        dispc_ovl_set_ba0(plane, paddr + offset0);
2742        dispc_ovl_set_ba1(plane, paddr + offset1);
2743
2744        if (OMAP_DSS_COLOR_NV12 == color_mode) {
2745                dispc_ovl_set_ba0_uv(plane, p_uv_addr + offset0);
2746                dispc_ovl_set_ba1_uv(plane, p_uv_addr + offset1);
2747        }
2748
2749        if (dispc.feat->last_pixel_inc_missing)
2750                row_inc += pix_inc - 1;
2751
2752        dispc_ovl_set_row_inc(plane, row_inc);
2753        dispc_ovl_set_pix_inc(plane, pix_inc);
2754
2755        DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, in_width,
2756                        in_height, out_width, out_height);
2757
2758        dispc_ovl_set_pos(plane, caps, pos_x, pos_y);
2759
2760        dispc_ovl_set_input_size(plane, in_width, in_height);
2761
2762        if (caps & OMAP_DSS_OVL_CAP_SCALE) {
2763                dispc_ovl_set_scaling(plane, in_width, in_height, out_width,
2764                                   out_height, ilace, five_taps, fieldmode,
2765                                   color_mode, rotation);
2766                dispc_ovl_set_output_size(plane, out_width, out_height);
2767                dispc_ovl_set_vid_color_conv(plane, cconv);
2768        }
2769
2770        dispc_ovl_set_rotation_attrs(plane, rotation, rotation_type, mirror,
2771                        color_mode);
2772
2773        dispc_ovl_set_zorder(plane, caps, zorder);
2774        dispc_ovl_set_pre_mult_alpha(plane, caps, pre_mult_alpha);
2775        dispc_ovl_setup_global_alpha(plane, caps, global_alpha);
2776
2777        dispc_ovl_enable_replication(plane, caps, replication);
2778
2779        return 0;
2780}
2781
2782int dispc_ovl_setup(enum omap_plane plane, const struct omap_overlay_info *oi,
2783                bool replication, const struct omap_video_timings *mgr_timings,
2784                bool mem_to_mem)
2785{
2786        int r;
2787        enum omap_overlay_caps caps = dss_feat_get_overlay_caps(plane);
2788        enum omap_channel channel;
2789
2790        channel = dispc_ovl_get_channel_out(plane);
2791
2792        DSSDBG("dispc_ovl_setup %d, pa %pad, pa_uv %pad, sw %d, %d,%d, %dx%d ->"
2793                " %dx%d, cmode %x, rot %d, mir %d, chan %d repl %d\n",
2794                plane, &oi->paddr, &oi->p_uv_addr, oi->screen_width, oi->pos_x,
2795                oi->pos_y, oi->width, oi->height, oi->out_width, oi->out_height,
2796                oi->color_mode, oi->rotation, oi->mirror, channel, replication);
2797
2798        r = dispc_ovl_setup_common(plane, caps, oi->paddr, oi->p_uv_addr,
2799                oi->screen_width, oi->pos_x, oi->pos_y, oi->width, oi->height,
2800                oi->out_width, oi->out_height, oi->color_mode, oi->rotation,
2801                oi->mirror, oi->zorder, oi->pre_mult_alpha, oi->global_alpha,
2802                oi->rotation_type, replication, mgr_timings, mem_to_mem);
2803
2804        return r;
2805}
2806EXPORT_SYMBOL(dispc_ovl_setup);
2807
2808int dispc_wb_setup(const struct omap_dss_writeback_info *wi,
2809                bool mem_to_mem, const struct omap_video_timings *mgr_timings)
2810{
2811        int r;
2812        u32 l;
2813        enum omap_plane plane = OMAP_DSS_WB;
2814        const int pos_x = 0, pos_y = 0;
2815        const u8 zorder = 0, global_alpha = 0;
2816        const bool replication = false;
2817        bool truncation;
2818        int in_width = mgr_timings->x_res;
2819        int in_height = mgr_timings->y_res;
2820        enum omap_overlay_caps caps =
2821                OMAP_DSS_OVL_CAP_SCALE | OMAP_DSS_OVL_CAP_PRE_MULT_ALPHA;
2822
2823        DSSDBG("dispc_wb_setup, pa %x, pa_uv %x, %d,%d -> %dx%d, cmode %x, "
2824                "rot %d, mir %d\n", wi->paddr, wi->p_uv_addr, in_width,
2825                in_height, wi->width, wi->height, wi->color_mode, wi->rotation,
2826                wi->mirror);
2827
2828        r = dispc_ovl_setup_common(plane, caps, wi->paddr, wi->p_uv_addr,
2829                wi->buf_width, pos_x, pos_y, in_width, in_height, wi->width,
2830                wi->height, wi->color_mode, wi->rotation, wi->mirror, zorder,
2831                wi->pre_mult_alpha, global_alpha, wi->rotation_type,
2832                replication, mgr_timings, mem_to_mem);
2833
2834        switch (wi->color_mode) {
2835        case OMAP_DSS_COLOR_RGB16:
2836        case OMAP_DSS_COLOR_RGB24P:
2837        case OMAP_DSS_COLOR_ARGB16:
2838        case OMAP_DSS_COLOR_RGBA16:
2839        case OMAP_DSS_COLOR_RGB12U:
2840        case OMAP_DSS_COLOR_ARGB16_1555:
2841        case OMAP_DSS_COLOR_XRGB16_1555:
2842        case OMAP_DSS_COLOR_RGBX16:
2843                truncation = true;
2844                break;
2845        default:
2846                truncation = false;
2847                break;
2848        }
2849
2850        /* setup extra DISPC_WB_ATTRIBUTES */
2851        l = dispc_read_reg(DISPC_OVL_ATTRIBUTES(plane));
2852        l = FLD_MOD(l, truncation, 10, 10);     /* TRUNCATIONENABLE */
2853        l = FLD_MOD(l, mem_to_mem, 19, 19);     /* WRITEBACKMODE */
2854        if (mem_to_mem)
2855                l = FLD_MOD(l, 1, 26, 24);      /* CAPTUREMODE */
2856        else
2857                l = FLD_MOD(l, 0, 26, 24);      /* CAPTUREMODE */
2858        dispc_write_reg(DISPC_OVL_ATTRIBUTES(plane), l);
2859
2860        if (mem_to_mem) {
2861                /* WBDELAYCOUNT */
2862                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), 0, 7, 0);
2863        } else {
2864                int wbdelay;
2865
2866                wbdelay = min(mgr_timings->vfp + mgr_timings->vsw +
2867                        mgr_timings->vbp, 255);
2868
2869                /* WBDELAYCOUNT */
2870                REG_FLD_MOD(DISPC_OVL_ATTRIBUTES2(plane), wbdelay, 7, 0);
2871        }
2872
2873        return r;
2874}
2875
2876int dispc_ovl_enable(enum omap_plane plane, bool enable)
2877{
2878        DSSDBG("dispc_enable_plane %d, %d\n", plane, enable);
2879
2880        REG_FLD_MOD(DISPC_OVL_ATTRIBUTES(plane), enable ? 1 : 0, 0, 0);
2881
2882        return 0;
2883}
2884EXPORT_SYMBOL(dispc_ovl_enable);
2885
2886bool dispc_ovl_enabled(enum omap_plane plane)
2887{
2888        return REG_GET(DISPC_OVL_ATTRIBUTES(plane), 0, 0);
2889}
2890EXPORT_SYMBOL(dispc_ovl_enabled);
2891
2892void dispc_mgr_enable(enum omap_channel channel, bool enable)
2893{
2894        mgr_fld_write(channel, DISPC_MGR_FLD_ENABLE, enable);
2895        /* flush posted write */
2896        mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
2897}
2898EXPORT_SYMBOL(dispc_mgr_enable);
2899
2900bool dispc_mgr_is_enabled(enum omap_channel channel)
2901{
2902        return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE);
2903}
2904EXPORT_SYMBOL(dispc_mgr_is_enabled);
2905
2906void dispc_wb_enable(bool enable)
2907{
2908        dispc_ovl_enable(OMAP_DSS_WB, enable);
2909}
2910
2911bool dispc_wb_is_enabled(void)
2912{
2913        return dispc_ovl_enabled(OMAP_DSS_WB);
2914}
2915
2916static void dispc_lcd_enable_signal_polarity(bool act_high)
2917{
2918        if (!dss_has_feature(FEAT_LCDENABLEPOL))
2919                return;
2920
2921        REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29);
2922}
2923
2924void dispc_lcd_enable_signal(bool enable)
2925{
2926        if (!dss_has_feature(FEAT_LCDENABLESIGNAL))
2927                return;
2928
2929        REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28);
2930}
2931
2932void dispc_pck_free_enable(bool enable)
2933{
2934        if (!dss_has_feature(FEAT_PCKFREEENABLE))
2935                return;
2936
2937        REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27);
2938}
2939
2940static void dispc_mgr_enable_fifohandcheck(enum omap_channel channel, bool enable)
2941{
2942        mgr_fld_write(channel, DISPC_MGR_FLD_FIFOHANDCHECK, enable);
2943}
2944
2945
2946static void dispc_mgr_set_lcd_type_tft(enum omap_channel channel)
2947{
2948        mgr_fld_write(channel, DISPC_MGR_FLD_STNTFT, 1);
2949}
2950
2951static void dispc_set_loadmode(enum omap_dss_load_mode mode)
2952{
2953        REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1);
2954}
2955
2956
2957static void dispc_mgr_set_default_color(enum omap_channel channel, u32 color)
2958{
2959        dispc_write_reg(DISPC_DEFAULT_COLOR(channel), color);
2960}
2961
2962static void dispc_mgr_set_trans_key(enum omap_channel ch,
2963                enum omap_dss_trans_key_type type,
2964                u32 trans_key)
2965{
2966        mgr_fld_write(ch, DISPC_MGR_FLD_TCKSELECTION, type);
2967
2968        dispc_write_reg(DISPC_TRANS_COLOR(ch), trans_key);
2969}
2970
2971static void dispc_mgr_enable_trans_key(enum omap_channel ch, bool enable)
2972{
2973        mgr_fld_write(ch, DISPC_MGR_FLD_TCKENABLE, enable);
2974}
2975
2976static void dispc_mgr_enable_alpha_fixed_zorder(enum omap_channel ch,
2977                bool enable)
2978{
2979        if (!dss_has_feature(FEAT_ALPHA_FIXED_ZORDER))
2980                return;
2981
2982        if (ch == OMAP_DSS_CHANNEL_LCD)
2983                REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18);
2984        else if (ch == OMAP_DSS_CHANNEL_DIGIT)
2985                REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19);
2986}
2987
2988void dispc_mgr_setup(enum omap_channel channel,
2989                const struct omap_overlay_manager_info *info)
2990{
2991        dispc_mgr_set_default_color(channel, info->default_color);
2992        dispc_mgr_set_trans_key(channel, info->trans_key_type, info->trans_key);
2993        dispc_mgr_enable_trans_key(channel, info->trans_enabled);
2994        dispc_mgr_enable_alpha_fixed_zorder(channel,
2995                        info->partial_alpha_enabled);
2996        if (dss_has_feature(FEAT_CPR)) {
2997                dispc_mgr_enable_cpr(channel, info->cpr_enable);
2998                dispc_mgr_set_cpr_coef(channel, &info->cpr_coefs);
2999        }
3000}
3001EXPORT_SYMBOL(dispc_mgr_setup);
3002
3003static void dispc_mgr_set_tft_data_lines(enum omap_channel channel, u8 data_lines)
3004{
3005        int code;
3006
3007        switch (data_lines) {
3008        case 12:
3009                code = 0;
3010                break;
3011        case 16:
3012                code = 1;
3013                break;
3014        case 18:
3015                code = 2;
3016                break;
3017        case 24:
3018                code = 3;
3019                break;
3020        default:
3021                BUG();
3022                return;
3023        }
3024
3025        mgr_fld_write(channel, DISPC_MGR_FLD_TFTDATALINES, code);
3026}
3027
3028static void dispc_mgr_set_io_pad_mode(enum dss_io_pad_mode mode)
3029{
3030        u32 l;
3031        int gpout0, gpout1;
3032
3033        switch (mode) {
3034        case DSS_IO_PAD_MODE_RESET:
3035                gpout0 = 0;
3036                gpout1 = 0;
3037                break;
3038        case DSS_IO_PAD_MODE_RFBI:
3039                gpout0 = 1;
3040                gpout1 = 0;
3041                break;
3042        case DSS_IO_PAD_MODE_BYPASS:
3043                gpout0 = 1;
3044                gpout1 = 1;
3045                break;
3046        default:
3047                BUG();
3048                return;
3049        }
3050
3051        l = dispc_read_reg(DISPC_CONTROL);
3052        l = FLD_MOD(l, gpout0, 15, 15);
3053        l = FLD_MOD(l, gpout1, 16, 16);
3054        dispc_write_reg(DISPC_CONTROL, l);
3055}
3056
3057static void dispc_mgr_enable_stallmode(enum omap_channel channel, bool enable)
3058{
3059        mgr_fld_write(channel, DISPC_MGR_FLD_STALLMODE, enable);
3060}
3061
3062void dispc_mgr_set_lcd_config(enum omap_channel channel,
3063                const struct dss_lcd_mgr_config *config)
3064{
3065        dispc_mgr_set_io_pad_mode(config->io_pad_mode);
3066
3067        dispc_mgr_enable_stallmode(channel, config->stallmode);
3068        dispc_mgr_enable_fifohandcheck(channel, config->fifohandcheck);
3069
3070        dispc_mgr_set_clock_div(channel, &config->clock_info);
3071
3072        dispc_mgr_set_tft_data_lines(channel, config->video_port_width);
3073
3074        dispc_lcd_enable_signal_polarity(config->lcden_sig_polarity);
3075
3076        dispc_mgr_set_lcd_type_tft(channel);
3077}
3078EXPORT_SYMBOL(dispc_mgr_set_lcd_config);
3079
3080static bool _dispc_mgr_size_ok(u16 width, u16 height)
3081{
3082        return width <= dispc.feat->mgr_width_max &&
3083                height <= dispc.feat->mgr_height_max;
3084}
3085
3086static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp,
3087                int vsw, int vfp, int vbp)
3088{
3089        if (hsw < 1 || hsw > dispc.feat->sw_max ||
3090                        hfp < 1 || hfp > dispc.feat->hp_max ||
3091                        hbp < 1 || hbp > dispc.feat->hp_max ||
3092                        vsw < 1 || vsw > dispc.feat->sw_max ||
3093                        vfp < 0 || vfp > dispc.feat->vp_max ||
3094                        vbp < 0 || vbp > dispc.feat->vp_max)
3095                return false;
3096        return true;
3097}
3098
3099static bool _dispc_mgr_pclk_ok(enum omap_channel channel,
3100                unsigned long pclk)
3101{
3102        if (dss_mgr_is_lcd(channel))
3103                return pclk <= dispc.feat->max_lcd_pclk ? true : false;
3104        else
3105                return pclk <= dispc.feat->max_tv_pclk ? true : false;
3106}
3107
3108bool dispc_mgr_timings_ok(enum omap_channel channel,
3109                const struct omap_video_timings *timings)
3110{
3111        if (!_dispc_mgr_size_ok(timings->x_res, timings->y_res))
3112                return false;
3113
3114        if (!_dispc_mgr_pclk_ok(channel, timings->pixelclock))
3115                return false;
3116
3117        if (dss_mgr_is_lcd(channel)) {
3118                /* TODO: OMAP4+ supports interlace for LCD outputs */
3119                if (timings->interlace)
3120                        return false;
3121
3122                if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp,
3123                                timings->hbp, timings->vsw, timings->vfp,
3124                                timings->vbp))
3125                        return false;
3126        }
3127
3128        return true;
3129}
3130
3131static void _dispc_mgr_set_lcd_timings(enum omap_channel channel, int hsw,
3132                int hfp, int hbp, int vsw, int vfp, int vbp,
3133                enum omap_dss_signal_level vsync_level,
3134                enum omap_dss_signal_level hsync_level,
3135                enum omap_dss_signal_edge data_pclk_edge,
3136                enum omap_dss_signal_level de_level,
3137                enum omap_dss_signal_edge sync_pclk_edge)
3138
3139{
3140        u32 timing_h, timing_v, l;
3141        bool onoff, rf, ipc, vs, hs, de;
3142
3143        timing_h = FLD_VAL(hsw-1, dispc.feat->sw_start, 0) |
3144                        FLD_VAL(hfp-1, dispc.feat->fp_start, 8) |
3145                        FLD_VAL(hbp-1, dispc.feat->bp_start, 20);
3146        timing_v = FLD_VAL(vsw-1, dispc.feat->sw_start, 0) |
3147                        FLD_VAL(vfp, dispc.feat->fp_start, 8) |
3148                        FLD_VAL(vbp, dispc.feat->bp_start, 20);
3149
3150        dispc_write_reg(DISPC_TIMING_H(channel), timing_h);
3151        dispc_write_reg(DISPC_TIMING_V(channel), timing_v);
3152
3153        switch (vsync_level) {
3154        case OMAPDSS_SIG_ACTIVE_LOW:
3155                vs = true;
3156                break;
3157        case OMAPDSS_SIG_ACTIVE_HIGH:
3158                vs = false;
3159                break;
3160        default:
3161                BUG();
3162        }
3163
3164        switch (hsync_level) {
3165        case OMAPDSS_SIG_ACTIVE_LOW:
3166                hs = true;
3167                break;
3168        case OMAPDSS_SIG_ACTIVE_HIGH:
3169                hs = false;
3170                break;
3171        default:
3172                BUG();
3173        }
3174
3175        switch (de_level) {
3176        case OMAPDSS_SIG_ACTIVE_LOW:
3177                de = true;
3178                break;
3179        case OMAPDSS_SIG_ACTIVE_HIGH:
3180                de = false;
3181                break;
3182        default:
3183                BUG();
3184        }
3185
3186        switch (data_pclk_edge) {
3187        case OMAPDSS_DRIVE_SIG_RISING_EDGE:
3188                ipc = false;
3189                break;
3190        case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
3191                ipc = true;
3192                break;
3193        default:
3194                BUG();
3195        }
3196
3197        /* always use the 'rf' setting */
3198        onoff = true;
3199
3200        switch (sync_pclk_edge) {
3201        case OMAPDSS_DRIVE_SIG_FALLING_EDGE:
3202                rf = false;
3203                break;
3204        case OMAPDSS_DRIVE_SIG_RISING_EDGE:
3205                rf = true;
3206                break;
3207        default:
3208                BUG();
3209        }
3210
3211        l = FLD_VAL(onoff, 17, 17) |
3212                FLD_VAL(rf, 16, 16) |
3213                FLD_VAL(de, 15, 15) |
3214                FLD_VAL(ipc, 14, 14) |
3215                FLD_VAL(hs, 13, 13) |
3216                FLD_VAL(vs, 12, 12);
3217
3218        /* always set ALIGN bit when available */
3219        if (dispc.feat->supports_sync_align)
3220                l |= (1 << 18);
3221
3222        dispc_write_reg(DISPC_POL_FREQ(channel), l);
3223
3224        if (dispc.syscon_pol) {
3225                const int shifts[] = {
3226                        [OMAP_DSS_CHANNEL_LCD] = 0,
3227                        [OMAP_DSS_CHANNEL_LCD2] = 1,
3228                        [OMAP_DSS_CHANNEL_LCD3] = 2,
3229                };
3230
3231                u32 mask, val;
3232
3233                mask = (1 << 0) | (1 << 3) | (1 << 6);
3234                val = (rf << 0) | (ipc << 3) | (onoff << 6);
3235
3236                mask <<= 16 + shifts[channel];
3237                val <<= 16 + shifts[channel];
3238
3239                regmap_update_bits(dispc.syscon_pol, dispc.syscon_pol_offset,
3240                        mask, val);
3241        }
3242}
3243
3244/* change name to mode? */
3245void dispc_mgr_set_timings(enum omap_channel channel,
3246                const struct omap_video_timings *timings)
3247{
3248        unsigned xtot, ytot;
3249        unsigned long ht, vt;
3250        struct omap_video_timings t = *timings;
3251
3252        DSSDBG("channel %d xres %u yres %u\n", channel, t.x_res, t.y_res);
3253
3254        if (!dispc_mgr_timings_ok(channel, &t)) {
3255                BUG();
3256                return;
3257        }
3258
3259        if (dss_mgr_is_lcd(channel)) {
3260                _dispc_mgr_set_lcd_timings(channel, t.hsw, t.hfp, t.hbp, t.vsw,
3261                                t.vfp, t.vbp, t.vsync_level, t.hsync_level,
3262                                t.data_pclk_edge, t.de_level, t.sync_pclk_edge);
3263
3264                xtot = t.x_res + t.hfp + t.hsw + t.hbp;
3265                ytot = t.y_res + t.vfp + t.vsw + t.vbp;
3266
3267                ht = timings->pixelclock / xtot;
3268                vt = timings->pixelclock / xtot / ytot;
3269
3270                DSSDBG("pck %u\n", timings->pixelclock);
3271                DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n",
3272                        t.hsw, t.hfp, t.hbp, t.vsw, t.vfp, t.vbp);
3273                DSSDBG("vsync_level %d hsync_level %d data_pclk_edge %d de_level %d sync_pclk_edge %d\n",
3274                        t.vsync_level, t.hsync_level, t.data_pclk_edge,
3275                        t.de_level, t.sync_pclk_edge);
3276
3277                DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt);
3278        } else {
3279                if (t.interlace)
3280                        t.y_res /= 2;
3281        }
3282
3283        dispc_mgr_set_size(channel, t.x_res, t.y_res);
3284}
3285EXPORT_SYMBOL(dispc_mgr_set_timings);
3286
3287static void dispc_mgr_set_lcd_divisor(enum omap_channel channel, u16 lck_div,
3288                u16 pck_div)
3289{
3290        BUG_ON(lck_div < 1);
3291        BUG_ON(pck_div < 1);
3292
3293        dispc_write_reg(DISPC_DIVISORo(channel),
3294                        FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0));
3295
3296        if (!dss_has_feature(FEAT_CORE_CLK_DIV) &&
3297                        channel == OMAP_DSS_CHANNEL_LCD)
3298                dispc.core_clk_rate = dispc_fclk_rate() / lck_div;
3299}
3300
3301static void dispc_mgr_get_lcd_divisor(enum omap_channel channel, int *lck_div,
3302                int *pck_div)
3303{
3304        u32 l;
3305        l = dispc_read_reg(DISPC_DIVISORo(channel));
3306        *lck_div = FLD_GET(l, 23, 16);
3307        *pck_div = FLD_GET(l, 7, 0);
3308}
3309
3310static unsigned long dispc_fclk_rate(void)
3311{
3312        struct dss_pll *pll;
3313        unsigned long r = 0;
3314
3315        switch (dss_get_dispc_clk_source()) {
3316        case OMAP_DSS_CLK_SRC_FCK:
3317                r = dss_get_dispc_clk_rate();
3318                break;
3319        case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
3320                pll = dss_pll_find("dsi0");
3321                if (!pll)
3322                        pll = dss_pll_find("video0");
3323
3324                r = pll->cinfo.clkout[0];
3325                break;
3326        case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
3327                pll = dss_pll_find("dsi1");
3328                if (!pll)
3329                        pll = dss_pll_find("video1");
3330
3331                r = pll->cinfo.clkout[0];
3332                break;
3333        default:
3334                BUG();
3335                return 0;
3336        }
3337
3338        return r;
3339}
3340
3341static unsigned long dispc_mgr_lclk_rate(enum omap_channel channel)
3342{
3343        struct dss_pll *pll;
3344        int lcd;
3345        unsigned long r;
3346        u32 l;
3347
3348        if (dss_mgr_is_lcd(channel)) {
3349                l = dispc_read_reg(DISPC_DIVISORo(channel));
3350
3351                lcd = FLD_GET(l, 23, 16);
3352
3353                switch (dss_get_lcd_clk_source(channel)) {
3354                case OMAP_DSS_CLK_SRC_FCK:
3355                        r = dss_get_dispc_clk_rate();
3356                        break;
3357                case OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC:
3358                        pll = dss_pll_find("dsi0");
3359                        if (!pll)
3360                                pll = dss_pll_find("video0");
3361
3362                        r = pll->cinfo.clkout[0];
3363                        break;
3364                case OMAP_DSS_CLK_SRC_DSI2_PLL_HSDIV_DISPC:
3365                        pll = dss_pll_find("dsi1");
3366                        if (!pll)
3367                                pll = dss_pll_find("video1");
3368
3369                        r = pll->cinfo.clkout[0];
3370                        break;
3371                default:
3372                        BUG();
3373                        return 0;
3374                }
3375
3376                return r / lcd;
3377        } else {
3378                return dispc_fclk_rate();
3379        }
3380}
3381
3382static unsigned long dispc_mgr_pclk_rate(enum omap_channel channel)
3383{
3384        unsigned long r;
3385
3386        if (dss_mgr_is_lcd(channel)) {
3387                int pcd;
3388                u32 l;
3389
3390                l = dispc_read_reg(DISPC_DIVISORo(channel));
3391
3392                pcd = FLD_GET(l, 7, 0);
3393
3394                r = dispc_mgr_lclk_rate(channel);
3395
3396                return r / pcd;
3397        } else {
3398                return dispc.tv_pclk_rate;
3399        }
3400}
3401
3402void dispc_set_tv_pclk(unsigned long pclk)
3403{
3404        dispc.tv_pclk_rate = pclk;
3405}
3406
3407static unsigned long dispc_core_clk_rate(void)
3408{
3409        return dispc.core_clk_rate;
3410}
3411
3412static unsigned long dispc_plane_pclk_rate(enum omap_plane plane)
3413{
3414        enum omap_channel channel;
3415
3416        if (plane == OMAP_DSS_WB)
3417                return 0;
3418
3419        channel = dispc_ovl_get_channel_out(plane);
3420
3421        return dispc_mgr_pclk_rate(channel);
3422}
3423
3424static unsigned long dispc_plane_lclk_rate(enum omap_plane plane)
3425{
3426        enum omap_channel channel;
3427
3428        if (plane == OMAP_DSS_WB)
3429                return 0;
3430
3431        channel = dispc_ovl_get_channel_out(plane);
3432
3433        return dispc_mgr_lclk_rate(channel);
3434}
3435
3436static void dispc_dump_clocks_channel(struct seq_file *s, enum omap_channel channel)
3437{
3438        int lcd, pcd;
3439        enum omap_dss_clk_source lcd_clk_src;
3440
3441        seq_printf(s, "- %s -\n", mgr_desc[channel].name);
3442
3443        lcd_clk_src = dss_get_lcd_clk_source(channel);
3444
3445        seq_printf(s, "%s clk source = %s (%s)\n", mgr_desc[channel].name,
3446                dss_get_generic_clk_source_name(lcd_clk_src),
3447                dss_feat_get_clk_source_name(lcd_clk_src));
3448
3449        dispc_mgr_get_lcd_divisor(channel, &lcd, &pcd);
3450
3451        seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
3452                dispc_mgr_lclk_rate(channel), lcd);
3453        seq_printf(s, "pck\t\t%-16lupck div\t%u\n",
3454                dispc_mgr_pclk_rate(channel), pcd);
3455}
3456
3457void dispc_dump_clocks(struct seq_file *s)
3458{
3459        int lcd;
3460        u32 l;
3461        enum omap_dss_clk_source dispc_clk_src = dss_get_dispc_clk_source();
3462
3463        if (dispc_runtime_get())
3464                return;
3465
3466        seq_printf(s, "- DISPC -\n");
3467
3468        seq_printf(s, "dispc fclk source = %s (%s)\n",
3469                        dss_get_generic_clk_source_name(dispc_clk_src),
3470                        dss_feat_get_clk_source_name(dispc_clk_src));
3471
3472        seq_printf(s, "fck\t\t%-16lu\n", dispc_fclk_rate());
3473
3474        if (dss_has_feature(FEAT_CORE_CLK_DIV)) {
3475                seq_printf(s, "- DISPC-CORE-CLK -\n");
3476                l = dispc_read_reg(DISPC_DIVISOR);
3477                lcd = FLD_GET(l, 23, 16);
3478
3479                seq_printf(s, "lck\t\t%-16lulck div\t%u\n",
3480                                (dispc_fclk_rate()/lcd), lcd);
3481        }
3482
3483        dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD);
3484
3485        if (dss_has_feature(FEAT_MGR_LCD2))
3486                dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD2);
3487        if (dss_has_feature(FEAT_MGR_LCD3))
3488                dispc_dump_clocks_channel(s, OMAP_DSS_CHANNEL_LCD3);
3489
3490        dispc_runtime_put();
3491}
3492
3493static void dispc_dump_regs(struct seq_file *s)
3494{
3495        int i, j;
3496        const char *mgr_names[] = {
3497                [OMAP_DSS_CHANNEL_LCD]          = "LCD",
3498                [OMAP_DSS_CHANNEL_DIGIT]        = "TV",
3499                [OMAP_DSS_CHANNEL_LCD2]         = "LCD2",
3500                [OMAP_DSS_CHANNEL_LCD3]         = "LCD3",
3501        };
3502        const char *ovl_names[] = {
3503                [OMAP_DSS_GFX]          = "GFX",
3504                [OMAP_DSS_VIDEO1]       = "VID1",
3505                [OMAP_DSS_VIDEO2]       = "VID2",
3506                [OMAP_DSS_VIDEO3]       = "VID3",
3507                [OMAP_DSS_WB]           = "WB",
3508        };
3509        const char **p_names;
3510
3511#define DUMPREG(r) seq_printf(s, "%-50s %08x\n", #r, dispc_read_reg(r))
3512
3513        if (dispc_runtime_get())
3514                return;
3515
3516        /* DISPC common registers */
3517        DUMPREG(DISPC_REVISION);
3518        DUMPREG(DISPC_SYSCONFIG);
3519        DUMPREG(DISPC_SYSSTATUS);
3520        DUMPREG(DISPC_IRQSTATUS);
3521        DUMPREG(DISPC_IRQENABLE);
3522        DUMPREG(DISPC_CONTROL);
3523        DUMPREG(DISPC_CONFIG);
3524        DUMPREG(DISPC_CAPABLE);
3525        DUMPREG(DISPC_LINE_STATUS);
3526        DUMPREG(DISPC_LINE_NUMBER);
3527        if (dss_has_feature(FEAT_ALPHA_FIXED_ZORDER) ||
3528                        dss_has_feature(FEAT_ALPHA_FREE_ZORDER))
3529                DUMPREG(DISPC_GLOBAL_ALPHA);
3530        if (dss_has_feature(FEAT_MGR_LCD2)) {
3531                DUMPREG(DISPC_CONTROL2);
3532                DUMPREG(DISPC_CONFIG2);
3533        }
3534        if (dss_has_feature(FEAT_MGR_LCD3)) {
3535                DUMPREG(DISPC_CONTROL3);
3536                DUMPREG(DISPC_CONFIG3);
3537        }
3538        if (dss_has_feature(FEAT_MFLAG))
3539                DUMPREG(DISPC_GLOBAL_MFLAG_ATTRIBUTE);
3540
3541#undef DUMPREG
3542
3543#define DISPC_REG(i, name) name(i)
3544#define DUMPREG(i, r) seq_printf(s, "%s(%s)%*s %08x\n", #r, p_names[i], \
3545        (int)(48 - strlen(#r) - strlen(p_names[i])), " ", \
3546        dispc_read_reg(DISPC_REG(i, r)))
3547
3548        p_names = mgr_names;
3549
3550        /* DISPC channel specific registers */
3551        for (i = 0; i < dss_feat_get_num_mgrs(); i++) {
3552                DUMPREG(i, DISPC_DEFAULT_COLOR);
3553                DUMPREG(i, DISPC_TRANS_COLOR);
3554                DUMPREG(i, DISPC_SIZE_MGR);
3555
3556                if (i == OMAP_DSS_CHANNEL_DIGIT)
3557                        continue;
3558
3559                DUMPREG(i, DISPC_TIMING_H);
3560                DUMPREG(i, DISPC_TIMING_V);
3561                DUMPREG(i, DISPC_POL_FREQ);
3562                DUMPREG(i, DISPC_DIVISORo);
3563
3564                DUMPREG(i, DISPC_DATA_CYCLE1);
3565                DUMPREG(i, DISPC_DATA_CYCLE2);
3566                DUMPREG(i, DISPC_DATA_CYCLE3);
3567
3568                if (dss_has_feature(FEAT_CPR)) {
3569                        DUMPREG(i, DISPC_CPR_COEF_R);
3570                        DUMPREG(i, DISPC_CPR_COEF_G);
3571                        DUMPREG(i, DISPC_CPR_COEF_B);
3572                }
3573        }
3574
3575        p_names = ovl_names;
3576
3577        for (i = 0; i < dss_feat_get_num_ovls(); i++) {
3578                DUMPREG(i, DISPC_OVL_BA0);
3579                DUMPREG(i, DISPC_OVL_BA1);
3580                DUMPREG(i, DISPC_OVL_POSITION);
3581                DUMPREG(i, DISPC_OVL_SIZE);
3582                DUMPREG(i, DISPC_OVL_ATTRIBUTES);
3583                DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD);
3584                DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS);
3585                DUMPREG(i, DISPC_OVL_ROW_INC);
3586                DUMPREG(i, DISPC_OVL_PIXEL_INC);
3587
3588                if (dss_has_feature(FEAT_PRELOAD))
3589                        DUMPREG(i, DISPC_OVL_PRELOAD);
3590                if (dss_has_feature(FEAT_MFLAG))
3591                        DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD);
3592
3593                if (i == OMAP_DSS_GFX) {
3594                        DUMPREG(i, DISPC_OVL_WINDOW_SKIP);
3595                        DUMPREG(i, DISPC_OVL_TABLE_BA);
3596                        continue;
3597                }
3598
3599                DUMPREG(i, DISPC_OVL_FIR);
3600                DUMPREG(i, DISPC_OVL_PICTURE_SIZE);
3601                DUMPREG(i, DISPC_OVL_ACCU0);
3602                DUMPREG(i, DISPC_OVL_ACCU1);
3603                if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
3604                        DUMPREG(i, DISPC_OVL_BA0_UV);
3605                        DUMPREG(i, DISPC_OVL_BA1_UV);
3606                        DUMPREG(i, DISPC_OVL_FIR2);
3607                        DUMPREG(i, DISPC_OVL_ACCU2_0);
3608                        DUMPREG(i, DISPC_OVL_ACCU2_1);
3609                }
3610                if (dss_has_feature(FEAT_ATTR2))
3611                        DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
3612        }
3613
3614        if (dispc.feat->has_writeback) {
3615                i = OMAP_DSS_WB;
3616                DUMPREG(i, DISPC_OVL_BA0);
3617                DUMPREG(i, DISPC_OVL_BA1);
3618                DUMPREG(i, DISPC_OVL_SIZE);
3619                DUMPREG(i, DISPC_OVL_ATTRIBUTES);
3620                DUMPREG(i, DISPC_OVL_FIFO_THRESHOLD);
3621                DUMPREG(i, DISPC_OVL_FIFO_SIZE_STATUS);
3622                DUMPREG(i, DISPC_OVL_ROW_INC);
3623                DUMPREG(i, DISPC_OVL_PIXEL_INC);
3624
3625                if (dss_has_feature(FEAT_MFLAG))
3626                        DUMPREG(i, DISPC_OVL_MFLAG_THRESHOLD);
3627
3628                DUMPREG(i, DISPC_OVL_FIR);
3629                DUMPREG(i, DISPC_OVL_PICTURE_SIZE);
3630                DUMPREG(i, DISPC_OVL_ACCU0);
3631                DUMPREG(i, DISPC_OVL_ACCU1);
3632                if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
3633                        DUMPREG(i, DISPC_OVL_BA0_UV);
3634                        DUMPREG(i, DISPC_OVL_BA1_UV);
3635                        DUMPREG(i, DISPC_OVL_FIR2);
3636                        DUMPREG(i, DISPC_OVL_ACCU2_0);
3637                        DUMPREG(i, DISPC_OVL_ACCU2_1);
3638                }
3639                if (dss_has_feature(FEAT_ATTR2))
3640                        DUMPREG(i, DISPC_OVL_ATTRIBUTES2);
3641        }
3642
3643#undef DISPC_REG
3644#undef DUMPREG
3645
3646#define DISPC_REG(plane, name, i) name(plane, i)
3647#define DUMPREG(plane, name, i) \
3648        seq_printf(s, "%s_%d(%s)%*s %08x\n", #name, i, p_names[plane], \
3649        (int)(46 - strlen(#name) - strlen(p_names[plane])), " ", \
3650        dispc_read_reg(DISPC_REG(plane, name, i)))
3651
3652        /* Video pipeline coefficient registers */
3653
3654        /* start from OMAP_DSS_VIDEO1 */
3655        for (i = 1; i < dss_feat_get_num_ovls(); i++) {
3656                for (j = 0; j < 8; j++)
3657                        DUMPREG(i, DISPC_OVL_FIR_COEF_H, j);
3658
3659                for (j = 0; j < 8; j++)
3660                        DUMPREG(i, DISPC_OVL_FIR_COEF_HV, j);
3661
3662                for (j = 0; j < 5; j++)
3663                        DUMPREG(i, DISPC_OVL_CONV_COEF, j);
3664
3665                if (dss_has_feature(FEAT_FIR_COEF_V)) {
3666                        for (j = 0; j < 8; j++)
3667                                DUMPREG(i, DISPC_OVL_FIR_COEF_V, j);
3668                }
3669
3670                if (dss_has_feature(FEAT_HANDLE_UV_SEPARATE)) {
3671                        for (j = 0; j < 8; j++)
3672                                DUMPREG(i, DISPC_OVL_FIR_COEF_H2, j);
3673
3674                        for (j = 0; j < 8; j++)
3675                                DUMPREG(i, DISPC_OVL_FIR_COEF_HV2, j);
3676
3677                        for (j = 0; j < 8; j++)
3678                                DUMPREG(i, DISPC_OVL_FIR_COEF_V2, j);
3679                }
3680        }
3681
3682        dispc_runtime_put();
3683
3684#undef DISPC_REG
3685#undef DUMPREG
3686}
3687
3688/* calculate clock rates using dividers in cinfo */
3689int dispc_calc_clock_rates(unsigned long dispc_fclk_rate,
3690                struct dispc_clock_info *cinfo)
3691{
3692        if (cinfo->lck_div > 255 || cinfo->lck_div == 0)
3693                return -EINVAL;
3694        if (cinfo->pck_div < 1 || cinfo->pck_div > 255)
3695                return -EINVAL;
3696
3697        cinfo->lck = dispc_fclk_rate / cinfo->lck_div;
3698        cinfo->pck = cinfo->lck / cinfo->pck_div;
3699
3700        return 0;
3701}
3702
3703bool dispc_div_calc(unsigned long dispc,
3704                unsigned long pck_min, unsigned long pck_max,
3705                dispc_div_calc_func func, void *data)
3706{
3707        int lckd, lckd_start, lckd_stop;
3708        int pckd, pckd_start, pckd_stop;
3709        unsigned long pck, lck;
3710        unsigned long lck_max;
3711        unsigned long pckd_hw_min, pckd_hw_max;
3712        unsigned min_fck_per_pck;
3713        unsigned long fck;
3714
3715#ifdef CONFIG_FB_OMAP2_DSS_MIN_FCK_PER_PCK
3716        min_fck_per_pck = CONFIG_FB_OMAP2_DSS_MIN_FCK_PER_PCK;
3717#else
3718        min_fck_per_pck = 0;
3719#endif
3720
3721        pckd_hw_min = dss_feat_get_param_min(FEAT_PARAM_DSS_PCD);
3722        pckd_hw_max = dss_feat_get_param_max(FEAT_PARAM_DSS_PCD);
3723
3724        lck_max = dss_feat_get_param_max(FEAT_PARAM_DSS_FCK);
3725
3726        pck_min = pck_min ? pck_min : 1;
3727        pck_max = pck_max ? pck_max : ULONG_MAX;
3728
3729        lckd_start = max(DIV_ROUND_UP(dispc, lck_max), 1ul);
3730        lckd_stop = min(dispc / pck_min, 255ul);
3731
3732        for (lckd = lckd_start; lckd <= lckd_stop; ++lckd) {
3733                lck = dispc / lckd;
3734
3735                pckd_start = max(DIV_ROUND_UP(lck, pck_max), pckd_hw_min);
3736                pckd_stop = min(lck / pck_min, pckd_hw_max);
3737
3738                for (pckd = pckd_start; pckd <= pckd_stop; ++pckd) {
3739                        pck = lck / pckd;
3740
3741                        /*
3742                         * For OMAP2/3 the DISPC fclk is the same as LCD's logic
3743                         * clock, which means we're configuring DISPC fclk here
3744                         * also. Thus we need to use the calculated lck. For
3745                         * OMAP4+ the DISPC fclk is a separate clock.
3746                         */
3747                        if (dss_has_feature(FEAT_CORE_CLK_DIV))
3748                                fck = dispc_core_clk_rate();
3749                        else
3750                                fck = lck;
3751
3752                        if (fck < pck * min_fck_per_pck)
3753                                continue;
3754
3755                        if (func(lckd, pckd, lck, pck, data))
3756                                return true;
3757                }
3758        }
3759
3760        return false;
3761}
3762
3763void dispc_mgr_set_clock_div(enum omap_channel channel,
3764                const struct dispc_clock_info *cinfo)
3765{
3766        DSSDBG("lck = %lu (%u)\n", cinfo->lck, cinfo->lck_div);
3767        DSSDBG("pck = %lu (%u)\n", cinfo->pck, cinfo->pck_div);
3768
3769        dispc_mgr_set_lcd_divisor(channel, cinfo->lck_div, cinfo->pck_div);
3770}
3771
3772int dispc_mgr_get_clock_div(enum omap_channel channel,
3773                struct dispc_clock_info *cinfo)
3774{
3775        unsigned long fck;
3776
3777        fck = dispc_fclk_rate();
3778
3779        cinfo->lck_div = REG_GET(DISPC_DIVISORo(channel), 23, 16);
3780        cinfo->pck_div = REG_GET(DISPC_DIVISORo(channel), 7, 0);
3781
3782        cinfo->lck = fck / cinfo->lck_div;
3783        cinfo->pck = cinfo->lck / cinfo->pck_div;
3784
3785        return 0;
3786}
3787
3788u32 dispc_read_irqstatus(void)
3789{
3790        return dispc_read_reg(DISPC_IRQSTATUS);
3791}
3792EXPORT_SYMBOL(dispc_read_irqstatus);
3793
3794void dispc_clear_irqstatus(u32 mask)
3795{
3796        dispc_write_reg(DISPC_IRQSTATUS, mask);
3797}
3798EXPORT_SYMBOL(dispc_clear_irqstatus);
3799
3800u32 dispc_read_irqenable(void)
3801{
3802        return dispc_read_reg(DISPC_IRQENABLE);
3803}
3804EXPORT_SYMBOL(dispc_read_irqenable);
3805
3806void dispc_write_irqenable(u32 mask)
3807{
3808        u32 old_mask = dispc_read_reg(DISPC_IRQENABLE);
3809
3810        /* clear the irqstatus for newly enabled irqs */
3811        dispc_clear_irqstatus((mask ^ old_mask) & mask);
3812
3813        dispc_write_reg(DISPC_IRQENABLE, mask);
3814}
3815EXPORT_SYMBOL(dispc_write_irqenable);
3816
3817void dispc_enable_sidle(void)
3818{
3819        REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3);  /* SIDLEMODE: smart idle */
3820}
3821
3822void dispc_disable_sidle(void)
3823{
3824        REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3);  /* SIDLEMODE: no idle */
3825}
3826
3827static void _omap_dispc_initial_config(void)
3828{
3829        u32 l;
3830
3831        /* Exclusively enable DISPC_CORE_CLK and set divider to 1 */
3832        if (dss_has_feature(FEAT_CORE_CLK_DIV)) {
3833                l = dispc_read_reg(DISPC_DIVISOR);
3834                /* Use DISPC_DIVISOR.LCD, instead of DISPC_DIVISOR1.LCD */
3835                l = FLD_MOD(l, 1, 0, 0);
3836                l = FLD_MOD(l, 1, 23, 16);
3837                dispc_write_reg(DISPC_DIVISOR, l);
3838
3839                dispc.core_clk_rate = dispc_fclk_rate();
3840        }
3841
3842        /* FUNCGATED */
3843        if (dss_has_feature(FEAT_FUNCGATED))
3844                REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9);
3845
3846        dispc_setup_color_conv_coef();
3847
3848        dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY);
3849
3850        dispc_init_fifos();
3851
3852        dispc_configure_burst_sizes();
3853
3854        dispc_ovl_enable_zorder_planes();
3855
3856        if (dispc.feat->mstandby_workaround)
3857                REG_FLD_MOD(DISPC_MSTANDBY_CTRL, 1, 0, 0);
3858
3859        if (dss_has_feature(FEAT_MFLAG))
3860                dispc_init_mflag();
3861}
3862
3863static const struct dispc_features omap24xx_dispc_feats = {
3864        .sw_start               =       5,
3865        .fp_start               =       15,
3866        .bp_start               =       27,
3867        .sw_max                 =       64,
3868        .vp_max                 =       255,
3869        .hp_max                 =       256,
3870        .mgr_width_start        =       10,
3871        .mgr_height_start       =       26,
3872        .mgr_width_max          =       2048,
3873        .mgr_height_max         =       2048,
3874        .max_lcd_pclk           =       66500000,
3875        .calc_scaling           =       dispc_ovl_calc_scaling_24xx,
3876        .calc_core_clk          =       calc_core_clk_24xx,
3877        .num_fifos              =       3,
3878        .no_framedone_tv        =       true,
3879        .set_max_preload        =       false,
3880        .last_pixel_inc_missing =       true,
3881};
3882
3883static const struct dispc_features omap34xx_rev1_0_dispc_feats = {
3884        .sw_start               =       5,
3885        .fp_start               =       15,
3886        .bp_start               =       27,
3887        .sw_max                 =       64,
3888        .vp_max                 =       255,
3889        .hp_max                 =       256,
3890        .mgr_width_start        =       10,
3891        .mgr_height_start       =       26,
3892        .mgr_width_max          =       2048,
3893        .mgr_height_max         =       2048,
3894        .max_lcd_pclk           =       173000000,
3895        .max_tv_pclk            =       59000000,
3896        .calc_scaling           =       dispc_ovl_calc_scaling_34xx,
3897        .calc_core_clk          =       calc_core_clk_34xx,
3898        .num_fifos              =       3,
3899        .no_framedone_tv        =       true,
3900        .set_max_preload        =       false,
3901        .last_pixel_inc_missing =       true,
3902};
3903
3904static const struct dispc_features omap34xx_rev3_0_dispc_feats = {
3905        .sw_start               =       7,
3906        .fp_start               =       19,
3907        .bp_start               =       31,
3908        .sw_max                 =       256,
3909        .vp_max                 =       4095,
3910        .hp_max                 =       4096,
3911        .mgr_width_start        =       10,
3912        .mgr_height_start       =       26,
3913        .mgr_width_max          =       2048,
3914        .mgr_height_max         =       2048,
3915        .max_lcd_pclk           =       173000000,
3916        .max_tv_pclk            =       59000000,
3917        .calc_scaling           =       dispc_ovl_calc_scaling_34xx,
3918        .calc_core_clk          =       calc_core_clk_34xx,
3919        .num_fifos              =       3,
3920        .no_framedone_tv        =       true,
3921        .set_max_preload        =       false,
3922        .last_pixel_inc_missing =       true,
3923};
3924
3925static const struct dispc_features omap44xx_dispc_feats = {
3926        .sw_start               =       7,
3927        .fp_start               =       19,
3928        .bp_start               =       31,
3929        .sw_max                 =       256,
3930        .vp_max                 =       4095,
3931        .hp_max                 =       4096,
3932        .mgr_width_start        =       10,
3933        .mgr_height_start       =       26,
3934        .mgr_width_max          =       2048,
3935        .mgr_height_max         =       2048,
3936        .max_lcd_pclk           =       170000000,
3937        .max_tv_pclk            =       185625000,
3938        .calc_scaling           =       dispc_ovl_calc_scaling_44xx,
3939        .calc_core_clk          =       calc_core_clk_44xx,
3940        .num_fifos              =       5,
3941        .gfx_fifo_workaround    =       true,
3942        .set_max_preload        =       true,
3943        .supports_sync_align    =       true,
3944        .has_writeback          =       true,
3945};
3946
3947static const struct dispc_features omap54xx_dispc_feats = {
3948        .sw_start               =       7,
3949        .fp_start               =       19,
3950        .bp_start               =       31,
3951        .sw_max                 =       256,
3952        .vp_max                 =       4095,
3953        .hp_max                 =       4096,
3954        .mgr_width_start        =       11,
3955        .mgr_height_start       =       27,
3956        .mgr_width_max          =       4096,
3957        .mgr_height_max         =       4096,
3958        .max_lcd_pclk           =       170000000,
3959        .max_tv_pclk            =       186000000,
3960        .calc_scaling           =       dispc_ovl_calc_scaling_44xx,
3961        .calc_core_clk          =       calc_core_clk_44xx,
3962        .num_fifos              =       5,
3963        .gfx_fifo_workaround    =       true,
3964        .mstandby_workaround    =       true,
3965        .set_max_preload        =       true,
3966        .supports_sync_align    =       true,
3967        .has_writeback          =       true,
3968};
3969
3970static const struct dispc_features *dispc_get_features(void)
3971{
3972        switch (omapdss_get_version()) {
3973        case OMAPDSS_VER_OMAP24xx:
3974                return &omap24xx_dispc_feats;
3975
3976        case OMAPDSS_VER_OMAP34xx_ES1:
3977                return &omap34xx_rev1_0_dispc_feats;
3978
3979        case OMAPDSS_VER_OMAP34xx_ES3:
3980        case OMAPDSS_VER_OMAP3630:
3981        case OMAPDSS_VER_AM35xx:
3982        case OMAPDSS_VER_AM43xx:
3983                return &omap34xx_rev3_0_dispc_feats;
3984
3985        case OMAPDSS_VER_OMAP4430_ES1:
3986        case OMAPDSS_VER_OMAP4430_ES2:
3987        case OMAPDSS_VER_OMAP4:
3988                return &omap44xx_dispc_feats;
3989
3990        case OMAPDSS_VER_OMAP5:
3991        case OMAPDSS_VER_DRA7xx:
3992                return &omap54xx_dispc_feats;
3993
3994        default:
3995                return NULL;
3996        }
3997}
3998
3999static irqreturn_t dispc_irq_handler(int irq, void *arg)
4000{
4001        if (!dispc.is_enabled)
4002                return IRQ_NONE;
4003
4004        return dispc.user_handler(irq, dispc.user_data);
4005}
4006
4007int dispc_request_irq(irq_handler_t handler, void *dev_id)
4008{
4009        int r;
4010
4011        if (dispc.user_handler != NULL)
4012                return -EBUSY;
4013
4014        dispc.user_handler = handler;
4015        dispc.user_data = dev_id;
4016
4017        /* ensure the dispc_irq_handler sees the values above */
4018        smp_wmb();
4019
4020        r = devm_request_irq(&dispc.pdev->dev, dispc.irq, dispc_irq_handler,
4021                             IRQF_SHARED, "OMAP DISPC", &dispc);
4022        if (r) {
4023                dispc.user_handler = NULL;
4024                dispc.user_data = NULL;
4025        }
4026
4027        return r;
4028}
4029EXPORT_SYMBOL(dispc_request_irq);
4030
4031void dispc_free_irq(void *dev_id)
4032{
4033        devm_free_irq(&dispc.pdev->dev, dispc.irq, &dispc);
4034
4035        dispc.user_handler = NULL;
4036        dispc.user_data = NULL;
4037}
4038EXPORT_SYMBOL(dispc_free_irq);
4039
4040/* DISPC HW IP initialisation */
4041static int dispc_bind(struct device *dev, struct device *master, void *data)
4042{
4043        struct platform_device *pdev = to_platform_device(dev);
4044        u32 rev;
4045        int r = 0;
4046        struct resource *dispc_mem;
4047        struct device_node *np = pdev->dev.of_node;
4048
4049        dispc.pdev = pdev;
4050
4051        spin_lock_init(&dispc.control_lock);
4052
4053        dispc.feat = dispc_get_features();
4054        if (!dispc.feat)
4055                return -ENODEV;
4056
4057        dispc_mem = platform_get_resource(dispc.pdev, IORESOURCE_MEM, 0);
4058        if (!dispc_mem) {
4059                DSSERR("can't get IORESOURCE_MEM DISPC\n");
4060                return -EINVAL;
4061        }
4062
4063        dispc.base = devm_ioremap(&pdev->dev, dispc_mem->start,
4064                                  resource_size(dispc_mem));
4065        if (!dispc.base) {
4066                DSSERR("can't ioremap DISPC\n");
4067                return -ENOMEM;
4068        }
4069
4070        dispc.irq = platform_get_irq(dispc.pdev, 0);
4071        if (dispc.irq < 0) {
4072                DSSERR("platform_get_irq failed\n");
4073                return -ENODEV;
4074        }
4075
4076        if (np && of_property_read_bool(np, "syscon-pol")) {
4077                dispc.syscon_pol = syscon_regmap_lookup_by_phandle(np, "syscon-pol");
4078                if (IS_ERR(dispc.syscon_pol)) {
4079                        dev_err(&pdev->dev, "failed to get syscon-pol regmap\n");
4080                        return PTR_ERR(dispc.syscon_pol);
4081                }
4082
4083                if (of_property_read_u32_index(np, "syscon-pol", 1,
4084                                &dispc.syscon_pol_offset)) {
4085                        dev_err(&pdev->dev, "failed to get syscon-pol offset\n");
4086                        return -EINVAL;
4087                }
4088        }
4089
4090        pm_runtime_enable(&pdev->dev);
4091
4092        r = dispc_runtime_get();
4093        if (r)
4094                goto err_runtime_get;
4095
4096        _omap_dispc_initial_config();
4097
4098        rev = dispc_read_reg(DISPC_REVISION);
4099        dev_dbg(&pdev->dev, "OMAP DISPC rev %d.%d\n",
4100               FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
4101
4102        dispc_runtime_put();
4103
4104        dss_init_overlay_managers();
4105
4106        dss_debugfs_create_file("dispc", dispc_dump_regs);
4107
4108        return 0;
4109
4110err_runtime_get:
4111        pm_runtime_disable(&pdev->dev);
4112        return r;
4113}
4114
4115static void dispc_unbind(struct device *dev, struct device *master,
4116                               void *data)
4117{
4118        pm_runtime_disable(dev);
4119
4120        dss_uninit_overlay_managers();
4121}
4122
4123static const struct component_ops dispc_component_ops = {
4124        .bind   = dispc_bind,
4125        .unbind = dispc_unbind,
4126};
4127
4128static int dispc_probe(struct platform_device *pdev)
4129{
4130        return component_add(&pdev->dev, &dispc_component_ops);
4131}
4132
4133static int dispc_remove(struct platform_device *pdev)
4134{
4135        component_del(&pdev->dev, &dispc_component_ops);
4136        return 0;
4137}
4138
4139static int dispc_runtime_suspend(struct device *dev)
4140{
4141        dispc.is_enabled = false;
4142        /* ensure the dispc_irq_handler sees the is_enabled value */
4143        smp_wmb();
4144        /* wait for current handler to finish before turning the DISPC off */
4145        synchronize_irq(dispc.irq);
4146
4147        dispc_save_context();
4148
4149        return 0;
4150}
4151
4152static int dispc_runtime_resume(struct device *dev)
4153{
4154        /*
4155         * The reset value for load mode is 0 (OMAP_DSS_LOAD_CLUT_AND_FRAME)
4156         * but we always initialize it to 2 (OMAP_DSS_LOAD_FRAME_ONLY) in
4157         * _omap_dispc_initial_config(). We can thus use it to detect if
4158         * we have lost register context.
4159         */
4160        if (REG_GET(DISPC_CONFIG, 2, 1) != OMAP_DSS_LOAD_FRAME_ONLY) {
4161                _omap_dispc_initial_config();
4162
4163                dispc_restore_context();
4164        }
4165
4166        dispc.is_enabled = true;
4167        /* ensure the dispc_irq_handler sees the is_enabled value */
4168        smp_wmb();
4169
4170        return 0;
4171}
4172
4173static const struct dev_pm_ops dispc_pm_ops = {
4174        .runtime_suspend = dispc_runtime_suspend,
4175        .runtime_resume = dispc_runtime_resume,
4176};
4177
4178static const struct of_device_id dispc_of_match[] = {
4179        { .compatible = "ti,omap2-dispc", },
4180        { .compatible = "ti,omap3-dispc", },
4181        { .compatible = "ti,omap4-dispc", },
4182        { .compatible = "ti,omap5-dispc", },
4183        { .compatible = "ti,dra7-dispc", },
4184        {},
4185};
4186
4187static struct platform_driver omap_dispchw_driver = {
4188        .probe          = dispc_probe,
4189        .remove         = dispc_remove,
4190        .driver         = {
4191                .name   = "omapdss_dispc",
4192                .pm     = &dispc_pm_ops,
4193                .of_match_table = dispc_of_match,
4194                .suppress_bind_attrs = true,
4195        },
4196};
4197
4198int __init dispc_init_platform_driver(void)
4199{
4200        return platform_driver_register(&omap_dispchw_driver);
4201}
4202
4203void dispc_uninit_platform_driver(void)
4204{
4205        platform_driver_unregister(&omap_dispchw_driver);
4206}
4207