Module Name: src Committed By: jmcneill Date: Thu Nov 12 00:43:52 UTC 2015
Modified Files: src/sys/arch/arm/nvidia: files.tegra tegra_drm.c tegra_drm.h tegra_drm_fb.c tegra_drm_mode.c tegra_fb.c Added Files: src/sys/arch/arm/nvidia: tegra_drm_gem.c Log Message: Use GEM for memory management. Fixes a couple issues while here: - No longer needs to allocate 35MB (!) for framebuffer console. - Allows xrandr to switch to modes larger than the framebuffer console. - Removes hack that redirected mmap calls to wsdisplay0 To generate a diff of this commit: cvs rdiff -u -r1.20 -r1.21 src/sys/arch/arm/nvidia/files.tegra cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/nvidia/tegra_drm.c \ src/sys/arch/arm/nvidia/tegra_drm.h cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/nvidia/tegra_drm_fb.c \ src/sys/arch/arm/nvidia/tegra_fb.c cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/nvidia/tegra_drm_gem.c cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/nvidia/tegra_drm_mode.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/nvidia/files.tegra diff -u src/sys/arch/arm/nvidia/files.tegra:1.20 src/sys/arch/arm/nvidia/files.tegra:1.21 --- src/sys/arch/arm/nvidia/files.tegra:1.20 Mon Nov 9 23:05:58 2015 +++ src/sys/arch/arm/nvidia/files.tegra Thu Nov 12 00:43:52 2015 @@ -1,4 +1,4 @@ -# $NetBSD: files.tegra,v 1.20 2015/11/09 23:05:58 jmcneill Exp $ +# $NetBSD: files.tegra,v 1.21 2015/11/12 00:43:52 jmcneill Exp $ # # Configuration info for NVIDIA Tegra ARM Peripherals # @@ -114,6 +114,7 @@ attach tegradrm at tegraio with tegra_dr file arch/arm/nvidia/tegra_drm.c tegra_drm file arch/arm/nvidia/tegra_drm_mode.c tegra_drm file arch/arm/nvidia/tegra_drm_fb.c tegra_drm +file arch/arm/nvidia/tegra_drm_gem.c tegra_drm # Framebuffer console device tegrafb: tegrafbbus, drmfb, wsemuldisplaydev Index: src/sys/arch/arm/nvidia/tegra_drm.c diff -u src/sys/arch/arm/nvidia/tegra_drm.c:1.2 src/sys/arch/arm/nvidia/tegra_drm.c:1.3 --- src/sys/arch/arm/nvidia/tegra_drm.c:1.2 Tue Nov 10 22:14:05 2015 +++ src/sys/arch/arm/nvidia/tegra_drm.c Thu Nov 12 00:43:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_drm.c,v 1.2 2015/11/10 22:14:05 jmcneill Exp $ */ +/* $NetBSD: tegra_drm.c,v 1.3 2015/11/12 00:43:52 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #include "locators.h" #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tegra_drm.c,v 1.2 2015/11/10 22:14:05 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tegra_drm.c,v 1.3 2015/11/12 00:43:52 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -57,27 +57,30 @@ static int tegra_drm_set_busid(struct dr static int tegra_drm_load(struct drm_device *, unsigned long); static int tegra_drm_unload(struct drm_device *); -static int tegra_drm_mmap_object(struct drm_device *, off_t, size_t, - vm_prot_t, struct uvm_object **, voff_t *, struct file *); - static int tegra_drm_dumb_create(struct drm_file *, struct drm_device *, struct drm_mode_create_dumb *); static int tegra_drm_dumb_map_offset(struct drm_file *, struct drm_device *, uint32_t, uint64_t *); -static int tegra_drm_dumb_destroy(struct drm_file *, struct drm_device *, - uint32_t); + +static const struct uvm_pagerops tegra_drm_gem_uvm_ops = { + .pgo_reference = drm_gem_pager_reference, + .pgo_detach = drm_gem_pager_detach, + .pgo_fault = tegra_drm_gem_fault, +}; static struct drm_driver tegra_drm_driver = { - .driver_features = DRIVER_MODESET, + .driver_features = DRIVER_MODESET | DRIVER_GEM, .dev_priv_size = 0, .load = tegra_drm_load, .unload = tegra_drm_unload, - .mmap_object = tegra_drm_mmap_object, + .gem_free_object = tegra_drm_gem_free_object, + .mmap_object = drm_gem_or_legacy_mmap_object, + .gem_uvm_ops = &tegra_drm_gem_uvm_ops, .dumb_create = tegra_drm_dumb_create, .dumb_map_offset = tegra_drm_dumb_map_offset, - .dumb_destroy = tegra_drm_dumb_destroy, + .dumb_destroy = drm_gem_dumb_destroy, .get_vblank_counter = tegra_drm_get_vblank_counter, .enable_vblank = tegra_drm_enable_vblank, @@ -114,7 +117,7 @@ tegra_drm_attach(device_t parent, device prop_dictionary_t prop = device_properties(self); struct drm_driver * const driver = &tegra_drm_driver; const char *pin, *dev; - int error, nsegs; + int error; sc->sc_dev = self; sc->sc_dmat = tio->tio_dmat; @@ -145,24 +148,6 @@ tegra_drm_attach(device_t parent, device aprint_naive("\n"); aprint_normal("\n"); - sc->sc_dmasize = 4096 * 2160 * 4; - error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, PAGE_SIZE, 0, - sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK); - if (error) - goto failed; - error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs, - sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT); - if (error) - goto free; - error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1, - sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap); - if (error) - goto unmap; - error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap, - sc->sc_dmasize, NULL, BUS_DMA_WAITOK); - if (error) - goto destroy; - driver->bus = &tegra_drm_bus; sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev); @@ -185,16 +170,6 @@ tegra_drm_attach(device_t parent, device driver->date, sc->sc_ddev->primary->index); return; - -destroy: - bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap); -unmap: - bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize); -free: - bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs); -failed: - - aprint_error_dev(sc->sc_dev, "bus_dma setup failed\n"); } static const char * @@ -256,35 +231,31 @@ tegra_drm_unload(struct drm_device *ddev } static int -tegra_drm_mmap_object(struct drm_device *ddev, off_t offset, size_t size, - vm_prot_t prot, struct uvm_object **uobjp, voff_t *uoffsetp, - struct file *file) -{ - /* XXX */ - extern const struct cdevsw wsdisplay_cdevsw; - devmajor_t maj = cdevsw_lookup_major(&wsdisplay_cdevsw); - dev_t devno = makedev(maj, 0); - struct uvm_object *uobj; - - KASSERT(offset == (offset & ~(PAGE_SIZE-1))); - - uobj = udv_attach(devno, prot, offset, size); - if (uobj == NULL) - return -EINVAL; - - *uobjp = uobj; - *uoffsetp = offset; - return 0; -} - -static int tegra_drm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev, struct drm_mode_create_dumb *args) { + struct tegra_gem_object *obj; + uint32_t handle; + int error; + args->pitch = args->width * ((args->bpp + 7) / 8); args->size = args->pitch * args->height; + args->size = roundup(args->size, PAGE_SIZE); args->handle = 0; + obj = tegra_drm_obj_alloc(ddev, args->size); + if (obj == NULL) + return -ENOMEM; + + error = drm_gem_handle_create(file_priv, &obj->base, &handle); + drm_gem_object_unreference_unlocked(&obj->base); + if (error) { + tegra_drm_obj_free(obj); + return error; + } + + args->handle = handle; + return 0; } @@ -292,13 +263,26 @@ static int tegra_drm_dumb_map_offset(struct drm_file *file_priv, struct drm_device *ddev, uint32_t handle, uint64_t *offset) { - *offset = 0; - return 0; -} + struct drm_gem_object *gem_obj; + struct tegra_gem_object *obj; + int error; -static int -tegra_drm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev, - uint32_t handle) -{ - return 0; + gem_obj = drm_gem_object_lookup(ddev, file_priv, handle); + if (gem_obj == NULL) + return -ENOENT; + + obj = to_tegra_gem_obj(gem_obj); + + if (drm_vma_node_has_offset(&obj->base.vma_node) == 0) { + error = drm_gem_create_mmap_offset(&obj->base); + if (error) + goto done; + } + + *offset = drm_vma_node_offset_addr(&obj->base.vma_node); + +done: + drm_gem_object_unreference_unlocked(&obj->base); + + return error; } Index: src/sys/arch/arm/nvidia/tegra_drm.h diff -u src/sys/arch/arm/nvidia/tegra_drm.h:1.2 src/sys/arch/arm/nvidia/tegra_drm.h:1.3 --- src/sys/arch/arm/nvidia/tegra_drm.h:1.2 Tue Nov 10 22:14:05 2015 +++ src/sys/arch/arm/nvidia/tegra_drm.h Thu Nov 12 00:43:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_drm.h,v 1.2 2015/11/10 22:14:05 jmcneill Exp $ */ +/* $NetBSD: tegra_drm.h,v 1.3 2015/11/12 00:43:52 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -48,6 +48,7 @@ struct tegra_drm_softc { struct drm_device *sc_ddev; bus_space_tag_t sc_bst; + bus_dma_tag_t sc_dmat; device_t sc_ddcdev; struct tegra_gpio_pin *sc_pin_hpd; @@ -56,12 +57,6 @@ struct tegra_drm_softc { bool sc_force_dvi; - bus_dma_tag_t sc_dmat; - bus_dma_segment_t sc_dmasegs[1]; - bus_size_t sc_dmasize; - bus_dmamap_t sc_dmamap; - void *sc_dmap; - uint32_t sc_vbl_received[2]; }; @@ -93,14 +88,25 @@ struct tegra_encoder { struct tegra_connector { struct drm_connector base; device_t ddcdev; + struct i2c_adapter *adapter; struct tegra_gpio_pin *hpd; bool has_hdmi_sink; bool has_audio; }; +struct tegra_gem_object { + struct drm_gem_object base; + bus_dma_tag_t dmat; + bus_dma_segment_t dmasegs[1]; + bus_size_t dmasize; + bus_dmamap_t dmamap; + void *dmap; +}; + struct tegra_framebuffer { struct drm_framebuffer base; + struct tegra_gem_object *obj; }; struct tegra_fbdev { @@ -129,11 +135,21 @@ struct tegra_fbdev { #define to_tegra_connector(x) container_of(x, struct tegra_connector, base) #define to_tegra_framebuffer(x) container_of(x, struct tegra_framebuffer, base) #define to_tegra_fbdev(x) container_of(x, struct tegra_fbdev, helper) +#define to_tegra_gem_obj(x) container_of(x, struct tegra_gem_object, base) int tegra_drm_mode_init(struct drm_device *); int tegra_drm_fb_init(struct drm_device *); u32 tegra_drm_get_vblank_counter(struct drm_device *, int); int tegra_drm_enable_vblank(struct drm_device *, int); void tegra_drm_disable_vblank(struct drm_device *, int); +int tegra_drm_framebuffer_init(struct drm_device *, + struct tegra_framebuffer *); + +struct tegra_gem_object *tegra_drm_obj_alloc(struct drm_device *, size_t); +void tegra_drm_obj_free(struct tegra_gem_object *); + +int tegra_drm_gem_fault(struct uvm_faultinfo *, vaddr_t, struct vm_page **, + int, int, vm_prot_t, int); +void tegra_drm_gem_free_object(struct drm_gem_object *); #endif /* _ARM_TEGRA_DRM_H */ Index: src/sys/arch/arm/nvidia/tegra_drm_fb.c diff -u src/sys/arch/arm/nvidia/tegra_drm_fb.c:1.1 src/sys/arch/arm/nvidia/tegra_drm_fb.c:1.2 --- src/sys/arch/arm/nvidia/tegra_drm_fb.c:1.1 Mon Nov 9 23:05:58 2015 +++ src/sys/arch/arm/nvidia/tegra_drm_fb.c Thu Nov 12 00:43:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_drm_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */ +/* $NetBSD: tegra_drm_fb.c,v 1.2 2015/11/12 00:43:52 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tegra_drm_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tegra_drm_fb.c,v 1.2 2015/11/12 00:43:52 jmcneill Exp $"); #include <drm/drmP.h> #include <drm/drm_crtc.h> @@ -36,6 +36,9 @@ __KERNEL_RCSID(0, "$NetBSD: tegra_drm_fb #include <arm/nvidia/tegra_drm.h> +static int tegra_fb_init(struct drm_device *, struct drm_framebuffer *, + struct drm_fb_helper_surface_size *); + static int tegra_fb_probe(struct drm_fb_helper *, struct drm_fb_helper_surface_size *); @@ -47,8 +50,6 @@ int tegra_drm_fb_init(struct drm_device *ddev) { struct tegra_fbdev *fbdev; - struct drm_framebuffer *fb; - struct drm_mode_fb_cmd2 cmd; int error; fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP); @@ -62,18 +63,22 @@ tegra_drm_fb_init(struct drm_device *dde return error; } - memset(&cmd, 0, sizeof(cmd)); - cmd.width = 4096; - cmd.height = 2160; - cmd.pixel_format = DRM_FORMAT_ARGB8888; - cmd.pitches[0] = cmd.width * (32 / 8); - - fb = ddev->mode_config.funcs->fb_create(ddev, NULL, &cmd); - if (fb == NULL) { - DRM_ERROR("couldn't create framebuffer\n"); - return -EIO; + /* + * This might seem silly, but it saves us from having to allocate + * enough memory for the largest possible framebuffer (around 35MB). + * + * Allocate the fb_helper's framebuffer here but don't initialize + * it yet. The fb helper won't configure a CRTC unless this field is + * set, and we don't know the preferred size at this time. + * + * The fb_probe callback will eventually be called with the preferred + * surface size, so defer allocating the buffer object until then. + */ + fbdev->helper.fb = + kmem_zalloc(sizeof(struct tegra_framebuffer), KM_SLEEP); + if (fbdev->helper.fb == NULL) { + DRM_ERROR("failed to create framebuffer\n"); } - fbdev->helper.fb = fb; drm_fb_helper_single_add_all_connectors(&fbdev->helper); @@ -92,6 +97,11 @@ tegra_fb_probe(struct drm_fb_helper *hel struct drm_device *ddev = helper->dev; struct tegra_drmfb_attach_args tfa; + if (tegra_fb_init(ddev, helper->fb, sizes) != 0) { + DRM_ERROR("failed to initialize framebuffer\n"); + return -ENOMEM; + } + memset(&tfa, 0, sizeof(tfa)); tfa.tfa_drm_dev = ddev; tfa.tfa_fb_helper = helper; @@ -107,3 +117,30 @@ tegra_fb_probe(struct drm_fb_helper *hel return 0; } + +static int +tegra_fb_init(struct drm_device *ddev, struct drm_framebuffer *fb, + struct drm_fb_helper_surface_size *sizes) +{ + struct tegra_framebuffer *tegra_fb = to_tegra_framebuffer(fb); + const u_int width = sizes->surface_width; + const u_int height = sizes->surface_height; + const u_int pitch = width * (32 / 8); + + const size_t size = roundup(height * pitch, PAGE_SIZE); + + tegra_fb->obj = tegra_drm_obj_alloc(ddev, size); + if (tegra_fb->obj == NULL) + return -ENOMEM; + + fb->pitches[0] = pitch; + fb->offsets[0] = 0; + fb->width = width; + fb->height = height; + fb->pixel_format = DRM_FORMAT_ARGB8888; + drm_fb_get_bpp_depth(fb->pixel_format, &fb->depth, + &fb->bits_per_pixel); + tegra_drm_framebuffer_init(ddev, tegra_fb); + + return 0; +} Index: src/sys/arch/arm/nvidia/tegra_fb.c diff -u src/sys/arch/arm/nvidia/tegra_fb.c:1.1 src/sys/arch/arm/nvidia/tegra_fb.c:1.2 --- src/sys/arch/arm/nvidia/tegra_fb.c:1.1 Mon Nov 9 23:05:58 2015 +++ src/sys/arch/arm/nvidia/tegra_fb.c Thu Nov 12 00:43:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */ +/* $NetBSD: tegra_fb.c,v 1.2 2015/11/12 00:43:52 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tegra_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tegra_fb.c,v 1.2 2015/11/12 00:43:52 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -49,6 +49,7 @@ struct tegra_fb_softc { device_t sc_dev; struct tegra_drm_softc *sc_drm; struct tegra_drmfb_attach_args sc_tfa; + struct tegra_framebuffer *sc_fb; }; static paddr_t tegra_fb_mmapfb(struct drmfb_softc *, off_t, int); @@ -83,6 +84,7 @@ tegra_fb_attach(device_t parent, device_ sc->sc_dev = self; sc->sc_drm = drmsc; sc->sc_tfa = *tfa; + sc->sc_fb = to_tegra_framebuffer(tfa->tfa_fb_helper->fb); aprint_naive("\n"); aprint_normal("\n"); @@ -90,7 +92,7 @@ tegra_fb_attach(device_t parent, device_ da.da_dev = self; da.da_fb_helper = tfa->tfa_fb_helper; da.da_fb_sizes = &tfa->tfa_fb_sizes; - da.da_fb_vaddr = drmsc->sc_dmap; + da.da_fb_vaddr = sc->sc_fb->obj->dmap; da.da_params = &tegrafb_drmfb_params; error = drmfb_attach(&sc->sc_drmfb, &da); @@ -114,12 +116,13 @@ static paddr_t tegra_fb_mmapfb(struct drmfb_softc *sc, off_t off, int prot) { struct tegra_fb_softc * const tfb_sc = (struct tegra_fb_softc *)sc; + struct tegra_gem_object *obj = tfb_sc->sc_fb->obj; KASSERT(off >= 0); - KASSERT(off < tfb_sc->sc_drm->sc_dmasize); + KASSERT(off < obj->dmasize); - return bus_dmamem_mmap(tfb_sc->sc_drm->sc_dmat, - tfb_sc->sc_drm->sc_dmasegs, 1, off, prot, BUS_DMA_PREFETCHABLE); + return bus_dmamem_mmap(obj->dmat, obj->dmasegs, 1, off, prot, + BUS_DMA_PREFETCHABLE); } static int Index: src/sys/arch/arm/nvidia/tegra_drm_mode.c diff -u src/sys/arch/arm/nvidia/tegra_drm_mode.c:1.4 src/sys/arch/arm/nvidia/tegra_drm_mode.c:1.5 --- src/sys/arch/arm/nvidia/tegra_drm_mode.c:1.4 Tue Nov 10 22:14:05 2015 +++ src/sys/arch/arm/nvidia/tegra_drm_mode.c Thu Nov 12 00:43:52 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: tegra_drm_mode.c,v 1.4 2015/11/10 22:14:05 jmcneill Exp $ */ +/* $NetBSD: tegra_drm_mode.c,v 1.5 2015/11/12 00:43:52 jmcneill Exp $ */ /*- * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: tegra_drm_mode.c,v 1.4 2015/11/10 22:14:05 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: tegra_drm_mode.c,v 1.5 2015/11/12 00:43:52 jmcneill Exp $"); #include <drm/drmP.h> #include <drm/drm_crtc.h> @@ -50,9 +50,12 @@ static const struct drm_mode_config_func .fb_create = tegra_fb_create }; +static int tegra_framebuffer_create_handle(struct drm_framebuffer *, + struct drm_file *, unsigned int *); static void tegra_framebuffer_destroy(struct drm_framebuffer *); static const struct drm_framebuffer_funcs tegra_framebuffer_funcs = { + .create_handle = tegra_framebuffer_create_handle, .destroy = tegra_framebuffer_destroy }; @@ -195,11 +198,19 @@ tegra_drm_mode_init(struct drm_device *d return 0; } +int +tegra_drm_framebuffer_init(struct drm_device *ddev, + struct tegra_framebuffer *fb) +{ + return drm_framebuffer_init(ddev, &fb->base, &tegra_framebuffer_funcs); +} + static struct drm_framebuffer * tegra_fb_create(struct drm_device *ddev, struct drm_file *file, struct drm_mode_fb_cmd2 *cmd) { struct tegra_framebuffer *fb; + struct drm_gem_object *gem_obj; int error; if (cmd->flags) @@ -209,10 +220,15 @@ tegra_fb_create(struct drm_device *ddev, return NULL; } + gem_obj = drm_gem_object_lookup(ddev, file, cmd->handles[0]); + if (gem_obj == NULL) + return NULL; + fb = kmem_zalloc(sizeof(*fb), KM_SLEEP); if (fb == NULL) - return NULL; + goto unref; + fb->obj = to_tegra_gem_obj(gem_obj); fb->base.pitches[0] = cmd->pitches[0]; fb->base.offsets[0] = cmd->offsets[0]; fb->base.width = cmd->width; @@ -221,7 +237,7 @@ tegra_fb_create(struct drm_device *ddev, drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth, &fb->base.bits_per_pixel); - error = drm_framebuffer_init(ddev, &fb->base, &tegra_framebuffer_funcs); + error = tegra_drm_framebuffer_init(ddev, fb); if (error) goto dealloc; @@ -230,15 +246,28 @@ tegra_fb_create(struct drm_device *ddev, drm_framebuffer_cleanup(&fb->base); dealloc: kmem_free(fb, sizeof(*fb)); +unref: + drm_gem_object_unreference_unlocked(gem_obj); + return NULL; } +static int +tegra_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file, unsigned int *handle) +{ + struct tegra_framebuffer *tegra_fb = to_tegra_framebuffer(fb); + + return drm_gem_handle_create(file, &tegra_fb->obj->base, handle); +} + static void tegra_framebuffer_destroy(struct drm_framebuffer *fb) { struct tegra_framebuffer *tegra_fb = to_tegra_framebuffer(fb); drm_framebuffer_cleanup(fb); + drm_gem_object_unreference_unlocked(&tegra_fb->obj->base); kmem_free(tegra_fb, sizeof(*tegra_fb)); } @@ -433,17 +462,14 @@ static int tegra_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb, int x, int y, int atomic) { - struct tegra_drm_softc * const sc = tegra_drm_private(crtc->dev); struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc); -#if 0 struct tegra_framebuffer *tegra_fb = atomic ? to_tegra_framebuffer(fb) : to_tegra_framebuffer(crtc->primary->fb); -#endif /* Framebuffer start address */ DC_WRITE(tegra_crtc, DC_WINBUF_A_START_ADDR_REG, - (uint32_t)sc->sc_dmamap->dm_segs[0].ds_addr); + (uint32_t)tegra_fb->obj->dmamap->dm_segs[0].ds_addr); /* Offsets */ DC_WRITE(tegra_crtc, DC_WINBUF_A_ADDR_H_OFFSET_REG, x); Added files: Index: src/sys/arch/arm/nvidia/tegra_drm_gem.c diff -u /dev/null src/sys/arch/arm/nvidia/tegra_drm_gem.c:1.1 --- /dev/null Thu Nov 12 00:43:52 2015 +++ src/sys/arch/arm/nvidia/tegra_drm_gem.c Thu Nov 12 00:43:52 2015 @@ -0,0 +1,158 @@ +/* $NetBSD: tegra_drm_gem.c,v 1.1 2015/11/12 00:43:52 jmcneill Exp $ */ + +/*- + * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: tegra_drm_gem.c,v 1.1 2015/11/12 00:43:52 jmcneill Exp $"); + +#include <drm/drmP.h> +#include <uvm/uvm.h> + +#include <arm/nvidia/tegra_drm.h> + +struct tegra_gem_object * +tegra_drm_obj_alloc(struct drm_device *ddev, size_t size) +{ + struct tegra_drm_softc * const sc = tegra_drm_private(ddev); + struct tegra_gem_object *obj; + int error, nsegs; + + obj = kmem_zalloc(sizeof(*obj), KM_SLEEP); + if (obj == NULL) + return NULL; + + obj->dmat = sc->sc_dmat; + obj->dmasize = size; + + error = bus_dmamem_alloc(obj->dmat, obj->dmasize, PAGE_SIZE, 0, + obj->dmasegs, 1, &nsegs, BUS_DMA_WAITOK); + if (error) + goto failed; + error = bus_dmamem_map(obj->dmat, obj->dmasegs, nsegs, + obj->dmasize, &obj->dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT); + if (error) + goto free; + error = bus_dmamap_create(obj->dmat, obj->dmasize, 1, + obj->dmasize, 0, BUS_DMA_WAITOK, &obj->dmamap); + if (error) + goto unmap; + error = bus_dmamap_load(obj->dmat, obj->dmamap, obj->dmap, + obj->dmasize, NULL, BUS_DMA_WAITOK); + if (error) + goto destroy; + + drm_gem_private_object_init(ddev, &obj->base, size); + + return obj; + +destroy: + bus_dmamap_destroy(obj->dmat, obj->dmamap); +unmap: + bus_dmamem_unmap(obj->dmat, obj->dmap, obj->dmasize); +free: + bus_dmamem_free(obj->dmat, obj->dmasegs, nsegs); +failed: + kmem_free(obj, sizeof(*obj)); + + return NULL; +} + +void +tegra_drm_obj_free(struct tegra_gem_object *obj) +{ + bus_dmamap_unload(obj->dmat, obj->dmamap); + bus_dmamap_destroy(obj->dmat, obj->dmamap); + bus_dmamem_unmap(obj->dmat, obj->dmap, obj->dmasize); + bus_dmamem_free(obj->dmat, obj->dmasegs, 1); + kmem_free(obj, sizeof(*obj)); +} + +void +tegra_drm_gem_free_object(struct drm_gem_object *gem_obj) +{ + struct tegra_gem_object *obj = to_tegra_gem_obj(gem_obj); + + drm_gem_free_mmap_offset(gem_obj); + drm_gem_object_release(gem_obj); + tegra_drm_obj_free(obj); +} + +int +tegra_drm_gem_fault(struct uvm_faultinfo *ufi, vaddr_t vaddr, + struct vm_page **pps, int npages, int centeridx, vm_prot_t access_type, + int flags) +{ + struct vm_map_entry *entry = ufi->entry; + struct uvm_object *uobj = entry->object.uvm_obj; + struct drm_gem_object *gem_obj = + container_of(uobj, struct drm_gem_object, gemo_uvmobj); + struct tegra_gem_object *obj = to_tegra_gem_obj(gem_obj); + off_t curr_offset; + vaddr_t curr_va; + paddr_t paddr, mdpgno; + u_int mmapflags; + int lcv, retval; + vm_prot_t mapprot; + + if (UVM_ET_ISCOPYONWRITE(entry)) + return -EIO; + + curr_offset = entry->offset + (vaddr - entry->start); + curr_va = vaddr; + + retval = 0; + for (lcv = 0; lcv < npages; lcv++, curr_offset += PAGE_SIZE, + curr_va += PAGE_SIZE) { + if ((flags & PGO_ALLPAGES) == 0 && lcv != centeridx) + continue; + if (pps[lcv] == PGO_DONTCARE) + continue; + + mdpgno = bus_dmamem_mmap(obj->dmat, obj->dmasegs, 1, + curr_offset, access_type, BUS_DMA_PREFETCHABLE); + if (mdpgno == -1) { + retval = -EIO; + break; + } + paddr = pmap_phys_address(mdpgno); + mmapflags = pmap_mmap_flags(mdpgno); + mapprot = ufi->entry->protection; + + if (pmap_enter(ufi->orig_map->pmap, curr_va, paddr, mapprot, + PMAP_CANFAIL | mapprot | mmapflags) != 0) { + pmap_update(ufi->orig_map->pmap); + uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj); + uvm_wait("tegra_drm_gem_fault"); + return -ERESTART; + } + } + + pmap_update(ufi->orig_map->pmap); + uvmfault_unlockall(ufi, ufi->entry->aref.ar_amap, uobj); + + return retval; +}