1239401Sandreast/*-
2239401Sandreast * Copyright 2012 by Andreas Tobler. All rights reserved.
3239401Sandreast *
4239401Sandreast * Redistribution and use in source and binary forms, with or without
5239401Sandreast * modification, are permitted provided that the following conditions
6239401Sandreast * are met:
7239401Sandreast * 1. Redistributions of source code must retain the above copyright
8239401Sandreast *    notice, this list of conditions and the following disclaimer.
9239401Sandreast * 2. Redistributions in binary form must reproduce the above copyright
10239401Sandreast *    notice, this list of conditions and the following disclaimer in the
11239401Sandreast *    documentation and/or other materials provided with the distribution.
12239401Sandreast *
13239401Sandreast * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14239401Sandreast * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15239401Sandreast * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16239401Sandreast * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17239401Sandreast * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18239401Sandreast * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19239401Sandreast * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20239401Sandreast * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21239401Sandreast * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22239401Sandreast * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23239401Sandreast * SUCH DAMAGE.
24239401Sandreast *
25239401Sandreast * $FreeBSD$
26239401Sandreast */
27239401Sandreast
28239401Sandreast/*
29239401Sandreast * Apple PCM3052 aka Onyx audio codec.
30239401Sandreast *
31239401Sandreast * Datasheet: http://www.ti.com/product/pcm3052a
32239401Sandreast */
33239401Sandreast
34239401Sandreast#include <sys/param.h>
35239401Sandreast#include <sys/systm.h>
36239401Sandreast#include <sys/kernel.h>
37239401Sandreast#include <sys/module.h>
38239401Sandreast#include <sys/bus.h>
39239401Sandreast#include <sys/malloc.h>
40239401Sandreast#include <sys/lock.h>
41239401Sandreast#include <sys/mutex.h>
42239401Sandreast#include <machine/dbdma.h>
43239401Sandreast#include <machine/intr_machdep.h>
44239401Sandreast#include <machine/resource.h>
45239401Sandreast#include <machine/bus.h>
46239401Sandreast#include <machine/pio.h>
47239401Sandreast#include <sys/rman.h>
48239401Sandreast
49239401Sandreast#include <dev/iicbus/iicbus.h>
50239401Sandreast#include <dev/iicbus/iiconf.h>
51239401Sandreast#include <dev/ofw/ofw_bus.h>
52239401Sandreast
53239401Sandreast#ifdef HAVE_KERNEL_OPTION_HEADERS
54239401Sandreast#include "opt_snd.h"
55239401Sandreast#endif
56239401Sandreast
57239401Sandreast#include <dev/sound/pcm/sound.h>
58239401Sandreast
59239401Sandreast#include "mixer_if.h"
60239401Sandreast
61239401Sandreastextern kobj_class_t i2s_mixer_class;
62239401Sandreastextern device_t	i2s_mixer;
63239401Sandreast
64239401Sandreaststruct onyx_softc
65239401Sandreast{
66239401Sandreast	device_t sc_dev;
67239401Sandreast	uint32_t sc_addr;
68239401Sandreast};
69239401Sandreast
70239401Sandreaststatic int	onyx_probe(device_t);
71239401Sandreaststatic int	onyx_attach(device_t);
72239401Sandreaststatic int	onyx_init(struct snd_mixer *m);
73239401Sandreaststatic int	onyx_uninit(struct snd_mixer *m);
74239401Sandreaststatic int	onyx_reinit(struct snd_mixer *m);
75239401Sandreaststatic int	onyx_set(struct snd_mixer *m, unsigned dev, unsigned left,
76239401Sandreast			    unsigned right);
77239401Sandreaststatic u_int32_t	onyx_setrecsrc(struct snd_mixer *m, u_int32_t src);
78239401Sandreast
79239401Sandreaststatic device_method_t onyx_methods[] = {
80239401Sandreast	/* Device interface. */
81239401Sandreast	DEVMETHOD(device_probe,		onyx_probe),
82239401Sandreast	DEVMETHOD(device_attach,	onyx_attach),
83239401Sandreast	{ 0, 0 }
84239401Sandreast};
85239401Sandreast
86239401Sandreaststatic driver_t onyx_driver = {
87239401Sandreast	"onyx",
88239401Sandreast	onyx_methods,
89239401Sandreast	sizeof(struct onyx_softc)
90239401Sandreast};
91239401Sandreaststatic devclass_t onyx_devclass;
92239401Sandreast
93239401SandreastDRIVER_MODULE(onyx, iicbus, onyx_driver, onyx_devclass, 0, 0);
94239401SandreastMODULE_VERSION(onyx, 1);
95239401SandreastMODULE_DEPEND(onyx, iicbus, 1, 1, 1);
96239401Sandreast
97239401Sandreaststatic kobj_method_t onyx_mixer_methods[] = {
98239401Sandreast	KOBJMETHOD(mixer_init,		onyx_init),
99239401Sandreast	KOBJMETHOD(mixer_uninit,	onyx_uninit),
100239401Sandreast	KOBJMETHOD(mixer_reinit,	onyx_reinit),
101239401Sandreast	KOBJMETHOD(mixer_set,		onyx_set),
102239401Sandreast	KOBJMETHOD(mixer_setrecsrc,	onyx_setrecsrc),
103239401Sandreast	KOBJMETHOD_END
104239401Sandreast};
105239401Sandreast
106239401SandreastMIXER_DECLARE(onyx_mixer);
107239401Sandreast
108239401Sandreast#define PCM3052_IICADDR	0x8C	/* Hard-coded I2C slave addr */
109239401Sandreast
110239401Sandreast/*
111239401Sandreast * PCM3052 registers.
112239401Sandreast * Numbering in decimal as used in the data sheet.
113239401Sandreast */
114239401Sandreast#define PCM3052_REG_LEFT_ATTN       65
115239401Sandreast#define PCM3052_REG_RIGHT_ATTN      66
116239401Sandreast#define PCM3052_REG_CONTROL         67
117239401Sandreast#define PCM3052_MRST                (1 << 7)
118239401Sandreast#define PCM3052_SRST                (1 << 6)
119239401Sandreast#define PCM3052_REG_DAC_CONTROL     68
120239401Sandreast#define PCM3052_OVR1                (1 << 6)
121239401Sandreast#define PCM3052_MUTE_L              (1 << 1)
122239401Sandreast#define PCM3052_MUTE_R              (1 << 0)
123239401Sandreast#define PCM3052_REG_DAC_DEEMPH      69
124239401Sandreast#define PCM3052_REG_DAC_FILTER      70
125239401Sandreast#define PCM3052_DAC_FILTER_ALWAYS   (1 << 2)
126239401Sandreast#define PCM3052_REG_OUT_PHASE       71
127239401Sandreast#define PCM3052_REG_ADC_CONTROL     72
128239401Sandreast#define PCM3052_REG_ADC_HPF_BP      75
129239401Sandreast#define PCM3052_HPF_ALWAYS          (1 << 2)
130239401Sandreast#define PCM3052_REG_INFO_1          77
131239401Sandreast#define PCM3052_REG_INFO_2          78
132239401Sandreast#define PCM3052_REG_INFO_3          79
133239401Sandreast#define PCM3052_REG_INFO_4          80
134239401Sandreast
135239401Sandreaststruct onyx_reg {
136239401Sandreast	u_char LEFT_ATTN;
137239401Sandreast	u_char RIGHT_ATTN;
138239401Sandreast	u_char CONTROL;
139239401Sandreast	u_char DAC_CONTROL;
140239401Sandreast	u_char DAC_DEEMPH;
141239401Sandreast	u_char DAC_FILTER;
142239401Sandreast	u_char OUT_PHASE;
143239401Sandreast	u_char ADC_CONTROL;
144239401Sandreast	u_char ADC_HPF_BP;
145239401Sandreast	u_char INFO_1;
146239401Sandreast	u_char INFO_2;
147239401Sandreast	u_char INFO_3;
148239401Sandreast	u_char INFO_4;
149239401Sandreast};
150239401Sandreast
151239401Sandreaststatic const struct onyx_reg onyx_initdata = {
152239401Sandreast	0x80,				  /* LEFT_ATTN, Mute default */
153239401Sandreast	0x80,				  /* RIGHT_ATTN, Mute default */
154239401Sandreast	PCM3052_MRST | PCM3052_SRST,      /* CONTROL */
155239401Sandreast	0,                                /* DAC_CONTROL */
156239401Sandreast	0,				  /* DAC_DEEMPH */
157239401Sandreast	PCM3052_DAC_FILTER_ALWAYS,	  /* DAC_FILTER */
158239401Sandreast	0,				  /* OUT_PHASE */
159239401Sandreast	(-1 /* dB */ + 8) & 0xf,          /* ADC_CONTROL */
160239401Sandreast	PCM3052_HPF_ALWAYS,		  /* ADC_HPF_BP */
161239401Sandreast	(1 << 2),			  /* INFO_1 */
162239401Sandreast	2,				  /* INFO_2,  */
163239401Sandreast	0,				  /* INFO_3, CLK 0 (level II),
164239401Sandreast					     SF 0 (44.1 kHz) */
165239401Sandreast	1				  /* INFO_4, VALIDL/R 0,
166239401Sandreast					     WL 24-bit depth */
167239401Sandreast};
168239401Sandreast
169239401Sandreaststatic int
170239401Sandreastonyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value)
171239401Sandreast{
172239401Sandreast	u_int size;
173239401Sandreast	uint8_t buf[16];
174239401Sandreast
175239401Sandreast	struct iic_msg msg[] = {
176239401Sandreast		{ sc->sc_addr, IIC_M_WR, 0, buf }
177239401Sandreast	};
178239401Sandreast
179239401Sandreast	size = 1;
180239401Sandreast	msg[0].len = size + 1;
181239401Sandreast	buf[0] = reg;
182239401Sandreast	buf[1] = value;
183239401Sandreast
184239401Sandreast	iicbus_transfer(sc->sc_dev, msg, 1);
185239401Sandreast
186239401Sandreast	return (0);
187239401Sandreast}
188239401Sandreast
189239401Sandreaststatic int
190239401Sandreastonyx_probe(device_t dev)
191239401Sandreast{
192239401Sandreast	const char *name, *compat;
193239401Sandreast
194239401Sandreast	name = ofw_bus_get_name(dev);
195239401Sandreast	if (name == NULL)
196239401Sandreast		return (ENXIO);
197239401Sandreast
198239401Sandreast	if (strcmp(name, "codec") == 0) {
199239401Sandreast		if (iicbus_get_addr(dev) != PCM3052_IICADDR)
200239401Sandreast			return (ENXIO);
201239401Sandreast	} else if (strcmp(name, "codec") == 0) {
202239401Sandreast		compat = ofw_bus_get_compat(dev);
203239401Sandreast		if (compat == NULL || strcmp(compat, "pcm3052") != 0)
204239401Sandreast			return (ENXIO);
205239401Sandreast	} else
206239401Sandreast		return (ENXIO);
207239401Sandreast
208239401Sandreast	device_set_desc(dev, "Texas Instruments PCM3052 Audio Codec");
209239401Sandreast	return (0);
210239401Sandreast}
211239401Sandreast
212239401Sandreaststatic int
213239401Sandreastonyx_attach(device_t dev)
214239401Sandreast{
215239401Sandreast	struct onyx_softc *sc;
216239401Sandreast
217239401Sandreast	sc = device_get_softc(dev);
218239401Sandreast	sc->sc_dev = dev;
219239401Sandreast	sc->sc_addr = iicbus_get_addr(dev);
220239401Sandreast
221239401Sandreast	i2s_mixer_class = &onyx_mixer_class;
222239401Sandreast	i2s_mixer = dev;
223239401Sandreast
224239401Sandreast	return (0);
225239401Sandreast}
226239401Sandreast
227239401Sandreaststatic int
228239401Sandreastonyx_init(struct snd_mixer *m)
229239401Sandreast{
230239401Sandreast	struct onyx_softc *sc;
231239401Sandreast	u_int  x = 0;
232239401Sandreast
233239401Sandreast	sc = device_get_softc(mix_getdevinfo(m));
234239401Sandreast
235239401Sandreast	onyx_write(sc, PCM3052_REG_LEFT_ATTN, onyx_initdata.LEFT_ATTN);
236239401Sandreast	onyx_write(sc, PCM3052_REG_RIGHT_ATTN, onyx_initdata.RIGHT_ATTN);
237239401Sandreast	onyx_write(sc, PCM3052_REG_CONTROL, onyx_initdata.CONTROL);
238239401Sandreast	onyx_write(sc, PCM3052_REG_DAC_CONTROL,
239239401Sandreast		      onyx_initdata.DAC_CONTROL);
240239401Sandreast	onyx_write(sc, PCM3052_REG_DAC_DEEMPH, onyx_initdata.DAC_DEEMPH);
241239401Sandreast	onyx_write(sc, PCM3052_REG_DAC_FILTER, onyx_initdata.DAC_FILTER);
242239401Sandreast	onyx_write(sc, PCM3052_REG_OUT_PHASE, onyx_initdata.OUT_PHASE);
243239401Sandreast	onyx_write(sc, PCM3052_REG_ADC_CONTROL,
244239401Sandreast		      onyx_initdata.ADC_CONTROL);
245239401Sandreast	onyx_write(sc, PCM3052_REG_ADC_HPF_BP, onyx_initdata.ADC_HPF_BP);
246239401Sandreast	onyx_write(sc, PCM3052_REG_INFO_1, onyx_initdata.INFO_1);
247239401Sandreast	onyx_write(sc, PCM3052_REG_INFO_2, onyx_initdata.INFO_2);
248239401Sandreast	onyx_write(sc, PCM3052_REG_INFO_3, onyx_initdata.INFO_3);
249239401Sandreast	onyx_write(sc, PCM3052_REG_INFO_4, onyx_initdata.INFO_4);
250239401Sandreast
251239401Sandreast	x |= SOUND_MASK_VOLUME;
252239401Sandreast	mix_setdevs(m, x);
253239401Sandreast
254239401Sandreast	return (0);
255239401Sandreast}
256239401Sandreast
257239401Sandreaststatic int
258239401Sandreastonyx_uninit(struct snd_mixer *m)
259239401Sandreast{
260239401Sandreast	return (0);
261239401Sandreast}
262239401Sandreast
263239401Sandreaststatic int
264239401Sandreastonyx_reinit(struct snd_mixer *m)
265239401Sandreast{
266239401Sandreast	return (0);
267239401Sandreast}
268239401Sandreast
269239401Sandreaststatic int
270239401Sandreastonyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
271239401Sandreast{
272239401Sandreast	struct onyx_softc *sc;
273239401Sandreast	struct mtx *mixer_lock;
274239401Sandreast	int locked;
275239401Sandreast	uint8_t l, r;
276239401Sandreast
277239401Sandreast	sc = device_get_softc(mix_getdevinfo(m));
278239401Sandreast	mixer_lock = mixer_get_lock(m);
279239401Sandreast	locked = mtx_owned(mixer_lock);
280239401Sandreast
281239401Sandreast	switch (dev) {
282239401Sandreast	case SOUND_MIXER_VOLUME:
283239401Sandreast
284239401Sandreast		/*
285239401Sandreast		 * We need to unlock the mixer lock because iicbus_transfer()
286239401Sandreast		 * may sleep. The mixer lock itself is unnecessary here
287239401Sandreast		 * because it is meant to serialize hardware access, which
288239401Sandreast		 * is taken care of by the I2C layer, so this is safe.
289239401Sandreast		 */
290239401Sandreast		if (left > 100 || right > 100)
291239401Sandreast			return (0);
292239401Sandreast
293239401Sandreast		l = left + 128;
294239401Sandreast		r = right + 128;
295239401Sandreast
296239401Sandreast		if (locked)
297239401Sandreast			mtx_unlock(mixer_lock);
298239401Sandreast
299239401Sandreast		onyx_write(sc, PCM3052_REG_LEFT_ATTN, l);
300239401Sandreast		onyx_write(sc, PCM3052_REG_RIGHT_ATTN, r);
301239401Sandreast
302239401Sandreast		if (locked)
303239401Sandreast			mtx_lock(mixer_lock);
304239401Sandreast
305239401Sandreast		return (left | (right << 8));
306239401Sandreast	}
307239401Sandreast
308239401Sandreast	return (0);
309239401Sandreast}
310239401Sandreast
311239401Sandreaststatic u_int32_t
312239401Sandreastonyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
313239401Sandreast{
314239401Sandreast	return (0);
315239401Sandreast}
316