tegra_nouveau.c revision 1.9
11.9Sjmcneill/* $NetBSD: tegra_nouveau.c,v 1.9 2015/12/22 22:10:36 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.9Sjmcneill__KERNEL_RCSID(0, "$NetBSD: tegra_nouveau.c,v 1.9 2015/12/22 22:10:36 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.1Sjmcneill 441.8Sjmcneill#include <dev/fdt/fdtvar.h> 451.8Sjmcneill 461.1Sjmcneill#include <drm/drmP.h> 471.1Sjmcneill#include <engine/device.h> 481.1Sjmcneill 491.1Sjmcneillextern char *nouveau_config; 501.1Sjmcneillextern char *nouveau_debug; 511.1Sjmcneillextern struct drm_driver *const nouveau_drm_driver; 521.1Sjmcneill 531.1Sjmcneillstatic int tegra_nouveau_match(device_t, cfdata_t, void *); 541.1Sjmcneillstatic void tegra_nouveau_attach(device_t, device_t, void *); 551.1Sjmcneill 561.1Sjmcneillstruct tegra_nouveau_softc { 571.1Sjmcneill device_t sc_dev; 581.8Sjmcneill bus_space_tag_t sc_bst; 591.3Sjmcneill bus_dma_tag_t sc_dmat; 601.8Sjmcneill int sc_phandle; 611.9Sjmcneill struct clk *sc_clk_gpu; 621.9Sjmcneill struct clk *sc_clk_pwr; 631.9Sjmcneill struct fdtbus_reset *sc_rst_gpu; 641.1Sjmcneill struct drm_device *sc_drm_dev; 651.1Sjmcneill struct platform_device sc_platform_dev; 661.1Sjmcneill struct nouveau_device *sc_nv_dev; 671.1Sjmcneill}; 681.1Sjmcneill 691.4Sjmcneillstatic void tegra_nouveau_init(device_t); 701.1Sjmcneill 711.1Sjmcneillstatic int tegra_nouveau_get_irq(struct drm_device *); 721.1Sjmcneillstatic const char *tegra_nouveau_get_name(struct drm_device *); 731.1Sjmcneillstatic int tegra_nouveau_set_busid(struct drm_device *, 741.1Sjmcneill struct drm_master *); 751.1Sjmcneillstatic int tegra_nouveau_irq_install(struct drm_device *, 761.1Sjmcneill irqreturn_t (*)(void *), 771.1Sjmcneill int, const char *, void *, 781.1Sjmcneill struct drm_bus_irq_cookie **); 791.1Sjmcneillstatic void tegra_nouveau_irq_uninstall(struct drm_device *, 801.1Sjmcneill struct drm_bus_irq_cookie *); 811.1Sjmcneill 821.1Sjmcneillstatic struct drm_bus drm_tegra_nouveau_bus = { 831.1Sjmcneill .bus_type = DRIVER_BUS_PLATFORM, 841.1Sjmcneill .get_irq = tegra_nouveau_get_irq, 851.1Sjmcneill .get_name = tegra_nouveau_get_name, 861.1Sjmcneill .set_busid = tegra_nouveau_set_busid, 871.1Sjmcneill .irq_install = tegra_nouveau_irq_install, 881.1Sjmcneill .irq_uninstall = tegra_nouveau_irq_uninstall 891.1Sjmcneill}; 901.1Sjmcneill 911.1SjmcneillCFATTACH_DECL_NEW(tegra_nouveau, sizeof(struct tegra_nouveau_softc), 921.1Sjmcneill tegra_nouveau_match, tegra_nouveau_attach, NULL, NULL); 931.1Sjmcneill 941.1Sjmcneillstatic int 951.1Sjmcneilltegra_nouveau_match(device_t parent, cfdata_t cf, void *aux) 961.1Sjmcneill{ 971.8Sjmcneill const char * const compatible[] = { "nvidia,gk20a", NULL }; 981.8Sjmcneill struct fdt_attach_args * const faa = aux; 991.8Sjmcneill 1001.8Sjmcneill return of_match_compatible(faa->faa_phandle, compatible); 1011.1Sjmcneill} 1021.1Sjmcneill 1031.1Sjmcneillstatic void 1041.1Sjmcneilltegra_nouveau_attach(device_t parent, device_t self, void *aux) 1051.1Sjmcneill{ 1061.1Sjmcneill struct tegra_nouveau_softc * const sc = device_private(self); 1071.8Sjmcneill struct fdt_attach_args * const faa = aux; 1081.6Sjmcneill prop_dictionary_t prop = device_properties(self); 1091.1Sjmcneill int error; 1101.1Sjmcneill 1111.1Sjmcneill sc->sc_dev = self; 1121.8Sjmcneill sc->sc_bst = faa->faa_bst; 1131.8Sjmcneill sc->sc_dmat = faa->faa_dmat; 1141.8Sjmcneill sc->sc_phandle = faa->faa_phandle; 1151.1Sjmcneill 1161.9Sjmcneill sc->sc_clk_gpu = fdtbus_clock_get(faa->faa_phandle, "gpu"); 1171.9Sjmcneill if (sc->sc_clk_gpu == NULL) { 1181.9Sjmcneill aprint_error(": couldn't get clock gpu\n"); 1191.9Sjmcneill return; 1201.9Sjmcneill } 1211.9Sjmcneill sc->sc_clk_pwr = fdtbus_clock_get(faa->faa_phandle, "pwr"); 1221.9Sjmcneill if (sc->sc_clk_pwr == NULL) { 1231.9Sjmcneill aprint_error(": couldn't get clock pwr\n"); 1241.9Sjmcneill return; 1251.9Sjmcneill } 1261.9Sjmcneill sc->sc_rst_gpu = fdtbus_reset_get(faa->faa_phandle, "gpu"); 1271.9Sjmcneill if (sc->sc_rst_gpu == NULL) { 1281.9Sjmcneill aprint_error(": couldn't get reset gpu\n"); 1291.9Sjmcneill return; 1301.9Sjmcneill } 1311.9Sjmcneill 1321.1Sjmcneill aprint_naive("\n"); 1331.1Sjmcneill aprint_normal(": GPU\n"); 1341.1Sjmcneill 1351.6Sjmcneill prop_dictionary_get_cstring(prop, "debug", &nouveau_debug); 1361.6Sjmcneill prop_dictionary_get_cstring(prop, "config", &nouveau_config); 1371.6Sjmcneill 1381.9Sjmcneill fdtbus_reset_assert(sc->sc_rst_gpu); 1391.9Sjmcneill error = clk_set_rate(sc->sc_clk_pwr, 204000000); 1401.9Sjmcneill if (error) { 1411.9Sjmcneill aprint_error_dev(self, "couldn't set clock pwr frequency: %d\n", 1421.9Sjmcneill error); 1431.9Sjmcneill return; 1441.9Sjmcneill } 1451.9Sjmcneill error = clk_enable(sc->sc_clk_pwr); 1461.9Sjmcneill if (error) { 1471.9Sjmcneill aprint_error_dev(self, "couldn't enable clock pwr: %d\n", 1481.9Sjmcneill error); 1491.9Sjmcneill return; 1501.9Sjmcneill } 1511.9Sjmcneill error = clk_enable(sc->sc_clk_gpu); 1521.9Sjmcneill if (error) { 1531.9Sjmcneill aprint_error_dev(self, "couldn't enable clock gpu: %d\n", 1541.9Sjmcneill error); 1551.9Sjmcneill return; 1561.9Sjmcneill } 1571.9Sjmcneill tegra_pmc_remove_clamping(PMC_PARTID_TD); 1581.9Sjmcneill fdtbus_reset_deassert(sc->sc_rst_gpu); 1591.1Sjmcneill 1601.1Sjmcneill error = -nouveau_device_create(&sc->sc_platform_dev, 1611.1Sjmcneill NOUVEAU_BUS_PLATFORM, -1, device_xname(self), 1621.1Sjmcneill nouveau_config, nouveau_debug, &sc->sc_nv_dev); 1631.1Sjmcneill if (error) { 1641.1Sjmcneill aprint_error_dev(self, "couldn't create nouveau device: %d\n", 1651.1Sjmcneill error); 1661.1Sjmcneill return; 1671.1Sjmcneill } 1681.1Sjmcneill 1691.4Sjmcneill config_mountroot(self, tegra_nouveau_init); 1701.1Sjmcneill} 1711.1Sjmcneill 1721.4Sjmcneillstatic void 1731.4Sjmcneilltegra_nouveau_init(device_t self) 1741.1Sjmcneill{ 1751.4Sjmcneill struct tegra_nouveau_softc * const sc = device_private(self); 1761.1Sjmcneill struct drm_driver * const driver = nouveau_drm_driver; 1771.1Sjmcneill struct drm_device *dev; 1781.8Sjmcneill bus_addr_t addr[2], size[2]; 1791.1Sjmcneill int error; 1801.1Sjmcneill 1811.8Sjmcneill if (fdtbus_get_reg(sc->sc_phandle, 0, &addr[0], &size[0]) != 0 || 1821.8Sjmcneill fdtbus_get_reg(sc->sc_phandle, 1, &addr[1], &size[1]) != 0) { 1831.8Sjmcneill aprint_error(": couldn't get registers\n"); 1841.8Sjmcneill return; 1851.8Sjmcneill } 1861.8Sjmcneill 1871.1Sjmcneill driver->kdriver.platform_device = &sc->sc_platform_dev; 1881.1Sjmcneill driver->bus = &drm_tegra_nouveau_bus; 1891.1Sjmcneill 1901.1Sjmcneill dev = drm_dev_alloc(driver, sc->sc_dev); 1911.4Sjmcneill if (dev == NULL) { 1921.4Sjmcneill aprint_error_dev(self, "couldn't allocate DRM device\n"); 1931.4Sjmcneill return; 1941.4Sjmcneill } 1951.8Sjmcneill dev->bst = sc->sc_bst; 1961.5Sjmcneill dev->bus_dmat = sc->sc_dmat; 1971.5Sjmcneill dev->dmat = dev->bus_dmat; 1981.5Sjmcneill dev->dmat_subregion_p = false; 1991.1Sjmcneill dev->platformdev = &sc->sc_platform_dev; 2001.1Sjmcneill 2011.1Sjmcneill dev->platformdev->id = -1; 2021.7Sriastrad dev->platformdev->pd_dev = sc->sc_dev; 2031.3Sjmcneill dev->platformdev->dmat = sc->sc_dmat; 2041.1Sjmcneill dev->platformdev->nresource = 2; 2051.8Sjmcneill dev->platformdev->resource[0].tag = sc->sc_bst; 2061.8Sjmcneill dev->platformdev->resource[0].start = addr[0]; 2071.8Sjmcneill dev->platformdev->resource[0].len = size[0]; 2081.8Sjmcneill dev->platformdev->resource[1].tag = sc->sc_bst; 2091.8Sjmcneill dev->platformdev->resource[1].start = addr[1]; 2101.8Sjmcneill dev->platformdev->resource[1].len = size[1]; 2111.1Sjmcneill 2121.1Sjmcneill error = -drm_dev_register(dev, 0); 2131.1Sjmcneill if (error) { 2141.1Sjmcneill drm_dev_unref(dev); 2151.4Sjmcneill aprint_error_dev(self, "couldn't register DRM device: %d\n", 2161.4Sjmcneill error); 2171.4Sjmcneill return; 2181.1Sjmcneill } 2191.1Sjmcneill 2201.4Sjmcneill aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 2211.1Sjmcneill driver->name, driver->major, driver->minor, driver->patchlevel, 2221.1Sjmcneill driver->date, dev->primary->index); 2231.1Sjmcneill} 2241.1Sjmcneill 2251.1Sjmcneillstatic int 2261.1Sjmcneilltegra_nouveau_get_irq(struct drm_device *dev) 2271.1Sjmcneill{ 2281.1Sjmcneill return TEGRA_INTR_GPU; 2291.1Sjmcneill} 2301.1Sjmcneill 2311.1Sjmcneillstatic const char *tegra_nouveau_get_name(struct drm_device *dev) 2321.1Sjmcneill{ 2331.1Sjmcneill return "tegra_nouveau"; 2341.1Sjmcneill} 2351.1Sjmcneill 2361.1Sjmcneillstatic int 2371.1Sjmcneilltegra_nouveau_set_busid(struct drm_device *dev, struct drm_master *master) 2381.1Sjmcneill{ 2391.1Sjmcneill int id; 2401.1Sjmcneill 2411.1Sjmcneill id = dev->platformdev->id; 2421.1Sjmcneill if (id < 0) 2431.1Sjmcneill id = 0; 2441.1Sjmcneill 2451.1Sjmcneill master->unique = kmem_asprintf("platform:tegra_nouveau:%02d", id); 2461.1Sjmcneill if (master->unique == NULL) 2471.1Sjmcneill return -ENOMEM; 2481.1Sjmcneill master->unique_len = strlen(master->unique); 2491.1Sjmcneill 2501.1Sjmcneill return 0; 2511.1Sjmcneill} 2521.1Sjmcneill 2531.1Sjmcneillstatic int 2541.1Sjmcneilltegra_nouveau_irq_install(struct drm_device *dev, 2551.1Sjmcneill irqreturn_t (*handler)(void *), int flags, const char *name, void *arg, 2561.1Sjmcneill struct drm_bus_irq_cookie **cookiep) 2571.1Sjmcneill{ 2581.8Sjmcneill struct tegra_nouveau_softc * const sc = device_private(dev->dev); 2591.8Sjmcneill char intrstr[128]; 2601.8Sjmcneill char *inames, *p; 2611.8Sjmcneill u_int index; 2621.1Sjmcneill void *ih; 2631.8Sjmcneill int len, resid; 2641.8Sjmcneill 2651.8Sjmcneill len = OF_getproplen(sc->sc_phandle, "interrupt-names"); 2661.8Sjmcneill if (len <= 0) { 2671.8Sjmcneill aprint_error_dev(dev->dev, "no interrupt-names property\n"); 2681.8Sjmcneill return -EIO; 2691.8Sjmcneill } 2701.1Sjmcneill 2711.8Sjmcneill inames = kmem_alloc(len, KM_SLEEP); 2721.8Sjmcneill if (OF_getprop(sc->sc_phandle, "interrupt-names", inames, len) != len) { 2731.8Sjmcneill aprint_error_dev(dev->dev, "failed to get interrupt-names\n"); 2741.8Sjmcneill kmem_free(inames, len); 2751.8Sjmcneill return -EIO; 2761.8Sjmcneill } 2771.8Sjmcneill p = inames; 2781.8Sjmcneill resid = len; 2791.8Sjmcneill index = 0; 2801.8Sjmcneill while (resid > 0) { 2811.8Sjmcneill if (strcmp(name, p) == 0) 2821.8Sjmcneill break; 2831.8Sjmcneill const int slen = strlen(p) + 1; 2841.8Sjmcneill p += slen; 2851.8Sjmcneill len -= slen; 2861.8Sjmcneill ++index; 2871.8Sjmcneill } 2881.8Sjmcneill kmem_free(inames, len); 2891.8Sjmcneill if (len == 0) { 2901.8Sjmcneill aprint_error_dev(dev->dev, "unknown interrupt name '%s'\n", 2911.8Sjmcneill name); 2921.8Sjmcneill return -EINVAL; 2931.8Sjmcneill } 2941.8Sjmcneill 2951.8Sjmcneill if (!fdtbus_intr_str(sc->sc_phandle, index, intrstr, sizeof(intrstr))) { 2961.8Sjmcneill aprint_error_dev(dev->dev, "failed to decode interrupt\n"); 2971.8Sjmcneill return -ENXIO; 2981.8Sjmcneill } 2991.8Sjmcneill 3001.8Sjmcneill ih = fdtbus_intr_establish(sc->sc_phandle, index, IPL_DRM, 3011.8Sjmcneill FDT_INTR_MPSAFE, handler, arg); 3021.1Sjmcneill if (ih == NULL) { 3031.1Sjmcneill aprint_error_dev(dev->dev, 3041.8Sjmcneill "failed to establish interrupt on %s\n", intrstr); 3051.1Sjmcneill return -ENOENT; 3061.1Sjmcneill } 3071.1Sjmcneill 3081.8Sjmcneill aprint_normal_dev(dev->dev, "interrupting on %s\n", intrstr); 3091.8Sjmcneill 3101.1Sjmcneill *cookiep = (struct drm_bus_irq_cookie *)ih; 3111.1Sjmcneill return 0; 3121.1Sjmcneill} 3131.1Sjmcneill 3141.1Sjmcneillstatic void 3151.1Sjmcneilltegra_nouveau_irq_uninstall(struct drm_device *dev, 3161.1Sjmcneill struct drm_bus_irq_cookie *cookie) 3171.1Sjmcneill{ 3181.8Sjmcneill struct tegra_nouveau_softc * const sc = device_private(dev->dev); 3191.8Sjmcneill 3201.8Sjmcneill fdtbus_intr_disestablish(sc->sc_phandle, cookie); 3211.1Sjmcneill} 322