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