1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright 2012 by Andreas Tobler. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 /* 29 * Apple PCM3052 aka Onyx audio codec. 30 * 31 * Datasheet: http://www.ti.com/product/pcm3052a 32 */ 33 34 #include <sys/param.h> 35 #include <sys/systm.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/bus.h> 39 #include <sys/malloc.h> 40 #include <sys/lock.h> 41 #include <sys/mutex.h> 42 #include <sys/taskqueue.h> 43 #include <machine/dbdma.h> 44 #include <machine/intr_machdep.h> 45 #include <machine/resource.h> 46 #include <machine/bus.h> 47 #include <machine/pio.h> 48 #include <sys/rman.h> 49 50 #include <dev/iicbus/iicbus.h> 51 #include <dev/iicbus/iiconf.h> 52 #include <dev/ofw/ofw_bus.h> 53 54 #ifdef HAVE_KERNEL_OPTION_HEADERS 55 #include "opt_snd.h" 56 #endif 57 58 #include <dev/sound/pcm/sound.h> 59 60 #include "mixer_if.h" 61 62 extern kobj_class_t i2s_mixer_class; 63 extern device_t i2s_mixer; 64 65 struct onyx_softc 66 { 67 device_t sc_dev; 68 uint32_t sc_addr; 69 struct mtx sc_volume_mtx; 70 struct task sc_volume_task; 71 uint8_t sc_left; 72 uint8_t sc_right; 73 }; 74 75 static int onyx_probe(device_t); 76 static int onyx_attach(device_t); 77 static int onyx_init(struct snd_mixer *m); 78 static int onyx_uninit(struct snd_mixer *m); 79 static int onyx_reinit(struct snd_mixer *m); 80 static int onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, 81 unsigned right); 82 static uint32_t onyx_setrecsrc(struct snd_mixer *m, uint32_t src); 83 84 static device_method_t onyx_methods[] = { 85 /* Device interface. */ 86 DEVMETHOD(device_probe, onyx_probe), 87 DEVMETHOD(device_attach, onyx_attach), 88 DEVMETHOD_END 89 }; 90 91 static driver_t onyx_driver = { 92 "onyx", 93 onyx_methods, 94 sizeof(struct onyx_softc) 95 }; 96 97 DRIVER_MODULE(onyx, iicbus, onyx_driver, 0, 0); 98 MODULE_VERSION(onyx, 1); 99 MODULE_DEPEND(onyx, iicbus, 1, 1, 1); 100 101 static kobj_method_t onyx_mixer_methods[] = { 102 KOBJMETHOD(mixer_init, onyx_init), 103 KOBJMETHOD(mixer_uninit, onyx_uninit), 104 KOBJMETHOD(mixer_reinit, onyx_reinit), 105 KOBJMETHOD(mixer_set, onyx_set), 106 KOBJMETHOD(mixer_setrecsrc, onyx_setrecsrc), 107 KOBJMETHOD_END 108 }; 109 110 MIXER_DECLARE(onyx_mixer); 111 112 #define PCM3052_IICADDR 0x8C /* Hard-coded I2C slave addr */ 113 114 /* 115 * PCM3052 registers. 116 * Numbering in decimal as used in the data sheet. 117 */ 118 #define PCM3052_REG_LEFT_ATTN 65 119 #define PCM3052_REG_RIGHT_ATTN 66 120 #define PCM3052_REG_CONTROL 67 121 #define PCM3052_MRST (1 << 7) 122 #define PCM3052_SRST (1 << 6) 123 #define PCM3052_REG_DAC_CONTROL 68 124 #define PCM3052_OVR1 (1 << 6) 125 #define PCM3052_MUTE_L (1 << 1) 126 #define PCM3052_MUTE_R (1 << 0) 127 #define PCM3052_REG_DAC_DEEMPH 69 128 #define PCM3052_REG_DAC_FILTER 70 129 #define PCM3052_DAC_FILTER_ALWAYS (1 << 2) 130 #define PCM3052_REG_OUT_PHASE 71 131 #define PCM3052_REG_ADC_CONTROL 72 132 #define PCM3052_REG_ADC_HPF_BP 75 133 #define PCM3052_HPF_ALWAYS (1 << 2) 134 #define PCM3052_REG_INFO_1 77 135 #define PCM3052_REG_INFO_2 78 136 #define PCM3052_REG_INFO_3 79 137 #define PCM3052_REG_INFO_4 80 138 139 struct onyx_reg { 140 uint8_t LEFT_ATTN; 141 uint8_t RIGHT_ATTN; 142 uint8_t CONTROL; 143 uint8_t DAC_CONTROL; 144 uint8_t DAC_DEEMPH; 145 uint8_t DAC_FILTER; 146 uint8_t OUT_PHASE; 147 uint8_t ADC_CONTROL; 148 uint8_t ADC_HPF_BP; 149 uint8_t INFO_1; 150 uint8_t INFO_2; 151 uint8_t INFO_3; 152 uint8_t INFO_4; 153 }; 154 155 static const struct onyx_reg onyx_initdata = { 156 0x80, /* LEFT_ATTN, Mute default */ 157 0x80, /* RIGHT_ATTN, Mute default */ 158 PCM3052_MRST | PCM3052_SRST, /* CONTROL */ 159 0, /* DAC_CONTROL */ 160 0, /* DAC_DEEMPH */ 161 PCM3052_DAC_FILTER_ALWAYS, /* DAC_FILTER */ 162 0, /* OUT_PHASE */ 163 (-1 /* dB */ + 8) & 0xf, /* ADC_CONTROL */ 164 PCM3052_HPF_ALWAYS, /* ADC_HPF_BP */ 165 (1 << 2), /* INFO_1 */ 166 2, /* INFO_2, */ 167 0, /* INFO_3, CLK 0 (level II), 168 SF 0 (44.1 kHz) */ 169 1 /* INFO_4, VALIDL/R 0, 170 WL 24-bit depth */ 171 }; 172 173 static int 174 onyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value) 175 { 176 unsigned int size; 177 uint8_t buf[16]; 178 179 struct iic_msg msg[] = { 180 { sc->sc_addr, IIC_M_WR, 0, buf } 181 }; 182 183 size = 1; 184 msg[0].len = size + 1; 185 buf[0] = reg; 186 buf[1] = value; 187 188 iicbus_transfer(sc->sc_dev, msg, 1); 189 190 return (0); 191 } 192 193 /* 194 * onyx_write() sleeps in iicbus_transfer(), so onyx_set() cannot program the 195 * volume registers inline. Hand the new values to a task instead, which runs 196 * with no lock held. 197 */ 198 static void 199 onyx_volume_task(void *arg, int pending __unused) 200 { 201 struct onyx_softc *sc = arg; 202 uint8_t l, r; 203 204 mtx_lock(&sc->sc_volume_mtx); 205 l = sc->sc_left; 206 r = sc->sc_right; 207 mtx_unlock(&sc->sc_volume_mtx); 208 209 onyx_write(sc, PCM3052_REG_LEFT_ATTN, l); 210 onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r); 211 } 212 213 static int 214 onyx_probe(device_t dev) 215 { 216 const char *name, *compat; 217 218 name = ofw_bus_get_name(dev); 219 if (name == NULL) 220 return (ENXIO); 221 222 if (strcmp(name, "codec") == 0) { 223 if (iicbus_get_addr(dev) != PCM3052_IICADDR) 224 return (ENXIO); 225 compat = ofw_bus_get_compat(dev); 226 if (compat == NULL || strcmp(compat, "pcm3052") != 0) 227 return (ENXIO); 228 } else 229 return (ENXIO); 230 231 device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec"); 232 return (0); 233 } 234 235 static int 236 onyx_attach(device_t dev) 237 { 238 struct onyx_softc *sc; 239 240 sc = device_get_softc(dev); 241 sc->sc_dev = dev; 242 sc->sc_addr = iicbus_get_addr(dev); 243 244 mtx_init(&sc->sc_volume_mtx, "onyx volume", NULL, MTX_DEF); 245 TASK_INIT(&sc->sc_volume_task, 0, onyx_volume_task, sc); 246 247 i2s_mixer_class = &onyx_mixer_class; 248 i2s_mixer = dev; 249 250 return (0); 251 } 252 253 static int 254 onyx_init(struct snd_mixer *m) 255 { 256 struct onyx_softc *sc; 257 unsigned int x = 0; 258 259 sc = device_get_softc(mix_getdevinfo(m)); 260 261 onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN); 262 onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN); 263 onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL); 264 onyx_write(sc, PCM3052_REG_DAC_CONTROL, 265 onyx_initdata.DAC_CONTROL); 266 onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH); 267 onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER); 268 onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE); 269 onyx_write(sc, PCM3052_REG_ADC_CONTROL, 270 onyx_initdata.ADC_CONTROL); 271 onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP); 272 onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1); 273 onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2); 274 onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3); 275 onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4); 276 277 x |= SOUND_MASK_VOLUME; 278 mix_setdevs(m, x); 279 280 return (0); 281 } 282 283 static int 284 onyx_uninit(struct snd_mixer *m) 285 { 286 struct onyx_softc *sc; 287 288 sc = device_get_softc(mix_getdevinfo(m)); 289 290 taskqueue_drain(taskqueue_thread, &sc->sc_volume_task); 291 292 return (0); 293 } 294 295 static int 296 onyx_reinit(struct snd_mixer *m) 297 { 298 return (0); 299 } 300 301 static int 302 onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 303 { 304 struct onyx_softc *sc; 305 uint8_t l, r; 306 307 sc = device_get_softc(mix_getdevinfo(m)); 308 309 switch (dev) { 310 case SOUND_MIXER_VOLUME: 311 if (left > 100 || right > 100) 312 return (0); 313 314 l = left + 128; 315 r = right + 128; 316 317 mtx_lock(&sc->sc_volume_mtx); 318 sc->sc_left = l; 319 sc->sc_right = r; 320 mtx_unlock(&sc->sc_volume_mtx); 321 322 taskqueue_enqueue(taskqueue_thread, &sc->sc_volume_task); 323 324 return (left | (right << 8)); 325 } 326 327 return (0); 328 } 329 330 static uint32_t 331 onyx_setrecsrc(struct snd_mixer *m, uint32_t src) 332 { 333 return (0); 334 } 335