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