1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * OMAP2plus display device setup / initialization. |
4 | * |
5 | * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ |
6 | * Senthilvadivu Guruswamy |
7 | * Sumit Semwal |
8 | */ |
9 | |
10 | #include <linux/string.h> |
11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/io.h> |
15 | #include <linux/clk.h> |
16 | #include <linux/err.h> |
17 | #include <linux/delay.h> |
18 | #include <linux/of.h> |
19 | #include <linux/of_platform.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/mfd/syscon.h> |
22 | #include <linux/regmap.h> |
23 | |
24 | #include <linux/platform_data/omapdss.h> |
25 | #include "omap_hwmod.h" |
26 | #include "omap_device.h" |
27 | #include "common.h" |
28 | |
29 | #include "soc.h" |
30 | #include "iomap.h" |
31 | #include "control.h" |
32 | #include "display.h" |
33 | #include "prm.h" |
34 | |
35 | #define DISPC_CONTROL 0x0040 |
36 | #define DISPC_CONTROL2 0x0238 |
37 | #define DISPC_CONTROL3 0x0848 |
38 | #define DISPC_IRQSTATUS 0x0018 |
39 | |
40 | #define DSS_CONTROL 0x40 |
41 | #define DSS_SDI_CONTROL 0x44 |
42 | #define DSS_PLL_CONTROL 0x48 |
43 | |
44 | #define LCD_EN_MASK (0x1 << 0) |
45 | #define DIGIT_EN_MASK (0x1 << 1) |
46 | |
47 | #define FRAMEDONE_IRQ_SHIFT 0 |
48 | #define EVSYNC_EVEN_IRQ_SHIFT 2 |
49 | #define EVSYNC_ODD_IRQ_SHIFT 3 |
50 | #define FRAMEDONE2_IRQ_SHIFT 22 |
51 | #define FRAMEDONE3_IRQ_SHIFT 30 |
52 | #define FRAMEDONETV_IRQ_SHIFT 24 |
53 | |
54 | /* |
55 | * FRAMEDONE_IRQ_TIMEOUT: how long (in milliseconds) to wait during DISPC |
56 | * reset before deciding that something has gone wrong |
57 | */ |
58 | #define FRAMEDONE_IRQ_TIMEOUT 100 |
59 | |
60 | #if defined(CONFIG_FB_OMAP2) |
61 | static struct platform_device omap_display_device = { |
62 | .name = "omapdss" , |
63 | .id = -1, |
64 | .dev = { |
65 | .platform_data = NULL, |
66 | }, |
67 | }; |
68 | |
69 | #define OMAP4_DSIPHY_SYSCON_OFFSET 0x78 |
70 | |
71 | static struct regmap *omap4_dsi_mux_syscon; |
72 | |
73 | static int omap4_dsi_mux_pads(int dsi_id, unsigned lanes) |
74 | { |
75 | u32 enable_mask, enable_shift; |
76 | u32 pipd_mask, pipd_shift; |
77 | u32 reg; |
78 | int ret; |
79 | |
80 | if (dsi_id == 0) { |
81 | enable_mask = OMAP4_DSI1_LANEENABLE_MASK; |
82 | enable_shift = OMAP4_DSI1_LANEENABLE_SHIFT; |
83 | pipd_mask = OMAP4_DSI1_PIPD_MASK; |
84 | pipd_shift = OMAP4_DSI1_PIPD_SHIFT; |
85 | } else if (dsi_id == 1) { |
86 | enable_mask = OMAP4_DSI2_LANEENABLE_MASK; |
87 | enable_shift = OMAP4_DSI2_LANEENABLE_SHIFT; |
88 | pipd_mask = OMAP4_DSI2_PIPD_MASK; |
89 | pipd_shift = OMAP4_DSI2_PIPD_SHIFT; |
90 | } else { |
91 | return -ENODEV; |
92 | } |
93 | |
94 | ret = regmap_read(map: omap4_dsi_mux_syscon, |
95 | OMAP4_DSIPHY_SYSCON_OFFSET, |
96 | val: ®); |
97 | if (ret) |
98 | return ret; |
99 | |
100 | reg &= ~enable_mask; |
101 | reg &= ~pipd_mask; |
102 | |
103 | reg |= (lanes << enable_shift) & enable_mask; |
104 | reg |= (lanes << pipd_shift) & pipd_mask; |
105 | |
106 | regmap_write(map: omap4_dsi_mux_syscon, OMAP4_DSIPHY_SYSCON_OFFSET, val: reg); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | static int omap_dsi_enable_pads(int dsi_id, unsigned lane_mask) |
112 | { |
113 | if (cpu_is_omap44xx()) |
114 | return omap4_dsi_mux_pads(dsi_id, lanes: lane_mask); |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask) |
120 | { |
121 | if (cpu_is_omap44xx()) |
122 | omap4_dsi_mux_pads(dsi_id, lanes: 0); |
123 | } |
124 | |
125 | static enum omapdss_version __init omap_display_get_version(void) |
126 | { |
127 | if (cpu_is_omap24xx()) |
128 | return OMAPDSS_VER_OMAP24xx; |
129 | else if (cpu_is_omap3630()) |
130 | return OMAPDSS_VER_OMAP3630; |
131 | else if (cpu_is_omap34xx()) { |
132 | if (soc_is_am35xx()) { |
133 | return OMAPDSS_VER_AM35xx; |
134 | } else { |
135 | if (omap_rev() < OMAP3430_REV_ES3_0) |
136 | return OMAPDSS_VER_OMAP34xx_ES1; |
137 | else |
138 | return OMAPDSS_VER_OMAP34xx_ES3; |
139 | } |
140 | } else if (omap_rev() == OMAP4430_REV_ES1_0) |
141 | return OMAPDSS_VER_OMAP4430_ES1; |
142 | else if (omap_rev() == OMAP4430_REV_ES2_0 || |
143 | omap_rev() == OMAP4430_REV_ES2_1 || |
144 | omap_rev() == OMAP4430_REV_ES2_2) |
145 | return OMAPDSS_VER_OMAP4430_ES2; |
146 | else if (cpu_is_omap44xx()) |
147 | return OMAPDSS_VER_OMAP4; |
148 | else if (soc_is_omap54xx()) |
149 | return OMAPDSS_VER_OMAP5; |
150 | else if (soc_is_am43xx()) |
151 | return OMAPDSS_VER_AM43xx; |
152 | else if (soc_is_dra7xx()) |
153 | return OMAPDSS_VER_DRA7xx; |
154 | else |
155 | return OMAPDSS_VER_UNKNOWN; |
156 | } |
157 | |
158 | static int __init omapdss_init_fbdev(void) |
159 | { |
160 | static struct omap_dss_board_info board_data = { |
161 | .dsi_enable_pads = omap_dsi_enable_pads, |
162 | .dsi_disable_pads = omap_dsi_disable_pads, |
163 | }; |
164 | struct device_node *node; |
165 | int r; |
166 | |
167 | board_data.version = omap_display_get_version(); |
168 | if (board_data.version == OMAPDSS_VER_UNKNOWN) { |
169 | pr_err("DSS not supported on this SoC\n" ); |
170 | return -ENODEV; |
171 | } |
172 | |
173 | omap_display_device.dev.platform_data = &board_data; |
174 | |
175 | r = platform_device_register(&omap_display_device); |
176 | if (r < 0) { |
177 | pr_err("Unable to register omapdss device\n" ); |
178 | return r; |
179 | } |
180 | |
181 | /* create vrfb device */ |
182 | r = omap_init_vrfb(); |
183 | if (r < 0) { |
184 | pr_err("Unable to register omapvrfb device\n" ); |
185 | return r; |
186 | } |
187 | |
188 | /* create FB device */ |
189 | r = omap_init_fb(); |
190 | if (r < 0) { |
191 | pr_err("Unable to register omapfb device\n" ); |
192 | return r; |
193 | } |
194 | |
195 | /* create V4L2 display device */ |
196 | r = omap_init_vout(); |
197 | if (r < 0) { |
198 | pr_err("Unable to register omap_vout device\n" ); |
199 | return r; |
200 | } |
201 | |
202 | /* add DSI info for omap4 */ |
203 | node = of_find_node_by_name(NULL, name: "omap4_padconf_global" ); |
204 | if (node) |
205 | omap4_dsi_mux_syscon = syscon_node_to_regmap(np: node); |
206 | of_node_put(node); |
207 | |
208 | return 0; |
209 | } |
210 | |
211 | static const char * const omapdss_compat_names[] __initconst = { |
212 | "ti,omap2-dss" , |
213 | "ti,omap3-dss" , |
214 | "ti,omap4-dss" , |
215 | "ti,omap5-dss" , |
216 | "ti,dra7-dss" , |
217 | }; |
218 | |
219 | static struct device_node * __init omapdss_find_dss_of_node(void) |
220 | { |
221 | struct device_node *node; |
222 | int i; |
223 | |
224 | for (i = 0; i < ARRAY_SIZE(omapdss_compat_names); ++i) { |
225 | node = of_find_compatible_node(NULL, NULL, |
226 | compat: omapdss_compat_names[i]); |
227 | if (node) |
228 | return node; |
229 | } |
230 | |
231 | return NULL; |
232 | } |
233 | |
234 | static int __init omapdss_init_of(void) |
235 | { |
236 | int r; |
237 | struct device_node *node; |
238 | struct platform_device *pdev; |
239 | |
240 | /* only create dss helper devices if dss is enabled in the .dts */ |
241 | |
242 | node = omapdss_find_dss_of_node(); |
243 | if (!node) |
244 | return 0; |
245 | |
246 | if (!of_device_is_available(device: node)) { |
247 | of_node_put(node); |
248 | return 0; |
249 | } |
250 | |
251 | pdev = of_find_device_by_node(np: node); |
252 | |
253 | if (!pdev) { |
254 | pr_err("Unable to find DSS platform device\n" ); |
255 | of_node_put(node); |
256 | return -ENODEV; |
257 | } |
258 | |
259 | r = of_platform_populate(root: node, NULL, NULL, parent: &pdev->dev); |
260 | put_device(dev: &pdev->dev); |
261 | of_node_put(node); |
262 | if (r) { |
263 | pr_err("Unable to populate DSS submodule devices\n" ); |
264 | return r; |
265 | } |
266 | |
267 | return omapdss_init_fbdev(); |
268 | } |
269 | omap_device_initcall(omapdss_init_of); |
270 | #endif /* CONFIG_FB_OMAP2 */ |
271 | |
272 | static void dispc_disable_outputs(void) |
273 | { |
274 | u32 v, irq_mask = 0; |
275 | bool lcd_en, digit_en, lcd2_en = false, lcd3_en = false; |
276 | int i; |
277 | struct omap_dss_dispc_dev_attr *da; |
278 | struct omap_hwmod *oh; |
279 | |
280 | oh = omap_hwmod_lookup("dss_dispc" ); |
281 | if (!oh) { |
282 | WARN(1, "display: could not disable outputs during reset - could not find dss_dispc hwmod\n" ); |
283 | return; |
284 | } |
285 | |
286 | if (!oh->dev_attr) { |
287 | pr_err("display: could not disable outputs during reset due to missing dev_attr\n" ); |
288 | return; |
289 | } |
290 | |
291 | da = (struct omap_dss_dispc_dev_attr *)oh->dev_attr; |
292 | |
293 | /* store value of LCDENABLE and DIGITENABLE bits */ |
294 | v = omap_hwmod_read(oh, DISPC_CONTROL); |
295 | lcd_en = v & LCD_EN_MASK; |
296 | digit_en = v & DIGIT_EN_MASK; |
297 | |
298 | /* store value of LCDENABLE for LCD2 */ |
299 | if (da->manager_count > 2) { |
300 | v = omap_hwmod_read(oh, DISPC_CONTROL2); |
301 | lcd2_en = v & LCD_EN_MASK; |
302 | } |
303 | |
304 | /* store value of LCDENABLE for LCD3 */ |
305 | if (da->manager_count > 3) { |
306 | v = omap_hwmod_read(oh, DISPC_CONTROL3); |
307 | lcd3_en = v & LCD_EN_MASK; |
308 | } |
309 | |
310 | if (!(lcd_en | digit_en | lcd2_en | lcd3_en)) |
311 | return; /* no managers currently enabled */ |
312 | |
313 | /* |
314 | * If any manager was enabled, we need to disable it before |
315 | * DSS clocks are disabled or DISPC module is reset |
316 | */ |
317 | if (lcd_en) |
318 | irq_mask |= 1 << FRAMEDONE_IRQ_SHIFT; |
319 | |
320 | if (digit_en) { |
321 | if (da->has_framedonetv_irq) { |
322 | irq_mask |= 1 << FRAMEDONETV_IRQ_SHIFT; |
323 | } else { |
324 | irq_mask |= 1 << EVSYNC_EVEN_IRQ_SHIFT | |
325 | 1 << EVSYNC_ODD_IRQ_SHIFT; |
326 | } |
327 | } |
328 | |
329 | if (lcd2_en) |
330 | irq_mask |= 1 << FRAMEDONE2_IRQ_SHIFT; |
331 | if (lcd3_en) |
332 | irq_mask |= 1 << FRAMEDONE3_IRQ_SHIFT; |
333 | |
334 | /* |
335 | * clear any previous FRAMEDONE, FRAMEDONETV, |
336 | * EVSYNC_EVEN/ODD, FRAMEDONE2 or FRAMEDONE3 interrupts |
337 | */ |
338 | omap_hwmod_write(irq_mask, oh, DISPC_IRQSTATUS); |
339 | |
340 | /* disable LCD and TV managers */ |
341 | v = omap_hwmod_read(oh, DISPC_CONTROL); |
342 | v &= ~(LCD_EN_MASK | DIGIT_EN_MASK); |
343 | omap_hwmod_write(v, oh, DISPC_CONTROL); |
344 | |
345 | /* disable LCD2 manager */ |
346 | if (da->manager_count > 2) { |
347 | v = omap_hwmod_read(oh, DISPC_CONTROL2); |
348 | v &= ~LCD_EN_MASK; |
349 | omap_hwmod_write(v, oh, DISPC_CONTROL2); |
350 | } |
351 | |
352 | /* disable LCD3 manager */ |
353 | if (da->manager_count > 3) { |
354 | v = omap_hwmod_read(oh, DISPC_CONTROL3); |
355 | v &= ~LCD_EN_MASK; |
356 | omap_hwmod_write(v, oh, DISPC_CONTROL3); |
357 | } |
358 | |
359 | i = 0; |
360 | while ((omap_hwmod_read(oh, DISPC_IRQSTATUS) & irq_mask) != |
361 | irq_mask) { |
362 | i++; |
363 | if (i > FRAMEDONE_IRQ_TIMEOUT) { |
364 | pr_err("didn't get FRAMEDONE1/2/3 or TV interrupt\n" ); |
365 | break; |
366 | } |
367 | mdelay(1); |
368 | } |
369 | } |
370 | |
371 | int omap_dss_reset(struct omap_hwmod *oh) |
372 | { |
373 | struct omap_hwmod_opt_clk *oc; |
374 | int c = 0; |
375 | int i, r; |
376 | |
377 | if (!(oh->class->sysc->sysc_flags & SYSS_HAS_RESET_STATUS)) { |
378 | pr_err("dss_core: hwmod data doesn't contain reset data\n" ); |
379 | return -EINVAL; |
380 | } |
381 | |
382 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) |
383 | clk_prepare_enable(clk: oc->_clk); |
384 | |
385 | dispc_disable_outputs(); |
386 | |
387 | /* clear SDI registers */ |
388 | if (cpu_is_omap3430()) { |
389 | omap_hwmod_write(0x0, oh, DSS_SDI_CONTROL); |
390 | omap_hwmod_write(0x0, oh, DSS_PLL_CONTROL); |
391 | } |
392 | |
393 | /* |
394 | * clear DSS_CONTROL register to switch DSS clock sources to |
395 | * PRCM clock, if any |
396 | */ |
397 | omap_hwmod_write(0x0, oh, DSS_CONTROL); |
398 | |
399 | omap_test_timeout((omap_hwmod_read(oh, oh->class->sysc->syss_offs) |
400 | & SYSS_RESETDONE_MASK), |
401 | MAX_MODULE_SOFTRESET_WAIT, c); |
402 | |
403 | if (c == MAX_MODULE_SOFTRESET_WAIT) |
404 | pr_warn("dss_core: waiting for reset to finish failed\n" ); |
405 | else |
406 | pr_debug("dss_core: softreset done\n" ); |
407 | |
408 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) |
409 | clk_disable_unprepare(clk: oc->_clk); |
410 | |
411 | r = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0; |
412 | |
413 | return r; |
414 | } |
415 | |