Module Name: src Committed By: jmcneill Date: Fri May 11 22:51:12 UTC 2018
Modified Files: src/sys/arch/arm/sunxi: sun50i_a64_acodec.c sun8i_codec.c Log Message: Add HP jack detect support. When HP is present, mute lineout. To generate a diff of this commit: cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/sunxi/sun50i_a64_acodec.c cvs rdiff -u -r1.1 -r1.2 src/sys/arch/arm/sunxi/sun8i_codec.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/sunxi/sun50i_a64_acodec.c diff -u src/sys/arch/arm/sunxi/sun50i_a64_acodec.c:1.2 src/sys/arch/arm/sunxi/sun50i_a64_acodec.c:1.3 --- src/sys/arch/arm/sunxi/sun50i_a64_acodec.c:1.2 Thu May 10 00:30:56 2018 +++ src/sys/arch/arm/sunxi/sun50i_a64_acodec.c Fri May 11 22:51:12 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sun50i_a64_acodec.c,v 1.2 2018/05/10 00:30:56 jmcneill Exp $ */ +/* $NetBSD: sun50i_a64_acodec.c,v 1.3 2018/05/11 22:51:12 jmcneill Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sun50i_a64_acodec.c,v 1.2 2018/05/10 00:30:56 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sun50i_a64_acodec.c,v 1.3 2018/05/11 22:51:12 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -89,6 +89,10 @@ __KERNEL_RCSID(0, "$NetBSD: sun50i_a64_a #define A64_ADCREN __BIT(7) #define A64_ADCLEN __BIT(6) #define A64_ADCG __BITS(2,0) +#define A64_JACK_MIC_CTRL 0x1d +#define A64_JACKDETEN __BIT(7) +#define A64_INNERRESEN __BIT(6) +#define A64_AUTOPLEN __BIT(1) struct a64_acodec_softc { device_t sc_dev; @@ -430,6 +434,29 @@ static struct fdtbus_dai_controller_func .get_tag = a64_acodec_dai_get_tag }; +static int +a64_acodec_dai_jack_detect(audio_dai_tag_t dai, u_int jack, int present) +{ + struct a64_acodec_softc * const sc = audio_dai_private(dai); + const uint32_t lineout_mask = A64_LINEOUT_LEFT_EN | A64_LINEOUT_RIGHT_EN; + + switch (jack) { + case AUDIO_DAI_JACK_HP: + if (present) + a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, + 0, lineout_mask); + else + a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0, + lineout_mask, 0); + break; + case AUDIO_DAI_JACK_MIC: + /* XXX TODO */ + break; + } + + return 0; +} + static const char * compatible[] = { "allwinner,sun50i-a64-codec-analog", NULL @@ -474,7 +501,11 @@ a64_acodec_attach(device_t parent, devic /* Right & Left Headphone PA enable */ a64_acodec_pr_set_clear(sc, A64_HP_CTRL, A64_HPPA_EN, 0); + /* Jack detect enable */ + a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL, + A64_JACKDETEN | A64_INNERRESEN | A64_AUTOPLEN, 0); + sc->sc_dai.dai_jack_detect = a64_acodec_dai_jack_detect; sc->sc_dai.dai_hw_if = &a64_acodec_hw_if; sc->sc_dai.dai_dev = self; sc->sc_dai.dai_priv = sc; Index: src/sys/arch/arm/sunxi/sun8i_codec.c diff -u src/sys/arch/arm/sunxi/sun8i_codec.c:1.1 src/sys/arch/arm/sunxi/sun8i_codec.c:1.2 --- src/sys/arch/arm/sunxi/sun8i_codec.c:1.1 Thu May 10 00:00:21 2018 +++ src/sys/arch/arm/sunxi/sun8i_codec.c Fri May 11 22:51:12 2018 @@ -1,4 +1,4 @@ -/* $NetBSD: sun8i_codec.c,v 1.1 2018/05/10 00:00:21 jmcneill Exp $ */ +/* $NetBSD: sun8i_codec.c,v 1.2 2018/05/11 22:51:12 jmcneill Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -27,7 +27,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sun8i_codec.c,v 1.1 2018/05/10 00:00:21 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sun8i_codec.c,v 1.2 2018/05/11 22:51:12 jmcneill Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -36,6 +36,7 @@ __KERNEL_RCSID(0, "$NetBSD: sun8i_codec. #include <sys/kmem.h> #include <sys/bitops.h> #include <sys/gpio.h> +#include <sys/workqueue.h> #include <dev/audio_dai.h> @@ -82,6 +83,21 @@ __KERNEL_RCSID(0, "$NetBSD: sun8i_codec. #define ADC_DIG_CTRL 0x100 #define ADC_DIG_CTRL_ENAD __BIT(15) +#define HMIC_CTRL1 0x110 +#define HMIC_CTRL1_N __BITS(11,8) +#define HMIC_CTRL1_JACK_IN_IRQ_EN __BIT(4) +#define HMIC_CTRL1_JACK_OUT_IRQ_EN __BIT(3) +#define HMIC_CTRL1_MIC_DET_IRQ_EN __BIT(0) + +#define HMIC_CTRL2 0x114 +#define HMIC_CTRL2_MDATA_THRES __BITS(12,8) + +#define HMIC_STS 0x118 +#define HMIC_STS_MIC_PRESENT __BIT(6) +#define HMIC_STS_JACK_DET_OIRQ __BIT(4) +#define HMIC_STS_JACK_DET_IIRQ __BIT(3) +#define HMIC_STS_MIC_DET_ST __BIT(0) + #define DAC_DIG_CTRL 0x120 #define DAC_DIG_CTRL_ENDA __BIT(15) @@ -97,7 +113,13 @@ struct sun8i_codec_softc { bus_space_handle_t sc_bsh; int sc_phandle; + struct workqueue *sc_workq; + struct work sc_work; + struct audio_dai_device sc_dai; + audio_dai_tag_t sc_codec_analog; + uint32_t sc_jackdet; + int sc_jackdet_pol; struct fdtbus_gpio_pin *sc_pin_pa; @@ -205,6 +227,91 @@ sun8i_codec_dai_set_format(audio_dai_tag return 0; } +static int +sun8i_codec_dai_add_device(audio_dai_tag_t dai, audio_dai_tag_t aux) +{ + struct sun8i_codec_softc * const sc = audio_dai_private(dai); + + if (sc->sc_codec_analog != NULL) + return 0; + + sc->sc_codec_analog = aux; + + return 0; +} + +static void +sun8i_codec_set_jackdet(struct sun8i_codec_softc *sc, bool enable) +{ + const uint32_t mask = + HMIC_CTRL1_JACK_IN_IRQ_EN | + HMIC_CTRL1_JACK_OUT_IRQ_EN | + HMIC_CTRL1_MIC_DET_IRQ_EN; + uint32_t val; + + val = RD4(sc, HMIC_CTRL1); + if (enable) + val |= mask; + else + val &= ~mask; + WR4(sc, HMIC_CTRL1, val); +} + +static int +sun8i_codec_intr(void *priv) +{ + struct sun8i_codec_softc * const sc = priv; + const uint32_t mask = + HMIC_STS_JACK_DET_OIRQ | + HMIC_STS_JACK_DET_IIRQ | + HMIC_STS_MIC_DET_ST; + + sc->sc_jackdet = RD4(sc, HMIC_STS); + + if (sc->sc_jackdet & mask) { + /* Disable jack detect IRQ until work is complete */ + sun8i_codec_set_jackdet(sc, false); + + /* Schedule pending jack detect task */ + workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL); + } + + WR4(sc, HMIC_STS, sc->sc_jackdet); + + return 1; +} + + +static void +sun8i_codec_thread(struct work *wk, void *priv) +{ + struct sun8i_codec_softc * const sc = priv; + const uint32_t sts = sc->sc_jackdet; + int hpdet = -1, micdet = -1; + + if (sc->sc_codec_analog) { + if (sts & HMIC_STS_JACK_DET_OIRQ) + hpdet = 0 ^ sc->sc_jackdet_pol; + else if (sts & HMIC_STS_JACK_DET_IIRQ) + hpdet = 1 ^ sc->sc_jackdet_pol; + + if (sts & HMIC_STS_MIC_DET_ST) + micdet = !!(sts & HMIC_STS_MIC_PRESENT); + + if (hpdet != -1) { + audio_dai_jack_detect(sc->sc_codec_analog, + AUDIO_DAI_JACK_HP, hpdet); + } + if (micdet != -1) { + audio_dai_jack_detect(sc->sc_codec_analog, + AUDIO_DAI_JACK_MIC, micdet); + } + } + + /* Re-enable jack detect IRQ */ + sun8i_codec_set_jackdet(sc, true); +} + static const char * compatible[] = { "allwinner,sun50i-a64-codec", NULL @@ -224,20 +331,29 @@ sun8i_codec_attach(device_t parent, devi struct sun8i_codec_softc * const sc = device_private(self); struct fdt_attach_args * const faa = aux; const int phandle = faa->faa_phandle; + char intrstr[128]; bus_addr_t addr; bus_size_t size; uint32_t val; + void *ih; - sc->sc_dev = self; if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { aprint_error(": couldn't get registers\n"); return; } + + if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { + aprint_error(": couldn't decode interrupt\n"); + return; + } + + sc->sc_dev = self; sc->sc_bst = faa->faa_bst; if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { aprint_error(": couldn't map registers\n"); return; } + sc->sc_jackdet_pol = 1; sc->sc_clk_gate = fdtbus_clock_get(phandle, "bus"); sc->sc_clk_mod = fdtbus_clock_get(phandle, "mod"); @@ -255,6 +371,12 @@ sun8i_codec_attach(device_t parent, devi aprint_naive("\n"); aprint_normal(": Audio Codec\n"); + if (workqueue_create(&sc->sc_workq, "jackdet", sun8i_codec_thread, + sc, PRI_NONE, IPL_VM, 0) != 0) { + aprint_error_dev(self, "couldn't create jackdet workqueue\n"); + return; + } + /* Enable clocks */ val = RD4(sc, SYSCLK_CTL); val |= AIF1CLK_ENA; @@ -301,7 +423,30 @@ sun8i_codec_attach(device_t parent, devi if (sc->sc_pin_pa) fdtbus_gpio_write(sc->sc_pin_pa, 1); + /* Enable jack detect */ + val = RD4(sc, HMIC_CTRL1); + val |= __SHIFTIN(0xff, HMIC_CTRL1_N); + WR4(sc, HMIC_CTRL1, val); + + val = RD4(sc, HMIC_CTRL2); + val &= ~HMIC_CTRL2_MDATA_THRES; + val |= __SHIFTIN(0x17, HMIC_CTRL2_MDATA_THRES); + WR4(sc, HMIC_CTRL2, val); + + /* Schedule initial jack detect task */ + workqueue_enqueue(sc->sc_workq, &sc->sc_work, NULL); + + ih = fdtbus_intr_establish(phandle, 0, IPL_VM, FDT_INTR_MPSAFE, + sun8i_codec_intr, sc); + if (ih == NULL) { + aprint_error_dev(self, "couldn't establish interrupt on %s\n", + intrstr); + return; + } + aprint_normal_dev(self, "interrupting on %s\n", intrstr); + sc->sc_dai.dai_set_format = sun8i_codec_dai_set_format; + sc->sc_dai.dai_add_device = sun8i_codec_dai_add_device; sc->sc_dai.dai_hw_if = &sun8i_codec_hw_if; sc->sc_dai.dai_dev = self; sc->sc_dai.dai_priv = sc;