linux/drivers/gpu/drm/armada/armada_510.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2012 Russell King
   4 *
   5 * Armada 510 (aka Dove) variant support
   6 */
   7#include <linux/clk.h>
   8#include <linux/io.h>
   9#include <drm/drm_probe_helper.h>
  10#include "armada_crtc.h"
  11#include "armada_drm.h"
  12#include "armada_hw.h"
  13
  14static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
  15{
  16        struct clk *clk;
  17
  18        clk = devm_clk_get(dev, "ext_ref_clk1");
  19        if (IS_ERR(clk))
  20                return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk);
  21
  22        dcrtc->extclk[0] = clk;
  23
  24        /* Lower the watermark so to eliminate jitter at higher bandwidths */
  25        armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
  26
  27        /* Initialise SPU register */
  28        writel_relaxed(ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
  29                       dcrtc->base + LCD_SPU_ADV_REG);
  30
  31        return 0;
  32}
  33
  34/*
  35 * Armada510 specific SCLK register selection.
  36 * This gets called with sclk = NULL to test whether the mode is
  37 * supportable, and again with sclk != NULL to set the clocks up for
  38 * that.  The former can return an error, but the latter is expected
  39 * not to.
  40 *
  41 * We currently are pretty rudimentary here, always selecting
  42 * EXT_REF_CLK_1 for LCD0 and erroring LCD1.  This needs improvement!
  43 */
  44static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
  45        const struct drm_display_mode *mode, uint32_t *sclk)
  46{
  47        struct clk *clk = dcrtc->extclk[0];
  48        int ret;
  49
  50        if (dcrtc->num == 1)
  51                return -EINVAL;
  52
  53        if (IS_ERR(clk))
  54                return PTR_ERR(clk);
  55
  56        if (dcrtc->clk != clk) {
  57                ret = clk_prepare_enable(clk);
  58                if (ret)
  59                        return ret;
  60                dcrtc->clk = clk;
  61        }
  62
  63        if (sclk) {
  64                uint32_t rate, ref, div;
  65
  66                rate = mode->clock * 1000;
  67                ref = clk_round_rate(clk, rate);
  68                div = DIV_ROUND_UP(ref, rate);
  69                if (div < 1)
  70                        div = 1;
  71
  72                clk_set_rate(clk, ref);
  73                *sclk = div | SCLK_510_EXTCLK1;
  74        }
  75
  76        return 0;
  77}
  78
  79static void armada510_crtc_disable(struct armada_crtc *dcrtc)
  80{
  81        if (!IS_ERR(dcrtc->clk)) {
  82                clk_disable_unprepare(dcrtc->clk);
  83                dcrtc->clk = ERR_PTR(-EINVAL);
  84        }
  85}
  86
  87static void armada510_crtc_enable(struct armada_crtc *dcrtc,
  88        const struct drm_display_mode *mode)
  89{
  90        if (IS_ERR(dcrtc->clk)) {
  91                dcrtc->clk = dcrtc->extclk[0];
  92                WARN_ON(clk_prepare_enable(dcrtc->clk));
  93        }
  94}
  95
  96const struct armada_variant armada510_ops = {
  97        .has_spu_adv_reg = true,
  98        .init = armada510_crtc_init,
  99        .compute_clock = armada510_crtc_compute_clock,
 100        .disable = armada510_crtc_disable,
 101        .enable = armada510_crtc_enable,
 102};
 103