Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/nvidia Add HDMI audio support



details:   https://anonhg.NetBSD.org/src/rev/da2f12c0fdaa
branches:  trunk
changeset: 339502:da2f12c0fdaa
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Sat Jul 25 15:50:42 2015 +0000

description:
Add HDMI audio support

diffstat:

 sys/arch/arm/nvidia/tegra_car.c     |   23 ++-
 sys/arch/arm/nvidia/tegra_carreg.h  |   20 +++-
 sys/arch/arm/nvidia/tegra_hdmi.c    |  197 +++++++++++++++++++++++++++++++----
 sys/arch/arm/nvidia/tegra_hdmireg.h |   49 ++++++++-
 4 files changed, 252 insertions(+), 37 deletions(-)

diffs (truncated from 451 to 300 lines):

diff -r 23e8435e72ac -r da2f12c0fdaa sys/arch/arm/nvidia/tegra_car.c
--- a/sys/arch/arm/nvidia/tegra_car.c   Sat Jul 25 15:20:49 2015 +0000
+++ b/sys/arch/arm/nvidia/tegra_car.c   Sat Jul 25 15:50:42 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_car.c,v 1.23 2015/07/23 18:22:05 jmcneill Exp $ */
+/* $NetBSD: tegra_car.c,v 1.24 2015/07/25 15:50:42 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -29,7 +29,7 @@
 #include "locators.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.23 2015/07/23 18:22:05 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_car.c,v 1.24 2015/07/25 15:50:42 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -535,24 +535,27 @@
 
        tegra_car_get_bs(&bst, &bsh);
 
-       /* HDA */
        bus_space_write_4(bst, bsh, CAR_RST_DEV_V_SET_REG, CAR_DEV_V_HDA);
        bus_space_write_4(bst, bsh, CAR_CLK_ENB_V_SET_REG, CAR_DEV_V_HDA);
-       bus_space_write_4(bst, bsh, CAR_RST_DEV_V_CLR_REG, CAR_DEV_V_HDA);
-
-       /* HDA2CODEC_2X */
        bus_space_write_4(bst, bsh, CAR_RST_DEV_V_SET_REG,
            CAR_DEV_V_HDA2CODEC_2X);
        bus_space_write_4(bst, bsh, CAR_CLK_ENB_V_SET_REG,
            CAR_DEV_V_HDA2CODEC_2X);
-       bus_space_write_4(bst, bsh, CAR_RST_DEV_V_CLR_REG,
-           CAR_DEV_V_HDA2CODEC_2X);
-
-       /* HDA2HDMICODEC */
        bus_space_write_4(bst, bsh, CAR_RST_DEV_W_SET_REG,
            CAR_DEV_W_HDA2HDMICODEC);
        bus_space_write_4(bst, bsh, CAR_CLK_ENB_W_SET_REG,
            CAR_DEV_W_HDA2HDMICODEC);
+
+       /* configure HDA2CODEC_2X for 48 MHz */
+       const u_int div = howmany(tegra_car_pllp0_rate() * 2, 48000000) - 2;
+       bus_space_write_4(bst, bsh, CAR_CLKSRC_HDA2CODEC_2X_REG,
+           __SHIFTIN(CAR_CLKSRC_HDA2CODEC_2X_SRC_PLLP_OUT0,
+                     CAR_CLKSRC_HDA2CODEC_2X_SRC) |
+           __SHIFTIN(div, CAR_CLKSRC_HDA2CODEC_2X_DIV));
+
+       bus_space_write_4(bst, bsh, CAR_RST_DEV_V_CLR_REG, CAR_DEV_V_HDA);
+       bus_space_write_4(bst, bsh, CAR_RST_DEV_V_CLR_REG,
+           CAR_DEV_V_HDA2CODEC_2X);
        bus_space_write_4(bst, bsh, CAR_RST_DEV_W_CLR_REG,
            CAR_DEV_W_HDA2HDMICODEC);
 }
