160 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2012 Russell King4 *5 * Armada 510 (aka Dove) variant support6 */7#include <linux/clk.h>8#include <linux/io.h>9#include <linux/of.h>10#include <drm/drm_probe_helper.h>11#include "armada_crtc.h"12#include "armada_drm.h"13#include "armada_hw.h"14 15struct armada510_variant_data {16 struct clk *clks[4];17 struct clk *sel_clk;18};19 20static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)21{22 struct armada510_variant_data *v;23 struct clk *clk;24 int idx;25 26 v = devm_kzalloc(dev, sizeof(*v), GFP_KERNEL);27 if (!v)28 return -ENOMEM;29 30 dcrtc->variant_data = v;31 32 if (dev->of_node) {33 struct property *prop;34 const char *s;35 36 of_property_for_each_string(dev->of_node, "clock-names", prop,37 s) {38 if (!strcmp(s, "ext_ref_clk0"))39 idx = 0;40 else if (!strcmp(s, "ext_ref_clk1"))41 idx = 1;42 else if (!strcmp(s, "plldivider"))43 idx = 2;44 else if (!strcmp(s, "axibus"))45 idx = 3;46 else47 continue;48 49 clk = devm_clk_get(dev, s);50 if (IS_ERR(clk))51 return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER :52 PTR_ERR(clk);53 v->clks[idx] = clk;54 }55 } else {56 clk = devm_clk_get(dev, "ext_ref_clk1");57 if (IS_ERR(clk))58 return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER :59 PTR_ERR(clk);60 61 v->clks[1] = clk;62 }63 64 /*65 * Lower the watermark so to eliminate jitter at higher bandwidths.66 * Disable SRAM read wait state to avoid system hang with external67 * clock.68 */69 armada_updatel(CFG_DMA_WM(0x20), CFG_SRAM_WAIT | CFG_DMA_WM_MASK,70 dcrtc->base + LCD_CFG_RDREG4F);71 72 /* Initialise SPU register */73 writel_relaxed(ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,74 dcrtc->base + LCD_SPU_ADV_REG);75 76 return 0;77}78 79static const u32 armada510_clk_sels[] = {80 SCLK_510_EXTCLK0,81 SCLK_510_EXTCLK1,82 SCLK_510_PLL,83 SCLK_510_AXI,84};85 86static const struct armada_clocking_params armada510_clocking = {87 /* HDMI requires -0.6%..+0.5% */88 .permillage_min = 994,89 .permillage_max = 1005,90 .settable = BIT(0) | BIT(1),91 .div_max = SCLK_510_INT_DIV_MASK,92};93 94/*95 * Armada510 specific SCLK register selection.96 * This gets called with sclk = NULL to test whether the mode is97 * supportable, and again with sclk != NULL to set the clocks up for98 * that. The former can return an error, but the latter is expected99 * not to.100 */101static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,102 const struct drm_display_mode *mode, uint32_t *sclk)103{104 struct armada510_variant_data *v = dcrtc->variant_data;105 unsigned long desired_khz = mode->crtc_clock;106 struct armada_clk_result res;107 int ret, idx;108 109 idx = armada_crtc_select_clock(dcrtc, &res, &armada510_clocking,110 v->clks, ARRAY_SIZE(v->clks),111 desired_khz);112 if (idx < 0)113 return idx;114 115 ret = clk_prepare_enable(res.clk);116 if (ret)117 return ret;118 119 if (sclk) {120 clk_set_rate(res.clk, res.desired_clk_hz);121 122 *sclk = res.div | armada510_clk_sels[idx];123 124 /* We are now using this clock */125 v->sel_clk = res.clk;126 swap(dcrtc->clk, res.clk);127 }128 129 clk_disable_unprepare(res.clk);130 131 return 0;132}133 134static void armada510_crtc_disable(struct armada_crtc *dcrtc)135{136 if (dcrtc->clk) {137 clk_disable_unprepare(dcrtc->clk);138 dcrtc->clk = NULL;139 }140}141 142static void armada510_crtc_enable(struct armada_crtc *dcrtc,143 const struct drm_display_mode *mode)144{145 struct armada510_variant_data *v = dcrtc->variant_data;146 147 if (!dcrtc->clk && v->sel_clk) {148 if (!WARN_ON(clk_prepare_enable(v->sel_clk)))149 dcrtc->clk = v->sel_clk;150 }151}152 153const struct armada_variant armada510_ops = {154 .has_spu_adv_reg = true,155 .init = armada510_crtc_init,156 .compute_clock = armada510_crtc_compute_clock,157 .disable = armada510_crtc_disable,158 .enable = armada510_crtc_enable,159};160