1 /* $NetBSD: tegra_drm.c,v 1.4 2015/12/13 17:39:19 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.4 2015/12/13 17:39:19 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 hdmi_supplies[] = { 125 "hdmi-supply", "pll-supply", "vdd-supply" 126 }; 127 struct fdtbus_regulator *reg; 128 u_int n; 129 130 sc->sc_dev = self; 131 sc->sc_dmat = faa->faa_dmat; 132 sc->sc_bst = faa->faa_bst; 133 sc->sc_phandle = faa->faa_phandle; 134 135 aprint_naive("\n"); 136 aprint_normal("\n"); 137 138 tegra_car_host1x_enable(); 139 140 hdmi_phandle = -1; 141 for (node = OF_child(faa->faa_phandle); node; node = OF_peer(node)) { 142 if (of_match_compatible(node, hdmi_compat)) { 143 hdmi_phandle = node; 144 break; 145 } 146 } 147 if (hdmi_phandle >= 0) { 148 ddc_phandle = fdtbus_get_phandle(hdmi_phandle, 149 "nvidia,ddc-i2c-bus"); 150 if (ddc_phandle >= 0) { 151 sc->sc_ddc = fdtbus_get_i2c_tag(ddc_phandle); 152 } 153 154 sc->sc_pin_hpd = fdtbus_gpio_acquire(hdmi_phandle, 155 "nvidia,hpd-gpio", GPIO_PIN_INPUT); 156 157 for (n = 0; n < __arraycount(hdmi_supplies); n++) { 158 const char *supply = hdmi_supplies[n]; 159 reg = fdtbus_regulator_acquire(hdmi_phandle, supply); 160 if (reg == NULL) { 161 aprint_error_dev(self, "couldn't acquire %s\n", 162 supply); 163 continue; 164 } 165 if (fdtbus_regulator_enable(reg) != 0) { 166 aprint_error_dev(self, "couldn't enable %s\n", 167 supply); 168 } 169 fdtbus_regulator_release(reg); 170 } 171 } 172 173 prop_dictionary_get_bool(prop, "force-dvi", &sc->sc_force_dvi); 174 175 driver->bus = &tegra_drm_bus; 176 177 sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev); 178 if (sc->sc_ddev == NULL) { 179 aprint_error_dev(self, "couldn't allocate DRM device\n"); 180 return; 181 } 182 sc->sc_ddev->dev_private = sc; 183 184 error = -drm_dev_register(sc->sc_ddev, 0); 185 if (error) { 186 drm_dev_unref(sc->sc_ddev); 187 aprint_error_dev(self, "couldn't register DRM device: %d\n", 188 error); 189 return; 190 } 191 192 aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 193 driver->name, driver->major, driver->minor, driver->patchlevel, 194 driver->date, sc->sc_ddev->primary->index); 195 196 return; 197 } 198 199 static const char * 200 tegra_drm_get_name(struct drm_device *ddev) 201 { 202 return DRIVER_NAME; 203 } 204 205 static int 206 tegra_drm_set_busid(struct drm_device *ddev, struct drm_master *master) 207 { 208 const char *id = "platform:tegra:0"; 209 210 master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL); 211 if (master->unique == NULL) 212 return -ENOMEM; 213 strcpy(master->unique, id); 214 master->unique_len = strlen(master->unique); 215 216 return 0; 217 } 218 219 220 static int 221 tegra_drm_load(struct drm_device *ddev, unsigned long flags) 222 { 223 char *devname; 224 int error; 225 226 devname = kzalloc(strlen(DRIVER_NAME) + 1, GFP_KERNEL); 227 if (devname == NULL) { 228 return -ENOMEM; 229 } 230 strcpy(devname, DRIVER_NAME); 231 ddev->devname = devname; 232 233 error = tegra_drm_mode_init(ddev); 234 if (error) 235 goto drmerr; 236 237 error = tegra_drm_fb_init(ddev); 238 if (error) 239 goto drmerr; 240 241 return 0; 242 243 drmerr: 244 drm_mode_config_cleanup(ddev); 245 246 return error; 247 } 248 249 static int 250 tegra_drm_unload(struct drm_device *ddev) 251 { 252 drm_mode_config_cleanup(ddev); 253 254 return 0; 255 } 256 257 static int 258 tegra_drm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev, 259 struct drm_mode_create_dumb *args) 260 { 261 struct tegra_gem_object *obj; 262 uint32_t handle; 263 int error; 264 265 args->pitch = args->width * ((args->bpp + 7) / 8); 266 args->size = args->pitch * args->height; 267 args->size = roundup(args->size, PAGE_SIZE); 268 args->handle = 0; 269 270 obj = tegra_drm_obj_alloc(ddev, args->size); 271 if (obj == NULL) 272 return -ENOMEM; 273 274 error = drm_gem_handle_create(file_priv, &obj->base, &handle); 275 drm_gem_object_unreference_unlocked(&obj->base); 276 if (error) { 277 tegra_drm_obj_free(obj); 278 return error; 279 } 280 281 args->handle = handle; 282 283 return 0; 284 } 285 286 static int 287 tegra_drm_dumb_map_offset(struct drm_file *file_priv, 288 struct drm_device *ddev, uint32_t handle, uint64_t *offset) 289 { 290 struct drm_gem_object *gem_obj; 291 struct tegra_gem_object *obj; 292 int error; 293 294 gem_obj = drm_gem_object_lookup(ddev, file_priv, handle); 295 if (gem_obj == NULL) 296 return -ENOENT; 297 298 obj = to_tegra_gem_obj(gem_obj); 299 300 if (drm_vma_node_has_offset(&obj->base.vma_node) == 0) { 301 error = drm_gem_create_mmap_offset(&obj->base); 302 if (error) 303 goto done; 304 } 305 306 *offset = drm_vma_node_offset_addr(&obj->base.vma_node); 307 308 done: 309 drm_gem_object_unreference_unlocked(&obj->base); 310 311 return error; 312 } 313