1a0a23564SMichal Meloun /*- 2a0a23564SMichal Meloun * Copyright (c) 2015 Michal Meloun 3a0a23564SMichal Meloun * All rights reserved. 4a0a23564SMichal Meloun * 5a0a23564SMichal Meloun * Redistribution and use in source and binary forms, with or without 6a0a23564SMichal Meloun * modification, are permitted provided that the following conditions 7a0a23564SMichal Meloun * are met: 8a0a23564SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9a0a23564SMichal Meloun * notice, this list of conditions and the following disclaimer. 10a0a23564SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11a0a23564SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12a0a23564SMichal Meloun * documentation and/or other materials provided with the distribution. 13a0a23564SMichal Meloun * 14a0a23564SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15a0a23564SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16a0a23564SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17a0a23564SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18a0a23564SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19a0a23564SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20a0a23564SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21a0a23564SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22a0a23564SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23a0a23564SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24a0a23564SMichal Meloun * SUCH DAMAGE. 25a0a23564SMichal Meloun */ 26a0a23564SMichal Meloun 27a0a23564SMichal Meloun #include <sys/param.h> 28a0a23564SMichal Meloun #include <sys/systm.h> 29a0a23564SMichal Meloun #include <sys/bus.h> 30a0a23564SMichal Meloun #include <sys/clock.h> 31a0a23564SMichal Meloun #include <sys/kernel.h> 32a0a23564SMichal Meloun #include <sys/limits.h> 33a0a23564SMichal Meloun #include <sys/lock.h> 34a0a23564SMichal Meloun 35a0a23564SMichal Meloun #include <sys/module.h> 36a0a23564SMichal Meloun #include <sys/resource.h> 37a0a23564SMichal Meloun #include <sys/sx.h> 38a0a23564SMichal Meloun #include <sys/rman.h> 39a0a23564SMichal Meloun 40a0a23564SMichal Meloun #include <machine/bus.h> 41a0a23564SMichal Meloun #include <machine/resource.h> 42a0a23564SMichal Meloun 43be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 441f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h> 45a0a23564SMichal Meloun #include <dev/drm2/drmP.h> 46a0a23564SMichal Meloun #include <dev/drm2/drm_crtc_helper.h> 47a0a23564SMichal Meloun #include <dev/drm2/drm_fb_helper.h> 48a0a23564SMichal Meloun #include <dev/fdt/simplebus.h> 49a0a23564SMichal Meloun #include <dev/ofw/ofw_bus.h> 50a0a23564SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 51a0a23564SMichal Meloun 52a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_drm.h> 53a0a23564SMichal Meloun 54a0a23564SMichal Meloun #include "fb_if.h" 55a0a23564SMichal Meloun #include "tegra_drm_if.h" 56a0a23564SMichal Meloun 57a0a23564SMichal Meloun #define WR4(_sc, _r, _v) bus_rite_4((_sc)->mem_res, (_r), (_v)) 58a0a23564SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r)) 59a0a23564SMichal Meloun 60a0a23564SMichal Meloun #define LOCK(_sc) sx_xlock(&(_sc)->lock) 61a0a23564SMichal Meloun #define UNLOCK(_sc) sx_xunlock(&(_sc)->lock) 62a0a23564SMichal Meloun #define SLEEP(_sc, timeout) sx_sleep(sc, &sc->lock, 0, "host1x", timeout); 63a0a23564SMichal Meloun #define LOCK_INIT(_sc) sx_init(&_sc->lock, "host1x") 64a0a23564SMichal Meloun #define LOCK_DESTROY(_sc) sx_destroy(&_sc->lock) 65a0a23564SMichal Meloun #define ASSERT_LOCKED(_sc) sx_assert(&_sc->lock, SA_LOCKED) 66a0a23564SMichal Meloun #define ASSERT_UNLOCKED(_sc) sx_assert(&_sc->lock, SA_UNLOCKED) 67a0a23564SMichal Meloun 68a0a23564SMichal Meloun static struct ofw_compat_data compat_data[] = { 69a0a23564SMichal Meloun {"nvidia,tegra124-host1x", 1}, 70a0a23564SMichal Meloun {NULL, 0} 71a0a23564SMichal Meloun }; 72a0a23564SMichal Meloun 73a0a23564SMichal Meloun #define DRIVER_NAME "tegra" 74a0a23564SMichal Meloun #define DRIVER_DESC "NVIDIA Tegra TK1" 75a0a23564SMichal Meloun #define DRIVER_DATE "20151101" 76a0a23564SMichal Meloun #define DRIVER_MAJOR 0 77a0a23564SMichal Meloun #define DRIVER_MINOR 0 78a0a23564SMichal Meloun #define DRIVER_PATCHLEVEL 0 79a0a23564SMichal Meloun 80a0a23564SMichal Meloun struct client_info; 81a0a23564SMichal Meloun TAILQ_HEAD(client_list, client_info); 82a0a23564SMichal Meloun typedef struct client_list client_list_t; 83a0a23564SMichal Meloun 84a0a23564SMichal Meloun struct client_info { 85a0a23564SMichal Meloun TAILQ_ENTRY(client_info) list_e; 86a0a23564SMichal Meloun device_t client; 87a0a23564SMichal Meloun int activated; 88a0a23564SMichal Meloun }; 89a0a23564SMichal Meloun 90a0a23564SMichal Meloun struct host1x_softc { 91a0a23564SMichal Meloun struct simplebus_softc simplebus_sc; /* must be first */ 92a0a23564SMichal Meloun device_t dev; 93a0a23564SMichal Meloun struct sx lock; 94a0a23564SMichal Meloun int attach_done; 95a0a23564SMichal Meloun 96a0a23564SMichal Meloun struct resource *mem_res; 97a0a23564SMichal Meloun struct resource *syncpt_irq_res; 98a0a23564SMichal Meloun void *syncpt_irq_h; 99a0a23564SMichal Meloun struct resource *gen_irq_res; 100a0a23564SMichal Meloun void *gen_irq_h; 101a0a23564SMichal Meloun 102a0a23564SMichal Meloun clk_t clk; 103a0a23564SMichal Meloun hwreset_t reset; 104a0a23564SMichal Meloun struct intr_config_hook irq_hook; 105a0a23564SMichal Meloun 106a0a23564SMichal Meloun int drm_inited; 107a0a23564SMichal Meloun client_list_t clients; 108a0a23564SMichal Meloun 109a0a23564SMichal Meloun struct tegra_drm *tegra_drm; 110a0a23564SMichal Meloun }; 111a0a23564SMichal Meloun 112a0a23564SMichal Meloun static void 113a0a23564SMichal Meloun host1x_output_poll_changed(struct drm_device *drm_dev) 114a0a23564SMichal Meloun { 115a0a23564SMichal Meloun struct tegra_drm *drm; 116a0a23564SMichal Meloun 117a0a23564SMichal Meloun drm = container_of(drm_dev, struct tegra_drm, drm_dev); 118a0a23564SMichal Meloun if (drm->fb != NULL) 119a0a23564SMichal Meloun drm_fb_helper_hotplug_event(&drm->fb->fb_helper); 120a0a23564SMichal Meloun } 121a0a23564SMichal Meloun 122a0a23564SMichal Meloun static const struct drm_mode_config_funcs mode_config_funcs = { 123a0a23564SMichal Meloun .fb_create = tegra_drm_fb_create, 124a0a23564SMichal Meloun .output_poll_changed = host1x_output_poll_changed, 125a0a23564SMichal Meloun }; 126a0a23564SMichal Meloun 127a0a23564SMichal Meloun static int 128a0a23564SMichal Meloun host1x_drm_init(struct host1x_softc *sc) 129a0a23564SMichal Meloun { 130a0a23564SMichal Meloun struct client_info *entry; 131a0a23564SMichal Meloun int rv; 132a0a23564SMichal Meloun 133a0a23564SMichal Meloun LOCK(sc); 134a0a23564SMichal Meloun 135a0a23564SMichal Meloun TAILQ_FOREACH(entry, &sc->clients, list_e) { 136a0a23564SMichal Meloun if (entry->activated) 137a0a23564SMichal Meloun continue; 138a0a23564SMichal Meloun rv = TEGRA_DRM_INIT_CLIENT(entry->client, sc->dev, 139a0a23564SMichal Meloun sc->tegra_drm); 140a0a23564SMichal Meloun if (rv != 0) { 141a0a23564SMichal Meloun device_printf(sc->dev, 142a0a23564SMichal Meloun "Cannot init DRM client %s: %d\n", 143a0a23564SMichal Meloun device_get_name(entry->client), rv); 144a0a23564SMichal Meloun return (rv); 145a0a23564SMichal Meloun } 146a0a23564SMichal Meloun entry->activated = 1; 147a0a23564SMichal Meloun } 148a0a23564SMichal Meloun UNLOCK(sc); 149a0a23564SMichal Meloun 150a0a23564SMichal Meloun return (0); 151a0a23564SMichal Meloun } 152a0a23564SMichal Meloun 153a0a23564SMichal Meloun static int 154a0a23564SMichal Meloun host1x_drm_exit(struct host1x_softc *sc) 155a0a23564SMichal Meloun { 156a0a23564SMichal Meloun struct client_info *entry; 157a0a23564SMichal Meloun int rv; 158a0a23564SMichal Meloun #ifdef FREEBSD_NOTYET 159a0a23564SMichal Meloun struct drm_device *dev, *tmp; 160a0a23564SMichal Meloun #endif 161a0a23564SMichal Meloun LOCK(sc); 162a0a23564SMichal Meloun if (!sc->drm_inited) { 163a0a23564SMichal Meloun UNLOCK(sc); 164a0a23564SMichal Meloun return (0); 165a0a23564SMichal Meloun } 166a0a23564SMichal Meloun TAILQ_FOREACH_REVERSE(entry, &sc->clients, client_list, list_e) { 167a0a23564SMichal Meloun if (!entry->activated) 168a0a23564SMichal Meloun continue; 169a0a23564SMichal Meloun rv = TEGRA_DRM_EXIT_CLIENT(entry->client, sc->dev, 170a0a23564SMichal Meloun sc->tegra_drm); 171a0a23564SMichal Meloun if (rv != 0) { 172a0a23564SMichal Meloun device_printf(sc->dev, 173a0a23564SMichal Meloun "Cannot exit DRM client %s: %d\n", 174a0a23564SMichal Meloun device_get_name(entry->client), rv); 175a0a23564SMichal Meloun } 176a0a23564SMichal Meloun entry->activated = 0; 177a0a23564SMichal Meloun } 178a0a23564SMichal Meloun 179a0a23564SMichal Meloun #ifdef FREEBSD_NOTYET 180a0a23564SMichal Meloun list_for_each_entry_safe(dev, tmp, &driver->device_list, driver_item) 181a0a23564SMichal Meloun drm_put_dev(dev); 182a0a23564SMichal Meloun #endif 183a0a23564SMichal Meloun sc->drm_inited = 0; 184a0a23564SMichal Meloun UNLOCK(sc); 185a0a23564SMichal Meloun 186a0a23564SMichal Meloun return (0); 187a0a23564SMichal Meloun } 188a0a23564SMichal Meloun 189a0a23564SMichal Meloun static int 190a0a23564SMichal Meloun host1x_drm_load(struct drm_device *drm_dev, unsigned long flags) 191a0a23564SMichal Meloun { 192a0a23564SMichal Meloun struct host1x_softc *sc; 193a0a23564SMichal Meloun int rv; 194a0a23564SMichal Meloun 195a0a23564SMichal Meloun sc = device_get_softc(drm_dev->dev); 196a0a23564SMichal Meloun 197a0a23564SMichal Meloun drm_mode_config_init(drm_dev); 198a0a23564SMichal Meloun drm_dev->mode_config.min_width = 32; 199a0a23564SMichal Meloun drm_dev->mode_config.min_height = 32; 200a0a23564SMichal Meloun drm_dev->mode_config.max_width = 4096; 201a0a23564SMichal Meloun drm_dev->mode_config.max_height = 4096; 202a0a23564SMichal Meloun drm_dev->mode_config.funcs = &mode_config_funcs; 203a0a23564SMichal Meloun 204a0a23564SMichal Meloun rv = host1x_drm_init(sc); 205a0a23564SMichal Meloun if (rv != 0) 206a0a23564SMichal Meloun goto fail_host1x; 207a0a23564SMichal Meloun 208a0a23564SMichal Meloun drm_dev->irq_enabled = true; 209a0a23564SMichal Meloun drm_dev->max_vblank_count = 0xffffffff; 210a0a23564SMichal Meloun drm_dev->vblank_disable_allowed = true; 211a0a23564SMichal Meloun 212a0a23564SMichal Meloun rv = drm_vblank_init(drm_dev, drm_dev->mode_config.num_crtc); 213a0a23564SMichal Meloun if (rv != 0) 214a0a23564SMichal Meloun goto fail_vblank; 215a0a23564SMichal Meloun 216a0a23564SMichal Meloun drm_mode_config_reset(drm_dev); 217a0a23564SMichal Meloun 218a0a23564SMichal Meloun rv = tegra_drm_fb_init(drm_dev); 219a0a23564SMichal Meloun if (rv != 0) 220a0a23564SMichal Meloun goto fail_fb; 221a0a23564SMichal Meloun drm_kms_helper_poll_init(drm_dev); 222a0a23564SMichal Meloun 223a0a23564SMichal Meloun return (0); 224a0a23564SMichal Meloun 225a0a23564SMichal Meloun fail_fb: 226a0a23564SMichal Meloun tegra_drm_fb_destroy(drm_dev); 227a0a23564SMichal Meloun drm_vblank_cleanup(drm_dev); 228a0a23564SMichal Meloun fail_vblank: 229a0a23564SMichal Meloun host1x_drm_exit(sc); 230a0a23564SMichal Meloun fail_host1x: 231a0a23564SMichal Meloun drm_mode_config_cleanup(drm_dev); 232a0a23564SMichal Meloun 233a0a23564SMichal Meloun return (rv); 234a0a23564SMichal Meloun } 235a0a23564SMichal Meloun 236a0a23564SMichal Meloun static int 237a0a23564SMichal Meloun host1x_drm_unload(struct drm_device *drm_dev) 238a0a23564SMichal Meloun { 239a0a23564SMichal Meloun struct host1x_softc *sc; 240a0a23564SMichal Meloun int rv; 241a0a23564SMichal Meloun 242a0a23564SMichal Meloun sc = device_get_softc(drm_dev->dev); 243a0a23564SMichal Meloun 244a0a23564SMichal Meloun drm_kms_helper_poll_fini(drm_dev); 245a0a23564SMichal Meloun tegra_drm_fb_destroy(drm_dev); 246a0a23564SMichal Meloun drm_mode_config_cleanup(drm_dev); 247a0a23564SMichal Meloun 248a0a23564SMichal Meloun rv = host1x_drm_exit(sc); 249a0a23564SMichal Meloun if (rv < 0) 250a0a23564SMichal Meloun return (rv); 251a0a23564SMichal Meloun return (0); 252a0a23564SMichal Meloun } 253a0a23564SMichal Meloun 254a0a23564SMichal Meloun static int 255a0a23564SMichal Meloun host1x_drm_open(struct drm_device *drm_dev, struct drm_file *filp) 256a0a23564SMichal Meloun { 257a0a23564SMichal Meloun 258a0a23564SMichal Meloun return (0); 259a0a23564SMichal Meloun } 260a0a23564SMichal Meloun 261a0a23564SMichal Meloun static void 262a0a23564SMichal Meloun tegra_drm_preclose(struct drm_device *drm, struct drm_file *file) 263a0a23564SMichal Meloun { 264a0a23564SMichal Meloun struct drm_crtc *crtc; 265a0a23564SMichal Meloun 266a0a23564SMichal Meloun list_for_each_entry(crtc, &drm->mode_config.crtc_list, head) 267a0a23564SMichal Meloun tegra_dc_cancel_page_flip(crtc, file); 268a0a23564SMichal Meloun } 269a0a23564SMichal Meloun 270a0a23564SMichal Meloun static void 271a0a23564SMichal Meloun host1x_drm_lastclose(struct drm_device *drm_dev) 272a0a23564SMichal Meloun { 273a0a23564SMichal Meloun 274a0a23564SMichal Meloun struct tegra_drm *drm; 275a0a23564SMichal Meloun 276a0a23564SMichal Meloun drm = container_of(drm_dev, struct tegra_drm, drm_dev); 277a0a23564SMichal Meloun if (drm->fb != NULL) 278a0a23564SMichal Meloun drm_fb_helper_restore_fbdev_mode(&drm->fb->fb_helper); 279a0a23564SMichal Meloun } 280a0a23564SMichal Meloun 281a0a23564SMichal Meloun static int 282a0a23564SMichal Meloun host1x_drm_enable_vblank(struct drm_device *drm_dev, int pipe) 283a0a23564SMichal Meloun { 284a0a23564SMichal Meloun struct drm_crtc *crtc; 285a0a23564SMichal Meloun 286a0a23564SMichal Meloun list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { 287a0a23564SMichal Meloun if (pipe == tegra_dc_get_pipe(crtc)) { 288a0a23564SMichal Meloun tegra_dc_enable_vblank(crtc); 289a0a23564SMichal Meloun return (0); 290a0a23564SMichal Meloun } 291a0a23564SMichal Meloun } 292a0a23564SMichal Meloun return (-ENODEV); 293a0a23564SMichal Meloun } 294a0a23564SMichal Meloun 295a0a23564SMichal Meloun static void 296a0a23564SMichal Meloun host1x_drm_disable_vblank(struct drm_device *drm_dev, int pipe) 297a0a23564SMichal Meloun { 298a0a23564SMichal Meloun struct drm_crtc *crtc; 299a0a23564SMichal Meloun 300a0a23564SMichal Meloun list_for_each_entry(crtc, &drm_dev->mode_config.crtc_list, head) { 301a0a23564SMichal Meloun if (pipe == tegra_dc_get_pipe(crtc)) { 302a0a23564SMichal Meloun tegra_dc_disable_vblank(crtc); 303a0a23564SMichal Meloun return; 304a0a23564SMichal Meloun } 305a0a23564SMichal Meloun } 306a0a23564SMichal Meloun } 307a0a23564SMichal Meloun 308a0a23564SMichal Meloun static struct drm_ioctl_desc host1x_drm_ioctls[] = { 309a0a23564SMichal Meloun }; 310a0a23564SMichal Meloun 311a0a23564SMichal Meloun struct drm_driver tegra_drm_driver = { 312a0a23564SMichal Meloun .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, 313a0a23564SMichal Meloun .load = host1x_drm_load, 314a0a23564SMichal Meloun .unload = host1x_drm_unload, 315a0a23564SMichal Meloun .open = host1x_drm_open, 316a0a23564SMichal Meloun .preclose = tegra_drm_preclose, 317a0a23564SMichal Meloun .lastclose = host1x_drm_lastclose, 318a0a23564SMichal Meloun 319a0a23564SMichal Meloun .get_vblank_counter = drm_vblank_count, 320a0a23564SMichal Meloun .enable_vblank = host1x_drm_enable_vblank, 321a0a23564SMichal Meloun .disable_vblank = host1x_drm_disable_vblank, 322a0a23564SMichal Meloun 323a0a23564SMichal Meloun /* Fields filled by tegra_bo_driver_register() 324a0a23564SMichal Meloun .gem_free_object 325a0a23564SMichal Meloun .gem_pager_ops 326a0a23564SMichal Meloun .dumb_create 327a0a23564SMichal Meloun .dumb_map_offset 328a0a23564SMichal Meloun .dumb_destroy 329a0a23564SMichal Meloun */ 330a0a23564SMichal Meloun .ioctls = host1x_drm_ioctls, 331a0a23564SMichal Meloun .num_ioctls = nitems(host1x_drm_ioctls), 332a0a23564SMichal Meloun 333a0a23564SMichal Meloun .name = DRIVER_NAME, 334a0a23564SMichal Meloun .desc = DRIVER_DESC, 335a0a23564SMichal Meloun .date = DRIVER_DATE, 336a0a23564SMichal Meloun .major = DRIVER_MAJOR, 337a0a23564SMichal Meloun .minor = DRIVER_MINOR, 338a0a23564SMichal Meloun .patchlevel = DRIVER_PATCHLEVEL, 339a0a23564SMichal Meloun }; 340a0a23564SMichal Meloun 341a0a23564SMichal Meloun /* 342a0a23564SMichal Meloun * ----------------- Device methods ------------------------- 343a0a23564SMichal Meloun */ 344a0a23564SMichal Meloun static void 345a0a23564SMichal Meloun host1x_irq_hook(void *arg) 346a0a23564SMichal Meloun { 347a0a23564SMichal Meloun struct host1x_softc *sc; 348a0a23564SMichal Meloun int rv; 349a0a23564SMichal Meloun 350a0a23564SMichal Meloun sc = arg; 351a0a23564SMichal Meloun config_intrhook_disestablish(&sc->irq_hook); 352a0a23564SMichal Meloun 353a0a23564SMichal Meloun tegra_bo_driver_register(&tegra_drm_driver); 354a0a23564SMichal Meloun rv = drm_get_platform_dev(sc->dev, &sc->tegra_drm->drm_dev, 355a0a23564SMichal Meloun &tegra_drm_driver); 356a0a23564SMichal Meloun if (rv != 0) { 357a0a23564SMichal Meloun device_printf(sc->dev, "drm_get_platform_dev(): %d\n", rv); 358a0a23564SMichal Meloun return; 359a0a23564SMichal Meloun } 360a0a23564SMichal Meloun 361a0a23564SMichal Meloun sc->drm_inited = 1; 362a0a23564SMichal Meloun } 363a0a23564SMichal Meloun 364a0a23564SMichal Meloun static struct fb_info * 365a0a23564SMichal Meloun host1x_fb_helper_getinfo(device_t dev) 366a0a23564SMichal Meloun { 367a0a23564SMichal Meloun struct host1x_softc *sc; 368a0a23564SMichal Meloun 369a0a23564SMichal Meloun sc = device_get_softc(dev); 370a0a23564SMichal Meloun if (sc->tegra_drm == NULL) 371a0a23564SMichal Meloun return (NULL); 372a0a23564SMichal Meloun return (tegra_drm_fb_getinfo(&sc->tegra_drm->drm_dev)); 373a0a23564SMichal Meloun } 374a0a23564SMichal Meloun 375a0a23564SMichal Meloun static int 376a0a23564SMichal Meloun host1x_register_client(device_t dev, device_t client) 377a0a23564SMichal Meloun { 378a0a23564SMichal Meloun struct host1x_softc *sc; 379a0a23564SMichal Meloun struct client_info *entry; 380a0a23564SMichal Meloun 381a0a23564SMichal Meloun sc = device_get_softc(dev); 382a0a23564SMichal Meloun 383a0a23564SMichal Meloun entry = malloc(sizeof(struct client_info), M_DEVBUF, M_WAITOK | M_ZERO); 384a0a23564SMichal Meloun entry->client = client; 385a0a23564SMichal Meloun entry->activated = 0; 386a0a23564SMichal Meloun 387a0a23564SMichal Meloun LOCK(sc); 388a0a23564SMichal Meloun TAILQ_INSERT_TAIL(&sc->clients, entry, list_e); 389a0a23564SMichal Meloun UNLOCK(sc); 390a0a23564SMichal Meloun 391a0a23564SMichal Meloun return (0); 392a0a23564SMichal Meloun } 393a0a23564SMichal Meloun 394a0a23564SMichal Meloun static int 395a0a23564SMichal Meloun host1x_deregister_client(device_t dev, device_t client) 396a0a23564SMichal Meloun { 397a0a23564SMichal Meloun struct host1x_softc *sc; 398a0a23564SMichal Meloun struct client_info *entry; 399a0a23564SMichal Meloun 400a0a23564SMichal Meloun sc = device_get_softc(dev); 401a0a23564SMichal Meloun 402a0a23564SMichal Meloun LOCK(sc); 403a0a23564SMichal Meloun TAILQ_FOREACH(entry, &sc->clients, list_e) { 404a0a23564SMichal Meloun if (entry->client == client) { 405a0a23564SMichal Meloun if (entry->activated) 406a0a23564SMichal Meloun panic("Tegra DRM: Attempt to deregister " 407a0a23564SMichal Meloun "activated client"); 408a0a23564SMichal Meloun TAILQ_REMOVE(&sc->clients, entry, list_e); 409a0a23564SMichal Meloun free(entry, M_DEVBUF); 410a0a23564SMichal Meloun UNLOCK(sc); 411a0a23564SMichal Meloun return (0); 412a0a23564SMichal Meloun } 413a0a23564SMichal Meloun } 414a0a23564SMichal Meloun UNLOCK(sc); 415a0a23564SMichal Meloun 416a0a23564SMichal Meloun return (0); 417a0a23564SMichal Meloun } 418a0a23564SMichal Meloun 419a0a23564SMichal Meloun static void 420a0a23564SMichal Meloun host1x_gen_intr(void *arg) 421a0a23564SMichal Meloun { 422a0a23564SMichal Meloun struct host1x_softc *sc; 423a0a23564SMichal Meloun 424a0a23564SMichal Meloun sc = (struct host1x_softc *)arg; 425a0a23564SMichal Meloun LOCK(sc); 426a0a23564SMichal Meloun UNLOCK(sc); 427a0a23564SMichal Meloun } 428a0a23564SMichal Meloun 429a0a23564SMichal Meloun static void 430a0a23564SMichal Meloun host1x_syncpt_intr(void *arg) 431a0a23564SMichal Meloun { 432a0a23564SMichal Meloun struct host1x_softc *sc; 433a0a23564SMichal Meloun 434a0a23564SMichal Meloun sc = (struct host1x_softc *)arg; 435a0a23564SMichal Meloun LOCK(sc); 436a0a23564SMichal Meloun UNLOCK(sc); 437a0a23564SMichal Meloun } 438a0a23564SMichal Meloun 439a0a23564SMichal Meloun static void 440a0a23564SMichal Meloun host1x_new_pass(device_t dev) 441a0a23564SMichal Meloun { 442a0a23564SMichal Meloun struct host1x_softc *sc; 443a0a23564SMichal Meloun int rv, rid; 444a0a23564SMichal Meloun phandle_t node; 445a0a23564SMichal Meloun 446a0a23564SMichal Meloun /* 447a0a23564SMichal Meloun * We attach during BUS_PASS_BUS (because we must overcome simplebus), 448a0a23564SMichal Meloun * but some of our FDT resources are not ready until BUS_PASS_DEFAULT 449a0a23564SMichal Meloun */ 450a0a23564SMichal Meloun sc = device_get_softc(dev); 4513514f989SElliott Mitchell if (sc->attach_done || bus_get_pass() < BUS_PASS_DEFAULT) { 452a0a23564SMichal Meloun bus_generic_new_pass(dev); 453a0a23564SMichal Meloun return; 454a0a23564SMichal Meloun } 455a0a23564SMichal Meloun 456a0a23564SMichal Meloun sc->attach_done = 1; 457a0a23564SMichal Meloun node = ofw_bus_get_node(dev); 458a0a23564SMichal Meloun 459a0a23564SMichal Meloun /* Allocate our IRQ resource. */ 460a0a23564SMichal Meloun rid = 0; 461a0a23564SMichal Meloun sc->syncpt_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 462a0a23564SMichal Meloun RF_ACTIVE); 463a0a23564SMichal Meloun if (sc->syncpt_irq_res == NULL) { 464a0a23564SMichal Meloun device_printf(dev, "Cannot allocate interrupt.\n"); 465a0a23564SMichal Meloun rv = ENXIO; 466a0a23564SMichal Meloun goto fail; 467a0a23564SMichal Meloun } 468a0a23564SMichal Meloun rid = 1; 469a0a23564SMichal Meloun sc->gen_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 470a0a23564SMichal Meloun RF_ACTIVE); 471a0a23564SMichal Meloun if (sc->gen_irq_res == NULL) { 472a0a23564SMichal Meloun device_printf(dev, "Cannot allocate interrupt.\n"); 473a0a23564SMichal Meloun rv = ENXIO; 474a0a23564SMichal Meloun goto fail; 475a0a23564SMichal Meloun } 476a0a23564SMichal Meloun 477a0a23564SMichal Meloun /* FDT resources */ 478a0a23564SMichal Meloun rv = hwreset_get_by_ofw_name(sc->dev, 0, "host1x", &sc->reset); 479a0a23564SMichal Meloun if (rv != 0) { 480a0a23564SMichal Meloun device_printf(dev, "Cannot get fuse reset\n"); 481a0a23564SMichal Meloun goto fail; 482a0a23564SMichal Meloun } 483a0a23564SMichal Meloun rv = clk_get_by_ofw_index(sc->dev, 0, 0, &sc->clk); 484a0a23564SMichal Meloun if (rv != 0) { 485a0a23564SMichal Meloun device_printf(dev, "Cannot get i2c clock: %d\n", rv); 486a0a23564SMichal Meloun goto fail; 487a0a23564SMichal Meloun } 488a0a23564SMichal Meloun 489a0a23564SMichal Meloun rv = clk_enable(sc->clk); 490a0a23564SMichal Meloun if (rv != 0) { 491a0a23564SMichal Meloun device_printf(dev, "Cannot enable clock: %d\n", rv); 492a0a23564SMichal Meloun goto fail; 493a0a23564SMichal Meloun } 494a0a23564SMichal Meloun rv = hwreset_deassert(sc->reset); 495a0a23564SMichal Meloun if (rv != 0) { 496a0a23564SMichal Meloun device_printf(sc->dev, "Cannot clear reset\n"); 497a0a23564SMichal Meloun goto fail; 498a0a23564SMichal Meloun } 499a0a23564SMichal Meloun 500a0a23564SMichal Meloun /* Setup interrupts */ 501a0a23564SMichal Meloun rv = bus_setup_intr(dev, sc->gen_irq_res, 502a0a23564SMichal Meloun INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_gen_intr, 503a0a23564SMichal Meloun sc, &sc->gen_irq_h); 504a0a23564SMichal Meloun if (rv) { 505a0a23564SMichal Meloun device_printf(dev, "Cannot setup gen interrupt.\n"); 506a0a23564SMichal Meloun goto fail; 507a0a23564SMichal Meloun } 508a0a23564SMichal Meloun 509a0a23564SMichal Meloun rv = bus_setup_intr(dev, sc->syncpt_irq_res, 510a0a23564SMichal Meloun INTR_TYPE_MISC | INTR_MPSAFE, NULL, host1x_syncpt_intr, 511a0a23564SMichal Meloun sc, &sc->syncpt_irq_h); 512a0a23564SMichal Meloun if (rv) { 513a0a23564SMichal Meloun device_printf(dev, "Cannot setup syncpt interrupt.\n"); 514a0a23564SMichal Meloun goto fail; 515a0a23564SMichal Meloun } 516a0a23564SMichal Meloun 517a0a23564SMichal Meloun simplebus_init(dev, 0); 518a0a23564SMichal Meloun for (node = OF_child(node); node > 0; node = OF_peer(node)) 519a0a23564SMichal Meloun simplebus_add_device(dev, node, 0, NULL, -1, NULL); 520a0a23564SMichal Meloun 521a0a23564SMichal Meloun sc->irq_hook.ich_func = host1x_irq_hook; 522a0a23564SMichal Meloun sc->irq_hook.ich_arg = sc; 523a0a23564SMichal Meloun config_intrhook_establish(&sc->irq_hook); 524a0a23564SMichal Meloun bus_generic_new_pass(dev); 525a0a23564SMichal Meloun return; 526a0a23564SMichal Meloun 527a0a23564SMichal Meloun fail: 528a0a23564SMichal Meloun device_detach(dev); 529a0a23564SMichal Meloun return; 530a0a23564SMichal Meloun } 531a0a23564SMichal Meloun 532a0a23564SMichal Meloun static int 533a0a23564SMichal Meloun host1x_probe(device_t dev) 534a0a23564SMichal Meloun { 535a0a23564SMichal Meloun 536a0a23564SMichal Meloun if (!ofw_bus_status_okay(dev)) 537a0a23564SMichal Meloun return (ENXIO); 538a0a23564SMichal Meloun 539a0a23564SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 540a0a23564SMichal Meloun return (ENXIO); 541a0a23564SMichal Meloun 542a0a23564SMichal Meloun return (BUS_PROBE_DEFAULT); 543a0a23564SMichal Meloun } 544a0a23564SMichal Meloun 545a0a23564SMichal Meloun static int 546a0a23564SMichal Meloun host1x_attach(device_t dev) 547a0a23564SMichal Meloun { 548a0a23564SMichal Meloun int rv, rid; 549a0a23564SMichal Meloun struct host1x_softc *sc; 550a0a23564SMichal Meloun 551a0a23564SMichal Meloun sc = device_get_softc(dev); 552a0a23564SMichal Meloun sc->tegra_drm = malloc(sizeof(struct tegra_drm), DRM_MEM_DRIVER, 553a0a23564SMichal Meloun M_WAITOK | M_ZERO); 554a0a23564SMichal Meloun 555a0a23564SMichal Meloun /* crosslink together all worlds */ 556a0a23564SMichal Meloun sc->dev = dev; 557a0a23564SMichal Meloun sc->tegra_drm->drm_dev.dev_private = &sc->tegra_drm; 558a0a23564SMichal Meloun sc->tegra_drm->drm_dev.dev = dev; 559a0a23564SMichal Meloun 560a0a23564SMichal Meloun TAILQ_INIT(&sc->clients); 561a0a23564SMichal Meloun 562a0a23564SMichal Meloun LOCK_INIT(sc); 563a0a23564SMichal Meloun 564a0a23564SMichal Meloun /* Get the memory resource for the register mapping. */ 565a0a23564SMichal Meloun rid = 0; 566a0a23564SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 567a0a23564SMichal Meloun RF_ACTIVE); 568a0a23564SMichal Meloun if (sc->mem_res == NULL) { 569a0a23564SMichal Meloun device_printf(dev, "Cannot map registers.\n"); 570a0a23564SMichal Meloun rv = ENXIO; 571a0a23564SMichal Meloun goto fail; 572a0a23564SMichal Meloun } 573a0a23564SMichal Meloun 574*18250ec6SJohn Baldwin bus_attach_children(dev); 575*18250ec6SJohn Baldwin return (0); 576a0a23564SMichal Meloun 577a0a23564SMichal Meloun fail: 578a0a23564SMichal Meloun if (sc->tegra_drm != NULL) 579a0a23564SMichal Meloun free(sc->tegra_drm, DRM_MEM_DRIVER); 580a0a23564SMichal Meloun if (sc->mem_res != NULL) 581a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 582a0a23564SMichal Meloun LOCK_DESTROY(sc); 583a0a23564SMichal Meloun return (rv); 584a0a23564SMichal Meloun } 585a0a23564SMichal Meloun 586a0a23564SMichal Meloun static int 587a0a23564SMichal Meloun host1x_detach(device_t dev) 588a0a23564SMichal Meloun { 589a0a23564SMichal Meloun struct host1x_softc *sc; 590d412c076SJohn Baldwin int error; 591d412c076SJohn Baldwin 592d412c076SJohn Baldwin error = bus_generic_detach(dev); 593d412c076SJohn Baldwin if (error != 0) 594d412c076SJohn Baldwin return (error); 595a0a23564SMichal Meloun 596a0a23564SMichal Meloun sc = device_get_softc(dev); 597a0a23564SMichal Meloun 598a0a23564SMichal Meloun host1x_drm_exit(sc); 599a0a23564SMichal Meloun 600a0a23564SMichal Meloun if (sc->gen_irq_h != NULL) 601a0a23564SMichal Meloun bus_teardown_intr(dev, sc->gen_irq_res, sc->gen_irq_h); 602a0a23564SMichal Meloun if (sc->tegra_drm != NULL) 603a0a23564SMichal Meloun free(sc->tegra_drm, DRM_MEM_DRIVER); 604a0a23564SMichal Meloun if (sc->clk != NULL) 605a0a23564SMichal Meloun clk_release(sc->clk); 606a0a23564SMichal Meloun if (sc->reset != NULL) 607a0a23564SMichal Meloun hwreset_release(sc->reset); 608a0a23564SMichal Meloun if (sc->syncpt_irq_h != NULL) 609a0a23564SMichal Meloun bus_teardown_intr(dev, sc->syncpt_irq_res, sc->syncpt_irq_h); 610a0a23564SMichal Meloun if (sc->gen_irq_res != NULL) 611a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 1, sc->gen_irq_res); 612a0a23564SMichal Meloun if (sc->syncpt_irq_res != NULL) 613a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->syncpt_irq_res); 614a0a23564SMichal Meloun if (sc->mem_res != NULL) 615a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 616a0a23564SMichal Meloun LOCK_DESTROY(sc); 617d412c076SJohn Baldwin return (0); 618a0a23564SMichal Meloun } 619a0a23564SMichal Meloun 620a0a23564SMichal Meloun static device_method_t host1x_methods[] = { 621a0a23564SMichal Meloun /* Device interface */ 622a0a23564SMichal Meloun DEVMETHOD(device_probe, host1x_probe), 623a0a23564SMichal Meloun DEVMETHOD(device_attach, host1x_attach), 624a0a23564SMichal Meloun DEVMETHOD(device_detach, host1x_detach), 625a0a23564SMichal Meloun 626a0a23564SMichal Meloun /* Bus interface */ 627a0a23564SMichal Meloun DEVMETHOD(bus_new_pass, host1x_new_pass), 628a0a23564SMichal Meloun 629a0a23564SMichal Meloun /* Framebuffer service methods */ 630a0a23564SMichal Meloun DEVMETHOD(fb_getinfo, host1x_fb_helper_getinfo), 631a0a23564SMichal Meloun 632a0a23564SMichal Meloun /* tegra drm interface */ 633a0a23564SMichal Meloun DEVMETHOD(tegra_drm_register_client, host1x_register_client), 634a0a23564SMichal Meloun DEVMETHOD(tegra_drm_deregister_client, host1x_deregister_client), 635a0a23564SMichal Meloun 636a0a23564SMichal Meloun DEVMETHOD_END 637a0a23564SMichal Meloun }; 638a0a23564SMichal Meloun 639a0a23564SMichal Meloun DEFINE_CLASS_1(host1x, host1x_driver, host1x_methods, 640a0a23564SMichal Meloun sizeof(struct host1x_softc), simplebus_driver); 641289f133bSJohn Baldwin EARLY_DRIVER_MODULE(host1x, simplebus, host1x_driver, 0, 0, BUS_PASS_BUS); 642a0a23564SMichal Meloun 643a0a23564SMichal Meloun /* Bindings for fbd device. */ 644a0a23564SMichal Meloun extern driver_t fbd_driver; 6459360510bSJohn Baldwin DRIVER_MODULE(fbd, host1x, fbd_driver, 0, 0); 646