xref: /freebsd/sys/dev/sound/macio/onyx.c (revision f9bc005b8ef3b507a5ecbc2d7fe4411ef70eff38)
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 <sys/taskqueue.h>
43 #include <machine/dbdma.h>
44 #include <machine/intr_machdep.h>
45 #include <machine/resource.h>
46 #include <machine/bus.h>
47 #include <machine/pio.h>
48 #include <sys/rman.h>
49 
50 #include <dev/iicbus/iicbus.h>
51 #include <dev/iicbus/iiconf.h>
52 #include <dev/ofw/ofw_bus.h>
53 
54 #ifdef HAVE_KERNEL_OPTION_HEADERS
55 #include "opt_snd.h"
56 #endif
57 
58 #include <dev/sound/pcm/sound.h>
59 
60 #include "mixer_if.h"
61 
62 extern kobj_class_t i2s_mixer_class;
63 extern device_t	i2s_mixer;
64 
65 struct onyx_softc
66 {
67 	device_t sc_dev;
68 	uint32_t sc_addr;
69 	struct mtx sc_volume_mtx;
70 	struct task sc_volume_task;
71 	uint8_t sc_left;
72 	uint8_t sc_right;
73 };
74 
75 static int	onyx_probe(device_t);
76 static int	onyx_attach(device_t);
77 static int	onyx_init(struct snd_mixer *m);
78 static int	onyx_uninit(struct snd_mixer *m);
79 static int	onyx_reinit(struct snd_mixer *m);
80 static int	onyx_set(struct snd_mixer *m, unsigned dev, unsigned left,
81 			    unsigned right);
82 static uint32_t	onyx_setrecsrc(struct snd_mixer *m, uint32_t src);
83 
84 static device_method_t onyx_methods[] = {
85 	/* Device interface. */
86 	DEVMETHOD(device_probe,		onyx_probe),
87 	DEVMETHOD(device_attach,	onyx_attach),
88 	DEVMETHOD_END
89 };
90 
91 static driver_t onyx_driver = {
92 	"onyx",
93 	onyx_methods,
94 	sizeof(struct onyx_softc)
95 };
96 
97 DRIVER_MODULE(onyx, iicbus, onyx_driver, 0, 0);
98 MODULE_VERSION(onyx, 1);
99 MODULE_DEPEND(onyx, iicbus, 1, 1, 1);
100 
101 static kobj_method_t onyx_mixer_methods[] = {
102 	KOBJMETHOD(mixer_init,		onyx_init),
103 	KOBJMETHOD(mixer_uninit,	onyx_uninit),
104 	KOBJMETHOD(mixer_reinit,	onyx_reinit),
105 	KOBJMETHOD(mixer_set,		onyx_set),
106 	KOBJMETHOD(mixer_setrecsrc,	onyx_setrecsrc),
107 	KOBJMETHOD_END
108 };
109 
110 MIXER_DECLARE(onyx_mixer);
111 
112 #define PCM3052_IICADDR	0x8C	/* Hard-coded I2C slave addr */
113 
114 /*
115  * PCM3052 registers.
116  * Numbering in decimal as used in the data sheet.
117  */
118 #define PCM3052_REG_LEFT_ATTN       65
119 #define PCM3052_REG_RIGHT_ATTN      66
120 #define PCM3052_REG_CONTROL         67
121 #define PCM3052_MRST                (1 << 7)
122 #define PCM3052_SRST                (1 << 6)
123 #define PCM3052_REG_DAC_CONTROL     68
124 #define PCM3052_OVR1                (1 << 6)
125 #define PCM3052_MUTE_L              (1 << 1)
126 #define PCM3052_MUTE_R              (1 << 0)
127 #define PCM3052_REG_DAC_DEEMPH      69
128 #define PCM3052_REG_DAC_FILTER      70
129 #define PCM3052_DAC_FILTER_ALWAYS   (1 << 2)
130 #define PCM3052_REG_OUT_PHASE       71
131 #define PCM3052_REG_ADC_CONTROL     72
132 #define PCM3052_REG_ADC_HPF_BP      75
133 #define PCM3052_HPF_ALWAYS          (1 << 2)
134 #define PCM3052_REG_INFO_1          77
135 #define PCM3052_REG_INFO_2          78
136 #define PCM3052_REG_INFO_3          79
137 #define PCM3052_REG_INFO_4          80
138 
139 struct onyx_reg {
140 	uint8_t LEFT_ATTN;
141 	uint8_t RIGHT_ATTN;
142 	uint8_t CONTROL;
143 	uint8_t DAC_CONTROL;
144 	uint8_t DAC_DEEMPH;
145 	uint8_t DAC_FILTER;
146 	uint8_t OUT_PHASE;
147 	uint8_t ADC_CONTROL;
148 	uint8_t ADC_HPF_BP;
149 	uint8_t INFO_1;
150 	uint8_t INFO_2;
151 	uint8_t INFO_3;
152 	uint8_t INFO_4;
153 };
154 
155 static const struct onyx_reg onyx_initdata = {
156 	0x80,				  /* LEFT_ATTN, Mute default */
157 	0x80,				  /* RIGHT_ATTN, Mute default */
158 	PCM3052_MRST | PCM3052_SRST,      /* CONTROL */
159 	0,                                /* DAC_CONTROL */
160 	0,				  /* DAC_DEEMPH */
161 	PCM3052_DAC_FILTER_ALWAYS,	  /* DAC_FILTER */
162 	0,				  /* OUT_PHASE */
163 	(-1 /* dB */ + 8) & 0xf,          /* ADC_CONTROL */
164 	PCM3052_HPF_ALWAYS,		  /* ADC_HPF_BP */
165 	(1 << 2),			  /* INFO_1 */
166 	2,				  /* INFO_2,  */
167 	0,				  /* INFO_3, CLK 0 (level II),
168 					     SF 0 (44.1 kHz) */
169 	1				  /* INFO_4, VALIDL/R 0,
170 					     WL 24-bit depth */
171 };
172 
173 static int
174 onyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value)
175 {
176 	unsigned int size;
177 	uint8_t buf[16];
178 
179 	struct iic_msg msg[] = {
180 		{ sc->sc_addr, IIC_M_WR, 0, buf }
181 	};
182 
183 	size = 1;
184 	msg[0].len = size + 1;
185 	buf[0] = reg;
186 	buf[1] = value;
187 
188 	iicbus_transfer(sc->sc_dev, msg, 1);
189 
190 	return (0);
191 }
192 
193 /*
194  * onyx_write() sleeps in iicbus_transfer(), so onyx_set() cannot program the
195  * volume registers inline. Hand the new values to a task instead, which runs
196  * with no lock held.
197  */
198 static void
199 onyx_volume_task(void *arg, int pending __unused)
200 {
201 	struct onyx_softc *sc = arg;
202 	uint8_t l, r;
203 
204 	mtx_lock(&sc->sc_volume_mtx);
205 	l = sc->sc_left;
206 	r = sc->sc_right;
207 	mtx_unlock(&sc->sc_volume_mtx);
208 
209 	onyx_write(sc, PCM3052_REG_LEFT_ATTN, l);
210 	onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r);
211 }
212 
213 static int
214 onyx_probe(device_t dev)
215 {
216 	const char *name, *compat;
217 
218 	name = ofw_bus_get_name(dev);
219 	if (name == NULL)
220 		return (ENXIO);
221 
222 	if (strcmp(name, "codec") == 0) {
223 		if (iicbus_get_addr(dev) != PCM3052_IICADDR)
224 			return (ENXIO);
225 		compat = ofw_bus_get_compat(dev);
226 		if (compat == NULL || strcmp(compat, "pcm3052") != 0)
227 			return (ENXIO);
228 	} else
229 		return (ENXIO);
230 
231 	device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec");
232 	return (0);
233 }
234 
235 static int
236 onyx_attach(device_t dev)
237 {
238 	struct onyx_softc *sc;
239 
240 	sc = device_get_softc(dev);
241 	sc->sc_dev = dev;
242 	sc->sc_addr = iicbus_get_addr(dev);
243 
244 	mtx_init(&sc->sc_volume_mtx, "onyx volume", NULL, MTX_DEF);
245 	TASK_INIT(&sc->sc_volume_task, 0, onyx_volume_task, sc);
246 
247 	i2s_mixer_class = &onyx_mixer_class;
248 	i2s_mixer = dev;
249 
250 	return (0);
251 }
252 
253 static int
254 onyx_init(struct snd_mixer *m)
255 {
256 	struct onyx_softc *sc;
257 	unsigned int  x = 0;
258 
259 	sc = device_get_softc(mix_getdevinfo(m));
260 
261 	onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN);
262 	onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN);
263 	onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL);
264 	onyx_write(sc, PCM3052_REG_DAC_CONTROL,
265 		      onyx_initdata.DAC_CONTROL);
266 	onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH);
267 	onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER);
268 	onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE);
269 	onyx_write(sc, PCM3052_REG_ADC_CONTROL,
270 		      onyx_initdata.ADC_CONTROL);
271 	onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP);
272 	onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1);
273 	onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2);
274 	onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3);
275 	onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4);
276 
277 	x |= SOUND_MASK_VOLUME;
278 	mix_setdevs(m, x);
279 
280 	return (0);
281 }
282 
283 static int
284 onyx_uninit(struct snd_mixer *m)
285 {
286 	struct onyx_softc *sc;
287 
288 	sc = device_get_softc(mix_getdevinfo(m));
289 
290 	taskqueue_drain(taskqueue_thread, &sc->sc_volume_task);
291 
292 	return (0);
293 }
294 
295 static int
296 onyx_reinit(struct snd_mixer *m)
297 {
298 	return (0);
299 }
300 
301 static int
302 onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
303 {
304 	struct onyx_softc *sc;
305 	uint8_t l, r;
306 
307 	sc = device_get_softc(mix_getdevinfo(m));
308 
309 	switch (dev) {
310 	case SOUND_MIXER_VOLUME:
311 		if (left > 100 || right > 100)
312 			return (0);
313 
314 		l = left + 128;
315 		r = right + 128;
316 
317 		mtx_lock(&sc->sc_volume_mtx);
318 		sc->sc_left = l;
319 		sc->sc_right = r;
320 		mtx_unlock(&sc->sc_volume_mtx);
321 
322 		taskqueue_enqueue(taskqueue_thread, &sc->sc_volume_task);
323 
324 		return (left | (right << 8));
325 	}
326 
327 	return (0);
328 }
329 
330 static uint32_t
331 onyx_setrecsrc(struct snd_mixer *m, uint32_t src)
332 {
333 	return (0);
334 }
335