Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: GPL-2.0-only
2
3/*
4 * Versatile family (ARM reference designs) handling for the PL11x.
5 * This is based on code and know-how in the previous frame buffer
6 * driver in drivers/video/fbdev/amba-clcd.c:
7 * Copyright (C) 2001 ARM Limited, by David A Rusling
8 * Updated to 2.5 by Deep Blue Solutions Ltd.
9 * Major contributions and discoveries by Russell King.
10 */
11
12#include <linux/bitops.h>
13#include <linux/device.h>
14#include <linux/mfd/syscon.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/of_platform.h>
18#include <linux/platform_device.h>
19#include <linux/regmap.h>
20#include <linux/vexpress.h>
21
22#include <drm/drm_fourcc.h>
23#include <drm/drm_print.h>
24
25#include "pl111_versatile.h"
26#include "pl111_drm.h"
27
28static struct regmap *versatile_syscon_map;
29
30/*
31 * We detect the different syscon types from the compatible strings.
32 */
33enum versatile_clcd {
34 INTEGRATOR_IMPD1,
35 INTEGRATOR_CLCD_CM,
36 VERSATILE_CLCD,
37 REALVIEW_CLCD_EB,
38 REALVIEW_CLCD_PB1176,
39 REALVIEW_CLCD_PB11MP,
40 REALVIEW_CLCD_PBA8,
41 REALVIEW_CLCD_PBX,
42 VEXPRESS_CLCD_V2M,
43};
44
45static const struct of_device_id versatile_clcd_of_match[] = {
46 {
47 .compatible = "arm,core-module-integrator",
48 .data = (void *)INTEGRATOR_CLCD_CM,
49 },
50 {
51 .compatible = "arm,versatile-sysreg",
52 .data = (void *)VERSATILE_CLCD,
53 },
54 {
55 .compatible = "arm,realview-eb-syscon",
56 .data = (void *)REALVIEW_CLCD_EB,
57 },
58 {
59 .compatible = "arm,realview-pb1176-syscon",
60 .data = (void *)REALVIEW_CLCD_PB1176,
61 },
62 {
63 .compatible = "arm,realview-pb11mp-syscon",
64 .data = (void *)REALVIEW_CLCD_PB11MP,
65 },
66 {
67 .compatible = "arm,realview-pba8-syscon",
68 .data = (void *)REALVIEW_CLCD_PBA8,
69 },
70 {
71 .compatible = "arm,realview-pbx-syscon",
72 .data = (void *)REALVIEW_CLCD_PBX,
73 },
74 {
75 .compatible = "arm,vexpress-muxfpga",
76 .data = (void *)VEXPRESS_CLCD_V2M,
77 },
78 {},
79};
80
81static const struct of_device_id impd1_clcd_of_match[] = {
82 {
83 .compatible = "arm,im-pd1-syscon",
84 .data = (void *)INTEGRATOR_IMPD1,
85 },
86 {},
87};
88
89/*
90 * Core module CLCD control on the Integrator/CP, bits
91 * 8 thru 19 of the CM_CONTROL register controls a bunch
92 * of CLCD settings.
93 */
94#define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
95#define INTEGRATOR_CLCD_LCDBIASEN BIT(8)
96#define INTEGRATOR_CLCD_LCDBIASUP BIT(9)
97#define INTEGRATOR_CLCD_LCDBIASDN BIT(10)
98/* Bits 11,12,13 controls the LCD or VGA bridge type */
99#define INTEGRATOR_CLCD_LCDMUX_LCD24 BIT(11)
100#define INTEGRATOR_CLCD_LCDMUX_SHARP (BIT(11)|BIT(12))
101#define INTEGRATOR_CLCD_LCDMUX_VGA555 BIT(13)
102#define INTEGRATOR_CLCD_LCDMUX_VGA24 (BIT(11)|BIT(12)|BIT(13))
103#define INTEGRATOR_CLCD_LCD0_EN BIT(14)
104#define INTEGRATOR_CLCD_LCD1_EN BIT(15)
105/* R/L flip on Sharp */
106#define INTEGRATOR_CLCD_LCD_STATIC1 BIT(16)
107/* U/D flip on Sharp */
108#define INTEGRATOR_CLCD_LCD_STATIC2 BIT(17)
109/* No connection on Sharp */
110#define INTEGRATOR_CLCD_LCD_STATIC BIT(18)
111/* 0 = 24bit VGA, 1 = 18bit VGA */
112#define INTEGRATOR_CLCD_LCD_N24BITEN BIT(19)
113
114#define INTEGRATOR_CLCD_MASK GENMASK(19, 8)
115
116static void pl111_integrator_enable(struct drm_device *drm, u32 format)
117{
118 u32 val;
119
120 drm_info(drm, "enable Integrator CLCD connectors\n");
121
122 /* FIXME: really needed? */
123 val = INTEGRATOR_CLCD_LCD_STATIC1 | INTEGRATOR_CLCD_LCD_STATIC2 |
124 INTEGRATOR_CLCD_LCD0_EN | INTEGRATOR_CLCD_LCD1_EN;
125
126 switch (format) {
127 case DRM_FORMAT_XBGR8888:
128 case DRM_FORMAT_XRGB8888:
129 /* 24bit formats */
130 val |= INTEGRATOR_CLCD_LCDMUX_VGA24;
131 break;
132 case DRM_FORMAT_XBGR1555:
133 case DRM_FORMAT_XRGB1555:
134 /* Pseudocolor, RGB555, BGR555 */
135 val |= INTEGRATOR_CLCD_LCDMUX_VGA555;
136 break;
137 default:
138 drm_err(drm, "unhandled format on Integrator 0x%08x\n",
139 format);
140 break;
141 }
142
143 regmap_update_bits(versatile_syscon_map,
144 INTEGRATOR_HDR_CTRL_OFFSET,
145 INTEGRATOR_CLCD_MASK,
146 val);
147}
148
149#define IMPD1_CTRL_OFFSET 0x18
150#define IMPD1_CTRL_DISP_LCD (0 << 0)
151#define IMPD1_CTRL_DISP_VGA (1 << 0)
152#define IMPD1_CTRL_DISP_LCD1 (2 << 0)
153#define IMPD1_CTRL_DISP_ENABLE (1 << 2)
154#define IMPD1_CTRL_DISP_MASK (7 << 0)
155
156static void pl111_impd1_enable(struct drm_device *drm, u32 format)
157{
158 u32 val;
159
160 drm_info(drm, "enable IM-PD1 CLCD connectors\n");
161 val = IMPD1_CTRL_DISP_VGA | IMPD1_CTRL_DISP_ENABLE;
162
163 regmap_update_bits(versatile_syscon_map,
164 IMPD1_CTRL_OFFSET,
165 IMPD1_CTRL_DISP_MASK,
166 val);
167}
168
169static void pl111_impd1_disable(struct drm_device *drm)
170{
171 drm_info(drm, "disable IM-PD1 CLCD connectors\n");
172
173 regmap_update_bits(versatile_syscon_map,
174 IMPD1_CTRL_OFFSET,
175 IMPD1_CTRL_DISP_MASK,
176 0);
177}
178
179/*
180 * This configuration register in the Versatile and RealView
181 * family is uniformly present but appears more and more
182 * unutilized starting with the RealView series.
183 */
184#define SYS_CLCD 0x50
185#define SYS_CLCD_MODE_MASK (BIT(0)|BIT(1))
186#define SYS_CLCD_MODE_888 0
187#define SYS_CLCD_MODE_5551 BIT(0)
188#define SYS_CLCD_MODE_565_R_LSB BIT(1)
189#define SYS_CLCD_MODE_565_B_LSB (BIT(0)|BIT(1))
190#define SYS_CLCD_CONNECTOR_MASK (BIT(2)|BIT(3)|BIT(4)|BIT(5))
191#define SYS_CLCD_NLCDIOON BIT(2)
192#define SYS_CLCD_VDDPOSSWITCH BIT(3)
193#define SYS_CLCD_PWR3V5SWITCH BIT(4)
194#define SYS_CLCD_VDDNEGSWITCH BIT(5)
195
196static void pl111_versatile_disable(struct drm_device *drm)
197{
198 drm_info(drm, "disable Versatile CLCD connectors\n");
199 regmap_update_bits(versatile_syscon_map,
200 SYS_CLCD,
201 SYS_CLCD_CONNECTOR_MASK,
202 0);
203}
204
205static void pl111_versatile_enable(struct drm_device *drm, u32 format)
206{
207 u32 val = 0;
208
209 drm_info(drm, "enable Versatile CLCD connectors\n");
210
211 switch (format) {
212 case DRM_FORMAT_ABGR8888:
213 case DRM_FORMAT_XBGR8888:
214 case DRM_FORMAT_ARGB8888:
215 case DRM_FORMAT_XRGB8888:
216 val |= SYS_CLCD_MODE_888;
217 break;
218 case DRM_FORMAT_BGR565:
219 val |= SYS_CLCD_MODE_565_R_LSB;
220 break;
221 case DRM_FORMAT_RGB565:
222 val |= SYS_CLCD_MODE_565_B_LSB;
223 break;
224 case DRM_FORMAT_ABGR1555:
225 case DRM_FORMAT_XBGR1555:
226 case DRM_FORMAT_ARGB1555:
227 case DRM_FORMAT_XRGB1555:
228 val |= SYS_CLCD_MODE_5551;
229 break;
230 default:
231 drm_err(drm, "unhandled format on Versatile 0x%08x\n",
232 format);
233 break;
234 }
235
236 /* Set up the MUX */
237 regmap_update_bits(versatile_syscon_map,
238 SYS_CLCD,
239 SYS_CLCD_MODE_MASK,
240 val);
241
242 /* Then enable the display */
243 regmap_update_bits(versatile_syscon_map,
244 SYS_CLCD,
245 SYS_CLCD_CONNECTOR_MASK,
246 SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
247}
248
249static void pl111_realview_clcd_disable(struct drm_device *drm)
250{
251 drm_info(drm, "disable RealView CLCD connectors\n");
252 regmap_update_bits(versatile_syscon_map,
253 SYS_CLCD,
254 SYS_CLCD_CONNECTOR_MASK,
255 0);
256}
257
258static void pl111_realview_clcd_enable(struct drm_device *drm, u32 format)
259{
260 drm_info(drm, "enable RealView CLCD connectors\n");
261 regmap_update_bits(versatile_syscon_map,
262 SYS_CLCD,
263 SYS_CLCD_CONNECTOR_MASK,
264 SYS_CLCD_NLCDIOON | SYS_CLCD_PWR3V5SWITCH);
265}
266
267/* PL110 pixel formats for Integrator, vanilla PL110 */
268static const u32 pl110_integrator_pixel_formats[] = {
269 DRM_FORMAT_ABGR8888,
270 DRM_FORMAT_XBGR8888,
271 DRM_FORMAT_ARGB8888,
272 DRM_FORMAT_XRGB8888,
273 DRM_FORMAT_ABGR1555,
274 DRM_FORMAT_XBGR1555,
275 DRM_FORMAT_ARGB1555,
276 DRM_FORMAT_XRGB1555,
277};
278
279/* Extended PL110 pixel formats for Integrator and Versatile */
280static const u32 pl110_versatile_pixel_formats[] = {
281 DRM_FORMAT_ABGR8888,
282 DRM_FORMAT_XBGR8888,
283 DRM_FORMAT_ARGB8888,
284 DRM_FORMAT_XRGB8888,
285 DRM_FORMAT_BGR565, /* Uses external PLD */
286 DRM_FORMAT_RGB565, /* Uses external PLD */
287 DRM_FORMAT_ABGR1555,
288 DRM_FORMAT_XBGR1555,
289 DRM_FORMAT_ARGB1555,
290 DRM_FORMAT_XRGB1555,
291};
292
293static const u32 pl111_realview_pixel_formats[] = {
294 DRM_FORMAT_ABGR8888,
295 DRM_FORMAT_XBGR8888,
296 DRM_FORMAT_ARGB8888,
297 DRM_FORMAT_XRGB8888,
298 DRM_FORMAT_BGR565,
299 DRM_FORMAT_RGB565,
300 DRM_FORMAT_ABGR1555,
301 DRM_FORMAT_XBGR1555,
302 DRM_FORMAT_ARGB1555,
303 DRM_FORMAT_XRGB1555,
304 DRM_FORMAT_ABGR4444,
305 DRM_FORMAT_XBGR4444,
306 DRM_FORMAT_ARGB4444,
307 DRM_FORMAT_XRGB4444,
308};
309
310/*
311 * The Integrator variant is a PL110 with a bunch of broken, or not
312 * yet implemented features
313 */
314static const struct pl111_variant_data pl110_integrator = {
315 .name = "PL110 Integrator",
316 .is_pl110 = true,
317 .broken_clockdivider = true,
318 .broken_vblank = true,
319 .formats = pl110_integrator_pixel_formats,
320 .nformats = ARRAY_SIZE(pl110_integrator_pixel_formats),
321 .fb_depth = 16,
322};
323
324/*
325 * The IM-PD1 variant is a PL110 with a bunch of broken, or not
326 * yet implemented features
327 */
328static const struct pl111_variant_data pl110_impd1 = {
329 .name = "PL110 IM-PD1",
330 .is_pl110 = true,
331 .broken_clockdivider = true,
332 .broken_vblank = true,
333 .formats = pl110_integrator_pixel_formats,
334 .nformats = ARRAY_SIZE(pl110_integrator_pixel_formats),
335 .fb_depth = 15,
336};
337
338/*
339 * This is the in-between PL110 variant found in the ARM Versatile,
340 * supporting RGB565/BGR565
341 */
342static const struct pl111_variant_data pl110_versatile = {
343 .name = "PL110 Versatile",
344 .is_pl110 = true,
345 .external_bgr = true,
346 .formats = pl110_versatile_pixel_formats,
347 .nformats = ARRAY_SIZE(pl110_versatile_pixel_formats),
348 .fb_depth = 16,
349};
350
351/*
352 * RealView PL111 variant, the only real difference from the vanilla
353 * PL111 is that we select 16bpp framebuffer by default to be able
354 * to get 1024x768 without saturating the memory bus.
355 */
356static const struct pl111_variant_data pl111_realview = {
357 .name = "PL111 RealView",
358 .formats = pl111_realview_pixel_formats,
359 .nformats = ARRAY_SIZE(pl111_realview_pixel_formats),
360 .fb_depth = 16,
361};
362
363/*
364 * Versatile Express PL111 variant, again we just push the maximum
365 * BPP to 16 to be able to get 1024x768 without saturating the memory
366 * bus. The clockdivider also seems broken on the Versatile Express.
367 */
368static const struct pl111_variant_data pl111_vexpress = {
369 .name = "PL111 Versatile Express",
370 .formats = pl111_realview_pixel_formats,
371 .nformats = ARRAY_SIZE(pl111_realview_pixel_formats),
372 .fb_depth = 16,
373 .broken_clockdivider = true,
374};
375
376#define VEXPRESS_FPGAMUX_MOTHERBOARD 0x00
377#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_1 0x01
378#define VEXPRESS_FPGAMUX_DAUGHTERBOARD_2 0x02
379
380static int pl111_vexpress_clcd_init(struct drm_device *dev, struct device_node *np,
381 struct pl111_drm_dev_private *priv)
382{
383 struct platform_device *pdev;
384 struct device_node *root;
385 struct device_node *child;
386 struct device_node *ct_clcd = NULL;
387 struct regmap *map;
388 bool has_coretile_clcd = false;
389 bool has_coretile_hdlcd = false;
390 bool mux_motherboard = true;
391 u32 val;
392 int ret;
393
394 if (!IS_ENABLED(CONFIG_VEXPRESS_CONFIG))
395 return -ENODEV;
396
397 /*
398 * Check if we have a CLCD or HDLCD on the core tile by checking if a
399 * CLCD or HDLCD is available in the root of the device tree.
400 */
401 root = of_find_node_by_path("/");
402 if (!root)
403 return -EINVAL;
404
405 for_each_available_child_of_node(root, child) {
406 if (of_device_is_compatible(child, "arm,pl111")) {
407 has_coretile_clcd = true;
408 ct_clcd = child;
409 of_node_put(child);
410 break;
411 }
412 if (of_device_is_compatible(child, "arm,hdlcd")) {
413 has_coretile_hdlcd = true;
414 of_node_put(child);
415 break;
416 }
417 }
418
419 of_node_put(root);
420
421 /*
422 * If there is a coretile HDLCD and it has a driver,
423 * do not mux the CLCD on the motherboard to the DVI.
424 */
425 if (has_coretile_hdlcd && IS_ENABLED(CONFIG_DRM_HDLCD))
426 mux_motherboard = false;
427
428 /*
429 * On the Vexpress CA9 we let the CLCD on the coretile
430 * take precedence, so also in this case do not mux the
431 * motherboard to the DVI.
432 */
433 if (has_coretile_clcd)
434 mux_motherboard = false;
435
436 if (mux_motherboard) {
437 drm_info(dev, "DVI muxed to motherboard CLCD\n");
438 val = VEXPRESS_FPGAMUX_MOTHERBOARD;
439 } else if (ct_clcd == dev->dev->of_node) {
440 drm_info(dev,
441 "DVI muxed to daughterboard 1 (core tile) CLCD\n");
442 val = VEXPRESS_FPGAMUX_DAUGHTERBOARD_1;
443 } else {
444 drm_info(dev, "core tile graphics present\n");
445 drm_info(dev, "this device will be deactivated\n");
446 return -ENODEV;
447 }
448
449 /* Call into deep Vexpress configuration API */
450 pdev = of_find_device_by_node(np);
451 if (!pdev) {
452 drm_err(dev, "can't find the sysreg device, deferring\n");
453 return -EPROBE_DEFER;
454 }
455
456 map = devm_regmap_init_vexpress_config(&pdev->dev);
457 if (IS_ERR(map)) {
458 platform_device_put(pdev);
459 return PTR_ERR(map);
460 }
461
462 ret = regmap_write(map, 0, val);
463 platform_device_put(pdev);
464 if (ret) {
465 drm_err(dev, "error setting DVI muxmode\n");
466 return -ENODEV;
467 }
468
469 priv->variant = &pl111_vexpress;
470 drm_info(dev, "initializing Versatile Express PL111\n");
471
472 return 0;
473}
474
475int pl111_versatile_init(struct drm_device *dev, struct pl111_drm_dev_private *priv)
476{
477 const struct of_device_id *clcd_id;
478 enum versatile_clcd versatile_clcd_type;
479 struct device_node *np;
480 struct regmap *map;
481
482 np = of_find_matching_node_and_match(NULL, versatile_clcd_of_match,
483 &clcd_id);
484 if (!np) {
485 /* Non-ARM reference designs, just bail out */
486 return 0;
487 }
488
489 versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
490
491 /* Versatile Express special handling */
492 if (versatile_clcd_type == VEXPRESS_CLCD_V2M) {
493 int ret = pl111_vexpress_clcd_init(dev, np, priv);
494 of_node_put(np);
495 if (ret)
496 drm_err(dev, "Versatile Express init failed - %d", ret);
497 return ret;
498 }
499
500 /*
501 * On the Integrator, check if we should use the IM-PD1 instead,
502 * if we find it, it will take precedence. This is on the Integrator/AP
503 * which only has this option for PL110 graphics.
504 */
505 if (versatile_clcd_type == INTEGRATOR_CLCD_CM) {
506 np = of_find_matching_node_and_match(NULL, impd1_clcd_of_match,
507 &clcd_id);
508 if (np)
509 versatile_clcd_type = (enum versatile_clcd)clcd_id->data;
510 }
511
512 map = syscon_node_to_regmap(np);
513 of_node_put(np);
514 if (IS_ERR(map)) {
515 drm_err(dev, "no Versatile syscon regmap\n");
516 return PTR_ERR(map);
517 }
518
519 switch (versatile_clcd_type) {
520 case INTEGRATOR_CLCD_CM:
521 versatile_syscon_map = map;
522 priv->variant = &pl110_integrator;
523 priv->variant_display_enable = pl111_integrator_enable;
524 drm_info(dev, "set up callbacks for Integrator PL110\n");
525 break;
526 case INTEGRATOR_IMPD1:
527 versatile_syscon_map = map;
528 priv->variant = &pl110_impd1;
529 priv->variant_display_enable = pl111_impd1_enable;
530 priv->variant_display_disable = pl111_impd1_disable;
531 drm_info(dev, "set up callbacks for IM-PD1 PL110\n");
532 break;
533 case VERSATILE_CLCD:
534 versatile_syscon_map = map;
535 /* This can do RGB565 with external PLD */
536 priv->variant = &pl110_versatile;
537 priv->variant_display_enable = pl111_versatile_enable;
538 priv->variant_display_disable = pl111_versatile_disable;
539 /*
540 * The Versatile has a variant halfway between PL110
541 * and PL111 where these two registers have already been
542 * swapped.
543 */
544 priv->ienb = CLCD_PL111_IENB;
545 priv->ctrl = CLCD_PL111_CNTL;
546 drm_info(dev, "set up callbacks for Versatile PL110\n");
547 break;
548 case REALVIEW_CLCD_EB:
549 case REALVIEW_CLCD_PB1176:
550 case REALVIEW_CLCD_PB11MP:
551 case REALVIEW_CLCD_PBA8:
552 case REALVIEW_CLCD_PBX:
553 versatile_syscon_map = map;
554 priv->variant = &pl111_realview;
555 priv->variant_display_enable = pl111_realview_clcd_enable;
556 priv->variant_display_disable = pl111_realview_clcd_disable;
557 drm_info(dev, "set up callbacks for RealView PL111\n");
558 break;
559 default:
560 drm_info(dev, "unknown Versatile system controller\n");
561 break;
562 }
563
564 return 0;
565}
566EXPORT_SYMBOL_GPL(pl111_versatile_init);