Module Name: src
Committed By: jmcneill
Date: Sat Oct 7 21:53:16 UTC 2017
Modified Files:
src/sys/arch/arm/sunxi: files.sunxi sunxi_codec.c sunxi_codec.h
Added Files:
src/sys/arch/arm/sunxi: sun6i_a31_codec.c
Log Message:
Add A31 audio codec support.
To generate a diff of this commit:
cvs rdiff -u -r1.30 -r1.31 src/sys/arch/arm/sunxi/files.sunxi
cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/sunxi/sun6i_a31_codec.c
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/sunxi/sunxi_codec.c
cvs rdiff -u -r1.3 -r1.4 src/sys/arch/arm/sunxi/sunxi_codec.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/arch/arm/sunxi/files.sunxi
diff -u src/sys/arch/arm/sunxi/files.sunxi:1.30 src/sys/arch/arm/sunxi/files.sunxi:1.31
--- src/sys/arch/arm/sunxi/files.sunxi:1.30 Sat Oct 7 15:12:35 2017
+++ src/sys/arch/arm/sunxi/files.sunxi Sat Oct 7 21:53:16 2017
@@ -1,4 +1,4 @@
-# $NetBSD: files.sunxi,v 1.30 2017/10/07 15:12:35 jmcneill Exp $
+# $NetBSD: files.sunxi,v 1.31 2017/10/07 21:53:16 jmcneill Exp $
#
# Configuration info for Allwinner sunxi family SoCs
#
@@ -166,6 +166,7 @@ device sunxicodec: audiobus, auconv, mul
attach sunxicodec at fdt with sunxi_codec
file arch/arm/sunxi/sunxi_codec.c sunxi_codec
file arch/arm/sunxi/sun4i_a10_codec.c sunxi_codec
+file arch/arm/sunxi/sun6i_a31_codec.c sunxi_codec
# H3 Audio codec (analog part)
device h3codec
Index: src/sys/arch/arm/sunxi/sunxi_codec.c
diff -u src/sys/arch/arm/sunxi/sunxi_codec.c:1.2 src/sys/arch/arm/sunxi/sunxi_codec.c:1.3
--- src/sys/arch/arm/sunxi/sunxi_codec.c:1.2 Sun Aug 27 16:05:26 2017
+++ src/sys/arch/arm/sunxi/sunxi_codec.c Sat Oct 7 21:53:16 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_codec.c,v 1.2 2017/08/27 16:05:26 jmcneill Exp $ */
+/* $NetBSD: sunxi_codec.c,v 1.3 2017/10/07 21:53:16 jmcneill Exp $ */
/*-
* Copyright (c) 2014-2017 Jared McNeill <[email protected]>
@@ -29,7 +29,7 @@
#include "opt_ddb.h"
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sunxi_codec.c,v 1.2 2017/08/27 16:05:26 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sunxi_codec.c,v 1.3 2017/10/07 21:53:16 jmcneill Exp $");
#include <sys/param.h>
#include <sys/bus.h>
@@ -91,6 +91,7 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_codec.
static const struct of_compat_data compat_data[] = {
A10_CODEC_COMPATDATA,
+ A31_CODEC_COMPATDATA,
H3_CODEC_COMPATDATA,
{ NULL }
};
Index: src/sys/arch/arm/sunxi/sunxi_codec.h
diff -u src/sys/arch/arm/sunxi/sunxi_codec.h:1.3 src/sys/arch/arm/sunxi/sunxi_codec.h:1.4
--- src/sys/arch/arm/sunxi/sunxi_codec.h:1.3 Sat Oct 7 14:11:11 2017
+++ src/sys/arch/arm/sunxi/sunxi_codec.h Sat Oct 7 21:53:16 2017
@@ -1,4 +1,4 @@
-/* $NetBSD: sunxi_codec.h,v 1.3 2017/10/07 14:11:11 jmcneill Exp $ */
+/* $NetBSD: sunxi_codec.h,v 1.4 2017/10/07 21:53:16 jmcneill Exp $ */
/*-
* Copyright (c) 2014-2017 Jared McNeill <[email protected]>
@@ -131,4 +131,8 @@ extern const struct sunxi_codec_conf sun
{ "allwinner,sun4i-a10-codec", (uintptr_t)&sun4i_a10_codecconf }, \
{ "allwinner,sun7i-a20-codec", (uintptr_t)&sun4i_a10_codecconf }
+extern const struct sunxi_codec_conf sun6i_a31_codecconf;
+#define A31_CODEC_COMPATDATA \
+ { "allwinner,sun6i-a31-codec", (uintptr_t)&sun6i_a31_codecconf }
+
#endif /* !_ARM_SUNXI_CODEC_H */
Added files:
Index: src/sys/arch/arm/sunxi/sun6i_a31_codec.c
diff -u /dev/null src/sys/arch/arm/sunxi/sun6i_a31_codec.c:1.1
--- /dev/null Sat Oct 7 21:53:16 2017
+++ src/sys/arch/arm/sunxi/sun6i_a31_codec.c Sat Oct 7 21:53:16 2017
@@ -0,0 +1,310 @@
+/* $NetBSD: sun6i_a31_codec.c,v 1.1 2017/10/07 21:53:16 jmcneill Exp $ */
+
+/*-
+ * Copyright (c) 2014-2017 Jared McNeill <[email protected]>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD: sun6i_a31_codec.c,v 1.1 2017/10/07 21:53:16 jmcneill Exp $");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cpu.h>
+#include <sys/device.h>
+#include <sys/kmem.h>
+#include <sys/bitops.h>
+
+#include <sys/audioio.h>
+#include <dev/audio_if.h>
+
+#include <arm/sunxi/sunxi_codec.h>
+
+#define A31_DEFAULT_HPVOL 0x20
+
+#define OMIXER_DACA_CTRL 0x20
+#define DACAREN __BIT(31)
+#define DACALEN __BIT(30)
+#define RMIXEN __BIT(29)
+#define LMIXEN __BIT(28)
+#define RMIXMUTE __BITS(23,17)
+#define RMIXMUTE_MIC1 __BIT(23)
+#define RMIXMUTE_MIC2 __BIT(22)
+#define RMIXMUTE_PHONEP_PHONEN __BIT(21)
+#define RMIXMUTE_PHONEP __BIT(20)
+#define RMIXMUTE_LINEINR __BIT(19)
+#define RMIXMUTE_DACR __BIT(18)
+#define RMIXMUTE_DACL __BIT(17)
+#define LMIXMUTE __BITS(16,10)
+#define LMIXMUTE_MIC1 __BIT(16)
+#define LMIXMUTE_MIC2 __BIT(15)
+#define LMIXMUTE_PHONEP_PHONEN __BIT(14)
+#define LMIXMUTE_PHONEN __BIT(13)
+#define LMIXMUTE_LINEINL __BIT(12)
+#define LMIXMUTE_DACL __BIT(11)
+#define LMIXMUTE_DACR __BIT(10)
+#define RHPIS __BIT(9)
+#define LHPIS __BIT(8)
+#define RHPPAMUTE __BIT(7)
+#define LHPPAMUTE __BIT(6)
+#define HPVOL __BITS(5,0)
+
+#define OMIXER_PA_CTRL 0x24
+#define HPPAEN __BIT(31)
+#define HPCOM_CTL __BITS(30,29)
+#define COMPTEN __BIT(28)
+#define PA_ANTI_POP_CTRL __BITS(27,26)
+#define MIC1G __BITS(17,15)
+#define MIC2G __BITS(14,12)
+#define LINEING __BITS(11,9)
+#define PHONEG __BITS(8,6)
+#define PHONEPG __BITS(5,3)
+#define PHONENG __BITS(2,0)
+
+#define AC_MIC_CTRL 0x28
+#define HBIASEN __BIT(31)
+#define MBIASEN __BIT(30)
+#define HBIASADCEN __BIT(29)
+#define MIC1AMPEN __BIT(28)
+#define MIC1BOOST __BITS(27,25)
+#define MIC2AMPEN __BIT(24)
+#define MIC2BOOST __BITS(23,21)
+#define MIC2SLT __BIT(20)
+#define LINEOUTLEN __BIT(19)
+#define LINEOUTREN __BIT(18)
+#define LINEOUTLSRC __BIT(17)
+#define LINEOUTRSRC __BIT(16)
+#define LINEOUTVC __BITS(15,11)
+#define PHONEPREG __BITS(10,8)
+#define PHONEOUTG __BITS(7,5)
+#define PHONEOUTEN __BIT(4)
+#define PHONEOUTS0 __BIT(3)
+#define PHONEOUTS1 __BIT(2)
+#define PHONEOUTS2 __BIT(1)
+#define PHONEOUTS3 __BIT(0)
+
+#define AC_ADCA_CTRL 0x2c
+#define ADCREN __BIT(31)
+#define ADCLEN __BIT(30)
+#define ADCRG __BITS(29,27)
+#define ADCLG __BITS(26,24)
+#define RADCMIXMUTE __BITS(13,7)
+#define RADCMIXMUTE_MIC1 __BIT(13)
+#define RADCMIXMUTE_MIC2 __BIT(12)
+#define RADCMIXMUTE_PHONEP_PHONEN __BIT(11)
+#define RADCMIXMUTE_PHONEP __BIT(10)
+#define RADCMIXMUTE_LINEINR __BIT(9)
+#define RADCMIXMUTE_ROM __BIT(8)
+#define RADCMIXMUTE_LOM __BIT(7)
+#define LADCMIXMUTE __BITS(6,0)
+#define LADCMIXMUTE_MIC1 __BIT(6)
+#define LADCMIXMUTE_MIC2 __BIT(5)
+#define LADCMIXMUTE_PHONEP_PHONEN __BIT(4)
+#define LADCMIXMUTE_PHONEN __BIT(3)
+#define LADCMIXMUTE_LINEINL __BIT(2)
+#define LADCMIXMUTE_LOM __BIT(1)
+#define LADCMIXMUTE_ROM __BIT(0)
+
+enum a31_codec_mixer_ctrl {
+ A31_CODEC_OUTPUT_CLASS,
+ A31_CODEC_INPUT_CLASS,
+
+ A31_CODEC_OUTPUT_MASTER_VOLUME,
+ A31_CODEC_INPUT_DAC_VOLUME,
+
+ A31_CODEC_MIXER_CTRL_LAST
+};
+
+static const struct a31_codec_mixer {
+ const char * name;
+ enum a31_codec_mixer_ctrl mixer_class;
+ u_int reg;
+ u_int mask;
+} a31_codec_mixers[A31_CODEC_MIXER_CTRL_LAST] = {
+ [A31_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster,
+ A31_CODEC_OUTPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
+ [A31_CODEC_INPUT_DAC_VOLUME] = { AudioNdac,
+ A31_CODEC_INPUT_CLASS, OMIXER_DACA_CTRL, HPVOL },
+};
+
+#define RD4(sc, reg) \
+ bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
+#define WR4(sc, reg, val) \
+ bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
+#define SET4(sc, reg, mask) \
+ WR4((sc), (reg), RD4((sc), (reg)) | (mask))
+#define CLR4(sc, reg, mask) \
+ WR4((sc), (reg), RD4((sc), (reg)) & ~(mask))
+
+static int
+a31_codec_init(struct sunxi_codec_softc *sc)
+{
+
+ /* Disable HPCOM and HPCOM output protection */
+ CLR4(sc, OMIXER_PA_CTRL, HPCOM_CTL | COMPTEN);
+ /* Enable headphone power amp */
+ SET4(sc, OMIXER_PA_CTRL, HPPAEN);
+
+ /* Set headphone PA input to DAC */
+ CLR4(sc, OMIXER_DACA_CTRL, RHPIS | LHPIS);
+ /* Mute inputs to headphone PA */
+ CLR4(sc, OMIXER_DACA_CTRL, RHPPAMUTE | LHPPAMUTE);
+ /* Set initial volume */
+ CLR4(sc, OMIXER_DACA_CTRL, HPVOL);
+ SET4(sc, OMIXER_DACA_CTRL, __SHIFTIN(A31_DEFAULT_HPVOL, HPVOL));
+
+ /* Disable lineout */
+ CLR4(sc, AC_MIC_CTRL, LINEOUTLEN | LINEOUTREN | LINEOUTVC);
+ /* Enable headset microphone bias, current sensor, and ADC */
+ SET4(sc, AC_MIC_CTRL, HBIASEN | HBIASADCEN);
+
+ return 0;
+}
+
+static void
+a31_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
+{
+ if (mode == AUMODE_PLAY) {
+ if (sc->sc_pin_pa != NULL)
+ fdtbus_gpio_write(sc->sc_pin_pa, !mute);
+
+ if (mute) {
+ CLR4(sc, OMIXER_DACA_CTRL, DACAREN | DACALEN);
+ } else {
+ CLR4(sc, OMIXER_DACA_CTRL, RMIXMUTE | LMIXMUTE);
+ SET4(sc, OMIXER_DACA_CTRL,
+ LHPIS | RHPIS | LHPPAMUTE | RHPPAMUTE |
+ DACAREN | DACALEN | RMIXEN | LMIXEN |
+ RMIXMUTE_DACR | LMIXMUTE_DACL);
+ }
+ } else {
+ if (mute) {
+ CLR4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
+ } else {
+ SET4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN);
+ }
+ }
+}
+
+static int
+a31_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
+{
+ const struct a31_codec_mixer *mix;
+ u_int val, shift;
+ int nvol;
+
+ switch (mc->dev) {
+ case A31_CODEC_OUTPUT_MASTER_VOLUME:
+ case A31_CODEC_INPUT_DAC_VOLUME:
+ mix = &a31_codec_mixers[mc->dev];
+ val = RD4(sc, mix->reg);
+ shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
+ nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
+ val &= ~mix->mask;
+ val |= __SHIFTIN(nvol, mix->mask);
+ WR4(sc, mix->reg, val);
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+a31_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc)
+{
+ const struct a31_codec_mixer *mix;
+ u_int val, shift;
+ int nvol;
+
+ switch (mc->dev) {
+ case A31_CODEC_OUTPUT_MASTER_VOLUME:
+ case A31_CODEC_INPUT_DAC_VOLUME:
+ mix = &a31_codec_mixers[mc->dev];
+ val = RD4(sc, mix->reg);
+ shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
+ nvol = __SHIFTOUT(val, mix->mask) << shift;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
+ mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+static int
+a31_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di)
+{
+ const struct a31_codec_mixer *mix;
+
+ switch (di->index) {
+ case A31_CODEC_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 A31_CODEC_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 A31_CODEC_OUTPUT_MASTER_VOLUME:
+ case A31_CODEC_INPUT_DAC_VOLUME:
+ mix = &a31_codec_mixers[di->index];
+ di->mixer_class = mix->mixer_class;
+ strcpy(di->label.name, mix->name);
+ di->un.v.delta =
+ 256 / (__SHIFTOUT_MASK(mix->mask) + 1);
+ di->type = AUDIO_MIXER_VALUE;
+ di->next = di->prev = AUDIO_MIXER_LAST;
+ di->un.v.num_channels = 2;
+ strcpy(di->un.v.units.name, AudioNvolume);
+ return 0;
+ }
+
+ return ENXIO;
+}
+
+const struct sunxi_codec_conf sun6i_a31_codecconf = {
+ .name = "A31 Audio Codec",
+
+ .init = a31_codec_init,
+ .mute = a31_codec_mute,
+ .set_port = a31_codec_set_port,
+ .get_port = a31_codec_get_port,
+ .query_devinfo = a31_codec_query_devinfo,
+
+ .DPC = 0x00,
+ .DAC_FIFOC = 0x04,
+ .DAC_FIFOS = 0x08,
+ .DAC_TXDATA = 0x0c,
+ .ADC_FIFOC = 0x10,
+ .ADC_FIFOS = 0x14,
+ .ADC_RXDATA = 0x18,
+ .DAC_CNT = 0x40,
+ .ADC_CNT = 0x44,
+};