xref: /linux/drivers/gpu/drm/sysfb/corebootdrm.c (revision d639d9fa162aadec1ae9980c4dcf6e50bd2f8290)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include <linux/aperture.h>
4 #include <linux/coreboot.h>
5 #include <linux/minmax.h>
6 #include <linux/platform_device.h>
7 #include <linux/pm.h>
8 
9 #include <drm/clients/drm_client_setup.h>
10 #include <drm/drm_atomic.h>
11 #include <drm/drm_atomic_state_helper.h>
12 #include <drm/drm_connector.h>
13 #include <drm/drm_damage_helper.h>
14 #include <drm/drm_device.h>
15 #include <drm/drm_drv.h>
16 #include <drm/drm_fbdev_shmem.h>
17 #include <drm/drm_framebuffer.h>
18 #include <drm/drm_gem_atomic_helper.h>
19 #include <drm/drm_gem_framebuffer_helper.h>
20 #include <drm/drm_gem_shmem_helper.h>
21 #include <drm/drm_managed.h>
22 #include <drm/drm_modeset_helper.h>
23 #include <drm/drm_modeset_helper_vtables.h>
24 #include <drm/drm_print.h>
25 #include <drm/drm_probe_helper.h>
26 
27 #include "drm_sysfb_helper.h"
28 
29 #define DRIVER_NAME	"corebootdrm"
30 #define DRIVER_DESC	"DRM driver for Coreboot framebuffers"
31 #define DRIVER_MAJOR	1
32 #define DRIVER_MINOR	0
33 
34 static const struct drm_format_info *
35 corebootdrm_get_format_fb(struct drm_device *dev, const struct lb_framebuffer *fb)
36 {
37 	static const struct drm_sysfb_format formats[] = {
38 		{ PIXEL_FORMAT_XRGB1555, DRM_FORMAT_XRGB1555, },
39 		{ PIXEL_FORMAT_RGB565, DRM_FORMAT_RGB565, },
40 		{ PIXEL_FORMAT_RGB888, DRM_FORMAT_RGB888, },
41 		{ PIXEL_FORMAT_XRGB8888, DRM_FORMAT_XRGB8888, },
42 		{ PIXEL_FORMAT_XBGR8888, DRM_FORMAT_XBGR8888, },
43 		{ PIXEL_FORMAT_XRGB2101010, DRM_FORMAT_XRGB2101010, },
44 	};
45 	const struct pixel_format pixel = {
46 		.bits_per_pixel = fb->bits_per_pixel,
47 		.indexed  = false,
48 		.alpha = {
49 			.offset = 0,
50 			.length = 0,
51 		},
52 		.red = {
53 			.offset = fb->red_mask_pos,
54 			.length = fb->red_mask_size,
55 		},
56 		.green = {
57 			.offset = fb->green_mask_pos,
58 			.length = fb->green_mask_size,
59 		},
60 		.blue = {
61 			.offset = fb->blue_mask_pos,
62 			.length = fb->blue_mask_size,
63 		},
64 	};
65 
66 	return drm_sysfb_get_format(dev, formats, ARRAY_SIZE(formats), &pixel);
67 }
68 
69 static int corebootdrm_get_width_fb(struct drm_device *dev, const struct lb_framebuffer *fb)
70 {
71 	return drm_sysfb_get_validated_int0(dev, "width", fb->x_resolution, INT_MAX);
72 }
73 
74 static int corebootdrm_get_height_fb(struct drm_device *dev, const struct lb_framebuffer *fb)
75 {
76 	return drm_sysfb_get_validated_int0(dev, "height", fb->y_resolution, INT_MAX);
77 }
78 
79 static int corebootdrm_get_pitch_fb(struct drm_device *dev, const struct drm_format_info *format,
80 				    unsigned int width, const struct lb_framebuffer *fb)
81 {
82 	u64 bytes_per_line = fb->bytes_per_line;
83 
84 	if (!bytes_per_line)
85 		bytes_per_line = drm_format_info_min_pitch(format, 0, width);
86 
87 	return drm_sysfb_get_validated_int0(dev, "pitch", bytes_per_line, INT_MAX);
88 }
89 
90 static resource_size_t corebootdrm_get_size_fb(struct drm_device *dev, unsigned int height,
91 					       unsigned int pitch,
92 					       const struct lb_framebuffer *fb)
93 {
94 	resource_size_t size;
95 
96 	if (check_mul_overflow(height, pitch, &size))
97 		return 0;
98 
99 	return size;
100 }
101 
102 static phys_addr_t corebootdrm_get_address_fb(struct drm_device *dev, resource_size_t size,
103 					      const struct lb_framebuffer *fb)
104 {
105 	if (size > PHYS_ADDR_MAX)
106 		return 0;
107 	if (!fb->physical_address)
108 		return 0;
109 	if (fb->physical_address > (PHYS_ADDR_MAX - size))
110 		return 0;
111 
112 	return fb->physical_address;
113 }
114 
115 static enum drm_panel_orientation corebootdrm_get_orientation_fb(struct drm_device *dev,
116 								 const struct lb_framebuffer *fb)
117 {
118 	if (!LB_FRAMEBUFFER_HAS_ORIENTATION(fb))
119 		return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
120 
121 	switch (fb->orientation) {
122 	case LB_FRAMEBUFFER_ORIENTATION_NORMAL:
123 		return DRM_MODE_PANEL_ORIENTATION_NORMAL;
124 	case LB_FRAMEBUFFER_ORIENTATION_BOTTOM_UP:
125 		return DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP;
126 	case LB_FRAMEBUFFER_ORIENTATION_LEFT_UP:
127 		return DRM_MODE_PANEL_ORIENTATION_LEFT_UP;
128 	case LB_FRAMEBUFFER_ORIENTATION_RIGHT_UP:
129 		return DRM_MODE_PANEL_ORIENTATION_RIGHT_UP;
130 	}
131 
132 	return DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
133 }
134 
135 /*
136  * Simple Framebuffer device
137  */
138 
139 struct corebootdrm_device {
140 	struct drm_sysfb_device sysfb;
141 
142 	/* modesetting */
143 	u32 formats[DRM_SYSFB_PLANE_NFORMATS(1)];
144 	struct drm_plane primary_plane;
145 	struct drm_crtc crtc;
146 	struct drm_encoder encoder;
147 	struct drm_connector connector;
148 };
149 
150 /*
151  * Modesetting
152  */
153 
154 static const u64 corebootdrm_primary_plane_format_modifiers[] = {
155 	DRM_SYSFB_PLANE_FORMAT_MODIFIERS,
156 };
157 
158 static const struct drm_plane_helper_funcs corebootdrm_primary_plane_helper_funcs = {
159 	DRM_SYSFB_PLANE_HELPER_FUNCS,
160 };
161 
162 static const struct drm_plane_funcs corebootdrm_primary_plane_funcs = {
163 	DRM_SYSFB_PLANE_FUNCS,
164 	.destroy = drm_plane_cleanup,
165 };
166 
167 static const struct drm_crtc_helper_funcs corebootdrm_crtc_helper_funcs = {
168 	DRM_SYSFB_CRTC_HELPER_FUNCS,
169 };
170 
171 static const struct drm_crtc_funcs corebootdrm_crtc_funcs = {
172 	DRM_SYSFB_CRTC_FUNCS,
173 	.destroy = drm_crtc_cleanup,
174 };
175 
176 static const struct drm_encoder_funcs corebootdrm_encoder_funcs = {
177 	.destroy = drm_encoder_cleanup,
178 };
179 
180 static const struct drm_connector_helper_funcs corebootdrm_connector_helper_funcs = {
181 	DRM_SYSFB_CONNECTOR_HELPER_FUNCS,
182 };
183 
184 static const struct drm_connector_funcs corebootdrm_connector_funcs = {
185 	DRM_SYSFB_CONNECTOR_FUNCS,
186 	.destroy = drm_connector_cleanup,
187 };
188 
189 static const struct drm_mode_config_funcs corebootdrm_mode_config_funcs = {
190 	DRM_SYSFB_MODE_CONFIG_FUNCS,
191 };
192 
193 static int corebootdrm_mode_config_init(struct corebootdrm_device *cdev,
194 					enum drm_panel_orientation orientation)
195 {
196 	struct drm_sysfb_device *sysfb = &cdev->sysfb;
197 	struct drm_device *dev = &sysfb->dev;
198 	const struct drm_format_info *format = sysfb->fb_format;
199 	unsigned int width = sysfb->fb_mode.hdisplay;
200 	unsigned int height = sysfb->fb_mode.vdisplay;
201 	struct drm_plane *primary_plane;
202 	struct drm_crtc *crtc;
203 	struct drm_encoder *encoder;
204 	struct drm_connector *connector;
205 	size_t nformats;
206 	int ret;
207 
208 	ret = drmm_mode_config_init(dev);
209 	if (ret)
210 		return ret;
211 
212 	dev->mode_config.min_width = width;
213 	dev->mode_config.max_width = max_t(unsigned int, width, DRM_SHADOW_PLANE_MAX_WIDTH);
214 	dev->mode_config.min_height = height;
215 	dev->mode_config.max_height = max_t(unsigned int, height, DRM_SHADOW_PLANE_MAX_HEIGHT);
216 	dev->mode_config.funcs = &corebootdrm_mode_config_funcs;
217 	dev->mode_config.preferred_depth = format->depth;
218 
219 	/* Primary plane */
220 
221 	nformats = drm_sysfb_build_fourcc_list(dev, &format->format, 1,
222 					       cdev->formats, ARRAY_SIZE(cdev->formats));
223 
224 	primary_plane = &cdev->primary_plane;
225 	ret = drm_universal_plane_init(dev, primary_plane, 0, &corebootdrm_primary_plane_funcs,
226 				       cdev->formats, nformats,
227 				       corebootdrm_primary_plane_format_modifiers,
228 				       DRM_PLANE_TYPE_PRIMARY, NULL);
229 	if (ret)
230 		return ret;
231 	drm_plane_helper_add(primary_plane, &corebootdrm_primary_plane_helper_funcs);
232 	drm_plane_enable_fb_damage_clips(primary_plane);
233 
234 	/* CRTC */
235 
236 	crtc = &cdev->crtc;
237 	ret = drm_crtc_init_with_planes(dev, crtc, primary_plane, NULL,
238 					&corebootdrm_crtc_funcs, NULL);
239 	if (ret)
240 		return ret;
241 	drm_crtc_helper_add(crtc, &corebootdrm_crtc_helper_funcs);
242 
243 	/* Encoder */
244 
245 	encoder = &cdev->encoder;
246 	ret = drm_encoder_init(dev, encoder, &corebootdrm_encoder_funcs,
247 			       DRM_MODE_ENCODER_NONE, NULL);
248 	if (ret)
249 		return ret;
250 	encoder->possible_crtcs = drm_crtc_mask(crtc);
251 
252 	/* Connector */
253 
254 	connector = &cdev->connector;
255 	ret = drm_connector_init(dev, connector, &corebootdrm_connector_funcs,
256 				 DRM_MODE_CONNECTOR_Unknown);
257 	if (ret)
258 		return ret;
259 	drm_connector_helper_add(connector, &corebootdrm_connector_helper_funcs);
260 	drm_connector_set_panel_orientation_with_quirk(connector, orientation,
261 						       width, height);
262 
263 	ret = drm_connector_attach_encoder(connector, encoder);
264 	if (ret)
265 		return ret;
266 
267 	return 0;
268 }
269 
270 /*
271  * DRM driver
272  */
273 
274 DEFINE_DRM_GEM_FOPS(corebootdrm_fops);
275 
276 static struct drm_driver corebootdrm_drm_driver = {
277 	DRM_GEM_SHMEM_DRIVER_OPS,
278 	DRM_FBDEV_SHMEM_DRIVER_OPS,
279 	.name			= DRIVER_NAME,
280 	.desc			= DRIVER_DESC,
281 	.major			= DRIVER_MAJOR,
282 	.minor			= DRIVER_MINOR,
283 	.driver_features	= DRIVER_ATOMIC | DRIVER_GEM | DRIVER_MODESET,
284 	.fops			= &corebootdrm_fops,
285 };
286 
287 /*
288  * Coreboot driver
289  */
290 
291 static int corebootdrm_pm_suspend(struct device *dev)
292 {
293 	struct corebootdrm_device *cdev = dev_get_drvdata(dev);
294 	struct drm_device *drm = &cdev->sysfb.dev;
295 
296 	return drm_mode_config_helper_suspend(drm);
297 }
298 
299 static int corebootdrm_pm_resume(struct device *dev)
300 {
301 	struct corebootdrm_device *cdev = dev_get_drvdata(dev);
302 	struct drm_device *drm = &cdev->sysfb.dev;
303 
304 	return drm_mode_config_helper_resume(drm);
305 }
306 
307 static DEFINE_SIMPLE_DEV_PM_OPS(corebootdrm_pm_ops, corebootdrm_pm_suspend, corebootdrm_pm_resume);
308 
309 static int corebootdrm_probe(struct platform_device *pdev)
310 {
311 	const struct lb_framebuffer *fb = dev_get_platdata(&pdev->dev);
312 	struct corebootdrm_device *cdev;
313 	struct drm_sysfb_device *sysfb;
314 	struct drm_device *dev;
315 	const struct drm_format_info *format;
316 	int width, height, pitch;
317 	resource_size_t size;
318 	phys_addr_t address;
319 	enum drm_panel_orientation orientation;
320 	struct resource *res, *mem = NULL;
321 	struct resource aperture;
322 	void __iomem *screen_base;
323 	int ret;
324 
325 	cdev = devm_drm_dev_alloc(&pdev->dev, &corebootdrm_drm_driver,
326 				  struct corebootdrm_device, sysfb.dev);
327 	if (IS_ERR(cdev))
328 		return PTR_ERR(cdev);
329 	platform_set_drvdata(pdev, cdev);
330 
331 	sysfb = &cdev->sysfb;
332 	dev = &sysfb->dev;
333 
334 	if (!fb) {
335 		drm_err(dev, "coreboot framebuffer not found\n");
336 		return -EINVAL;
337 	} else if (!LB_FRAMEBUFFER_HAS_LFB(fb)) {
338 		drm_err(dev, "coreboot framebuffer entry too small\n");
339 		return -EINVAL;
340 	}
341 
342 	/*
343 	 * Hardware settings
344 	 */
345 
346 	format = corebootdrm_get_format_fb(dev, fb);
347 	if (!format)
348 		return -EINVAL;
349 	width = corebootdrm_get_width_fb(dev, fb);
350 	if (width < 0)
351 		return width;
352 	height = corebootdrm_get_height_fb(dev, fb);
353 	if (height < 0)
354 		return height;
355 	pitch = corebootdrm_get_pitch_fb(dev, format, width, fb);
356 	if (pitch < 0)
357 		return pitch;
358 	size = corebootdrm_get_size_fb(dev, height, pitch, fb);
359 	if (!size)
360 		return -EINVAL;
361 	address = corebootdrm_get_address_fb(dev, size, fb);
362 	if (!address)
363 		return -EINVAL;
364 	orientation = corebootdrm_get_orientation_fb(dev, fb);
365 
366 	sysfb->fb_mode = drm_sysfb_mode(width, height, 0, 0);
367 	sysfb->fb_format = format;
368 	sysfb->fb_pitch = pitch;
369 
370 	drm_dbg(dev, "display mode={" DRM_MODE_FMT "}\n", DRM_MODE_ARG(&sysfb->fb_mode));
371 	drm_dbg(dev, "framebuffer format=%p4cc, size=%dx%d, pitch=%d byte\n",
372 		&format->format, width, height, pitch);
373 
374 	/*
375 	 * Memory management
376 	 */
377 
378 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
379 	if (!res) {
380 		drm_err(dev, "memory resource not found\n");
381 		return -EINVAL;
382 	}
383 
384 	mem = devm_request_mem_region(&pdev->dev, res->start, resource_size(res),
385 				      dev->driver->name);
386 	if (!mem) {
387 		drm_warn(dev, "could not acquire memory resource at %pr\n", res);
388 		/*
389 		 * We cannot make this fatal. Sometimes this comes from magic
390 		 * spaces our resource handlers simply don't know about. Use
391 		 * the memory resource as-is and try to map that instead.
392 		 */
393 		mem = res;
394 	}
395 
396 	drm_dbg(dev, "using memory resource at %pr\n", mem);
397 
398 	aperture = DEFINE_RES_MEM(address, size);
399 	if (!resource_contains(mem, &aperture)) {
400 		drm_err(dev, "framebuffer aperture at invalid memory range %pr\n", &aperture);
401 		return -EINVAL;
402 	}
403 
404 	ret = devm_aperture_acquire_for_platform_device(pdev, address, size);
405 	if (ret) {
406 		drm_err(dev, "could not acquire framebuffer aperture: %d\n", ret);
407 		return ret;
408 	}
409 
410 	screen_base = devm_ioremap_wc(&pdev->dev, address, size);
411 	if (!screen_base)
412 		return -ENOMEM;
413 
414 	iosys_map_set_vaddr_iomem(&sysfb->fb_addr, screen_base);
415 
416 	/*
417 	 * DRM mode setting and registration
418 	 */
419 
420 	ret = corebootdrm_mode_config_init(cdev, orientation);
421 	if (ret)
422 		return ret;
423 
424 	drm_mode_config_reset(dev);
425 
426 	ret = drm_dev_register(dev, 0);
427 	if (ret)
428 		return ret;
429 
430 	drm_client_setup(dev, sysfb->fb_format);
431 
432 	return 0;
433 }
434 
435 static void corebootdrm_remove(struct platform_device *pdev)
436 {
437 	struct corebootdrm_device *cdev = platform_get_drvdata(pdev);
438 	struct drm_device *dev = &cdev->sysfb.dev;
439 
440 	drm_dev_unplug(dev);
441 }
442 
443 static struct platform_driver corebootdrm_platform_driver = {
444 	.driver = {
445 		.name = "coreboot-framebuffer",
446 		.pm = pm_sleep_ptr(&corebootdrm_pm_ops),
447 	},
448 	.probe = corebootdrm_probe,
449 	.remove = corebootdrm_remove,
450 };
451 
452 module_platform_driver(corebootdrm_platform_driver);
453 
454 MODULE_DESCRIPTION(DRIVER_DESC);
455 MODULE_LICENSE("GPL");
456