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
onyx_write(struct onyx_softc * sc,uint8_t reg,const uint8_t value)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
onyx_probe(device_t dev)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
onyx_attach(device_t dev)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
onyx_init(struct snd_mixer * m)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
onyx_uninit(struct snd_mixer * m)2569c3fbfbdSAndreas Tobler onyx_uninit(struct snd_mixer *m)
2579c3fbfbdSAndreas Tobler {
2589c3fbfbdSAndreas Tobler return (0);
2599c3fbfbdSAndreas Tobler }
2609c3fbfbdSAndreas Tobler
2619c3fbfbdSAndreas Tobler static int
onyx_reinit(struct snd_mixer * m)2629c3fbfbdSAndreas Tobler onyx_reinit(struct snd_mixer *m)
2639c3fbfbdSAndreas Tobler {
2649c3fbfbdSAndreas Tobler return (0);
2659c3fbfbdSAndreas Tobler }
2669c3fbfbdSAndreas Tobler
2679c3fbfbdSAndreas Tobler static int
onyx_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)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
onyx_setrecsrc(struct snd_mixer * m,u_int32_t src)3109c3fbfbdSAndreas Tobler onyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
3119c3fbfbdSAndreas Tobler {
3129c3fbfbdSAndreas Tobler return (0);
3139c3fbfbdSAndreas Tobler }
314