Annotation of src/sys/arch/arm/rockchip/rk_drm.c, Revision 1.9
1.9 ! riastrad 1: /* $NetBSD: rk_drm.c,v 1.8 2021/12/19 11:00:46 riastradh Exp $ */
1.1 jmcneill 2:
3: /*-
4: * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca>
5: * All rights reserved.
6: *
7: * Redistribution and use in source and binary forms, with or without
8: * modification, are permitted provided that the following conditions
9: * are met:
10: * 1. Redistributions of source code must retain the above copyright
11: * notice, this list of conditions and the following disclaimer.
12: * 2. Redistributions in binary form must reproduce the above copyright
13: * notice, this list of conditions and the following disclaimer in the
14: * documentation and/or other materials provided with the distribution.
15: *
16: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19: * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21: * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23: * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26: * SUCH DAMAGE.
27: */
28:
29: #include <sys/cdefs.h>
1.9 ! riastrad 30: __KERNEL_RCSID(0, "$NetBSD: rk_drm.c,v 1.8 2021/12/19 11:00:46 riastradh Exp $");
1.1 jmcneill 31:
32: #include <sys/param.h>
33: #include <sys/bus.h>
1.9 ! riastrad 34: #include <sys/conf.h>
1.1 jmcneill 35: #include <sys/device.h>
36: #include <sys/intr.h>
1.9 ! riastrad 37: #include <sys/kernel.h>
1.1 jmcneill 38: #include <sys/systm.h>
39:
1.9 ! riastrad 40: #include <uvm/uvm_device.h>
1.1 jmcneill 41: #include <uvm/uvm_extern.h>
42: #include <uvm/uvm_object.h>
1.9 ! riastrad 43:
! 44: #include <dev/fdt/fdt_port.h>
! 45: #include <dev/fdt/fdtvar.h>
! 46:
! 47: #include <arm/rockchip/rk_drm.h>
1.1 jmcneill 48:
1.8 riastrad 49: #include <drm/drm_auth.h>
1.9 ! riastrad 50: #include <drm/drm_crtc_helper.h>
1.8 riastrad 51: #include <drm/drm_drv.h>
1.1 jmcneill 52: #include <drm/drm_fb_helper.h>
1.8 riastrad 53: #include <drm/drm_fourcc.h>
54: #include <drm/drm_vblank.h>
1.1 jmcneill 55:
56: #define RK_DRM_MAX_WIDTH 3840
57: #define RK_DRM_MAX_HEIGHT 2160
58:
59: static TAILQ_HEAD(, rk_drm_ports) rk_drm_ports =
60: TAILQ_HEAD_INITIALIZER(rk_drm_ports);
61:
1.4 thorpej 62: static const struct device_compatible_entry compat_data[] = {
63: { .compat = "rockchip,display-subsystem" },
64: DEVICE_COMPAT_EOL
1.1 jmcneill 65: };
66:
67: static const char * fb_compatible[] = {
68: "simple-framebuffer",
69: NULL
70: };
71:
72: static int rk_drm_match(device_t, cfdata_t, void *);
73: static void rk_drm_attach(device_t, device_t, void *);
74:
75: static void rk_drm_init(device_t);
76: static vmem_t *rk_drm_alloc_cma_pool(struct drm_device *, size_t);
77:
78: static int rk_drm_set_busid(struct drm_device *, struct drm_master *);
79:
80: static uint32_t rk_drm_get_vblank_counter(struct drm_device *, unsigned int);
81: static int rk_drm_enable_vblank(struct drm_device *, unsigned int);
82: static void rk_drm_disable_vblank(struct drm_device *, unsigned int);
83:
84: static int rk_drm_load(struct drm_device *, unsigned long);
1.8 riastrad 85: static void rk_drm_unload(struct drm_device *);
1.1 jmcneill 86:
87: static struct drm_driver rk_drm_driver = {
1.8 riastrad 88: .driver_features = DRIVER_MODESET | DRIVER_GEM,
1.1 jmcneill 89: .dev_priv_size = 0,
90: .load = rk_drm_load,
91: .unload = rk_drm_unload,
92:
93: .gem_free_object = drm_gem_cma_free_object,
94: .mmap_object = drm_gem_or_legacy_mmap_object,
95: .gem_uvm_ops = &drm_gem_cma_uvm_ops,
96:
97: .dumb_create = drm_gem_cma_dumb_create,
98: .dumb_destroy = drm_gem_dumb_destroy,
99:
100: .get_vblank_counter = rk_drm_get_vblank_counter,
101: .enable_vblank = rk_drm_enable_vblank,
102: .disable_vblank = rk_drm_disable_vblank,
103:
104: .name = DRIVER_NAME,
105: .desc = DRIVER_DESC,
106: .date = DRIVER_DATE,
107: .major = DRIVER_MAJOR,
108: .minor = DRIVER_MINOR,
109: .patchlevel = DRIVER_PATCHLEVEL,
110:
111: .set_busid = rk_drm_set_busid,
112: };
113:
114: CFATTACH_DECL_NEW(rk_drm, sizeof(struct rk_drm_softc),
115: rk_drm_match, rk_drm_attach, NULL, NULL);
116:
117: static int
118: rk_drm_match(device_t parent, cfdata_t cf, void *aux)
119: {
120: struct fdt_attach_args * const faa = aux;
121:
1.4 thorpej 122: return of_compatible_match(faa->faa_phandle, compat_data);
1.1 jmcneill 123: }
124:
125: static void
126: rk_drm_attach(device_t parent, device_t self, void *aux)
127: {
128: struct rk_drm_softc * const sc = device_private(self);
129: struct fdt_attach_args * const faa = aux;
130: struct drm_driver * const driver = &rk_drm_driver;
131: prop_dictionary_t dict = device_properties(self);
132: bool is_disabled;
133:
134: sc->sc_dev = self;
135: sc->sc_dmat = faa->faa_dmat;
136: sc->sc_bst = faa->faa_bst;
137: sc->sc_phandle = faa->faa_phandle;
138:
139: aprint_naive("\n");
140:
141: if (prop_dictionary_get_bool(dict, "disabled", &is_disabled) && is_disabled) {
142: aprint_normal(": (disabled)\n");
143: return;
144: }
145:
146: aprint_normal("\n");
147:
148: sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev);
149: if (sc->sc_ddev == NULL) {
150: aprint_error_dev(self, "couldn't allocate DRM device\n");
151: return;
152: }
153: sc->sc_ddev->dev_private = sc;
154: sc->sc_ddev->bst = sc->sc_bst;
155: sc->sc_ddev->bus_dmat = sc->sc_dmat;
156: sc->sc_ddev->dmat = sc->sc_ddev->bus_dmat;
157: sc->sc_ddev->dmat_subregion_p = false;
158:
159: fdt_remove_bycompat(fb_compatible);
160:
161: config_defer(self, rk_drm_init);
162: }
163:
164: static void
165: rk_drm_init(device_t dev)
166: {
167: struct rk_drm_softc * const sc = device_private(dev);
168: struct drm_driver * const driver = &rk_drm_driver;
169: int error;
170:
171: error = -drm_dev_register(sc->sc_ddev, 0);
172: if (error) {
173: aprint_error_dev(dev, "couldn't register DRM device: %d\n",
174: error);
175: return;
176: }
177:
178: aprint_normal_dev(dev, "initialized %s %d.%d.%d %s on minor %d\n",
179: driver->name, driver->major, driver->minor, driver->patchlevel,
180: driver->date, sc->sc_ddev->primary->index);
181: }
182:
183: static vmem_t *
184: rk_drm_alloc_cma_pool(struct drm_device *ddev, size_t cma_size)
185: {
186: struct rk_drm_softc * const sc = rk_drm_private(ddev);
187: bus_dma_segment_t segs[1];
188: int nsegs;
189: int error;
190:
191: error = bus_dmamem_alloc(sc->sc_dmat, cma_size, PAGE_SIZE, 0,
192: segs, 1, &nsegs, BUS_DMA_NOWAIT);
193: if (error) {
194: aprint_error_dev(sc->sc_dev, "couldn't allocate CMA pool\n");
195: return NULL;
196: }
197:
198: return vmem_create("rkdrm", segs[0].ds_addr, segs[0].ds_len,
199: PAGE_SIZE, NULL, NULL, NULL, 0, VM_SLEEP, IPL_NONE);
200: }
201:
202: static int
203: rk_drm_set_busid(struct drm_device *ddev, struct drm_master *master)
204: {
205: struct rk_drm_softc * const sc = rk_drm_private(ddev);
206: char id[32];
207:
208: snprintf(id, sizeof(id), "platform:rk:%u", device_unit(sc->sc_dev));
209:
210: master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL);
211: if (master->unique == NULL)
212: return -ENOMEM;
213: strcpy(master->unique, id);
214: master->unique_len = strlen(master->unique);
215:
216: return 0;
217: }
218:
219: static int
220: rk_drm_fb_create_handle(struct drm_framebuffer *fb,
221: struct drm_file *file, unsigned int *handle)
222: {
223: struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(fb);
224:
225: return drm_gem_handle_create(file, &sfb->obj->base, handle);
226: }
227:
228: static void
229: rk_drm_fb_destroy(struct drm_framebuffer *fb)
230: {
231: struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(fb);
232:
233: drm_framebuffer_cleanup(fb);
1.8 riastrad 234: drm_gem_object_put_unlocked(&sfb->obj->base);
1.1 jmcneill 235: kmem_free(sfb, sizeof(*sfb));
236: }
237:
238: static const struct drm_framebuffer_funcs rk_drm_framebuffer_funcs = {
239: .create_handle = rk_drm_fb_create_handle,
240: .destroy = rk_drm_fb_destroy,
241: };
242:
243: static struct drm_framebuffer *
244: rk_drm_fb_create(struct drm_device *ddev, struct drm_file *file,
1.8 riastrad 245: const struct drm_mode_fb_cmd2 *cmd)
1.1 jmcneill 246: {
247: struct rk_drm_framebuffer *fb;
248: struct drm_gem_object *gem_obj;
249: int error;
250:
251: if (cmd->flags)
252: return NULL;
253:
1.8 riastrad 254: gem_obj = drm_gem_object_lookup(file, cmd->handles[0]);
1.1 jmcneill 255: if (gem_obj == NULL)
256: return NULL;
257:
258: fb = kmem_zalloc(sizeof(*fb), KM_SLEEP);
259: fb->obj = to_drm_gem_cma_obj(gem_obj);
260: fb->base.pitches[0] = cmd->pitches[0];
261: fb->base.pitches[1] = cmd->pitches[1];
262: fb->base.pitches[2] = cmd->pitches[2];
263: fb->base.offsets[0] = cmd->offsets[0];
264: fb->base.offsets[1] = cmd->offsets[2];
265: fb->base.offsets[2] = cmd->offsets[1];
266: fb->base.width = cmd->width;
267: fb->base.height = cmd->height;
1.8 riastrad 268: fb->base.format = drm_format_info(cmd->pixel_format);
1.1 jmcneill 269:
270: error = drm_framebuffer_init(ddev, &fb->base, &rk_drm_framebuffer_funcs);
271: if (error != 0)
272: goto dealloc;
273:
274: return &fb->base;
275:
276: dealloc:
277: drm_framebuffer_cleanup(&fb->base);
278: kmem_free(fb, sizeof(*fb));
1.8 riastrad 279: drm_gem_object_put_unlocked(gem_obj);
1.1 jmcneill 280:
281: return NULL;
282: }
283:
284: static struct drm_mode_config_funcs rk_drm_mode_config_funcs = {
285: .fb_create = rk_drm_fb_create,
286: };
287:
288: static int
289: rk_drm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes)
290: {
291: struct rk_drm_softc * const sc = rk_drm_private(helper->dev);
292: struct drm_device *ddev = helper->dev;
293: struct rk_drm_framebuffer *sfb = to_rk_drm_framebuffer(helper->fb);
294: struct drm_framebuffer *fb = helper->fb;
295: struct rk_drmfb_attach_args sfa;
296: size_t cma_size;
297: int error;
298:
299: const u_int width = sizes->surface_width;
300: const u_int height = sizes->surface_height;
301: const u_int pitch = width * (32 / 8);
302:
303: const size_t size = roundup(height * pitch, PAGE_SIZE);
304:
305: /* Reserve enough memory for the FB console plus a 4K plane, rounded to 1MB */
306: cma_size = size;
307: cma_size += (RK_DRM_MAX_WIDTH * RK_DRM_MAX_HEIGHT * 4);
308: cma_size = roundup(cma_size, 1024 * 1024);
309: sc->sc_ddev->cma_pool = rk_drm_alloc_cma_pool(sc->sc_ddev, cma_size);
310: if (sc->sc_ddev->cma_pool != NULL)
311: aprint_normal_dev(sc->sc_dev, "reserved %u MB DRAM for CMA\n",
312: (u_int)(cma_size / (1024 * 1024)));
313:
314: sfb->obj = drm_gem_cma_create(ddev, size);
315: if (sfb->obj == NULL) {
316: DRM_ERROR("failed to allocate memory for framebuffer\n");
317: return -ENOMEM;
318: }
319:
320: fb->pitches[0] = pitch;
321: fb->offsets[0] = 0;
322: fb->width = width;
323: fb->height = height;
1.6 mrg 324: #ifdef __ARM_BIG_ENDIAN
1.8 riastrad 325: fb->format = drm_format_info(DRM_FORMAT_BGRX8888);
1.6 mrg 326: #else
1.8 riastrad 327: fb->format = drm_format_info(DRM_FORMAT_XRGB8888);
1.6 mrg 328: #endif
1.1 jmcneill 329:
330: error = drm_framebuffer_init(ddev, fb, &rk_drm_framebuffer_funcs);
331: if (error != 0) {
332: DRM_ERROR("failed to initialize framebuffer\n");
333: return error;
334: }
335:
336: memset(&sfa, 0, sizeof(sfa));
337: sfa.sfa_drm_dev = ddev;
338: sfa.sfa_fb_helper = helper;
339: sfa.sfa_fb_sizes = *sizes;
340: sfa.sfa_fb_bst = sc->sc_bst;
341: sfa.sfa_fb_dmat = sc->sc_dmat;
342: sfa.sfa_fb_linebytes = helper->fb->pitches[0];
343:
1.5 thorpej 344: helper->fbdev = config_found(ddev->dev, &sfa, NULL,
1.7 thorpej 345: CFARGS(.iattr = "rkfbbus"));
1.1 jmcneill 346: if (helper->fbdev == NULL) {
347: DRM_ERROR("unable to attach framebuffer\n");
348: return -ENXIO;
349: }
350:
351: return 0;
352: }
353:
354: static struct drm_fb_helper_funcs rk_drm_fb_helper_funcs = {
355: .fb_probe = rk_drm_fb_probe,
356: };
357:
358: static int
359: rk_drm_load(struct drm_device *ddev, unsigned long flags)
360: {
361: struct rk_drm_softc * const sc = rk_drm_private(ddev);
362: struct rk_drm_ports *sport;
363: struct rk_drm_fbdev *fbdev;
364: struct fdt_endpoint *ep;
365: const u_int *data;
366: int datalen, error, num_crtc, ep_index;
367:
368: drm_mode_config_init(ddev);
369: ddev->mode_config.min_width = 0;
370: ddev->mode_config.min_height = 0;
371: ddev->mode_config.max_width = RK_DRM_MAX_WIDTH;
372: ddev->mode_config.max_height = RK_DRM_MAX_HEIGHT;
373: ddev->mode_config.funcs = &rk_drm_mode_config_funcs;
374:
375: num_crtc = 0;
376: data = fdtbus_get_prop(sc->sc_phandle, "ports", &datalen);
377: while (datalen >= 4) {
378: const int crtc_phandle = fdtbus_get_phandle_from_native(be32dec(data));
379:
380: TAILQ_FOREACH(sport, &rk_drm_ports, entries)
381: if (sport->phandle == crtc_phandle && sport->ddev == NULL) {
382: sport->ddev = ddev;
383: for (ep_index = 0; (ep = fdt_endpoint_get_from_index(sport->port, 0, ep_index)) != NULL; ep_index++) {
384: error = fdt_endpoint_activate_direct(ep, true);
385: if (error != 0)
386: aprint_debug_dev(sc->sc_dev,
387: "failed to activate endpoint %d: %d\n",
388: ep_index, error);
389: }
390: num_crtc++;
391: }
392:
393: datalen -= 4;
394: data++;
395: }
396:
397: if (num_crtc == 0) {
398: aprint_error_dev(sc->sc_dev, "no display interface ports configured\n");
1.3 mrg 399: error = ENXIO;
400: goto drmerr;
1.1 jmcneill 401: }
402:
403: fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP);
404:
405: drm_fb_helper_prepare(ddev, &fbdev->helper, &rk_drm_fb_helper_funcs);
406:
1.8 riastrad 407: error = drm_fb_helper_init(ddev, &fbdev->helper, num_crtc);
1.1 jmcneill 408: if (error)
1.3 mrg 409: goto allocerr;
1.1 jmcneill 410:
411: fbdev->helper.fb = kmem_zalloc(sizeof(struct rk_drm_framebuffer), KM_SLEEP);
412:
413: drm_fb_helper_single_add_all_connectors(&fbdev->helper);
414:
415: drm_helper_disable_unused_functions(ddev);
416:
417: drm_fb_helper_initial_config(&fbdev->helper, 32);
418:
419: /* XXX */
420: ddev->irq_enabled = true;
421: drm_vblank_init(ddev, num_crtc);
422:
423: return 0;
424:
1.3 mrg 425: allocerr:
426: kmem_free(fbdev, sizeof(*fbdev));
1.1 jmcneill 427: drmerr:
428: drm_mode_config_cleanup(ddev);
429:
430: return error;
431: }
432:
433: static uint32_t
434: rk_drm_get_vblank_counter(struct drm_device *ddev, unsigned int crtc)
435: {
436: struct rk_drm_softc * const sc = rk_drm_private(ddev);
437:
438: if (crtc >= __arraycount(sc->sc_vbl))
439: return 0;
440:
441: if (sc->sc_vbl[crtc].get_vblank_counter == NULL)
442: return 0;
443:
444: return sc->sc_vbl[crtc].get_vblank_counter(sc->sc_vbl[crtc].priv);
445: }
446:
447: static int
448: rk_drm_enable_vblank(struct drm_device *ddev, unsigned int crtc)
449: {
450: struct rk_drm_softc * const sc = rk_drm_private(ddev);
451:
452: if (crtc >= __arraycount(sc->sc_vbl))
453: return 0;
454:
455: if (sc->sc_vbl[crtc].enable_vblank == NULL)
456: return 0;
457:
458: sc->sc_vbl[crtc].enable_vblank(sc->sc_vbl[crtc].priv);
459:
460: return 0;
461: }
462:
463: static void
464: rk_drm_disable_vblank(struct drm_device *ddev, unsigned int crtc)
465: {
466: struct rk_drm_softc * const sc = rk_drm_private(ddev);
467:
468: if (crtc >= __arraycount(sc->sc_vbl))
469: return;
470:
471: if (sc->sc_vbl[crtc].disable_vblank == NULL)
472: return;
473:
474: sc->sc_vbl[crtc].disable_vblank(sc->sc_vbl[crtc].priv);
475: }
476:
1.8 riastrad 477: static void
1.1 jmcneill 478: rk_drm_unload(struct drm_device *ddev)
479: {
480: drm_mode_config_cleanup(ddev);
481: }
482:
483: int
484: rk_drm_register_port(int phandle, struct fdt_device_ports *port)
485: {
486: struct rk_drm_ports *sport;
487:
488: sport = kmem_zalloc(sizeof(*sport), KM_SLEEP);
489: sport->phandle = phandle;
490: sport->port = port;
491: sport->ddev = NULL;
492: TAILQ_INSERT_TAIL(&rk_drm_ports, sport, entries);
493:
494: return 0;
495: }
496:
497: struct drm_device *
498: rk_drm_port_device(struct fdt_device_ports *port)
499: {
500: struct rk_drm_ports *sport;
501:
502: TAILQ_FOREACH(sport, &rk_drm_ports, entries)
503: if (sport->port == port)
504: return sport->ddev;
505:
506: return NULL;
507: }
CVSweb <webmaster@jp.NetBSD.org>