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