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