Module Name:    src
Committed By:   jmcneill
Date:           Sat Nov 16 12:50:08 UTC 2019

Modified Files:
        src/sys/dev/ic: dw_hdmi.c dw_hdmi.h

Log Message:
Add I2S audio input support.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/ic/dw_hdmi.c src/sys/dev/ic/dw_hdmi.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/dev/ic/dw_hdmi.c
diff -u src/sys/dev/ic/dw_hdmi.c:1.2 src/sys/dev/ic/dw_hdmi.c:1.3
--- src/sys/dev/ic/dw_hdmi.c:1.2	Sat Nov  9 23:27:50 2019
+++ src/sys/dev/ic/dw_hdmi.c	Sat Nov 16 12:50:08 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: dw_hdmi.c,v 1.2 2019/11/09 23:27:50 jmcneill Exp $ */
+/* $NetBSD: dw_hdmi.c,v 1.3 2019/11/16 12:50:08 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.2 2019/11/09 23:27:50 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 1.3 2019/11/16 12:50:08 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -45,6 +45,8 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 
 #include <dev/videomode/videomode.h>
 #include <dev/videomode/edidvar.h>
 
+#include <dev/audio/audio_dai.h>
+
 #include <drm/drmP.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_crtc_helper.h>
@@ -52,6 +54,8 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 
 
 #define	HDMI_DESIGN_ID		0x0000
 #define	HDMI_REVISION_ID	0x0001
+#define	HDMI_CONFIG0_ID		0x0004
+#define	 HDMI_CONFIG0_ID_AUDI2S			__BIT(4)
 #define	HDMI_CONFIG2_ID		0x0006
 
 #define	HDMI_IH_I2CM_STAT0	0x0105
@@ -131,6 +135,10 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 
 #define	 HDMI_FC_CH1PREAM_DEFAULT		0x16
 #define	HDMI_FC_CH2PREAM	0x1016
 #define	 HDMI_FC_CH2PREAM_DEFAULT		0x21
+#define	HDMI_FC_AUDCONF0	0x1025
+#define	HDMI_FC_AUDCONF1	0x1026
+#define	HDMI_FC_AUDCONF2	0x1027
+#define	HDMI_FC_AUDCONF3	0x1028
 
 #define	HDMI_PHY_CONF0		0x3000
 #define	 HDMI_PHY_CONF0_PDZ			__BIT(7)
@@ -149,6 +157,28 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 
 #define	 HDMI_PHY_STAT0_HPD			__BIT(1)
 #define	 HDMI_PHY_STAT0_TX_PHY_LOCK		__BIT(0)
 
+#define	HDMI_AUD_CONF0		0x3100
+#define	 HDMI_AUD_CONF0_SW_AUDIO_FIFO_RST	__BIT(7)
+#define	 HDMI_AUD_CONF0_I2S_SELECT		__BIT(5)
+#define	 HDMI_AUD_CONF0_I2S_IN_EN		__BITS(3,0)
+#define	HDMI_AUD_CONF1		0x3101
+#define	 HDMI_AUD_CONF1_I2S_WIDTH		__BITS(4,0)
+#define	HDMI_AUD_INT		0x3102
+#define	HDMI_AUD_CONF2		0x3103
+#define	 HDMI_AUD_CONF2_INSERT_PCUV		__BIT(2)
+#define	 HDMI_AUD_CONF2_NLPCM			__BIT(1)
+#define	 HDMI_AUD_CONF2_HBR			__BIT(0)
+#define	HDMI_AUD_INT1		0x3104
+
+#define	HDMI_AUD_N1		0x3200
+#define	HDMI_AUD_N2		0x3201
+#define	HDMI_AUD_N3		0x3202
+#define	HDMI_AUD_CTS1		0x3203
+#define	HDMI_AUD_CTS2		0x3204
+#define	HDMI_AUD_CTS3		0x3205
+#define	HDMI_AUD_INPUTCLKFS	0x3206
+#define	 HDMI_AUD_INPUTCLKFS_IFSFACTOR		__BITS(2,0)
+
 #define	HDMI_MC_CLKDIS		0x4001
 #define	 HDMI_MC_CLKDIS_HDCPCLK_DISABLE		__BIT(6)
 #define	 HDMI_MC_CLKDIS_CECCLK_DISABLE		__BIT(5)
@@ -210,6 +240,16 @@ __KERNEL_RCSID(0, "$NetBSD: dw_hdmi.c,v 
 #define	 HDMI_I2CM_SOFTRSTZ_I2C_SOFTRST		__BIT(0)
 #define	HDMI_I2CM_SEGPTR	0x7e0a
 
+enum dwhdmi_dai_mixer_ctrl {
+	DWHDMI_DAI_OUTPUT_CLASS,
+	DWHDMI_DAI_INPUT_CLASS,
+
+	DWHDMI_DAI_OUTPUT_MASTER_VOLUME,
+	DWHDMI_DAI_INPUT_DAC_VOLUME,
+
+	DWHDMI_DAI_MIXER_CTRL_LAST
+};
+
 static int
 dwhdmi_ddc_acquire_bus(void *priv, int flags)
 {
@@ -433,7 +473,6 @@ dwhdmi_fc_init(struct dwhdmi_softc *sc, 
 static void
 dwhdmi_mc_init(struct dwhdmi_softc *sc)
 {
-	struct dwhdmi_connector *dwhdmi_connector = &sc->sc_connector;
 	uint8_t val;
 	u_int n, iter;
 
@@ -445,8 +484,6 @@ dwhdmi_mc_init(struct dwhdmi_softc *sc)
 	      HDMI_MC_CLKDIS_CECCLK_DISABLE |
 	      HDMI_MC_CLKDIS_CSCCLK_DISABLE |
 	      HDMI_MC_CLKDIS_PREPCLK_DISABLE;
-	if (!dwhdmi_connector->monitor_audio)
-		val |= HDMI_MC_CLKDIS_AUDCLK_DISABLE;
 	dwhdmi_write(sc, HDMI_MC_CLKDIS, val);
 
 	/* Soft reset TMDS */
