Home | History | Annotate | Line # | Download | only in sunxi
sunxi_drm.c revision 1.1
      1 /* $NetBSD: sunxi_drm.c,v 1.1 2019/01/30 01:24:00 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2019 Jared D. McNeill <jmcneill (at) 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>
     30 __KERNEL_RCSID(0, "$NetBSD: sunxi_drm.c,v 1.1 2019/01/30 01:24:00 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/device.h>
     35 #include <sys/intr.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 #include <sys/conf.h>
     39 
     40 #include <uvm/uvm_extern.h>
     41 #include <uvm/uvm_object.h>
     42 #include <uvm/uvm_device.h>
     43 
     44 #include <drm/drmP.h>
     45 #include <drm/drm_crtc_helper.h>
     46 #include <drm/drm_fb_helper.h>
     47 
     48 #include <dev/fdt/fdtvar.h>
     49 #include <dev/fdt/fdt_port.h>
     50 
     51 #include <arm/sunxi/sunxi_drm.h>
     52 
     53 static TAILQ_HEAD(, sunxi_drm_endpoint) sunxi_drm_endpoints =
     54     TAILQ_HEAD_INITIALIZER(sunxi_drm_endpoints);
     55 
     56 static const char * const compatible[] = {
     57 	"allwinner,sun50i-a64-display-engine",
     58 	NULL
     59 };
     60 
     61 static const char * fb_compatible[] = {
     62 	"allwinner,simple-framebuffer",
     63 	NULL
     64 };
     65 
     66 static int	sunxi_drm_match(device_t, cfdata_t, void *);
     67 static void	sunxi_drm_attach(device_t, device_t, void *);
     68 
     69 static void	sunxi_drm_init(device_t);
     70 
     71 static int	sunxi_drm_set_busid(struct drm_device *, struct drm_master *);
     72 
     73 static int	sunxi_drm_load(struct drm_device *, unsigned long);
     74 static int	sunxi_drm_unload(struct drm_device *);
     75 
     76 static struct drm_driver sunxi_drm_driver = {
     77 	.driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
     78 	.dev_priv_size = 0,
     79 	.load = sunxi_drm_load,
     80 	.unload = sunxi_drm_unload,
     81 
     82 	.gem_free_object = drm_gem_cma_free_object,
     83 	.mmap_object = drm_gem_or_legacy_mmap_object,
     84 	.gem_uvm_ops = &drm_gem_cma_uvm_ops,
     85 
     86 	.dumb_create = drm_gem_cma_dumb_create,
     87 	.dumb_map_offset = drm_gem_cma_dumb_map_offset,
     88 	.dumb_destroy = drm_gem_dumb_destroy,
     89 
     90 #if notyet
     91 	.get_vblank_counter = sunxi_drm_get_vblank_counter,
     92 	.enable_vblank = sunxi_drm_enable_vblank,
     93 	.disable_vblank = sunxi_drm_disable_vblank,
     94 #endif
     95 
     96 	.name = DRIVER_NAME,
     97 	.desc = DRIVER_DESC,
     98 	.date = DRIVER_DATE,
     99 	.major = DRIVER_MAJOR,
    100 	.minor = DRIVER_MINOR,
    101 	.patchlevel = DRIVER_PATCHLEVEL,
    102 
    103 	.set_busid = sunxi_drm_set_busid,
    104 };
    105 
    106 CFATTACH_DECL_NEW(sunxi_drm, sizeof(struct sunxi_drm_softc),
    107 	sunxi_drm_match, sunxi_drm_attach, NULL, NULL);
    108 
    109 static int
    110 sunxi_drm_match(device_t parent, cfdata_t cf, void *aux)
    111 {
    112 	struct fdt_attach_args * const faa = aux;
    113 
    114 	return of_match_compatible(faa->faa_phandle, compatible);
    115 }
    116 
    117 static void
    118 sunxi_drm_attach(device_t parent, device_t self, void *aux)
    119 {
    120 	struct sunxi_drm_softc * const sc = device_private(self);
    121 	struct fdt_attach_args * const faa = aux;
    122 	struct drm_driver * const driver = &sunxi_drm_driver;
    123 
    124 	sc->sc_dev = self;
    125 	sc->sc_dmat = faa->faa_dmat;
    126 	sc->sc_bst = faa->faa_bst;
    127 	sc->sc_phandle = faa->faa_phandle;
    128 
    129 	aprint_naive("\n");
    130 	aprint_normal(": Display Engine Pipeline\n");
    131 
    132 	sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev);
    133 	if (sc->sc_ddev == NULL) {
    134 		aprint_error_dev(self, "couldn't allocate DRM device\n");
    135 		return;
    136 	}
    137 	sc->sc_ddev->dev_private = sc;
    138 	sc->sc_ddev->bst = sc->sc_bst;
    139 	sc->sc_ddev->bus_dmat = sc->sc_dmat;
    140 	sc->sc_ddev->dmat = sc->sc_ddev->bus_dmat;
    141 	sc->sc_ddev->dmat_subregion_p = false;
    142 
    143 	fdt_remove_bycompat(fb_compatible);
    144 
    145 	config_defer(self, sunxi_drm_init);
    146 }
    147 
    148 static void
    149 sunxi_drm_init(device_t dev)
    150 {
    151 	struct sunxi_drm_softc * const sc = device_private(dev);
    152 	struct drm_driver * const driver = &sunxi_drm_driver;
    153 	int error;
    154 
    155 	error = -drm_dev_register(sc->sc_ddev, 0);
    156 	if (error) {
    157 		drm_dev_unref(sc->sc_ddev);
    158 		aprint_error_dev(dev, "couldn't register DRM device: %d\n",
    159 		    error);
    160 		return;
    161 	}
    162 
    163 	aprint_normal_dev(dev, "initialized %s %d.%d.%d %s on minor %d\n",
    164 	    driver->name, driver->major, driver->minor, driver->patchlevel,
    165 	    driver->date, sc->sc_ddev->primary->index);
    166 }
    167 
    168 static int
    169 sunxi_drm_set_busid(struct drm_device *ddev, struct drm_master *master)
    170 {
    171 	struct sunxi_drm_softc * const sc = sunxi_drm_private(ddev);
    172 	char id[32];
    173 
    174 	snprintf(id, sizeof(id), "platform:sunxi:%u", device_unit(sc->sc_dev));
    175 
    176 	master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL);
    177 	if (master->unique == NULL)
    178 		return -ENOMEM;
    179 	strcpy(master->unique, id);
    180 	master->unique_len = strlen(master->unique);
    181 
    182 	return 0;
    183 }
    184 
    185 static int
    186 sunxi_drm_fb_create_handle(struct drm_framebuffer *fb,
    187     struct drm_file *file, unsigned int *handle)
    188 {
    189 	struct sunxi_drm_framebuffer *sfb = to_sunxi_drm_framebuffer(fb);
    190 
    191 	return drm_gem_handle_create(file, &sfb->obj->base, handle);
    192 }
    193 
    194 static void
    195 sunxi_drm_fb_destroy(struct drm_framebuffer *fb)
    196 {
    197 	struct sunxi_drm_framebuffer *sfb = to_sunxi_drm_framebuffer(fb);
    198 
    199 	drm_framebuffer_cleanup(fb);
    200 	drm_gem_object_unreference_unlocked(&sfb->obj->base);
    201 	kmem_free(sfb, sizeof(*sfb));
    202 }
    203 
    204 static const struct drm_framebuffer_funcs sunxi_drm_framebuffer_funcs = {
    205 	.create_handle = sunxi_drm_fb_create_handle,
    206 	.destroy = sunxi_drm_fb_destroy,
    207 };
    208 
    209 static struct drm_framebuffer *
    210 sunxi_drm_fb_create(struct drm_device *ddev, struct drm_file *file,
    211     struct drm_mode_fb_cmd2 *cmd)
    212 {
    213 	struct sunxi_drm_framebuffer *fb;
    214 	struct drm_gem_object *gem_obj;
    215 	int error;
    216 
    217 	if (cmd->flags)
    218 		return NULL;
    219 
    220 	gem_obj = drm_gem_object_lookup(ddev, file, cmd->handles[0]);
    221 	if (gem_obj == NULL)
    222 		return NULL;
    223 
    224 	fb = kmem_zalloc(sizeof(*fb), KM_SLEEP);
    225 	fb->obj = to_drm_gem_cma_obj(gem_obj);
    226 	fb->base.pitches[0] = cmd->pitches[0];
    227 	fb->base.offsets[0] = cmd->offsets[0];
    228 	fb->base.width = cmd->width;
    229 	fb->base.height = cmd->height;
    230 	fb->base.pixel_format = cmd->pixel_format;
    231 	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
    232 	    &fb->base.bits_per_pixel);
    233 
    234 	error = drm_framebuffer_init(ddev, &fb->base, &sunxi_drm_framebuffer_funcs);
    235 	if (error != 0)
    236 		goto dealloc;
    237 
    238 	return &fb->base;
    239 
    240 dealloc:
    241 	drm_framebuffer_cleanup(&fb->base);
    242 	kmem_free(fb, sizeof(*fb));
    243 	drm_gem_object_unreference_unlocked(gem_obj);
    244 
    245 	return NULL;
    246 }
    247 
    248 static struct drm_mode_config_funcs sunxi_drm_mode_config_funcs = {
    249 	.fb_create = sunxi_drm_fb_create,
    250 };
    251 
    252 static int
    253 sunxi_drm_fb_probe(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes)
    254 {
    255 	struct sunxi_drm_softc * const sc = sunxi_drm_private(helper->dev);
    256 	struct drm_device *ddev = helper->dev;
    257 	struct sunxi_drm_framebuffer *sfb = to_sunxi_drm_framebuffer(helper->fb);
    258 	struct drm_framebuffer *fb = helper->fb;
    259 	struct sunxi_drmfb_attach_args sfa;
    260 	int error;
    261 
    262 	const u_int width = sizes->surface_width;
    263 	const u_int height = sizes->surface_height;
    264 	const u_int pitch = width * (32 / 8);
    265 
    266 	const size_t size = roundup(height * pitch, PAGE_SIZE);
    267 
    268 	sfb->obj = drm_gem_cma_create(ddev, size);
    269 	if (sfb->obj == NULL) {
    270 		DRM_ERROR("failed to allocate memory for framebuffer\n");
    271 		return -ENOMEM;
    272 	}
    273 
    274 	fb->pitches[0] = pitch;
    275 	fb->offsets[0] = 0;
    276 	fb->width = width;
    277 	fb->height = height;
    278 	fb->pixel_format = DRM_FORMAT_XRGB8888;
    279 	drm_fb_get_bpp_depth(fb->pixel_format, &fb->depth, &fb->bits_per_pixel);
    280 
    281 	error = drm_framebuffer_init(ddev, fb, &sunxi_drm_framebuffer_funcs);
    282 	if (error != 0) {
    283 		DRM_ERROR("failed to initialize framebuffer\n");
    284 		return error;
    285 	}
    286 
    287 	memset(&sfa, 0, sizeof(sfa));
    288 	sfa.sfa_drm_dev = ddev;
    289 	sfa.sfa_fb_helper = helper;
    290 	sfa.sfa_fb_sizes = *sizes;
    291 	sfa.sfa_fb_bst = sc->sc_bst;
    292 	sfa.sfa_fb_dmat = sc->sc_dmat;
    293 	sfa.sfa_fb_linebytes = helper->fb->pitches[0];
    294 
    295 	helper->fbdev = config_found_ia(ddev->dev, "sunxifbbus", &sfa, NULL);
    296 	if (helper->fbdev == NULL) {
    297 		DRM_ERROR("unable to attach framebuffer\n");
    298 		return -ENXIO;
    299 	}
    300 
    301 	return 0;
    302 }
    303 
    304 static struct drm_fb_helper_funcs sunxi_drm_fb_helper_funcs = {
    305 	.fb_probe = sunxi_drm_fb_probe,
    306 };
    307 
    308 static int
    309 sunxi_drm_load(struct drm_device *ddev, unsigned long flags)
    310 {
    311 	struct sunxi_drm_softc * const sc = sunxi_drm_private(ddev);
    312 	struct sunxi_drm_endpoint *sep;
    313 	struct sunxi_drm_fbdev *fbdev;
    314 	const u_int *data;
    315 	int datalen, error, num_crtc;
    316 
    317 	drm_mode_config_init(ddev);
    318 	ddev->mode_config.min_width = 0;
    319 	ddev->mode_config.min_height = 0;
    320 	ddev->mode_config.max_width = 3840;
    321 	ddev->mode_config.max_height = 2160;
    322 	ddev->mode_config.funcs = &sunxi_drm_mode_config_funcs;
    323 
    324 	num_crtc = 0;
    325 	data = fdtbus_get_prop(sc->sc_phandle, "allwinner,pipelines", &datalen);
    326 	while (datalen >= 4) {
    327 		const int crtc_phandle = fdtbus_get_phandle_from_native(be32dec(data));
    328 
    329 		TAILQ_FOREACH(sep, &sunxi_drm_endpoints, entries)
    330 			if (sep->phandle == crtc_phandle && sep->ddev == NULL) {
    331 				sep->ddev = ddev;
    332 				error = fdt_endpoint_activate_direct(sep->ep, true);
    333 				if (error != 0) {
    334 					aprint_error_dev(sc->sc_dev, "failed to activate endpoint: %d\n",
    335 					    error);
    336 				}
    337 				if (fdt_endpoint_type(sep->ep) == EP_DRM_CRTC)
    338 					num_crtc++;
    339 			}
    340 
    341 		datalen -= 4;
    342 		data++;
    343 	}
    344 
    345 	if (num_crtc == 0) {
    346 		aprint_error_dev(sc->sc_dev, "no pipelines configured\n");
    347 		return ENXIO;
    348 	}
    349 
    350 	fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP);
    351 
    352 	drm_fb_helper_prepare(ddev, &fbdev->helper, &sunxi_drm_fb_helper_funcs);
    353 
    354 	error = drm_fb_helper_init(ddev, &fbdev->helper, num_crtc, num_crtc);
    355 	if (error)
    356 		goto drmerr;
    357 
    358 	fbdev->helper.fb = kmem_zalloc(sizeof(struct sunxi_drm_framebuffer), KM_SLEEP);
    359 
    360 	drm_fb_helper_single_add_all_connectors(&fbdev->helper);
    361 
    362 	drm_helper_disable_unused_functions(ddev);
    363 
    364 	drm_fb_helper_initial_config(&fbdev->helper, 32);
    365 
    366 	return 0;
    367 
    368 drmerr:
    369 	drm_mode_config_cleanup(ddev);
    370 	kmem_free(fbdev, sizeof(*fbdev));
    371 
    372 	return error;
    373 }
    374 
    375 static int
    376 sunxi_drm_unload(struct drm_device *ddev)
    377 {
    378 	drm_mode_config_cleanup(ddev);
    379 
    380 	return 0;
    381 }
    382 
    383 int
    384 sunxi_drm_register_endpoint(int phandle, struct fdt_endpoint *ep)
    385 {
    386 	struct sunxi_drm_endpoint *sep;
    387 
    388 	sep = kmem_zalloc(sizeof(*sep), KM_SLEEP);
    389 	sep->phandle = phandle;
    390 	sep->ep = ep;
    391 	sep->ddev = NULL;
    392 	TAILQ_INSERT_TAIL(&sunxi_drm_endpoints, sep, entries);
    393 
    394 	return 0;
    395 }
    396 
    397 struct drm_device *
    398 sunxi_drm_endpoint_device(struct fdt_endpoint *ep)
    399 {
    400 	struct sunxi_drm_endpoint *sep;
    401 
    402 	TAILQ_FOREACH(sep, &sunxi_drm_endpoints, entries)
    403 		if (sep->ep == ep)
    404 			return sep->ddev;
    405 
    406 	return NULL;
    407 }
    408