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 <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 | |
15 | struct armada510_variant_data { |
16 | struct clk *clks[4]; |
17 | struct clk *sel_clk; |
18 | }; |
19 | |
20 | static 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, size: 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 | else |
47 | continue; |
48 | |
49 | clk = devm_clk_get(dev, id: s); |
50 | if (IS_ERR(ptr: clk)) |
51 | return PTR_ERR(ptr: clk) == -ENOENT ? -EPROBE_DEFER : |
52 | PTR_ERR(ptr: clk); |
53 | v->clks[idx] = clk; |
54 | } |
55 | } else { |
56 | clk = devm_clk_get(dev, id: "ext_ref_clk1" ); |
57 | if (IS_ERR(ptr: clk)) |
58 | return PTR_ERR(ptr: clk) == -ENOENT ? -EPROBE_DEFER : |
59 | PTR_ERR(ptr: 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 external |
67 | * clock. |
68 | */ |
69 | armada_updatel(CFG_DMA_WM(0x20), mask: CFG_SRAM_WAIT | CFG_DMA_WM_MASK, |
70 | ptr: 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 | |
79 | static const u32 armada510_clk_sels[] = { |
80 | SCLK_510_EXTCLK0, |
81 | SCLK_510_EXTCLK1, |
82 | SCLK_510_PLL, |
83 | SCLK_510_AXI, |
84 | }; |
85 | |
86 | static 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 is |
97 | * supportable, and again with sclk != NULL to set the clocks up for |
98 | * that. The former can return an error, but the latter is expected |
99 | * not to. |
100 | */ |
101 | static 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: &res, params: &armada510_clocking, |
110 | clks: v->clks, ARRAY_SIZE(v->clks), |
111 | desired_khz); |
112 | if (idx < 0) |
113 | return idx; |
114 | |
115 | ret = clk_prepare_enable(clk: res.clk); |
116 | if (ret) |
117 | return ret; |
118 | |
119 | if (sclk) { |
120 | clk_set_rate(clk: res.clk, rate: 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(clk: res.clk); |
130 | |
131 | return 0; |
132 | } |
133 | |
134 | static void armada510_crtc_disable(struct armada_crtc *dcrtc) |
135 | { |
136 | if (dcrtc->clk) { |
137 | clk_disable_unprepare(clk: dcrtc->clk); |
138 | dcrtc->clk = NULL; |
139 | } |
140 | } |
141 | |
142 | static 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 | |
153 | const 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 | |