Home | History | Annotate | Line # | Download | only in sunxi
      1 /* $NetBSD: sun50i_a64_acodec.c,v 1.10 2021/01/27 03:10:20 thorpej Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: sun50i_a64_acodec.c,v 1.10 2021/01/27 03:10:20 thorpej Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/bus.h>
     34 #include <sys/cpu.h>
     35 #include <sys/device.h>
     36 #include <sys/kmem.h>
     37 #include <sys/bitops.h>
     38 
     39 #include <dev/audio/audio_dai.h>
     40 
     41 #include <dev/fdt/fdtvar.h>
     42 
     43 #define	A64_PR_CFG		0x00
     44 #define	 A64_AC_PR_RST		__BIT(28)
     45 #define	 A64_AC_PR_RW		__BIT(24)
     46 #define	 A64_AC_PR_ADDR		__BITS(20,16)
     47 #define	 A64_ACDA_PR_WDAT	__BITS(15,8)
     48 #define	 A64_ACDA_PR_RDAT	__BITS(7,0)
     49 
     50 #define	A64_HP_CTRL		0x00
     51 #define	 A64_HPPA_EN		__BIT(6)
     52 #define	 A64_HPVOL		__BITS(5,0)
     53 #define	A64_OL_MIX_CTRL		0x01
     54 #define	 A64_LMIXMUTE_LDAC	__BIT(1)
     55 #define	A64_OR_MIX_CTRL		0x02
     56 #define	 A64_RMIXMUTE_RDAC	__BIT(1)
     57 #define	A64_LINEOUT_CTRL0	0x05
     58 #define	 A64_LINEOUT_LEFT_EN	__BIT(7)
     59 #define	 A64_LINEOUT_RIGHT_EN	__BIT(6)
     60 #define	 A64_LINEOUT_EN		(A64_LINEOUT_LEFT_EN|A64_LINEOUT_RIGHT_EN)
     61 #define	A64_LINEOUT_CTRL1	0x06
     62 #define	 A64_LINEOUT_VOL	__BITS(4,0)
     63 #define	A64_MIC1_CTRL		0x07
     64 #define	 A64_MIC1G		__BITS(6,4)
     65 #define	 A64_MIC1AMPEN		__BIT(3)
     66 #define	 A64_MIC1BOOST		__BITS(2,0)
     67 #define	A64_MIC2_CTRL		0x08
     68 #define	 A64_MIC2_SEL		__BIT(7)
     69 #define	 A64_MIC2G		__BITS(6,4)
     70 #define	 A64_MIC2AMPEN		__BIT(3)
     71 #define	 A64_MIC2BOOST		__BITS(2,0)
     72 #define	A64_LINEIN_CTRL		0x09
     73 #define	 A64_LINEING		__BITS(6,4)
     74 #define	A64_MIX_DAC_CTRL	0x0a
     75 #define	 A64_DACAREN		__BIT(7)
     76 #define	 A64_DACALEN		__BIT(6)
     77 #define	 A64_RMIXEN		__BIT(5)
     78 #define	 A64_LMIXEN		__BIT(4)
     79 #define	 A64_RHPPAMUTE		__BIT(3)
     80 #define	 A64_LHPPAMUTE		__BIT(2)
     81 #define	 A64_RHPIS		__BIT(1)
     82 #define	 A64_LHPIS		__BIT(0)
     83 #define	A64_L_ADCMIX_SRC	0x0b
     84 #define	A64_R_ADCMIX_SRC	0x0c
     85 #define	 A64_ADCMIX_SRC_MIC1	__BIT(6)
     86 #define	 A64_ADCMIX_SRC_MIC2	__BIT(5)
     87 #define	 A64_ADCMIX_SRC_LINEIN	__BIT(2)
     88 #define	 A64_ADCMIX_SRC_OMIXER	__BIT(1)
     89 #define	A64_ADC_CTRL		0x0d
     90 #define	 A64_ADCREN		__BIT(7)
     91 #define	 A64_ADCLEN		__BIT(6)
     92 #define	 A64_ADCG		__BITS(2,0)
     93 #define	A64_JACK_MIC_CTRL	0x1d
     94 #define	 A64_JACKDETEN		__BIT(7)
     95 #define	 A64_INNERRESEN		__BIT(6)
     96 #define	 A64_AUTOPLEN		__BIT(1)
     97 
     98 struct a64_acodec_softc {
     99 	device_t		sc_dev;
    100 	bus_space_tag_t		sc_bst;
    101 	bus_space_handle_t	sc_bsh;
    102 	int			sc_phandle;
    103 
    104 	struct audio_dai_device	sc_dai;
    105 	int			sc_master_dev;
    106 };
    107 
    108 enum a64_acodec_mixer_ctrl {
    109 	A64_CODEC_OUTPUT_CLASS,
    110 	A64_CODEC_INPUT_CLASS,
    111 	A64_CODEC_RECORD_CLASS,
    112 
    113 	A64_CODEC_OUTPUT_MASTER_VOLUME,
    114 	A64_CODEC_OUTPUT_MUTE,
    115 	A64_CODEC_OUTPUT_SOURCE,
    116 	A64_CODEC_INPUT_LINE_VOLUME,
    117 	A64_CODEC_INPUT_HP_VOLUME,
    118 	A64_CODEC_RECORD_LINE_VOLUME,
    119 	A64_CODEC_RECORD_MIC1_VOLUME,
    120 	A64_CODEC_RECORD_MIC1_PREAMP,
    121 	A64_CODEC_RECORD_MIC2_VOLUME,
    122 	A64_CODEC_RECORD_MIC2_PREAMP,
    123 	A64_CODEC_RECORD_AGC_VOLUME,
    124 	A64_CODEC_RECORD_SOURCE,
    125 
    126 	A64_CODEC_MIXER_CTRL_LAST
    127 };
    128 
    129 #define	A64_OUTPUT_SOURCE_LINE	__BIT(0)
    130 #define	A64_OUTPUT_SOURCE_HP	__BIT(1)
    131 
    132 static const struct a64_acodec_mixer {
    133 	const char *			name;
    134 	enum a64_acodec_mixer_ctrl	mixer_class;
    135 	u_int				reg;
    136 	u_int				mask;
    137 } a64_acodec_mixers[A64_CODEC_MIXER_CTRL_LAST] = {
    138 	[A64_CODEC_INPUT_LINE_VOLUME]	= { AudioNline,
    139 	    A64_CODEC_INPUT_CLASS, A64_LINEOUT_CTRL1, A64_LINEOUT_VOL },
    140 	[A64_CODEC_INPUT_HP_VOLUME]	= { AudioNheadphone,
    141 	    A64_CODEC_INPUT_CLASS, A64_HP_CTRL, A64_HPVOL },
    142 
    143 	[A64_CODEC_RECORD_LINE_VOLUME]	= { AudioNline,
    144 	    A64_CODEC_RECORD_CLASS, A64_LINEIN_CTRL, A64_LINEING },
    145 	[A64_CODEC_RECORD_MIC1_VOLUME]	= { AudioNmicrophone,
    146 	    A64_CODEC_RECORD_CLASS, A64_MIC1_CTRL, A64_MIC1G },
    147 	[A64_CODEC_RECORD_MIC2_VOLUME]	= { AudioNmicrophone "2",
    148 	    A64_CODEC_RECORD_CLASS, A64_MIC2_CTRL, A64_MIC2G },
    149 	[A64_CODEC_RECORD_AGC_VOLUME]	= { AudioNagc,
    150 	    A64_CODEC_RECORD_CLASS, A64_ADC_CTRL, A64_ADCG },
    151 };
    152 
    153 #define	RD4(sc, reg)			\
    154 	bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
    155 #define	WR4(sc, reg, val)		\
    156 	bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
    157 
    158 static u_int
    159 a64_acodec_pr_read(struct a64_acodec_softc *sc, u_int addr)
    160 {
    161 	uint32_t val;
    162 
    163 	/* Read current value */
    164 	val = RD4(sc, A64_PR_CFG);
    165 
    166 	/* De-assert reset */
    167 	val |= A64_AC_PR_RST;
    168 	WR4(sc, A64_PR_CFG, val);
    169 
    170 	/* Read mode */
    171 	val &= ~A64_AC_PR_RW;
    172 	WR4(sc, A64_PR_CFG, val);
    173 
    174 	/* Set address */
    175 	val &= ~A64_AC_PR_ADDR;
    176 	val |= __SHIFTIN(addr, A64_AC_PR_ADDR);
    177 	WR4(sc, A64_PR_CFG, val);
    178 
    179 	/* Read data */
    180 	return __SHIFTOUT(RD4(sc, A64_PR_CFG), A64_ACDA_PR_RDAT);
    181 }
    182 
    183 static void
    184 a64_acodec_pr_write(struct a64_acodec_softc *sc, u_int addr, u_int data)
    185 {
    186 	uint32_t val;
    187 
    188 	/* Read current value */
    189 	val = RD4(sc, A64_PR_CFG);
    190 
    191 	/* De-assert reset */
    192 	val |= A64_AC_PR_RST;
    193 	WR4(sc, A64_PR_CFG, val);
    194 
    195 	/* Set address */
    196 	val &= ~A64_AC_PR_ADDR;
    197 	val |= __SHIFTIN(addr, A64_AC_PR_ADDR);
    198 	WR4(sc, A64_PR_CFG, val);
    199 
    200 	/* Write data */
    201 	val &= ~A64_ACDA_PR_WDAT;
    202 	val |= __SHIFTIN(data, A64_ACDA_PR_WDAT);
    203 	WR4(sc, A64_PR_CFG, val);
    204 
    205 	/* Write mode */
    206 	val |= A64_AC_PR_RW;
    207 	WR4(sc, A64_PR_CFG, val);
    208 
    209 	/* Clear write mode */
    210 	val &= ~A64_AC_PR_RW;
    211 	WR4(sc, A64_PR_CFG, val);
    212 }
    213 
    214 static void
    215 a64_acodec_pr_set_clear(struct a64_acodec_softc *sc, u_int addr, u_int set, u_int clr)
    216 {
    217 	u_int old, new;
    218 
    219 	old = a64_acodec_pr_read(sc, addr);
    220 	new = set | (old & ~clr);
    221 	a64_acodec_pr_write(sc, addr, new);
    222 }
    223 
    224 static int
    225 a64_acodec_trigger_output(void *priv, void *start, void *end, int blksize,
    226     void (*intr)(void *), void *intrarg, const audio_params_t *params)
    227 {
    228 	struct a64_acodec_softc * const sc = priv;
    229 
    230 	/* Enable DAC analog l/r channels, HP PA, and output mixer */
    231 	a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL,
    232 	    A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN |
    233 	    A64_RHPPAMUTE | A64_LHPPAMUTE, 0);
    234 
    235 	return 0;
    236 }
    237 
    238 static int
    239 a64_acodec_trigger_input(void *priv, void *start, void *end, int blksize,
    240     void (*intr)(void *), void *intrarg, const audio_params_t *params)
    241 {
    242 	struct a64_acodec_softc * const sc = priv;
    243 
    244 	/* Enable ADC analog l/r channels */
    245 	a64_acodec_pr_set_clear(sc, A64_ADC_CTRL,
    246 	    A64_ADCREN | A64_ADCLEN, 0);
    247 
    248 	return 0;
    249 }
    250 
    251 static int
    252 a64_acodec_halt_output(void *priv)
    253 {
    254 	struct a64_acodec_softc * const sc = priv;
    255 
    256 	/* Disable DAC analog l/r channels, HP PA, and output mixer */
    257 	a64_acodec_pr_set_clear(sc, A64_MIX_DAC_CTRL,
    258 	    0, A64_DACAREN | A64_DACALEN | A64_RMIXEN | A64_LMIXEN |
    259 	    A64_RHPPAMUTE | A64_LHPPAMUTE);
    260 
    261 	return 0;
    262 }
    263 
    264 static int
    265 a64_acodec_halt_input(void *priv)
    266 {
    267 	struct a64_acodec_softc * const sc = priv;
    268 
    269 	/* Disable ADC analog l/r channels */
    270 	a64_acodec_pr_set_clear(sc, A64_ADC_CTRL,
    271 	    0, A64_ADCREN | A64_ADCLEN);
    272 
    273 	return 0;
    274 }
    275 
    276 static int
    277 a64_acodec_set_port(void *priv, mixer_ctrl_t *mc)
    278 {
    279 	struct a64_acodec_softc * const sc = priv;
    280 	const struct a64_acodec_mixer *mix;
    281 	u_int val, shift;
    282 	int nvol, dev;
    283 
    284 	dev = mc->dev;
    285 	if (dev == A64_CODEC_OUTPUT_MASTER_VOLUME)
    286 		dev = sc->sc_master_dev;
    287 
    288 	switch (dev) {
    289 	case A64_CODEC_INPUT_LINE_VOLUME:
    290 	case A64_CODEC_INPUT_HP_VOLUME:
    291 	case A64_CODEC_RECORD_LINE_VOLUME:
    292 	case A64_CODEC_RECORD_MIC1_VOLUME:
    293 	case A64_CODEC_RECORD_MIC2_VOLUME:
    294 	case A64_CODEC_RECORD_AGC_VOLUME:
    295 		mix = &a64_acodec_mixers[dev];
    296 		val = a64_acodec_pr_read(sc, mix->reg);
    297 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
    298 		nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift;
    299 		val &= ~mix->mask;
    300 		val |= __SHIFTIN(nvol, mix->mask);
    301 		a64_acodec_pr_write(sc, mix->reg, val);
    302 		return 0;
    303 
    304 	case A64_CODEC_RECORD_MIC1_PREAMP:
    305 		if (mc->un.ord < 0 || mc->un.ord > 1)
    306 			return EINVAL;
    307 		if (mc->un.ord) {
    308 			a64_acodec_pr_set_clear(sc, A64_MIC1_CTRL, A64_MIC1AMPEN, 0);
    309 		} else {
    310 			a64_acodec_pr_set_clear(sc, A64_MIC1_CTRL, 0, A64_MIC1AMPEN);
    311 		}
    312 		return 0;
    313 
    314 	case A64_CODEC_RECORD_MIC2_PREAMP:
    315 		if (mc->un.ord < 0 || mc->un.ord > 1)
    316 			return EINVAL;
    317 		if (mc->un.ord) {
    318 			a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL, A64_MIC2AMPEN, 0);
    319 		} else {
    320 			a64_acodec_pr_set_clear(sc, A64_MIC2_CTRL, 0, A64_MIC2AMPEN);
    321 		}
    322 		return 0;
    323 
    324 	case A64_CODEC_OUTPUT_MUTE:
    325 		if (mc->un.ord < 0 || mc->un.ord > 1)
    326 			return EINVAL;
    327 		if (mc->un.ord) {
    328 			a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
    329 			    0, A64_LMIXMUTE_LDAC);
    330 			a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
    331 			    0, A64_RMIXMUTE_RDAC);
    332 		} else {
    333 			a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
    334 			    A64_LMIXMUTE_LDAC, 0);
    335 			a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
    336 			    A64_RMIXMUTE_RDAC, 0);
    337 		}
    338 		return 0;
    339 
    340 	case A64_CODEC_OUTPUT_SOURCE:
    341 		if (mc->un.mask & A64_OUTPUT_SOURCE_LINE)
    342 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
    343 			    A64_LINEOUT_EN, 0);
    344 		else
    345 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
    346 			    0, A64_LINEOUT_EN);
    347 
    348 		if (mc->un.mask & A64_OUTPUT_SOURCE_HP)
    349 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
    350 			    A64_HPPA_EN, 0);
    351 		else
    352 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
    353 			    0, A64_HPPA_EN);
    354 		return 0;
    355 
    356 	case A64_CODEC_RECORD_SOURCE:
    357 		a64_acodec_pr_write(sc, A64_L_ADCMIX_SRC, mc->un.mask);
    358 		a64_acodec_pr_write(sc, A64_R_ADCMIX_SRC, mc->un.mask);
    359 		return 0;
    360 	}
    361 
    362 	return ENXIO;
    363 }
    364 
    365 static int
    366 a64_acodec_get_port(void *priv, mixer_ctrl_t *mc)
    367 {
    368 	struct a64_acodec_softc * const sc = priv;
    369 	const struct a64_acodec_mixer *mix;
    370 	u_int val, shift;
    371 	int nvol, dev;
    372 
    373 	dev = mc->dev;
    374 	if (dev == A64_CODEC_OUTPUT_MASTER_VOLUME)
    375 		dev = sc->sc_master_dev;
    376 
    377 	switch (dev) {
    378 	case A64_CODEC_INPUT_LINE_VOLUME:
    379 	case A64_CODEC_INPUT_HP_VOLUME:
    380 	case A64_CODEC_RECORD_LINE_VOLUME:
    381 	case A64_CODEC_RECORD_MIC1_VOLUME:
    382 	case A64_CODEC_RECORD_MIC2_VOLUME:
    383 	case A64_CODEC_RECORD_AGC_VOLUME:
    384 		mix = &a64_acodec_mixers[dev];
    385 		val = a64_acodec_pr_read(sc, mix->reg);
    386 		shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask));
    387 		nvol = __SHIFTOUT(val, mix->mask) << shift;
    388 		mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol;
    389 		mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol;
    390 		return 0;
    391 
    392 	case A64_CODEC_RECORD_MIC1_PREAMP:
    393 		mc->un.ord = !!(a64_acodec_pr_read(sc, A64_MIC1_CTRL) & A64_MIC1AMPEN);
    394 		return 0;
    395 
    396 	case A64_CODEC_RECORD_MIC2_PREAMP:
    397 		mc->un.ord = !!(a64_acodec_pr_read(sc, A64_MIC2_CTRL) & A64_MIC2AMPEN);
    398 		return 0;
    399 
    400 	case A64_CODEC_OUTPUT_MUTE:
    401 		mc->un.ord = 1;
    402 		if (a64_acodec_pr_read(sc, A64_OL_MIX_CTRL) & A64_LMIXMUTE_LDAC)
    403 			mc->un.ord = 0;
    404 		if (a64_acodec_pr_read(sc, A64_OR_MIX_CTRL) & A64_RMIXMUTE_RDAC)
    405 			mc->un.ord = 0;
    406 		return 0;
    407 
    408 	case A64_CODEC_OUTPUT_SOURCE:
    409 		mc->un.mask = 0;
    410 		if (a64_acodec_pr_read(sc, A64_LINEOUT_CTRL0) & A64_LINEOUT_EN)
    411 			mc->un.mask |= A64_OUTPUT_SOURCE_LINE;
    412 		if (a64_acodec_pr_read(sc, A64_HP_CTRL) & A64_HPPA_EN)
    413 			mc->un.mask |= A64_OUTPUT_SOURCE_HP;
    414 		return 0;
    415 
    416 	case A64_CODEC_RECORD_SOURCE:
    417 		mc->un.mask =
    418 		    a64_acodec_pr_read(sc, A64_L_ADCMIX_SRC) |
    419 		    a64_acodec_pr_read(sc, A64_R_ADCMIX_SRC);
    420 		return 0;
    421 	}
    422 
    423 	return ENXIO;
    424 }
    425 
    426 static int
    427 a64_acodec_query_devinfo(void *priv, mixer_devinfo_t *di)
    428 {
    429 	struct a64_acodec_softc * const sc = priv;
    430 	const struct a64_acodec_mixer *mix;
    431 
    432 	switch (di->index) {
    433 	case A64_CODEC_OUTPUT_CLASS:
    434 		di->mixer_class = di->index;
    435 		strcpy(di->label.name, AudioCoutputs);
    436 		di->type = AUDIO_MIXER_CLASS;
    437 		di->next = di->prev = AUDIO_MIXER_LAST;
    438 		return 0;
    439 
    440 	case A64_CODEC_INPUT_CLASS:
    441 		di->mixer_class = di->index;
    442 		strcpy(di->label.name, AudioCinputs);
    443 		di->type = AUDIO_MIXER_CLASS;
    444 		di->next = di->prev = AUDIO_MIXER_LAST;
    445 		return 0;
    446 
    447 	case A64_CODEC_RECORD_CLASS:
    448 		di->mixer_class = di->index;
    449 		strcpy(di->label.name, AudioCrecord);
    450 		di->type = AUDIO_MIXER_CLASS;
    451 		di->next = di->prev = AUDIO_MIXER_LAST;
    452 		return 0;
    453 
    454 	case A64_CODEC_OUTPUT_MASTER_VOLUME:
    455 		mix = &a64_acodec_mixers[sc->sc_master_dev];
    456 		di->mixer_class = A64_CODEC_OUTPUT_CLASS;
    457 		strcpy(di->label.name, AudioNmaster);
    458 		di->un.v.delta =
    459 		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
    460 		di->type = AUDIO_MIXER_VALUE;
    461 		di->next = di->prev = AUDIO_MIXER_LAST;
    462 		di->un.v.num_channels = 2;
    463 		strcpy(di->un.v.units.name, AudioNvolume);
    464 		return 0;
    465 
    466 	case A64_CODEC_INPUT_LINE_VOLUME:
    467 	case A64_CODEC_INPUT_HP_VOLUME:
    468 	case A64_CODEC_RECORD_LINE_VOLUME:
    469 	case A64_CODEC_RECORD_MIC1_VOLUME:
    470 	case A64_CODEC_RECORD_MIC2_VOLUME:
    471 	case A64_CODEC_RECORD_AGC_VOLUME:
    472 		mix = &a64_acodec_mixers[di->index];
    473 		di->mixer_class = mix->mixer_class;
    474 		strcpy(di->label.name, mix->name);
    475 		di->un.v.delta =
    476 		    256 / (__SHIFTOUT_MASK(mix->mask) + 1);
    477 		di->type = AUDIO_MIXER_VALUE;
    478 		di->prev = AUDIO_MIXER_LAST;
    479 		if (di->index == A64_CODEC_RECORD_MIC1_VOLUME)
    480 			di->next = A64_CODEC_RECORD_MIC1_PREAMP;
    481 		else if (di->index == A64_CODEC_RECORD_MIC2_VOLUME)
    482 			di->next = A64_CODEC_RECORD_MIC2_PREAMP;
    483 		else
    484 			di->next = AUDIO_MIXER_LAST;
    485 		di->un.v.num_channels = 2;
    486 		strcpy(di->un.v.units.name, AudioNvolume);
    487 		return 0;
    488 
    489 	case A64_CODEC_RECORD_MIC1_PREAMP:
    490 	case A64_CODEC_RECORD_MIC2_PREAMP:
    491 		di->mixer_class = A64_CODEC_RECORD_CLASS;
    492 		strcpy(di->label.name, AudioNpreamp);
    493 		di->type = AUDIO_MIXER_ENUM;
    494 		if (di->index == A64_CODEC_RECORD_MIC1_PREAMP)
    495 			di->prev = A64_CODEC_RECORD_MIC1_VOLUME;
    496 		else
    497 			di->prev = A64_CODEC_RECORD_MIC2_VOLUME;
    498 		di->next = AUDIO_MIXER_LAST;
    499 		di->un.e.num_mem = 2;
    500 		strcpy(di->un.e.member[0].label.name, AudioNoff);
    501 		di->un.e.member[0].ord = 0;
    502 		strcpy(di->un.e.member[1].label.name, AudioNon);
    503 		di->un.e.member[1].ord = 1;
    504 		return 0;
    505 
    506 	case A64_CODEC_OUTPUT_MUTE:
    507 		di->mixer_class = A64_CODEC_OUTPUT_CLASS;
    508 		strcpy(di->label.name, AudioNmute);
    509 		di->type = AUDIO_MIXER_ENUM;
    510 		di->next = di->prev = AUDIO_MIXER_LAST;
    511 		di->un.e.num_mem = 2;
    512 		strcpy(di->un.e.member[0].label.name, AudioNoff);
    513 		di->un.e.member[0].ord = 0;
    514 		strcpy(di->un.e.member[1].label.name, AudioNon);
    515 		di->un.e.member[1].ord = 1;
    516 		return 0;
    517 
    518 	case A64_CODEC_OUTPUT_SOURCE:
    519 		di->mixer_class = A64_CODEC_OUTPUT_CLASS;
    520 		strcpy(di->label.name, AudioNsource);
    521 		di->type = AUDIO_MIXER_SET;
    522 		di->next = di->prev = AUDIO_MIXER_LAST;
    523 		di->un.s.num_mem = 2;
    524 		strcpy(di->un.s.member[0].label.name, AudioNline);
    525 		di->un.s.member[0].mask = A64_OUTPUT_SOURCE_LINE;
    526 		strcpy(di->un.s.member[1].label.name, AudioNheadphone);
    527 		di->un.s.member[1].mask = A64_OUTPUT_SOURCE_HP;
    528 		return 0;
    529 
    530 	case A64_CODEC_RECORD_SOURCE:
    531 		di->mixer_class = A64_CODEC_RECORD_CLASS;
    532 		strcpy(di->label.name, AudioNsource);
    533 		di->type = AUDIO_MIXER_SET;
    534 		di->next = di->prev = AUDIO_MIXER_LAST;
    535 		di->un.s.num_mem = 4;
    536 		strcpy(di->un.s.member[0].label.name, AudioNline);
    537 		di->un.s.member[0].mask = A64_ADCMIX_SRC_LINEIN;
    538 		strcpy(di->un.s.member[1].label.name, AudioNmicrophone);
    539 		di->un.s.member[1].mask = A64_ADCMIX_SRC_MIC1;
    540 		strcpy(di->un.s.member[2].label.name, AudioNmicrophone "2");
    541 		di->un.s.member[2].mask = A64_ADCMIX_SRC_MIC2;
    542 		strcpy(di->un.s.member[3].label.name, AudioNdac);
    543 		di->un.s.member[3].mask = A64_ADCMIX_SRC_OMIXER;
    544 		return 0;
    545 
    546 	}
    547 
    548 	return ENXIO;
    549 }
    550 
    551 static const struct audio_hw_if a64_acodec_hw_if = {
    552 	.trigger_output = a64_acodec_trigger_output,
    553 	.trigger_input = a64_acodec_trigger_input,
    554 	.halt_output = a64_acodec_halt_output,
    555 	.halt_input = a64_acodec_halt_input,
    556 	.set_port = a64_acodec_set_port,
    557 	.get_port = a64_acodec_get_port,
    558 	.query_devinfo = a64_acodec_query_devinfo,
    559 };
    560 
    561 static audio_dai_tag_t
    562 a64_acodec_dai_get_tag(device_t dev, const void *data, size_t len)
    563 {
    564 	struct a64_acodec_softc * const sc = device_private(dev);
    565 
    566 	if (len != 4)
    567 		return NULL;
    568 
    569 	return &sc->sc_dai;
    570 }
    571 
    572 static struct fdtbus_dai_controller_func a64_acodec_dai_funcs = {
    573 	.get_tag = a64_acodec_dai_get_tag
    574 };
    575 
    576 static int
    577 a64_acodec_dai_jack_detect(audio_dai_tag_t dai, u_int jack, int present)
    578 {
    579 	struct a64_acodec_softc * const sc = audio_dai_private(dai);
    580 
    581 	switch (jack) {
    582 	case AUDIO_DAI_JACK_HP:
    583 		if (present) {
    584 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
    585 			    0, A64_LINEOUT_EN);
    586 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
    587 			    A64_HPPA_EN, 0);
    588 		} else {
    589 			a64_acodec_pr_set_clear(sc, A64_LINEOUT_CTRL0,
    590 			    A64_LINEOUT_EN, 0);
    591 			a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
    592 			    0, A64_HPPA_EN);
    593 		}
    594 
    595 		/* Master volume controls either HP or line out */
    596 		sc->sc_master_dev = present ?
    597 		    A64_CODEC_INPUT_HP_VOLUME : A64_CODEC_INPUT_LINE_VOLUME;
    598 
    599 		break;
    600 
    601 	case AUDIO_DAI_JACK_MIC:
    602 		/* XXX TODO */
    603 		break;
    604 	}
    605 
    606 	return 0;
    607 }
    608 
    609 static const struct device_compatible_entry compat_data[] = {
    610 	{ .compat = "allwinner,sun50i-a64-codec-analog" },
    611 	DEVICE_COMPAT_EOL
    612 };
    613 
    614 static int
    615 a64_acodec_match(device_t parent, cfdata_t cf, void *aux)
    616 {
    617 	struct fdt_attach_args * const faa = aux;
    618 
    619 	return of_compatible_match(faa->faa_phandle, compat_data);
    620 }
    621 
    622 static void
    623 a64_acodec_attach(device_t parent, device_t self, void *aux)
    624 {
    625 	struct a64_acodec_softc * const sc = device_private(self);
    626 	struct fdt_attach_args * const faa = aux;
    627 	const int phandle = faa->faa_phandle;
    628 	bus_addr_t addr;
    629 	bus_size_t size;
    630 
    631 	sc->sc_dev = self;
    632 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
    633 		aprint_error(": couldn't get registers\n");
    634 		return;
    635 	}
    636 	sc->sc_bst = faa->faa_bst;
    637 	if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
    638 		aprint_error(": couldn't map registers\n");
    639 		return;
    640 	}
    641 
    642 	sc->sc_phandle = phandle;
    643 
    644 	aprint_naive("\n");
    645 	aprint_normal(": A64 Audio Codec (analog part)\n");
    646 
    647 	/* Right & Left Headphone PA enable */
    648 	a64_acodec_pr_set_clear(sc, A64_HP_CTRL,
    649 	    A64_HPPA_EN, 0);
    650 
    651 	/* Jack detect enable */
    652 	sc->sc_master_dev = A64_CODEC_INPUT_HP_VOLUME;
    653 	a64_acodec_pr_set_clear(sc, A64_JACK_MIC_CTRL,
    654 	    A64_JACKDETEN | A64_INNERRESEN | A64_AUTOPLEN, 0);
    655 
    656 	/* Unmute DAC to output mixer */
    657 	a64_acodec_pr_set_clear(sc, A64_OL_MIX_CTRL,
    658 	    A64_LMIXMUTE_LDAC, 0);
    659 	a64_acodec_pr_set_clear(sc, A64_OR_MIX_CTRL,
    660 	    A64_RMIXMUTE_RDAC, 0);
    661 
    662 	sc->sc_dai.dai_jack_detect = a64_acodec_dai_jack_detect;
    663 	sc->sc_dai.dai_hw_if = &a64_acodec_hw_if;
    664 	sc->sc_dai.dai_dev = self;
    665 	sc->sc_dai.dai_priv = sc;
    666 	fdtbus_register_dai_controller(self, phandle, &a64_acodec_dai_funcs);
    667 }
    668 
    669 CFATTACH_DECL_NEW(a64_acodec, sizeof(struct a64_acodec_softc),
    670     a64_acodec_match, a64_acodec_attach, NULL, NULL);
    671