xref: /linux/drivers/gpu/drm/pl111/pl111_versatile.c (revision c0d6f52f9b62479d61f8cd4faf9fb2f8bce6e301)
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 
28 static struct regmap *versatile_syscon_map;
29 
30 /*
31  * We detect the different syscon types from the compatible strings.
32  */
33 enum 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 
45 static 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 
81 static 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 
116 static 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 
156 static 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 
169 static 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 
196 static 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 
205 static 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 
249 static 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 
258 static 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 */
268 static 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 */
280 static 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 
293 static 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  */
314 static 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  */
328 static 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  */
342 static 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  */
356 static 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  */
368 static 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 
380 static 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 
475 int 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 }
566 EXPORT_SYMBOL_GPL(pl111_versatile_init);
567