xref: /openbsd-src/sys/dev/fdt/rkdrm.c (revision 25c4e8bd056e974b28f4a0ffd39d76c190a56013)
1 /* $OpenBSD: rkdrm.c,v 1.14 2022/07/15 17:57:26 kettenis Exp $ */
2 /* $NetBSD: rk_drm.c,v 1.3 2019/12/15 01:00:58 mrg Exp $ */
3 /*-
4  * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
5  * Copyright (c) 2020 Patrick Wildt <patrick@blueri.se>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
22  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/device.h>
32 #include <sys/systm.h>
33 
34 #include <machine/bus.h>
35 #include <machine/fdt.h>
36 
37 #include <dev/ofw/openfirm.h>
38 #include <dev/ofw/ofw_misc.h>
39 
40 #include <drm/drm_atomic_helper.h>
41 #include <drm/drm_drv.h>
42 #include <drm/drm_fb_helper.h>
43 #include <drm/drm_gem.h>
44 
45 #include <dev/fdt/rkdrm.h>
46 
47 #define	RK_DRM_MAX_WIDTH	3840
48 #define	RK_DRM_MAX_HEIGHT	2160
49 
50 TAILQ_HEAD(, rkdrm_ports) rkdrm_ports =
51     TAILQ_HEAD_INITIALIZER(rkdrm_ports);
52 
53 int	rkdrm_match(struct device *, void *, void *);
54 void	rkdrm_attach(struct device *, struct device *, void *);
55 void	rkdrm_attachhook(struct device *);
56 
57 #ifdef notyet
58 vmem_t	*rkdrm_alloc_cma_pool(struct drm_device *, size_t);
59 #endif
60 
61 int	rkdrm_load(struct drm_device *, unsigned long);
62 int	rkdrm_unload(struct drm_device *);
63 
64 struct drm_driver rkdrm_driver = {
65 	.driver_features = DRIVER_ATOMIC | DRIVER_MODESET | DRIVER_GEM,
66 
67 	.dumb_create = drm_gem_cma_dumb_create,
68 	.dumb_map_offset = drm_gem_dumb_map_offset,
69 
70 	.gem_fault = drm_gem_cma_fault,
71 
72 	.name = DRIVER_NAME,
73 	.desc = DRIVER_DESC,
74 	.date = DRIVER_DATE,
75 	.major = DRIVER_MAJOR,
76 	.minor = DRIVER_MINOR,
77 	.patchlevel = DRIVER_PATCHLEVEL,
78 };
79 
80 const struct drm_gem_object_funcs rkdrm_gem_object_funcs = {
81 	.free = drm_gem_cma_free_object,
82 };
83 
84 const struct cfattach rkdrm_ca = {
85 	sizeof (struct rkdrm_softc), rkdrm_match, rkdrm_attach
86 };
87 
88 struct cfdriver rkdrm_cd = {
89 	NULL, "rkdrm", DV_DULL
90 };
91 
92 int
93 rkdrm_match(struct device *parent, void *match, void *aux)
94 {
95 	struct fdt_attach_args *faa = aux;
96 
97 	return OF_is_compatible(faa->fa_node, "rockchip,display-subsystem");
98 }
99 
100 void
101 rkdrm_attach(struct device *parent, struct device *self, void *aux)
102 {
103 	struct rkdrm_softc *sc = (struct rkdrm_softc *)self;
104 	struct fdt_attach_args *faa = aux;
105 
106 	sc->sc_dmat = faa->fa_dmat;
107 	sc->sc_iot = faa->fa_iot;
108 	sc->sc_node = faa->fa_node;
109 
110 	printf("\n");
111 
112 	/*
113 	 * Update our understanding of the console output node if
114 	 * we're using the framebuffer console.
115 	 */
116 	if (OF_is_compatible(stdout_node, "simple-framebuffer"))
117 		stdout_node = sc->sc_node;
118 
119 	drm_attach_platform(&rkdrm_driver, faa->fa_iot, faa->fa_dmat, self,
120 	    &sc->sc_ddev);
121 	config_mountroot(self, rkdrm_attachhook);
122 }
123 
124 int
125 rkdrm_fb_create_handle(struct drm_framebuffer *fb,
126     struct drm_file *file, unsigned int *handle)
127 {
128 	struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(fb);
129 
130 	return drm_gem_handle_create(file, &sfb->obj->base, handle);
131 }
132 
133 void
134 rkdrm_fb_destroy(struct drm_framebuffer *fb)
135 {
136 	struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(fb);
137 
138 	drm_framebuffer_cleanup(fb);
139 	drm_gem_object_put(&sfb->obj->base);
140 	free(sfb, M_DRM, sizeof(*sfb));
141 }
142 
143 struct drm_framebuffer_funcs rkdrm_framebuffer_funcs = {
144 	.create_handle = rkdrm_fb_create_handle,
145 	.destroy = rkdrm_fb_destroy,
146 };
147 
148 struct drm_framebuffer *
149 rkdrm_fb_create(struct drm_device *ddev, struct drm_file *file,
150     const struct drm_mode_fb_cmd2 *cmd)
151 {
152 	struct rkdrm_framebuffer *fb;
153 	struct drm_gem_object *gem_obj;
154 	int error;
155 
156 	if (cmd->flags)
157 		return NULL;
158 
159 	gem_obj = drm_gem_object_lookup(file, cmd->handles[0]);
160 	if (gem_obj == NULL)
161 		return NULL;
162 
163 	fb = malloc(sizeof(*fb), M_DRM, M_ZERO | M_WAITOK);
164 	drm_helper_mode_fill_fb_struct(ddev, &fb->base, cmd);
165 	fb->base.format = drm_format_info(DRM_FORMAT_ARGB8888);
166 	fb->base.obj[0] = gem_obj;
167 	fb->obj = to_drm_gem_cma_obj(gem_obj);
168 
169 	error = drm_framebuffer_init(ddev, &fb->base, &rkdrm_framebuffer_funcs);
170 	if (error != 0)
171 		goto dealloc;
172 
173 	return &fb->base;
174 
175 dealloc:
176 	drm_framebuffer_cleanup(&fb->base);
177 	free(fb, M_DRM, sizeof(*fb));
178 	drm_gem_object_put(gem_obj);
179 
180 	return NULL;
181 }
182 
183 struct drm_mode_config_helper_funcs rkdrm_mode_config_helper_funcs =
184 {
185 	.atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
186 };
187 
188 struct drm_mode_config_funcs rkdrm_mode_config_funcs = {
189 	.fb_create = rkdrm_fb_create,
190 	.atomic_check = drm_atomic_helper_check,
191 	.atomic_commit = drm_atomic_helper_commit,
192 };
193 
194 int rkdrm_fb_probe(struct drm_fb_helper *, struct drm_fb_helper_surface_size *);
195 
196 struct drm_fb_helper_funcs rkdrm_fb_helper_funcs = {
197 	.fb_probe = rkdrm_fb_probe,
198 };
199 
200 int
201 rkdrm_unload(struct drm_device *ddev)
202 {
203 	drm_mode_config_cleanup(ddev);
204 
205 	return 0;
206 }
207 
208 void rkdrm_burner(void *, u_int, u_int);
209 int rkdrm_wsioctl(void *, u_long, caddr_t, int, struct proc *);
210 paddr_t rkdrm_wsmmap(void *, off_t, int);
211 int rkdrm_alloc_screen(void *, const struct wsscreen_descr *,
212     void **, int *, int *, uint32_t *);
213 void rkdrm_free_screen(void *, void *);
214 int rkdrm_show_screen(void *, void *, int,
215     void (*)(void *, int, int), void *);
216 void rkdrm_doswitch(void *);
217 void rkdrm_enter_ddb(void *, void *);
218 int rkdrm_get_param(struct wsdisplay_param *);
219 int rkdrm_set_param(struct wsdisplay_param *);
220 
221 struct wsscreen_descr rkdrm_stdscreen = {
222 	"std",
223 	0, 0,
224 	0,
225 	0, 0,
226 	WSSCREEN_UNDERLINE | WSSCREEN_HILIT |
227 	WSSCREEN_REVERSE | WSSCREEN_WSCOLORS
228 };
229 
230 const struct wsscreen_descr *rkdrm_scrlist[] = {
231 	&rkdrm_stdscreen,
232 };
233 
234 struct wsscreen_list rkdrm_screenlist = {
235 	nitems(rkdrm_scrlist), rkdrm_scrlist
236 };
237 
238 struct wsdisplay_accessops rkdrm_accessops = {
239 	.ioctl = rkdrm_wsioctl,
240 	.mmap = rkdrm_wsmmap,
241 	.alloc_screen = rkdrm_alloc_screen,
242 	.free_screen = rkdrm_free_screen,
243 	.show_screen = rkdrm_show_screen,
244 	.enter_ddb = rkdrm_enter_ddb,
245 	.getchar = rasops_getchar,
246 	.load_font = rasops_load_font,
247 	.list_font = rasops_list_font,
248 	.scrollback = rasops_scrollback,
249 #ifdef notyet
250 	.burn_screen = rkdrm_burner
251 #endif
252 };
253 
254 int
255 rkdrm_wsioctl(void *v, u_long cmd, caddr_t data, int flag, struct proc *p)
256 {
257 	struct rasops_info *ri = v;
258 	struct rkdrm_softc *sc = ri->ri_hw;
259 	struct wsdisplay_param *dp = (struct wsdisplay_param *)data;
260 	struct wsdisplay_fbinfo *wdf;
261 
262 	switch (cmd) {
263 	case WSDISPLAYIO_GETPARAM:
264 		if (ws_get_param)
265 			return ws_get_param(dp);
266 		return -1;
267 	case WSDISPLAYIO_SETPARAM:
268 		if (ws_set_param)
269 			return ws_set_param(dp);
270 		return -1;
271 	case WSDISPLAYIO_GTYPE:
272 		*(u_int *)data = WSDISPLAY_TYPE_RKDRM;
273 		return 0;
274 	case WSDISPLAYIO_GINFO:
275 		wdf = (struct wsdisplay_fbinfo *)data;
276 		wdf->width = ri->ri_width;
277 		wdf->height = ri->ri_height;
278 		wdf->depth = ri->ri_depth;
279 		wdf->stride = ri->ri_stride;
280 		wdf->offset = 0;
281 		wdf->cmsize = 0;
282 		return 0;
283 	case WSDISPLAYIO_LINEBYTES:
284 		*(u_int *)data = ri->ri_stride;
285 		return 0;
286 	}
287 
288 	return (-1);
289 }
290 
291 paddr_t
292 rkdrm_wsmmap(void *v, off_t off, int prot)
293 {
294 	struct rasops_info *ri = v;
295 	struct rkdrm_softc *sc = ri->ri_hw;
296 	struct drm_fb_helper *helper = &sc->helper;
297 	struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(helper->fb);
298 	uint64_t paddr = (uint64_t)sfb->obj->dmamap->dm_segs[0].ds_addr;
299 	size_t size = sfb->obj->dmamap->dm_segs[0].ds_len;
300 
301 	if (off < 0 || off >= size)
302 		return -1;
303 
304 	return ((paddr + off) | PMAP_NOCACHE);
305 }
306 
307 int
308 rkdrm_alloc_screen(void *v, const struct wsscreen_descr *type,
309     void **cookiep, int *curxp, int *curyp, uint32_t *attrp)
310 {
311 	return rasops_alloc_screen(v, cookiep, curxp, curyp, attrp);
312 }
313 
314 void
315 rkdrm_free_screen(void *v, void *cookie)
316 {
317 	return rasops_free_screen(v, cookie);
318 }
319 
320 int
321 rkdrm_show_screen(void *v, void *cookie, int waitok,
322     void (*cb)(void *, int, int), void *cbarg)
323 {
324 	struct rasops_info *ri = v;
325 	struct rkdrm_softc *sc = ri->ri_hw;
326 
327 	if (cookie == ri->ri_active)
328 		return (0);
329 
330 	sc->switchcb = cb;
331 	sc->switchcbarg = cbarg;
332 	sc->switchcookie = cookie;
333 	if (cb) {
334 		task_add(systq, &sc->switchtask);
335 		return (EAGAIN);
336 	}
337 
338 	rkdrm_doswitch(v);
339 
340 	return (0);
341 }
342 
343 void
344 rkdrm_doswitch(void *v)
345 {
346 	struct rasops_info *ri = v;
347 	struct rkdrm_softc *sc = ri->ri_hw;
348 	struct rkdrm_crtc *rkdrm_crtc;
349 	int i, crtc;
350 
351 	rasops_show_screen(ri, sc->switchcookie, 0, NULL, NULL);
352 	drm_fb_helper_restore_fbdev_mode_unlocked(&sc->helper);
353 
354 	if (sc->switchcb)
355 		(sc->switchcb)(sc->switchcbarg, 0, 0);
356 }
357 
358 void
359 rkdrm_enter_ddb(void *v, void *cookie)
360 {
361 	struct rasops_info *ri = v;
362 	struct rkdrm_softc *sc = ri->ri_hw;
363 	struct drm_fb_helper *fb_helper = &sc->helper;
364 
365 	if (cookie == ri->ri_active)
366 		return;
367 
368 	rasops_show_screen(ri, cookie, 0, NULL, NULL);
369 	drm_fb_helper_debug_enter(fb_helper->fbdev);
370 }
371 
372 void
373 rkdrm_attachhook(struct device *dev)
374 {
375 	struct rkdrm_softc *sc = (struct rkdrm_softc *)dev;
376 	struct wsemuldisplaydev_attach_args aa;
377 	struct drm_fb_helper *helper = &sc->helper;
378 	struct rasops_info *ri = &sc->ro;
379 	struct rkdrm_framebuffer *sfb;
380 	struct drm_device *ddev;
381 	uint32_t *ports;
382 	int i, portslen, nports;
383 	int console = 0;
384 	uint32_t defattr;
385 	int error;
386 
387 	if (sc->sc_node == stdout_node)
388 		console = 1;
389 
390 	portslen = OF_getproplen(sc->sc_node, "ports");
391 	if (portslen < 0) {
392 		printf("%s: no display interface ports specified\n",
393 		    sc->sc_dev.dv_xname);
394 		return;
395 	}
396 
397 	drm_mode_config_init(&sc->sc_ddev);
398 	sc->sc_ddev.mode_config.min_width = 0;
399 	sc->sc_ddev.mode_config.min_height = 0;
400 	sc->sc_ddev.mode_config.max_width = RK_DRM_MAX_WIDTH;
401 	sc->sc_ddev.mode_config.max_height = RK_DRM_MAX_HEIGHT;
402 	sc->sc_ddev.mode_config.funcs = &rkdrm_mode_config_funcs;
403 	sc->sc_ddev.mode_config.helper_private =
404 	    &rkdrm_mode_config_helper_funcs;
405 
406 	nports = 0;
407 	ports = malloc(portslen, M_TEMP, M_WAITOK);
408 	OF_getpropintarray(sc->sc_node, "ports", ports, portslen);
409 	for (i = 0; i < portslen / sizeof(uint32_t); i++) {
410 		error = device_port_activate(ports[i], &sc->sc_ddev);
411 		if (error == 0)
412 			nports++;
413 	}
414 	free(ports, M_TEMP, portslen);
415 
416 	if (nports == 0) {
417 		printf("%s: no display interface ports configured\n",
418 		    sc->sc_dev.dv_xname);
419 		drm_mode_config_cleanup(&sc->sc_ddev);
420 		return;
421 	}
422 
423 	drm_mode_config_reset(&sc->sc_ddev);
424 
425 	drm_fb_helper_prepare(&sc->sc_ddev, &sc->helper, &rkdrm_fb_helper_funcs);
426 	if (drm_fb_helper_init(&sc->sc_ddev, &sc->helper)) {
427 		printf("%s: can't initialize framebuffer helper\n",
428 		    sc->sc_dev.dv_xname);
429 		drm_mode_config_cleanup(&sc->sc_ddev);
430 		return;
431 	}
432 
433 	sc->helper.fb = malloc(sizeof(struct rkdrm_framebuffer),
434 	    M_DRM, M_WAITOK | M_ZERO);
435 
436 	drm_fb_helper_initial_config(&sc->helper, 32);
437 
438 	task_set(&sc->switchtask, rkdrm_doswitch, ri);
439 
440 	drm_fb_helper_restore_fbdev_mode_unlocked(&sc->helper);
441 
442 	sfb = to_rkdrm_framebuffer(helper->fb);
443 	ri->ri_bits = sfb->obj->vaddr;
444 	ri->ri_flg = RI_CENTER | RI_VCONS;
445 	ri->ri_depth = helper->fb->format->depth;
446 	ri->ri_width = helper->fb->width;
447 	ri->ri_height = helper->fb->height;
448 	ri->ri_stride = ri->ri_width * ri->ri_depth / 8;
449 	ri->ri_rnum = 8;	/* ARGB8888 */
450 	ri->ri_rpos = 16;
451 	ri->ri_gnum = 8;
452 	ri->ri_gpos = 8;
453 	ri->ri_bnum = 8;
454 	ri->ri_bpos = 0;
455 	rasops_init(ri, 160, 160);
456 	ri->ri_hw = sc;
457 
458 	rkdrm_stdscreen.capabilities = ri->ri_caps;
459 	rkdrm_stdscreen.nrows = ri->ri_rows;
460 	rkdrm_stdscreen.ncols = ri->ri_cols;
461 	rkdrm_stdscreen.textops = &ri->ri_ops;
462 	rkdrm_stdscreen.fontwidth = ri->ri_font->fontwidth;
463 	rkdrm_stdscreen.fontheight = ri->ri_font->fontheight;
464 
465 	if (console) {
466 		ri->ri_ops.pack_attr(ri->ri_active, 0, 0, 0, &defattr);
467 		wsdisplay_cnattach(&rkdrm_stdscreen, ri->ri_active,
468 		    ri->ri_ccol, ri->ri_crow, defattr);
469 	}
470 
471 	memset(&aa, 0, sizeof(aa));
472 	aa.scrdata = &rkdrm_screenlist;
473 	aa.accessops = &rkdrm_accessops;
474 	aa.accesscookie = ri;
475 	aa.console = console;
476 
477 	printf("%s: %dx%d, %dbpp\n", sc->sc_dev.dv_xname,
478 	    ri->ri_width, ri->ri_height, ri->ri_depth);
479 
480 	config_found_sm(&sc->sc_dev, &aa, wsemuldisplaydevprint,
481 	    wsemuldisplaydevsubmatch);
482 
483 	drm_dev_register(&sc->sc_ddev, 0);
484 }
485 
486 int
487 rkdrm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes)
488 {
489 	struct rkdrm_softc *sc = rkdrm_private(helper->dev);
490 	struct drm_device *ddev = helper->dev;
491 	struct rkdrm_framebuffer *sfb = to_rkdrm_framebuffer(helper->fb);
492 	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
493 	struct drm_framebuffer *fb = helper->fb;
494 	struct wsemuldisplaydev_attach_args aa;
495 	struct rasops_info *ri = &sc->ro;
496 	struct rkdrmfb_attach_args sfa;
497 	unsigned int bytes_per_pixel;
498 	struct fb_info *info;
499 	size_t size;
500 	int error;
501 
502 	bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
503 
504 	mode_cmd.width = sizes->surface_width;
505 	mode_cmd.height = sizes->surface_height;
506 	mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
507 	mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
508 	    sizes->surface_depth);
509 
510 	size = roundup(mode_cmd.pitches[0] * mode_cmd.height, PAGE_SIZE);
511 
512 	/* FIXME: CMA pool? */
513 
514 	sfb->obj = drm_gem_cma_create(ddev, size);
515 	if (sfb->obj == NULL) {
516 		DRM_ERROR("failed to allocate memory for framebuffer\n");
517 		return -ENOMEM;
518 	}
519 
520 	drm_helper_mode_fill_fb_struct(ddev, fb, &mode_cmd);
521 	fb->format = drm_format_info(DRM_FORMAT_ARGB8888);
522 	fb->obj[0] = &sfb->obj->base;
523 	error = drm_framebuffer_init(ddev, fb, &rkdrm_framebuffer_funcs);
524 	if (error != 0) {
525 		DRM_ERROR("failed to initialize framebuffer\n");
526 		return error;
527 	}
528 
529 	info = drm_fb_helper_alloc_fbi(helper);
530 	if (IS_ERR(info)) {
531 		DRM_ERROR("Failed to allocate fb_info\n");
532 		return error;
533 	}
534 	info->par = helper;
535 	return 0;
536 }
537