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