1a0a23564SMichal Meloun /*- 2a0a23564SMichal Meloun * Copyright (c) 2015 Michal Meloun 3a0a23564SMichal Meloun * All rights reserved. 4a0a23564SMichal Meloun * 5a0a23564SMichal Meloun * Redistribution and use in source and binary forms, with or without 6a0a23564SMichal Meloun * modification, are permitted provided that the following conditions 7a0a23564SMichal Meloun * are met: 8a0a23564SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9a0a23564SMichal Meloun * notice, this list of conditions and the following disclaimer. 10a0a23564SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11a0a23564SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12a0a23564SMichal Meloun * documentation and/or other materials provided with the distribution. 13a0a23564SMichal Meloun * 14a0a23564SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15a0a23564SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16a0a23564SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17a0a23564SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18a0a23564SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19a0a23564SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20a0a23564SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21a0a23564SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22a0a23564SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23a0a23564SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24a0a23564SMichal Meloun * SUCH DAMAGE. 25a0a23564SMichal Meloun */ 26a0a23564SMichal Meloun 27a0a23564SMichal Meloun #include <sys/param.h> 28a0a23564SMichal Meloun #include <sys/systm.h> 29a0a23564SMichal Meloun #include <sys/bus.h> 30a0a23564SMichal Meloun #include <sys/gpio.h> 31a0a23564SMichal Meloun #include <sys/kernel.h> 32a0a23564SMichal Meloun #include <sys/module.h> 33a0a23564SMichal Meloun #include <sys/malloc.h> 34a0a23564SMichal Meloun #include <sys/rman.h> 35a0a23564SMichal Meloun #include <sys/sysctl.h> 36a0a23564SMichal Meloun 37a0a23564SMichal Meloun #include <machine/bus.h> 38a0a23564SMichal Meloun 39be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h> 401f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h> 41b2f0caf1SEmmanuel Vadot #include <dev/regulator/regulator.h> 42a0a23564SMichal Meloun #include <dev/drm2/drmP.h> 43a0a23564SMichal Meloun #include <dev/drm2/drm_crtc.h> 44a0a23564SMichal Meloun #include <dev/drm2/drm_crtc_helper.h> 45a0a23564SMichal Meloun #include <dev/drm2/drm_fb_helper.h> 46a0a23564SMichal Meloun #include <dev/gpio/gpiobusvar.h> 47a0a23564SMichal Meloun #include <dev/ofw/ofw_bus.h> 48a0a23564SMichal Meloun #include <dev/ofw/ofw_bus_subr.h> 49a0a23564SMichal Meloun 50a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_drm.h> 51a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_hdmi_reg.h> 52a0a23564SMichal Meloun #include <arm/nvidia/drm2/tegra_dc_reg.h> 53a0a23564SMichal Meloun #include <arm/nvidia/drm2/hdmi.h> 54a0a23564SMichal Meloun 55a0a23564SMichal Meloun #include "tegra_dc_if.h" 56a0a23564SMichal Meloun #include "tegra_drm_if.h" 57a0a23564SMichal Meloun 58a0a23564SMichal Meloun #define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, 4 * (_r), (_v)) 59a0a23564SMichal Meloun #define RD4(_sc, _r) bus_read_4((_sc)->mem_res, 4 * (_r)) 60a0a23564SMichal Meloun 61a0a23564SMichal Meloun /* HDA stream format verb. */ 62a0a23564SMichal Meloun #define AC_FMT_CHAN_GET(x) (((x) >> 0) & 0xf) 63a0a23564SMichal Meloun #define AC_FMT_CHAN_BITS_GET(x) (((x) >> 4) & 0x7) 64a0a23564SMichal Meloun #define AC_FMT_DIV_GET(x) (((x) >> 8) & 0x7) 65a0a23564SMichal Meloun #define AC_FMT_MUL_GET(x) (((x) >> 11) & 0x7) 66a0a23564SMichal Meloun #define AC_FMT_BASE_44K (1 << 14) 67a0a23564SMichal Meloun #define AC_FMT_TYPE_NON_PCM (1 << 15) 68a0a23564SMichal Meloun 69a0a23564SMichal Meloun #define HDMI_REKEY_DEFAULT 56 70a0a23564SMichal Meloun #define HDMI_ELD_BUFFER_SIZE 96 71a0a23564SMichal Meloun 72a0a23564SMichal Meloun #define HDMI_DC_CLOCK_MULTIPIER 2 73a0a23564SMichal Meloun 74a0a23564SMichal Meloun struct audio_reg { 75a0a23564SMichal Meloun uint32_t audio_clk; 76a0a23564SMichal Meloun bus_size_t acr_reg; 77a0a23564SMichal Meloun bus_size_t nval_reg; 78a0a23564SMichal Meloun bus_size_t aval_reg; 79a0a23564SMichal Meloun }; 80a0a23564SMichal Meloun 81a0a23564SMichal Meloun static const struct audio_reg audio_regs[] = 82a0a23564SMichal Meloun { 83a0a23564SMichal Meloun { 84a0a23564SMichal Meloun .audio_clk = 32000, 85a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0320_SUBPACK_LOW, 86a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0320, 87a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0320, 88a0a23564SMichal Meloun }, 89a0a23564SMichal Meloun { 90a0a23564SMichal Meloun .audio_clk = 44100, 91a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW, 92a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0441, 93a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0441, 94a0a23564SMichal Meloun }, 95a0a23564SMichal Meloun { 96a0a23564SMichal Meloun .audio_clk = 88200, 97a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0882_SUBPACK_LOW, 98a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0882, 99a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0882, 100a0a23564SMichal Meloun }, 101a0a23564SMichal Meloun { 102a0a23564SMichal Meloun .audio_clk = 176400, 103a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1764_SUBPACK_LOW, 104a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1764, 105a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1764, 106a0a23564SMichal Meloun }, 107a0a23564SMichal Meloun { 108a0a23564SMichal Meloun .audio_clk = 48000, 109a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0480_SUBPACK_LOW, 110a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0480, 111a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480, 112a0a23564SMichal Meloun }, 113a0a23564SMichal Meloun { 114a0a23564SMichal Meloun .audio_clk = 96000, 115a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_0960_SUBPACK_LOW, 116a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_0960, 117a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_0960, 118a0a23564SMichal Meloun }, 119a0a23564SMichal Meloun { 120a0a23564SMichal Meloun .audio_clk = 192000, 121a0a23564SMichal Meloun .acr_reg = HDMI_NV_PDISP_HDMI_ACR_1920_SUBPACK_LOW, 122a0a23564SMichal Meloun .nval_reg = HDMI_NV_PDISP_SOR_AUDIO_NVAL_1920, 123a0a23564SMichal Meloun .aval_reg = HDMI_NV_PDISP_SOR_AUDIO_AVAL_1920, 124a0a23564SMichal Meloun }, 125a0a23564SMichal Meloun }; 126a0a23564SMichal Meloun 127a0a23564SMichal Meloun struct tmds_config { 128a0a23564SMichal Meloun uint32_t pclk; 129a0a23564SMichal Meloun uint32_t pll0; 130a0a23564SMichal Meloun uint32_t pll1; 131a0a23564SMichal Meloun uint32_t drive_c; 132a0a23564SMichal Meloun uint32_t pe_c; 133a0a23564SMichal Meloun uint32_t peak_c; 134a0a23564SMichal Meloun uint32_t pad_ctls; 135a0a23564SMichal Meloun }; 136a0a23564SMichal Meloun 137a0a23564SMichal Meloun static const struct tmds_config tegra124_tmds_config[] = 138a0a23564SMichal Meloun { 139a0a23564SMichal Meloun { /* 480p/576p / 25.2MHz/27MHz */ 140a0a23564SMichal Meloun .pclk = 27000000, 141a0a23564SMichal Meloun .pll0 = 0x01003010, 142a0a23564SMichal Meloun .pll1 = 0x00301B00, 143a0a23564SMichal Meloun .drive_c = 0x1F1F1F1F, 144a0a23564SMichal Meloun .pe_c = 0x00000000, 145a0a23564SMichal Meloun .peak_c = 0x03030303, 146a0a23564SMichal Meloun .pad_ctls = 0x800034BB, 147a0a23564SMichal Meloun }, 148a0a23564SMichal Meloun { /* 720p/1080i / 74.25MHz */ 149a0a23564SMichal Meloun .pclk = 74250000, 150a0a23564SMichal Meloun .pll0 = 0x01003110, 151a0a23564SMichal Meloun .pll1 = 0x00301500, 152a0a23564SMichal Meloun .drive_c = 0x2C2C2C2C, 153a0a23564SMichal Meloun .pe_c = 0x00000000, 154a0a23564SMichal Meloun .peak_c = 0x07070707, 155a0a23564SMichal Meloun .pad_ctls = 0x800034BB, 156a0a23564SMichal Meloun }, 157a0a23564SMichal Meloun { /* 1080p / 148.5MHz */ 158a0a23564SMichal Meloun .pclk = 148500000, 159a0a23564SMichal Meloun .pll0 = 0x01003310, 160a0a23564SMichal Meloun .pll1 = 0x00301500, 161a0a23564SMichal Meloun .drive_c = 0x33333333, 162a0a23564SMichal Meloun .pe_c = 0x00000000, 163a0a23564SMichal Meloun .peak_c = 0x0C0C0C0C, 164a0a23564SMichal Meloun .pad_ctls = 0x800034BB, 165a0a23564SMichal Meloun }, 166a0a23564SMichal Meloun { /* 2216p / 297MHz */ 167a0a23564SMichal Meloun .pclk = UINT_MAX, 168a0a23564SMichal Meloun .pll0 = 0x01003F10, 169a0a23564SMichal Meloun .pll1 = 0x00300F00, 170a0a23564SMichal Meloun .drive_c = 0x37373737, 171a0a23564SMichal Meloun .pe_c = 0x00000000, 172a0a23564SMichal Meloun .peak_c = 0x17171717, 173a0a23564SMichal Meloun .pad_ctls = 0x800036BB, 174a0a23564SMichal Meloun }, 175a0a23564SMichal Meloun }; 176a0a23564SMichal Meloun 177a0a23564SMichal Meloun struct hdmi_softc { 178a0a23564SMichal Meloun device_t dev; 179a0a23564SMichal Meloun struct resource *mem_res; 180a0a23564SMichal Meloun struct resource *irq_res; 181a0a23564SMichal Meloun void *irq_ih; 182a0a23564SMichal Meloun 183a0a23564SMichal Meloun clk_t clk_parent; 184a0a23564SMichal Meloun clk_t clk_hdmi; 185a0a23564SMichal Meloun hwreset_t hwreset_hdmi; 186a0a23564SMichal Meloun regulator_t supply_hdmi; 187a0a23564SMichal Meloun regulator_t supply_pll; 188a0a23564SMichal Meloun regulator_t supply_vdd; 189a0a23564SMichal Meloun 190a0a23564SMichal Meloun uint64_t pclk; 191a0a23564SMichal Meloun boolean_t hdmi_mode; 192a0a23564SMichal Meloun 193a0a23564SMichal Meloun int audio_src_type; 194a0a23564SMichal Meloun int audio_freq; 195a0a23564SMichal Meloun int audio_chans; 196a0a23564SMichal Meloun 197a0a23564SMichal Meloun struct tegra_drm *drm; 198a0a23564SMichal Meloun struct tegra_drm_encoder output; 199a0a23564SMichal Meloun 200a0a23564SMichal Meloun const struct tmds_config *tmds_config; 201a0a23564SMichal Meloun int n_tmds_configs; 202a0a23564SMichal Meloun }; 203a0a23564SMichal Meloun 204a0a23564SMichal Meloun static struct ofw_compat_data compat_data[] = { 205a0a23564SMichal Meloun {"nvidia,tegra124-hdmi", 1}, 206a0a23564SMichal Meloun {NULL, 0}, 207a0a23564SMichal Meloun }; 208a0a23564SMichal Meloun 209a0a23564SMichal Meloun /* These functions have been copied from newer version of drm_edid.c */ 210a0a23564SMichal Meloun /* ELD Header Block */ 211a0a23564SMichal Meloun #define DRM_ELD_HEADER_BLOCK_SIZE 4 212a0a23564SMichal Meloun #define DRM_ELD_BASELINE_ELD_LEN 2 /* in dwords! */ 213a0a23564SMichal Meloun static int drm_eld_size(const uint8_t *eld) 214a0a23564SMichal Meloun { 215a0a23564SMichal Meloun return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4; 216a0a23564SMichal Meloun } 217a0a23564SMichal Meloun 218a0a23564SMichal Meloun static int 219a0a23564SMichal Meloun drm_hdmi_avi_infoframe_from_display_mode(struct hdmi_avi_infoframe *frame, 220a0a23564SMichal Meloun struct drm_display_mode *mode) 221a0a23564SMichal Meloun { 222a0a23564SMichal Meloun int rv; 223a0a23564SMichal Meloun 224a0a23564SMichal Meloun if (!frame || !mode) 225a0a23564SMichal Meloun return -EINVAL; 226a0a23564SMichal Meloun 227a0a23564SMichal Meloun rv = hdmi_avi_infoframe_init(frame); 228a0a23564SMichal Meloun if (rv < 0) 229a0a23564SMichal Meloun return rv; 230a0a23564SMichal Meloun 231a0a23564SMichal Meloun if (mode->flags & DRM_MODE_FLAG_DBLCLK) 232a0a23564SMichal Meloun frame->pixel_repeat = 1; 233a0a23564SMichal Meloun 234a0a23564SMichal Meloun frame->video_code = drm_match_cea_mode(mode); 235a0a23564SMichal Meloun 236a0a23564SMichal Meloun frame->picture_aspect = HDMI_PICTURE_ASPECT_NONE; 237a0a23564SMichal Meloun #ifdef FREEBSD_NOTYET 238a0a23564SMichal Meloun /* 239a0a23564SMichal Meloun * Populate picture aspect ratio from either 240a0a23564SMichal Meloun * user input (if specified) or from the CEA mode list. 241a0a23564SMichal Meloun */ 242a0a23564SMichal Meloun if (mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_4_3 || 243a0a23564SMichal Meloun mode->picture_aspect_ratio == HDMI_PICTURE_ASPECT_16_9) 244a0a23564SMichal Meloun frame->picture_aspect = mode->picture_aspect_ratio; 245a0a23564SMichal Meloun else if (frame->video_code > 0) 246a0a23564SMichal Meloun frame->picture_aspect = drm_get_cea_aspect_ratio( 247a0a23564SMichal Meloun frame->video_code); 248a0a23564SMichal Meloun #endif 249a0a23564SMichal Meloun 250a0a23564SMichal Meloun frame->active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; 251a0a23564SMichal Meloun frame->scan_mode = HDMI_SCAN_MODE_UNDERSCAN; 252a0a23564SMichal Meloun 253a0a23564SMichal Meloun return 0; 254a0a23564SMichal Meloun } 255a0a23564SMichal Meloun /* --------------------------------------------------------------------- */ 256a0a23564SMichal Meloun 257a0a23564SMichal Meloun static int 258a0a23564SMichal Meloun hdmi_setup_clock(struct tegra_drm_encoder *output, clk_t clk, uint64_t pclk) 259a0a23564SMichal Meloun { 260a0a23564SMichal Meloun struct hdmi_softc *sc; 261a0a23564SMichal Meloun uint64_t freq; 262a0a23564SMichal Meloun int rv; 263a0a23564SMichal Meloun 264a0a23564SMichal Meloun sc = device_get_softc(output->dev); 265a0a23564SMichal Meloun 266a0a23564SMichal Meloun /* Disable consumers clock for while. */ 267a0a23564SMichal Meloun rv = clk_disable(sc->clk_hdmi); 268a0a23564SMichal Meloun if (rv != 0) { 269a0a23564SMichal Meloun device_printf(sc->dev, "Cannot disable 'hdmi' clock\n"); 270a0a23564SMichal Meloun return (rv); 271a0a23564SMichal Meloun } 272a0a23564SMichal Meloun rv = clk_disable(clk); 273a0a23564SMichal Meloun if (rv != 0) { 274a0a23564SMichal Meloun device_printf(sc->dev, "Cannot disable display clock\n"); 275a0a23564SMichal Meloun return (rv); 276a0a23564SMichal Meloun } 277a0a23564SMichal Meloun 278a0a23564SMichal Meloun /* Set frequency for Display Controller PLL. */ 279a0a23564SMichal Meloun freq = HDMI_DC_CLOCK_MULTIPIER * pclk; 280a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_parent, freq, 0); 281a0a23564SMichal Meloun if (rv != 0) { 282a0a23564SMichal Meloun device_printf(output->dev, 283a0a23564SMichal Meloun "Cannot set display pixel frequency\n"); 284a0a23564SMichal Meloun return (rv); 285a0a23564SMichal Meloun } 286a0a23564SMichal Meloun 287a0a23564SMichal Meloun /* Reparent display controller */ 288a0a23564SMichal Meloun rv = clk_set_parent_by_clk(clk, sc->clk_parent); 289a0a23564SMichal Meloun if (rv != 0) { 290a0a23564SMichal Meloun device_printf(output->dev, "Cannot set parent clock\n"); 291a0a23564SMichal Meloun return (rv); 292a0a23564SMichal Meloun } 293a0a23564SMichal Meloun rv = clk_set_freq(clk, freq, 0); 294a0a23564SMichal Meloun if (rv != 0) { 295a0a23564SMichal Meloun device_printf(output->dev, 296a0a23564SMichal Meloun "Cannot set display controller frequency\n"); 297a0a23564SMichal Meloun return (rv); 298a0a23564SMichal Meloun } 299a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_hdmi, pclk, 0); 300a0a23564SMichal Meloun if (rv != 0) { 301a0a23564SMichal Meloun device_printf(output->dev, 302a0a23564SMichal Meloun "Cannot set display controller frequency\n"); 303a0a23564SMichal Meloun return (rv); 304a0a23564SMichal Meloun } 305a0a23564SMichal Meloun 306a0a23564SMichal Meloun /* And reenable consumers clock. */ 307a0a23564SMichal Meloun rv = clk_enable(clk); 308a0a23564SMichal Meloun if (rv != 0) { 309a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable display clock\n"); 310a0a23564SMichal Meloun return (rv); 311a0a23564SMichal Meloun } 312a0a23564SMichal Meloun rv = clk_enable(sc->clk_hdmi); 313a0a23564SMichal Meloun if (rv != 0) { 314a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'hdmi' clock\n"); 315a0a23564SMichal Meloun return (rv); 316a0a23564SMichal Meloun } 317a0a23564SMichal Meloun 318a0a23564SMichal Meloun rv = clk_get_freq(clk, &freq); 319a0a23564SMichal Meloun if (rv != 0) { 320a0a23564SMichal Meloun device_printf(output->dev, 321a0a23564SMichal Meloun "Cannot get display controller frequency\n"); 322a0a23564SMichal Meloun return (rv); 323a0a23564SMichal Meloun } 324a0a23564SMichal Meloun 325a0a23564SMichal Meloun DRM_DEBUG_KMS("DC frequency: %llu\n", freq); 326a0a23564SMichal Meloun 327a0a23564SMichal Meloun return (0); 328a0a23564SMichal Meloun } 329a0a23564SMichal Meloun 330a0a23564SMichal Meloun /* ------------------------------------------------------------------- 331a0a23564SMichal Meloun * 332a0a23564SMichal Meloun * Infoframes. 333a0a23564SMichal Meloun * 334a0a23564SMichal Meloun */ 335a0a23564SMichal Meloun static void 336a0a23564SMichal Meloun avi_setup_infoframe(struct hdmi_softc *sc, struct drm_display_mode *mode) 337a0a23564SMichal Meloun { 338a0a23564SMichal Meloun struct hdmi_avi_infoframe frame; 339aeb665b5SEd Maste uint8_t buf[17], *hdr, *pb; 340a0a23564SMichal Meloun ssize_t rv; 341a0a23564SMichal Meloun 342a0a23564SMichal Meloun rv = drm_hdmi_avi_infoframe_from_display_mode(&frame, mode); 343a0a23564SMichal Meloun if (rv < 0) { 344a0a23564SMichal Meloun device_printf(sc->dev, "Cannot setup AVI infoframe: %zd\n", rv); 345a0a23564SMichal Meloun return; 346a0a23564SMichal Meloun } 347a0a23564SMichal Meloun rv = hdmi_avi_infoframe_pack(&frame, buf, sizeof(buf)); 348a0a23564SMichal Meloun if (rv < 0) { 349a0a23564SMichal Meloun device_printf(sc->dev, "Cannot pack AVI infoframe: %zd\n", rv); 350a0a23564SMichal Meloun return; 351a0a23564SMichal Meloun } 352a0a23564SMichal Meloun hdr = buf + 0; 353a0a23564SMichal Meloun pb = buf + 3; 354a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_HEADER, 355a0a23564SMichal Meloun (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0)); 356a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_LOW, 357a0a23564SMichal Meloun (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0)); 358a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK0_HIGH, 359a0a23564SMichal Meloun (pb[6] << 16) | (pb[5] << 8) | (pb[4] << 0)); 360a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_LOW, 361a0a23564SMichal Meloun (pb[10] << 24) |(pb[9] << 16) | (pb[8] << 8) | (pb[7] << 0)); 362a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_SUBPACK1_HIGH, 363a0a23564SMichal Meloun (pb[13] << 16) | (pb[12] << 8) | (pb[11] << 0)); 364a0a23564SMichal Meloun 365a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, 366a0a23564SMichal Meloun AVI_INFOFRAME_CTRL_ENABLE); 367a0a23564SMichal Meloun } 368a0a23564SMichal Meloun 369a0a23564SMichal Meloun static void 370a0a23564SMichal Meloun audio_setup_infoframe(struct hdmi_softc *sc) 371a0a23564SMichal Meloun { 372a0a23564SMichal Meloun struct hdmi_audio_infoframe frame; 373a0a23564SMichal Meloun uint8_t buf[14], *hdr, *pb; 374a0a23564SMichal Meloun ssize_t rv; 375a0a23564SMichal Meloun 376a0a23564SMichal Meloun rv = hdmi_audio_infoframe_init(&frame); 377a0a23564SMichal Meloun frame.channels = sc->audio_chans; 378a0a23564SMichal Meloun rv = hdmi_audio_infoframe_pack(&frame, buf, sizeof(buf)); 379a0a23564SMichal Meloun if (rv < 0) { 380a0a23564SMichal Meloun device_printf(sc->dev, "Cannot pack audio infoframe\n"); 381a0a23564SMichal Meloun return; 382a0a23564SMichal Meloun } 383a0a23564SMichal Meloun hdr = buf + 0; 384a0a23564SMichal Meloun pb = buf + 3; 385a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_HEADER, 386a0a23564SMichal Meloun (hdr[2] << 16) | (hdr[1] << 8) | (hdr[0] << 0)); 387a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_LOW, 388a0a23564SMichal Meloun (pb[3] << 24) |(pb[2] << 16) | (pb[1] << 8) | (pb[0] << 0)); 389a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_SUBPACK0_HIGH, 390a0a23564SMichal Meloun (pb[5] << 8) | (pb[4] << 0)); 391a0a23564SMichal Meloun 392a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, 393a0a23564SMichal Meloun AUDIO_INFOFRAME_CTRL_ENABLE); 394a0a23564SMichal Meloun } 395a0a23564SMichal Meloun 396a0a23564SMichal Meloun /* ------------------------------------------------------------------- 397a0a23564SMichal Meloun * 398a0a23564SMichal Meloun * Audio 399a0a23564SMichal Meloun * 400a0a23564SMichal Meloun */ 401a0a23564SMichal Meloun static void 402a0a23564SMichal Meloun init_hda_eld(struct hdmi_softc *sc) 403a0a23564SMichal Meloun { 404a0a23564SMichal Meloun size_t size; 405a0a23564SMichal Meloun int i ; 406a0a23564SMichal Meloun uint32_t val; 407a0a23564SMichal Meloun 408a0a23564SMichal Meloun size = drm_eld_size(sc->output.connector.eld); 409a0a23564SMichal Meloun for (i = 0; i < HDMI_ELD_BUFFER_SIZE; i++) { 410a0a23564SMichal Meloun val = i << 8; 411a0a23564SMichal Meloun if (i < size) 412a0a23564SMichal Meloun val |= sc->output.connector.eld[i]; 413a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR, val); 414a0a23564SMichal Meloun } 415a0a23564SMichal Meloun WR4(sc,HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE, 416a0a23564SMichal Meloun SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT); 417a0a23564SMichal Meloun } 418a0a23564SMichal Meloun 419a0a23564SMichal Meloun static int 420a0a23564SMichal Meloun get_audio_regs(int freq, bus_size_t *acr_reg, bus_size_t *nval_reg, 421a0a23564SMichal Meloun bus_size_t *aval_reg) 422a0a23564SMichal Meloun { 423a0a23564SMichal Meloun int i; 424a0a23564SMichal Meloun const struct audio_reg *reg; 425a0a23564SMichal Meloun 426a0a23564SMichal Meloun for (i = 0; i < nitems(audio_regs) ; i++) { 427a0a23564SMichal Meloun reg = audio_regs + i; 428a0a23564SMichal Meloun if (reg->audio_clk == freq) { 429a0a23564SMichal Meloun if (acr_reg != NULL) 430a0a23564SMichal Meloun *acr_reg = reg->acr_reg; 431a0a23564SMichal Meloun if (nval_reg != NULL) 432a0a23564SMichal Meloun *nval_reg = reg->nval_reg; 433a0a23564SMichal Meloun if (aval_reg != NULL) 434a0a23564SMichal Meloun *aval_reg = reg->aval_reg; 435a0a23564SMichal Meloun return (0); 436a0a23564SMichal Meloun } 437a0a23564SMichal Meloun } 438a0a23564SMichal Meloun return (ERANGE); 439a0a23564SMichal Meloun } 440a0a23564SMichal Meloun 441a0a23564SMichal Meloun #define FR_BITS 16 442a0a23564SMichal Meloun #define TO_FFP(x) (((int64_t)(x)) << FR_BITS) 443a0a23564SMichal Meloun #define TO_INT(x) ((int)((x) >> FR_BITS)) 444a0a23564SMichal Meloun static int 445a0a23564SMichal Meloun get_hda_cts_n(uint32_t audio_freq_hz, uint32_t pixclk_freq_hz, 446a0a23564SMichal Meloun uint32_t *best_cts, uint32_t *best_n, uint32_t *best_a) 447a0a23564SMichal Meloun { 448a0a23564SMichal Meloun int min_n; 449a0a23564SMichal Meloun int max_n; 450a0a23564SMichal Meloun int ideal_n; 451a0a23564SMichal Meloun int n; 452a0a23564SMichal Meloun int cts; 453a0a23564SMichal Meloun int aval; 454a0a23564SMichal Meloun int64_t err_f; 455a0a23564SMichal Meloun int64_t min_err_f; 456a0a23564SMichal Meloun int64_t cts_f; 457a0a23564SMichal Meloun int64_t aval_f; 458a0a23564SMichal Meloun int64_t half_f; /* constant 0.5 */ 459a0a23564SMichal Meloun bool better_n; 460a0a23564SMichal Meloun 461a0a23564SMichal Meloun /* 462a0a23564SMichal Meloun * All floats are in fixed I48.16 format. 463a0a23564SMichal Meloun * 464a0a23564SMichal Meloun * Ideal ACR interval is 1000 hz (1 ms); 465a0a23564SMichal Meloun * acceptable is 300 hz .. 1500 hz 466a0a23564SMichal Meloun */ 467a0a23564SMichal Meloun min_n = 128 * audio_freq_hz / 1500; 468a0a23564SMichal Meloun max_n = 128 * audio_freq_hz / 300; 469a0a23564SMichal Meloun ideal_n = 128 * audio_freq_hz / 1000; 470a0a23564SMichal Meloun min_err_f = TO_FFP(100); 471a0a23564SMichal Meloun half_f = TO_FFP(1) / 2; 472a0a23564SMichal Meloun 473a0a23564SMichal Meloun *best_n = 0; 474a0a23564SMichal Meloun *best_cts = 0; 475a0a23564SMichal Meloun *best_a = 0; 476a0a23564SMichal Meloun 477a0a23564SMichal Meloun for (n = min_n; n <= max_n; n++) { 478a0a23564SMichal Meloun cts_f = TO_FFP(pixclk_freq_hz); 479a0a23564SMichal Meloun cts_f *= n; 480a0a23564SMichal Meloun cts_f /= 128 * audio_freq_hz; 481a0a23564SMichal Meloun cts = TO_INT(cts_f + half_f); /* round */ 482a0a23564SMichal Meloun err_f = cts_f - TO_FFP(cts); 483a0a23564SMichal Meloun if (err_f < 0) 484a0a23564SMichal Meloun err_f = -err_f; 485a0a23564SMichal Meloun aval_f = TO_FFP(24000000); 486a0a23564SMichal Meloun aval_f *= n; 487a0a23564SMichal Meloun aval_f /= 128 * audio_freq_hz; 488a0a23564SMichal Meloun aval = TO_INT(aval_f); /* truncate */ 489a0a23564SMichal Meloun 490a0a23564SMichal Meloun better_n = abs(n - ideal_n) < abs((int)(*best_n) - ideal_n); 491a0a23564SMichal Meloun if (TO_FFP(aval) == aval_f && 492a0a23564SMichal Meloun (err_f < min_err_f || (err_f == min_err_f && better_n))) { 493a0a23564SMichal Meloun min_err_f = err_f; 494a0a23564SMichal Meloun *best_n = (uint32_t)n; 495a0a23564SMichal Meloun *best_cts = (uint32_t)cts; 496a0a23564SMichal Meloun *best_a = (uint32_t)aval; 497a0a23564SMichal Meloun 498a0a23564SMichal Meloun if (err_f == 0 && n == ideal_n) 499a0a23564SMichal Meloun break; 500a0a23564SMichal Meloun } 501a0a23564SMichal Meloun } 502a0a23564SMichal Meloun return (0); 503a0a23564SMichal Meloun } 504a0a23564SMichal Meloun #undef FR_BITS 505a0a23564SMichal Meloun #undef TO_FFP 506a0a23564SMichal Meloun #undef TO_INT 507a0a23564SMichal Meloun 508a0a23564SMichal Meloun static int 509a0a23564SMichal Meloun audio_setup(struct hdmi_softc *sc) 510a0a23564SMichal Meloun { 511a0a23564SMichal Meloun uint32_t val; 512a0a23564SMichal Meloun uint32_t audio_n; 513a0a23564SMichal Meloun uint32_t audio_cts; 514a0a23564SMichal Meloun uint32_t audio_aval; 515a0a23564SMichal Meloun uint64_t hdmi_freq; 516a0a23564SMichal Meloun bus_size_t aval_reg; 517a0a23564SMichal Meloun int rv; 518a0a23564SMichal Meloun 519a0a23564SMichal Meloun if (!sc->hdmi_mode) 520a0a23564SMichal Meloun return (ENOTSUP); 521a0a23564SMichal Meloun rv = get_audio_regs(sc->audio_freq, NULL, NULL, &aval_reg); 522a0a23564SMichal Meloun if (rv != 0) { 523a0a23564SMichal Meloun device_printf(sc->dev, "Unsupported audio frequency.\n"); 524a0a23564SMichal Meloun return (rv); 525a0a23564SMichal Meloun } 526a0a23564SMichal Meloun 527a0a23564SMichal Meloun rv = clk_get_freq(sc->clk_hdmi, &hdmi_freq); 528a0a23564SMichal Meloun if (rv != 0) { 529a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get hdmi frequency: %d\n", rv); 530a0a23564SMichal Meloun return (rv); 531a0a23564SMichal Meloun } 532a0a23564SMichal Meloun 533a0a23564SMichal Meloun rv = get_hda_cts_n(sc->audio_freq, hdmi_freq, &audio_cts, &audio_n, 534a0a23564SMichal Meloun &audio_aval); 535a0a23564SMichal Meloun if (rv != 0) { 536a0a23564SMichal Meloun device_printf(sc->dev, "Cannot compute audio coefs: %d\n", rv); 537a0a23564SMichal Meloun return (rv); 538a0a23564SMichal Meloun } 539a0a23564SMichal Meloun 540a0a23564SMichal Meloun /* Audio infoframe. */ 541a0a23564SMichal Meloun audio_setup_infoframe(sc); 542a0a23564SMichal Meloun /* Setup audio source */ 543a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_CNTRL0, 544a0a23564SMichal Meloun SOR_AUDIO_CNTRL0_SOURCE_SELECT(sc->audio_src_type) | 545a0a23564SMichal Meloun SOR_AUDIO_CNTRL0_INJECT_NULLSMPL); 546a0a23564SMichal Meloun 547a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0); 548a0a23564SMichal Meloun val |= SOR_AUDIO_SPARE0_HBR_ENABLE; 549a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_AUDIO_SPARE0, val); 550a0a23564SMichal Meloun 551a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL, 0); 552a0a23564SMichal Meloun 553a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_AUDIO_N, 554a0a23564SMichal Meloun AUDIO_N_RESETF | 555a0a23564SMichal Meloun AUDIO_N_GENERATE_ALTERNATE | 556a0a23564SMichal Meloun AUDIO_N_VALUE(audio_n - 1)); 557a0a23564SMichal Meloun 558a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH, 559a0a23564SMichal Meloun ACR_SUBPACK_N(audio_n) | ACR_ENABLE); 560a0a23564SMichal Meloun 561a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW, 562a0a23564SMichal Meloun ACR_SUBPACK_CTS(audio_cts)); 563a0a23564SMichal Meloun 564a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_SPARE, 565a0a23564SMichal Meloun SPARE_HW_CTS | SPARE_FORCE_SW_CTS | SPARE_CTS_RESET_VAL(1)); 566a0a23564SMichal Meloun 567a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_AUDIO_N); 568a0a23564SMichal Meloun val &= ~AUDIO_N_RESETF; 569a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_AUDIO_N, val); 570a0a23564SMichal Meloun 571a0a23564SMichal Meloun WR4(sc, aval_reg, audio_aval); 572a0a23564SMichal Meloun 573a0a23564SMichal Meloun return (0); 574a0a23564SMichal Meloun } 575a0a23564SMichal Meloun 576a0a23564SMichal Meloun static void 577a0a23564SMichal Meloun audio_disable(struct hdmi_softc *sc) { 578a0a23564SMichal Meloun uint32_t val; 579a0a23564SMichal Meloun 580a0a23564SMichal Meloun /* Disable audio */ 581a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 582a0a23564SMichal Meloun val &= ~GENERIC_CTRL_AUDIO; 583a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val); 584a0a23564SMichal Meloun 585a0a23564SMichal Meloun /* Disable audio infoframes */ 586a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 587a0a23564SMichal Meloun val &= ~AUDIO_INFOFRAME_CTRL_ENABLE; 588a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val); 589a0a23564SMichal Meloun } 590a0a23564SMichal Meloun 591a0a23564SMichal Meloun static void 592a0a23564SMichal Meloun audio_enable(struct hdmi_softc *sc) { 593a0a23564SMichal Meloun uint32_t val; 594a0a23564SMichal Meloun 595a0a23564SMichal Meloun if (!sc->hdmi_mode) 596a0a23564SMichal Meloun audio_disable(sc); 597a0a23564SMichal Meloun 598a0a23564SMichal Meloun /* Enable audio infoframes */ 599a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL); 600a0a23564SMichal Meloun val |= AUDIO_INFOFRAME_CTRL_ENABLE; 601a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL, val); 602a0a23564SMichal Meloun 603a0a23564SMichal Meloun /* Enable audio */ 604a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); 605a0a23564SMichal Meloun val |= GENERIC_CTRL_AUDIO; 606a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL, val); 607a0a23564SMichal Meloun } 608a0a23564SMichal Meloun 609a0a23564SMichal Meloun /* ------------------------------------------------------------------- 610a0a23564SMichal Meloun * 611a0a23564SMichal Meloun * HDMI. 612a0a23564SMichal Meloun * 613a0a23564SMichal Meloun */ 614a0a23564SMichal Meloun /* Process format change notification from HDA */ 615a0a23564SMichal Meloun static void 616a0a23564SMichal Meloun hda_intr(struct hdmi_softc *sc) 617a0a23564SMichal Meloun { 618a0a23564SMichal Meloun uint32_t val; 619a0a23564SMichal Meloun int rv; 620a0a23564SMichal Meloun 621a0a23564SMichal Meloun if (!sc->hdmi_mode) 622a0a23564SMichal Meloun return; 623a0a23564SMichal Meloun 624a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_AUDIO_HDA_CODEC_SCRATCH0); 625a0a23564SMichal Meloun if ((val & (1 << 30)) == 0) { 626a0a23564SMichal Meloun audio_disable(sc); 627a0a23564SMichal Meloun return; 628a0a23564SMichal Meloun } 629a0a23564SMichal Meloun 630a0a23564SMichal Meloun /* XXX Move this to any header */ 631a0a23564SMichal Meloun /* Keep in sync with HDA */ 632a0a23564SMichal Meloun sc->audio_freq = val & 0x00FFFFFF; 633a0a23564SMichal Meloun sc->audio_chans = (val >> 24) & 0x0f; 634a0a23564SMichal Meloun DRM_DEBUG_KMS("%d channel(s) at %dHz\n", sc->audio_chans, 635a0a23564SMichal Meloun sc->audio_freq); 636a0a23564SMichal Meloun 637a0a23564SMichal Meloun rv = audio_setup(sc); 638a0a23564SMichal Meloun if (rv != 0) { 639a0a23564SMichal Meloun audio_disable(sc); 640a0a23564SMichal Meloun return; 641a0a23564SMichal Meloun } 642a0a23564SMichal Meloun 643a0a23564SMichal Meloun audio_enable(sc); 644a0a23564SMichal Meloun } 645a0a23564SMichal Meloun 646a0a23564SMichal Meloun static void 647a0a23564SMichal Meloun tmds_init(struct hdmi_softc *sc, const struct tmds_config *tmds) 648a0a23564SMichal Meloun { 649a0a23564SMichal Meloun 650a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, tmds->pll0); 651a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL1, tmds->pll1); 652a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_PE_CURRENT, tmds->pe_c); 653a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT, tmds->drive_c); 654a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT, tmds->peak_c); 655a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0, tmds->pad_ctls); 656a0a23564SMichal Meloun } 657a0a23564SMichal Meloun 658a0a23564SMichal Meloun static int 659a0a23564SMichal Meloun hdmi_sor_start(struct hdmi_softc *sc, struct drm_display_mode *mode) 660a0a23564SMichal Meloun { 661a0a23564SMichal Meloun int i; 662a0a23564SMichal Meloun uint32_t val; 663a0a23564SMichal Meloun 664a0a23564SMichal Meloun /* Enable TMDS macro */ 665a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); 666a0a23564SMichal Meloun val &= ~SOR_PLL0_PWR; 667a0a23564SMichal Meloun val &= ~SOR_PLL0_VCOPD; 668a0a23564SMichal Meloun val &= ~SOR_PLL0_PULLDOWN; 669a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); 670a0a23564SMichal Meloun DELAY(10); 671a0a23564SMichal Meloun 672a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); 673a0a23564SMichal Meloun val &= ~SOR_PLL0_PDBG; 674a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); 675a0a23564SMichal Meloun 676a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PWR, SOR_PWR_SETTING_NEW); 677a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PWR, 0); 678a0a23564SMichal Meloun 679a0a23564SMichal Meloun /* Wait until SOR is ready */ 680a0a23564SMichal Meloun for (i = 1000; i > 0; i--) { 681a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PWR); 682a0a23564SMichal Meloun if ((val & SOR_PWR_SETTING_NEW) == 0) 683a0a23564SMichal Meloun break; 684a0a23564SMichal Meloun DELAY(10); 685a0a23564SMichal Meloun } 686a0a23564SMichal Meloun if (i <= 0) { 687a0a23564SMichal Meloun device_printf(sc->dev, "Timeouted while enabling SOR power.\n"); 688a0a23564SMichal Meloun return (ETIMEDOUT); 689a0a23564SMichal Meloun } 690a0a23564SMichal Meloun 691a0a23564SMichal Meloun val = SOR_STATE2_ASY_OWNER(ASY_OWNER_HEAD0) | 692a0a23564SMichal Meloun SOR_STATE2_ASY_SUBOWNER(SUBOWNER_BOTH) | 693a0a23564SMichal Meloun SOR_STATE2_ASY_CRCMODE(ASY_CRCMODE_COMPLETE) | 694a0a23564SMichal Meloun SOR_STATE2_ASY_PROTOCOL(ASY_PROTOCOL_SINGLE_TMDS_A); 695a0a23564SMichal Meloun if (mode->flags & DRM_MODE_FLAG_NHSYNC) 696a0a23564SMichal Meloun val |= SOR_STATE2_ASY_HSYNCPOL_NEG; 697a0a23564SMichal Meloun if (mode->flags & DRM_MODE_FLAG_NVSYNC) 698a0a23564SMichal Meloun val |= SOR_STATE2_ASY_VSYNCPOL_NEG; 699a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE2, val); 700a0a23564SMichal Meloun 701a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE1, SOR_STATE1_ASY_ORMODE_NORMAL | 702a0a23564SMichal Meloun SOR_STATE1_ASY_HEAD_OPMODE(ASY_HEAD_OPMODE_AWAKE)); 703a0a23564SMichal Meloun 704a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0); 705a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE0, SOR_STATE0_UPDATE); 706a0a23564SMichal Meloun 707a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_STATE1); 708a0a23564SMichal Meloun val |= SOR_STATE1_ATTACHED; 709a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE1, val); 710a0a23564SMichal Meloun 711a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_STATE0, 0); 712a0a23564SMichal Meloun 713a0a23564SMichal Meloun return 0; 714a0a23564SMichal Meloun } 715a0a23564SMichal Meloun 716a0a23564SMichal Meloun static int 717a0a23564SMichal Meloun hdmi_disable(struct hdmi_softc *sc) 718a0a23564SMichal Meloun { 719a0a23564SMichal Meloun struct tegra_crtc *crtc; 720a0a23564SMichal Meloun device_t dc; 721a0a23564SMichal Meloun uint32_t val; 722a0a23564SMichal Meloun 723a0a23564SMichal Meloun dc = NULL; 724a0a23564SMichal Meloun if (sc->output.encoder.crtc != NULL) { 725a0a23564SMichal Meloun crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc, 726a0a23564SMichal Meloun drm_crtc); 727a0a23564SMichal Meloun dc = crtc->dev; 728a0a23564SMichal Meloun } 729a0a23564SMichal Meloun 730a0a23564SMichal Meloun if (dc != NULL) { 731a0a23564SMichal Meloun TEGRA_DC_HDMI_ENABLE(dc, false); 732a0a23564SMichal Meloun TEGRA_DC_DISPLAY_ENABLE(dc, false); 733a0a23564SMichal Meloun } 734a0a23564SMichal Meloun audio_disable(sc); 735a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL); 736a0a23564SMichal Meloun val &= ~AVI_INFOFRAME_CTRL_ENABLE; 737a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL, val); 738a0a23564SMichal Meloun 739a0a23564SMichal Meloun /* Disable interrupts */ 740a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_ENABLE, 0); 741a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_MASK, 0); 742a0a23564SMichal Meloun 743a0a23564SMichal Meloun return (0); 744a0a23564SMichal Meloun } 745a0a23564SMichal Meloun 746a0a23564SMichal Meloun static int 747a0a23564SMichal Meloun hdmi_enable(struct hdmi_softc *sc) 748a0a23564SMichal Meloun { 749a0a23564SMichal Meloun uint64_t freq; 750a0a23564SMichal Meloun struct drm_display_mode *mode; 751a0a23564SMichal Meloun struct tegra_crtc *crtc; 752a0a23564SMichal Meloun uint32_t val, h_sync_width, h_back_porch, h_front_porch, h_pulse_start; 753a0a23564SMichal Meloun uint32_t h_max_ac_packet, div8_2; 754a0a23564SMichal Meloun device_t dc; 755a0a23564SMichal Meloun int i, rv; 756a0a23564SMichal Meloun 757a0a23564SMichal Meloun mode = &sc->output.encoder.crtc->mode; 758a0a23564SMichal Meloun crtc = container_of(sc->output.encoder.crtc, struct tegra_crtc, 759a0a23564SMichal Meloun drm_crtc); 760a0a23564SMichal Meloun dc = crtc->dev; 761a0a23564SMichal Meloun 762a0a23564SMichal Meloun /* Compute all timings first. */ 763a0a23564SMichal Meloun sc->pclk = mode->clock * 1000; 764a0a23564SMichal Meloun h_sync_width = mode->hsync_end - mode->hsync_start; 765a0a23564SMichal Meloun h_back_porch = mode->htotal - mode->hsync_end; 766a0a23564SMichal Meloun h_front_porch = mode->hsync_start - mode->hdisplay; 767a0a23564SMichal Meloun h_pulse_start = 1 + h_sync_width + h_back_porch - 10; 768a0a23564SMichal Meloun h_max_ac_packet = (h_sync_width + h_back_porch + h_front_porch - 769a0a23564SMichal Meloun HDMI_REKEY_DEFAULT - 18) / 32; 770a0a23564SMichal Meloun 771a0a23564SMichal Meloun /* Check if HDMI device is connected and detected. */ 772a0a23564SMichal Meloun if (sc->output.connector.edid_blob_ptr == NULL) { 773a0a23564SMichal Meloun sc->hdmi_mode = false; 774a0a23564SMichal Meloun } else { 775a0a23564SMichal Meloun sc->hdmi_mode = drm_detect_hdmi_monitor( 776a0a23564SMichal Meloun (struct edid *)sc->output.connector.edid_blob_ptr->data); 777a0a23564SMichal Meloun } 778a0a23564SMichal Meloun 779a0a23564SMichal Meloun /* Get exact HDMI pixel frequency. */ 780a0a23564SMichal Meloun rv = clk_get_freq(sc->clk_hdmi, &freq); 781a0a23564SMichal Meloun if (rv != 0) { 782a0a23564SMichal Meloun device_printf(sc->dev, 783a0a23564SMichal Meloun "Cannot get 'hdmi' clock frequency\n"); 784a0a23564SMichal Meloun return (rv); 785a0a23564SMichal Meloun } 786a0a23564SMichal Meloun DRM_DEBUG_KMS("HDMI frequency: %llu Hz\n", freq); 787a0a23564SMichal Meloun 788a0a23564SMichal Meloun /* Wakeup SOR power */ 789a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); 790a0a23564SMichal Meloun val &= ~SOR_PLL0_PDBG; 791a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); 792a0a23564SMichal Meloun DELAY(10); 793a0a23564SMichal Meloun 794a0a23564SMichal Meloun val = RD4(sc, HDMI_NV_PDISP_SOR_PLL0); 795a0a23564SMichal Meloun val &= ~SOR_PLL0_PWR; 796a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_PLL0, val); 797a0a23564SMichal Meloun 798a0a23564SMichal Meloun /* Setup timings */ 799a0a23564SMichal Meloun TEGRA_DC_SETUP_TIMING(dc, h_pulse_start); 800a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_VSYNC_WINDOW, 801a0a23564SMichal Meloun VSYNC_WINDOW_START(0x200) | VSYNC_WINDOW_END(0x210) | 802a0a23564SMichal Meloun VSYNC_WINDOW_ENABLE); 803a0a23564SMichal Meloun 804a0a23564SMichal Meloun /* Setup video source and adjust video range */ 805a0a23564SMichal Meloun val = 0; 806a0a23564SMichal Meloun if (crtc->nvidia_head != 0) 807a0a23564SMichal Meloun HDMI_SRC_DISPLAYB; 808a0a23564SMichal Meloun if ((mode->hdisplay != 640) || (mode->vdisplay != 480)) 809a0a23564SMichal Meloun val |= ARM_VIDEO_RANGE_LIMITED; 810a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INPUT_CONTROL, val); 811a0a23564SMichal Meloun 812a0a23564SMichal Meloun /* Program SOR reference clock - it uses 8.2 fractional divisor */ 813a0a23564SMichal Meloun div8_2 = (freq * 4) / 1000000; 814a0a23564SMichal Meloun val = SOR_REFCLK_DIV_INT(div8_2 >> 2) | SOR_REFCLK_DIV_FRAC(div8_2); 815a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_REFCLK, val); 816a0a23564SMichal Meloun 817a0a23564SMichal Meloun /* Setup audio */ 818a0a23564SMichal Meloun if (sc->hdmi_mode) { 819a0a23564SMichal Meloun rv = audio_setup(sc); 820a0a23564SMichal Meloun if (rv != 0) 821a0a23564SMichal Meloun sc->hdmi_mode = false; 822a0a23564SMichal Meloun } 823a0a23564SMichal Meloun 824a0a23564SMichal Meloun /* Init HDA ELD */ 825a0a23564SMichal Meloun init_hda_eld(sc); 826a0a23564SMichal Meloun val = HDMI_CTRL_REKEY(HDMI_REKEY_DEFAULT); 827a0a23564SMichal Meloun val |= HDMI_CTRL_MAX_AC_PACKET(h_max_ac_packet); 828a0a23564SMichal Meloun if (sc->hdmi_mode) 829a0a23564SMichal Meloun val |= HDMI_CTRL_ENABLE; 830a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_HDMI_CTRL, val); 831a0a23564SMichal Meloun 832a0a23564SMichal Meloun /* Setup TMDS */ 833a0a23564SMichal Meloun for (i = 0; i < sc->n_tmds_configs; i++) { 834a0a23564SMichal Meloun if (sc->pclk <= sc->tmds_config[i].pclk) { 835a0a23564SMichal Meloun tmds_init(sc, sc->tmds_config + i); 836a0a23564SMichal Meloun break; 837a0a23564SMichal Meloun } 838a0a23564SMichal Meloun } 839a0a23564SMichal Meloun 840a0a23564SMichal Meloun /* Program sequencer. */ 841a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_SEQ_CTL, 842a0a23564SMichal Meloun SOR_SEQ_PU_PC(0) | SOR_SEQ_PU_PC_ALT(0) | 843a0a23564SMichal Meloun SOR_SEQ_PD_PC(8) | SOR_SEQ_PD_PC_ALT(8)); 844a0a23564SMichal Meloun 845a0a23564SMichal Meloun val = SOR_SEQ_INST_WAIT_TIME(1) | 846a0a23564SMichal Meloun SOR_SEQ_INST_WAIT_UNITS(WAIT_UNITS_VSYNC) | 847a0a23564SMichal Meloun SOR_SEQ_INST_HALT | 848a0a23564SMichal Meloun SOR_SEQ_INST_DRIVE_PWM_OUT_LO; 849a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(0), val); 850a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_SEQ_INST(8), val); 851a0a23564SMichal Meloun 852a0a23564SMichal Meloun val = RD4(sc,HDMI_NV_PDISP_SOR_CSTM); 853a0a23564SMichal Meloun val &= ~SOR_CSTM_LVDS_ENABLE; 854a0a23564SMichal Meloun val &= ~SOR_CSTM_ROTCLK(~0); 855a0a23564SMichal Meloun val |= SOR_CSTM_ROTCLK(2); 856a0a23564SMichal Meloun val &= ~SOR_CSTM_MODE(~0); 857a0a23564SMichal Meloun val |= SOR_CSTM_MODE(CSTM_MODE_TMDS); 858a0a23564SMichal Meloun val |= SOR_CSTM_PLLDIV; 859a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_SOR_CSTM, val); 860a0a23564SMichal Meloun 861a0a23564SMichal Meloun TEGRA_DC_DISPLAY_ENABLE(dc, false); 862a0a23564SMichal Meloun 863a0a23564SMichal Meloun rv = hdmi_sor_start(sc, mode); 864a0a23564SMichal Meloun if (rv != 0) 865a0a23564SMichal Meloun return (rv); 866a0a23564SMichal Meloun 867a0a23564SMichal Meloun TEGRA_DC_HDMI_ENABLE(dc, true); 868a0a23564SMichal Meloun TEGRA_DC_DISPLAY_ENABLE(dc, true); 869a0a23564SMichal Meloun 870a0a23564SMichal Meloun /* Enable HDA codec interrupt */ 871a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_MASK, INT_CODEC_SCRATCH0); 872a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_ENABLE, INT_CODEC_SCRATCH0); 873a0a23564SMichal Meloun 874a0a23564SMichal Meloun if (sc->hdmi_mode) { 875a0a23564SMichal Meloun avi_setup_infoframe(sc, mode); 876a0a23564SMichal Meloun audio_enable(sc); 877a0a23564SMichal Meloun } 878a0a23564SMichal Meloun 879a0a23564SMichal Meloun return (0); 880a0a23564SMichal Meloun } 881a0a23564SMichal Meloun 882a0a23564SMichal Meloun /* ------------------------------------------------------------------- 883a0a23564SMichal Meloun * 884a0a23564SMichal Meloun * DRM Interface. 885a0a23564SMichal Meloun * 886a0a23564SMichal Meloun */ 887a0a23564SMichal Meloun static enum drm_mode_status 888a0a23564SMichal Meloun hdmi_connector_mode_valid(struct drm_connector *connector, 889a0a23564SMichal Meloun struct drm_display_mode *mode) 890a0a23564SMichal Meloun { 891a0a23564SMichal Meloun struct tegra_drm_encoder *output; 892a0a23564SMichal Meloun struct hdmi_softc *sc; 893a0a23564SMichal Meloun int rv; 894a0a23564SMichal Meloun uint64_t freq; 895a0a23564SMichal Meloun 896a0a23564SMichal Meloun output = container_of(connector, struct tegra_drm_encoder, 897a0a23564SMichal Meloun connector); 898a0a23564SMichal Meloun sc = device_get_softc(output->dev); 899a0a23564SMichal Meloun 900a0a23564SMichal Meloun freq = HDMI_DC_CLOCK_MULTIPIER * mode->clock * 1000; 901a0a23564SMichal Meloun rv = clk_test_freq(sc->clk_parent, freq, 0); 902a0a23564SMichal Meloun DRM_DEBUG_KMS("Test HDMI frequency: %u kHz, rv: %d\n", mode->clock, rv); 903a0a23564SMichal Meloun if (rv != 0) 904a0a23564SMichal Meloun return (MODE_NOCLOCK); 905a0a23564SMichal Meloun 906a0a23564SMichal Meloun return (MODE_OK); 907a0a23564SMichal Meloun } 908a0a23564SMichal Meloun 909a0a23564SMichal Meloun static const struct drm_connector_helper_funcs hdmi_connector_helper_funcs = { 910a0a23564SMichal Meloun .get_modes = tegra_drm_connector_get_modes, 911a0a23564SMichal Meloun .mode_valid = hdmi_connector_mode_valid, 912a0a23564SMichal Meloun .best_encoder = tegra_drm_connector_best_encoder, 913a0a23564SMichal Meloun }; 914a0a23564SMichal Meloun 915a0a23564SMichal Meloun static const struct drm_connector_funcs hdmi_connector_funcs = { 916a0a23564SMichal Meloun .dpms = drm_helper_connector_dpms, 917a0a23564SMichal Meloun .detect = tegra_drm_connector_detect, 918a0a23564SMichal Meloun .fill_modes = drm_helper_probe_single_connector_modes, 919a0a23564SMichal Meloun .destroy = drm_connector_cleanup, 920a0a23564SMichal Meloun }; 921a0a23564SMichal Meloun 922a0a23564SMichal Meloun static const struct drm_encoder_funcs hdmi_encoder_funcs = { 923a0a23564SMichal Meloun .destroy = drm_encoder_cleanup, 924a0a23564SMichal Meloun }; 925a0a23564SMichal Meloun 926a0a23564SMichal Meloun static void 927a0a23564SMichal Meloun hdmi_encoder_dpms(struct drm_encoder *encoder, int mode) 928a0a23564SMichal Meloun { 929a0a23564SMichal Meloun 930a0a23564SMichal Meloun /* Empty function. */ 931a0a23564SMichal Meloun } 932a0a23564SMichal Meloun 933a0a23564SMichal Meloun static bool 934a0a23564SMichal Meloun hdmi_encoder_mode_fixup(struct drm_encoder *encoder, 935a0a23564SMichal Meloun const struct drm_display_mode *mode, 936a0a23564SMichal Meloun struct drm_display_mode *adjusted) 937a0a23564SMichal Meloun { 938a0a23564SMichal Meloun 939a0a23564SMichal Meloun return (true); 940a0a23564SMichal Meloun } 941a0a23564SMichal Meloun 942a0a23564SMichal Meloun static void 943a0a23564SMichal Meloun hdmi_encoder_prepare(struct drm_encoder *encoder) 944a0a23564SMichal Meloun { 945a0a23564SMichal Meloun 946a0a23564SMichal Meloun /* Empty function. */ 947a0a23564SMichal Meloun } 948a0a23564SMichal Meloun 949a0a23564SMichal Meloun static void 950a0a23564SMichal Meloun hdmi_encoder_commit(struct drm_encoder *encoder) 951a0a23564SMichal Meloun { 952a0a23564SMichal Meloun 953a0a23564SMichal Meloun /* Empty function. */ 954a0a23564SMichal Meloun } 955a0a23564SMichal Meloun 956a0a23564SMichal Meloun static void 957a0a23564SMichal Meloun hdmi_encoder_mode_set(struct drm_encoder *encoder, 958a0a23564SMichal Meloun struct drm_display_mode *mode, struct drm_display_mode *adjusted) 959a0a23564SMichal Meloun { 960a0a23564SMichal Meloun struct tegra_drm_encoder *output; 961a0a23564SMichal Meloun struct hdmi_softc *sc; 962a0a23564SMichal Meloun int rv; 963a0a23564SMichal Meloun 964a0a23564SMichal Meloun output = container_of(encoder, struct tegra_drm_encoder, encoder); 965a0a23564SMichal Meloun sc = device_get_softc(output->dev); 966a0a23564SMichal Meloun rv = hdmi_enable(sc); 967a0a23564SMichal Meloun if (rv != 0) 968a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable HDMI port\n"); 969a0a23564SMichal Meloun 970a0a23564SMichal Meloun } 971a0a23564SMichal Meloun 972a0a23564SMichal Meloun static void 973a0a23564SMichal Meloun hdmi_encoder_disable(struct drm_encoder *encoder) 974a0a23564SMichal Meloun { 975a0a23564SMichal Meloun struct tegra_drm_encoder *output; 976a0a23564SMichal Meloun struct hdmi_softc *sc; 977a0a23564SMichal Meloun int rv; 978a0a23564SMichal Meloun 979a0a23564SMichal Meloun output = container_of(encoder, struct tegra_drm_encoder, encoder); 980a0a23564SMichal Meloun sc = device_get_softc(output->dev); 981a0a23564SMichal Meloun if (sc == NULL) 982a0a23564SMichal Meloun return; 983a0a23564SMichal Meloun rv = hdmi_disable(sc); 984a0a23564SMichal Meloun if (rv != 0) 985a0a23564SMichal Meloun device_printf(sc->dev, "Cannot disable HDMI port\n"); 986a0a23564SMichal Meloun } 987a0a23564SMichal Meloun 988a0a23564SMichal Meloun static const struct drm_encoder_helper_funcs hdmi_encoder_helper_funcs = { 989a0a23564SMichal Meloun .dpms = hdmi_encoder_dpms, 990a0a23564SMichal Meloun .mode_fixup = hdmi_encoder_mode_fixup, 991a0a23564SMichal Meloun .prepare = hdmi_encoder_prepare, 992a0a23564SMichal Meloun .commit = hdmi_encoder_commit, 993a0a23564SMichal Meloun .mode_set = hdmi_encoder_mode_set, 994a0a23564SMichal Meloun .disable = hdmi_encoder_disable, 995a0a23564SMichal Meloun }; 996a0a23564SMichal Meloun 997a0a23564SMichal Meloun /* ------------------------------------------------------------------- 998a0a23564SMichal Meloun * 999a0a23564SMichal Meloun * Bus and infrastructure. 1000a0a23564SMichal Meloun * 1001a0a23564SMichal Meloun */ 1002a0a23564SMichal Meloun static int 1003a0a23564SMichal Meloun hdmi_init_client(device_t dev, device_t host1x, struct tegra_drm *drm) 1004a0a23564SMichal Meloun { 1005a0a23564SMichal Meloun struct hdmi_softc *sc; 1006a0a23564SMichal Meloun phandle_t node; 1007a0a23564SMichal Meloun int rv; 1008a0a23564SMichal Meloun 1009a0a23564SMichal Meloun sc = device_get_softc(dev); 1010a0a23564SMichal Meloun node = ofw_bus_get_node(sc->dev); 1011a0a23564SMichal Meloun sc->drm = drm; 1012a0a23564SMichal Meloun sc->output.setup_clock = &hdmi_setup_clock; 1013a0a23564SMichal Meloun 1014a0a23564SMichal Meloun rv = tegra_drm_encoder_attach(&sc->output, node); 1015a0a23564SMichal Meloun if (rv != 0) { 1016a0a23564SMichal Meloun device_printf(dev, "Cannot attach output connector\n"); 1017a0a23564SMichal Meloun return(ENXIO); 1018a0a23564SMichal Meloun } 1019a0a23564SMichal Meloun 1020a0a23564SMichal Meloun /* Connect this encoder + connector to DRM. */ 1021a0a23564SMichal Meloun drm_connector_init(&drm->drm_dev, &sc->output.connector, 1022a0a23564SMichal Meloun &hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); 1023a0a23564SMichal Meloun 1024a0a23564SMichal Meloun drm_connector_helper_add(&sc->output.connector, 1025a0a23564SMichal Meloun &hdmi_connector_helper_funcs); 1026a0a23564SMichal Meloun 1027a0a23564SMichal Meloun sc->output.connector.dpms = DRM_MODE_DPMS_OFF; 1028a0a23564SMichal Meloun 1029a0a23564SMichal Meloun drm_encoder_init(&drm->drm_dev, &sc->output.encoder, 1030a0a23564SMichal Meloun &hdmi_encoder_funcs, DRM_MODE_ENCODER_TMDS); 1031a0a23564SMichal Meloun 1032a0a23564SMichal Meloun drm_encoder_helper_add(&sc->output.encoder, &hdmi_encoder_helper_funcs); 1033a0a23564SMichal Meloun 1034a0a23564SMichal Meloun drm_mode_connector_attach_encoder(&sc->output.connector, 1035a0a23564SMichal Meloun &sc->output.encoder); 1036a0a23564SMichal Meloun 1037a0a23564SMichal Meloun rv = tegra_drm_encoder_init(&sc->output, drm); 1038a0a23564SMichal Meloun if (rv < 0) { 1039a0a23564SMichal Meloun device_printf(sc->dev, "Unable to init HDMI output\n"); 1040a0a23564SMichal Meloun return (rv); 1041a0a23564SMichal Meloun } 1042a0a23564SMichal Meloun sc->output.encoder.possible_crtcs = 0x3; 1043a0a23564SMichal Meloun return (0); 1044a0a23564SMichal Meloun } 1045a0a23564SMichal Meloun 1046a0a23564SMichal Meloun static int 1047a0a23564SMichal Meloun hdmi_exit_client(device_t dev, device_t host1x, struct tegra_drm *drm) 1048a0a23564SMichal Meloun { 1049a0a23564SMichal Meloun struct hdmi_softc *sc; 1050a0a23564SMichal Meloun 1051a0a23564SMichal Meloun sc = device_get_softc(dev); 1052a0a23564SMichal Meloun tegra_drm_encoder_exit(&sc->output, drm); 1053a0a23564SMichal Meloun return (0); 1054a0a23564SMichal Meloun } 1055a0a23564SMichal Meloun 1056a0a23564SMichal Meloun static int 1057a0a23564SMichal Meloun get_fdt_resources(struct hdmi_softc *sc, phandle_t node) 1058a0a23564SMichal Meloun { 1059a0a23564SMichal Meloun int rv; 1060a0a23564SMichal Meloun 1061a0a23564SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev, 0, "hdmi-supply", 1062a0a23564SMichal Meloun &sc->supply_hdmi); 1063a0a23564SMichal Meloun if (rv != 0) { 1064a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'hdmi' regulator\n"); 1065a0a23564SMichal Meloun return (ENXIO); 1066a0a23564SMichal Meloun } 1067a0a23564SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev,0, "pll-supply", 1068a0a23564SMichal Meloun &sc->supply_pll); 1069a0a23564SMichal Meloun if (rv != 0) { 1070a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'pll' regulator\n"); 1071a0a23564SMichal Meloun return (ENXIO); 1072a0a23564SMichal Meloun } 1073a0a23564SMichal Meloun rv = regulator_get_by_ofw_property(sc->dev, 0, "vdd-supply", 1074a0a23564SMichal Meloun &sc->supply_vdd); 1075a0a23564SMichal Meloun if (rv != 0) { 1076a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'vdd' regulator\n"); 1077a0a23564SMichal Meloun return (ENXIO); 1078a0a23564SMichal Meloun } 1079a0a23564SMichal Meloun 1080a0a23564SMichal Meloun rv = hwreset_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->hwreset_hdmi); 1081a0a23564SMichal Meloun if (rv != 0) { 1082a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'hdmi' reset\n"); 1083a0a23564SMichal Meloun return (ENXIO); 1084a0a23564SMichal Meloun } 1085a0a23564SMichal Meloun rv = clk_get_by_ofw_name(sc->dev, 0, "parent", &sc->clk_parent); 1086a0a23564SMichal Meloun if (rv != 0) { 1087a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'parent' clock\n"); 1088a0a23564SMichal Meloun return (ENXIO); 1089a0a23564SMichal Meloun } 1090a0a23564SMichal Meloun rv = clk_get_by_ofw_name(sc->dev, 0, "hdmi", &sc->clk_hdmi); 1091a0a23564SMichal Meloun if (rv != 0) { 1092a0a23564SMichal Meloun device_printf(sc->dev, "Cannot get 'hdmi' clock\n"); 1093a0a23564SMichal Meloun return (ENXIO); 1094a0a23564SMichal Meloun } 1095a0a23564SMichal Meloun 1096a0a23564SMichal Meloun return (0); 1097a0a23564SMichal Meloun } 1098a0a23564SMichal Meloun 1099a0a23564SMichal Meloun static int 1100a0a23564SMichal Meloun enable_fdt_resources(struct hdmi_softc *sc) 1101a0a23564SMichal Meloun { 1102a0a23564SMichal Meloun int rv; 1103a0a23564SMichal Meloun 1104a0a23564SMichal Meloun rv = clk_set_parent_by_clk(sc->clk_hdmi, sc->clk_parent); 1105a0a23564SMichal Meloun if (rv != 0) { 1106a0a23564SMichal Meloun device_printf(sc->dev, 1107a0a23564SMichal Meloun "Cannot set parent for 'hdmi' clock\n"); 1108a0a23564SMichal Meloun return (rv); 1109a0a23564SMichal Meloun } 1110a0a23564SMichal Meloun 1111a0a23564SMichal Meloun /* 594 MHz is arbitrarily selected value */ 1112a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_parent, 594000000, 0); 1113a0a23564SMichal Meloun if (rv != 0) { 1114a0a23564SMichal Meloun device_printf(sc->dev, 1115a0a23564SMichal Meloun "Cannot set frequency for 'hdmi' parent clock\n"); 1116a0a23564SMichal Meloun return (rv); 1117a0a23564SMichal Meloun } 1118a0a23564SMichal Meloun rv = clk_set_freq(sc->clk_hdmi, 594000000 / 4, 0); 1119a0a23564SMichal Meloun if (rv != 0) { 1120a0a23564SMichal Meloun device_printf(sc->dev, 1121a0a23564SMichal Meloun "Cannot set frequency for 'hdmi' parent clock\n"); 1122a0a23564SMichal Meloun return (rv); 1123a0a23564SMichal Meloun } 1124a0a23564SMichal Meloun 1125a0a23564SMichal Meloun rv = regulator_enable(sc->supply_hdmi); 1126a0a23564SMichal Meloun if (rv != 0) { 1127a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'hdmi' regulator\n"); 1128a0a23564SMichal Meloun return (rv); 1129a0a23564SMichal Meloun } 1130a0a23564SMichal Meloun rv = regulator_enable(sc->supply_pll); 1131a0a23564SMichal Meloun if (rv != 0) { 1132a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'pll' regulator\n"); 1133a0a23564SMichal Meloun return (rv); 1134a0a23564SMichal Meloun } 1135a0a23564SMichal Meloun rv = regulator_enable(sc->supply_vdd); 1136a0a23564SMichal Meloun if (rv != 0) { 1137a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'vdd' regulator\n"); 1138a0a23564SMichal Meloun return (rv); 1139a0a23564SMichal Meloun } 1140a0a23564SMichal Meloun 1141a0a23564SMichal Meloun rv = clk_enable(sc->clk_hdmi); 1142a0a23564SMichal Meloun if (rv != 0) { 1143a0a23564SMichal Meloun device_printf(sc->dev, "Cannot enable 'hdmi' clock\n"); 1144a0a23564SMichal Meloun return (rv); 1145a0a23564SMichal Meloun } 1146a0a23564SMichal Meloun 1147a0a23564SMichal Meloun rv = hwreset_deassert(sc->hwreset_hdmi); 1148a0a23564SMichal Meloun if (rv != 0) { 1149a0a23564SMichal Meloun device_printf(sc->dev, "Cannot unreset 'hdmi' reset\n"); 1150a0a23564SMichal Meloun return (rv); 1151a0a23564SMichal Meloun } 1152a0a23564SMichal Meloun return (0); 1153a0a23564SMichal Meloun } 1154a0a23564SMichal Meloun 1155a0a23564SMichal Meloun static void 1156a0a23564SMichal Meloun hdmi_intr(void *arg) 1157a0a23564SMichal Meloun { 1158a0a23564SMichal Meloun struct hdmi_softc *sc; 1159a0a23564SMichal Meloun uint32_t status; 1160a0a23564SMichal Meloun 1161a0a23564SMichal Meloun sc = arg; 1162a0a23564SMichal Meloun 1163a0a23564SMichal Meloun /* Confirm interrupt */ 1164a0a23564SMichal Meloun status = RD4(sc, HDMI_NV_PDISP_INT_STATUS); 1165a0a23564SMichal Meloun WR4(sc, HDMI_NV_PDISP_INT_STATUS, status); 1166a0a23564SMichal Meloun 1167a0a23564SMichal Meloun /* process audio verb from HDA */ 1168a0a23564SMichal Meloun if (status & INT_CODEC_SCRATCH0) 1169a0a23564SMichal Meloun hda_intr(sc); 1170a0a23564SMichal Meloun } 1171a0a23564SMichal Meloun 1172a0a23564SMichal Meloun static int 1173a0a23564SMichal Meloun hdmi_probe(device_t dev) 1174a0a23564SMichal Meloun { 1175a0a23564SMichal Meloun 1176a0a23564SMichal Meloun if (!ofw_bus_status_okay(dev)) 1177a0a23564SMichal Meloun return (ENXIO); 1178a0a23564SMichal Meloun 1179a0a23564SMichal Meloun if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 1180a0a23564SMichal Meloun return (ENXIO); 1181a0a23564SMichal Meloun 1182a0a23564SMichal Meloun device_set_desc(dev, "Tegra HDMI"); 1183a0a23564SMichal Meloun return (BUS_PROBE_DEFAULT); 1184a0a23564SMichal Meloun } 1185a0a23564SMichal Meloun 1186a0a23564SMichal Meloun static int 1187a0a23564SMichal Meloun hdmi_attach(device_t dev) 1188a0a23564SMichal Meloun { 1189a0a23564SMichal Meloun struct hdmi_softc *sc; 1190a0a23564SMichal Meloun phandle_t node; 1191a0a23564SMichal Meloun int rid, rv; 1192a0a23564SMichal Meloun 1193a0a23564SMichal Meloun sc = device_get_softc(dev); 1194a0a23564SMichal Meloun sc->dev = dev; 1195a0a23564SMichal Meloun sc->output.dev = sc->dev; 1196a0a23564SMichal Meloun node = ofw_bus_get_node(sc->dev); 1197a0a23564SMichal Meloun 1198a0a23564SMichal Meloun sc->audio_src_type = SOURCE_SELECT_AUTO; 1199a0a23564SMichal Meloun sc->audio_freq = 44100; 1200a0a23564SMichal Meloun sc->audio_chans = 2; 1201a0a23564SMichal Meloun sc->hdmi_mode = false; 1202a0a23564SMichal Meloun 1203a0a23564SMichal Meloun sc->tmds_config = tegra124_tmds_config; 1204a0a23564SMichal Meloun sc->n_tmds_configs = nitems(tegra124_tmds_config); 1205a0a23564SMichal Meloun 1206a0a23564SMichal Meloun rid = 0; 1207a0a23564SMichal Meloun sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 1208a0a23564SMichal Meloun RF_ACTIVE); 1209a0a23564SMichal Meloun if (sc->mem_res == NULL) { 1210a0a23564SMichal Meloun device_printf(dev, "Cannot allocate memory resources\n"); 1211a0a23564SMichal Meloun goto fail; 1212a0a23564SMichal Meloun } 1213a0a23564SMichal Meloun 1214a0a23564SMichal Meloun rid = 0; 1215a0a23564SMichal Meloun sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); 1216a0a23564SMichal Meloun if (sc->irq_res == NULL) { 1217a0a23564SMichal Meloun device_printf(dev, "Cannot allocate IRQ resources\n"); 1218a0a23564SMichal Meloun goto fail; 1219a0a23564SMichal Meloun } 1220a0a23564SMichal Meloun 1221a0a23564SMichal Meloun rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 1222a0a23564SMichal Meloun NULL, hdmi_intr, sc, &sc->irq_ih); 1223a0a23564SMichal Meloun if (rv != 0) { 1224a0a23564SMichal Meloun device_printf(dev, 1225a0a23564SMichal Meloun "WARNING: unable to register interrupt handler\n"); 1226a0a23564SMichal Meloun goto fail; 1227a0a23564SMichal Meloun } 1228a0a23564SMichal Meloun 1229a0a23564SMichal Meloun rv = get_fdt_resources(sc, node); 1230a0a23564SMichal Meloun if (rv != 0) { 1231a0a23564SMichal Meloun device_printf(dev, "Cannot parse FDT resources\n"); 1232a0a23564SMichal Meloun goto fail; 1233a0a23564SMichal Meloun } 1234a0a23564SMichal Meloun rv = enable_fdt_resources(sc); 1235a0a23564SMichal Meloun if (rv != 0) { 1236a0a23564SMichal Meloun device_printf(dev, "Cannot enable FDT resources\n"); 1237a0a23564SMichal Meloun goto fail; 1238a0a23564SMichal Meloun } 1239a0a23564SMichal Meloun 1240a0a23564SMichal Meloun rv = TEGRA_DRM_REGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); 1241a0a23564SMichal Meloun if (rv != 0) { 1242a0a23564SMichal Meloun device_printf(dev, "Cannot register DRM device\n"); 1243a0a23564SMichal Meloun goto fail; 1244a0a23564SMichal Meloun } 1245*18250ec6SJohn Baldwin bus_attach_children(dev); 1246*18250ec6SJohn Baldwin return (0); 1247a0a23564SMichal Meloun 1248a0a23564SMichal Meloun fail: 1249a0a23564SMichal Meloun TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); 1250a0a23564SMichal Meloun 1251a0a23564SMichal Meloun if (sc->irq_ih != NULL) 1252a0a23564SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); 1253a0a23564SMichal Meloun if (sc->clk_parent != NULL) 1254a0a23564SMichal Meloun clk_release(sc->clk_parent); 1255a0a23564SMichal Meloun if (sc->clk_hdmi != NULL) 1256a0a23564SMichal Meloun clk_release(sc->clk_hdmi); 1257a0a23564SMichal Meloun if (sc->hwreset_hdmi != NULL) 1258a0a23564SMichal Meloun hwreset_release(sc->hwreset_hdmi); 1259a0a23564SMichal Meloun if (sc->supply_hdmi != NULL) 1260a0a23564SMichal Meloun regulator_release(sc->supply_hdmi); 1261a0a23564SMichal Meloun if (sc->supply_pll != NULL) 1262a0a23564SMichal Meloun regulator_release(sc->supply_pll); 1263a0a23564SMichal Meloun if (sc->supply_vdd != NULL) 1264a0a23564SMichal Meloun regulator_release(sc->supply_vdd); 1265a0a23564SMichal Meloun if (sc->irq_res != NULL) 1266a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 1267a0a23564SMichal Meloun if (sc->mem_res != NULL) 1268a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 1269a0a23564SMichal Meloun return (ENXIO); 1270a0a23564SMichal Meloun } 1271a0a23564SMichal Meloun 1272a0a23564SMichal Meloun static int 1273a0a23564SMichal Meloun hdmi_detach(device_t dev) 1274a0a23564SMichal Meloun { 1275a0a23564SMichal Meloun struct hdmi_softc *sc; 1276d412c076SJohn Baldwin int error; 1277d412c076SJohn Baldwin 1278d412c076SJohn Baldwin error = bus_generic_detach(dev); 1279d412c076SJohn Baldwin if (error != 0) 1280d412c076SJohn Baldwin return (error); 1281d412c076SJohn Baldwin 1282a0a23564SMichal Meloun sc = device_get_softc(dev); 1283a0a23564SMichal Meloun 1284a0a23564SMichal Meloun TEGRA_DRM_DEREGISTER_CLIENT(device_get_parent(sc->dev), sc->dev); 1285a0a23564SMichal Meloun 1286a0a23564SMichal Meloun if (sc->irq_ih != NULL) 1287a0a23564SMichal Meloun bus_teardown_intr(dev, sc->irq_res, sc->irq_ih); 1288a0a23564SMichal Meloun if (sc->clk_parent != NULL) 1289a0a23564SMichal Meloun clk_release(sc->clk_parent); 1290a0a23564SMichal Meloun if (sc->clk_hdmi != NULL) 1291a0a23564SMichal Meloun clk_release(sc->clk_hdmi); 1292a0a23564SMichal Meloun if (sc->hwreset_hdmi != NULL) 1293a0a23564SMichal Meloun hwreset_release(sc->hwreset_hdmi); 1294a0a23564SMichal Meloun if (sc->supply_hdmi != NULL) 1295a0a23564SMichal Meloun regulator_release(sc->supply_hdmi); 1296a0a23564SMichal Meloun if (sc->supply_pll != NULL) 1297a0a23564SMichal Meloun regulator_release(sc->supply_pll); 1298a0a23564SMichal Meloun if (sc->supply_vdd != NULL) 1299a0a23564SMichal Meloun regulator_release(sc->supply_vdd); 1300a0a23564SMichal Meloun if (sc->irq_res != NULL) 1301a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res); 1302a0a23564SMichal Meloun if (sc->mem_res != NULL) 1303a0a23564SMichal Meloun bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res); 1304d412c076SJohn Baldwin return (0); 1305a0a23564SMichal Meloun } 1306a0a23564SMichal Meloun 1307a0a23564SMichal Meloun static device_method_t tegra_hdmi_methods[] = { 1308a0a23564SMichal Meloun /* Device interface */ 1309a0a23564SMichal Meloun DEVMETHOD(device_probe, hdmi_probe), 1310a0a23564SMichal Meloun DEVMETHOD(device_attach, hdmi_attach), 1311a0a23564SMichal Meloun DEVMETHOD(device_detach, hdmi_detach), 1312a0a23564SMichal Meloun 1313a0a23564SMichal Meloun /* tegra drm interface */ 1314a0a23564SMichal Meloun DEVMETHOD(tegra_drm_init_client, hdmi_init_client), 1315a0a23564SMichal Meloun DEVMETHOD(tegra_drm_exit_client, hdmi_exit_client), 1316a0a23564SMichal Meloun 1317a0a23564SMichal Meloun DEVMETHOD_END 1318a0a23564SMichal Meloun }; 1319a0a23564SMichal Meloun 1320a0a23564SMichal Meloun DEFINE_CLASS_0(tegra_hdmi, tegra_hdmi_driver, tegra_hdmi_methods, 1321a0a23564SMichal Meloun sizeof(struct hdmi_softc)); 1322289f133bSJohn Baldwin DRIVER_MODULE(tegra_hdmi, host1x, tegra_hdmi_driver, 0, 0); 1323