pl041.c revision 1.2 1 /* $NetBSD: pl041.c,v 1.2 2017/06/08 10:40:13 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2017 Jared McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pl041.c,v 1.2 2017/06/08 10:40:13 jmcneill Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/kmem.h>
36 #include <sys/bus.h>
37 #include <sys/audioio.h>
38
39 #include <dev/audio_if.h>
40 #include <dev/auconv.h>
41
42 #include <dev/ic/ac97var.h>
43 #include <dev/ic/ac97reg.h>
44
45 #include <dev/ic/pl041var.h>
46
47 #define AACIRXCR 0x00
48 #define AACITXCR 0x04
49 #define AACITXCR_TXFEN __BIT(16)
50 #define AACITXCR_TXCM __BIT(15)
51 #define AACITXCR_TSIZE __BITS(14,13)
52 #define AACITXCR_TSIZE_16 __SHIFTIN(0, AACITXCR_TSIZE)
53 #define AACITXCR_TX(n) __BIT(n)
54 #define AACITXCR_TXEN __BIT(0)
55 #define AACISR 0x08
56 #define AACISR_TXFF __BIT(5)
57 #define AACISR_TXHE __BIT(3)
58 #define AACIISR 0x0c
59 #define AACIISR_URINTR __BIT(5)
60 #define AACIISR_TXINTR __BIT(2)
61 #define AACIIE 0x10
62 #define AACIIE_TXUIE __BIT(5)
63 #define AACIIE_TXIE __BIT(2)
64 #define AACISL1RX 0x50
65 #define AACISL1TX 0x54
66 #define AACISL2RX 0x58
67 #define AACISL2TX 0x5c
68 #define AACISLFR 0x68
69 #define AACISLISTAT 0x6c
70 #define AACISLIEN 0x70
71 #define AACIINTCLR 0x74
72 #define AACIMAINCR 0x78
73 #define AACIRESET 0x7c
74 #define AACISYNC 0x80
75 #define AACIALLINTS 0x84
76 #define AACIMAINFR 0x88
77 #define AACIDR 0x90
78
79 #define AACI_FIFO_DEPTH 512
80 #define AACI_BLOCK_ALIGN 4
81
82 #define AACI_READ(sc, reg) \
83 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
84 #define AACI_WRITE(sc, reg, val) \
85 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
86 #define AACI_WRITE_MULTI(sc, reg, val, cnt) \
87 bus_space_write_multi_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val), (cnt))
88
89 static const struct audio_format aaci_format = {
90 .mode = AUMODE_PLAY,
91 .encoding = AUDIO_ENCODING_SLINEAR_LE,
92 .validbits = 16,
93 .precision = 16,
94 .channels = 2,
95 .channel_mask = AUFMT_STEREO,
96 .frequency_type = 0,
97 .frequency = { 48000, 48000 }
98 };
99
100 static void
101 aaci_write_data(struct aaci_softc *sc)
102 {
103 KASSERT(mutex_owned(&sc->sc_intr_lock));
104
105 if (sc->sc_pint == NULL)
106 return;
107
108 while ((AACI_READ(sc, AACISR) & AACISR_TXHE) != 0) {
109 const int len = min(AACI_FIFO_DEPTH / 2, min(sc->sc_pblkresid,
110 (uintptr_t)sc->sc_pend - (uintptr_t)sc->sc_pcur));
111 KASSERT((len & 3) == 0);
112 AACI_WRITE_MULTI(sc, AACIDR, sc->sc_pcur, len);
113 sc->sc_pcur += (len >> 2);
114 if (sc->sc_pcur == sc->sc_pend)
115 sc->sc_pcur = sc->sc_pstart;
116 sc->sc_pblkresid -= len;
117 if (sc->sc_pblkresid == 0) {
118 sc->sc_pblkresid = sc->sc_pblksize;
119 sc->sc_pint(sc->sc_pintarg);
120 }
121 }
122 }
123
124 static int
125 aaci_query_encoding(void *priv, struct audio_encoding *enc)
126 {
127 struct aaci_softc * const sc = priv;
128
129 return auconv_query_encoding(sc->sc_encodings, enc);
130 }
131
132 static int
133 aaci_set_params(void *priv, int setmode, int usermode,
134 audio_params_t *play, audio_params_t *rec,
135 stream_filter_list_t *pfil, stream_filter_list_t *rfil)
136 {
137 int index;
138
139 if (play && (setmode & AUMODE_PLAY) != 0) {
140 index = auconv_set_converter(&aaci_format, 1, AUMODE_PLAY,
141 play, true, pfil);
142 if (index < 0)
143 return EINVAL;
144 }
145
146 return 0;
147 }
148
149 static int
150 aaci_getdev(void *priv, struct audio_device *audiodev)
151 {
152 snprintf(audiodev->name, sizeof(audiodev->name), "ARM");
153 snprintf(audiodev->version, sizeof(audiodev->version),
154 "PrimeCell AACI");
155 snprintf(audiodev->config, sizeof(audiodev->config), "aaci");
156 return 0;
157 }
158
159 static int
160 aaci_set_port(void *priv, mixer_ctrl_t *mc)
161 {
162 struct aaci_softc * const sc = priv;
163
164 return sc->sc_ac97_codec->vtbl->mixer_set_port(sc->sc_ac97_codec, mc);
165 }
166
167 static int
168 aaci_get_port(void *priv, mixer_ctrl_t *mc)
169 {
170 struct aaci_softc * const sc = priv;
171
172 return sc->sc_ac97_codec->vtbl->mixer_get_port(sc->sc_ac97_codec, mc);
173 }
174
175 static int
176 aaci_query_devinfo(void *priv, mixer_devinfo_t *di)
177 {
178 struct aaci_softc * const sc = priv;
179
180 return sc->sc_ac97_codec->vtbl->query_devinfo(sc->sc_ac97_codec, di);
181 }
182
183 static int
184 aaci_get_props(void *priv)
185 {
186 return AUDIO_PROP_PLAYBACK;
187 }
188
189 static int
190 aaci_trigger_output(void *priv, void *start, void *end, int blksize,
191 void (*intr)(void *), void *intrarg, const audio_params_t *params)
192 {
193 struct aaci_softc * const sc = priv;
194
195 sc->sc_pcur = start;
196 sc->sc_pstart = start;
197 sc->sc_pend = end;
198 sc->sc_pblksize = blksize;
199 sc->sc_pblkresid = blksize;
200 sc->sc_pint = intr;
201 sc->sc_pintarg = intrarg;
202
203 AACI_WRITE(sc, AACIIE, AACIIE_TXIE | AACIIE_TXUIE);
204 AACI_WRITE(sc, AACITXCR, AACITXCR_TXFEN | AACITXCR_TXEN |
205 AACITXCR_TXCM | AACITXCR_TSIZE_16 |
206 AACITXCR_TX(3) | AACITXCR_TX(4));
207
208 return 0;
209 }
210
211 static int
212 aaci_trigger_input(void *priv, void *start, void *end, int blksize,
213 void (*intr)(void *), void *intrarg, const audio_params_t *params)
214 {
215 return ENXIO;
216 }
217
218 static int
219 aaci_halt_output(void *priv)
220 {
221 struct aaci_softc * const sc = priv;
222
223 sc->sc_pint = NULL;
224
225 AACI_WRITE(sc, AACITXCR, 0);
226 AACI_WRITE(sc, AACIIE, 0);
227
228 return 0;
229 }
230
231 static int
232 aaci_halt_input(void *priv)
233 {
234 return ENXIO;
235 }
236
237 static int
238 aaci_round_blocksize(void *priv, int bs, int mode, const audio_params_t *params)
239 {
240 return roundup(bs, AACI_BLOCK_ALIGN);
241 }
242
243 static void
244 aaci_get_locks(void *priv, kmutex_t **intr, kmutex_t **thread)
245 {
246 struct aaci_softc * const sc = priv;
247
248 *intr = &sc->sc_intr_lock;
249 *thread = &sc->sc_lock;
250 }
251
252 static const struct audio_hw_if aaci_hw_if = {
253 .query_encoding = aaci_query_encoding,
254 .set_params = aaci_set_params,
255 .getdev = aaci_getdev,
256 .set_port = aaci_set_port,
257 .get_port = aaci_get_port,
258 .query_devinfo = aaci_query_devinfo,
259 .get_props = aaci_get_props,
260 .trigger_output = aaci_trigger_output,
261 .trigger_input = aaci_trigger_input,
262 .halt_output = aaci_halt_output,
263 .halt_input = aaci_halt_input,
264 .round_blocksize = aaci_round_blocksize,
265 .get_locks = aaci_get_locks,
266 };
267
268 static int
269 aaci_ac97_attach(void *priv, struct ac97_codec_if *codec)
270 {
271 struct aaci_softc * const sc = priv;
272
273 sc->sc_ac97_codec = codec;
274
275 return 0;
276 }
277
278 static int
279 aaci_ac97_read(void *priv, uint8_t reg, uint16_t *val)
280 {
281 struct aaci_softc * const sc = priv;
282
283 AACI_WRITE(sc, AACISL1TX, (reg << 12) | (1 << 19));
284
285 if (AACI_READ(sc, AACISL1RX) != (reg << 12))
286 return 1;
287
288 *val = AACI_READ(sc, AACISL2RX) >> 4;
289 return 0;
290 }
291
292 static int
293 aaci_ac97_write(void *priv, uint8_t reg, uint16_t val)
294 {
295 struct aaci_softc * const sc = priv;
296
297 AACI_WRITE(sc, AACISL2TX, val << 4);
298 AACI_WRITE(sc, AACISL1TX, (reg << 12) | (0 << 19));
299
300 return 0;
301 }
302
303 void
304 aaci_attach(struct aaci_softc *sc)
305 {
306 int error;
307
308 aprint_naive("\n");
309 aprint_normal(": Advanced Audio CODEC\n");
310
311 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
312 mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO);
313
314 sc->sc_ac97_host.arg = sc;
315 sc->sc_ac97_host.attach = aaci_ac97_attach;
316 sc->sc_ac97_host.read = aaci_ac97_read;
317 sc->sc_ac97_host.write = aaci_ac97_write;
318 error = ac97_attach(&sc->sc_ac97_host, sc->sc_dev, &sc->sc_lock);
319 if (error) {
320 aprint_error_dev(sc->sc_dev, "couldn't attach codec (%d)\n",
321 error);
322 return;
323 }
324
325 error = auconv_create_encodings(&aaci_format, 1, &sc->sc_encodings);
326 if (error) {
327 aprint_error_dev(sc->sc_dev, "couldn't create encodings (%d)\n",
328 error);
329 return;
330 }
331
332 sc->sc_audiodev = audio_attach_mi(&aaci_hw_if, sc, sc->sc_dev);
333 }
334
335 int
336 aaci_intr(void *priv)
337 {
338 struct aaci_softc * const sc = priv;
339 uint32_t isr;
340
341 if (sc->sc_audiodev == NULL)
342 return 0;
343
344 isr = AACI_READ(sc, AACIISR);
345
346 if (isr & AACIISR_URINTR)
347 AACI_WRITE(sc, AACIINTCLR, AACIISR_URINTR);
348
349 if (isr & AACIISR_TXINTR) {
350 mutex_enter(&sc->sc_intr_lock);
351 if (sc->sc_pint == NULL)
352 AACI_WRITE(sc, AACIIE, 0);
353 aaci_write_data(sc);
354 mutex_exit(&sc->sc_intr_lock);
355 }
356
357 return 1;
358 }
359