uboot/drivers/video/mali_dp.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2016-2018 ARM Ltd.
   4 * Author: Liviu Dudau <liviu.dudau@foss.arm.com>
   5 *
   6 */
   7#define DEBUG
   8#include <common.h>
   9#include <malloc.h>
  10#include <video.h>
  11#include <dm.h>
  12#ifdef CONFIG_DISPLAY
  13#include <display.h>
  14#endif
  15#include <fdtdec.h>
  16#include <asm/global_data.h>
  17#include <asm/io.h>
  18#include <os.h>
  19#include <fdt_support.h>
  20#include <clk.h>
  21#include <dm/device_compat.h>
  22#include <linux/delay.h>
  23#include <linux/sizes.h>
  24
  25#define MALIDP_CORE_ID          0x0018
  26#define MALIDP_REG_BG_COLOR     0x0044
  27#define MALIDP_LAYER_LV1        0x0100
  28#define MALIDP_DC_STATUS        0xc000
  29#define MALIDP_DC_CONTROL       0xc010
  30#define MALIDP_DC_CFG_VALID     0xc014
  31
  32/* offsets inside the modesetting register block */
  33#define MALIDP_H_INTERVALS      0x0000
  34#define MALIDP_V_INTERVALS      0x0004
  35#define MALIDP_SYNC_CONTROL     0x0008
  36#define MALIDP_HV_ACTIVESIZE    0x000c
  37#define MALIDP_OUTPUT_DEPTH     0x001c
  38
  39/* offsets inside the layer register block */
  40#define MALIDP_LAYER_FORMAT     0x0000
  41#define MALIDP_LAYER_CONTROL    0x0004
  42#define MALIDP_LAYER_IN_SIZE    0x000c
  43#define MALIDP_LAYER_CMP_SIZE   0x0010
  44#define MALIDP_LAYER_STRIDE     0x0018
  45#define MALIDP_LAYER_PTR_LOW    0x0024
  46#define MALIDP_LAYER_PTR_HIGH   0x0028
  47
  48/* offsets inside the IRQ control blocks */
  49#define MALIDP_REG_MASKIRQ      0x0008
  50#define MALIDP_REG_CLEARIRQ     0x000c
  51
  52#define M1BITS  0x0001
  53#define M2BITS  0x0003
  54#define M4BITS  0x000f
  55#define M8BITS  0x00ff
  56#define M10BITS 0x03ff
  57#define M12BITS 0x0fff
  58#define M13BITS 0x1fff
  59#define M16BITS 0xffff
  60#define M17BITS 0x1ffff
  61
  62#define MALIDP_H_FRONTPORCH(x)  (((x) & M12BITS) << 0)
  63#define MALIDP_H_BACKPORCH(x)   (((x) & M10BITS) << 16)
  64#define MALIDP_V_FRONTPORCH(x)  (((x) & M12BITS) << 0)
  65#define MALIDP_V_BACKPORCH(x)   (((x) & M8BITS) << 16)
  66#define MALIDP_H_SYNCWIDTH(x)   (((x) & M10BITS) << 0)
  67#define MALIDP_V_SYNCWIDTH(x)   (((x) & M8BITS) << 16)
  68#define MALIDP_H_ACTIVE(x)      (((x) & M13BITS) << 0)
  69#define MALIDP_V_ACTIVE(x)      (((x) & M13BITS) << 16)
  70
  71#define MALIDP_CMP_V_SIZE(x)    (((x) & M13BITS) << 16)
  72#define MALIDP_CMP_H_SIZE(x)    (((x) & M13BITS) << 0)
  73
  74#define MALIDP_IN_V_SIZE(x)     (((x) & M13BITS) << 16)
  75#define MALIDP_IN_H_SIZE(x)     (((x) & M13BITS) << 0)
  76
  77#define MALIDP_DC_CM_CONTROL(x) ((x) & M1BITS) << 16, 1 << 16
  78#define MALIDP_DC_STATUS_GET_CM(reg) (((reg) >> 16) & M1BITS)
  79
  80#define MALIDP_FORMAT_ARGB8888  0x08
  81#define MALIDP_DEFAULT_BG_R 0x0
  82#define MALIDP_DEFAULT_BG_G 0x0
  83#define MALIDP_DEFAULT_BG_B 0x0
  84
  85#define MALIDP_PRODUCT_ID(core_id)      ((u32)(core_id) >> 16)
  86
  87#define MALIDP500       0x500
  88
  89DECLARE_GLOBAL_DATA_PTR;
  90
  91struct malidp_priv {
  92        phys_addr_t base_addr;
  93        phys_addr_t dc_status_addr;
  94        phys_addr_t dc_control_addr;
  95        phys_addr_t cval_addr;
  96        struct udevice *display;        /* display device attached */
  97        struct clk aclk;
  98        struct clk pxlclk;
  99        u16 modeset_regs_offset;
 100        u8 config_bit_shift;
 101        u8 clear_irq;                   /* offset for IRQ clear register */
 102};
 103
 104static const struct video_ops malidp_ops = {
 105};
 106
 107static int malidp_get_hwid(phys_addr_t base_addr)
 108{
 109        int hwid;
 110
 111        /*
 112         * reading from the old CORE_ID offset will always
 113         * return 0x5000000 on DP500
 114         */
 115        hwid = readl(base_addr + MALIDP_CORE_ID);
 116        if (MALIDP_PRODUCT_ID(hwid) == MALIDP500)
 117                return hwid;
 118        /* otherwise try the other gen CORE_ID offset */
 119        hwid = readl(base_addr + MALIDP_DC_STATUS + MALIDP_CORE_ID);
 120
 121        return hwid;
 122}
 123
 124/*
 125 * wait for config mode bit setup to be acted upon by the hardware
 126 */
 127static int malidp_wait_configdone(struct malidp_priv *malidp)
 128{
 129        u32 status, tries = 300;
 130
 131        while (tries--) {
 132                status = readl(malidp->dc_status_addr);
 133                if ((status >> malidp->config_bit_shift) & 1)
 134                        break;
 135                udelay(500);
 136        }
 137
 138        if (!tries)
 139                return -ETIMEDOUT;
 140
 141        return 0;
 142}
 143
 144/*
 145 * signal the hardware to enter configuration mode
 146 */
 147static int malidp_enter_config(struct malidp_priv *malidp)
 148{
 149        setbits_le32(malidp->dc_control_addr, 1 << malidp->config_bit_shift);
 150        return malidp_wait_configdone(malidp);
 151}
 152
 153/*
 154 * signal the hardware to exit configuration mode
 155 */
 156static int malidp_leave_config(struct malidp_priv *malidp)
 157{
 158        clrbits_le32(malidp->dc_control_addr, 1 << malidp->config_bit_shift);
 159        return malidp_wait_configdone(malidp);
 160}
 161
 162static void malidp_setup_timings(struct malidp_priv *malidp,
 163                                 struct display_timing *timings)
 164{
 165        u32 val = MALIDP_H_SYNCWIDTH(timings->hsync_len.typ) |
 166                  MALIDP_V_SYNCWIDTH(timings->vsync_len.typ);
 167        writel(val, malidp->base_addr + malidp->modeset_regs_offset +
 168               MALIDP_SYNC_CONTROL);
 169        val = MALIDP_H_BACKPORCH(timings->hback_porch.typ) |
 170                MALIDP_H_FRONTPORCH(timings->hfront_porch.typ);
 171        writel(val, malidp->base_addr + malidp->modeset_regs_offset +
 172               MALIDP_H_INTERVALS);
 173        val = MALIDP_V_BACKPORCH(timings->vback_porch.typ) |
 174                MALIDP_V_FRONTPORCH(timings->vfront_porch.typ);
 175        writel(val, malidp->base_addr + malidp->modeset_regs_offset +
 176               MALIDP_V_INTERVALS);
 177        val = MALIDP_H_ACTIVE(timings->hactive.typ) |
 178                MALIDP_V_ACTIVE(timings->vactive.typ);
 179        writel(val, malidp->base_addr + malidp->modeset_regs_offset +
 180               MALIDP_HV_ACTIVESIZE);
 181        /* default output bit-depth per colour is 8 bits */
 182        writel(0x080808, malidp->base_addr + malidp->modeset_regs_offset +
 183               MALIDP_OUTPUT_DEPTH);
 184}
 185
 186static int malidp_setup_mode(struct malidp_priv *malidp,
 187                             struct display_timing *timings)
 188{
 189        int err;
 190
 191        if (clk_set_rate(&malidp->pxlclk, timings->pixelclock.typ) == 0)
 192                return -EIO;
 193
 194        malidp_setup_timings(malidp, timings);
 195
 196        err = display_enable(malidp->display, 8, timings);
 197        if (err)
 198                printf("display_enable failed with %d\n", err);
 199
 200        return err;
 201}
 202
 203static void malidp_setup_layer(struct malidp_priv *malidp,
 204                               struct display_timing *timings,
 205                               u32 layer_offset, phys_addr_t fb_addr)
 206{
 207        u32 val;
 208
 209        /* setup the base layer's pixel format to A8R8G8B8 */
 210        writel(MALIDP_FORMAT_ARGB8888, malidp->base_addr + layer_offset +
 211               MALIDP_LAYER_FORMAT);
 212        /* setup layer composition size */
 213        val = MALIDP_CMP_V_SIZE(timings->vactive.typ) |
 214                MALIDP_CMP_H_SIZE(timings->hactive.typ);
 215        writel(val, malidp->base_addr + layer_offset +
 216               MALIDP_LAYER_CMP_SIZE);
 217        /* setup layer input size */
 218        val = MALIDP_IN_V_SIZE(timings->vactive.typ) |
 219                MALIDP_IN_H_SIZE(timings->hactive.typ);
 220        writel(val, malidp->base_addr + layer_offset + MALIDP_LAYER_IN_SIZE);
 221        /* setup layer stride in bytes */
 222        writel(timings->hactive.typ << 2, malidp->base_addr + layer_offset +
 223               MALIDP_LAYER_STRIDE);
 224        /* set framebuffer address */
 225        writel(lower_32_bits(fb_addr), malidp->base_addr + layer_offset +
 226               MALIDP_LAYER_PTR_LOW);
 227        writel(upper_32_bits(fb_addr), malidp->base_addr + layer_offset +
 228               MALIDP_LAYER_PTR_HIGH);
 229        /* enable layer */
 230        setbits_le32(malidp->base_addr + layer_offset +
 231                     MALIDP_LAYER_CONTROL, 1);
 232}
 233
 234static void malidp_set_configvalid(struct malidp_priv *malidp)
 235{
 236        setbits_le32(malidp->cval_addr, 1);
 237}
 238
 239static int malidp_update_timings_from_edid(struct udevice *dev,
 240                                           struct display_timing *timings)
 241{
 242#ifdef CONFIG_DISPLAY
 243        struct malidp_priv *priv = dev_get_priv(dev);
 244        struct udevice *disp_dev;
 245        int err;
 246
 247        err = uclass_first_device(UCLASS_DISPLAY, &disp_dev);
 248        if (err)
 249                return err;
 250
 251        priv->display = disp_dev;
 252
 253        err = display_read_timing(disp_dev, timings);
 254        if (err)
 255                return err;
 256
 257#endif
 258        return 0;
 259}
 260
 261static int malidp_probe(struct udevice *dev)
 262{
 263        struct video_priv *uc_priv = dev_get_uclass_priv(dev);
 264        struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
 265        ofnode framebuffer = ofnode_find_subnode(dev_ofnode(dev), "framebuffer");
 266        struct malidp_priv *priv = dev_get_priv(dev);
 267        struct display_timing timings;
 268        phys_addr_t fb_base, fb_size;
 269        const char *format;
 270        u32 value;
 271        int err;
 272
 273        if (!ofnode_valid(framebuffer))
 274                return -EINVAL;
 275
 276        err = clk_get_by_name(dev, "pxlclk", &priv->pxlclk);
 277        if (err) {
 278                dev_err(dev, "failed to get pixel clock\n");
 279                return err;
 280        }
 281        err = clk_get_by_name(dev, "aclk", &priv->aclk);
 282        if (err) {
 283                dev_err(dev, "failed to get AXI clock\n");
 284                goto fail_aclk;
 285        }
 286
 287        err = ofnode_decode_display_timing(dev_ofnode(dev), 1, &timings);
 288        if (err) {
 289                dev_err(dev, "failed to get any display timings\n");
 290                goto fail_timings;
 291        }
 292
 293        err = malidp_update_timings_from_edid(dev, &timings);
 294        if (err) {
 295                printf("malidp_update_timings_from_edid failed: %d\n", err);
 296                goto fail_timings;
 297        }
 298
 299        fb_base = ofnode_get_addr_size(framebuffer, "reg", &fb_size);
 300        if (fb_base != FDT_ADDR_T_NONE) {
 301                uc_plat->base = fb_base;
 302                uc_plat->size = fb_size;
 303        } else {
 304                printf("cannot get address size for framebuffer\n");
 305        }
 306
 307        err = ofnode_read_u32(framebuffer, "width", &value);
 308        if (err)
 309                goto fail_timings;
 310        uc_priv->xsize = (ushort)value;
 311
 312        err = ofnode_read_u32(framebuffer, "height", &value);
 313        if (err)
 314                goto fail_timings;
 315        uc_priv->ysize = (ushort)value;
 316
 317        format = ofnode_read_string(framebuffer, "format");
 318        if (!format) {
 319                err = -EINVAL;
 320                goto fail_timings;
 321        } else if (!strncmp(format, "a8r8g8b8", 8)) {
 322                uc_priv->bpix = VIDEO_BPP32;
 323        }
 324
 325        uc_priv->rot = 0;
 326        priv->base_addr = (phys_addr_t)dev_read_addr(dev);
 327
 328        clk_enable(&priv->pxlclk);
 329        clk_enable(&priv->aclk);
 330
 331        value = malidp_get_hwid(priv->base_addr);
 332        printf("Display: Arm Mali DP%3x r%dp%d\n", MALIDP_PRODUCT_ID(value),
 333               (value >> 12) & 0xf, (value >> 8) & 0xf);
 334
 335        if (MALIDP_PRODUCT_ID(value) == MALIDP500) {
 336                /* DP500 is special */
 337                priv->modeset_regs_offset = 0x28;
 338                priv->dc_status_addr = priv->base_addr;
 339                priv->dc_control_addr = priv->base_addr + 0xc;
 340                priv->cval_addr = priv->base_addr + 0xf00;
 341                priv->config_bit_shift = 17;
 342                priv->clear_irq = 0;
 343        } else {
 344                priv->modeset_regs_offset = 0x30;
 345                priv->dc_status_addr = priv->base_addr + MALIDP_DC_STATUS;
 346                priv->dc_control_addr = priv->base_addr + MALIDP_DC_CONTROL;
 347                priv->cval_addr = priv->base_addr + MALIDP_DC_CFG_VALID;
 348                priv->config_bit_shift = 16;
 349                priv->clear_irq = MALIDP_REG_CLEARIRQ;
 350        }
 351
 352        /* enter config mode */
 353        err  = malidp_enter_config(priv);
 354        if (err)
 355                return err;
 356
 357        /* disable interrupts */
 358        writel(0, priv->dc_status_addr + MALIDP_REG_MASKIRQ);
 359        writel(0xffffffff, priv->dc_status_addr + priv->clear_irq);
 360
 361        err = malidp_setup_mode(priv, &timings);
 362        if (err)
 363                goto fail_timings;
 364
 365        malidp_setup_layer(priv, &timings, MALIDP_LAYER_LV1,
 366                           (phys_addr_t)uc_plat->base);
 367
 368        err = malidp_leave_config(priv);
 369        if (err)
 370                goto fail_timings;
 371
 372        malidp_set_configvalid(priv);
 373
 374        return 0;
 375
 376fail_timings:
 377        clk_free(&priv->aclk);
 378fail_aclk:
 379        clk_free(&priv->pxlclk);
 380
 381        return err;
 382}
 383
 384static int malidp_bind(struct udevice *dev)
 385{
 386        struct video_uc_plat *uc_plat = dev_get_uclass_plat(dev);
 387
 388        /* choose max possible size: 2K x 2K, XRGB888 framebuffer */
 389        uc_plat->size = 4 * 2048 * 2048;
 390
 391        return 0;
 392}
 393
 394static const struct udevice_id malidp_ids[] = {
 395        { .compatible = "arm,mali-dp500" },
 396        { .compatible = "arm,mali-dp550" },
 397        { .compatible = "arm,mali-dp650" },
 398        { }
 399};
 400
 401U_BOOT_DRIVER(mali_dp) = {
 402        .name           = "mali_dp",
 403        .id             = UCLASS_VIDEO,
 404        .of_match       = malidp_ids,
 405        .bind           = malidp_bind,
 406        .probe          = malidp_probe,
 407        .priv_auto      = sizeof(struct malidp_priv),
 408        .ops            = &malidp_ops,
 409};
 410