diff -r 23e8435e72ac -r da2f12c0fdaa sys/arch/arm/nvidia/tegra_carreg.h
--- a/sys/arch/arm/nvidia/tegra_carreg.h        Sat Jul 25 15:20:49 2015 +0000
+++ b/sys/arch/arm/nvidia/tegra_carreg.h        Sat Jul 25 15:50:42 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_carreg.h,v 1.19 2015/07/23 15:07:31 skrll Exp $ */
+/* $NetBSD: tegra_carreg.h,v 1.20 2015/07/25 15:50:42 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -390,6 +390,16 @@
 #define CAR_CCLKG_BURST_POLICY_CWAKEUP_SOURCE_CLKM             0
 #define CAR_CCLKG_BURST_POLICY_CWAKEUP_SOURCE_PLLX_OUT0_LJ     8
 
+#define CAR_CLKSRC_HDA2CODEC_2X_REG    0x3e4
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC    __BITS(31,29)
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC_PLLP_OUT0  0
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC_PLLC2_OUT0 1
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC_PLLC_OUT0  2
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC_PLLC3_OUT0 3
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC_PLLM_OUT0  4
+#define CAR_CLKSRC_HDA2CODEC_2X_SRC_CLKM       6
+#define CAR_CLKSRC_HDA2CODEC_2X_DIV    __BITS(7,0)
+
 #define CAR_CLKSRC_SATA_OOB_REG                0x420
 #define CAR_CLKSRC_SATA_OOB_SRC                __BITS(31,29)
 #define CAR_CLKSRC_SATA_OOB_SRC_PLLP_OUT0      0
@@ -462,4 +472,12 @@
 #define CAR_SATA_PLL_CFG1_PADPLL_PU_POST_DLY   __BITS(15,8)
 #define CAR_SATA_PLL_CFG1_LANE_IDDQ2_PADPLL_IDDQ_DLY __BITS(7,0)
 
+#define CAR_CLKSRC_HDMI_AUDIO_REG      0x668
+#define CAR_CLKSRC_HDMI_AUDIO_SRC      __BITS(31,29)
+#define CAR_CLKSRC_HDMI_AUDIO_SRC_PLLP_OUT0    0
+#define CAR_CLKSRC_HDMI_AUDIO_SRC_PLLC_OUT0    1
+#define CAR_CLKSRC_HDMI_AUDIO_SRC_PLLC2_OUT0   2
+#define CAR_CLKSRC_HDMI_AUDIO_SRC_CLKM         3
+#define CAR_CLKSRC_HDMI_AUDIO_DIV      __BITS(7,0)
+
 #endif /* _ARM_TEGRA_CARREG_H */
diff -r 23e8435e72ac -r da2f12c0fdaa sys/arch/arm/nvidia/tegra_hdmi.c
--- a/sys/arch/arm/nvidia/tegra_hdmi.c  Sat Jul 25 15:20:49 2015 +0000
+++ b/sys/arch/arm/nvidia/tegra_hdmi.c  Sat Jul 25 15:50:42 2015 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: tegra_hdmi.c,v 1.5 2015/07/23 15:43:06 jmcneill Exp $ */
+/* $NetBSD: tegra_hdmi.c,v 1.6 2015/07/25 15:50:42 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <jmcneill%invisible.ca@localhost>
@@ -29,7 +29,7 @@
 #include "locators.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: tegra_hdmi.c,v 1.5 2015/07/23 15:43:06 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: tegra_hdmi.c,v 1.6 2015/07/25 15:50:42 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -87,6 +87,7 @@
 
        bool                    sc_connected;
        const struct videomode  *sc_curmode;
+       bool                    sc_hdmimode;
 };
 
 static void    tegra_hdmi_hpd(struct tegra_hdmi_softc *);
@@ -94,6 +95,10 @@
 static void    tegra_hdmi_disconnect(struct tegra_hdmi_softc *);
 static void    tegra_hdmi_enable(struct tegra_hdmi_softc *, const uint8_t *);
 static int     tegra_hdmi_sor_start(struct tegra_hdmi_softc *);
+static bool    tegra_hdmi_is_hdmi(struct tegra_hdmi_softc *,
+                                  const struct edid_info *);
+static void    tegra_hdmi_setup_audio_infoframe(struct tegra_hdmi_softc *);
+static uint8_t tegra_hdmi_infoframe_csum(const uint8_t *, size_t);
 
 CFATTACH_DECL_NEW(tegra_hdmi, sizeof(struct tegra_hdmi_softc),
        tegra_hdmi_match, tegra_hdmi_attach, NULL, NULL);
@@ -230,6 +235,13 @@
        }
 
        sc->sc_curmode = mode;
+       sc->sc_hdmimode = tegra_hdmi_is_hdmi(sc, &ei);
+device_printf(sc->sc_dev, "connected to %s display\n", sc->sc_hdmimode ? "HDMI" : "DVI");
+       if (sc->sc_hdmimode == false) {
+               device_printf(sc->sc_dev, "forcing HDMI mode\n");
+               sc->sc_hdmimode = true;
+       }
+
        tegra_hdmi_enable(sc, pedid);
 }
 
@@ -244,34 +256,33 @@
        const struct tegra_hdmi_tmds_config *tmds = NULL;
        const struct videomode *mode = sc->sc_curmode;
        uint32_t input_ctrl;
-       u_int n;
+       u_int i;
 
        KASSERT(sc->sc_curmode != NULL);
        tegra_pmc_hdmi_enable();
 
        tegra_car_hdmi_enable(mode->dot_clock * 1000);
 
-       for (n = 0; n < __arraycount(tegra_hdmi_tmds_config); n++) {
-               if (tegra_hdmi_tmds_config[n].dot_clock >= mode->dot_clock) {
+       for (i = 0; i < __arraycount(tegra_hdmi_tmds_config); i++) {
+               if (tegra_hdmi_tmds_config[i].dot_clock >= mode->dot_clock) {
                        break;
                }
        }
-       if (n < __arraycount(tegra_hdmi_tmds_config)) {
-               tmds = &tegra_hdmi_tmds_config[n];
+       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];
        }
-       if (tmds != NULL) {
-               HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_PLL0_REG, tmds->sor_pll0);
-               HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_PLL1_REG, tmds->sor_pll1);
-               HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT_REG,
-                   tmds->sor_lane_drive_current);
-               HDMI_WRITE(sc, HDMI_NV_PDISP_PE_CURRENT_REG, tmds->pe_current);
-               HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT_REG,
-                   tmds->sor_io_peak_current);
-               HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0_REG,
-                   tmds->sor_pad_ctls0);
-       }
+
+       HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_PLL0_REG, tmds->sor_pll0);
+       HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_PLL1_REG, tmds->sor_pll1);
+       HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_LANE_DRIVE_CURRENT_REG,
+           tmds->sor_lane_drive_current);
+       HDMI_WRITE(sc, HDMI_NV_PDISP_PE_CURRENT_REG, tmds->pe_current);
+       HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_IO_PEAK_CURRENT_REG,
+           tmds->sor_io_peak_current);
+       HDMI_WRITE(sc, HDMI_NV_PDISP_SOR_PAD_CTLS0_REG,
+           tmds->sor_pad_ctls0);
 
        tegra_dc_enable(sc->sc_displaydev, sc->sc_dev, mode, edid);
 
@@ -314,17 +325,57 @@
        uint32_t ctrl =
            __SHIFTIN(rekey, HDMI_NV_PDISP_HDMI_CTRL_REKEY) |
            __SHIFTIN(max_ac_packet, HDMI_NV_PDISP_HDMI_CTRL_MAX_AC_PACKET);
-#if notyet
-       if (HDMI mode) {
+       if (sc->sc_hdmimode) {
                ctrl |= HDMI_NV_PDISP_HDMI_CTRL_ENABLE; /* HDMI ENABLE */
        }