@@ -467,6 +504,59 @@ dwhdmi_mc_disable(struct dwhdmi_softc *s
 	dwhdmi_write(sc, HDMI_MC_CLKDIS, 0xff);
 }
 
+static void
+dwhdmi_audio_init(struct dwhdmi_softc *sc)
+{
+	uint8_t val;
+	u_int n;
+
+	/* The following values are for 48 kHz */
+	switch (sc->sc_curmode.clock) {
+	case 25170:
+		n = 6864;
+		break;
+	case 74170:
+		n = 11648;
+		break;
+	case 148350:
+		n = 5824;
+		break;
+	default:
+		n = 6144;
+		break;
+	}
+
+	/* Use automatic CTS generation */
+	dwhdmi_write(sc, HDMI_AUD_CTS1, 0);
+	dwhdmi_write(sc, HDMI_AUD_CTS2, 0);
+	dwhdmi_write(sc, HDMI_AUD_CTS3, 0);
+
+	/* Set N factor for audio clock regeneration */
+	dwhdmi_write(sc, HDMI_AUD_N1, n & 0xff);
+	dwhdmi_write(sc, HDMI_AUD_N2, (n >> 8) & 0xff);
+	dwhdmi_write(sc, HDMI_AUD_N3, (n >> 16) & 0xff);
+
+	val = dwhdmi_read(sc, HDMI_AUD_CONF0);
+	val |= HDMI_AUD_CONF0_I2S_SELECT;		/* XXX i2s mode */
+	val &= ~HDMI_AUD_CONF0_I2S_IN_EN;
+	val |= __SHIFTIN(1, HDMI_AUD_CONF0_I2S_IN_EN);	/* XXX 2ch */
+	dwhdmi_write(sc, HDMI_AUD_CONF0, val);
+	
+	val = __SHIFTIN(16, HDMI_AUD_CONF1_I2S_WIDTH);
+	dwhdmi_write(sc, HDMI_AUD_CONF1, val);
+
+	dwhdmi_write(sc, HDMI_AUD_INPUTCLKFS, 4);	/* XXX 64 FS */
+
+	dwhdmi_write(sc, HDMI_FC_AUDCONF0, 1 << 4);	/* XXX 2ch */
+	dwhdmi_write(sc, HDMI_FC_AUDCONF1, 0);
+	dwhdmi_write(sc, HDMI_FC_AUDCONF2, 0);
+	dwhdmi_write(sc, HDMI_FC_AUDCONF3, 0);
+
+	val = dwhdmi_read(sc, HDMI_MC_CLKDIS);
+	val &= ~HDMI_MC_CLKDIS_PREPCLK_DISABLE;
+	dwhdmi_write(sc, HDMI_MC_CLKDIS, val);
+}
+
 static enum drm_connector_status
 dwhdmi_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -594,6 +684,9 @@ dwhdmi_bridge_enable(struct drm_bridge *
 
 	dwhdmi_tx_init(sc);
 	dwhdmi_mc_init(sc);
+
+	if (sc->sc_connector.monitor_audio)
+		dwhdmi_audio_init(sc);
 }
 
 static void
@@ -646,6 +739,94 @@ static const struct drm_bridge_funcs dwh
 	.mode_fixup = dwhdmi_bridge_mode_fixup,
 };
 
