1 /* $NetBSD: tegra_drm.c,v 1.7 2017/04/16 12:28:21 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_drm.c,v 1.7 2017/04/16 12:28:21 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/conf.h> 39 40 #include <uvm/uvm_extern.h> 41 #include <uvm/uvm_device.h> 42 43 #include <drm/drmP.h> 44 45 #include <arm/nvidia/tegra_reg.h> 46 #include <arm/nvidia/tegra_var.h> 47 #include <arm/nvidia/tegra_drm.h> 48 49 #include <dev/fdt/fdtvar.h> 50 51 static int tegra_drm_match(device_t, cfdata_t, void *); 52 static void tegra_drm_attach(device_t, device_t, void *); 53 54 static const char *tegra_drm_get_name(struct drm_device *); 55 static int tegra_drm_set_busid(struct drm_device *, struct drm_master *); 56 57 static int tegra_drm_load(struct drm_device *, unsigned long); 58 static int tegra_drm_unload(struct drm_device *); 59 60 static int tegra_drm_dumb_create(struct drm_file *, struct drm_device *, 61 struct drm_mode_create_dumb *); 62 static int tegra_drm_dumb_map_offset(struct drm_file *, 63 struct drm_device *, uint32_t, uint64_t *); 64 65 static const struct uvm_pagerops tegra_drm_gem_uvm_ops = { 66 .pgo_reference = drm_gem_pager_reference, 67 .pgo_detach = drm_gem_pager_detach, 68 .pgo_fault = tegra_drm_gem_fault, 69 }; 70 71 static struct drm_driver tegra_drm_driver = { 72 .driver_features = DRIVER_MODESET | DRIVER_GEM, 73 .dev_priv_size = 0, 74 .load = tegra_drm_load, 75 .unload = tegra_drm_unload, 76 77 .gem_free_object = tegra_drm_gem_free_object, 78 .mmap_object = drm_gem_or_legacy_mmap_object, 79 .gem_uvm_ops = &tegra_drm_gem_uvm_ops, 80 81 .dumb_create = tegra_drm_dumb_create, 82 .dumb_map_offset = tegra_drm_dumb_map_offset, 83 .dumb_destroy = drm_gem_dumb_destroy, 84 85 .get_vblank_counter = tegra_drm_get_vblank_counter, 86 .enable_vblank = tegra_drm_enable_vblank, 87 .disable_vblank = tegra_drm_disable_vblank, 88 89 .name = DRIVER_NAME, 90 .desc = DRIVER_DESC, 91 .date = DRIVER_DATE, 92 .major = DRIVER_MAJOR, 93 .minor = DRIVER_MINOR, 94 .patchlevel = DRIVER_PATCHLEVEL 95 }; 96 97 static const struct drm_bus tegra_drm_bus = { 98 .bus_type = DRIVER_BUS_PLATFORM, 99 .get_name = tegra_drm_get_name, 100 .set_busid = tegra_drm_set_busid 101 }; 102 103 CFATTACH_DECL_NEW(tegra_drm, sizeof(struct tegra_drm_softc), 104 tegra_drm_match, tegra_drm_attach, NULL, NULL); 105 106 static int 107 tegra_drm_match(device_t parent, cfdata_t cf, void *aux) 108 { 109 const char * compatible[] = { "nvidia,tegra124-host1x", NULL }; 110 struct fdt_attach_args * const faa = aux; 111 112 return of_match_compatible(faa->faa_phandle, compatible); 113 } 114 115 static void 116 tegra_drm_attach(device_t parent, device_t self, void *aux) 117 { 118 struct tegra_drm_softc * const sc = device_private(self); 119 struct fdt_attach_args * const faa = aux; 120 struct drm_driver * const driver = &tegra_drm_driver; 121 prop_dictionary_t prop = device_properties(self); 122 int error, node, hdmi_phandle, ddc_phandle; 123 const char * const hdmi_compat[] = { "nvidia,tegra124-hdmi", NULL }; 124 const char * const dc_compat[] = { "nvidia,tegra124-dc", NULL }; 125 const char * const hdmi_supplies[] = { 126 "hdmi-supply", "pll-supply", "vdd-supply" 127 }; 128 struct fdtbus_regulator *reg; 129 u_int n, ndc; 130 131 sc->sc_dev = self; 132 sc->sc_dmat = faa->faa_dmat; 133 sc->sc_bst = faa->faa_bst; 134 sc->sc_phandle = faa->faa_phandle; 135 136 aprint_naive("\n"); 137 aprint_normal("\n"); 138 139 sc->sc_clk_host1x = fdtbus_clock_get_index(faa->faa_phandle, 0); 140 if (sc->sc_clk_host1x == NULL) { 141 aprint_error_dev(self, "couldn't get clock host1x\n"); 142 return; 143 } 144 sc->sc_rst_host1x = fdtbus_reset_get(faa->faa_phandle, "host1x"); 145 if (sc->sc_clk_host1x == NULL || sc->sc_rst_host1x == NULL) { 146 aprint_error_dev(self, "couldn't get reset host1x\n"); 147 return; 148 } 149 150 ndc = 0; 151 hdmi_phandle = -1; 152 for (node = OF_child(faa->faa_phandle); node; node = OF_peer(node)) { 153 if (of_match_compatible(node, hdmi_compat)) { 154 sc->sc_clk_hdmi = fdtbus_clock_get(node, "hdmi"); 155 sc->sc_clk_hdmi_parent = fdtbus_clock_get(node, 156 "parent"); 157 sc->sc_rst_hdmi = fdtbus_reset_get(node, "hdmi"); 158 hdmi_phandle = node; 159 } else if (of_match_compatible(node, dc_compat) && 160 ndc < __arraycount(sc->sc_clk_dc)) { 161 sc->sc_clk_dc[ndc] = fdtbus_clock_get(node, "dc"); 162 sc->sc_clk_dc_parent[ndc] = fdtbus_clock_get(node, 163 "parent"); 164 sc->sc_rst_dc[ndc] = fdtbus_reset_get(node, "dc"); 165 ++ndc; 166 } 167 } 168 if (hdmi_phandle >= 0) { 169 ddc_phandle = fdtbus_get_phandle(hdmi_phandle, 170 "nvidia,ddc-i2c-bus"); 171 if (ddc_phandle >= 0) { 172 sc->sc_ddc = fdtbus_get_i2c_tag(ddc_phandle); 173 } 174 175 sc->sc_pin_hpd = fdtbus_gpio_acquire(hdmi_phandle, 176 "nvidia,hpd-gpio", GPIO_PIN_INPUT); 177 178 for (n = 0; n < __arraycount(hdmi_supplies); n++) { 179 const char *supply = hdmi_supplies[n]; 180 reg = fdtbus_regulator_acquire(hdmi_phandle, supply); 181 if (reg == NULL) { 182 aprint_error_dev(self, "couldn't acquire %s\n", 183 supply); 184 continue; 185 } 186 if (fdtbus_regulator_enable(reg) != 0) { 187 aprint_error_dev(self, "couldn't enable %s\n", 188 supply); 189 } 190 fdtbus_regulator_release(reg); 191 } 192 } 193 194 fdtbus_reset_assert(sc->sc_rst_host1x); 195 error = clk_enable(sc->sc_clk_host1x); 196 if (error) { 197 aprint_error_dev(self, "couldn't enable clock host1x: %d\n", 198 error); 199 return; 200 } 201 fdtbus_reset_deassert(sc->sc_rst_host1x); 202 203 prop_dictionary_get_bool(prop, "force-dvi", &sc->sc_force_dvi); 204 205 driver->bus = &tegra_drm_bus; 206 207 sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev); 208 if (sc->sc_ddev == NULL) { 209 aprint_error_dev(self, "couldn't allocate DRM device\n"); 210 return; 211 } 212 sc->sc_ddev->dev_private = sc; 213 214 error = -drm_dev_register(sc->sc_ddev, 0); 215 if (error) { 216 drm_dev_unref(sc->sc_ddev); 217 aprint_error_dev(self, "couldn't register DRM device: %d\n", 218 error); 219 return; 220 } 221 222 aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 223 driver->name, driver->major, driver->minor, driver->patchlevel, 224 driver->date, sc->sc_ddev->primary->index); 225 226 return; 227 } 228 229 static const char * 230 tegra_drm_get_name(struct drm_device *ddev) 231 { 232 return DRIVER_NAME; 233 } 234 235 static int 236 tegra_drm_set_busid(struct drm_device *ddev, struct drm_master *master) 237 { 238 const char *id = "platform:tegra:0"; 239 240 master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL); 241 if (master->unique == NULL) 242 return -ENOMEM; 243 strcpy(master->unique, id); 244 master->unique_len = strlen(master->unique); 245 246 return 0; 247 } 248 249 250 static int 251 tegra_drm_load(struct drm_device *ddev, unsigned long flags) 252 { 253 char *devname; 254 int error; 255 256 devname = kzalloc(strlen(DRIVER_NAME) + 1, GFP_KERNEL); 257 if (devname == NULL) { 258 return -ENOMEM; 259 } 260 strcpy(devname, DRIVER_NAME); 261 ddev->devname = devname; 262 263 error = tegra_drm_mode_init(ddev); 264 if (error) 265 goto drmerr; 266 267 error = tegra_drm_fb_init(ddev); 268 if (error) 269 goto drmerr; 270 271 return 0; 272 273 drmerr: 274 drm_mode_config_cleanup(ddev); 275 276 return error; 277 } 278 279 static int 280 tegra_drm_unload(struct drm_device *ddev) 281 { 282 drm_mode_config_cleanup(ddev); 283 284 return 0; 285 } 286 287 static int 288 tegra_drm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev, 289 struct drm_mode_create_dumb *args) 290 { 291 struct tegra_gem_object *obj; 292 uint32_t handle; 293 int error; 294 295 args->pitch = args->width * ((args->bpp + 7) / 8); 296 args->size = args->pitch * args->height; 297 args->size = roundup(args->size, PAGE_SIZE); 298 args->handle = 0; 299 300 obj = tegra_drm_obj_alloc(ddev, args->size); 301 if (obj == NULL) 302 return -ENOMEM; 303 304 error = drm_gem_handle_create(file_priv, &obj->base, &handle); 305 drm_gem_object_unreference_unlocked(&obj->base); 306 if (error) { 307 tegra_drm_obj_free(obj); 308 return error; 309 } 310 311 args->handle = handle; 312 313 return 0; 314 } 315 316 static int 317 tegra_drm_dumb_map_offset(struct drm_file *file_priv, 318 struct drm_device *ddev, uint32_t handle, uint64_t *offset) 319 { 320 struct drm_gem_object *gem_obj; 321 struct tegra_gem_object *obj; 322 int error; 323 324 gem_obj = drm_gem_object_lookup(ddev, file_priv, handle); 325 if (gem_obj == NULL) 326 return -ENOENT; 327 328 obj = to_tegra_gem_obj(gem_obj); 329 330 if (drm_vma_node_has_offset(&obj->base.vma_node) == 0) { 331 error = drm_gem_create_mmap_offset(&obj->base); 332 if (error) 333 goto done; 334 } else { 335 error = 0; 336 } 337 338 *offset = drm_vma_node_offset_addr(&obj->base.vma_node); 339 340 done: 341 drm_gem_object_unreference_unlocked(&obj->base); 342 343 return error; 344 } 345