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