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 <[email protected]>
@@ -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 <[email protected]>
@@ -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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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 <[email protected]>
+ * 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;
+ }
+}