1 /* $NetBSD: tegra_drm.c,v 1.9 2017/12/28 14:02:08 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.9 2017/12/28 14:02:08 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 struct drm_driver tegra_drm_driver = { 61 .driver_features = DRIVER_MODESET | DRIVER_GEM, 62 .dev_priv_size = 0, 63 .load = tegra_drm_load, 64 .unload = tegra_drm_unload, 65 66 .gem_free_object = drm_gem_cma_free_object, 67 .mmap_object = drm_gem_or_legacy_mmap_object, 68 .gem_uvm_ops = &drm_gem_cma_uvm_ops, 69 70 .dumb_create = drm_gem_cma_dumb_create, 71 .dumb_map_offset = drm_gem_cma_dumb_map_offset, 72 .dumb_destroy = drm_gem_dumb_destroy, 73 74 .get_vblank_counter = tegra_drm_get_vblank_counter, 75 .enable_vblank = tegra_drm_enable_vblank, 76 .disable_vblank = tegra_drm_disable_vblank, 77 78 .name = DRIVER_NAME, 79 .desc = DRIVER_DESC, 80 .date = DRIVER_DATE, 81 .major = DRIVER_MAJOR, 82 .minor = DRIVER_MINOR, 83 .patchlevel = DRIVER_PATCHLEVEL 84 }; 85 86 static const struct drm_bus tegra_drm_bus = { 87 .bus_type = DRIVER_BUS_PLATFORM, 88 .get_name = tegra_drm_get_name, 89 .set_busid = tegra_drm_set_busid 90 }; 91 92 CFATTACH_DECL_NEW(tegra_drm, sizeof(struct tegra_drm_softc), 93 tegra_drm_match, tegra_drm_attach, NULL, NULL); 94 95 static int 96 tegra_drm_match(device_t parent, cfdata_t cf, void *aux) 97 { 98 const char * compatible[] = { "nvidia,tegra124-host1x", NULL }; 99 struct fdt_attach_args * const faa = aux; 100 101 return of_match_compatible(faa->faa_phandle, compatible); 102 } 103 104 static void 105 tegra_drm_attach(device_t parent, device_t self, void *aux) 106 { 107 struct tegra_drm_softc * const sc = device_private(self); 108 struct fdt_attach_args * const faa = aux; 109 struct drm_driver * const driver = &tegra_drm_driver; 110 prop_dictionary_t prop = device_properties(self); 111 int error, node, hdmi_phandle, ddc_phandle; 112 const char * const hdmi_compat[] = { "nvidia,tegra124-hdmi", NULL }; 113 const char * const dc_compat[] = { "nvidia,tegra124-dc", NULL }; 114 const char * const hdmi_supplies[] = { 115 "hdmi-supply", "pll-supply", "vdd-supply" 116 }; 117 struct fdtbus_regulator *reg; 118 u_int n, ndc; 119 120 sc->sc_dev = self; 121 sc->sc_dmat = faa->faa_dmat; 122 sc->sc_bst = faa->faa_bst; 123 sc->sc_phandle = faa->faa_phandle; 124 125 aprint_naive("\n"); 126 aprint_normal("\n"); 127 128 sc->sc_clk_host1x = fdtbus_clock_get_index(faa->faa_phandle, 0); 129 if (sc->sc_clk_host1x == NULL) { 130 aprint_error_dev(self, "couldn't get clock host1x\n"); 131 return; 132 } 133 sc->sc_rst_host1x = fdtbus_reset_get(faa->faa_phandle, "host1x"); 134 if (sc->sc_clk_host1x == NULL || sc->sc_rst_host1x == NULL) { 135 aprint_error_dev(self, "couldn't get reset host1x\n"); 136 return; 137 } 138 139 ndc = 0; 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 sc->sc_clk_hdmi = fdtbus_clock_get(node, "hdmi"); 144 sc->sc_clk_hdmi_parent = fdtbus_clock_get(node, 145 "parent"); 146 sc->sc_rst_hdmi = fdtbus_reset_get(node, "hdmi"); 147 hdmi_phandle = node; 148 } else if (of_match_compatible(node, dc_compat) && 149 ndc < __arraycount(sc->sc_clk_dc)) { 150 sc->sc_clk_dc[ndc] = fdtbus_clock_get(node, "dc"); 151 sc->sc_clk_dc_parent[ndc] = fdtbus_clock_get(node, 152 "parent"); 153 sc->sc_rst_dc[ndc] = fdtbus_reset_get(node, "dc"); 154 ++ndc; 155 } 156 } 157 if (hdmi_phandle >= 0) { 158 ddc_phandle = fdtbus_get_phandle(hdmi_phandle, 159 "nvidia,ddc-i2c-bus"); 160 if (ddc_phandle >= 0) { 161 sc->sc_ddc = fdtbus_get_i2c_tag(ddc_phandle); 162 } 163 164 sc->sc_pin_hpd = fdtbus_gpio_acquire(hdmi_phandle, 165 "nvidia,hpd-gpio", GPIO_PIN_INPUT); 166 167 for (n = 0; n < __arraycount(hdmi_supplies); n++) { 168 const char *supply = hdmi_supplies[n]; 169 reg = fdtbus_regulator_acquire(hdmi_phandle, supply); 170 if (reg == NULL) { 171 aprint_error_dev(self, "couldn't acquire %s\n", 172 supply); 173 continue; 174 } 175 if (fdtbus_regulator_enable(reg) != 0) { 176 aprint_error_dev(self, "couldn't enable %s\n", 177 supply); 178 } 179 fdtbus_regulator_release(reg); 180 } 181 } 182 183 fdtbus_reset_assert(sc->sc_rst_host1x); 184 error = clk_enable(sc->sc_clk_host1x); 185 if (error) { 186 aprint_error_dev(self, "couldn't enable clock host1x: %d\n", 187 error); 188 return; 189 } 190 fdtbus_reset_deassert(sc->sc_rst_host1x); 191 192 prop_dictionary_get_bool(prop, "force-dvi", &sc->sc_force_dvi); 193 194 driver->bus = &tegra_drm_bus; 195 196 sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev); 197 if (sc->sc_ddev == NULL) { 198 aprint_error_dev(self, "couldn't allocate DRM device\n"); 199 return; 200 } 201 sc->sc_ddev->dev_private = sc; 202 sc->sc_ddev->bst = sc->sc_bst; 203 sc->sc_ddev->bus_dmat = sc->sc_dmat; 204 sc->sc_ddev->dmat = sc->sc_ddev->bus_dmat; 205 sc->sc_ddev->dmat_subregion_p = false; 206 207 error = -drm_dev_register(sc->sc_ddev, 0); 208 if (error) { 209 drm_dev_unref(sc->sc_ddev); 210 aprint_error_dev(self, "couldn't register DRM device: %d\n", 211 error); 212 return; 213 } 214 215 aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n", 216 driver->name, driver->major, driver->minor, driver->patchlevel, 217 driver->date, sc->sc_ddev->primary->index); 218 219 return; 220 } 221 222 static const char * 223 tegra_drm_get_name(struct drm_device *ddev) 224 { 225 return DRIVER_NAME; 226 } 227 228 static int 229 tegra_drm_set_busid(struct drm_device *ddev, struct drm_master *master) 230 { 231 const char *id = "platform:tegra:0"; 232 233 master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL); 234 if (master->unique == NULL) 235 return -ENOMEM; 236 strcpy(master->unique, id); 237 master->unique_len = strlen(master->unique); 238 239 return 0; 240 } 241 242 243 static int 244 tegra_drm_load(struct drm_device *ddev, unsigned long flags) 245 { 246 char *devname; 247 int error; 248 249 devname = kzalloc(strlen(DRIVER_NAME) + 1, GFP_KERNEL); 250 if (devname == NULL) { 251 return -ENOMEM; 252 } 253 strcpy(devname, DRIVER_NAME); 254 ddev->devname = devname; 255 256 error = tegra_drm_mode_init(ddev); 257 if (error) 258 goto drmerr; 259 260 error = tegra_drm_fb_init(ddev); 261 if (error) 262 goto drmerr; 263 264 return 0; 265 266 drmerr: 267 drm_mode_config_cleanup(ddev); 268 269 return error; 270 } 271 272 static int 273 tegra_drm_unload(struct drm_device *ddev) 274 { 275 drm_mode_config_cleanup(ddev); 276 277 return 0; 278 } 279