+static int
+dwhdmi_dai_set_format(audio_dai_tag_t dai, u_int format)
+{
+	return 0;
+}
+
+static int
+dwhdmi_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux)
+{
+	/* Not supported */
+	return 0;
+}
+
+static int
+dwhdmi_dai_set_port(void *priv, mixer_ctrl_t *mc)
+{
+	switch (mc->dev) {
+	case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
+	case DWHDMI_DAI_INPUT_DAC_VOLUME:
+		return 0;
+	default:
+		return ENXIO;
+	}
+}
+
+static int
+dwhdmi_dai_get_port(void *priv, mixer_ctrl_t *mc)
+{
+	switch (mc->dev) {
+	case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
+	case DWHDMI_DAI_INPUT_DAC_VOLUME:
+		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = 255;
+		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = 255;
+		return 0;
+	default:
+		return ENXIO;
+	}
+}
+
+static int
+dwhdmi_dai_query_devinfo(void *priv, mixer_devinfo_t *di)
+{
+	switch (di->index) {
+	case DWHDMI_DAI_OUTPUT_CLASS:
+		di->mixer_class = di->index;
+		strcpy(di->label.name, AudioCoutputs);
+		di->type = AUDIO_MIXER_CLASS;
+		di->next = di->prev = AUDIO_MIXER_LAST;
+		return 0;
+
+	case DWHDMI_DAI_INPUT_CLASS:
+		di->mixer_class = di->index;
+		strcpy(di->label.name, AudioCinputs);
+		di->type = AUDIO_MIXER_CLASS;
+		di->next = di->prev = AUDIO_MIXER_LAST;
+		return 0;
+
+	case DWHDMI_DAI_OUTPUT_MASTER_VOLUME:
+		di->mixer_class = DWHDMI_DAI_OUTPUT_CLASS;
+		strcpy(di->label.name, AudioNmaster);
+		di->un.v.delta = 1;
+		di->un.v.num_channels = 2;
+		strcpy(di->un.v.units.name, AudioNvolume);
+		di->type = AUDIO_MIXER_VALUE;
+		di->next = di->prev = AUDIO_MIXER_LAST;
+		return 0;
+
+	case DWHDMI_DAI_INPUT_DAC_VOLUME:
+		di->mixer_class = DWHDMI_DAI_INPUT_CLASS;
+		strcpy(di->label.name, AudioNdac);
+		di->un.v.delta = 1;
+		di->un.v.num_channels = 2;
+		strcpy(di->un.v.units.name, AudioNvolume);
+		di->type = AUDIO_MIXER_VALUE;
+		di->next = di->prev = AUDIO_MIXER_LAST;
+		return 0;
+
+	default:
+		return ENXIO;
+	}
+}
+
+static const struct audio_hw_if dwhdmi_dai_hw_if = {
+	.set_port = dwhdmi_dai_set_port,
+	.get_port = dwhdmi_dai_get_port,
+	.query_devinfo = dwhdmi_dai_query_devinfo,
+};
+
 int
 dwhdmi_attach(struct dwhdmi_softc *sc)
 {
@@ -690,6 +871,15 @@ dwhdmi_attach(struct dwhdmi_softc *sc)
 		dwhdmi_write(sc, HDMI_PHY_CONF0, val);
 	}
 
+	/*
+	 * Initialize audio DAI
+	 */
+	sc->sc_dai.dai_set_format = dwhdmi_dai_set_format;
+	sc->sc_dai.dai_add_device = dwhdmi_dai_add_device;
+	sc->sc_dai.dai_hw_if = &dwhdmi_dai_hw_if;
+	sc->sc_dai.dai_dev = sc->sc_dev;
+	sc->sc_dai.dai_priv = sc;
+
 	return 0;
 }
 
Index: src/sys/dev/ic/dw_hdmi.h
diff -u src/sys/dev/ic/dw_hdmi.h:1.2 src/sys/dev/ic/dw_hdmi.h:1.3
--- src/sys/dev/ic/dw_hdmi.h:1.2	Sat Nov  9 23:27:50 2019
+++ src/sys/dev/ic/dw_hdmi.h	Sat Nov 16 12:50:08 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: dw_hdmi.h,v 1.2 2019/11/09 23:27:50 jmcneill Exp $ */
+/* $NetBSD: dw_hdmi.h,v 1.3 2019/11/16 12:50:08 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2019 Jared D. McNeill <jmcne...@invisible.ca>
@@ -32,6 +32,8 @@
 #include <dev/i2c/i2cvar.h>
 #include <dev/i2c/ddcreg.h>
 
+#include <dev/audio/audio_dai.h>
+
 #include <drm/drmP.h>
 
 struct dwhdmi_softc;
@@ -73,6 +75,8 @@ struct dwhdmi_softc {
 	kmutex_t		sc_ic_lock;
 	struct i2c_controller	sc_ic_builtin;
 
+	struct audio_dai_device	sc_dai;
+
 	struct dwhdmi_connector	sc_connector;
 	struct drm_bridge	sc_bridge;
 

Reply via email to