19c3fbfbdSAndreas Tobler /*- 2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3718cf2ccSPedro F. Giffuni * 49c3fbfbdSAndreas Tobler * Copyright 2012 by Andreas Tobler. All rights reserved. 59c3fbfbdSAndreas Tobler * 69c3fbfbdSAndreas Tobler * Redistribution and use in source and binary forms, with or without 79c3fbfbdSAndreas Tobler * modification, are permitted provided that the following conditions 89c3fbfbdSAndreas Tobler * are met: 99c3fbfbdSAndreas Tobler * 1. Redistributions of source code must retain the above copyright 109c3fbfbdSAndreas Tobler * notice, this list of conditions and the following disclaimer. 119c3fbfbdSAndreas Tobler * 2. Redistributions in binary form must reproduce the above copyright 129c3fbfbdSAndreas Tobler * notice, this list of conditions and the following disclaimer in the 139c3fbfbdSAndreas Tobler * documentation and/or other materials provided with the distribution. 149c3fbfbdSAndreas Tobler * 159c3fbfbdSAndreas Tobler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 169c3fbfbdSAndreas Tobler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 179c3fbfbdSAndreas Tobler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 189c3fbfbdSAndreas Tobler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 199c3fbfbdSAndreas Tobler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 209c3fbfbdSAndreas Tobler * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 219c3fbfbdSAndreas Tobler * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 229c3fbfbdSAndreas Tobler * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 239c3fbfbdSAndreas Tobler * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 249c3fbfbdSAndreas Tobler * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 259c3fbfbdSAndreas Tobler * SUCH DAMAGE. 269c3fbfbdSAndreas Tobler */ 279c3fbfbdSAndreas Tobler 289c3fbfbdSAndreas Tobler /* 299c3fbfbdSAndreas Tobler * Apple PCM3052 aka Onyx audio codec. 309c3fbfbdSAndreas Tobler * 319c3fbfbdSAndreas Tobler * Datasheet: http://www.ti.com/product/pcm3052a 329c3fbfbdSAndreas Tobler */ 339c3fbfbdSAndreas Tobler 349c3fbfbdSAndreas Tobler #include <sys/param.h> 359c3fbfbdSAndreas Tobler #include <sys/systm.h> 369c3fbfbdSAndreas Tobler #include <sys/kernel.h> 379c3fbfbdSAndreas Tobler #include <sys/module.h> 389c3fbfbdSAndreas Tobler #include <sys/bus.h> 399c3fbfbdSAndreas Tobler #include <sys/malloc.h> 409c3fbfbdSAndreas Tobler #include <sys/lock.h> 419c3fbfbdSAndreas Tobler #include <sys/mutex.h> 429c3fbfbdSAndreas Tobler #include <machine/dbdma.h> 439c3fbfbdSAndreas Tobler #include <machine/intr_machdep.h> 449c3fbfbdSAndreas Tobler #include <machine/resource.h> 459c3fbfbdSAndreas Tobler #include <machine/bus.h> 469c3fbfbdSAndreas Tobler #include <machine/pio.h> 479c3fbfbdSAndreas Tobler #include <sys/rman.h> 489c3fbfbdSAndreas Tobler 499c3fbfbdSAndreas Tobler #include <dev/iicbus/iicbus.h> 509c3fbfbdSAndreas Tobler #include <dev/iicbus/iiconf.h> 519c3fbfbdSAndreas Tobler #include <dev/ofw/ofw_bus.h> 529c3fbfbdSAndreas Tobler 539c3fbfbdSAndreas Tobler #ifdef HAVE_KERNEL_OPTION_HEADERS 549c3fbfbdSAndreas Tobler #include "opt_snd.h" 559c3fbfbdSAndreas Tobler #endif 569c3fbfbdSAndreas Tobler 579c3fbfbdSAndreas Tobler #include <dev/sound/pcm/sound.h> 589c3fbfbdSAndreas Tobler 599c3fbfbdSAndreas Tobler #include "mixer_if.h" 609c3fbfbdSAndreas Tobler 619c3fbfbdSAndreas Tobler extern kobj_class_t i2s_mixer_class; 629c3fbfbdSAndreas Tobler extern device_t i2s_mixer; 639c3fbfbdSAndreas Tobler 649c3fbfbdSAndreas Tobler struct onyx_softc 659c3fbfbdSAndreas Tobler { 669c3fbfbdSAndreas Tobler device_t sc_dev; 679c3fbfbdSAndreas Tobler uint32_t sc_addr; 689c3fbfbdSAndreas Tobler }; 699c3fbfbdSAndreas Tobler 709c3fbfbdSAndreas Tobler static int onyx_probe(device_t); 719c3fbfbdSAndreas Tobler static int onyx_attach(device_t); 729c3fbfbdSAndreas Tobler static int onyx_init(struct snd_mixer *m); 739c3fbfbdSAndreas Tobler static int onyx_uninit(struct snd_mixer *m); 749c3fbfbdSAndreas Tobler static int onyx_reinit(struct snd_mixer *m); 759c3fbfbdSAndreas Tobler static int onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, 769c3fbfbdSAndreas Tobler unsigned right); 779c3fbfbdSAndreas Tobler static u_int32_t onyx_setrecsrc(struct snd_mixer *m, u_int32_t src); 789c3fbfbdSAndreas Tobler 799c3fbfbdSAndreas Tobler static device_method_t onyx_methods[] = { 809c3fbfbdSAndreas Tobler /* Device interface. */ 819c3fbfbdSAndreas Tobler DEVMETHOD(device_probe, onyx_probe), 829c3fbfbdSAndreas Tobler DEVMETHOD(device_attach, onyx_attach), 839c3fbfbdSAndreas Tobler { 0, 0 } 849c3fbfbdSAndreas Tobler }; 859c3fbfbdSAndreas Tobler 869c3fbfbdSAndreas Tobler static driver_t onyx_driver = { 879c3fbfbdSAndreas Tobler "onyx", 889c3fbfbdSAndreas Tobler onyx_methods, 899c3fbfbdSAndreas Tobler sizeof(struct onyx_softc) 909c3fbfbdSAndreas Tobler }; 919c3fbfbdSAndreas Tobler 923390adfeSJohn Baldwin DRIVER_MODULE(onyx, iicbus, onyx_driver, 0, 0); 939c3fbfbdSAndreas Tobler MODULE_VERSION(onyx, 1); 949c3fbfbdSAndreas Tobler MODULE_DEPEND(onyx, iicbus, 1, 1, 1); 959c3fbfbdSAndreas Tobler 969c3fbfbdSAndreas Tobler static kobj_method_t onyx_mixer_methods[] = { 979c3fbfbdSAndreas Tobler KOBJMETHOD(mixer_init, onyx_init), 989c3fbfbdSAndreas Tobler KOBJMETHOD(mixer_uninit, onyx_uninit), 999c3fbfbdSAndreas Tobler KOBJMETHOD(mixer_reinit, onyx_reinit), 1009c3fbfbdSAndreas Tobler KOBJMETHOD(mixer_set, onyx_set), 1019c3fbfbdSAndreas Tobler KOBJMETHOD(mixer_setrecsrc, onyx_setrecsrc), 1029c3fbfbdSAndreas Tobler KOBJMETHOD_END 1039c3fbfbdSAndreas Tobler }; 1049c3fbfbdSAndreas Tobler 1059c3fbfbdSAndreas Tobler MIXER_DECLARE(onyx_mixer); 1069c3fbfbdSAndreas Tobler 1079c3fbfbdSAndreas Tobler #define PCM3052_IICADDR 0x8C /* Hard-coded I2C slave addr */ 1089c3fbfbdSAndreas Tobler 1099c3fbfbdSAndreas Tobler /* 1109c3fbfbdSAndreas Tobler * PCM3052 registers. 1119c3fbfbdSAndreas Tobler * Numbering in decimal as used in the data sheet. 1129c3fbfbdSAndreas Tobler */ 1139c3fbfbdSAndreas Tobler #define PCM3052_REG_LEFT_ATTN 65 1149c3fbfbdSAndreas Tobler #define PCM3052_REG_RIGHT_ATTN 66 1159c3fbfbdSAndreas Tobler #define PCM3052_REG_CONTROL 67 1169c3fbfbdSAndreas Tobler #define PCM3052_MRST (1 << 7) 1179c3fbfbdSAndreas Tobler #define PCM3052_SRST (1 << 6) 1189c3fbfbdSAndreas Tobler #define PCM3052_REG_DAC_CONTROL 68 1199c3fbfbdSAndreas Tobler #define PCM3052_OVR1 (1 << 6) 1209c3fbfbdSAndreas Tobler #define PCM3052_MUTE_L (1 << 1) 1219c3fbfbdSAndreas Tobler #define PCM3052_MUTE_R (1 << 0) 1229c3fbfbdSAndreas Tobler #define PCM3052_REG_DAC_DEEMPH 69 1239c3fbfbdSAndreas Tobler #define PCM3052_REG_DAC_FILTER 70 1249c3fbfbdSAndreas Tobler #define PCM3052_DAC_FILTER_ALWAYS (1 << 2) 1259c3fbfbdSAndreas Tobler #define PCM3052_REG_OUT_PHASE 71 1269c3fbfbdSAndreas Tobler #define PCM3052_REG_ADC_CONTROL 72 1279c3fbfbdSAndreas Tobler #define PCM3052_REG_ADC_HPF_BP 75 1289c3fbfbdSAndreas Tobler #define PCM3052_HPF_ALWAYS (1 << 2) 1299c3fbfbdSAndreas Tobler #define PCM3052_REG_INFO_1 77 1309c3fbfbdSAndreas Tobler #define PCM3052_REG_INFO_2 78 1319c3fbfbdSAndreas Tobler #define PCM3052_REG_INFO_3 79 1329c3fbfbdSAndreas Tobler #define PCM3052_REG_INFO_4 80 1339c3fbfbdSAndreas Tobler 1349c3fbfbdSAndreas Tobler struct onyx_reg { 1359c3fbfbdSAndreas Tobler u_char LEFT_ATTN; 1369c3fbfbdSAndreas Tobler u_char RIGHT_ATTN; 1379c3fbfbdSAndreas Tobler u_char CONTROL; 1389c3fbfbdSAndreas Tobler u_char DAC_CONTROL; 1399c3fbfbdSAndreas Tobler u_char DAC_DEEMPH; 1409c3fbfbdSAndreas Tobler u_char DAC_FILTER; 1419c3fbfbdSAndreas Tobler u_char OUT_PHASE; 1429c3fbfbdSAndreas Tobler u_char ADC_CONTROL; 1439c3fbfbdSAndreas Tobler u_char ADC_HPF_BP; 1449c3fbfbdSAndreas Tobler u_char INFO_1; 1459c3fbfbdSAndreas Tobler u_char INFO_2; 1469c3fbfbdSAndreas Tobler u_char INFO_3; 1479c3fbfbdSAndreas Tobler u_char INFO_4; 1489c3fbfbdSAndreas Tobler }; 1499c3fbfbdSAndreas Tobler 1509c3fbfbdSAndreas Tobler static const struct onyx_reg onyx_initdata = { 1519c3fbfbdSAndreas Tobler 0x80, /* LEFT_ATTN, Mute default */ 1529c3fbfbdSAndreas Tobler 0x80, /* RIGHT_ATTN, Mute default */ 1539c3fbfbdSAndreas Tobler PCM3052_MRST | PCM3052_SRST, /* CONTROL */ 1549c3fbfbdSAndreas Tobler 0, /* DAC_CONTROL */ 1559c3fbfbdSAndreas Tobler 0, /* DAC_DEEMPH */ 1569c3fbfbdSAndreas Tobler PCM3052_DAC_FILTER_ALWAYS, /* DAC_FILTER */ 1579c3fbfbdSAndreas Tobler 0, /* OUT_PHASE */ 1589c3fbfbdSAndreas Tobler (-1 /* dB */ + 8) & 0xf, /* ADC_CONTROL */ 1599c3fbfbdSAndreas Tobler PCM3052_HPF_ALWAYS, /* ADC_HPF_BP */ 1609c3fbfbdSAndreas Tobler (1 << 2), /* INFO_1 */ 1619c3fbfbdSAndreas Tobler 2, /* INFO_2, */ 1629c3fbfbdSAndreas Tobler 0, /* INFO_3, CLK 0 (level II), 1639c3fbfbdSAndreas Tobler SF 0 (44.1 kHz) */ 1649c3fbfbdSAndreas Tobler 1 /* INFO_4, VALIDL/R 0, 1659c3fbfbdSAndreas Tobler WL 24-bit depth */ 1669c3fbfbdSAndreas Tobler }; 1679c3fbfbdSAndreas Tobler 1689c3fbfbdSAndreas Tobler static int 1699c3fbfbdSAndreas Tobler onyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value) 1709c3fbfbdSAndreas Tobler { 1719c3fbfbdSAndreas Tobler u_int size; 1729c3fbfbdSAndreas Tobler uint8_t buf[16]; 1739c3fbfbdSAndreas Tobler 1749c3fbfbdSAndreas Tobler struct iic_msg msg[] = { 1759c3fbfbdSAndreas Tobler { sc->sc_addr, IIC_M_WR, 0, buf } 1769c3fbfbdSAndreas Tobler }; 1779c3fbfbdSAndreas Tobler 1789c3fbfbdSAndreas Tobler size = 1; 1799c3fbfbdSAndreas Tobler msg[0].len = size + 1; 1809c3fbfbdSAndreas Tobler buf[0] = reg; 1819c3fbfbdSAndreas Tobler buf[1] = value; 1829c3fbfbdSAndreas Tobler 1839c3fbfbdSAndreas Tobler iicbus_transfer(sc->sc_dev, msg, 1); 1849c3fbfbdSAndreas Tobler 1859c3fbfbdSAndreas Tobler return (0); 1869c3fbfbdSAndreas Tobler } 1879c3fbfbdSAndreas Tobler 1889c3fbfbdSAndreas Tobler static int 1899c3fbfbdSAndreas Tobler onyx_probe(device_t dev) 1909c3fbfbdSAndreas Tobler { 1919c3fbfbdSAndreas Tobler const char *name, *compat; 1929c3fbfbdSAndreas Tobler 1939c3fbfbdSAndreas Tobler name = ofw_bus_get_name(dev); 1949c3fbfbdSAndreas Tobler if (name == NULL) 1959c3fbfbdSAndreas Tobler return (ENXIO); 1969c3fbfbdSAndreas Tobler 1979c3fbfbdSAndreas Tobler if (strcmp(name, "codec") == 0) { 1989c3fbfbdSAndreas Tobler if (iicbus_get_addr(dev) != PCM3052_IICADDR) 1999c3fbfbdSAndreas Tobler return (ENXIO); 2009c3fbfbdSAndreas Tobler compat = ofw_bus_get_compat(dev); 2019c3fbfbdSAndreas Tobler if (compat == NULL || strcmp(compat, "pcm3052") != 0) 2029c3fbfbdSAndreas Tobler return (ENXIO); 2039c3fbfbdSAndreas Tobler } else 2049c3fbfbdSAndreas Tobler return (ENXIO); 2059c3fbfbdSAndreas Tobler 2069c3fbfbdSAndreas Tobler device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec"); 2079c3fbfbdSAndreas Tobler return (0); 2089c3fbfbdSAndreas Tobler } 2099c3fbfbdSAndreas Tobler 2109c3fbfbdSAndreas Tobler static int 2119c3fbfbdSAndreas Tobler onyx_attach(device_t dev) 2129c3fbfbdSAndreas Tobler { 2139c3fbfbdSAndreas Tobler struct onyx_softc *sc; 2149c3fbfbdSAndreas Tobler 2159c3fbfbdSAndreas Tobler sc = device_get_softc(dev); 2169c3fbfbdSAndreas Tobler sc->sc_dev = dev; 2179c3fbfbdSAndreas Tobler sc->sc_addr = iicbus_get_addr(dev); 2189c3fbfbdSAndreas Tobler 2199c3fbfbdSAndreas Tobler i2s_mixer_class = &onyx_mixer_class; 2209c3fbfbdSAndreas Tobler i2s_mixer = dev; 2219c3fbfbdSAndreas Tobler 2229c3fbfbdSAndreas Tobler return (0); 2239c3fbfbdSAndreas Tobler } 2249c3fbfbdSAndreas Tobler 2259c3fbfbdSAndreas Tobler static int 2269c3fbfbdSAndreas Tobler onyx_init(struct snd_mixer *m) 2279c3fbfbdSAndreas Tobler { 2289c3fbfbdSAndreas Tobler struct onyx_softc *sc; 2299c3fbfbdSAndreas Tobler u_int x = 0; 2309c3fbfbdSAndreas Tobler 2319c3fbfbdSAndreas Tobler sc = device_get_softc(mix_getdevinfo(m)); 2329c3fbfbdSAndreas Tobler 2339c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN); 2349c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN); 2359c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL); 2369c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_DAC_CONTROL, 2379c3fbfbdSAndreas Tobler onyx_initdata.DAC_CONTROL); 2389c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH); 2399c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER); 2409c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE); 2419c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_ADC_CONTROL, 2429c3fbfbdSAndreas Tobler onyx_initdata.ADC_CONTROL); 2439c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP); 2449c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1); 2459c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2); 2469c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3); 2479c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4); 2489c3fbfbdSAndreas Tobler 2499c3fbfbdSAndreas Tobler x |= SOUND_MASK_VOLUME; 2509c3fbfbdSAndreas Tobler mix_setdevs(m, x); 2519c3fbfbdSAndreas Tobler 2529c3fbfbdSAndreas Tobler return (0); 2539c3fbfbdSAndreas Tobler } 2549c3fbfbdSAndreas Tobler 2559c3fbfbdSAndreas Tobler static int 2569c3fbfbdSAndreas Tobler onyx_uninit(struct snd_mixer *m) 2579c3fbfbdSAndreas Tobler { 2589c3fbfbdSAndreas Tobler return (0); 2599c3fbfbdSAndreas Tobler } 2609c3fbfbdSAndreas Tobler 2619c3fbfbdSAndreas Tobler static int 2629c3fbfbdSAndreas Tobler onyx_reinit(struct snd_mixer *m) 2639c3fbfbdSAndreas Tobler { 2649c3fbfbdSAndreas Tobler return (0); 2659c3fbfbdSAndreas Tobler } 2669c3fbfbdSAndreas Tobler 2679c3fbfbdSAndreas Tobler static int 2689c3fbfbdSAndreas Tobler onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right) 2699c3fbfbdSAndreas Tobler { 2709c3fbfbdSAndreas Tobler struct onyx_softc *sc; 2719c3fbfbdSAndreas Tobler struct mtx *mixer_lock; 2729c3fbfbdSAndreas Tobler int locked; 2739c3fbfbdSAndreas Tobler uint8_t l, r; 2749c3fbfbdSAndreas Tobler 2759c3fbfbdSAndreas Tobler sc = device_get_softc(mix_getdevinfo(m)); 2769c3fbfbdSAndreas Tobler mixer_lock = mixer_get_lock(m); 2779c3fbfbdSAndreas Tobler locked = mtx_owned(mixer_lock); 2789c3fbfbdSAndreas Tobler 2799c3fbfbdSAndreas Tobler switch (dev) { 2809c3fbfbdSAndreas Tobler case SOUND_MIXER_VOLUME: 2819c3fbfbdSAndreas Tobler 2829c3fbfbdSAndreas Tobler /* 2839c3fbfbdSAndreas Tobler * We need to unlock the mixer lock because iicbus_transfer() 2849c3fbfbdSAndreas Tobler * may sleep. The mixer lock itself is unnecessary here 2859c3fbfbdSAndreas Tobler * because it is meant to serialize hardware access, which 2869c3fbfbdSAndreas Tobler * is taken care of by the I2C layer, so this is safe. 2879c3fbfbdSAndreas Tobler */ 2889c3fbfbdSAndreas Tobler if (left > 100 || right > 100) 2899c3fbfbdSAndreas Tobler return (0); 2909c3fbfbdSAndreas Tobler 2919c3fbfbdSAndreas Tobler l = left + 128; 2929c3fbfbdSAndreas Tobler r = right + 128; 2939c3fbfbdSAndreas Tobler 2949c3fbfbdSAndreas Tobler if (locked) 2959c3fbfbdSAndreas Tobler mtx_unlock(mixer_lock); 2969c3fbfbdSAndreas Tobler 2979c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_LEFT_ATTN, l); 2989c3fbfbdSAndreas Tobler onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r); 2999c3fbfbdSAndreas Tobler 3009c3fbfbdSAndreas Tobler if (locked) 3019c3fbfbdSAndreas Tobler mtx_lock(mixer_lock); 3029c3fbfbdSAndreas Tobler 3039c3fbfbdSAndreas Tobler return (left | (right << 8)); 3049c3fbfbdSAndreas Tobler } 3059c3fbfbdSAndreas Tobler 3069c3fbfbdSAndreas Tobler return (0); 3079c3fbfbdSAndreas Tobler } 3089c3fbfbdSAndreas Tobler 3099c3fbfbdSAndreas Tobler static u_int32_t 3109c3fbfbdSAndreas Tobler onyx_setrecsrc(struct snd_mixer *m, u_int32_t src) 3119c3fbfbdSAndreas Tobler { 3129c3fbfbdSAndreas Tobler return (0); 3139c3fbfbdSAndreas Tobler } 314