Module Name:    src
Committed By:   jmcneill
Date:           Mon Nov  9 23:05:58 UTC 2015

Modified Files:
        src/sys/arch/arm/nvidia: files.tegra tegra_io.c
        src/sys/arch/evbarm/conf: JETSONTK1 NYAN-BIG
        src/sys/arch/evbarm/tegra: tegra_machdep.c
Added Files:
        src/sys/arch/arm/nvidia: tegra_drm.c tegra_drm.h tegra_drm_fb.c
            tegra_drm_mode.c tegra_fb.c
Removed Files:
        src/sys/arch/arm/nvidia: tegra_dc.c tegra_hdmi.c

Log Message:
Port the Tegra (2D) display drivers to the DRM framework.

tegradrm0 at tegraio0
tegrafb0 at tegradrm0
tegrafb0: framebuffer at 0x9b000000, size 1280x720, depth 32, stride 5120
wsdisplay0 at tegrafb0 kbdmux 1
wsmux1: connecting to wsdisplay0
wsdisplay0: screen 0-3 added (default, vt100 emulation)
tegradrm0: info: registered panic notifier
tegradrm0: initialized tegra 0.1.0 20151108 on minor 0

Same features as before (fb console, X wsfb driver works) with the addition
of being able to use xf86-video-modesetting and xrandr to switch video
modes at runtime.


To generate a diff of this commit:
cvs rdiff -u -r1.19 -r1.20 src/sys/arch/arm/nvidia/files.tegra
cvs rdiff -u -r1.3 -r0 src/sys/arch/arm/nvidia/tegra_dc.c
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/nvidia/tegra_drm.c \
    src/sys/arch/arm/nvidia/tegra_drm.h \
    src/sys/arch/arm/nvidia/tegra_drm_fb.c \
    src/sys/arch/arm/nvidia/tegra_drm_mode.c \
    src/sys/arch/arm/nvidia/tegra_fb.c
cvs rdiff -u -r1.10 -r0 src/sys/arch/arm/nvidia/tegra_hdmi.c
cvs rdiff -u -r1.16 -r1.17 src/sys/arch/arm/nvidia/tegra_io.c
cvs rdiff -u -r1.34 -r1.35 src/sys/arch/evbarm/conf/JETSONTK1
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/evbarm/conf/NYAN-BIG
cvs rdiff -u -r1.24 -r1.25 src/sys/arch/evbarm/tegra/tegra_machdep.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/nvidia/files.tegra
diff -u src/sys/arch/arm/nvidia/files.tegra:1.19 src/sys/arch/arm/nvidia/files.tegra:1.20
--- src/sys/arch/arm/nvidia/files.tegra:1.19	Wed Oct 21 20:02:12 2015
+++ src/sys/arch/arm/nvidia/files.tegra	Mon Nov  9 23:05:58 2015
@@ -1,4 +1,4 @@
-#	$NetBSD: files.tegra,v 1.19 2015/10/21 20:02:12 jmcneill Exp $
+#	$NetBSD: files.tegra,v 1.20 2015/11/09 23:05:58 jmcneill Exp $
 #
 # Configuration info for NVIDIA Tegra ARM Peripherals
 #
@@ -102,26 +102,24 @@ device	tegrahost1x
 attach	tegrahost1x at tegraio with tegra_host1x
 file	arch/arm/nvidia/tegra_host1x.c		tegra_host1x
 
-# Display controller
-device	tegradc { }
-attach	tegradc at tegraio with tegra_dc
-file	arch/arm/nvidia/tegra_dc.c		tegra_dc
-
-# Framebuffer console
-attach	genfb at tegradc with tegra_genfb
-file	arch/arm/nvidia/tegra_genfb.c		tegra_genfb
-
-# HDMI
-device	tegrahdmi: edid, ddc_read_edid, videomode
-attach	tegrahdmi at tegraio with tegra_hdmi
-file	arch/arm/nvidia/tegra_hdmi.c		tegra_hdmi
-defflag	opt_tegra.h			TEGRA_HDMI_DEBUG
-
 # HDMI CEC
 device	tegracec: hdmicecbus
 attach	tegracec at tegraio with tegra_cec
 file	arch/arm/nvidia/tegra_cec.c		tegra_cec
 
+# Display
+define	tegrafbbus { }
+device	tegradrm: drmkms, tegrafbbus
+attach	tegradrm at tegraio with tegra_drm
+file	arch/arm/nvidia/tegra_drm.c		tegra_drm
+file	arch/arm/nvidia/tegra_drm_mode.c	tegra_drm
+file	arch/arm/nvidia/tegra_drm_fb.c		tegra_drm
+
+# Framebuffer console
+device	tegrafb: tegrafbbus, drmfb, wsemuldisplaydev
+attach	tegrafb at tegrafbbus with tegra_fb
+file	arch/arm/nvidia/tegra_fb.c		tegra_fb
+
 # GPU
 attach	nouveau at tegraio with tegra_nouveau
 file	arch/arm/nvidia/tegra_nouveau.c		tegra_nouveau

