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; + } +}