FreeBSD kernel sound device code
onyx.c
Go to the documentation of this file.
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
63extern kobj_class_t i2s_mixer_class;
64extern device_t i2s_mixer;
65
67{
68 device_t sc_dev;
69 uint32_t sc_addr;
70};
71
72static int onyx_probe(device_t);
73static int onyx_attach(device_t);
74static int onyx_init(struct snd_mixer *m);
75static int onyx_uninit(struct snd_mixer *m);
76static int onyx_reinit(struct snd_mixer *m);
77static int onyx_set(struct snd_mixer *m, unsigned dev, unsigned left,
78 unsigned right);
79static u_int32_t onyx_setrecsrc(struct snd_mixer *m, u_int32_t src);
80
81static 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
88static driver_t onyx_driver = {
89 "onyx",
91 sizeof(struct onyx_softc)
92};
93static devclass_t onyx_devclass;
94
97MODULE_DEPEND(onyx, iicbus, 1, 1, 1);
98
99static 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),
106};
107
108MIXER_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
137struct onyx_reg {
138 u_char LEFT_ATTN;
140 u_char CONTROL;
144 u_char OUT_PHASE;
147 u_char INFO_1;
148 u_char INFO_2;
149 u_char INFO_3;
150 u_char INFO_4;
151};
152
153static 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
171static int
172onyx_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
191static int
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
214static int
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
229static int
231{
232 struct onyx_softc *sc;
233 u_int x = 0;
234
235 sc = device_get_softc(mix_getdevinfo(m));
236
252
253 x |= SOUND_MASK_VOLUME;
254 mix_setdevs(m, x);
255
256 return (0);
257}
258
259static int
261{
262 return (0);
263}
264
265static int
267{
268 return (0);
269}
270
271static int
272onyx_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
303
304 if (locked)
305 mtx_lock(mixer_lock);
306
307 return (left | (right << 8));
308 }
309
310 return (0);
311}
312
313static u_int32_t
314onyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
315{
316 return (0);
317}
const char * name
Definition: audio_soc.c:90
struct pcmchan_matrix * m
Definition: channel_if.m:232
unsigned right
Definition: es137x.c:261
unsigned left
Definition: es137x.c:260
uint32_t value
Definition: hdaa.c:58
uint8_t reg
Definition: hdac.c:211
bus_addr_t buf
Definition: hdac_if.m:63
uint8_t size
uint8_t r
#define KOBJMETHOD_END
Definition: midi.c:76
static int mixer_setrecsrc(struct snd_mixer *mixer, u_int32_t src)
Definition: mixer.c:373
struct mtx * mixer_get_lock(struct snd_mixer *m)
Definition: mixer.c:1567
int mixer_init(device_t dev, kobj_class_t cls, void *devinfo)
Definition: mixer.c:725
static int mixer_set(struct snd_mixer *m, u_int dev, u_int32_t muted, u_int lev)
Definition: mixer.c:247
int mixer_uninit(device_t dev)
Definition: mixer.c:805
void mix_setdevs(struct snd_mixer *m, u_int32_t v)
Definition: mixer.c:489
int mixer_reinit(device_t dev)
Definition: mixer.c:860
void * mix_getdevinfo(struct snd_mixer *m)
Definition: mixer.c:645
unsigned dev
Definition: mixer_if.m:59
u_int32_t src
Definition: mixer_if.m:66
#define PCM3052_REG_OUT_PHASE
Definition: onyx.c:128
static int onyx_attach(device_t)
Definition: onyx.c:215
static kobj_method_t onyx_mixer_methods[]
Definition: onyx.c:99
static int onyx_set(struct snd_mixer *m, unsigned dev, unsigned left, unsigned right)
Definition: onyx.c:272
static int onyx_reinit(struct snd_mixer *m)
Definition: onyx.c:266
device_t i2s_mixer
Definition: i2s.c:108
#define PCM3052_REG_DAC_FILTER
Definition: onyx.c:126
static const struct onyx_reg onyx_initdata
Definition: onyx.c:153
#define PCM3052_REG_INFO_1
Definition: onyx.c:132
static int onyx_write(struct onyx_softc *sc, uint8_t reg, const uint8_t value)
Definition: onyx.c:172
static int onyx_probe(device_t)
Definition: onyx.c:192
#define PCM3052_REG_ADC_CONTROL
Definition: onyx.c:129
static int onyx_uninit(struct snd_mixer *m)
Definition: onyx.c:260
#define PCM3052_REG_RIGHT_ATTN
Definition: onyx.c:117
#define PCM3052_REG_LEFT_ATTN
Definition: onyx.c:116
static devclass_t onyx_devclass
Definition: onyx.c:93
static device_method_t onyx_methods[]
Definition: onyx.c:81
#define PCM3052_REG_DAC_CONTROL
Definition: onyx.c:121
MODULE_DEPEND(onyx, iicbus, 1, 1, 1)
#define PCM3052_REG_CONTROL
Definition: onyx.c:118
static driver_t onyx_driver
Definition: onyx.c:88
#define PCM3052_IICADDR
Definition: onyx.c:110
static int onyx_init(struct snd_mixer *m)
Definition: onyx.c:230
#define PCM3052_DAC_FILTER_ALWAYS
Definition: onyx.c:127
#define PCM3052_REG_INFO_4
Definition: onyx.c:135
#define PCM3052_REG_INFO_2
Definition: onyx.c:133
MIXER_DECLARE(onyx_mixer)
#define PCM3052_SRST
Definition: onyx.c:120
#define PCM3052_HPF_ALWAYS
Definition: onyx.c:131
#define PCM3052_REG_INFO_3
Definition: onyx.c:134
#define PCM3052_REG_DAC_DEEMPH
Definition: onyx.c:125
kobj_class_t i2s_mixer_class
Definition: i2s.c:107
static u_int32_t onyx_setrecsrc(struct snd_mixer *m, u_int32_t src)
Definition: onyx.c:314
DRIVER_MODULE(onyx, iicbus, onyx_driver, onyx_devclass, 0, 0)
#define PCM3052_MRST
Definition: onyx.c:119
#define PCM3052_REG_ADC_HPF_BP
Definition: onyx.c:130
MODULE_VERSION(onyx, 1)
Definition: onyx.c:137
u_char LEFT_ATTN
Definition: onyx.c:138
u_char INFO_1
Definition: onyx.c:147
u_char ADC_CONTROL
Definition: onyx.c:145
u_char OUT_PHASE
Definition: onyx.c:144
u_char INFO_4
Definition: onyx.c:150
u_char ADC_HPF_BP
Definition: onyx.c:146
u_char RIGHT_ATTN
Definition: onyx.c:139
u_char CONTROL
Definition: onyx.c:140
u_char DAC_CONTROL
Definition: onyx.c:141
u_char DAC_FILTER
Definition: onyx.c:143
u_char INFO_2
Definition: onyx.c:148
u_char INFO_3
Definition: onyx.c:149
u_char DAC_DEEMPH
Definition: onyx.c:142
uint32_t sc_addr
Definition: onyx.c:69
device_t sc_dev
Definition: onyx.c:68