1*b950503fSisaki /* $NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki Exp $ */
29f2e809cSjmcneill
39f2e809cSjmcneill /*-
49f2e809cSjmcneill * Copyright (c) 2014-2017 Jared McNeill <jmcneill@invisible.ca>
59f2e809cSjmcneill * All rights reserved.
69f2e809cSjmcneill *
79f2e809cSjmcneill * Redistribution and use in source and binary forms, with or without
89f2e809cSjmcneill * modification, are permitted provided that the following conditions
99f2e809cSjmcneill * are met:
109f2e809cSjmcneill * 1. Redistributions of source code must retain the above copyright
119f2e809cSjmcneill * notice, this list of conditions and the following disclaimer.
129f2e809cSjmcneill * 2. Redistributions in binary form must reproduce the above copyright
139f2e809cSjmcneill * notice, this list of conditions and the following disclaimer in the
149f2e809cSjmcneill * documentation and/or other materials provided with the distribution.
159f2e809cSjmcneill *
169f2e809cSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
179f2e809cSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
189f2e809cSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
199f2e809cSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
209f2e809cSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
219f2e809cSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
229f2e809cSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
239f2e809cSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
249f2e809cSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
259f2e809cSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
269f2e809cSjmcneill * SUCH DAMAGE.
279f2e809cSjmcneill */
289f2e809cSjmcneill
299f2e809cSjmcneill #include <sys/cdefs.h>
30*b950503fSisaki __KERNEL_RCSID(0, "$NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki Exp $");
319f2e809cSjmcneill
329f2e809cSjmcneill #include <sys/param.h>
339f2e809cSjmcneill #include <sys/bus.h>
349f2e809cSjmcneill #include <sys/cpu.h>
359f2e809cSjmcneill #include <sys/device.h>
369f2e809cSjmcneill #include <sys/kmem.h>
379f2e809cSjmcneill #include <sys/bitops.h>
389f2e809cSjmcneill
399f2e809cSjmcneill #include <sys/audioio.h>
40*b950503fSisaki #include <dev/audio/audio_if.h>
419f2e809cSjmcneill
429f2e809cSjmcneill #include <arm/sunxi/sunxi_codec.h>
439f2e809cSjmcneill
449f2e809cSjmcneill #define A31_DEFAULT_HPVOL 0x20
459f2e809cSjmcneill
469f2e809cSjmcneill #define OMIXER_DACA_CTRL 0x20
479f2e809cSjmcneill #define DACAREN __BIT(31)
489f2e809cSjmcneill #define DACALEN __BIT(30)
499f2e809cSjmcneill #define RMIXEN __BIT(29)
509f2e809cSjmcneill #define LMIXEN __BIT(28)
519f2e809cSjmcneill #define RMIXMUTE __BITS(23,17)
529f2e809cSjmcneill #define RMIXMUTE_MIC1 __BIT(23)
539f2e809cSjmcneill #define RMIXMUTE_MIC2 __BIT(22)
549f2e809cSjmcneill #define RMIXMUTE_PHONEP_PHONEN __BIT(21)
559f2e809cSjmcneill #define RMIXMUTE_PHONEP __BIT(20)
569f2e809cSjmcneill #define RMIXMUTE_LINEINR __BIT(19)
579f2e809cSjmcneill #define RMIXMUTE_DACR __BIT(18)
589f2e809cSjmcneill #define RMIXMUTE_DACL __BIT(17)
599f2e809cSjmcneill #define LMIXMUTE __BITS(16,10)
609f2e809cSjmcneill #define LMIXMUTE_MIC1 __BIT(16)
619f2e809cSjmcneill #define LMIXMUTE_MIC2 __BIT(15)
629f2e809cSjmcneill #define LMIXMUTE_PHONEP_PHONEN __BIT(14)
639f2e809cSjmcneill #define LMIXMUTE_PHONEN __BIT(13)
649f2e809cSjmcneill #define LMIXMUTE_LINEINL __BIT(12)
659f2e809cSjmcneill #define LMIXMUTE_DACL __BIT(11)
669f2e809cSjmcneill #define LMIXMUTE_DACR __BIT(10)
679f2e809cSjmcneill #define RHPIS __BIT(9)
689f2e809cSjmcneill #define LHPIS __BIT(8)
699f2e809cSjmcneill #define RHPPAMUTE __BIT(7)
709f2e809cSjmcneill #define LHPPAMUTE __BIT(6)
719f2e809cSjmcneill #define HPVOL __BITS(5,0)
729f2e809cSjmcneill
739f2e809cSjmcneill #define OMIXER_PA_CTRL 0x24
749f2e809cSjmcneill #define HPPAEN __BIT(31)
759f2e809cSjmcneill #define HPCOM_CTL __BITS(30,29)
769f2e809cSjmcneill #define COMPTEN __BIT(28)
779f2e809cSjmcneill #define PA_ANTI_POP_CTRL __BITS(27,26)
789f2e809cSjmcneill #define MIC1G __BITS(17,15)
799f2e809cSjmcneill #define MIC2G __BITS(14,12)
809f2e809cSjmcneill #define LINEING __BITS(11,9)
819f2e809cSjmcneill #define PHONEG __BITS(8,6)
829f2e809cSjmcneill #define PHONEPG __BITS(5,3)
839f2e809cSjmcneill #define PHONENG __BITS(2,0)
849f2e809cSjmcneill
859f2e809cSjmcneill #define AC_MIC_CTRL 0x28
869f2e809cSjmcneill #define HBIASEN __BIT(31)
879f2e809cSjmcneill #define MBIASEN __BIT(30)
889f2e809cSjmcneill #define HBIASADCEN __BIT(29)
899f2e809cSjmcneill #define MIC1AMPEN __BIT(28)
909f2e809cSjmcneill #define MIC1BOOST __BITS(27,25)
919f2e809cSjmcneill #define MIC2AMPEN __BIT(24)
929f2e809cSjmcneill #define MIC2BOOST __BITS(23,21)
939f2e809cSjmcneill #define MIC2SLT __BIT(20)
949f2e809cSjmcneill #define LINEOUTLEN __BIT(19)
959f2e809cSjmcneill #define LINEOUTREN __BIT(18)
969f2e809cSjmcneill #define LINEOUTLSRC __BIT(17)
979f2e809cSjmcneill #define LINEOUTRSRC __BIT(16)
989f2e809cSjmcneill #define LINEOUTVC __BITS(15,11)
999f2e809cSjmcneill #define PHONEPREG __BITS(10,8)
1009f2e809cSjmcneill #define PHONEOUTG __BITS(7,5)
1019f2e809cSjmcneill #define PHONEOUTEN __BIT(4)
1029f2e809cSjmcneill #define PHONEOUTS0 __BIT(3)
1039f2e809cSjmcneill #define PHONEOUTS1 __BIT(2)
1049f2e809cSjmcneill #define PHONEOUTS2 __BIT(1)
1059f2e809cSjmcneill #define PHONEOUTS3 __BIT(0)
1069f2e809cSjmcneill
1079f2e809cSjmcneill #define AC_ADCA_CTRL 0x2c
1089f2e809cSjmcneill #define ADCREN __BIT(31)
1099f2e809cSjmcneill #define ADCLEN __BIT(30)
1109f2e809cSjmcneill #define ADCRG __BITS(29,27)
1119f2e809cSjmcneill #define ADCLG __BITS(26,24)
1129f2e809cSjmcneill #define RADCMIXMUTE __BITS(13,7)
1139f2e809cSjmcneill #define RADCMIXMUTE_MIC1 __BIT(13)
1149f2e809cSjmcneill #define RADCMIXMUTE_MIC2 __BIT(12)
1159f2e809cSjmcneill #define RADCMIXMUTE_PHONEP_PHONEN __BIT(11)
1169f2e809cSjmcneill #define RADCMIXMUTE_PHONEP __BIT(10)
1179f2e809cSjmcneill #define RADCMIXMUTE_LINEINR __BIT(9)
1189f2e809cSjmcneill #define RADCMIXMUTE_ROM __BIT(8)
1199f2e809cSjmcneill #define RADCMIXMUTE_LOM __BIT(7)
1209f2e809cSjmcneill #define LADCMIXMUTE __BITS(6,0)
1219f2e809cSjmcneill #define LADCMIXMUTE_MIC1 __BIT(6)
1229f2e809cSjmcneill #define LADCMIXMUTE_MIC2 __BIT(5)
1239f2e809cSjmcneill #define LADCMIXMUTE_PHONEP_PHONEN __BIT(4)
1249f2e809cSjmcneill #define LADCMIXMUTE_PHONEN __BIT(3)
1259f2e809cSjmcneill #define LADCMIXMUTE_LINEINL __BIT(2)
1269f2e809cSjmcneill #define LADCMIXMUTE_LOM __BIT(1)
1279f2e809cSjmcneill #define LADCMIXMUTE_ROM __BIT(0)
1289f2e809cSjmcneill
1299f2e809cSjmcneill enum a31_codec_mixer_ctrl {
1309f2e809cSjmcneill A31_CODEC_OUTPUT_CLASS,
1319f2e809cSjmcneill A31_CODEC_INPUT_CLASS,
1329f2e809cSjmcneill
1339f2e809cSjmcneill A31_CODEC_OUTPUT_MASTER_VOLUME,
1349f2e809cSjmcneill A31_CODEC_INPUT_DAC_VOLUME,
1359f2e809cSjmcneill
1369f2e809cSjmcneill A31_CODEC_MIXER_CTRL_LAST
1379f2e809cSjmcneill };
1389f2e809cSjmcneill
1399f2e809cSjmcneill static const struct a31_codec_mixer {
1409f2e809cSjmcneill const char * name;
1419f2e809cSjmcneill enum a31_codec_mixer_ctrl mixer_class;
1429f2e809cSjmcneill u_int reg;
1439f2e809cSjmcneill u_int mask;
1449f2e809cSjmcneill } a31_codec_mixers[A31_CODEC_MIXER_CTRL_LAST] = {
1459f2e809cSjmcneill [A31_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster,
1469f2e809cSjmcneill A31_CODEC_OUTPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
1479f2e809cSjmcneill [A31_CODEC_INPUT_DAC_VOLUME] = { AudioNdac,
1489f2e809cSjmcneill A31_CODEC_INPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
1499f2e809cSjmcneill };
1509f2e809cSjmcneill
1519f2e809cSjmcneill #define RD4(sc, reg) \
1529f2e809cSjmcneill bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
1539f2e809cSjmcneill #define WR4(sc, reg, val) \
1549f2e809cSjmcneill bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
1559f2e809cSjmcneill #define SET4(sc, reg, mask) \
1569f2e809cSjmcneill WR4((sc), (reg), RD4((sc), (reg)) | (mask))
1579f2e809cSjmcneill #define CLR4(sc, reg, mask) \
1589f2e809cSjmcneill WR4((sc), (reg), RD4((sc), (reg)) & ~(mask))
1599f2e809cSjmcneill
1609f2e809cSjmcneill static int
a31_codec_init(struct sunxi_codec_softc * sc)1619f2e809cSjmcneill a31_codec_init(struct sunxi_codec_softc *sc)
1629f2e809cSjmcneill {
1639f2e809cSjmcneill
1649f2e809cSjmcneill /* Disable HPCOM and HPCOM output protection */
1659f2e809cSjmcneill CLR4(sc, OMIXER_PA_CTRL, HPCOM_CTL | COMPTEN);
1669f2e809cSjmcneill /* Enable headphone power amp */
1679f2e809cSjmcneill SET4(sc, OMIXER_PA_CTRL, HPPAEN);
1689f2e809cSjmcneill
1699f2e809cSjmcneill /* Set headphone PA input to DAC */
1709f2e809cSjmcneill CLR4(sc, OMIXER_DACA_CTRL, RHPIS | LHPIS);
1719f2e809cSjmcneill /* Mute inputs to headphone PA */
1729f2e809cSjmcneill CLR4(sc, OMIXER_DACA_CTRL, RHPPAMUTE | LHPPAMUTE);
1739f2e809cSjmcneill /* Set initial volume */
1749f2e809cSjmcneill CLR4(sc, OMIXER_DACA_CTRL, HPVOL);
1759f2e809cSjmcneill SET4(sc, OMIXER_DACA_CTRL, __SHIFTIN(A31_DEFAULT_HPVOL, HPVOL));
1769f2e809cSjmcneill
1779f2e809cSjmcneill /* Disable lineout */
1789f2e809cSjmcneill CLR4(sc, AC_MIC_CTRL, LINEOUTLEN | LINEOUTREN | LINEOUTVC);
1799f2e809cSjmcneill /* Enable headset microphone bias, current sensor, and ADC */
1809f2e809cSjmcneill SET4(sc, AC_MIC_CTRL, HBIASEN | HBIASADCEN);
1819f2e809cSjmcneill
1829f2e809cSjmcneill return 0;
1839f2e809cSjmcneill }
1849f2e809cSjmcneill
1859f2e809cSjmcneill static void
a31_codec_mute(struct sunxi_codec_softc * sc,int mute,u_int mode)1869f2e809cSjmcneill a31_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
1879f2e809cSjmcneill {
1889f2e809cSjmcneill if (mode == AUMODE_PLAY) {
1899f2e809cSjmcneill if (sc->sc_pin_pa != NULL)
1909f2e809cSjmcneill fdtbus_gpio_write(sc->sc_pin_pa, !mute);
1919f2e809cSjmcneill
1929f2e809cSjmcneill if (mute) {
1939f2e809cSjmcneill CLR4(sc, OMIXER_DACA_CTRL, DACAREN | DACALEN);
1949f2e809cSjmcneill } else {
1959f2e809cSjmcneill CLR4(sc, OMIXER_DACA_CTRL, RMIXMUTE | LMIXMUTE);
1969f2e809cSjmcneill SET4(sc, OMIXER_DACA_CTRL,
1979f2e809cSjmcneill LHPIS | RHPIS | LHPPAMUTE | RHPPAMUTE |
1989f2e809cSjmcneill DACAREN | DACALEN | RMIXEN | LMIXEN |
1999f2e809cSjmcneill RMIXMUTE_DACR | LMIXMUTE_DACL);
2009f2e809cSjmcneill }
2019f2e809cSjmcneill } else {
2029f2e809cSjmcneill if (mute) {
2039f2e809cSjmcneill CLR4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
2049f2e809cSjmcneill } else {
2059f2e809cSjmcneill SET4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
2069f2e809cSjmcneill }
2079f2e809cSjmcneill }
2089f2e809cSjmcneill }
2099f2e809cSjmcneill
2109f2e809cSjmcneill static int
a31_codec_set_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)2119f2e809cSjmcneill a31_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
2129f2e809cSjmcneill {
2139f2e809cSjmcneill const struct a31_codec_mixer *mix;
2149f2e809cSjmcneill u_int val, shift;
2159f2e809cSjmcneill int nvol;
2169f2e809cSjmcneill
2179f2e809cSjmcneill switch (mc->dev) {
2189f2e809cSjmcneill case A31_CODEC_OUTPUT_MASTER_VOLUME:
2199f2e809cSjmcneill case A31_CODEC_INPUT_DAC_VOLUME:
2209f2e809cSjmcneill mix = &a31_codec_mixers[mc->dev];
2219f2e809cSjmcneill val = RD4(sc, mix->reg);
2229f2e809cSjmcneill shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
2239f2e809cSjmcneill nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
2249f2e809cSjmcneill val &= ~mix->mask;
2259f2e809cSjmcneill val |= __SHIFTIN(nvol, mix->mask);
2269f2e809cSjmcneill WR4(sc, mix->reg, val);
2279f2e809cSjmcneill return 0;
2289f2e809cSjmcneill }
2299f2e809cSjmcneill
2309f2e809cSjmcneill return ENXIO;
2319f2e809cSjmcneill }
2329f2e809cSjmcneill
2339f2e809cSjmcneill static int
a31_codec_get_port(struct sunxi_codec_softc * sc,mixer_ctrl_t * mc)2349f2e809cSjmcneill a31_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
2359f2e809cSjmcneill {
2369f2e809cSjmcneill const struct a31_codec_mixer *mix;
2379f2e809cSjmcneill u_int val, shift;
2389f2e809cSjmcneill int nvol;
2399f2e809cSjmcneill
2409f2e809cSjmcneill switch (mc->dev) {
2419f2e809cSjmcneill case A31_CODEC_OUTPUT_MASTER_VOLUME:
2429f2e809cSjmcneill case A31_CODEC_INPUT_DAC_VOLUME:
2439f2e809cSjmcneill mix = &a31_codec_mixers[mc->dev];
2449f2e809cSjmcneill val = RD4(sc, mix->reg);
2459f2e809cSjmcneill shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
2469f2e809cSjmcneill nvol = __SHIFTOUT(val, mix->mask) << shift;
2479f2e809cSjmcneill mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
2489f2e809cSjmcneill mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
2499f2e809cSjmcneill return 0;
2509f2e809cSjmcneill }
2519f2e809cSjmcneill
2529f2e809cSjmcneill return ENXIO;
2539f2e809cSjmcneill }
2549f2e809cSjmcneill
2559f2e809cSjmcneill static int
a31_codec_query_devinfo(struct sunxi_codec_softc * sc,mixer_devinfo_t * di)2569f2e809cSjmcneill a31_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
2579f2e809cSjmcneill {
2589f2e809cSjmcneill const struct a31_codec_mixer *mix;
2599f2e809cSjmcneill
2609f2e809cSjmcneill switch (di->index) {
2619f2e809cSjmcneill case A31_CODEC_OUTPUT_CLASS:
2629f2e809cSjmcneill di->mixer_class = di->index;
2639f2e809cSjmcneill strcpy(di->label.name, AudioCoutputs);
2649f2e809cSjmcneill di->type = AUDIO_MIXER_CLASS;
2659f2e809cSjmcneill di->next = di->prev = AUDIO_MIXER_LAST;
2669f2e809cSjmcneill return 0;
2679f2e809cSjmcneill
2689f2e809cSjmcneill case A31_CODEC_INPUT_CLASS:
2699f2e809cSjmcneill di->mixer_class = di->index;
2709f2e809cSjmcneill strcpy(di->label.name, AudioCinputs);
2719f2e809cSjmcneill di->type = AUDIO_MIXER_CLASS;
2729f2e809cSjmcneill di->next = di->prev = AUDIO_MIXER_LAST;
2739f2e809cSjmcneill return 0;
2749f2e809cSjmcneill
2759f2e809cSjmcneill case A31_CODEC_OUTPUT_MASTER_VOLUME:
2769f2e809cSjmcneill case A31_CODEC_INPUT_DAC_VOLUME:
2779f2e809cSjmcneill mix = &a31_codec_mixers[di->index];
2789f2e809cSjmcneill di->mixer_class = mix->mixer_class;
2799f2e809cSjmcneill strcpy(di->label.name, mix->name);
2809f2e809cSjmcneill di->un.v.delta =
2819f2e809cSjmcneill 256 / (__SHIFTOUT_MASK(mix->mask) + 1);
2829f2e809cSjmcneill di->type = AUDIO_MIXER_VALUE;
2839f2e809cSjmcneill di->next = di->prev = AUDIO_MIXER_LAST;
2849f2e809cSjmcneill di->un.v.num_channels = 2;
2859f2e809cSjmcneill strcpy(di->un.v.units.name, AudioNvolume);
2869f2e809cSjmcneill return 0;
2879f2e809cSjmcneill }
2889f2e809cSjmcneill
2899f2e809cSjmcneill return ENXIO;
2909f2e809cSjmcneill }
2919f2e809cSjmcneill
2929f2e809cSjmcneill const struct sunxi_codec_conf sun6i_a31_codecconf = {
2939f2e809cSjmcneill .name = "A31 Audio Codec",
2949f2e809cSjmcneill
2959f2e809cSjmcneill .init = a31_codec_init,
2969f2e809cSjmcneill .mute = a31_codec_mute,
2979f2e809cSjmcneill .set_port = a31_codec_set_port,
2989f2e809cSjmcneill .get_port = a31_codec_get_port,
2999f2e809cSjmcneill .query_devinfo = a31_codec_query_devinfo,
3009f2e809cSjmcneill
3019f2e809cSjmcneill .DPC = 0x00,
3029f2e809cSjmcneill .DAC_FIFOC = 0x04,
3039f2e809cSjmcneill .DAC_FIFOS = 0x08,
3049f2e809cSjmcneill .DAC_TXDATA = 0x0c,
3059f2e809cSjmcneill .ADC_FIFOC = 0x10,
3069f2e809cSjmcneill .ADC_FIFOS = 0x14,
3079f2e809cSjmcneill .ADC_RXDATA = 0x18,
3089f2e809cSjmcneill .DAC_CNT = 0x40,
3099f2e809cSjmcneill .ADC_CNT = 0x44,
3109f2e809cSjmcneill };
311