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