linux/drivers/gpu/drm/msm/disp/dpu1/dpu_hw_intf.c
<<
>>
Prefs
   1/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
   2 *
   3 * This program is free software; you can redistribute it and/or modify
   4 * it under the terms of the GNU General Public License version 2 and
   5 * only version 2 as published by the Free Software Foundation.
   6 *
   7 * This program is distributed in the hope that it will be useful,
   8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
   9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  10 * GNU General Public License for more details.
  11 */
  12
  13#include "dpu_hwio.h"
  14#include "dpu_hw_catalog.h"
  15#include "dpu_hw_intf.h"
  16#include "dpu_kms.h"
  17
  18#define INTF_TIMING_ENGINE_EN           0x000
  19#define INTF_CONFIG                     0x004
  20#define INTF_HSYNC_CTL                  0x008
  21#define INTF_VSYNC_PERIOD_F0            0x00C
  22#define INTF_VSYNC_PERIOD_F1            0x010
  23#define INTF_VSYNC_PULSE_WIDTH_F0       0x014
  24#define INTF_VSYNC_PULSE_WIDTH_F1       0x018
  25#define INTF_DISPLAY_V_START_F0         0x01C
  26#define INTF_DISPLAY_V_START_F1         0x020
  27#define INTF_DISPLAY_V_END_F0           0x024
  28#define INTF_DISPLAY_V_END_F1           0x028
  29#define INTF_ACTIVE_V_START_F0          0x02C
  30#define INTF_ACTIVE_V_START_F1          0x030
  31#define INTF_ACTIVE_V_END_F0            0x034
  32#define INTF_ACTIVE_V_END_F1            0x038
  33#define INTF_DISPLAY_HCTL               0x03C
  34#define INTF_ACTIVE_HCTL                0x040
  35#define INTF_BORDER_COLOR               0x044
  36#define INTF_UNDERFLOW_COLOR            0x048
  37#define INTF_HSYNC_SKEW                 0x04C
  38#define INTF_POLARITY_CTL               0x050
  39#define INTF_TEST_CTL                   0x054
  40#define INTF_TP_COLOR0                  0x058
  41#define INTF_TP_COLOR1                  0x05C
  42#define INTF_FRAME_LINE_COUNT_EN        0x0A8
  43#define INTF_FRAME_COUNT                0x0AC
  44#define   INTF_LINE_COUNT               0x0B0
  45
  46#define   INTF_DEFLICKER_CONFIG         0x0F0
  47#define   INTF_DEFLICKER_STRNG_COEFF    0x0F4
  48#define   INTF_DEFLICKER_WEAK_COEFF     0x0F8
  49
  50#define   INTF_DSI_CMD_MODE_TRIGGER_EN  0x084
  51#define   INTF_PANEL_FORMAT             0x090
  52#define   INTF_TPG_ENABLE               0x100
  53#define   INTF_TPG_MAIN_CONTROL         0x104
  54#define   INTF_TPG_VIDEO_CONFIG         0x108
  55#define   INTF_TPG_COMPONENT_LIMITS     0x10C
  56#define   INTF_TPG_RECTANGLE            0x110
  57#define   INTF_TPG_INITIAL_VALUE        0x114
  58#define   INTF_TPG_BLK_WHITE_PATTERN_FRAMES   0x118
  59#define   INTF_TPG_RGB_MAPPING          0x11C
  60#define   INTF_PROG_FETCH_START         0x170
  61#define   INTF_PROG_ROT_START           0x174
  62
  63#define   INTF_FRAME_LINE_COUNT_EN      0x0A8
  64#define   INTF_FRAME_COUNT              0x0AC
  65#define   INTF_LINE_COUNT               0x0B0
  66
  67static struct dpu_intf_cfg *_intf_offset(enum dpu_intf intf,
  68                struct dpu_mdss_cfg *m,
  69                void __iomem *addr,
  70                struct dpu_hw_blk_reg_map *b)
  71{
  72        int i;
  73
  74        for (i = 0; i < m->intf_count; i++) {
  75                if ((intf == m->intf[i].id) &&
  76                (m->intf[i].type != INTF_NONE)) {
  77                        b->base_off = addr;
  78                        b->blk_off = m->intf[i].base;
  79                        b->length = m->intf[i].len;
  80                        b->hwversion = m->hwversion;
  81                        b->log_mask = DPU_DBG_MASK_INTF;
  82                        return &m->intf[i];
  83                }
  84        }
  85
  86        return ERR_PTR(-EINVAL);
  87}
  88
  89static void dpu_hw_intf_setup_timing_engine(struct dpu_hw_intf *ctx,
  90                const struct intf_timing_params *p,
  91                const struct dpu_format *fmt)
  92{
  93        struct dpu_hw_blk_reg_map *c = &ctx->hw;
  94        u32 hsync_period, vsync_period;
  95        u32 display_v_start, display_v_end;
  96        u32 hsync_start_x, hsync_end_x;
  97        u32 active_h_start, active_h_end;
  98        u32 active_v_start, active_v_end;
  99        u32 active_hctl, display_hctl, hsync_ctl;
 100        u32 polarity_ctl, den_polarity, hsync_polarity, vsync_polarity;
 101        u32 panel_format;
 102        u32 intf_cfg;
 103
 104        /* read interface_cfg */
 105        intf_cfg = DPU_REG_READ(c, INTF_CONFIG);
 106        hsync_period = p->hsync_pulse_width + p->h_back_porch + p->width +
 107        p->h_front_porch;
 108        vsync_period = p->vsync_pulse_width + p->v_back_porch + p->height +
 109        p->v_front_porch;
 110
 111        display_v_start = ((p->vsync_pulse_width + p->v_back_porch) *
 112        hsync_period) + p->hsync_skew;
 113        display_v_end = ((vsync_period - p->v_front_porch) * hsync_period) +
 114        p->hsync_skew - 1;
 115
 116        if (ctx->cap->type == INTF_EDP || ctx->cap->type == INTF_DP) {
 117                display_v_start += p->hsync_pulse_width + p->h_back_porch;
 118                display_v_end -= p->h_front_porch;
 119        }
 120
 121        hsync_start_x = p->h_back_porch + p->hsync_pulse_width;
 122        hsync_end_x = hsync_period - p->h_front_porch - 1;
 123
 124        if (p->width != p->xres) {
 125                active_h_start = hsync_start_x;
 126                active_h_end = active_h_start + p->xres - 1;
 127        } else {
 128                active_h_start = 0;
 129                active_h_end = 0;
 130        }
 131
 132        if (p->height != p->yres) {
 133                active_v_start = display_v_start;
 134                active_v_end = active_v_start + (p->yres * hsync_period) - 1;
 135        } else {
 136                active_v_start = 0;
 137                active_v_end = 0;
 138        }
 139
 140        if (active_h_end) {
 141                active_hctl = (active_h_end << 16) | active_h_start;
 142                intf_cfg |= BIT(29);    /* ACTIVE_H_ENABLE */
 143        } else {
 144                active_hctl = 0;
 145        }
 146
 147        if (active_v_end)
 148                intf_cfg |= BIT(30); /* ACTIVE_V_ENABLE */
 149
 150        hsync_ctl = (hsync_period << 16) | p->hsync_pulse_width;
 151        display_hctl = (hsync_end_x << 16) | hsync_start_x;
 152
 153        den_polarity = 0;
 154        if (ctx->cap->type == INTF_HDMI) {
 155                hsync_polarity = p->yres >= 720 ? 0 : 1;
 156                vsync_polarity = p->yres >= 720 ? 0 : 1;
 157        } else {
 158                hsync_polarity = 0;
 159                vsync_polarity = 0;
 160        }
 161        polarity_ctl = (den_polarity << 2) | /*  DEN Polarity  */
 162                (vsync_polarity << 1) | /* VSYNC Polarity */
 163                (hsync_polarity << 0);  /* HSYNC Polarity */
 164
 165        if (!DPU_FORMAT_IS_YUV(fmt))
 166                panel_format = (fmt->bits[C0_G_Y] |
 167                                (fmt->bits[C1_B_Cb] << 2) |
 168                                (fmt->bits[C2_R_Cr] << 4) |
 169                                (0x21 << 8));
 170        else
 171                /* Interface treats all the pixel data in RGB888 format */
 172                panel_format = (COLOR_8BIT |
 173                                (COLOR_8BIT << 2) |
 174                                (COLOR_8BIT << 4) |
 175                                (0x21 << 8));
 176
 177        DPU_REG_WRITE(c, INTF_HSYNC_CTL, hsync_ctl);
 178        DPU_REG_WRITE(c, INTF_VSYNC_PERIOD_F0, vsync_period * hsync_period);
 179        DPU_REG_WRITE(c, INTF_VSYNC_PULSE_WIDTH_F0,
 180                        p->vsync_pulse_width * hsync_period);
 181        DPU_REG_WRITE(c, INTF_DISPLAY_HCTL, display_hctl);
 182        DPU_REG_WRITE(c, INTF_DISPLAY_V_START_F0, display_v_start);
 183        DPU_REG_WRITE(c, INTF_DISPLAY_V_END_F0, display_v_end);
 184        DPU_REG_WRITE(c, INTF_ACTIVE_HCTL,  active_hctl);
 185        DPU_REG_WRITE(c, INTF_ACTIVE_V_START_F0, active_v_start);
 186        DPU_REG_WRITE(c, INTF_ACTIVE_V_END_F0, active_v_end);
 187        DPU_REG_WRITE(c, INTF_BORDER_COLOR, p->border_clr);
 188        DPU_REG_WRITE(c, INTF_UNDERFLOW_COLOR, p->underflow_clr);
 189        DPU_REG_WRITE(c, INTF_HSYNC_SKEW, p->hsync_skew);
 190        DPU_REG_WRITE(c, INTF_POLARITY_CTL, polarity_ctl);
 191        DPU_REG_WRITE(c, INTF_FRAME_LINE_COUNT_EN, 0x3);
 192        DPU_REG_WRITE(c, INTF_CONFIG, intf_cfg);
 193        DPU_REG_WRITE(c, INTF_PANEL_FORMAT, panel_format);
 194}
 195
 196static void dpu_hw_intf_enable_timing_engine(
 197                struct dpu_hw_intf *intf,
 198                u8 enable)
 199{
 200        struct dpu_hw_blk_reg_map *c = &intf->hw;
 201        /* Note: Display interface select is handled in top block hw layer */
 202        DPU_REG_WRITE(c, INTF_TIMING_ENGINE_EN, enable != 0);
 203}
 204
 205static void dpu_hw_intf_setup_prg_fetch(
 206                struct dpu_hw_intf *intf,
 207                const struct intf_prog_fetch *fetch)
 208{
 209        struct dpu_hw_blk_reg_map *c = &intf->hw;
 210        int fetch_enable;
 211
 212        /*
 213         * Fetch should always be outside the active lines. If the fetching
 214         * is programmed within active region, hardware behavior is unknown.
 215         */
 216
 217        fetch_enable = DPU_REG_READ(c, INTF_CONFIG);
 218        if (fetch->enable) {
 219                fetch_enable |= BIT(31);
 220                DPU_REG_WRITE(c, INTF_PROG_FETCH_START,
 221                                fetch->fetch_start);
 222        } else {
 223                fetch_enable &= ~BIT(31);
 224        }
 225
 226        DPU_REG_WRITE(c, INTF_CONFIG, fetch_enable);
 227}
 228
 229static void dpu_hw_intf_get_status(
 230                struct dpu_hw_intf *intf,
 231                struct intf_status *s)
 232{
 233        struct dpu_hw_blk_reg_map *c = &intf->hw;
 234
 235        s->is_en = DPU_REG_READ(c, INTF_TIMING_ENGINE_EN);
 236        if (s->is_en) {
 237                s->frame_count = DPU_REG_READ(c, INTF_FRAME_COUNT);
 238                s->line_count = DPU_REG_READ(c, INTF_LINE_COUNT);
 239        } else {
 240                s->line_count = 0;
 241                s->frame_count = 0;
 242        }
 243}
 244
 245static u32 dpu_hw_intf_get_line_count(struct dpu_hw_intf *intf)
 246{
 247        struct dpu_hw_blk_reg_map *c;
 248
 249        if (!intf)
 250                return 0;
 251
 252        c = &intf->hw;
 253
 254        return DPU_REG_READ(c, INTF_LINE_COUNT);
 255}
 256
 257static void _setup_intf_ops(struct dpu_hw_intf_ops *ops,
 258                unsigned long cap)
 259{
 260        ops->setup_timing_gen = dpu_hw_intf_setup_timing_engine;
 261        ops->setup_prg_fetch  = dpu_hw_intf_setup_prg_fetch;
 262        ops->get_status = dpu_hw_intf_get_status;
 263        ops->enable_timing = dpu_hw_intf_enable_timing_engine;
 264        ops->get_line_count = dpu_hw_intf_get_line_count;
 265}
 266
 267static struct dpu_hw_blk_ops dpu_hw_ops;
 268
 269struct dpu_hw_intf *dpu_hw_intf_init(enum dpu_intf idx,
 270                void __iomem *addr,
 271                struct dpu_mdss_cfg *m)
 272{
 273        struct dpu_hw_intf *c;
 274        struct dpu_intf_cfg *cfg;
 275
 276        c = kzalloc(sizeof(*c), GFP_KERNEL);
 277        if (!c)
 278                return ERR_PTR(-ENOMEM);
 279
 280        cfg = _intf_offset(idx, m, addr, &c->hw);
 281        if (IS_ERR_OR_NULL(cfg)) {
 282                kfree(c);
 283                pr_err("failed to create dpu_hw_intf %d\n", idx);
 284                return ERR_PTR(-EINVAL);
 285        }
 286
 287        /*
 288         * Assign ops
 289         */
 290        c->idx = idx;
 291        c->cap = cfg;
 292        c->mdss = m;
 293        _setup_intf_ops(&c->ops, c->cap->features);
 294
 295        dpu_hw_blk_init(&c->base, DPU_HW_BLK_INTF, idx, &dpu_hw_ops);
 296
 297        return c;
 298}
 299
 300void dpu_hw_intf_destroy(struct dpu_hw_intf *intf)
 301{
 302        if (intf)
 303                dpu_hw_blk_destroy(&intf->base);
 304        kfree(intf);
 305}
 306
 307