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 support for Allwinner H3 audio codec.
details: https://anonhg.NetBSD.org/src/rev/98eed202df9b
branches: trunk
changeset: 355622:98eed202df9b
user: jmcneill <jmcneill%NetBSD.org@localhost>
date: Sun Aug 06 17:15:45 2017 +0000
description:
Add support for Allwinner H3 audio codec.
diffstat:
sys/arch/arm/sunxi/files.sunxi | 12 +-
sys/arch/arm/sunxi/sun8i_h3_codec.c | 471 ++++++++++++++++++++++
sys/arch/arm/sunxi/sunxi_codec.c | 751 ++++++++++++++++++++++++++++++++++++
sys/arch/arm/sunxi/sunxi_codec.h | 129 ++++++
4 files changed, 1362 insertions(+), 1 deletions(-)
diffs (truncated from 1389 to 300 lines):
diff -r f8a46b7d18c0 -r 98eed202df9b sys/arch/arm/sunxi/files.sunxi
--- a/sys/arch/arm/sunxi/files.sunxi Sun Aug 06 17:14:37 2017 +0000
+++ b/sys/arch/arm/sunxi/files.sunxi Sun Aug 06 17:15:45 2017 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: files.sunxi,v 1.15 2017/08/05 17:51:49 jmcneill Exp $
+# $NetBSD: files.sunxi,v 1.16 2017/08/06 17:15:45 jmcneill Exp $
#
# Configuration info for Allwinner sunxi family SoCs
#
@@ -113,6 +113,16 @@
attach sun6idma at fdt with sun6i_dma
file arch/arm/sunxi/sun6i_dma.c sun6i_dma
+# Audio codec
+device sunxicodec: audiobus, auconv, mulaw, aurateconv
+attach sunxicodec at fdt with sunxi_codec
+file arch/arm/sunxi/sunxi_codec.c sunxi_codec
+
+# Audio codec (analog part)
+device h3codec
+attach h3codec at fdt with h3_codec
+file arch/arm/sunxi/sun8i_h3_codec.c h3_codec needs-flag
+
# SOC parameters
defflag opt_soc.h SOC_SUNXI
defflag opt_soc.h SOC_SUN8I: SOC_SUNXI
diff -r f8a46b7d18c0 -r 98eed202df9b sys/arch/arm/sunxi/sun8i_h3_codec.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/arch/arm/sunxi/sun8i_h3_codec.c Sun Aug 06 17:15:45 2017 +0000
@@ -0,0 +1,471 @@
+/* $NetBSD: sun8i_h3_codec.c,v 1.1 2017/08/06 17:15:45 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: sun8i_h3_codec.c,v 1.1 2017/08/06 17:15:45 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 H3_PR_CFG 0x00
+#define H3_AC_PR_RW __BIT(24)
+#define H3_AC_PR_RST __BIT(18)
+#define H3_AC_PR_ADDR __BITS(20,16)
+#define H3_ACDA_PR_WDAT __BITS(15,8)
+#define H3_ACDA_PR_RDAT __BITS(7,0)
+
+#define H3_LOMIXSC 0x01
+#define H3_LOMIXSC_LDAC __BIT(1)
+#define H3_ROMIXSC 0x02
+#define H3_ROMIXSC_RDAC __BIT(1)
+#define H3_DAC_PA_SRC 0x03
+#define H3_DACAREN __BIT(7)
+#define H3_DACALEN __BIT(6)
+#define H3_RMIXEN __BIT(5)
+#define H3_LMIXEN __BIT(4)
+#define H3_LINEIN_GCTR 0x05
+#define H3_LINEING __BITS(6,4)
+#define H3_MIC_GCTR 0x06
+#define H3_MIC1_GAIN __BITS(6,4)
+#define H3_MIC2_GAIN __BITS(2,0)
+#define H3_PAEN_CTR 0x07
+#define H3_LINEOUTEN __BIT(7)
+#define H3_LINEOUT_VOLC 0x09
+#define H3_LINEOUTVOL __BITS(7,3)
+#define H3_MIC2G_LINEOUT_CTR 0x0a
+#define H3_LINEOUT_LSEL __BIT(3)
+#define H3_LINEOUT_RSEL __BIT(2)
+#define H3_LADCMIXSC 0x0c
+#define H3_RADCMIXSC 0x0d
+#define H3_ADCMIXSC_MIC1 __BIT(6)
+#define H3_ADCMIXSC_MIC2 __BIT(5)
+#define H3_ADCMIXSC_LINEIN __BIT(2)
+#define H3_ADCMIXSC_OMIXER __BITS(1,0)
+#define H3_ADC_AP_EN 0x0f
+#define H3_ADCREN __BIT(7)
+#define H3_ADCLEN __BIT(6)
+#define H3_ADCG __BITS(2,0)
+
+struct h3_codec_softc {
+ device_t sc_dev;
+ bus_space_tag_t sc_bst;
+ bus_space_handle_t sc_bsh;
+ int sc_phandle;
+};
+
+enum h3_codec_mixer_ctrl {
+ H3_CODEC_OUTPUT_CLASS,
+ H3_CODEC_INPUT_CLASS,
+ H3_CODEC_RECORD_CLASS,
+
+ H3_CODEC_OUTPUT_MASTER_VOLUME,
+ H3_CODEC_INPUT_DAC_VOLUME,
+ H3_CODEC_INPUT_LINEIN_VOLUME,
+ H3_CODEC_INPUT_MIC1_VOLUME,
+ H3_CODEC_INPUT_MIC2_VOLUME,
+ H3_CODEC_RECORD_AGC_VOLUME,
+ H3_CODEC_RECORD_SOURCE,
+
+ H3_CODEC_MIXER_CTRL_LAST
+};
+
+static const struct h3_codec_mixer {
+ const char * name;
+ enum h3_codec_mixer_ctrl mixer_class;
+ u_int reg;
+ u_int mask;
+} h3_codec_mixers[H3_CODEC_MIXER_CTRL_LAST] = {
+ [H3_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster,
+ H3_CODEC_OUTPUT_CLASS, H3_LINEOUT_VOLC, H3_LINEOUTVOL },
+ [H3_CODEC_INPUT_DAC_VOLUME] = { AudioNdac,
+ H3_CODEC_INPUT_CLASS, H3_LINEOUT_VOLC, H3_LINEOUTVOL },
+ [H3_CODEC_INPUT_LINEIN_VOLUME] = { AudioNline,
+ H3_CODEC_INPUT_CLASS, H3_LINEIN_GCTR, H3_LINEING },
+ [H3_CODEC_INPUT_MIC1_VOLUME] = { "mic1",
+ H3_CODEC_INPUT_CLASS, H3_MIC_GCTR, H3_MIC1_GAIN },
+ [H3_CODEC_INPUT_MIC2_VOLUME] = { "mic2",
+ H3_CODEC_INPUT_CLASS, H3_MIC_GCTR, H3_MIC2_GAIN },
+ [H3_CODEC_RECORD_AGC_VOLUME] = { AudioNagc,
+ H3_CODEC_RECORD_CLASS, H3_ADC_AP_EN, H3_ADCG },
+};
+
+#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))
+
+static struct h3_codec_softc *
+h3_codec_find(int phandle)
+{
+ struct h3_codec_softc *csc;
+ device_t dev;
+
+ dev = device_find_by_driver_unit("h3codec", 0);
+ if (dev == NULL)
+ return NULL;
+ csc = device_private(dev);
+ if (csc->sc_phandle != phandle)
+ return NULL;
+
+ return csc;
+}
+
+static u_int
+h3_codec_pr_read(struct h3_codec_softc *csc, u_int addr)
+{
+ uint32_t val;
+
+ /* Read current value */
+ val = RD4(csc, H3_PR_CFG);
+
+ /* De-assert reset */
+ val |= H3_AC_PR_RST;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Read mode */
+ val &= ~H3_AC_PR_RW;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Set address */
+ val &= ~H3_AC_PR_ADDR;
+ val |= __SHIFTIN(addr, H3_AC_PR_ADDR);
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Read data */
+ return __SHIFTOUT(RD4(csc, H3_PR_CFG), H3_ACDA_PR_RDAT);
+}
+
+static void
+h3_codec_pr_write(struct h3_codec_softc *csc, u_int addr, u_int data)
+{
+ uint32_t val;
+
+ /* Read current value */
+ val = RD4(csc, H3_PR_CFG);
+
+ /* De-assert reset */
+ val |= H3_AC_PR_RST;
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Set address */
+ val &= ~H3_AC_PR_ADDR;
+ val |= __SHIFTIN(addr, H3_AC_PR_ADDR);
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Write data */
+ val &= ~H3_ACDA_PR_WDAT;
+ val |= __SHIFTIN(data, H3_ACDA_PR_WDAT);
+ WR4(csc, H3_PR_CFG, val);
+
+ /* Write mode */
+ val |= H3_AC_PR_RW;
+ WR4(csc, H3_PR_CFG, val);
+}
+
+static void
+h3_codec_pr_set_clear(struct h3_codec_softc *csc, u_int addr, u_int set, u_int clr)
+{
+ u_int old, new;
+
+ old = h3_codec_pr_read(csc, addr);
+ new = set | (old & ~clr);
+ h3_codec_pr_write(csc, addr, new);
+}
+
+static int
+h3_codec_init(struct sunxi_codec_softc *sc)
+{
+ struct h3_codec_softc *csc;
+ int phandle;
+
+ /* Lookup the codec analog controls phandle */
+ phandle = fdtbus_get_phandle(sc->sc_phandle,
+ "allwinner,codec-analog-controls");
+ if (phandle < 0) {
+ aprint_error_dev(sc->sc_dev,
+ "missing allwinner,codec-analog-controls property\n");
+ return ENXIO;
+ }
+
+ /* Find a matching h3codec instance */
+ sc->sc_codec_priv = h3_codec_find(phandle);
+ if (sc->sc_codec_priv == NULL) {
+ aprint_error_dev(sc->sc_dev, "couldn't find codec analog controls\n");
+ return ENOENT;
+ }
+ csc = sc->sc_codec_priv;
+
+ /* Right & Left LINEOUT enable */
+ h3_codec_pr_set_clear(csc, H3_PAEN_CTR, H3_LINEOUTEN, 0);
+ h3_codec_pr_set_clear(csc, H3_MIC2G_LINEOUT_CTR,
+ H3_LINEOUT_LSEL | H3_LINEOUT_RSEL, 0);
+
+ return 0;
+}
+
+static void
+h3_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode)
+{
+ struct h3_codec_softc * const csc = sc->sc_codec_priv;
+
+ if (mode == AUMODE_PLAY) {
+ if (mute) {
+ /* Mute DAC l/r channels to output mixer */
+ h3_codec_pr_set_clear(csc, H3_LOMIXSC,
+ 0, H3_LOMIXSC_LDAC);
+ h3_codec_pr_set_clear(csc, H3_ROMIXSC,
+ 0, H3_ROMIXSC_RDAC);
+ /* Disable DAC analog l/r channels and output mixer */
+ h3_codec_pr_set_clear(csc, H3_DAC_PA_SRC,
+ 0, H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN);
+ } else {
+ /* Enable DAC analog l/r channels and output mixer */
+ h3_codec_pr_set_clear(csc, H3_DAC_PA_SRC,
+ H3_DACAREN | H3_DACALEN | H3_RMIXEN | H3_LMIXEN, 0);
+ /* Unmute DAC l/r channels to output mixer */
+ h3_codec_pr_set_clear(csc, H3_LOMIXSC, H3_LOMIXSC_LDAC, 0);
+ h3_codec_pr_set_clear(csc, H3_ROMIXSC, H3_ROMIXSC_RDAC, 0);
+ }
+ } else {
+ if (mute) {
+ /* Disable ADC analog l/r channels */
+ h3_codec_pr_set_clear(csc, H3_ADC_AP_EN,
+ 0, H3_ADCREN | H3_ADCLEN);
+ } else {
+ /* Enable ADC analog l/r channels */
+ h3_codec_pr_set_clear(csc, H3_ADC_AP_EN,
Home |
Main Index |
Thread Index |
Old Index