tegra_nouveau.c revision 1.10
1/* $NetBSD: tegra_nouveau.c,v 1.10 2017/05/30 22:00:25 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: tegra_nouveau.c,v 1.10 2017/05/30 22:00:25 jmcneill Exp $"); 31 32#include <sys/param.h> 33#include <sys/bus.h> 34#include <sys/device.h> 35#include <sys/intr.h> 36#include <sys/systm.h> 37#include <sys/kernel.h> 38#include <sys/module.h> 39 40#include <arm/nvidia/tegra_reg.h> 41#include <arm/nvidia/tegra_pmcreg.h> 42#include <arm/nvidia/tegra_var.h> 43#include <arm/nvidia/tegra_intr.h> 44 45#include <dev/fdt/fdtvar.h> 46 47#include <drm/drmP.h> 48#include <engine/device.h> 49 50extern char *nouveau_config; 51extern char *nouveau_debug; 52extern struct drm_driver *const nouveau_drm_driver; 53 54static int tegra_nouveau_match(device_t, cfdata_t, void *); 55static void tegra_nouveau_attach(device_t, device_t, void *); 56 57struct tegra_nouveau_softc { 58 device_t sc_dev; 59 bus_space_tag_t sc_bst; 60 bus_dma_tag_t sc_dmat; 61 int sc_phandle; 62 struct clk *sc_clk_gpu; 63 struct clk *sc_clk_pwr; 64 struct fdtbus_reset *sc_rst_gpu; 65 struct drm_device *sc_drm_dev; 66 struct platform_device sc_platform_dev; 67 struct nouveau_device *sc_nv_dev; 68}; 69 70static void tegra_nouveau_init(device_t); 71 72static int tegra_nouveau_get_irq(struct drm_device *); 73static const char *tegra_nouveau_get_name(struct drm_device *); 74static int tegra_nouveau_set_busid(struct drm_device *, 75 struct drm_master *); 76static int tegra_nouveau_irq_install(struct drm_device *, 77 irqreturn_t (*)(void *), 78 int, const char *, void *, 79 struct drm_bus_irq_cookie **); 80static void tegra_nouveau_irq_uninstall(struct drm_device *, 81 struct drm_bus_irq_cookie *); 82 83static struct drm_bus drm_tegra_nouveau_bus = { 84 .bus_type = DRIVER_BUS_PLATFORM, 85 .get_irq = tegra_nouveau_get_irq, 86 .get_name = tegra_nouveau_get_name, 87 .set_busid = tegra_nouveau_set_busid, 88 .irq_install = tegra_nouveau_irq_install, 89 .irq_uninstall = tegra_nouveau_irq_uninstall 90}; 91 92CFATTACH_DECL_NEW(tegra_nouveau, sizeof(struct tegra_nouveau_softc), 93 tegra_nouveau_match, tegra_nouveau_attach, NULL, NULL); 94 95static int 96tegra_nouveau_match(device_t parent, cfdata_t cf, void *aux) 97{ 98 const char * const compatible[] = { "nvidia,gk20a", NULL }; 99 struct fdt_attach_args * const faa = aux; 100 101 return of_match_compatible(faa->faa_phandle, compatible); 102} 103 104static void 105tegra_nouveau_attach(device_t parent, device_t self, void *aux) 106{ 107 struct tegra_nouveau_softc * const sc = device_private(self); 108 struct fdt_attach_args * const faa = aux; 109 prop_dictionary_t prop = device_properties(self); 110 int error; 111 112 sc->sc_dev = self; 113 sc->sc_bst = faa->faa_bst; 114 sc->sc_dmat = faa->faa_dmat; 115 sc->sc_phandle = faa->faa_phandle; 116 117 sc->sc_clk_gpu = fdtbus_clock_get(faa->faa_phandle, "gpu"); 118 if (sc->sc_clk_gpu == NULL) { 119 aprint_error(": couldn't get clock gpu\n"); 120 return; 121 } 122 sc->sc_clk_pwr = fdtbus_clock_get(faa->faa_phandle, "pwr"); 123 if (sc->sc_clk_pwr == NULL) { 124 aprint_error(": couldn't get clock pwr\n"); 125 return; 126 } 127 sc->sc_rst_gpu = fdtbus_reset_get(faa->faa_phandle, "gpu"); 128 if (sc->sc_rst_gpu == NULL) { 129 aprint_error(": couldn't get reset gpu\n"); 130 return; 131 } 132 133 aprint_naive("\n"); 134 aprint_normal(": GPU\n"); 135 136 prop_dictionary_get_cstring(prop, "debug", &nouveau_debug); 137 prop_dictionary_get_cstring(prop, "config", &nouveau_config); 138 139 fdtbus_reset_assert(sc->sc_rst_gpu); 140 error = clk_set_rate(sc->sc_clk_pwr, 204000000); 141 if (error) { 142 aprint_error_dev(self, "couldn't set clock pwr frequency: %d\n", 143 error); 144 return; 145 } 146 error = clk_enable(sc->sc_clk_pwr); 147 if (error) { 148 aprint_error_dev(self, "couldn't enable clock pwr: %d\n", 149 error); 150 return; 151 } 152 error = clk_enable(sc->sc_clk_gpu); 153 if (error) { 154 aprint_error_dev(self, "couldn't enable clock gpu: %d\n", 155 error); 156 return; 157 } 158 tegra_pmc_remove_clamping(PMC_PARTID_TD); 159 fdtbus_reset_deassert(sc->sc_rst_gpu); 160 161 error = -nouveau_device_create(&sc->sc_platform_dev, 162 NOUVEAU_BUS_PLATFORM, -1, device_xname(self), 163 nouveau_config, nouveau_debug, &sc->sc_nv_dev); 164 if (error) { 165 aprint_error_dev(self, "couldn't create nouveau device: %d\n", 166 error); 167 return; 168 } 169 170 config_mountroot(self, tegra_nouveau_init); 171} 172 173static void 174tegra_nouveau_init(device_t self) 175{ 176 struct tegra_nouveau_softc * const sc = device_private(self); 177 struct drm_driver * const driver = nouveau_drm_driver; 178 struct drm_device *dev; 179 bus_addr_t addr[2], size[2]; 180 int error; 181 182 if (fdtbus_get_reg(sc->sc_phandle, 0, &addr[0], &size[0]) != 0 || 183 fdtbus_get_reg(sc->sc_phandle, 1, &addr[1], &size[1]) != 0) { 184 aprint_error(": couldn't get registers\n"); 185 return; 186 } 187 188 driver->kdriver.platform_device = &sc->sc_platform_dev; 189 driver->bus = &drm_tegra_nouveau_bus; 190 191 dev = drm_dev_alloc(driver, sc->sc_dev); 192 if (dev == NULL) { 193 aprint_error_dev(self, "couldn't allocate DRM device\n"); 194 return; 195 } 196 dev->bst = sc->sc_bst; 197 dev->bus_dmat = sc->sc_dmat; 198 dev->dmat = dev->bus_dmat; 199 dev->dmat_subregion_p = false; 200 dev->platformdev = &sc->sc_platform_dev; 201 202 dev->platformdev->id = -1; 203 dev->platformdev->pd_dev = sc->sc_dev; 204 dev->platformdev->dmat = sc->sc_dmat; 205 dev->platformdev->nresource = 2; 206 dev->platformdev->resource[0].tag = sc->sc_bst; 207 dev->platformdev->resource[0].start = addr[0]; 208 dev->platformdev->resource[0].len = size[0]; 209 dev->platformdev->resource[1].tag = sc->sc_bst; 210 dev->platformdev->resource[1].start = addr[1]; 211 dev->platformdev->resource[1].len = size[1]; 212 213 error = -drm_dev_register(dev, 0); 214 if (error) { 215 drm_dev_unref(dev); 216 aprint_error_dev(self, "couldn't register DRM device: %d\n", 217 error); 218 return; 219 } 220 221 aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 222 driver->name, driver->major, driver->minor, driver->patchlevel, 223 driver->date, dev->primary->index); 224} 225 226static int 227tegra_nouveau_get_irq(struct drm_device *dev) 228{ 229 return TEGRA_INTR_GPU; 230} 231 232static const char *tegra_nouveau_get_name(struct drm_device *dev) 233{ 234 return "tegra_nouveau"; 235} 236 237static int 238tegra_nouveau_set_busid(struct drm_device *dev, struct drm_master *master) 239{ 240 int id; 241 242 id = dev->platformdev->id; 243 if (id < 0) 244 id = 0; 245 246 master->unique = kmem_asprintf("platform:tegra_nouveau:%02d", id); 247 if (master->unique == NULL) 248 return -ENOMEM; 249 master->unique_len = strlen(master->unique); 250 251 return 0; 252} 253 254static int 255tegra_nouveau_irq_install(struct drm_device *dev, 256 irqreturn_t (*handler)(void *), int flags, const char *name, void *arg, 257 struct drm_bus_irq_cookie **cookiep) 258{ 259 struct tegra_nouveau_softc * const sc = device_private(dev->dev); 260 char intrstr[128]; 261 char *inames, *p; 262 u_int index; 263 void *ih; 264 int len, resid; 265 266 len = OF_getproplen(sc->sc_phandle, "interrupt-names"); 267 if (len <= 0) { 268 aprint_error_dev(dev->dev, "no interrupt-names property\n"); 269 return -EIO; 270 } 271 272 inames = kmem_alloc(len, KM_SLEEP); 273 if (OF_getprop(sc->sc_phandle, "interrupt-names", inames, len) != len) { 274 aprint_error_dev(dev->dev, "failed to get interrupt-names\n"); 275 kmem_free(inames, len); 276 return -EIO; 277 } 278 p = inames; 279 resid = len; 280 index = 0; 281 while (resid > 0) { 282 if (strcmp(name, p) == 0) 283 break; 284 const int slen = strlen(p) + 1; 285 p += slen; 286 len -= slen; 287 ++index; 288 } 289 kmem_free(inames, len); 290 if (len == 0) { 291 aprint_error_dev(dev->dev, "unknown interrupt name '%s'\n", 292 name); 293 return -EINVAL; 294 } 295 296 if (!fdtbus_intr_str(sc->sc_phandle, index, intrstr, sizeof(intrstr))) { 297 aprint_error_dev(dev->dev, "failed to decode interrupt\n"); 298 return -ENXIO; 299 } 300 301 ih = fdtbus_intr_establish(sc->sc_phandle, index, IPL_DRM, 302 FDT_INTR_MPSAFE, handler, arg); 303 if (ih == NULL) { 304 aprint_error_dev(dev->dev, 305 "failed to establish interrupt on %s\n", intrstr); 306 return -ENOENT; 307 } 308 309 aprint_normal_dev(dev->dev, "interrupting on %s\n", intrstr); 310 311 *cookiep = (struct drm_bus_irq_cookie *)ih; 312 return 0; 313} 314 315static void 316tegra_nouveau_irq_uninstall(struct drm_device *dev, 317 struct drm_bus_irq_cookie *cookie) 318{ 319 struct tegra_nouveau_softc * const sc = device_private(dev->dev); 320 321 fdtbus_intr_disestablish(sc->sc_phandle, cookie); 322} 323