tegra_nouveau.c revision 1.8
11.8Sjmcneill/* $NetBSD: tegra_nouveau.c,v 1.8 2015/12/13 22:05:52 jmcneill Exp $ */ 21.1Sjmcneill 31.1Sjmcneill/*- 41.1Sjmcneill * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 51.1Sjmcneill * All rights reserved. 61.1Sjmcneill * 71.1Sjmcneill * Redistribution and use in source and binary forms, with or without 81.1Sjmcneill * modification, are permitted provided that the following conditions 91.1Sjmcneill * are met: 101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright 111.1Sjmcneill * notice, this list of conditions and the following disclaimer. 121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 131.1Sjmcneill * notice, this list of conditions and the following disclaimer in the 141.1Sjmcneill * documentation and/or other materials provided with the distribution. 151.1Sjmcneill * 161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 171.1Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 181.1Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 191.1Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 201.1Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 211.1Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 221.1Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 231.1Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 241.1Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 261.1Sjmcneill * SUCH DAMAGE. 271.1Sjmcneill */ 281.1Sjmcneill 291.1Sjmcneill#include <sys/cdefs.h> 301.8Sjmcneill__KERNEL_RCSID(0, "$NetBSD: tegra_nouveau.c,v 1.8 2015/12/13 22:05:52 jmcneill Exp $"); 311.1Sjmcneill 321.1Sjmcneill#include <sys/param.h> 331.1Sjmcneill#include <sys/bus.h> 341.1Sjmcneill#include <sys/device.h> 351.1Sjmcneill#include <sys/intr.h> 361.1Sjmcneill#include <sys/systm.h> 371.1Sjmcneill#include <sys/kernel.h> 381.1Sjmcneill#include <sys/module.h> 391.1Sjmcneill 401.1Sjmcneill#include <arm/nvidia/tegra_reg.h> 411.1Sjmcneill#include <arm/nvidia/tegra_var.h> 421.1Sjmcneill 431.8Sjmcneill#include <dev/fdt/fdtvar.h> 441.8Sjmcneill 451.1Sjmcneill#include <drm/drmP.h> 461.1Sjmcneill#include <engine/device.h> 471.1Sjmcneill 481.1Sjmcneillextern char *nouveau_config; 491.1Sjmcneillextern char *nouveau_debug; 501.1Sjmcneillextern struct drm_driver *const nouveau_drm_driver; 511.1Sjmcneill 521.1Sjmcneillstatic int tegra_nouveau_match(device_t, cfdata_t, void *); 531.1Sjmcneillstatic void tegra_nouveau_attach(device_t, device_t, void *); 541.1Sjmcneill 551.1Sjmcneillstruct tegra_nouveau_softc { 561.1Sjmcneill device_t sc_dev; 571.8Sjmcneill bus_space_tag_t sc_bst; 581.3Sjmcneill bus_dma_tag_t sc_dmat; 591.8Sjmcneill int sc_phandle; 601.1Sjmcneill struct drm_device *sc_drm_dev; 611.1Sjmcneill struct platform_device sc_platform_dev; 621.1Sjmcneill struct nouveau_device *sc_nv_dev; 631.1Sjmcneill}; 641.1Sjmcneill 651.4Sjmcneillstatic void tegra_nouveau_init(device_t); 661.1Sjmcneill 671.1Sjmcneillstatic int tegra_nouveau_get_irq(struct drm_device *); 681.1Sjmcneillstatic const char *tegra_nouveau_get_name(struct drm_device *); 691.1Sjmcneillstatic int tegra_nouveau_set_busid(struct drm_device *, 701.1Sjmcneill struct drm_master *); 711.1Sjmcneillstatic int tegra_nouveau_irq_install(struct drm_device *, 721.1Sjmcneill irqreturn_t (*)(void *), 731.1Sjmcneill int, const char *, void *, 741.1Sjmcneill struct drm_bus_irq_cookie **); 751.1Sjmcneillstatic void tegra_nouveau_irq_uninstall(struct drm_device *, 761.1Sjmcneill struct drm_bus_irq_cookie *); 771.1Sjmcneill 781.1Sjmcneillstatic struct drm_bus drm_tegra_nouveau_bus = { 791.1Sjmcneill .bus_type = DRIVER_BUS_PLATFORM, 801.1Sjmcneill .get_irq = tegra_nouveau_get_irq, 811.1Sjmcneill .get_name = tegra_nouveau_get_name, 821.1Sjmcneill .set_busid = tegra_nouveau_set_busid, 831.1Sjmcneill .irq_install = tegra_nouveau_irq_install, 841.1Sjmcneill .irq_uninstall = tegra_nouveau_irq_uninstall 851.1Sjmcneill}; 861.1Sjmcneill 871.1SjmcneillCFATTACH_DECL_NEW(tegra_nouveau, sizeof(struct tegra_nouveau_softc), 881.1Sjmcneill tegra_nouveau_match, tegra_nouveau_attach, NULL, NULL); 891.1Sjmcneill 901.1Sjmcneillstatic int 911.1Sjmcneilltegra_nouveau_match(device_t parent, cfdata_t cf, void *aux) 921.1Sjmcneill{ 931.8Sjmcneill const char * const compatible[] = { "nvidia,gk20a", NULL }; 941.8Sjmcneill struct fdt_attach_args * const faa = aux; 951.8Sjmcneill 961.8Sjmcneill return of_match_compatible(faa->faa_phandle, compatible); 971.1Sjmcneill} 981.1Sjmcneill 991.1Sjmcneillstatic void 1001.1Sjmcneilltegra_nouveau_attach(device_t parent, device_t self, void *aux) 1011.1Sjmcneill{ 1021.1Sjmcneill struct tegra_nouveau_softc * const sc = device_private(self); 1031.8Sjmcneill struct fdt_attach_args * const faa = aux; 1041.6Sjmcneill prop_dictionary_t prop = device_properties(self); 1051.1Sjmcneill int error; 1061.1Sjmcneill 1071.1Sjmcneill sc->sc_dev = self; 1081.8Sjmcneill sc->sc_bst = faa->faa_bst; 1091.8Sjmcneill sc->sc_dmat = faa->faa_dmat; 1101.8Sjmcneill sc->sc_phandle = faa->faa_phandle; 1111.1Sjmcneill 1121.1Sjmcneill aprint_naive("\n"); 1131.1Sjmcneill aprint_normal(": GPU\n"); 1141.1Sjmcneill 1151.6Sjmcneill prop_dictionary_get_cstring(prop, "debug", &nouveau_debug); 1161.6Sjmcneill prop_dictionary_get_cstring(prop, "config", &nouveau_config); 1171.6Sjmcneill 1181.1Sjmcneill tegra_car_gpu_enable(); 1191.1Sjmcneill 1201.1Sjmcneill error = -nouveau_device_create(&sc->sc_platform_dev, 1211.1Sjmcneill NOUVEAU_BUS_PLATFORM, -1, device_xname(self), 1221.1Sjmcneill nouveau_config, nouveau_debug, &sc->sc_nv_dev); 1231.1Sjmcneill if (error) { 1241.1Sjmcneill aprint_error_dev(self, "couldn't create nouveau device: %d\n", 1251.1Sjmcneill error); 1261.1Sjmcneill return; 1271.1Sjmcneill } 1281.1Sjmcneill 1291.4Sjmcneill config_mountroot(self, tegra_nouveau_init); 1301.1Sjmcneill} 1311.1Sjmcneill 1321.4Sjmcneillstatic void 1331.4Sjmcneilltegra_nouveau_init(device_t self) 1341.1Sjmcneill{ 1351.4Sjmcneill struct tegra_nouveau_softc * const sc = device_private(self); 1361.1Sjmcneill struct drm_driver * const driver = nouveau_drm_driver; 1371.1Sjmcneill struct drm_device *dev; 1381.8Sjmcneill bus_addr_t addr[2], size[2]; 1391.1Sjmcneill int error; 1401.1Sjmcneill 1411.8Sjmcneill if (fdtbus_get_reg(sc->sc_phandle, 0, &addr[0], &size[0]) != 0 || 1421.8Sjmcneill fdtbus_get_reg(sc->sc_phandle, 1, &addr[1], &size[1]) != 0) { 1431.8Sjmcneill aprint_error(": couldn't get registers\n"); 1441.8Sjmcneill return; 1451.8Sjmcneill } 1461.8Sjmcneill 1471.1Sjmcneill driver->kdriver.platform_device = &sc->sc_platform_dev; 1481.1Sjmcneill driver->bus = &drm_tegra_nouveau_bus; 1491.1Sjmcneill 1501.1Sjmcneill dev = drm_dev_alloc(driver, sc->sc_dev); 1511.4Sjmcneill if (dev == NULL) { 1521.4Sjmcneill aprint_error_dev(self, "couldn't allocate DRM device\n"); 1531.4Sjmcneill return; 1541.4Sjmcneill } 1551.8Sjmcneill dev->bst = sc->sc_bst; 1561.5Sjmcneill dev->bus_dmat = sc->sc_dmat; 1571.5Sjmcneill dev->dmat = dev->bus_dmat; 1581.5Sjmcneill dev->dmat_subregion_p = false; 1591.1Sjmcneill dev->platformdev = &sc->sc_platform_dev; 1601.1Sjmcneill 1611.1Sjmcneill dev->platformdev->id = -1; 1621.7Sriastrad dev->platformdev->pd_dev = sc->sc_dev; 1631.3Sjmcneill dev->platformdev->dmat = sc->sc_dmat; 1641.1Sjmcneill dev->platformdev->nresource = 2; 1651.8Sjmcneill dev->platformdev->resource[0].tag = sc->sc_bst; 1661.8Sjmcneill dev->platformdev->resource[0].start = addr[0]; 1671.8Sjmcneill dev->platformdev->resource[0].len = size[0]; 1681.8Sjmcneill dev->platformdev->resource[1].tag = sc->sc_bst; 1691.8Sjmcneill dev->platformdev->resource[1].start = addr[1]; 1701.8Sjmcneill dev->platformdev->resource[1].len = size[1]; 1711.1Sjmcneill 1721.1Sjmcneill error = -drm_dev_register(dev, 0); 1731.1Sjmcneill if (error) { 1741.1Sjmcneill drm_dev_unref(dev); 1751.4Sjmcneill aprint_error_dev(self, "couldn't register DRM device: %d\n", 1761.4Sjmcneill error); 1771.4Sjmcneill return; 1781.1Sjmcneill } 1791.1Sjmcneill 1801.4Sjmcneill aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 1811.1Sjmcneill driver->name, driver->major, driver->minor, driver->patchlevel, 1821.1Sjmcneill driver->date, dev->primary->index); 1831.1Sjmcneill} 1841.1Sjmcneill 1851.1Sjmcneillstatic int 1861.1Sjmcneilltegra_nouveau_get_irq(struct drm_device *dev) 1871.1Sjmcneill{ 1881.1Sjmcneill return TEGRA_INTR_GPU; 1891.1Sjmcneill} 1901.1Sjmcneill 1911.1Sjmcneillstatic const char *tegra_nouveau_get_name(struct drm_device *dev) 1921.1Sjmcneill{ 1931.1Sjmcneill return "tegra_nouveau"; 1941.1Sjmcneill} 1951.1Sjmcneill 1961.1Sjmcneillstatic int 1971.1Sjmcneilltegra_nouveau_set_busid(struct drm_device *dev, struct drm_master *master) 1981.1Sjmcneill{ 1991.1Sjmcneill int id; 2001.1Sjmcneill 2011.1Sjmcneill id = dev->platformdev->id; 2021.1Sjmcneill if (id < 0) 2031.1Sjmcneill id = 0; 2041.1Sjmcneill 2051.1Sjmcneill master->unique = kmem_asprintf("platform:tegra_nouveau:%02d", id); 2061.1Sjmcneill if (master->unique == NULL) 2071.1Sjmcneill return -ENOMEM; 2081.1Sjmcneill master->unique_len = strlen(master->unique); 2091.1Sjmcneill 2101.1Sjmcneill return 0; 2111.1Sjmcneill} 2121.1Sjmcneill 2131.1Sjmcneillstatic int 2141.1Sjmcneilltegra_nouveau_irq_install(struct drm_device *dev, 2151.1Sjmcneill irqreturn_t (*handler)(void *), int flags, const char *name, void *arg, 2161.1Sjmcneill struct drm_bus_irq_cookie **cookiep) 2171.1Sjmcneill{ 2181.8Sjmcneill struct tegra_nouveau_softc * const sc = device_private(dev->dev); 2191.8Sjmcneill char intrstr[128]; 2201.8Sjmcneill char *inames, *p; 2211.8Sjmcneill u_int index; 2221.1Sjmcneill void *ih; 2231.8Sjmcneill int len, resid; 2241.8Sjmcneill 2251.8Sjmcneill len = OF_getproplen(sc->sc_phandle, "interrupt-names"); 2261.8Sjmcneill if (len <= 0) { 2271.8Sjmcneill aprint_error_dev(dev->dev, "no interrupt-names property\n"); 2281.8Sjmcneill return -EIO; 2291.8Sjmcneill } 2301.1Sjmcneill 2311.8Sjmcneill inames = kmem_alloc(len, KM_SLEEP); 2321.8Sjmcneill if (OF_getprop(sc->sc_phandle, "interrupt-names", inames, len) != len) { 2331.8Sjmcneill aprint_error_dev(dev->dev, "failed to get interrupt-names\n"); 2341.8Sjmcneill kmem_free(inames, len); 2351.8Sjmcneill return -EIO; 2361.8Sjmcneill } 2371.8Sjmcneill p = inames; 2381.8Sjmcneill resid = len; 2391.8Sjmcneill index = 0; 2401.8Sjmcneill while (resid > 0) { 2411.8Sjmcneill if (strcmp(name, p) == 0) 2421.8Sjmcneill break; 2431.8Sjmcneill const int slen = strlen(p) + 1; 2441.8Sjmcneill p += slen; 2451.8Sjmcneill len -= slen; 2461.8Sjmcneill ++index; 2471.8Sjmcneill } 2481.8Sjmcneill kmem_free(inames, len); 2491.8Sjmcneill if (len == 0) { 2501.8Sjmcneill aprint_error_dev(dev->dev, "unknown interrupt name '%s'\n", 2511.8Sjmcneill name); 2521.8Sjmcneill return -EINVAL; 2531.8Sjmcneill } 2541.8Sjmcneill 2551.8Sjmcneill if (!fdtbus_intr_str(sc->sc_phandle, index, intrstr, sizeof(intrstr))) { 2561.8Sjmcneill aprint_error_dev(dev->dev, "failed to decode interrupt\n"); 2571.8Sjmcneill return -ENXIO; 2581.8Sjmcneill } 2591.8Sjmcneill 2601.8Sjmcneill ih = fdtbus_intr_establish(sc->sc_phandle, index, IPL_DRM, 2611.8Sjmcneill FDT_INTR_MPSAFE, handler, arg); 2621.1Sjmcneill if (ih == NULL) { 2631.1Sjmcneill aprint_error_dev(dev->dev, 2641.8Sjmcneill "failed to establish interrupt on %s\n", intrstr); 2651.1Sjmcneill return -ENOENT; 2661.1Sjmcneill } 2671.1Sjmcneill 2681.8Sjmcneill aprint_normal_dev(dev->dev, "interrupting on %s\n", intrstr); 2691.8Sjmcneill 2701.1Sjmcneill *cookiep = (struct drm_bus_irq_cookie *)ih; 2711.1Sjmcneill return 0; 2721.1Sjmcneill} 2731.1Sjmcneill 2741.1Sjmcneillstatic void 2751.1Sjmcneilltegra_nouveau_irq_uninstall(struct drm_device *dev, 2761.1Sjmcneill struct drm_bus_irq_cookie *cookie) 2771.1Sjmcneill{ 2781.8Sjmcneill struct tegra_nouveau_softc * const sc = device_private(dev->dev); 2791.8Sjmcneill 2801.8Sjmcneill fdtbus_intr_disestablish(sc->sc_phandle, cookie); 2811.1Sjmcneill} 282