Module Name: src Committed By: riastradh Date: Sun Sep 8 16:41:07 UTC 2013
Modified Files: src/sys/external/bsd/drm2/i915drm [riastradh-drm2]: i915_pci.c Log Message: First draft of genfb attachment for i915. To generate a diff of this commit: cvs rdiff -u -r1.1.2.3 -r1.1.2.4 src/sys/external/bsd/drm2/i915drm/i915_pci.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/external/bsd/drm2/i915drm/i915_pci.c diff -u src/sys/external/bsd/drm2/i915drm/i915_pci.c:1.1.2.3 src/sys/external/bsd/drm2/i915drm/i915_pci.c:1.1.2.4 --- src/sys/external/bsd/drm2/i915drm/i915_pci.c:1.1.2.3 Wed Jul 24 03:54:43 2013 +++ src/sys/external/bsd/drm2/i915drm/i915_pci.c Sun Sep 8 16:41:07 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: i915_pci.c,v 1.1.2.3 2013/07/24 03:54:43 riastradh Exp $ */ +/* $NetBSD: i915_pci.c,v 1.1.2.4 2013/09/08 16:41:07 riastradh Exp $ */ /*- * Copyright (c) 2013 The NetBSD Foundation, Inc. @@ -30,27 +30,51 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: i915_pci.c,v 1.1.2.3 2013/07/24 03:54:43 riastradh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: i915_pci.c,v 1.1.2.4 2013/09/08 16:41:07 riastradh Exp $"); #include <sys/types.h> #include <sys/systm.h> +#include <dev/pci/pciio.h> #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#if 0 /* XXX genfb */ +#include <dev/pci/wsdisplay_pci.h> +#include <dev/wsfb/genfbvar.h> +#endif + #include <drm/drmP.h> #include "i915_drv.h" struct i915drm_softc { - struct drm_device sc_drm_dev; - struct pci_dev sc_pci_dev; + device_t sc_dev; + struct drm_device sc_drm_dev; + struct pci_dev sc_pci_dev; + struct drm_i915_gem_object *sc_fb_obj; + bus_space_handle_t sc_fb_bsh; +#if 0 /* XXX genfb */ + struct genfb_softc sc_genfb; +#endif }; static int i915drm_match(device_t, cfdata_t, void *); static void i915drm_attach(device_t, device_t, void *); static int i915drm_detach(device_t, int); +static void i915drm_attach_framebuffer(device_t); +static int i915drm_detach_framebuffer(device_t, int); + +static int i915drm_fb_probe(struct drm_fb_helper *, + struct drm_fb_helper_surface_size *); + +#if 0 /* XXX genfb */ +static int i915drm_genfb_ioctl(void *, void *, unsigned long, void *, + int, struct lwp *); +static paddr_t i915drm_genfb_mmap(void *, void *, off_t, int); +#endif + CFATTACH_DECL_NEW(i915drm, sizeof(struct i915drm_softc), i915drm_match, i915drm_attach, i915drm_detach, NULL); @@ -115,10 +139,9 @@ i915drm_attach(device_t parent, device_t KASSERT(info != NULL); - /* XXX Show more information... */ - aprint_naive("\n"); - aprint_normal(": vendor 0x%04x, device 0x%04x, gen %u\n", - PCI_VENDOR(pa->pa_id), PCI_PRODUCT(pa->pa_id), info->gen); + sc->sc_dev = self; + + pci_aprint_devinfo(pa, NULL); /* XXX Whattakludge! */ if (info->gen != 3) { @@ -132,6 +155,8 @@ i915drm_attach(device_t parent, device_t /* Attach the drm driver. */ drm_config_found(self, i915_drm_driver, flags, &sc->sc_drm_dev); + + i915drm_attach_framebuffer(self); } static int @@ -145,6 +170,10 @@ i915drm_detach(device_t self, int flags) if (error) return error; + error = i915drm_detach_framebuffer(self, flags); + if (error) + return error; + /* drm driver is gone. We can safely drop drm pci driver state. */ error = drm_pci_detach(&sc->sc_drm_dev, flags); if (error) @@ -152,3 +181,331 @@ i915drm_detach(device_t self, int flags) return 0; } + +static struct drm_fb_helper_funcs i915drm_fb_helper_funcs = { + .gamma_set = &intel_crtc_fb_gamma_set, + .gamma_get = &intel_crtc_fb_gamma_get, + .fb_probe = &i915drm_fb_probe, +}; + +static void +i915drm_attach_framebuffer(device_t self) +{ + struct i915drm_softc *const sc = device_private(self); + struct drm_device *const dev = &sc->sc_drm_dev; + struct drm_i915_private *const dev_priv = dev->dev_private; + int ret; + + aprint_debug_dev(self, "attach framebuffer\n"); + dev_priv->fbdev = kmem_zalloc(sizeof(*dev_priv->fbdev), KM_SLEEP); + + struct drm_fb_helper *const fb_helper = &dev_priv->fbdev->helper; + + aprint_debug_dev(self, "drm_fb_helper_init\n"); + fb_helper->funcs = &i915drm_fb_helper_funcs; + ret = drm_fb_helper_init(dev, fb_helper, dev_priv->num_pipe, + INTELFB_CONN_LIMIT); + if (ret) { + aprint_error_dev(self, "unable to init drm fb helper: %d\n", + ret); + goto fail0; + } + + aprint_debug_dev(self, "drm_fb_helper_single_add_all_connectors\n"); + drm_fb_helper_single_add_all_connectors(fb_helper); + aprint_debug_dev(self, "drm_fb_helper_initial_config\n"); + drm_fb_helper_initial_config(fb_helper, 32 /* XXX ? */); + + /* Success! */ + return; + +fail0: kmem_free(dev_priv->fbdev, sizeof(*dev_priv->fbdev)); + dev_priv->fbdev = NULL; +} + +static int +i915drm_detach_framebuffer(device_t self, int flags) +{ + struct i915drm_softc *const sc = device_private(self); + struct drm_device *const dev = &sc->sc_drm_dev; + struct drm_i915_private *const dev_priv = dev->dev_private; + struct intel_fbdev *const fbdev = dev_priv->fbdev; + + if (fbdev != NULL) { + struct drm_fb_helper *const fb_helper = &fbdev->helper; + + if (fb_helper->fb != NULL) { + struct drm_i915_gem_object *const obj = sc->sc_fb_obj; + + /* + * genfb children have already been detached, + * so all we need do is unmap the space and + * clean up the drm structures. + */ + bus_space_unmap(dev->bst, sc->sc_fb_bsh, + obj->base.size); + drm_framebuffer_unreference(fb_helper->fb); + fb_helper->fb = NULL; + drm_gem_object_unreference_unlocked(&obj->base); + sc->sc_fb_obj = NULL; + } + + KASSERT(sc->sc_fb_obj == NULL); + drm_fb_helper_fini(fb_helper); + kmem_free(fbdev, sizeof(*fbdev)); + dev_priv->fbdev = NULL; + } + + return 0; +} + +static int +i915drm_fb_probe(struct drm_fb_helper *fb_helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_device *const dev = fb_helper->dev; + struct drm_i915_private *const dev_priv = dev->dev_private; + struct i915drm_softc *const sc = container_of(dev, + struct i915drm_softc, sc_drm_dev); +#if 0 /* XXX genfb */ + const prop_dictionary_t dict = device_properties(sc->sc_dev); + static const struct genfb_ops zero_genfb_ops; + struct genfb_ops genfb_ops = zero_genfb_ops; +#endif + static const struct drm_mode_fb_cmd2 zero_mode_cmd; + struct drm_mode_fb_cmd2 mode_cmd = zero_mode_cmd; + bus_size_t size; + int ret; + + aprint_debug_dev(sc->sc_dev, "probe framebuffer" + ": %"PRIu32" by %"PRIu32"\n", + sizes->surface_width, + sizes->surface_height); + + if (fb_helper->fb != NULL) { + aprint_debug_dev(sc->sc_dev, "already have a framebuffer" + ": %p\n", fb_helper->fb); + return 0; + } + + /* + * XXX Cargo-culted from Linux. Using sizes as an input/output + * parameter seems sketchy... + */ + if (sizes->surface_bpp == 24) + sizes->surface_bpp = 32; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + KASSERT(sizes->surface_bpp <= (UINT32_MAX - 7)); + KASSERT(mode_cmd.width <= + ((UINT32_MAX / howmany(sizes->surface_bpp, 8)) - 63)); + mode_cmd.pitches[0] = round_up((mode_cmd.width * + howmany(sizes->surface_bpp, 8)), 64); + mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, + sizes->surface_depth); + + KASSERT(mode_cmd.pitches[0] <= + ((__type_max(bus_size_t) / mode_cmd.height) - 63)); + size = round_up((mode_cmd.pitches[0] * mode_cmd.height), PAGE_SIZE); + + sc->sc_fb_obj = i915_gem_alloc_object(dev, size); + if (sc->sc_fb_obj == NULL) { + aprint_error_dev(sc->sc_dev, "unable to create framebuffer\n"); + ret = -ENODEV; /* XXX ? */ + goto fail0; + } + + mutex_lock(&dev->struct_mutex); + ret = intel_pin_and_fence_fb_obj(dev, sc->sc_fb_obj, NULL); + if (ret) { + aprint_error_dev(sc->sc_dev, "unable to pin fb: %d\n", ret); + goto fail1; + } + + ret = intel_framebuffer_init(dev, &dev_priv->fbdev->ifb, &mode_cmd, + sc->sc_fb_obj); + if (ret) { + aprint_error_dev(sc->sc_dev, "unable to init framebuffer" + ": %d\n", ret); + goto fail2; + } + + /* + * XXX Kludge: The intel_framebuffer abstraction `steals' a + * reference to the object. intel_framebuffer_init doesn't add + * a reference, but when the drm_framebuffer inside is + * destroyed, it will drop a reference. + */ + drm_gem_object_reference(&sc->sc_fb_obj->base); + + fb_helper->fb = &dev_priv->fbdev->ifb.base; + mutex_unlock(&dev->struct_mutex); + + aprint_error_dev(sc->sc_dev, "GTT base 0x%"PRIxMAX + ", GTT offset 0x%"PRIxMAX"\n", + (uintmax_t)dev_priv->mm.gtt_base_addr, + (uintmax_t)sc->sc_fb_obj->gtt_offset); + /* XXX errno NetBSD->Linux */ + ret = -bus_space_map(dev->bst, + (dev_priv->mm.gtt_base_addr + sc->sc_fb_obj->gtt_offset), + size, + (BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE), + &sc->sc_fb_bsh); + if (ret) { + aprint_error_dev(sc->sc_dev, "unable to map framebuffer: %d\n", + ret); + goto fail3; + } + + if (0) + goto fail4; + + aprint_error_dev(sc->sc_dev, "write crap to the framebuffer\n"); + { + bus_size_t i; + + for (i = 0; i < size; i++) + bus_space_write_1(dev->bst, sc->sc_fb_bsh, i, + ((i % 4) == 0? 0x53 : + (i % 4) == 1? 0x00 : + (i % 4) == 2? 0x35 : + 0xff)); + } +#if 0 + (void)memset(bus_space_vaddr(dev->bst, sc->sc_fb_bsh), 0x53, size/4); + (void)kpause("drmfbxxx", false, hz, NULL); + (void)memset((char *)bus_space_vaddr(dev->bst, sc->sc_fb_bsh) + size/4, + 0x00, size/4); + (void)kpause("drmfbxxx", false, hz, NULL); + (void)memset((char *)bus_space_vaddr(dev->bst, sc->sc_fb_bsh) + size/2, + 0x35, size/4); + (void)kpause("drmfbxxx", false, hz, NULL); + (void)memset((char *)bus_space_vaddr(dev->bst, sc->sc_fb_bsh) + size/2 + + size/4, 0xff, size/4); +#endif + +#if 0 /* XXX genfb */ + prop_dictionary_set_uint32(dict, "is_console", 1); /* XXX */ + prop_dictionary_set_uint32(dict, "width", mode_cmd.width); + prop_dictionary_set_uint32(dict, "height", mode_cmd.height); + prop_dictionary_set_uint8(dict, "depth", sizes->surface_depth); +#if 0 /* XXX fb stride/linebytes? */ + prop_dictionary_set_uint16(dict, "linebytes", ...); +#endif + prop_dictionary_set_uint32(dict, "address", 0); /* XXX >32-bit */ + prop_dictionary_set_uint64(dict, "virtual_address", + (uint64_t)bus_space_vaddr(dev->bst, sc->sc_fb_bsh)); + sc->sc_genfb.sc_dev = sc->sc_dev; + genfb_init(&sc->sc_genfb); + + genfb_ops.genfb_ioctl = i915drm_genfb_ioctl; + genfb_ops.genfb_mmap = i915drm_genfb_mmap; + + /* XXX errno NetBSD->Linux */ + ret = -genfb_attach(&sc->sc_genfb, &genfb_ops); + if (ret) { + aprint_error_dev(sc->sc_dev, "unable to attach genfb: %d\n", + ret); + goto fail4; + } +#endif + + /* Success! */ + return 1; + +fail4: bus_space_unmap(dev->bst, sc->sc_fb_bsh, size); + fb_helper->fb = NULL; +fail3: drm_framebuffer_unreference(&dev_priv->fbdev->ifb.base); +fail2: i915_gem_object_unpin(sc->sc_fb_obj); +fail1: drm_gem_object_unreference_unlocked(&sc->sc_fb_obj->base); + mutex_unlock(&dev->struct_mutex); +fail0: KASSERT(ret < 0); + return ret; +} + +#if 0 /* XXX genfb */ +static int +i915drm_genfb_ioctl(void *v, void *vs, unsigned long cmd, void *data, int flag, + struct lwp *l) +{ + struct genfb_softc *const genfb = v; + struct i915drm_softc *const sc = container_of(genfb, + struct i915drm_softc, sc_genfb); + const struct pci_attach_args *const pa = &sc->sc_pci_dev.pd_pa; + + switch (cmd) { + case WSDISPLAYIO_GTYPE: + *(unsigned int *)data = WSDISPLAY_TYPE_PCIVGA; + return 0; + + /* PCI config read/write passthrough. */ + case PCI_IOC_CFGREAD: + case PCI_IOC_CFGWRITE: + return pci_devioctl(pa->pa_pc, pa->pa_tag, cmd, data, flag, l); + + case WSDISPLAYIO_GET_BUSID: + return wsdisplayio_busid_pci(genfb->sc_dev, + pa->pa_pc, pa->pa_tag, data); + + default: + return EPASSTHROUGH; + } +} + +static paddr_t +i915drm_genfb_mmap(void *v, void *vs, off_t offset, int prot) +{ + struct genfb_softc *const genfb = v; + struct i915drm_softc *const sc = container_of(genfb, + struct i915drm_softc, sc_genfb); + const struct pci_attach_args *const pa = &sc->sc_drm_dev.pdev->pd_pa; + unsigned int i; + + if (offset < 0) + return -1; + + /* Treat low memory as the framebuffer itself. */ + if (offset < genfb->sc_fbsize) + return bus_space_mmap(pa->pa_memt, genfb->sc_fboffset, offset, + prot, (BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE)); + + /* XXX Cargo-culted from genfb_pci. */ + if (kauth_authorize_machdep(kauth_cred_get(), + KAUTH_MACHDEP_UNMANAGEDMEM, NULL, NULL, NULL, NULL) != 0) { + aprint_normal_dev(sc->sc_dev, "mmap at %"PRIxMAX" rejected\n", + (uintmax_t)offset); + return -1; + } + + for (i = 0; PCI_BAR(i) <= PCI_MAPREG_ROM; i++) { + pcireg_t type; + bus_addr_t addr; + bus_size_t size; + int flags; + + /* Interrogate the BAR. */ + if (!pci_mapreg_probe(pa->pa_pc, pa->pa_tag, PCI_BAR(i), + &type)) + continue; + if (PCI_MAPREG_TYPE(type) != PCI_MAPREG_TYPE_MEM) + continue; + if (pci_mapreg_info(pa->pa_pc, pa->pa_tag, PCI_BAR(i), type, + &addr, &size, &flags)) + continue; + + /* Try to map it if it's in range. */ + if ((addr <= offset) && (offset < (addr + size))) + return bus_space_mmap(pa->pa_memt, offset, 0, prot, + flags); + + /* Skip a slot if this was a 64-bit BAR. */ + if ((PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_MEM) && + (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT)) + i += 1; + } + + /* Failure! */ + return -1; +} +#endif