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/9d37cc117d4e
branches:  trunk
changeset: 825923:9d37cc117d4e
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 18819a0db4a5 -r 9d37cc117d4e 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 18819a0db4a5 -r 9d37cc117d4e 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