Source-Changes-HG archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
[src/trunk]: src/sys/arch/arm/sunxi Add A31 audio codec support.
details: https://anonhg.NetBSD.org/src/rev/e150caac3104
branches: trunk
changeset: 356680:e150caac3104
user: jmcneill <jmcneill%NetBSD.org@localhost>
date: Sat Oct 07 21:53:16 2017 +0000
description:
Add A31 audio codec support.
diffstat:
sys/arch/arm/sunxi/files.sunxi | 3 +-
sys/arch/arm/sunxi/sun6i_a31_codec.c | 310 +++++++++++++++++++++++++++++++++++
sys/arch/arm/sunxi/sunxi_codec.c | 5 +-
sys/arch/arm/sunxi/sunxi_codec.h | 6 +-
4 files changed, 320 insertions(+), 4 deletions(-)
diffs (truncated from 375 to 300 lines):
diff -r bb55e48c2b7d -r e150caac3104 sys/arch/arm/sunxi/files.sunxi
--- a/sys/arch/arm/sunxi/files.sunxi Sat Oct 07 21:52:53 2017 +0000
+++ b/sys/arch/arm/sunxi/files.sunxi Sat Oct 07 21:53:16 2017 +0000
@@ -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 @@
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
diff -r bb55e48c2b7d -r e150caac3104 sys/arch/arm/sunxi/sun6i_a31_codec.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/arch/arm/sunxi/sun6i_a31_codec.c Sat Oct 07 21:53:16 2017 +0000
@@ -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 <jmcneill%invisible.ca@localhost>
+ * 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);
Home |
Main Index |
Thread Index |
Old Index