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 <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
61 extern kobj_class_t i2s_mixer_class;
62 extern device_t i2s_mixer;
63
64 struct onyx_softc
65 {
66 device_t sc_dev;
67 uint32_t sc_addr;
68 };
69
70 static int onyx_probe(device_t);
71 static int onyx_attach(device_t);
72 static int onyx_init(struct snd_mixer *m);
73 static int onyx_uninit(struct snd_mixer *m);
74 static int onyx_reinit(struct snd_mixer *m);
75 static int onyx_set(struct snd_mixer *m, unsigned dev, unsigned left,
76 unsigned right);
77 static u_int32_t onyx_setrecsrc(struct snd_mixer *m, u_int32_t src);
78
79 static 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
86 static driver_t onyx_driver = {
87 "onyx",
88 onyx_methods,
89 sizeof(struct onyx_softc)
90 };
91
92 DRIVER_MODULE(onyx, iicbus, onyx_driver, 0, 0);
93 MODULE_VERSION(onyx, 1);
94 MODULE_DEPEND(onyx, iicbus, 1, 1, 1);
95
96 static kobj_method_t onyx_mixer_methods[] = {
97 KOBJMETHOD(mixer_init, onyx_init),
98 KOBJMETHOD(mixer_uninit, onyx_uninit),
99 KOBJMETHOD(mixer_reinit, onyx_reinit),
100 KOBJMETHOD(mixer_set, onyx_set),
101 KOBJMETHOD(mixer_setrecsrc, onyx_setrecsrc),
102 KOBJMETHOD_END
103 };
104
105 MIXER_DECLARE(onyx_mixer);
106
107 #define PCM3052_IICADDR 0x8C /* Hard-coded I2C slave addr */
108
109 /*
110 * PCM3052 registers.
111 * Numbering in decimal as used in the data sheet.
112 */
113 #define PCM3052_REG_LEFT_ATTN 65
114 #define PCM3052_REG_RIGHT_ATTN 66
115 #define PCM3052_REG_CONTROL 67
116 #define PCM3052_MRST (1 << 7)
117 #define PCM3052_SRST (1 << 6)
118 #define PCM3052_REG_DAC_CONTROL 68
119 #define PCM3052_OVR1 (1 << 6)
120 #define PCM3052_MUTE_L (1 << 1)
121 #define PCM3052_MUTE_R (1 << 0)
122 #define PCM3052_REG_DAC_DEEMPH 69
123 #define PCM3052_REG_DAC_FILTER 70
124 #define PCM3052_DAC_FILTER_ALWAYS (1 << 2)
125 #define PCM3052_REG_OUT_PHASE 71
126 #define PCM3052_REG_ADC_CONTROL 72
127 #define PCM3052_REG_ADC_HPF_BP 75
128 #define PCM3052_HPF_ALWAYS (1 << 2)
129 #define PCM3052_REG_INFO_1 77
130 #define PCM3052_REG_INFO_2 78
131 #define PCM3052_REG_INFO_3 79
132 #define PCM3052_REG_INFO_4 80
133
134 struct onyx_reg {
135 u_char LEFT_ATTN;
136 u_char RIGHT_ATTN;
137 u_char CONTROL;
138 u_char DAC_CONTROL;
139 u_char DAC_DEEMPH;
140 u_char DAC_FILTER;
141 u_char OUT_PHASE;
142 u_char ADC_CONTROL;
143 u_char ADC_HPF_BP;
144 u_char INFO_1;
145 u_char INFO_2;
146 u_char INFO_3;
147 u_char INFO_4;
148 };
149
150 static const struct onyx_reg onyx_initdata = {
151 0x80, /* LEFT_ATTN, Mute default */
152 0x80, /* RIGHT_ATTN, Mute default */
153 PCM3052_MRST | PCM3052_SRST, /* CONTROL */
154 0, /* DAC_CONTROL */
155 0, /* DAC_DEEMPH */
156 PCM3052_DAC_FILTER_ALWAYS, /* DAC_FILTER */
157 0, /* OUT_PHASE */
158 (-1 /* dB */ + 8) & 0xf, /* ADC_CONTROL */
159 PCM3052_HPF_ALWAYS, /* ADC_HPF_BP */
160 (1 << 2), /* INFO_1 */
161 2, /* INFO_2, */
162 0, /* INFO_3, CLK 0 (level II),
163 SF 0 (44.1 kHz) */
164 1 /* INFO_4, VALIDL/R 0,
165 WL 24-bit depth */
166 };
167
168 static int
onyx_write(struct onyx_softc * sc,uint8_t reg,const uint8_t value)169 onyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value)
170 {
171 u_int size;
172 uint8_t buf[16];
173
174 struct iic_msg msg[] = {
175 { sc->sc_addr, IIC_M_WR, 0, buf }
176 };
177
178 size = 1;
179 msg[0].len = size + 1;
180 buf[0] = reg;
181 buf[1] = value;
182
183 iicbus_transfer(sc->sc_dev, msg, 1);
184
185 return (0);
186 }
187
188 static int
onyx_probe(device_t dev)189 onyx_probe(device_t dev)
190 {
191 const char *name, *compat;
192
193 name = ofw_bus_get_name(dev);
194 if (name == NULL)
195 return (ENXIO);
196
197 if (strcmp(name, "codec") == 0) {
198 if (iicbus_get_addr(dev) != PCM3052_IICADDR)
199 return (ENXIO);
200 compat = ofw_bus_get_compat(dev);
201 if (compat == NULL || strcmp(compat, "pcm3052") != 0)
202 return (ENXIO);
203 } else
204 return (ENXIO);
205
206 device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec");
207 return (0);
208 }
209
210 static int
onyx_attach(device_t dev)211 onyx_attach(device_t dev)
212 {
213 struct onyx_softc *sc;
214
215 sc = device_get_softc(dev);
216 sc->sc_dev = dev;
217 sc->sc_addr = iicbus_get_addr(dev);
218
219 i2s_mixer_class = &onyx_mixer_class;
220 i2s_mixer = dev;
221
222 return (0);
223 }
224
225 static int
onyx_init(struct snd_mixer * m)226 onyx_init(struct snd_mixer *m)
227 {
228 struct onyx_softc *sc;
229 u_int x = 0;
230
231 sc = device_get_softc(mix_getdevinfo(m));
232
233 onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN);
234 onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN);
235 onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL);
236 onyx_write(sc, PCM3052_REG_DAC_CONTROL,
237 onyx_initdata.DAC_CONTROL);
238 onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH);
239 onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER);
240 onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE);
241 onyx_write(sc, PCM3052_REG_ADC_CONTROL,
242 onyx_initdata.ADC_CONTROL);
243 onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP);
244 onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1);
245 onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2);
246 onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3);
247 onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4);
248
249 x |= SOUND_MASK_VOLUME;
250 mix_setdevs(m, x);
251
252 return (0);
253 }
254
255 static int
onyx_uninit(struct snd_mixer * m)256 onyx_uninit(struct snd_mixer *m)
257 {
258 return (0);
259 }
260
261 static int
onyx_reinit(struct snd_mixer * m)262 onyx_reinit(struct snd_mixer *m)
263 {
264 return (0);
265 }
266
267 static int
onyx_set(struct snd_mixer * m,unsigned dev,unsigned left,unsigned right)268 onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
269 {
270 struct onyx_softc *sc;
271 struct mtx *mixer_lock;
272 int locked;
273 uint8_t l, r;
274
275 sc = device_get_softc(mix_getdevinfo(m));
276 mixer_lock = mixer_get_lock(m);
277 locked = mtx_owned(mixer_lock);
278
279 switch (dev) {
280 case SOUND_MIXER_VOLUME:
281
282 /*
283 * We need to unlock the mixer lock because iicbus_transfer()
284 * may sleep. The mixer lock itself is unnecessary here
285 * because it is meant to serialize hardware access, which
286 * is taken care of by the I2C layer, so this is safe.
287 */
288 if (left > 100 || right > 100)
289 return (0);
290
291 l = left + 128;
292 r = right + 128;
293
294 if (locked)
295 mtx_unlock(mixer_lock);
296
297 onyx_write(sc, PCM3052_REG_LEFT_ATTN, l);
298 onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r);
299
300 if (locked)
301 mtx_lock(mixer_lock);
302
303 return (left | (right << 8));
304 }
305
306 return (0);
307 }
308
309 static u_int32_t
onyx_setrecsrc(struct snd_mixer * m,u_int32_t src)310 onyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
311 {
312 return (0);
313 }
314