Index: src/sys/arch/arm/nvidia/tegra_io.c
diff -u src/sys/arch/arm/nvidia/tegra_io.c:1.16 src/sys/arch/arm/nvidia/tegra_io.c:1.17
--- src/sys/arch/arm/nvidia/tegra_io.c:1.16	Fri Oct 30 19:11:57 2015
+++ src/sys/arch/arm/nvidia/tegra_io.c	Mon Nov  9 23:05:58 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_io.c,v 1.16 2015/10/30 19:11:57 jmcneill Exp $ */
+/* $NetBSD: tegra_io.c,v 1.17 2015/11/09 23:05:58 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
@@ -29,7 +29,7 @@
 #include "opt_tegra.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_io.c,v 1.16 2015/10/30 19:11:57 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_io.c,v 1.17 2015/11/09 23:05:58 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -142,12 +142,7 @@ static const struct tegra_locators tegra
 };
 
 static const struct tegra_locators tegra_ghost_locators[] = {
-  { "tegradc",
-    TEGRA_DISPLAYA_OFFSET, TEGRA_DISPLAYA_SIZE, 0, TEGRA_INTR_DISPLAYA },
-  { "tegradc",
-    TEGRA_DISPLAYB_OFFSET, TEGRA_DISPLAYB_SIZE, 1, TEGRA_INTR_DISPLAYB },
-  { "tegrahdmi",
-    TEGRA_HDMI_OFFSET, TEGRA_HDMI_SIZE, NOPORT, TEGRA_INTR_HDMI },
+  { "tegradrm", 0, 0, NOPORT, NOINTR },
 };
 
 static const struct tegra_locators tegra_gpu_locators[] = {

Index: src/sys/arch/evbarm/conf/JETSONTK1
diff -u src/sys/arch/evbarm/conf/JETSONTK1:1.34 src/sys/arch/evbarm/conf/JETSONTK1:1.35
--- src/sys/arch/evbarm/conf/JETSONTK1:1.34	Fri Oct 30 19:11:57 2015
+++ src/sys/arch/evbarm/conf/JETSONTK1	Mon Nov  9 23:05:58 2015
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: JETSONTK1,v 1.34 2015/10/30 19:11:57 jmcneill Exp $
+#	$NetBSD: JETSONTK1,v 1.35 2015/11/09 23:05:58 jmcneill Exp $
 #
 #	NVIDIA Jetson TK1 - Tegra K1 development kit
 #	https://developer.nvidia.com/jetson-tk1
@@ -122,14 +122,17 @@ options 	HDAUDIO_32BIT_ACCESS
 options 	HDAUDIO_ENABLE_HDMI
 options 	HDAUDIO_ENABLE_DISPLAYPORT
 
+# HDMI CEC
+tegracec0	at tegraio?		# HDMI CEC
+hdmicec*	at hdmicecbus?
+
 # Host1x subsystem
 tegrahost1x0	at tegraio?		# HOST1X
 
-# Display controller
-tegradc0	at tegraio? port 0	# DISPLAYA
-tegradc1	at tegraio? port 1	# DISPLAYB
-genfb*		at tegradc?
-wsdisplay*	at genfb?
+# Display
+tegradrm0	at tegraio?		# Display
+tegrafb*	at tegrafbbus?
+wsdisplay*	at wsemuldisplaydev?
 options 	VCONS_DRAW_INTR
 options 	WSEMUL_VT100
 options 	WS_DEFAULT_FG=WSCOL_WHITE
@@ -144,11 +147,6 @@ options 	WSDISPLAY_DEFAULTSCREENS=4
 pseudo-device	wsmux
 pseudo-device	wsfont
 
-# HDMI
-tegrahdmi0	at tegraio?		# HDMI
-tegracec0	at tegraio?		# HDMI CEC
-hdmicec*	at hdmicecbus?
-
 # GPU
 #nouveau0	at tegraio?		# GPU
 

Index: src/sys/arch/evbarm/conf/NYAN-BIG
diff -u src/sys/arch/evbarm/conf/NYAN-BIG:1.1 src/sys/arch/evbarm/conf/NYAN-BIG:1.2
--- src/sys/arch/evbarm/conf/NYAN-BIG:1.1	Sat Aug 22 15:10:04 2015
+++ src/sys/arch/evbarm/conf/NYAN-BIG	Mon Nov  9 23:05:58 2015
@@ -1,5 +1,5 @@
 #
-#	$NetBSD: NYAN-BIG,v 1.1 2015/08/22 15:10:04 jmcneill Exp $
+#	$NetBSD: NYAN-BIG,v 1.2 2015/11/09 23:05:58 jmcneill Exp $
 #
 #	Chrome OS nyan_big board - Tegra K1
 #	- Acer Chromebook 13 (CB5-311)
@@ -7,143 +7,12 @@
 
 include	"arch/evbarm/conf/std.tegra"
 include	"arch/evbarm/conf/GENERIC.common"
+include "arch/evbarm/conf/JETSONTK1"
 
-options 	BOOT_ARGS="\"console=fb\""
-
-options 	CPU_CORTEXA15
-options 	SOC_TEGRA124
+no options 	BOARD_JETSONTK1
 options 	BOARD_NYAN_BIG
-#options 	CPUFREQ_BOOT=xxx
-options 	MULTIPROCESSOR
-#options 	MEMSIZE=2048
-
-options 	DIAGNOSTIC	# internal consistency checks
-#options 	DEBUG
-#options 	LOCKDEBUG
-#options 	PMAP_DEBUG	# Enable pmap_debug_level code
-#options 	IPKDB		# remote kernel debugging
-#options 	VERBOSE_INIT_ARM # verbose bootstraping messages
-makeoptions	DEBUG="-g"	# compile full symbol table
-makeoptions	COPY_SYMTAB=1
-
-config		netbsd		root on ? type ?
-
-mainbus0	at root
-cpu*		at mainbus?
-
-# A15 core devices
-armperiph0	at mainbus?
-armgic0		at armperiph?				# Interrupt Controller
-armgtmr0	at armperiph?				# ARM Generic Timer
-
-# On-board I/O
-tegraio0	at mainbus?
-
-# Memory controller
-tegramc0	at tegraio?		# MC
-
-# Power management controller
-tegrapmc0	at tegraio?		# PMC
-
-# Clock and Reset controller
-tegracar0	at tegraio?		# CAR
-
-# GPIO controller
-tegragpio0	at tegraio?		# GPIO
-gpio*		at gpiobus?
-#gpiobutton0	at gpio16 offset 0 mask 1 flag 0x01	# Power button
-#gpiorfkill0	at gpio23 offset 7 mask 1		# WiFi enable
-
-# Timers
-tegratimer0	at tegraio?		# Timers
-
-# MPIO / Pinmux
-tegrampio0	at tegraio?		# MPIO
+no options 	CPUFREQ_BOOT
 
-# XUSB PADCTL
-tegraxusbpad0	at tegraio?		# XUSB PADCTL
-
-# PCIE
-tegrapcie0	at tegraio?		# PCIE
-pci*		at tegrapcie0
-ppb*		at pci? dev ? function ?
-pci*		at ppb?
-
-# UART
-com0		at tegraio? port 0	# UART-A
-options 	CONSADDR=0x70006000, CONSPEED=115200
-
-# I2C
-tegrai2c0	at tegraio? port 0	# I2C1
-iic0		at tegrai2c0
-titemp0		at iic0 addr 0x4c		# TI TMP451 
-tegrai2c1	at tegraio? port 1	# I2C2
-iic1		at tegrai2c1
-tegrai2c2	at tegraio? port 2	# I2C3
-iic2		at tegrai2c2
-tegrai2c3	at tegraio? port 3	# I2C4
-iic3		at tegrai2c3
-ddc0		at iic3 addr 0x50		# HDMI DDC
-tegrai2c4	at tegraio? port 4	# I2C5
-iic4		at tegrai2c4
-
-# RTC
-tegrartc0	at tegraio?		# RTC
-
-# SDMMC
-#sdhc0		at tegraio? port 0	# SDMMC1 (WiFi/BT)
-#sdmmc0		at sdhc0
-sdhc2		at tegraio? port 2	# SDMMC3 (SD card)
-sdmmc2		at sdhc2
-sdhc3		at tegraio? port 3	# SDMMC4 (eMMC)
-sdmmc3		at sdhc3
-
-ld0		at sdmmc3		# eMMC
-ld1		at sdmmc2		# SD card
-
-# HDA
-hdaudio*	at tegraio?		# HDA
-hdafg*		at hdaudiobus?
-audio*		at audiobus?
-options 	HDAUDIOVERBOSE
-options 	HDAUDIO_32BIT_ACCESS
-options 	HDAUDIO_ENABLE_HDMI
-options 	HDAUDIO_ENABLE_DISPLAYPORT
-
-# Host1x subsystem
-tegrahost1x0	at tegraio?		# HOST1X
-
-# Display controller
-tegradc0	at tegraio? port 0	# DISPLAYA
-tegradc1	at tegraio? port 1	# DISPLAYB
-genfb*		at tegradc?
-wsdisplay*	at genfb?
-options 	VCONS_DRAW_INTR
-options 	WSEMUL_VT100
-options 	WS_DEFAULT_FG=WSCOL_WHITE
-options 	WS_DEFAULT_BG=WSCOL_BLACK
-options 	WS_KERNEL_FG=WSCOL_GREEN
-options 	WS_KERNEL_BG=WSCOL_BLACK
-options 	WSDISPLAY_COMPAT_PCVT
-options 	WSDISPLAY_COMPAT_SYSCONS
-options 	WSDISPLAY_COMPAT_USL
-options 	WSDISPLAY_COMPAT_RAWKBD
-options 	WSDISPLAY_DEFAULTSCREENS=4
-pseudo-device	wsmux
-pseudo-device	wsfont
-
-# HDMI
-tegrahdmi0	at tegraio?		# HDMI
-tegracec0	at tegraio?		# HDMI CEC
-hdmicec*	at hdmicecbus?
-
-# USB 2.0
-ehci0		at tegraio? port 0	# USB1
-ehci1		at tegraio? port 1	# USB2
-ehci2		at tegraio? port 2	# USB3
-usb*		at ehci?
-
-include "dev/usb/usbdevices.config"
-midi*		at midibus?
+options 	BOOT_ARGS="\"console=fb\""
 
 cinclude "arch/evbarm/conf/NYAN-BIG.local"

Index: src/sys/arch/evbarm/tegra/tegra_machdep.c
diff -u src/sys/arch/evbarm/tegra/tegra_machdep.c:1.24 src/sys/arch/evbarm/tegra/tegra_machdep.c:1.25
--- src/sys/arch/evbarm/tegra/tegra_machdep.c:1.24	Thu Oct 22 23:29:01 2015
+++ src/sys/arch/evbarm/tegra/tegra_machdep.c	Mon Nov  9 23:05:58 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_machdep.c,v 1.24 2015/10/22 23:29:01 jmcneill Exp $ */
+/* $NetBSD: tegra_machdep.c,v 1.25 2015/11/09 23:05:58 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_machdep.c,v 1.24 2015/10/22 23:29:01 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_machdep.c,v 1.25 2015/11/09 23:05:58 jmcneill Exp $");
 
 #include "opt_tegra.h"
 #include "opt_machdep.h"
@@ -415,7 +415,7 @@ tegra_device_register(device_t self, voi
 		tegra_cpuinit();
 	}
 
-	if (device_is_a(self, "tegradc")
+	if (device_is_a(self, "tegrafb")
 	    && tegra_bootconf_match("console", "fb")) {
 		prop_dictionary_set_bool(dict, "is_console", true);
 #if NUKBD > 0
@@ -423,6 +423,12 @@ tegra_device_register(device_t self, voi
 #endif
 	}
 
+	if (device_is_a(self, "tegradrm")) {
+		if (tegra_bootconf_match("hdmi.forcemode", "dvi")) {
+			prop_dictionary_set_bool(dict, "force-dvi", true);
+		}
+	}
+
 	if (device_is_a(self, "tegracec")) {
 		prop_dictionary_set_cstring(dict, "hdmi-device", "tegrahdmi0");
 	}
@@ -481,15 +487,11 @@ tegra_device_register(device_t self, voi
 		}
 	}
 
-	if (device_is_a(self, "tegrahdmi")) {
+	if (device_is_a(self, "tegradrm")) {
 		prop_dictionary_set_cstring(dict, "hpd-gpio", "N7");
 		prop_dictionary_set_cstring(dict, "pll-gpio", "H7");
 		prop_dictionary_set_cstring(dict, "power-gpio", "K6");
 		prop_dictionary_set_cstring(dict, "ddc-device", "ddc0");
-		prop_dictionary_set_cstring(dict, "display-device", "tegradc1");
-		if (tegra_bootconf_match("hdmi.forcemode", "dvi")) {
-			prop_dictionary_set_bool(dict, "force-dvi", true);
-		}
 	}
 #endif
 
@@ -517,12 +519,11 @@ tegra_device_register(device_t self, voi
 		}
 	}
 
-	if (device_is_a(self, "tegrahdmi")) {
+	if (device_is_a(self, "tegradrm")) {
 		prop_dictionary_set_cstring(dict, "hpd-gpio", "N7");
 		prop_dictionary_set_cstring(dict, "pll-gpio", "H7");
 		prop_dictionary_set_cstring(dict, "power-gpio", "K6");
 		prop_dictionary_set_cstring(dict, "ddc-device", "ddc0");
-		prop_dictionary_set_cstring(dict, "display-device", "tegradc1");
 	}
 #endif
 }

Added files:

Index: src/sys/arch/arm/nvidia/tegra_drm.c
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_drm.c:1.1
--- /dev/null	Mon Nov  9 23:05:58 2015
+++ src/sys/arch/arm/nvidia/tegra_drm.c	Mon Nov  9 23:05:58 2015
@@ -0,0 +1,300 @@
+/* $NetBSD: tegra_drm.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "locators.h"
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tegra_drm.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+#include <sys/intr.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+
+#include <uvm/uvm_extern.h>
+#include <uvm/uvm_device.h>
+
+#include <drm/drmP.h>
+
+#include <arm/nvidia/tegra_reg.h>
+#include <arm/nvidia/tegra_var.h>
+#include <arm/nvidia/tegra_drm.h>
+
+static int	tegra_drm_match(device_t, cfdata_t, void *);
+static void	tegra_drm_attach(device_t, device_t, void *);
+
+static const char *tegra_drm_get_name(struct drm_device *);
+static int	tegra_drm_set_busid(struct drm_device *, struct drm_master *);
+
+static int	tegra_drm_load(struct drm_device *, unsigned long);
+static int	tegra_drm_unload(struct drm_device *);
+
+static int	tegra_drm_mmap_object(struct drm_device *, off_t, size_t,
+		    vm_prot_t, struct uvm_object **, voff_t *, struct file *);
+
+static int	tegra_drm_dumb_create(struct drm_file *, struct drm_device *,
+		    struct drm_mode_create_dumb *);
+static int	tegra_drm_dumb_map_offset(struct drm_file *,
+		    struct drm_device *, uint32_t, uint64_t *);
+static int	tegra_drm_dumb_destroy(struct drm_file *, struct drm_device *,
+		    uint32_t);
+
+static struct drm_driver tegra_drm_driver = {
+	.driver_features = DRIVER_MODESET,
+	.dev_priv_size = 0,
+	.load = tegra_drm_load,
+	.unload = tegra_drm_unload,
+
+	.mmap_object = tegra_drm_mmap_object,
+
+	.dumb_create = tegra_drm_dumb_create,
+	.dumb_map_offset = tegra_drm_dumb_map_offset,
+	.dumb_destroy = tegra_drm_dumb_destroy,
+
+	.name = DRIVER_NAME,
+	.desc = DRIVER_DESC,
+	.date = DRIVER_DATE,
+	.major = DRIVER_MAJOR,
+	.minor = DRIVER_MINOR,
+	.patchlevel = DRIVER_PATCHLEVEL 
+};
+
+static const struct drm_bus tegra_drm_bus = {
+	.bus_type = DRIVER_BUS_PLATFORM,
+	.get_name = tegra_drm_get_name,
+	.set_busid = tegra_drm_set_busid
+};
+
+CFATTACH_DECL_NEW(tegra_drm, sizeof(struct tegra_drm_softc),
+	tegra_drm_match, tegra_drm_attach, NULL, NULL);
+
+static int
+tegra_drm_match(device_t parent, cfdata_t cf, void *aux)
+{
+	return 1;
+}
+
+static void
+tegra_drm_attach(device_t parent, device_t self, void *aux)
+{
+	struct tegra_drm_softc * const sc = device_private(self);
+	struct tegraio_attach_args * const tio = aux;
+	prop_dictionary_t prop = device_properties(self);
+	struct drm_driver * const driver = &tegra_drm_driver;
+	const char *pin, *dev;
+	int error, nsegs;
+
+	sc->sc_dev = self;
+	sc->sc_dmat = tio->tio_dmat;
+	sc->sc_bst = tio->tio_bst;
+
+	if (prop_dictionary_get_cstring_nocopy(prop, "hpd-gpio", &pin)) {
+		sc->sc_pin_hpd = tegra_gpio_acquire(pin, GPIO_PIN_INPUT);
+	}
+	if (prop_dictionary_get_cstring_nocopy(prop, "pll-gpio", &pin)) {
+		sc->sc_pin_pll = tegra_gpio_acquire(pin, GPIO_PIN_OUTPUT);
+		if (sc->sc_pin_pll) {
+			tegra_gpio_write(sc->sc_pin_pll, 0);
+		} else {
+			panic("couldn't get pll-gpio pin");
+		}
+	}
+	if (prop_dictionary_get_cstring_nocopy(prop, "power-gpio", &pin)) {
+		sc->sc_pin_power = tegra_gpio_acquire(pin, GPIO_PIN_OUTPUT);
+		if (sc->sc_pin_power) {
+			tegra_gpio_write(sc->sc_pin_power, 1);
+		}
+	}
+	if (prop_dictionary_get_cstring_nocopy(prop, "ddc-device", &dev)) {
+		sc->sc_ddcdev = device_find_by_xname(dev);
+	}
+	prop_dictionary_get_bool(prop, "force-dvi", &sc->sc_force_dvi);
+
+	aprint_naive("\n");
+	aprint_normal("\n");
+
+        sc->sc_dmasize = 4096 * 2160 * 4;
+        error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_dmasize, PAGE_SIZE, 0,
+            sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
+        if (error)
+                goto failed;
+        error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs,
+	    sc->sc_dmasize, &sc->sc_dmap, BUS_DMA_WAITOK | BUS_DMA_COHERENT);
+        if (error)
+                goto free;
+        error = bus_dmamap_create(sc->sc_dmat, sc->sc_dmasize, 1,
+	    sc->sc_dmasize, 0, BUS_DMA_WAITOK, &sc->sc_dmamap);
+        if (error)
+                goto unmap;
+        error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_dmap,
+	    sc->sc_dmasize, NULL, BUS_DMA_WAITOK);
+        if (error)
+                goto destroy;
+
+	driver->bus = &tegra_drm_bus;
+
+	sc->sc_ddev = drm_dev_alloc(driver, sc->sc_dev);
+	if (sc->sc_ddev == NULL) {
+		aprint_error_dev(self, "couldn't allocate DRM device\n");
+		return;
+	}
+	sc->sc_ddev->dev_private = sc;
+
+	error = -drm_dev_register(sc->sc_ddev, 0);
+	if (error) {
+		drm_dev_unref(sc->sc_ddev);
+		aprint_error_dev(self, "couldn't register DRM device: %d\n",
+		    error);
+		return;
+	}
+
+	aprint_normal_dev(self, "initialized %s %d.%d.%d %s on minor %d\n",
+	    driver->name, driver->major, driver->minor, driver->patchlevel,
+	    driver->date, sc->sc_ddev->primary->index);
+
+	return;
+
+destroy:
+        bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
+unmap:
+        bus_dmamem_unmap(sc->sc_dmat, sc->sc_dmap, sc->sc_dmasize);
+free:
+        bus_dmamem_free(sc->sc_dmat, sc->sc_dmasegs, nsegs);
+failed:
+
+	aprint_error_dev(sc->sc_dev, "bus_dma setup failed\n");
+}
+
+static const char *
+tegra_drm_get_name(struct drm_device *ddev)
+{
+	return DRIVER_NAME;
+}
+
+static int
+tegra_drm_set_busid(struct drm_device *ddev, struct drm_master *master)
+{
+	const char *id = "platform:tegra:0";
+
+	master->unique = kzalloc(strlen(id) + 1, GFP_KERNEL);
+	if (master->unique == NULL)
+		return -ENOMEM;
+	strcpy(master->unique, id);
+	master->unique_len = strlen(master->unique);
+
+	return 0;
+}
+
+
+static int
+tegra_drm_load(struct drm_device *ddev, unsigned long flags)
+{
+	char *devname;
+	int error;
+
+	devname = kzalloc(strlen(DRIVER_NAME) + 1, GFP_KERNEL);
+	if (devname == NULL) {
+		return -ENOMEM;
+	}
+	strcpy(devname, DRIVER_NAME);
+	ddev->devname = devname;
+
+	error = tegra_drm_mode_init(ddev);
+	if (error)
+		goto drmerr;
+
+	error = tegra_drm_fb_init(ddev);
+	if (error)
+		goto drmerr;
+
+	return 0;
+
+drmerr:
+	drm_mode_config_cleanup(ddev);
+
+	return error;
+}
+
+static int
+tegra_drm_unload(struct drm_device *ddev)
+{
+	drm_mode_config_cleanup(ddev);
+
+	return 0;
+}
+
+static int
+tegra_drm_mmap_object(struct drm_device *ddev, off_t offset, size_t size,
+    vm_prot_t prot, struct uvm_object **uobjp, voff_t *uoffsetp,
+    struct file *file)
+{
+	/* XXX */
+	extern const struct cdevsw wsdisplay_cdevsw;
+	devmajor_t maj = cdevsw_lookup_major(&wsdisplay_cdevsw);
+	dev_t devno = makedev(maj, 0);
+	struct uvm_object *uobj;
+
+	KASSERT(offset == (offset & ~(PAGE_SIZE-1)));
+
+	uobj = udv_attach(devno, prot, offset, size);
+	if (uobj == NULL)
+		return -EINVAL;
+
+	*uobjp = uobj;
+	*uoffsetp = offset;
+	return 0;
+}
+
+static int
+tegra_drm_dumb_create(struct drm_file *file_priv, struct drm_device *ddev,
+    struct drm_mode_create_dumb *args)
+{
+	args->pitch = args->width * ((args->bpp + 7) / 8);
+	args->size = args->pitch * args->height;
+	args->handle = 0;
+
+	return 0;
+}
+
+static int
+tegra_drm_dumb_map_offset(struct drm_file *file_priv,
+    struct drm_device *ddev, uint32_t handle, uint64_t *offset)
+{
+	*offset = 0;
+	return 0;
+}
+
+static int
+tegra_drm_dumb_destroy(struct drm_file *file_priv, struct drm_device *ddev,
+    uint32_t handle)
+{
+	return 0;
+}
Index: src/sys/arch/arm/nvidia/tegra_drm.h
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_drm.h:1.1
--- /dev/null	Mon Nov  9 23:05:58 2015
+++ src/sys/arch/arm/nvidia/tegra_drm.h	Mon Nov  9 23:05:58 2015
@@ -0,0 +1,133 @@
+/* $NetBSD: tegra_drm.h,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _ARM_TEGRA_DRM_H
+#define _ARM_TEGRA_DRM_H
+
+#include <drm/drm_fb_helper.h>
+
+#define DRIVER_AUTHOR		"Jared McNeill"
+
+#define DRIVER_NAME		"tegra"
+#define DRIVER_DESC		"NVIDIA Tegra K1"
+#define DRIVER_DATE		"20151108"
+
+#define DRIVER_MAJOR		0
+#define DRIVER_MINOR		1
+#define DRIVER_PATCHLEVEL	0
+
+struct tegra_framebuffer;
+
+struct tegra_drm_softc {
+	device_t		sc_dev;
+	struct drm_device	*sc_ddev;
+
+	bus_space_tag_t		sc_bst;
+
+	device_t		sc_ddcdev;
+	struct tegra_gpio_pin	*sc_pin_hpd;
+	struct tegra_gpio_pin	*sc_pin_pll;
+	struct tegra_gpio_pin	*sc_pin_power;
+
+	bool			sc_force_dvi;
+
+	bus_dma_tag_t		sc_dmat;
+	bus_dma_segment_t	sc_dmasegs[1];
+	bus_size_t		sc_dmasize;
+	bus_dmamap_t		sc_dmamap;
+	void			*sc_dmap;
+};
+
+struct tegra_drmfb_attach_args {
+	struct drm_device	*tfa_drm_dev;
+	struct drm_fb_helper	*tfa_fb_helper;
+	struct drm_fb_helper_surface_size tfa_fb_sizes;
+	bus_space_tag_t		tfa_fb_bst;
+	bus_dma_tag_t		tfa_fb_dmat;
+};
+
+struct tegra_crtc {
+	struct drm_crtc		base;
+	bus_space_tag_t		bst;
+	bus_space_handle_t	bsh;
+	bus_size_t		size;
+	int			intr;
+	int			index;
+};
+
+struct tegra_encoder {
+	struct drm_encoder	base;
+	bus_space_tag_t		bst;
+	bus_space_handle_t	bsh;
+	bus_size_t		size;
+};
+
+struct tegra_connector {
+	struct drm_connector	base;
+	device_t		ddcdev;
+	struct tegra_gpio_pin	*hpd;
+
+	bool			has_hdmi_sink;
+	bool			has_audio;
+};
+
+struct tegra_framebuffer {
+	struct drm_framebuffer	base;
+};
+
+struct tegra_fbdev {
+	struct drm_fb_helper	helper;
+};
+
+#define HDMI_READ(enc, reg)			\
+    bus_space_read_4((enc)->bst, (enc)->bsh, (reg))
+#define HDMI_WRITE(enc, reg, val)		\
+    bus_space_write_4((enc)->bst, (enc)->bsh, (reg), (val))
+#define HDMI_SET_CLEAR(enc, reg, set, clr)	\
+    tegra_reg_set_clear((enc)->bst, (enc)->bsh, (reg), (set), (clr))
+
+#define DC_READ(crtc, reg)			\
+    bus_space_read_4((crtc)->bst, (crtc)->bsh, (reg))
+#define DC_WRITE(crtc, reg, val)		\
+    bus_space_write_4((crtc)->bst, (crtc)->bsh, (reg), (val))
+#define DC_SET_CLEAR(crtc, reg, set, clr)	\
+    tegra_reg_set_clear((crtc)->bst, (crtc)->bsh, (reg), (set), (clr))
+
+#define TEGRA_DC_DEPTH		32
+
+#define tegra_drm_private(ddev)	(ddev)->dev_private
+#define to_tegra_crtc(x)	container_of(x, struct tegra_crtc, base)
+#define to_tegra_encoder(x)	container_of(x, struct tegra_encoder, base)
+#define to_tegra_connector(x)	container_of(x, struct tegra_connector, base)
+#define to_tegra_framebuffer(x)	container_of(x, struct tegra_framebuffer, base)
+#define to_tegra_fbdev(x)	container_of(x, struct tegra_fbdev, helper)
+
+int	tegra_drm_mode_init(struct drm_device *);
+int	tegra_drm_fb_init(struct drm_device *);
+
+#endif /* _ARM_TEGRA_DRM_H */
Index: src/sys/arch/arm/nvidia/tegra_drm_fb.c
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_drm_fb.c:1.1
--- /dev/null	Mon Nov  9 23:05:58 2015
+++ src/sys/arch/arm/nvidia/tegra_drm_fb.c	Mon Nov  9 23:05:58 2015
@@ -0,0 +1,109 @@
+/* $NetBSD: tegra_drm_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tegra_drm_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $");
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <arm/nvidia/tegra_drm.h>
+
+static int	tegra_fb_probe(struct drm_fb_helper *,
+		    struct drm_fb_helper_surface_size *);
+
+static struct drm_fb_helper_funcs tegra_fb_helper_funcs = {
+	.fb_probe = tegra_fb_probe
+};
+
+int
+tegra_drm_fb_init(struct drm_device *ddev)
+{
+	struct tegra_fbdev *fbdev;
+	struct drm_framebuffer *fb;
+	struct drm_mode_fb_cmd2 cmd;
+	int error;
+
+	fbdev = kmem_zalloc(sizeof(*fbdev), KM_SLEEP);
+	if (fbdev == NULL)
+		return -ENOMEM;
+	fbdev->helper.funcs = &tegra_fb_helper_funcs;
+
+	error = drm_fb_helper_init(ddev, &fbdev->helper, 2, 1);
+	if (error) {
+		kmem_free(fbdev, sizeof(*fbdev));
+		return error;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.width = 4096;
+	cmd.height = 2160;
+	cmd.pixel_format = DRM_FORMAT_ARGB8888;
+	cmd.pitches[0] = cmd.width * (32 / 8);
+
+	fb = ddev->mode_config.funcs->fb_create(ddev, NULL, &cmd);
+	if (fb == NULL) {
+		DRM_ERROR("couldn't create framebuffer\n");
+		return -EIO;
+	}
+	fbdev->helper.fb = fb;
+
+	drm_fb_helper_single_add_all_connectors(&fbdev->helper);
+
+	drm_helper_disable_unused_functions(ddev);
+
+	drm_fb_helper_initial_config(&fbdev->helper, 32);
+
+	return 0;
+}
+
+static int
+tegra_fb_probe(struct drm_fb_helper *helper,
+    struct drm_fb_helper_surface_size *sizes)
+{
+	struct tegra_drm_softc * const sc = tegra_drm_private(helper->dev);
+	struct drm_device *ddev = helper->dev;
+	struct tegra_drmfb_attach_args tfa;
+
+	memset(&tfa, 0, sizeof(tfa));
+	tfa.tfa_drm_dev = ddev;
+	tfa.tfa_fb_helper = helper;
+	tfa.tfa_fb_sizes = *sizes;
+	tfa.tfa_fb_bst = sc->sc_bst;
+	tfa.tfa_fb_dmat = sc->sc_dmat;
+
+	helper->fbdev = config_found_ia(ddev->dev, "tegrafbbus", &tfa, NULL);
+	if (helper->fbdev == NULL) {
+		DRM_ERROR("unable to attach tegrafb\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
Index: src/sys/arch/arm/nvidia/tegra_drm_mode.c
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_drm_mode.c:1.1
--- /dev/null	Mon Nov  9 23:05:58 2015
+++ src/sys/arch/arm/nvidia/tegra_drm_mode.c	Mon Nov  9 23:05:58 2015
@@ -0,0 +1,958 @@
+/* $NetBSD: tegra_drm_mode.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tegra_drm_mode.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $");
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_edid.h>
+
+#include <dev/i2c/ddcvar.h>
+
+#include <arm/nvidia/tegra_reg.h>
+#include <arm/nvidia/tegra_var.h>
+#include <arm/nvidia/tegra_intr.h>
+#include <arm/nvidia/tegra_dcreg.h>
+#include <arm/nvidia/tegra_hdmireg.h>
+#include <arm/nvidia/tegra_drm.h>
+
+static struct drm_framebuffer *tegra_fb_create(struct drm_device *,
+		    struct drm_file *, struct drm_mode_fb_cmd2 *);
+
+static const struct drm_mode_config_funcs tegra_mode_config_funcs = {
+	.fb_create = tegra_fb_create
+};
+
+static void	tegra_framebuffer_destroy(struct drm_framebuffer *);
+
+static const struct drm_framebuffer_funcs tegra_framebuffer_funcs = {
+	.destroy = tegra_framebuffer_destroy
+};
+
+static int	tegra_crtc_init(struct drm_device *, int);
+static void	tegra_crtc_destroy(struct drm_crtc *);
+
+static const struct drm_crtc_funcs tegra_crtc_funcs = {
+	.set_config = drm_crtc_helper_set_config,
+	.destroy = tegra_crtc_destroy
+};
+
+static void	tegra_crtc_dpms(struct drm_crtc *, int);
+static bool	tegra_crtc_mode_fixup(struct drm_crtc *,
+		    const struct drm_display_mode *,
+		    struct drm_display_mode *);
+static int	tegra_crtc_mode_set(struct drm_crtc *,
+		    struct drm_display_mode *, struct drm_display_mode *,
+		    int, int, struct drm_framebuffer *);
+static int	tegra_crtc_mode_set_base(struct drm_crtc *,
+		    int, int, struct drm_framebuffer *);
+static int	tegra_crtc_mode_set_base_atomic(struct drm_crtc *,
+		    struct drm_framebuffer *, int, int, enum mode_set_atomic);
+static void	tegra_crtc_disable(struct drm_crtc *);
+static void	tegra_crtc_prepare(struct drm_crtc *);
+static void	tegra_crtc_commit(struct drm_crtc *);
+
+static int	tegra_crtc_do_set_base(struct drm_crtc *,
+		    struct drm_framebuffer *, int, int, int);
+
+static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
+	.dpms = tegra_crtc_dpms,
+	.mode_fixup = tegra_crtc_mode_fixup,
+	.mode_set = tegra_crtc_mode_set,
+	.mode_set_base = tegra_crtc_mode_set_base,
+	.mode_set_base_atomic = tegra_crtc_mode_set_base_atomic,
+	.disable = tegra_crtc_disable,
+	.prepare = tegra_crtc_prepare,
+	.commit = tegra_crtc_commit
+};
+
+static int	tegra_encoder_init(struct drm_device *);
+static void	tegra_encoder_destroy(struct drm_encoder *);
+
+static const struct drm_encoder_funcs tegra_encoder_funcs = {
+	.destroy = tegra_encoder_destroy
+};
+
+static void	tegra_encoder_dpms(struct drm_encoder *, int);
+static bool	tegra_encoder_mode_fixup(struct drm_encoder *,
+		    const struct drm_display_mode *, struct drm_display_mode *);
+static void	tegra_encoder_mode_set(struct drm_encoder *,
+		    struct drm_display_mode *, struct drm_display_mode *);
+static void	tegra_encoder_prepare(struct drm_encoder *);
+static void	tegra_encoder_commit(struct drm_encoder *);
+
+static const struct drm_encoder_helper_funcs tegra_encoder_helper_funcs = {
+	.dpms = tegra_encoder_dpms,
+	.mode_fixup = tegra_encoder_mode_fixup,
+	.prepare = tegra_encoder_prepare,
+	.commit = tegra_encoder_commit,
+	.mode_set = tegra_encoder_mode_set
+};
+
+static int	tegra_connector_init(struct drm_device *, struct drm_encoder *);
+static void	tegra_connector_destroy(struct drm_connector *);
+static enum drm_connector_status tegra_connector_detect(struct drm_connector *,
+		    bool);
+
+static const struct drm_connector_funcs tegra_connector_funcs = {
+	.dpms = drm_helper_connector_dpms,
+	.detect = tegra_connector_detect,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = tegra_connector_destroy
+};
+
+static int	tegra_connector_mode_valid(struct drm_connector *,
+		    struct drm_display_mode *);
+static int	tegra_connector_get_modes(struct drm_connector *);
+static struct drm_encoder *tegra_connector_best_encoder(struct drm_connector *);
+
+static const struct drm_connector_helper_funcs tegra_connector_helper_funcs = {
+	.mode_valid = tegra_connector_mode_valid,
+	.get_modes = tegra_connector_get_modes,
+	.best_encoder = tegra_connector_best_encoder
+};
+
+static const struct tegra_hdmi_tmds_config {
+	u_int		dot_clock;
+	uint32_t	sor_pll0;
+	uint32_t	sor_pll1;
+	uint32_t	sor_lane_drive_current;
+	uint32_t	pe_current;
+	uint32_t	sor_io_peak_current;
+	uint32_t	sor_pad_ctls0;
+	uint32_t	car_plld_misc;	/* XXX unused? */
+} tegra_hdmi_tmds_config[] = {
+	/* 480p */
+	{ 27000,      0x01003010, 0x00301b00, 0x1f1f1f1f,
+	  0x00000000, 0x03030303, 0x800034bb, 0x40400820 },
+	/* 720p / 1080i */
+	{ 74250,      0x01003110, 0x00301500, 0x2c2c2c2c,
+	  0x00000000, 0x07070707, 0x800034bb, 0x40400820 },
+	/* 1080p */
+	{ 148500,     0x01003310, 0x00301500, 0x2d2d2d2d,
+	  0x00000000, 0x05050505, 0x800034bb, 0x40400820 },
+	/* 2160p */
+	{ 297000,     0x01003f10, 0x00300f00, 0x37373737,
+	  0x00000000, 0x17171717, 0x800036bb, 0x40400f20 },
+};
+
+int
+tegra_drm_mode_init(struct drm_device *ddev)
+{
+	int error;
+
+	drm_mode_config_init(ddev);
+	ddev->mode_config.min_width = 0;
+	ddev->mode_config.min_height = 0;
+	ddev->mode_config.max_width = 4096;
+	ddev->mode_config.max_height = 2160;
+	ddev->mode_config.funcs = &tegra_mode_config_funcs;
+
+	error = tegra_crtc_init(ddev, 0);
+	if (error)
+		return error;
+
+	error = tegra_crtc_init(ddev, 1);
+	if (error)
+		return error;
+
+	error = tegra_encoder_init(ddev);
+	if (error)
+		return error;
+
+	return 0;
+}
+
+static struct drm_framebuffer *
+tegra_fb_create(struct drm_device *ddev, struct drm_file *file,
+    struct drm_mode_fb_cmd2 *cmd)
+{
+	struct tegra_framebuffer *fb;
+	int error;
+
+	if (cmd->flags)
+		return NULL;
+	if (cmd->pixel_format != DRM_FORMAT_ARGB8888 &&
+	    cmd->pixel_format != DRM_FORMAT_XRGB8888) {
+		return NULL;
+	}
+
+	fb = kmem_zalloc(sizeof(*fb), KM_SLEEP);
+	if (fb == NULL)
+		return NULL;
+
+	fb->base.pitches[0] = cmd->pitches[0];
+	fb->base.offsets[0] = cmd->offsets[0];
+	fb->base.width = cmd->width;
+	fb->base.height = cmd->height;
+	fb->base.pixel_format = cmd->pixel_format;
+	drm_fb_get_bpp_depth(cmd->pixel_format, &fb->base.depth,
+	    &fb->base.bits_per_pixel);
+
+	error = drm_framebuffer_init(ddev, &fb->base, &tegra_framebuffer_funcs);
+	if (error)
+		goto dealloc;
+
+	return &fb->base;
+
+	drm_framebuffer_cleanup(&fb->base);
+dealloc:
+	kmem_free(fb, sizeof(*fb));
+	return NULL;
+}
+
+static void
+tegra_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+	struct tegra_framebuffer *tegra_fb = to_tegra_framebuffer(fb);
+
+	drm_framebuffer_cleanup(fb);
+	kmem_free(tegra_fb, sizeof(*tegra_fb));
+}
+
+static int
+tegra_crtc_init(struct drm_device *ddev, int index)
+{
+	struct tegra_drm_softc * const sc = tegra_drm_private(ddev);
+	struct tegra_crtc *crtc;
+	bus_addr_t offset;
+	bus_size_t size;
+	u_int intr;
+	int error;
+
+	switch (index) {
+	case 0:
+		offset = TEGRA_GHOST_BASE + TEGRA_DISPLAYA_OFFSET;
+		size = TEGRA_DISPLAYA_SIZE;
+		intr = TEGRA_INTR_DISPLAYA;
+		break;
+	case 1:
+		offset = TEGRA_GHOST_BASE + TEGRA_DISPLAYB_OFFSET;
+		size = TEGRA_DISPLAYB_SIZE;
+		intr = TEGRA_INTR_DISPLAYB;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	crtc = kmem_zalloc(sizeof(*crtc), KM_SLEEP);
+	if (crtc == NULL)
+		return -ENOMEM;
+
+	crtc->index = index;
+	crtc->bst = sc->sc_bst;
+	error = bus_space_map(crtc->bst, offset, size, 0, &crtc->bsh);
+	if (error) {
+		kmem_free(crtc, sizeof(*crtc));
+		return -error;
+	}
+	crtc->size = size;
+	crtc->intr = intr;
+
+	tegra_car_dc_enable(crtc->index);
+
+	drm_crtc_init(ddev, &crtc->base, &tegra_crtc_funcs);
+	drm_crtc_helper_add(&crtc->base, &tegra_crtc_helper_funcs);
+
+	return 0;
+}
+
+static void
+tegra_crtc_destroy(struct drm_crtc *crtc)
+{
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc);
+	drm_crtc_cleanup(crtc);
+	bus_space_unmap(tegra_crtc->bst, tegra_crtc->bsh, tegra_crtc->size);
+	kmem_free(tegra_crtc, sizeof(*tegra_crtc));
+}
+
+static void
+tegra_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc);
+
+	switch (mode) {
+	case DRM_MODE_DPMS_ON:
+	case DRM_MODE_DPMS_STANDBY:
+	case DRM_MODE_DPMS_SUSPEND:
+		DC_SET_CLEAR(tegra_crtc, DC_WINC_A_WIN_OPTIONS_REG,
+		    DC_WINC_A_WIN_OPTIONS_WIN_ENABLE, 0);
+		break;
+	case DRM_MODE_DPMS_OFF:
+		DC_SET_CLEAR(tegra_crtc, DC_WINC_A_WIN_OPTIONS_REG,
+		    0, DC_WINC_A_WIN_OPTIONS_WIN_ENABLE);
+		break;
+	}
+}
+
+static bool
+tegra_crtc_mode_fixup(struct drm_crtc *crtc,
+    const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static int
+tegra_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
+    struct drm_display_mode *adjusted_mode, int x, int y,
+    struct drm_framebuffer *old_fb)
+{
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc);
+	const u_int hspw = mode->crtc_hsync_end - mode->crtc_hsync_start;
+	const u_int hbp = mode->crtc_htotal - mode->crtc_hsync_end;
+	const u_int hfp = mode->crtc_hsync_start - mode->crtc_hdisplay;
+	const u_int vspw = mode->crtc_vsync_end - mode->crtc_vsync_start;
+	const u_int vbp = mode->crtc_vtotal - mode->crtc_vsync_end;
+	const u_int vfp = mode->crtc_vsync_start - mode->crtc_vdisplay;
+
+	/* Set colour depth to ARGB8888 */
+	DC_WRITE(tegra_crtc, DC_WINC_A_COLOR_DEPTH_REG,
+	    __SHIFTIN(DC_WINC_A_COLOR_DEPTH_DEPTH_T_A8R8G8B8,
+	    DC_WINC_A_COLOR_DEPTH_DEPTH));
+
+	/* Disable byte swapping */
+	DC_WRITE(tegra_crtc, DC_WINC_A_BYTE_SWAP_REG,
+	    __SHIFTIN(DC_WINC_A_BYTE_SWAP_SWAP_NOSWAP,
+	    DC_WINC_A_BYTE_SWAP_SWAP));
+
+	/* Initial DDA */
+	DC_WRITE(tegra_crtc, DC_WINC_A_H_INITIAL_DDA_REG, 0);
+	DC_WRITE(tegra_crtc, DC_WINC_A_V_INITIAL_DDA_REG, 0);
+	DC_WRITE(tegra_crtc, DC_WINC_A_DDA_INCREMENT_REG, 0x10001000);
+
+	/* Window position, size, stride */
+	DC_WRITE(tegra_crtc, DC_WINC_A_POSITION_REG,
+	    __SHIFTIN(0, DC_WINC_A_POSITION_V) |
+	    __SHIFTIN(0, DC_WINC_A_POSITION_H));
+	DC_WRITE(tegra_crtc, DC_WINC_A_SIZE_REG,
+	    __SHIFTIN(mode->crtc_vdisplay, DC_WINC_A_SIZE_V) |
+	    __SHIFTIN(mode->crtc_hdisplay, DC_WINC_A_SIZE_H));
+	DC_WRITE(tegra_crtc, DC_WINC_A_PRESCALED_SIZE_REG,
+	    __SHIFTIN(mode->crtc_vdisplay, DC_WINC_A_PRESCALED_SIZE_V) |
+	    __SHIFTIN(mode->crtc_hdisplay * (TEGRA_DC_DEPTH / 8),
+		      DC_WINC_A_PRESCALED_SIZE_H));
+	DC_WRITE(tegra_crtc, DC_WINC_A_LINE_STRIDE_REG,
+	    __SHIFTIN(mode->crtc_hdisplay * (TEGRA_DC_DEPTH / 8),
+	    DC_WINC_A_LINE_STRIDE_LINE_STRIDE));
+
+	tegra_crtc_do_set_base(crtc, old_fb, x, y, 0);
+
+	/* Enable window A */
+	DC_WRITE(tegra_crtc, DC_WINC_A_WIN_OPTIONS_REG,
+	    DC_WINC_A_WIN_OPTIONS_WIN_ENABLE);
+
+	/* Timing and signal options */
+	DC_WRITE(tegra_crtc, DC_DISP_DISP_TIMING_OPTIONS_REG,
+	    __SHIFTIN(1, DC_DISP_DISP_TIMING_OPTIONS_VSYNC_POS));
+	DC_WRITE(tegra_crtc, DC_DISP_DISP_COLOR_CONTROL_REG,
+	    __SHIFTIN(DC_DISP_DISP_COLOR_CONTROL_BASE_COLOR_SIZE_888,
+		      DC_DISP_DISP_COLOR_CONTROL_BASE_COLOR_SIZE));
+	DC_WRITE(tegra_crtc, DC_DISP_DISP_SIGNAL_OPTIONS0_REG,
+	    DC_DISP_DISP_SIGNAL_OPTIONS0_H_PULSE2_ENABLE);
+	DC_WRITE(tegra_crtc, DC_DISP_H_PULSE2_CONTROL_REG,
+	    __SHIFTIN(DC_DISP_H_PULSE2_CONTROL_V_QUAL_VACTIVE,
+		      DC_DISP_H_PULSE2_CONTROL_V_QUAL) |
+	    __SHIFTIN(DC_DISP_H_PULSE2_CONTROL_LAST_END_A,
+		      DC_DISP_H_PULSE2_CONTROL_LAST));
+
+	const u_int pulse_start = 1 + hspw + hbp - 10;
+	DC_WRITE(tegra_crtc, DC_DISP_H_PULSE2_POSITION_A_REG,
+	    __SHIFTIN(pulse_start, DC_DISP_H_PULSE2_POSITION_A_START) |
+	    __SHIFTIN(pulse_start + 8, DC_DISP_H_PULSE2_POSITION_A_END));
+
+	/* Pixel clock */
+	const u_int div = (tegra_car_plld2_rate() * 2) /
+	    (mode->crtc_clock * 1000) - 2;
+	DC_WRITE(tegra_crtc, DC_DISP_DISP_CLOCK_CONTROL_REG,
+	    __SHIFTIN(0, DC_DISP_DISP_CLOCK_CONTROL_PIXEL_CLK_DIVIDER) |
+	    __SHIFTIN(div, DC_DISP_DISP_CLOCK_CONTROL_SHIFT_CLK_DIVIDER));
+
+	/* Mode timings */
+	DC_WRITE(tegra_crtc, DC_DISP_REF_TO_SYNC_REG,
+	    __SHIFTIN(1, DC_DISP_REF_TO_SYNC_V) |
+	    __SHIFTIN(1, DC_DISP_REF_TO_SYNC_H));
+	DC_WRITE(tegra_crtc, DC_DISP_SYNC_WIDTH_REG,
+	    __SHIFTIN(vspw, DC_DISP_SYNC_WIDTH_V) |
+	    __SHIFTIN(hspw, DC_DISP_SYNC_WIDTH_H));
+	DC_WRITE(tegra_crtc, DC_DISP_BACK_PORCH_REG,
+	    __SHIFTIN(vbp, DC_DISP_BACK_PORCH_V) |
+	    __SHIFTIN(hbp, DC_DISP_BACK_PORCH_H));
+	DC_WRITE(tegra_crtc, DC_DISP_FRONT_PORCH_REG,
+	    __SHIFTIN(vfp, DC_DISP_FRONT_PORCH_V) |
+	    __SHIFTIN(hfp, DC_DISP_FRONT_PORCH_H));
+	DC_WRITE(tegra_crtc, DC_DISP_DISP_ACTIVE_REG,
+	    __SHIFTIN(mode->crtc_vdisplay, DC_DISP_DISP_ACTIVE_V) |
+	    __SHIFTIN(mode->crtc_hdisplay, DC_DISP_DISP_ACTIVE_H));
+
+	return 0;
+}
+
+static int
+tegra_crtc_do_set_base(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+    int x, int y, int atomic)
+{
+	struct tegra_drm_softc * const sc = tegra_drm_private(crtc->dev);
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc);
+#if 0
+	struct tegra_framebuffer *tegra_fb = atomic ?
+	    to_tegra_framebuffer(fb) :
+	    to_tegra_framebuffer(crtc->primary->fb);
+#endif
+
+	/* Framebuffer start address */
+	DC_WRITE(tegra_crtc, DC_WINBUF_A_START_ADDR_REG,
+	    (uint32_t)sc->sc_dmamap->dm_segs[0].ds_addr);
+
+	/* Offsets */
+	DC_WRITE(tegra_crtc, DC_WINBUF_A_ADDR_H_OFFSET_REG, x);
+	DC_WRITE(tegra_crtc, DC_WINBUF_A_ADDR_V_OFFSET_REG, y);
+
+	/* Surface kind */
+	DC_WRITE(tegra_crtc, DC_WINBUF_A_SURFACE_KIND_REG,
+	    __SHIFTIN(DC_WINBUF_A_SURFACE_KIND_SURFACE_KIND_PITCH,
+	    DC_WINBUF_A_SURFACE_KIND_SURFACE_KIND));
+
+	return 0;
+}
+
+static int
+tegra_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
+    struct drm_framebuffer *old_fb)
+{
+	return tegra_crtc_do_set_base(crtc, old_fb, x, y, 0);
+}
+
+static int
+tegra_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
+    struct drm_framebuffer *fb, int x, int y, enum mode_set_atomic state)
+{
+	return tegra_crtc_do_set_base(crtc, fb, x, y, 1);
+}
+
+static void
+tegra_crtc_disable(struct drm_crtc *crtc)
+{
+}
+
+static void
+tegra_crtc_prepare(struct drm_crtc *crtc)
+{
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc);
+
+	/* Access control */
+	DC_WRITE(tegra_crtc, DC_CMD_STATE_ACCESS_REG,
+	    DC_CMD_STATE_ACCESS_READ_MUX);
+	/* Enable window A programming */
+	DC_WRITE(tegra_crtc, DC_CMD_DISPLAY_WINDOW_HEADER_REG,
+	    DC_CMD_DISPLAY_WINDOW_HEADER_WINDOW_A_SELECT);
+}
+
+static void
+tegra_crtc_commit(struct drm_crtc *crtc)
+{
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(crtc);
+
+	/* Enable continuous display mode */
+	DC_WRITE(tegra_crtc, DC_CMD_DISPLAY_COMMAND_REG,
+	    __SHIFTIN(DC_CMD_DISPLAY_COMMAND_DISPLAY_CTRL_MODE_C_DISPLAY,
+	    DC_CMD_DISPLAY_COMMAND_DISPLAY_CTRL_MODE));
+
+	/* Enable power */
+	DC_SET_CLEAR(tegra_crtc, DC_CMD_DISPLAY_POWER_CONTROL_REG,
+	    DC_CMD_DISPLAY_POWER_CONTROL_PM1_ENABLE |
+	    DC_CMD_DISPLAY_POWER_CONTROL_PM0_ENABLE |
+	    DC_CMD_DISPLAY_POWER_CONTROL_PW4_ENABLE |
+	    DC_CMD_DISPLAY_POWER_CONTROL_PW3_ENABLE |
+	    DC_CMD_DISPLAY_POWER_CONTROL_PW2_ENABLE |
+	    DC_CMD_DISPLAY_POWER_CONTROL_PW1_ENABLE |
+	    DC_CMD_DISPLAY_POWER_CONTROL_PW0_ENABLE,
+	    0);
+
+	/* Commit settings */
+	DC_WRITE(tegra_crtc, DC_CMD_STATE_CONTROL_REG,
+	    DC_CMD_STATE_CONTROL_GENERAL_UPDATE |
+	    DC_CMD_STATE_CONTROL_WIN_A_UPDATE);
+	DC_WRITE(tegra_crtc, DC_CMD_STATE_CONTROL_REG,
+	    DC_CMD_STATE_CONTROL_GENERAL_ACT_REQ |
+	    DC_CMD_STATE_CONTROL_WIN_A_ACT_REQ);
+}
+
+static int
+tegra_encoder_init(struct drm_device *ddev)
+{
+	struct tegra_drm_softc * const sc = tegra_drm_private(ddev);
+	struct tegra_encoder *encoder;
+	int error;
+
+	encoder = kmem_zalloc(sizeof(*encoder), KM_SLEEP);
+	if (encoder == NULL)
+		return -ENOMEM;
+
+	const bus_addr_t offset = TEGRA_GHOST_BASE + TEGRA_HDMI_OFFSET;
+	const bus_size_t size = TEGRA_HDMI_SIZE;
+
+	encoder->bst = sc->sc_bst;
+	error = bus_space_map(encoder->bst, offset, size, 0, &encoder->bsh);
+	if (error) {
+		kmem_free(encoder, sizeof(*encoder));
+		return -error;
+	}
+	encoder->size = size;
+
+	tegra_pmc_hdmi_enable();
+
+	drm_encoder_init(ddev, &encoder->base, &tegra_encoder_funcs,
+	    DRM_MODE_ENCODER_TMDS);
+	drm_encoder_helper_add(&encoder->base, &tegra_encoder_helper_funcs);
+
+#if 0
+	encoder->base.possible_crtcs = (1 << 0) | (1 << 1);
+#else
+	encoder->base.possible_crtcs = (1 << 1);
+#endif
+
+	return tegra_connector_init(ddev, &encoder->base);
+}
+
+static void
+tegra_encoder_destroy(struct drm_encoder *encoder)
+{
+	struct tegra_encoder *tegra_encoder = to_tegra_encoder(encoder);
+	drm_encoder_cleanup(encoder);
+	kmem_free(tegra_encoder, sizeof(*tegra_encoder));
+}
+
+static void
+tegra_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool
+tegra_encoder_mode_fixup(struct drm_encoder *encoder,
+    const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+	return true;
+}
+
+static void
+tegra_encoder_mode_set(struct drm_encoder *encoder,
+    struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode)
+{
+	struct drm_device *ddev = encoder->dev;
+	struct tegra_encoder *tegra_encoder = to_tegra_encoder(encoder);
+	struct tegra_crtc *tegra_crtc = to_tegra_crtc(encoder->crtc);
+	struct tegra_connector *tegra_connector = NULL;
+	struct drm_connector *connector;
+	const struct tegra_hdmi_tmds_config *tmds = NULL;
+	uint32_t input_ctrl;
+	int retry;
+	u_int i;
+
+	tegra_car_hdmi_enable(mode->crtc_clock * 1000);
+
+	/* find the connector for this encoder */
+	list_for_each_entry(connector, &ddev->mode_config.connector_list, head) {
+		if (connector->encoder == encoder) {
+			tegra_connector = to_tegra_connector(connector);
+			break;
+		}
+	}
+
+	for (i = 0; i < __arraycount(tegra_hdmi_tmds_config); i++) {
+		if (tegra_hdmi_tmds_config[i].dot_clock >= mode->crtc_clock) {
+			break;
+		}
+	}
+	if (i < __arraycount(tegra_hdmi_tmds_config)) {
+		tmds = &tegra_hdmi_tmds_config[i];
+	} else {
+		tmds = &tegra_hdmi_tmds_config[__arraycount(tegra_hdmi_tmds_config) - 1];
+	}
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_PLL0_REG, tmds->sor_pll0);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_PLL1_REG, tmds->sor_pll1);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT_REG,
+	    tmds->sor_lane_drive_current);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_PE_CURRENT_REG,
+	    tmds->pe_current);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT_REG,
+	    tmds->sor_io_peak_current);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_PAD_CTLS0_REG,
+	    tmds->sor_pad_ctls0);
+
+	const u_int div = (mode->crtc_clock / 1000) * 4;
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_REFCLK_REG,
+	    __SHIFTIN(div >> 2, HDMI_NV_PDISP_SOR_REFCLK_DIV_INT) |
+	    __SHIFTIN(div & 3, HDMI_NV_PDISP_SOR_REFCLK_DIV_FRAC));
+
+	HDMI_SET_CLEAR(tegra_encoder, HDMI_NV_PDISP_SOR_CSTM_REG,
+	    __SHIFTIN(HDMI_NV_PDISP_SOR_CSTM_MODE_TMDS,
+		      HDMI_NV_PDISP_SOR_CSTM_MODE) |
+	    __SHIFTIN(2, HDMI_NV_PDISP_SOR_CSTM_ROTCLK) |
+	    HDMI_NV_PDISP_SOR_CSTM_PLLDIV,
+	    HDMI_NV_PDISP_SOR_CSTM_MODE |
+	    HDMI_NV_PDISP_SOR_CSTM_ROTCLK |
+	    HDMI_NV_PDISP_SOR_CSTM_LVDS_EN);
+
+	const uint32_t inst =
+	    HDMI_NV_PDISP_SOR_SEQ_INST_DRIVE_PWM_OUT_LO |
+	    HDMI_NV_PDISP_SOR_SEQ_INST_HALT |
+	    __SHIFTIN(2, HDMI_NV_PDISP_SOR_SEQ_INST_WAIT_UNITS) |
+	    __SHIFTIN(1, HDMI_NV_PDISP_SOR_SEQ_INST_WAIT_TIME);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_SEQ_INST0_REG, inst);
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_SEQ_INST8_REG, inst);
+
+	input_ctrl = __SHIFTIN(tegra_crtc->index,
+			       HDMI_NV_PDISP_INPUT_CONTROL_HDMI_SRC_SELECT);
+	if (mode->crtc_hdisplay != 640 || mode->crtc_vdisplay != 480)
+		input_ctrl |= HDMI_NV_PDISP_INPUT_CONTROL_ARM_VIDEO_RANGE;
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_INPUT_CONTROL_REG, input_ctrl);
+
+	/* Start SOR */
+	HDMI_SET_CLEAR(tegra_encoder, HDMI_NV_PDISP_SOR_PLL0_REG,
+	    0,
+	    HDMI_NV_PDISP_SOR_PLL0_PWR |
+	    HDMI_NV_PDISP_SOR_PLL0_VCOPD |
+	    HDMI_NV_PDISP_SOR_PLL0_PULLDOWN);
+	delay(10);
+	HDMI_SET_CLEAR(tegra_encoder, HDMI_NV_PDISP_SOR_PLL0_REG,
+	    0,
+	    HDMI_NV_PDISP_SOR_PLL0_PDBG);
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_PWR_REG,
+	    HDMI_NV_PDISP_SOR_PWR_NORMAL_STATE |
+	    HDMI_NV_PDISP_SOR_PWR_SETTING_NEW);
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_PWR_REG,
+	    HDMI_NV_PDISP_SOR_PWR_NORMAL_STATE);
+
+	for (retry = 10000; retry > 0; retry--) {
+		const uint32_t pwr = HDMI_READ(tegra_encoder,
+		    HDMI_NV_PDISP_SOR_PWR_REG);
+		if ((pwr & HDMI_NV_PDISP_SOR_PWR_SETTING_NEW) == 0)
+			break;
+		delay(10);
+	}
+	if (retry == 0) {
+		DRM_ERROR("timeout enabling SOR power\n");
+	}
+
+	uint32_t state2 =
+	    __SHIFTIN(1, HDMI_NV_PDISP_SOR_STATE2_ASY_OWNER) |
+	    __SHIFTIN(3, HDMI_NV_PDISP_SOR_STATE2_ASY_SUBOWNER) |
+	    __SHIFTIN(1, HDMI_NV_PDISP_SOR_STATE2_ASY_CRCMODE) |
+	    __SHIFTIN(1, HDMI_NV_PDISP_SOR_STATE2_ASY_PROTOCOL);
+	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
+		state2 |= HDMI_NV_PDISP_SOR_STATE2_ASY_HSYNCPOL;
+	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
+		state2 |= HDMI_NV_PDISP_SOR_STATE2_ASY_VSYNCPOL;
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_STATE2_REG, state2);
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_STATE1_REG,
+	    __SHIFTIN(HDMI_NV_PDISP_SOR_STATE1_ASY_HEAD_OPMODE_AWAKE,
+		      HDMI_NV_PDISP_SOR_STATE1_ASY_HEAD_OPMODE) |
+	    HDMI_NV_PDISP_SOR_STATE1_ASY_ORMODE);
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_STATE0_REG, 0);
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_STATE0_REG,
+	    HDMI_NV_PDISP_SOR_STATE0_UPDATE);
+
+	HDMI_SET_CLEAR(tegra_encoder, HDMI_NV_PDISP_SOR_STATE1_REG,
+	    HDMI_NV_PDISP_SOR_STATE1_ATTACHED, 0);
+
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_STATE0_REG, 0);
+
+	const u_int rekey = 56;
+	const u_int hspw = mode->hsync_end - mode->hsync_start;
+	const u_int hbp = mode->htotal - mode->hsync_end;
+	const u_int hfp = mode->hsync_start - mode->hdisplay;
+	const u_int max_ac_packet = (hspw + hbp + hfp - rekey - 18) / 32;
+	uint32_t ctrl =
+	    __SHIFTIN(rekey, HDMI_NV_PDISP_HDMI_CTRL_REKEY) |
+	    __SHIFTIN(max_ac_packet, HDMI_NV_PDISP_HDMI_CTRL_MAX_AC_PACKET);
+	if (tegra_connector && tegra_connector->has_hdmi_sink) {
+		ctrl |= HDMI_NV_PDISP_HDMI_CTRL_ENABLE; /* HDMI ENABLE */
+	}
+	HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_HDMI_CTRL_REG, ctrl);
+
+	if (tegra_connector && tegra_connector->has_hdmi_sink &&
+	    tegra_connector->has_audio) {
+		struct hdmi_audio_infoframe ai;
+		struct hdmi_avi_infoframe avii;
+		uint8_t aibuf[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
+		uint8_t aviibuf[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
+		const u_int n = 6144;	/* 48 kHz */
+		const u_int cts = ((mode->crtc_clock * 10) * (n / 128)) / 480;
+
+		HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_REG,
+		    __SHIFTIN(HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_SOURCE_SELECT_AUTO,
+			      HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_SOURCE_SELECT) |
+		    HDMI_NV_PDISP_SOR_AUDIO_CNTRL0_INJECT_NULLSMPL);
+		HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_AUDIO_N_REG,
+		    HDMI_NV_PDISP_AUDIO_N_RESETF |
+		    HDMI_NV_PDISP_AUDIO_N_GENERATE |
+		    __SHIFTIN(n - 1, HDMI_NV_PDISP_AUDIO_N_VALUE));
+
+		HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_HDMI_SPARE_REG,
+		    HDMI_NV_PDISP_HDMI_SPARE_HW_CTS |
+		    HDMI_NV_PDISP_HDMI_SPARE_FORCE_SW_CTS |
+		    __SHIFTIN(1, HDMI_NV_PDISP_HDMI_SPARE_CTS_RESET_VAL));
+
+		/*
+		 * When HW_CTS=1 and FORCE_SW_CTS=1, the CTS is programmed by
+		 * software in the 44.1 kHz register regardless of chosen rate.
+		 */
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW_REG,
+		    cts << 8);
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH_REG,
+		    0x80000000 | n);
+
+		HDMI_SET_CLEAR(tegra_encoder, HDMI_NV_PDISP_AUDIO_N_REG, 0,
+		    HDMI_NV_PDISP_AUDIO_N_RESETF);
+
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480_REG, 24000);
+
+		hdmi_audio_infoframe_init(&ai);
+		ai.channels = 2;
+		hdmi_audio_infoframe_pack(&ai, aibuf, sizeof(aibuf));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER_REG,
+		    aibuf[0] | (aibuf[1] << 8) | (aibuf[2] << 16));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW_REG,
+		    aibuf[3] | (aibuf[4] << 8) |
+		    (aibuf[5] << 16) | (aibuf[6] << 24));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH_REG,
+		    aibuf[7] | (aibuf[8] << 8) | (aibuf[9] << 16));
+
+		hdmi_avi_infoframe_init(&avii);
+		drm_hdmi_avi_infoframe_from_display_mode(&avii, mode);
+		hdmi_avi_infoframe_pack(&avii, aviibuf, sizeof(aviibuf));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER_REG,
+		    aviibuf[0] | (aviibuf[1] << 8) | (aviibuf[2] << 16));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW_REG,
+		    aviibuf[3] | (aviibuf[4] << 8) |
+		    (aviibuf[5] << 16) | (aviibuf[6] << 24));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH_REG,
+		    aviibuf[7] | (aviibuf[8] << 8) | (aviibuf[9] << 16));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW_REG,
+		    aviibuf[10] | (aviibuf[11] << 8) |
+		    (aviibuf[12] << 16) | (aviibuf[13] << 24));
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH_REG,
+		    aviibuf[14] | (aviibuf[15] << 8) | (aviibuf[16] << 16));
+		
+		HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_HDMI_GENERIC_CTRL_REG,
+		    HDMI_NV_PDISP_HDMI_GENERIC_CTRL_AUDIO);
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL_REG,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL_ENABLE);
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_REG,
+		    HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_ENABLE);
+		HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_HDMI_ACR_CTRL_REG, 0);
+	} else {
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_GENERIC_CTRL_REG, 0);
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL_REG, 0);
+		HDMI_WRITE(tegra_encoder,
+		    HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_REG, 0);
+		HDMI_WRITE(tegra_encoder, HDMI_NV_PDISP_HDMI_ACR_CTRL_REG, 0);
+	}
+
+	/* Enable DC output to HDMI */
+	DC_SET_CLEAR(tegra_crtc, DC_DISP_DISP_WIN_OPTIONS_REG,
+	    DC_DISP_DISP_WIN_OPTIONS_HDMI_ENABLE, 0);
+
+	/* Commit settings */
+	DC_WRITE(tegra_crtc, DC_CMD_STATE_CONTROL_REG,
+	    DC_CMD_STATE_CONTROL_GENERAL_UPDATE |
+	    DC_CMD_STATE_CONTROL_WIN_A_UPDATE);
+	DC_WRITE(tegra_crtc, DC_CMD_STATE_CONTROL_REG,
+	    DC_CMD_STATE_CONTROL_GENERAL_ACT_REQ |
+	    DC_CMD_STATE_CONTROL_WIN_A_ACT_REQ);
+}
+
+static void
+tegra_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void
+tegra_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static int
+tegra_connector_init(struct drm_device *ddev, struct drm_encoder *encoder)
+{
+	struct tegra_drm_softc * const sc = tegra_drm_private(ddev);
+	struct tegra_connector *connector;
+
+	connector = kmem_zalloc(sizeof(*connector), KM_SLEEP);
+	if (connector == NULL)
+		return -ENOMEM;
+
+	drm_connector_init(ddev, &connector->base, &tegra_connector_funcs,
+	    DRM_MODE_CONNECTOR_HDMIA);
+	drm_connector_helper_add(&connector->base,
+	    &tegra_connector_helper_funcs);
+
+	connector->base.interlace_allowed = 0;
+	connector->base.doublescan_allowed = 0;
+
+	drm_sysfs_connector_add(&connector->base);
+
+	connector->base.polled =
+	    DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
+
+	drm_mode_connector_attach_encoder(&connector->base, encoder);
+
+	connector->hpd = sc->sc_pin_hpd;
+	if (!connector->hpd)
+		DRM_ERROR("failed to find hpd pin for connector\n");
+
+	connector->ddcdev = sc->sc_ddcdev;
+	if (!connector->ddcdev)
+		DRM_ERROR("failed to find ddc device for connector\n");
+
+	return 0;
+}
+
+static void
+tegra_connector_destroy(struct drm_connector *connector)
+{
+	struct tegra_connector *tegra_connector = to_tegra_connector(connector);
+
+	drm_sysfs_connector_remove(connector);
+	drm_connector_cleanup(connector);
+	kmem_free(tegra_connector, sizeof(*tegra_connector));
+}
+
+static enum drm_connector_status
+tegra_connector_detect(struct drm_connector *connector, bool force)
+{
+	struct tegra_connector *tegra_connector = to_tegra_connector(connector);
+	bool con;
+
+
+	if (!tegra_connector->hpd) {
+		tegra_connector->has_hdmi_sink = false;
+		tegra_connector->has_audio = false;
+		return connector_status_connected;
+	}
+
+	con = tegra_gpio_read(tegra_connector->hpd);
+	if (con) {
+		return connector_status_connected;
+	} else {
+		tegra_connector->has_hdmi_sink = false;
+		tegra_connector->has_audio = false;
+		return connector_status_disconnected;
+	}
+}
+
+static int
+tegra_connector_mode_valid(struct drm_connector *connector,
+    struct drm_display_mode *mode)
+{
+	return MODE_OK;
+}
+
+static int
+tegra_connector_get_modes(struct drm_connector *connector)
+{
+	struct tegra_connector *tegra_connector = to_tegra_connector(connector);
+	struct tegra_drm_softc * const sc = tegra_drm_private(connector->dev);
+	char edid[EDID_LENGTH * 4];
+	struct edid *pedid = NULL;
+	int error, block;
+
+	if (tegra_connector->ddcdev) {
+		memset(edid, 0, sizeof(edid));
+		for (block = 0; block < 4; block++) {
+			error = ddc_dev_read_edid_block(tegra_connector->ddcdev,
+			    &edid[block * EDID_LENGTH], EDID_LENGTH, block);
+			if (error)
+				break;
+			if (block == 0) {
+				pedid = (struct edid *)edid;
+				if (edid[0x7e] == 0)
+					break;
+			}
+		}
+	}
+
+	if (pedid) {
+		if (sc->sc_force_dvi) {
+			tegra_connector->has_hdmi_sink = false;
+			tegra_connector->has_audio = false;
+		} else {
+			tegra_connector->has_hdmi_sink =
+			    drm_detect_hdmi_monitor(pedid);
+			tegra_connector->has_audio =
+			    drm_detect_monitor_audio(pedid);
+		}
+		drm_mode_connector_update_edid_property(connector, pedid);
+		return drm_add_edid_modes(connector, pedid);
+		    
+	} else {
+		drm_mode_connector_update_edid_property(connector, NULL);
+		return 0;
+	}
+}
+
+static struct drm_encoder *
+tegra_connector_best_encoder(struct drm_connector *connector)
+{
+	int enc_id = connector->encoder_ids[0];
+	struct drm_mode_object *obj;
+	struct drm_encoder *encoder = NULL;
+
+	if (enc_id) {
+		obj = drm_mode_object_find(connector->dev, enc_id,
+		    DRM_MODE_OBJECT_ENCODER);
+		if (obj == NULL)
+			return NULL;
+		encoder = obj_to_encoder(obj);
+	}
+
+	return encoder;
+}
Index: src/sys/arch/arm/nvidia/tegra_fb.c
diff -u /dev/null src/sys/arch/arm/nvidia/tegra_fb.c:1.1
--- /dev/null	Mon Nov  9 23:05:58 2015
+++ src/sys/arch/arm/nvidia/tegra_fb.c	Mon Nov  9 23:05:58 2015
@@ -0,0 +1,150 @@
+/* $NetBSD: tegra_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2015 Jared D. McNeill <jmcne...@invisible.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: tegra_fb.c,v 1.1 2015/11/09 23:05:58 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/device.h>
+
+#include <drm/drmP.h>
+#include <drm/drmfb.h>
+
+#include <arm/nvidia/tegra_var.h>
+#include <arm/nvidia/tegra_drm.h>
+
+static int	tegra_fb_match(device_t, cfdata_t, void *);
+static void	tegra_fb_attach(device_t, device_t, void *);
+
+static bool	tegra_fb_shutdown(device_t, int);
+
+struct tegra_fb_softc {
+	struct drmfb_softc	sc_drmfb;
+	device_t		sc_dev;
+	struct tegra_drm_softc	*sc_drm;
+	struct tegra_drmfb_attach_args sc_tfa;
+};
+
+static paddr_t	tegra_fb_mmapfb(struct drmfb_softc *, off_t, int);
+static int	tegra_fb_ioctl(struct drmfb_softc *, u_long, void *, int,
+			       lwp_t *);
+
+
+static const struct drmfb_params tegrafb_drmfb_params = {
+	.dp_mmapfb = tegra_fb_mmapfb,
+	.dp_ioctl = tegra_fb_ioctl,
+	
+};
+
+CFATTACH_DECL_NEW(tegra_fb, sizeof(struct tegra_fb_softc),
+	tegra_fb_match, tegra_fb_attach, NULL, NULL);
+
+static int
+tegra_fb_match(device_t parent, cfdata_t cf, void *aux)
+{
+	return 1;
+}
+
+static void
+tegra_fb_attach(device_t parent, device_t self, void *aux)
+{
+	struct tegra_fb_softc * const sc = device_private(self);
+	struct tegra_drm_softc * const drmsc = device_private(parent);
+	struct tegra_drmfb_attach_args * const tfa = aux;
+	struct drmfb_attach_args da;
+	int error;
+
+	sc->sc_dev = self;
+	sc->sc_drm = drmsc;
+	sc->sc_tfa = *tfa;
+
+	aprint_naive("\n");
+	aprint_normal("\n");
+
+	da.da_dev = self;
+	da.da_fb_helper = tfa->tfa_fb_helper;
+	da.da_fb_sizes = &tfa->tfa_fb_sizes;
+	da.da_fb_vaddr = drmsc->sc_dmap;
+	da.da_params = &tegrafb_drmfb_params;
+
+	error = drmfb_attach(&sc->sc_drmfb, &da);
+	if (error) {
+		aprint_error_dev(self, "failed to attach drmfb: %d\n", error);
+		return;
+	}
+
+	pmf_device_register1(self, NULL, NULL, tegra_fb_shutdown);
+}
+
+static bool
+tegra_fb_shutdown(device_t self, int flags)
+{
+	struct tegra_fb_softc * const sc = device_private(self);
+
+	return drmfb_shutdown(&sc->sc_drmfb, flags);
+}
+
+static paddr_t
+tegra_fb_mmapfb(struct drmfb_softc *sc, off_t off, int prot)
+{
+	struct tegra_fb_softc * const tfb_sc = (struct tegra_fb_softc *)sc;
+
+	KASSERT(off >= 0);
+	KASSERT(off < tfb_sc->sc_drm->sc_dmasize);
+
+	return bus_dmamem_mmap(tfb_sc->sc_drm->sc_dmat,
+	    tfb_sc->sc_drm->sc_dmasegs, 1, off, prot, BUS_DMA_PREFETCHABLE);
+}
+
+static int
+tegra_fb_ioctl(struct drmfb_softc *sc, u_long cmd, void *data, int flag,
+    lwp_t *l)
+{
+	struct wsdisplayio_bus_id *busid;
+	struct wsdisplayio_fbinfo *fbi;
+	struct rasops_info *ri = &sc->sc_genfb.vd.active->scr_ri;
+	int error;
+
+	switch (cmd) {
+	case WSDISPLAYIO_GET_BUSID:
+		busid = data;
+		busid->bus_type = WSDISPLAYIO_BUS_SOC;
+		return 0;
+	case WSDISPLAYIO_GTYPE:
+		*(u_int *)data = WSDISPLAY_TYPE_TEGRA;
+		return 0;
+	case WSDISPLAYIO_GET_FBINFO:
+		fbi = data;
+		error = wsdisplayio_get_fbinfo(ri, fbi);
+		fbi->fbi_flags |= WSFB_VRAM_IS_RAM;
+		return error;
+	default:
+		return EPASSTHROUGH;
+	}
+}

Reply via email to