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 129 enum 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 139 static 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 160 static int 161 a31_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 185 static void 186 a31_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 210 static int 211 a31_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 233 static int 234 a31_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 255 static int 256 a31_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 292 const 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