tegra_nouveau.c revision 1.10
11.10Sjmcneill/* $NetBSD: tegra_nouveau.c,v 1.10 2017/05/30 22:00:25 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.10Sjmcneill__KERNEL_RCSID(0, "$NetBSD: tegra_nouveau.c,v 1.10 2017/05/30 22:00:25 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.9Sjmcneill#include <arm/nvidia/tegra_pmcreg.h> 421.1Sjmcneill#include <arm/nvidia/tegra_var.h> 431.10Sjmcneill#include <arm/nvidia/tegra_intr.h> 441.1Sjmcneill 451.8Sjmcneill#include <dev/fdt/fdtvar.h> 461.8Sjmcneill 471.1Sjmcneill#include <drm/drmP.h> 481.1Sjmcneill#include <engine/device.h> 491.1Sjmcneill 501.1Sjmcneillextern char *nouveau_config; 511.1Sjmcneillextern char *nouveau_debug; 521.1Sjmcneillextern struct drm_driver *const nouveau_drm_driver; 531.1Sjmcneill 541.1Sjmcneillstatic int tegra_nouveau_match(device_t, cfdata_t, void *); 551.1Sjmcneillstatic void tegra_nouveau_attach(device_t, device_t, void *); 561.1Sjmcneill 571.1Sjmcneillstruct tegra_nouveau_softc { 581.1Sjmcneill device_t sc_dev; 591.8Sjmcneill bus_space_tag_t sc_bst; 601.3Sjmcneill bus_dma_tag_t sc_dmat; 611.8Sjmcneill int sc_phandle; 621.9Sjmcneill struct clk *sc_clk_gpu; 631.9Sjmcneill struct clk *sc_clk_pwr; 641.9Sjmcneill struct fdtbus_reset *sc_rst_gpu; 651.1Sjmcneill struct drm_device *sc_drm_dev; 661.1Sjmcneill struct platform_device sc_platform_dev; 671.1Sjmcneill struct nouveau_device *sc_nv_dev; 681.1Sjmcneill}; 691.1Sjmcneill 701.4Sjmcneillstatic void tegra_nouveau_init(device_t); 711.1Sjmcneill 721.1Sjmcneillstatic int tegra_nouveau_get_irq(struct drm_device *); 731.1Sjmcneillstatic const char *tegra_nouveau_get_name(struct drm_device *); 741.1Sjmcneillstatic int tegra_nouveau_set_busid(struct drm_device *, 751.1Sjmcneill struct drm_master *); 761.1Sjmcneillstatic int tegra_nouveau_irq_install(struct drm_device *, 771.1Sjmcneill irqreturn_t (*)(void *), 781.1Sjmcneill int, const char *, void *, 791.1Sjmcneill struct drm_bus_irq_cookie **); 801.1Sjmcneillstatic void tegra_nouveau_irq_uninstall(struct drm_device *, 811.1Sjmcneill struct drm_bus_irq_cookie *); 821.1Sjmcneill 831.1Sjmcneillstatic struct drm_bus drm_tegra_nouveau_bus = { 841.1Sjmcneill .bus_type = DRIVER_BUS_PLATFORM, 851.1Sjmcneill .get_irq = tegra_nouveau_get_irq, 861.1Sjmcneill .get_name = tegra_nouveau_get_name, 871.1Sjmcneill .set_busid = tegra_nouveau_set_busid, 881.1Sjmcneill .irq_install = tegra_nouveau_irq_install, 891.1Sjmcneill .irq_uninstall = tegra_nouveau_irq_uninstall 901.1Sjmcneill}; 911.1Sjmcneill 921.1SjmcneillCFATTACH_DECL_NEW(tegra_nouveau, sizeof(struct tegra_nouveau_softc), 931.1Sjmcneill tegra_nouveau_match, tegra_nouveau_attach, NULL, NULL); 941.1Sjmcneill 951.1Sjmcneillstatic int 961.1Sjmcneilltegra_nouveau_match(device_t parent, cfdata_t cf, void *aux) 971.1Sjmcneill{ 981.8Sjmcneill const char * const compatible[] = { "nvidia,gk20a", NULL }; 991.8Sjmcneill struct fdt_attach_args * const faa = aux; 1001.8Sjmcneill 1011.8Sjmcneill return of_match_compatible(faa->faa_phandle, compatible); 1021.1Sjmcneill} 1031.1Sjmcneill 1041.1Sjmcneillstatic void 1051.1Sjmcneilltegra_nouveau_attach(device_t parent, device_t self, void *aux) 1061.1Sjmcneill{ 1071.1Sjmcneill struct tegra_nouveau_softc * const sc = device_private(self); 1081.8Sjmcneill struct fdt_attach_args * const faa = aux; 1091.6Sjmcneill prop_dictionary_t prop = device_properties(self); 1101.1Sjmcneill int error; 1111.1Sjmcneill 1121.1Sjmcneill sc->sc_dev = self; 1131.8Sjmcneill sc->sc_bst = faa->faa_bst; 1141.8Sjmcneill sc->sc_dmat = faa->faa_dmat; 1151.8Sjmcneill sc->sc_phandle = faa->faa_phandle; 1161.1Sjmcneill 1171.9Sjmcneill sc->sc_clk_gpu = fdtbus_clock_get(faa->faa_phandle, "gpu"); 1181.9Sjmcneill if (sc->sc_clk_gpu == NULL) { 1191.9Sjmcneill aprint_error(": couldn't get clock gpu\n"); 1201.9Sjmcneill return; 1211.9Sjmcneill } 1221.9Sjmcneill sc->sc_clk_pwr = fdtbus_clock_get(faa->faa_phandle, "pwr"); 1231.9Sjmcneill if (sc->sc_clk_pwr == NULL) { 1241.9Sjmcneill aprint_error(": couldn't get clock pwr\n"); 1251.9Sjmcneill return; 1261.9Sjmcneill } 1271.9Sjmcneill sc->sc_rst_gpu = fdtbus_reset_get(faa->faa_phandle, "gpu"); 1281.9Sjmcneill if (sc->sc_rst_gpu == NULL) { 1291.9Sjmcneill aprint_error(": couldn't get reset gpu\n"); 1301.9Sjmcneill return; 1311.9Sjmcneill } 1321.9Sjmcneill 1331.1Sjmcneill aprint_naive("\n"); 1341.1Sjmcneill aprint_normal(": GPU\n"); 1351.1Sjmcneill 1361.6Sjmcneill prop_dictionary_get_cstring(prop, "debug", &nouveau_debug); 1371.6Sjmcneill prop_dictionary_get_cstring(prop, "config", &nouveau_config); 1381.6Sjmcneill 1391.9Sjmcneill fdtbus_reset_assert(sc->sc_rst_gpu); 1401.9Sjmcneill error = clk_set_rate(sc->sc_clk_pwr, 204000000); 1411.9Sjmcneill if (error) { 1421.9Sjmcneill aprint_error_dev(self, "couldn't set clock pwr frequency: %d\n", 1431.9Sjmcneill error); 1441.9Sjmcneill return; 1451.9Sjmcneill } 1461.9Sjmcneill error = clk_enable(sc->sc_clk_pwr); 1471.9Sjmcneill if (error) { 1481.9Sjmcneill aprint_error_dev(self, "couldn't enable clock pwr: %d\n", 1491.9Sjmcneill error); 1501.9Sjmcneill return; 1511.9Sjmcneill } 1521.9Sjmcneill error = clk_enable(sc->sc_clk_gpu); 1531.9Sjmcneill if (error) { 1541.9Sjmcneill aprint_error_dev(self, "couldn't enable clock gpu: %d\n", 1551.9Sjmcneill error); 1561.9Sjmcneill return; 1571.9Sjmcneill } 1581.9Sjmcneill tegra_pmc_remove_clamping(PMC_PARTID_TD); 1591.9Sjmcneill fdtbus_reset_deassert(sc->sc_rst_gpu); 1601.1Sjmcneill 1611.1Sjmcneill error = -nouveau_device_create(&sc->sc_platform_dev, 1621.1Sjmcneill NOUVEAU_BUS_PLATFORM, -1, device_xname(self), 1631.1Sjmcneill nouveau_config, nouveau_debug, &sc->sc_nv_dev); 1641.1Sjmcneill if (error) { 1651.1Sjmcneill aprint_error_dev(self, "couldn't create nouveau device: %d\n", 1661.1Sjmcneill error); 1671.1Sjmcneill return; 1681.1Sjmcneill } 1691.1Sjmcneill 1701.4Sjmcneill config_mountroot(self, tegra_nouveau_init); 1711.1Sjmcneill} 1721.1Sjmcneill 1731.4Sjmcneillstatic void 1741.4Sjmcneilltegra_nouveau_init(device_t self) 1751.1Sjmcneill{ 1761.4Sjmcneill struct tegra_nouveau_softc * const sc = device_private(self); 1771.1Sjmcneill struct drm_driver * const driver = nouveau_drm_driver; 1781.1Sjmcneill struct drm_device *dev; 1791.8Sjmcneill bus_addr_t addr[2], size[2]; 1801.1Sjmcneill int error; 1811.1Sjmcneill 1821.8Sjmcneill if (fdtbus_get_reg(sc->sc_phandle, 0, &addr[0], &size[0]) != 0 || 1831.8Sjmcneill fdtbus_get_reg(sc->sc_phandle, 1, &addr[1], &size[1]) != 0) { 1841.8Sjmcneill aprint_error(": couldn't get registers\n"); 1851.8Sjmcneill return; 1861.8Sjmcneill } 1871.8Sjmcneill 1881.1Sjmcneill driver->kdriver.platform_device = &sc->sc_platform_dev; 1891.1Sjmcneill driver->bus = &drm_tegra_nouveau_bus; 1901.1Sjmcneill 1911.1Sjmcneill dev = drm_dev_alloc(driver, sc->sc_dev); 1921.4Sjmcneill if (dev == NULL) { 1931.4Sjmcneill aprint_error_dev(self, "couldn't allocate DRM device\n"); 1941.4Sjmcneill return; 1951.4Sjmcneill } 1961.8Sjmcneill dev->bst = sc->sc_bst; 1971.5Sjmcneill dev->bus_dmat = sc->sc_dmat; 1981.5Sjmcneill dev->dmat = dev->bus_dmat; 1991.5Sjmcneill dev->dmat_subregion_p = false; 2001.1Sjmcneill dev->platformdev = &sc->sc_platform_dev; 2011.1Sjmcneill 2021.1Sjmcneill dev->platformdev->id = -1; 2031.7Sriastrad dev->platformdev->pd_dev = sc->sc_dev; 2041.3Sjmcneill dev->platformdev->dmat = sc->sc_dmat; 2051.1Sjmcneill dev->platformdev->nresource = 2; 2061.8Sjmcneill dev->platformdev->resource[0].tag = sc->sc_bst; 2071.8Sjmcneill dev->platformdev->resource[0].start = addr[0]; 2081.8Sjmcneill dev->platformdev->resource[0].len = size[0]; 2091.8Sjmcneill dev->platformdev->resource[1].tag = sc->sc_bst; 2101.8Sjmcneill dev->platformdev->resource[1].start = addr[1]; 2111.8Sjmcneill dev->platformdev->resource[1].len = size[1]; 2121.1Sjmcneill 2131.1Sjmcneill error = -drm_dev_register(dev, 0); 2141.1Sjmcneill if (error) { 2151.1Sjmcneill drm_dev_unref(dev); 2161.4Sjmcneill aprint_error_dev(self, "couldn't register DRM device: %d\n", 2171.4Sjmcneill error); 2181.4Sjmcneill return; 2191.1Sjmcneill } 2201.1Sjmcneill 2211.4Sjmcneill aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 2221.1Sjmcneill driver->name, driver->major, driver->minor, driver->patchlevel, 2231.1Sjmcneill driver->date, dev->primary->index); 2241.1Sjmcneill} 2251.1Sjmcneill 2261.1Sjmcneillstatic int 2271.1Sjmcneilltegra_nouveau_get_irq(struct drm_device *dev) 2281.1Sjmcneill{ 2291.1Sjmcneill return TEGRA_INTR_GPU; 2301.1Sjmcneill} 2311.1Sjmcneill 2321.1Sjmcneillstatic const char *tegra_nouveau_get_name(struct drm_device *dev) 2331.1Sjmcneill{ 2341.1Sjmcneill return "tegra_nouveau"; 2351.1Sjmcneill} 2361.1Sjmcneill 2371.1Sjmcneillstatic int 2381.1Sjmcneilltegra_nouveau_set_busid(struct drm_device *dev, struct drm_master *master) 2391.1Sjmcneill{ 2401.1Sjmcneill int id; 2411.1Sjmcneill 2421.1Sjmcneill id = dev->platformdev->id; 2431.1Sjmcneill if (id < 0) 2441.1Sjmcneill id = 0; 2451.1Sjmcneill 2461.1Sjmcneill master->unique = kmem_asprintf("platform:tegra_nouveau:%02d", id); 2471.1Sjmcneill if (master->unique == NULL) 2481.1Sjmcneill return -ENOMEM; 2491.1Sjmcneill master->unique_len = strlen(master->unique); 2501.1Sjmcneill 2511.1Sjmcneill return 0; 2521.1Sjmcneill} 2531.1Sjmcneill 2541.1Sjmcneillstatic int 2551.1Sjmcneilltegra_nouveau_irq_install(struct drm_device *dev, 2561.1Sjmcneill irqreturn_t (*handler)(void *), int flags, const char *name, void *arg, 2571.1Sjmcneill struct drm_bus_irq_cookie **cookiep) 2581.1Sjmcneill{ 2591.8Sjmcneill struct tegra_nouveau_softc * const sc = device_private(dev->dev); 2601.8Sjmcneill char intrstr[128]; 2611.8Sjmcneill char *inames, *p; 2621.8Sjmcneill u_int index; 2631.1Sjmcneill void *ih; 2641.8Sjmcneill int len, resid; 2651.8Sjmcneill 2661.8Sjmcneill len = OF_getproplen(sc->sc_phandle, "interrupt-names"); 2671.8Sjmcneill if (len <= 0) { 2681.8Sjmcneill aprint_error_dev(dev->dev, "no interrupt-names property\n"); 2691.8Sjmcneill return -EIO; 2701.8Sjmcneill } 2711.1Sjmcneill 2721.8Sjmcneill inames = kmem_alloc(len, KM_SLEEP); 2731.8Sjmcneill if (OF_getprop(sc->sc_phandle, "interrupt-names", inames, len) != len) { 2741.8Sjmcneill aprint_error_dev(dev->dev, "failed to get interrupt-names\n"); 2751.8Sjmcneill kmem_free(inames, len); 2761.8Sjmcneill return -EIO; 2771.8Sjmcneill } 2781.8Sjmcneill p = inames; 2791.8Sjmcneill resid = len; 2801.8Sjmcneill index = 0; 2811.8Sjmcneill while (resid > 0) { 2821.8Sjmcneill if (strcmp(name, p) == 0) 2831.8Sjmcneill break; 2841.8Sjmcneill const int slen = strlen(p) + 1; 2851.8Sjmcneill p += slen; 2861.8Sjmcneill len -= slen; 2871.8Sjmcneill ++index; 2881.8Sjmcneill } 2891.8Sjmcneill kmem_free(inames, len); 2901.8Sjmcneill if (len == 0) { 2911.8Sjmcneill aprint_error_dev(dev->dev, "unknown interrupt name '%s'\n", 2921.8Sjmcneill name); 2931.8Sjmcneill return -EINVAL; 2941.8Sjmcneill } 2951.8Sjmcneill 2961.8Sjmcneill if (!fdtbus_intr_str(sc->sc_phandle, index, intrstr, sizeof(intrstr))) { 2971.8Sjmcneill aprint_error_dev(dev->dev, "failed to decode interrupt\n"); 2981.8Sjmcneill return -ENXIO; 2991.8Sjmcneill } 3001.8Sjmcneill 3011.8Sjmcneill ih = fdtbus_intr_establish(sc->sc_phandle, index, IPL_DRM, 3021.8Sjmcneill FDT_INTR_MPSAFE, handler, arg); 3031.1Sjmcneill if (ih == NULL) { 3041.1Sjmcneill aprint_error_dev(dev->dev, 3051.8Sjmcneill "failed to establish interrupt on %s\n", intrstr); 3061.1Sjmcneill return -ENOENT; 3071.1Sjmcneill } 3081.1Sjmcneill 3091.8Sjmcneill aprint_normal_dev(dev->dev, "interrupting on %s\n", intrstr); 3101.8Sjmcneill 3111.1Sjmcneill *cookiep = (struct drm_bus_irq_cookie *)ih; 3121.1Sjmcneill return 0; 3131.1Sjmcneill} 3141.1Sjmcneill 3151.1Sjmcneillstatic void 3161.1Sjmcneilltegra_nouveau_irq_uninstall(struct drm_device *dev, 3171.1Sjmcneill struct drm_bus_irq_cookie *cookie) 3181.1Sjmcneill{ 3191.8Sjmcneill struct tegra_nouveau_softc * const sc = device_private(dev->dev); 3201.8Sjmcneill 3211.8Sjmcneill fdtbus_intr_disestablish(sc->sc_phandle, cookie); 3221.1Sjmcneill} 323