sun6i_a31_codec.c revision 1.2
1/* $NetBSD: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki Exp $ */ 2 3/*- 4 * Copyright (c) 2014-2017 Jared McNeill <jmcneill@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: sun6i_a31_codec.c,v 1.2 2019/05/08 13:40:14 isaki 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 <sys/audioio.h> 40#include <dev/audio/audio_if.h> 41 42#include <arm/sunxi/sunxi_codec.h> 43 44#define A31_DEFAULT_HPVOL 0x20 45 46#define OMIXER_DACA_CTRL 0x20 47#define DACAREN __BIT(31) 48#define DACALEN __BIT(30) 49#define RMIXEN __BIT(29) 50#define LMIXEN __BIT(28) 51#define RMIXMUTE __BITS(23,17) 52#define RMIXMUTE_MIC1 __BIT(23) 53#define RMIXMUTE_MIC2 __BIT(22) 54#define RMIXMUTE_PHONEP_PHONEN __BIT(21) 55#define RMIXMUTE_PHONEP __BIT(20) 56#define RMIXMUTE_LINEINR __BIT(19) 57#define RMIXMUTE_DACR __BIT(18) 58#define RMIXMUTE_DACL __BIT(17) 59#define LMIXMUTE __BITS(16,10) 60#define LMIXMUTE_MIC1 __BIT(16) 61#define LMIXMUTE_MIC2 __BIT(15) 62#define LMIXMUTE_PHONEP_PHONEN __BIT(14) 63#define LMIXMUTE_PHONEN __BIT(13) 64#define LMIXMUTE_LINEINL __BIT(12) 65#define LMIXMUTE_DACL __BIT(11) 66#define LMIXMUTE_DACR __BIT(10) 67#define RHPIS __BIT(9) 68#define LHPIS __BIT(8) 69#define RHPPAMUTE __BIT(7) 70#define LHPPAMUTE __BIT(6) 71#define HPVOL __BITS(5,0) 72 73#define OMIXER_PA_CTRL 0x24 74#define HPPAEN __BIT(31) 75#define HPCOM_CTL __BITS(30,29) 76#define COMPTEN __BIT(28) 77#define PA_ANTI_POP_CTRL __BITS(27,26) 78#define MIC1G __BITS(17,15) 79#define MIC2G __BITS(14,12) 80#define LINEING __BITS(11,9) 81#define PHONEG __BITS(8,6) 82#define PHONEPG __BITS(5,3) 83#define PHONENG __BITS(2,0) 84 85#define AC_MIC_CTRL 0x28 86#define HBIASEN __BIT(31) 87#define MBIASEN __BIT(30) 88#define HBIASADCEN __BIT(29) 89#define MIC1AMPEN __BIT(28) 90#define MIC1BOOST __BITS(27,25) 91#define MIC2AMPEN __BIT(24) 92#define MIC2BOOST __BITS(23,21) 93#define MIC2SLT __BIT(20) 94#define LINEOUTLEN __BIT(19) 95#define LINEOUTREN __BIT(18) 96#define LINEOUTLSRC __BIT(17) 97#define LINEOUTRSRC __BIT(16) 98#define LINEOUTVC __BITS(15,11) 99#define PHONEPREG __BITS(10,8) 100#define PHONEOUTG __BITS(7,5) 101#define PHONEOUTEN __BIT(4) 102#define PHONEOUTS0 __BIT(3) 103#define PHONEOUTS1 __BIT(2) 104#define PHONEOUTS2 __BIT(1) 105#define PHONEOUTS3 __BIT(0) 106 107#define AC_ADCA_CTRL 0x2c 108#define ADCREN __BIT(31) 109#define ADCLEN __BIT(30) 110#define ADCRG __BITS(29,27) 111#define ADCLG __BITS(26,24) 112#define RADCMIXMUTE __BITS(13,7) 113#define RADCMIXMUTE_MIC1 __BIT(13) 114#define RADCMIXMUTE_MIC2 __BIT(12) 115#define RADCMIXMUTE_PHONEP_PHONEN __BIT(11) 116#define RADCMIXMUTE_PHONEP __BIT(10) 117#define RADCMIXMUTE_LINEINR __BIT(9) 118#define RADCMIXMUTE_ROM __BIT(8) 119#define RADCMIXMUTE_LOM __BIT(7) 120#define LADCMIXMUTE __BITS(6,0) 121#define LADCMIXMUTE_MIC1 __BIT(6) 122#define LADCMIXMUTE_MIC2 __BIT(5) 123#define LADCMIXMUTE_PHONEP_PHONEN __BIT(4) 124#define LADCMIXMUTE_PHONEN __BIT(3) 125#define LADCMIXMUTE_LINEINL __BIT(2) 126#define LADCMIXMUTE_LOM __BIT(1) 127#define LADCMIXMUTE_ROM __BIT(0) 128 129enum a31_codec_mixer_ctrl { 130 A31_CODEC_OUTPUT_CLASS, 131 A31_CODEC_INPUT_CLASS, 132 133 A31_CODEC_OUTPUT_MASTER_VOLUME, 134 A31_CODEC_INPUT_DAC_VOLUME, 135 136 A31_CODEC_MIXER_CTRL_LAST 137}; 138 139static const struct a31_codec_mixer { 140 const char * name; 141 enum a31_codec_mixer_ctrl mixer_class; 142 u_int reg; 143 u_int mask; 144} a31_codec_mixers[A31_CODEC_MIXER_CTRL_LAST] = { 145 [A31_CODEC_OUTPUT_MASTER_VOLUME] = { AudioNmaster, 146 A31_CODEC_OUTPUT_CLASS, OMIXER_DACA_CTRL, HPVOL }, 147 [A31_CODEC_INPUT_DAC_VOLUME] = { AudioNdac, 148 A31_CODEC_INPUT_CLASS, OMIXER_DACA_CTRL, HPVOL }, 149}; 150 151#define RD4(sc, reg) \ 152 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 153#define WR4(sc, reg, val) \ 154 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 155#define SET4(sc, reg, mask) \ 156 WR4((sc), (reg), RD4((sc), (reg)) | (mask)) 157#define CLR4(sc, reg, mask) \ 158 WR4((sc), (reg), RD4((sc), (reg)) & ~(mask)) 159 160static int 161a31_codec_init(struct sunxi_codec_softc *sc) 162{ 163 164 /* Disable HPCOM and HPCOM output protection */ 165 CLR4(sc, OMIXER_PA_CTRL, HPCOM_CTL | COMPTEN); 166 /* Enable headphone power amp */ 167 SET4(sc, OMIXER_PA_CTRL, HPPAEN); 168 169 /* Set headphone PA input to DAC */ 170 CLR4(sc, OMIXER_DACA_CTRL, RHPIS | LHPIS); 171 /* Mute inputs to headphone PA */ 172 CLR4(sc, OMIXER_DACA_CTRL, RHPPAMUTE | LHPPAMUTE); 173 /* Set initial volume */ 174 CLR4(sc, OMIXER_DACA_CTRL, HPVOL); 175 SET4(sc, OMIXER_DACA_CTRL, __SHIFTIN(A31_DEFAULT_HPVOL, HPVOL)); 176 177 /* Disable lineout */ 178 CLR4(sc, AC_MIC_CTRL, LINEOUTLEN | LINEOUTREN | LINEOUTVC); 179 /* Enable headset microphone bias, current sensor, and ADC */ 180 SET4(sc, AC_MIC_CTRL, HBIASEN | HBIASADCEN); 181 182 return 0; 183} 184 185static void 186a31_codec_mute(struct sunxi_codec_softc *sc, int mute, u_int mode) 187{ 188 if (mode == AUMODE_PLAY) { 189 if (sc->sc_pin_pa != NULL) 190 fdtbus_gpio_write(sc->sc_pin_pa, !mute); 191 192 if (mute) { 193 CLR4(sc, OMIXER_DACA_CTRL, DACAREN | DACALEN); 194 } else { 195 CLR4(sc, OMIXER_DACA_CTRL, RMIXMUTE | LMIXMUTE); 196 SET4(sc, OMIXER_DACA_CTRL, 197 LHPIS | RHPIS | LHPPAMUTE | RHPPAMUTE | 198 DACAREN | DACALEN | RMIXEN | LMIXEN | 199 RMIXMUTE_DACR | LMIXMUTE_DACL); 200 } 201 } else { 202 if (mute) { 203 CLR4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN); 204 } else { 205 SET4(sc, AC_ADCA_CTRL, ADCREN | ADCLEN); 206 } 207 } 208} 209 210static int 211a31_codec_set_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc) 212{ 213 const struct a31_codec_mixer *mix; 214 u_int val, shift; 215 int nvol; 216 217 switch (mc->dev) { 218 case A31_CODEC_OUTPUT_MASTER_VOLUME: 219 case A31_CODEC_INPUT_DAC_VOLUME: 220 mix = &a31_codec_mixers[mc->dev]; 221 val = RD4(sc, mix->reg); 222 shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask)); 223 nvol = mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] >> shift; 224 val &= ~mix->mask; 225 val |= __SHIFTIN(nvol, mix->mask); 226 WR4(sc, mix->reg, val); 227 return 0; 228 } 229 230 return ENXIO; 231} 232 233static int 234a31_codec_get_port(struct sunxi_codec_softc *sc, mixer_ctrl_t *mc) 235{ 236 const struct a31_codec_mixer *mix; 237 u_int val, shift; 238 int nvol; 239 240 switch (mc->dev) { 241 case A31_CODEC_OUTPUT_MASTER_VOLUME: 242 case A31_CODEC_INPUT_DAC_VOLUME: 243 mix = &a31_codec_mixers[mc->dev]; 244 val = RD4(sc, mix->reg); 245 shift = 8 - fls32(__SHIFTOUT_MASK(mix->mask)); 246 nvol = __SHIFTOUT(val, mix->mask) << shift; 247 mc->un.value.level[AUDIO_MIXER_LEVEL_LEFT] = nvol; 248 mc->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = nvol; 249 return 0; 250 } 251 252 return ENXIO; 253} 254 255static int 256a31_codec_query_devinfo(struct sunxi_codec_softc *sc, mixer_devinfo_t *di) 257{ 258 const struct a31_codec_mixer *mix; 259 260 switch (di->index) { 261 case A31_CODEC_OUTPUT_CLASS: 262 di->mixer_class = di->index; 263 strcpy(di->label.name, AudioCoutputs); 264 di->type = AUDIO_MIXER_CLASS; 265 di->next = di->prev = AUDIO_MIXER_LAST; 266 return 0; 267 268 case A31_CODEC_INPUT_CLASS: 269 di->mixer_class = di->index; 270 strcpy(di->label.name, AudioCinputs); 271 di->type = AUDIO_MIXER_CLASS; 272 di->next = di->prev = AUDIO_MIXER_LAST; 273 return 0; 274 275 case A31_CODEC_OUTPUT_MASTER_VOLUME: 276 case A31_CODEC_INPUT_DAC_VOLUME: 277 mix = &a31_codec_mixers[di->index]; 278 di->mixer_class = mix->mixer_class; 279 strcpy(di->label.name, mix->name); 280 di->un.v.delta = 281 256 / (__SHIFTOUT_MASK(mix->mask) + 1); 282 di->type = AUDIO_MIXER_VALUE; 283 di->next = di->prev = AUDIO_MIXER_LAST; 284 di->un.v.num_channels = 2; 285 strcpy(di->un.v.units.name, AudioNvolume); 286 return 0; 287 } 288 289 return ENXIO; 290} 291 292const struct sunxi_codec_conf sun6i_a31_codecconf = { 293 .name = "A31 Audio Codec", 294 295 .init = a31_codec_init, 296 .mute = a31_codec_mute, 297 .set_port = a31_codec_set_port, 298 .get_port = a31_codec_get_port, 299 .query_devinfo = a31_codec_query_devinfo, 300 301 .DPC = 0x00, 302 .DAC_FIFOC = 0x04, 303 .DAC_FIFOS = 0x08, 304 .DAC_TXDATA = 0x0c, 305 .ADC_FIFOC = 0x10, 306 .ADC_FIFOS = 0x14, 307 .ADC_RXDATA = 0x18, 308 .DAC_CNT = 0x40, 309 .ADC_CNT = 0x44, 310}; 311