1/*-
2 * Copyright 2012 by Andreas Tobler. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD$
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
61extern kobj_class_t i2s_mixer_class;
62extern device_t	i2s_mixer;
63
64struct onyx_softc
65{
66	device_t sc_dev;
67	uint32_t sc_addr;
68};
69
70static int	onyx_probe(device_t);
71static int	onyx_attach(device_t);
72static int	onyx_init(struct snd_mixer *m);
73static int	onyx_uninit(struct snd_mixer *m);
74static int	onyx_reinit(struct snd_mixer *m);
75static int	onyx_set(struct snd_mixer *m, unsigned dev, unsigned left,
76			    unsigned right);
77static u_int32_t	onyx_setrecsrc(struct snd_mixer *m, u_int32_t src);
78
79static 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
86static driver_t onyx_driver = {
87	"onyx",
88	onyx_methods,
89	sizeof(struct onyx_softc)
90};
91static devclass_t onyx_devclass;
92
93DRIVER_MODULE(onyx, iicbus, onyx_driver, onyx_devclass, 0, 0);
94MODULE_VERSION(onyx, 1);
95MODULE_DEPEND(onyx, iicbus, 1, 1, 1);
96
97static kobj_method_t onyx_mixer_methods[] = {
98	KOBJMETHOD(mixer_init,		onyx_init),
99	KOBJMETHOD(mixer_uninit,	onyx_uninit),
100	KOBJMETHOD(mixer_reinit,	onyx_reinit),
101	KOBJMETHOD(mixer_set,		onyx_set),
102	KOBJMETHOD(mixer_setrecsrc,	onyx_setrecsrc),
103	KOBJMETHOD_END
104};
105
106MIXER_DECLARE(onyx_mixer);
107
108#define PCM3052_IICADDR	0x8C	/* Hard-coded I2C slave addr */
109
110/*
111 * PCM3052 registers.
112 * Numbering in decimal as used in the data sheet.
113 */
114#define PCM3052_REG_LEFT_ATTN       65
115#define PCM3052_REG_RIGHT_ATTN      66
116#define PCM3052_REG_CONTROL         67
117#define PCM3052_MRST                (1 << 7)
118#define PCM3052_SRST                (1 << 6)
119#define PCM3052_REG_DAC_CONTROL     68
120#define PCM3052_OVR1                (1 << 6)
121#define PCM3052_MUTE_L              (1 << 1)
122#define PCM3052_MUTE_R              (1 << 0)
123#define PCM3052_REG_DAC_DEEMPH      69
124#define PCM3052_REG_DAC_FILTER      70
125#define PCM3052_DAC_FILTER_ALWAYS   (1 << 2)
126#define PCM3052_REG_OUT_PHASE       71
127#define PCM3052_REG_ADC_CONTROL     72
128#define PCM3052_REG_ADC_HPF_BP      75
129#define PCM3052_HPF_ALWAYS          (1 << 2)
130#define PCM3052_REG_INFO_1          77
131#define PCM3052_REG_INFO_2          78
132#define PCM3052_REG_INFO_3          79
133#define PCM3052_REG_INFO_4          80
134
135struct onyx_reg {
136	u_char LEFT_ATTN;
137	u_char RIGHT_ATTN;
138	u_char CONTROL;
139	u_char DAC_CONTROL;
140	u_char DAC_DEEMPH;
141	u_char DAC_FILTER;
142	u_char OUT_PHASE;
143	u_char ADC_CONTROL;
144	u_char ADC_HPF_BP;
145	u_char INFO_1;
146	u_char INFO_2;
147	u_char INFO_3;
148	u_char INFO_4;
149};
150
151static const struct onyx_reg onyx_initdata = {
152	0x80,				  /* LEFT_ATTN, Mute default */
153	0x80,				  /* RIGHT_ATTN, Mute default */
154	PCM3052_MRST | PCM3052_SRST,      /* CONTROL */
155	0,                                /* DAC_CONTROL */
156	0,				  /* DAC_DEEMPH */
157	PCM3052_DAC_FILTER_ALWAYS,	  /* DAC_FILTER */
158	0,				  /* OUT_PHASE */
159	(-1 /* dB */ + 8) & 0xf,          /* ADC_CONTROL */
160	PCM3052_HPF_ALWAYS,		  /* ADC_HPF_BP */
161	(1 << 2),			  /* INFO_1 */
162	2,				  /* INFO_2,  */
163	0,				  /* INFO_3, CLK 0 (level II),
164					     SF 0 (44.1 kHz) */
165	1				  /* INFO_4, VALIDL/R 0,
166					     WL 24-bit depth */
167};
168
169static int
170onyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value)
171{
172	u_int size;
173	uint8_t buf[16];
174
175	struct iic_msg msg[] = {
176		{ sc->sc_addr, IIC_M_WR, 0, buf }
177	};
178
179	size = 1;
180	msg[0].len = size + 1;
181	buf[0] = reg;
182	buf[1] = value;
183
184	iicbus_transfer(sc->sc_dev, msg, 1);
185
186	return (0);
187}
188
189static int
190onyx_probe(device_t dev)
191{
192	const char *name, *compat;
193
194	name = ofw_bus_get_name(dev);
195	if (name == NULL)
196		return (ENXIO);
197
198	if (strcmp(name, "codec") == 0) {
199		if (iicbus_get_addr(dev) != PCM3052_IICADDR)
200			return (ENXIO);
201	} else if (strcmp(name, "codec") == 0) {
202		compat = ofw_bus_get_compat(dev);
203		if (compat == NULL || strcmp(compat, "pcm3052") != 0)
204			return (ENXIO);
205	} else
206		return (ENXIO);
207
208	device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec");
209	return (0);
210}
211
212static int
213onyx_attach(device_t dev)
214{
215	struct onyx_softc *sc;
216
217	sc = device_get_softc(dev);
218	sc->sc_dev = dev;
219	sc->sc_addr = iicbus_get_addr(dev);
220
221	i2s_mixer_class = &onyx_mixer_class;
222	i2s_mixer = dev;
223
224	return (0);
225}
226
227static int
228onyx_init(struct snd_mixer *m)
229{
230	struct onyx_softc *sc;
231	u_int  x = 0;
232
233	sc = device_get_softc(mix_getdevinfo(m));
234
235	onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN);
236	onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN);
237	onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL);
238	onyx_write(sc, PCM3052_REG_DAC_CONTROL,
239		      onyx_initdata.DAC_CONTROL);
240	onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH);
241	onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER);
242	onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE);
243	onyx_write(sc, PCM3052_REG_ADC_CONTROL,
244		      onyx_initdata.ADC_CONTROL);
245	onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP);
246	onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1);
247	onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2);
248	onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3);
249	onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4);
250
251	x |= SOUND_MASK_VOLUME;
252	mix_setdevs(m, x);
253
254	return (0);
255}
256
257static int
258onyx_uninit(struct snd_mixer *m)
259{
260	return (0);
261}
262
263static int
264onyx_reinit(struct snd_mixer *m)
265{
266	return (0);
267}
268
269static int
270onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
271{
272	struct onyx_softc *sc;
273	struct mtx *mixer_lock;
274	int locked;
275	uint8_t l, r;
276
277	sc = device_get_softc(mix_getdevinfo(m));
278	mixer_lock = mixer_get_lock(m);
279	locked = mtx_owned(mixer_lock);
280
281	switch (dev) {
282	case SOUND_MIXER_VOLUME:
283
284		/*
285		 * We need to unlock the mixer lock because iicbus_transfer()
286		 * may sleep. The mixer lock itself is unnecessary here
287		 * because it is meant to serialize hardware access, which
288		 * is taken care of by the I2C layer, so this is safe.
289		 */
290		if (left > 100 || right > 100)
291			return (0);
292
293		l = left + 128;
294		r = right + 128;
295
296		if (locked)
297			mtx_unlock(mixer_lock);
298
299		onyx_write(sc, PCM3052_REG_LEFT_ATTN, l);
300		onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r);
301
302		if (locked)
303			mtx_lock(mixer_lock);
304
305		return (left | (right << 8));
306	}
307
308	return (0);
309}
310
311static u_int32_t
312onyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
313{
314	return (0);
315}
316