xref: /freebsd/sys/dev/sound/macio/onyx.c (revision 1d386b48)
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
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
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 	} else if (strcmp(name, "codec") == 0) {
201 		compat = ofw_bus_get_compat(dev);
202 		if (compat == NULL || strcmp(compat, "pcm3052") != 0)
203 			return (ENXIO);
204 	} else
205 		return (ENXIO);
206 
207 	device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec");
208 	return (0);
209 }
210 
211 static int
212 onyx_attach(device_t dev)
213 {
214 	struct onyx_softc *sc;
215 
216 	sc = device_get_softc(dev);
217 	sc->sc_dev = dev;
218 	sc->sc_addr = iicbus_get_addr(dev);
219 
220 	i2s_mixer_class = &onyx_mixer_class;
221 	i2s_mixer = dev;
222 
223 	return (0);
224 }
225 
226 static int
227 onyx_init(struct snd_mixer *m)
228 {
229 	struct onyx_softc *sc;
230 	u_int  x = 0;
231 
232 	sc = device_get_softc(mix_getdevinfo(m));
233 
234 	onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN);
235 	onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN);
236 	onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL);
237 	onyx_write(sc, PCM3052_REG_DAC_CONTROL,
238 		      onyx_initdata.DAC_CONTROL);
239 	onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH);
240 	onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER);
241 	onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE);
242 	onyx_write(sc, PCM3052_REG_ADC_CONTROL,
243 		      onyx_initdata.ADC_CONTROL);
244 	onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP);
245 	onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1);
246 	onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2);
247 	onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3);
248 	onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4);
249 
250 	x |= SOUND_MASK_VOLUME;
251 	mix_setdevs(m, x);
252 
253 	return (0);
254 }
255 
256 static int
257 onyx_uninit(struct snd_mixer *m)
258 {
259 	return (0);
260 }
261 
262 static int
263 onyx_reinit(struct snd_mixer *m)
264 {
265 	return (0);
266 }
267 
268 static int
269 onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
270 {
271 	struct onyx_softc *sc;
272 	struct mtx *mixer_lock;
273 	int locked;
274 	uint8_t l, r;
275 
276 	sc = device_get_softc(mix_getdevinfo(m));
277 	mixer_lock = mixer_get_lock(m);
278 	locked = mtx_owned(mixer_lock);
279 
280 	switch (dev) {
281 	case SOUND_MIXER_VOLUME:
282 
283 		/*
284 		 * We need to unlock the mixer lock because iicbus_transfer()
285 		 * may sleep. The mixer lock itself is unnecessary here
286 		 * because it is meant to serialize hardware access, which
287 		 * is taken care of by the I2C layer, so this is safe.
288 		 */
289 		if (left > 100 || right > 100)
290 			return (0);
291 
292 		l = left + 128;
293 		r = right + 128;
294 
295 		if (locked)
296 			mtx_unlock(mixer_lock);
297 
298 		onyx_write(sc, PCM3052_REG_LEFT_ATTN, l);
299 		onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r);
300 
301 		if (locked)
302 			mtx_lock(mixer_lock);
303 
304 		return (left | (right << 8));
305 	}
306 
307 	return (0);
308 }
309 
310 static u_int32_t
311 onyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
312 {
313 	return (0);
314 }
315