-#endif
        HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_CTRL_REG, ctrl);
 
-       /* XXX DVI */
-       HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL_REG, 0);
-       HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL_REG, 0);
-       HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_REG, 0);
+       if (sc->sc_hdmimode) {
+               const u_int n = 6144;   /* 48 kHz */
+               const u_int cts = ((mode->dot_clock * 10) * (n / 128)) / 480;
+
+               HDMI_WRITE(sc, 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(sc, 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(sc, 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(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_LOW_REG,
+                   cts << 8);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_ACR_0441_SUBPACK_HIGH_REG,
+                   0x80000000 | n);
+
+               HDMI_SET_CLEAR(sc, HDMI_NV_PDISP_AUDIO_N_REG, 0,
+                   HDMI_NV_PDISP_AUDIO_N_RESETF);
+
+               HDMI_WRITE(sc, 24000, HDMI_NV_PDISP_SOR_AUDIO_AVAL_0480_REG);
+
+               tegra_hdmi_setup_audio_infoframe(sc);
+
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL_REG,
+                   HDMI_NV_PDISP_HDMI_GENERIC_CTRL_AUDIO);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL_REG, 0);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_REG,
+                   HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_ENABLE);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL_REG, 0);
+       } else {
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_GENERIC_CTRL_REG, 0);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_AVI_INFOFRAME_CTRL_REG, 0);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_AUDIO_INFOFRAME_CTRL_REG, 0);
+               HDMI_WRITE(sc, HDMI_NV_PDISP_HDMI_ACR_CTRL_REG, 0);
+       }
 
        /* Start HDMI output */
        tegra_dc_hdmi_start(sc->sc_displaydev);
@@ -391,3 +442,99 @@
 
        return 0;
 }
+
+static bool
+tegra_hdmi_is_hdmi(struct tegra_hdmi_softc *sc, const struct edid_info *ei)
+{
+       char edid[128];
+       bool found_hdmi = false;
+       unsigned int n, p;
+
+       /*
+        * Scan through extension blocks, looking for a CEA-861-D v3
+        * block. If an HDMI Vendor-Specific Data Block (HDMI VSDB) is
+        * found in that, assume HDMI mode.
+        */
+       for (n = 1; n <= MIN(ei->edid_ext_block_count, 4); n++) {
+               if (ddc_dev_read_edid_block(sc->sc_ddcdev, edid,
+                   sizeof(edid), n)) {
+                       break;
+               }
+
+               const uint8_t tag = edid[0];
+               const uint8_t rev = edid[1];
+               const uint8_t off = edid[2];
+
+               /* We are looking for a CEA-861-D tag (02h) with revision 3 */
+               if (tag != 0x02 || rev != 3)
+                       continue;
+               /*
+                * CEA data block collection starts at byte 4, so the
+                * DTD blocks must start after it.
+                */
+               if (off <= 4)
+                       continue;
+
+               /* Parse the CEA data blocks */
+               for (p = 4; p < off;) {
+                       const uint8_t btag = (edid[p] >> 5) & 0x7;
+                       const uint8_t blen = edid[p] & 0x1f;



Home | Main Index | Thread Index | Old Index