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;