1 /* $OpenBSD: auglx.c,v 1.25 2024/05/13 01:15:51 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2008 Marc Balmer <mbalmer@openbsd.org>
5 * All rights reserved.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER IN
16 * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
17 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 /*
21 * AMD CS5536 series AC'97 audio driver.
22 *
23 * The following datasheets were helpful in the development of this
24 * driver:
25 *
26 * AMD Geode LX Processors Data Book
27 * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\
28 * 33234F_LX_databook.pdf
29 *
30 * AMD Geode CS5536 Companion Device Data Book
31 * http://www.amd.com/files/connectivitysolutions/geode/geode_lx/\
32 * 33238G_cs5536_db.pdf
33 *
34 * Realtek ALC203 Two-Channel AC'97 2.3 Audio Codec
35 * ftp://202.65.194.211/pc/audio/ALC203_DataSheet_1.6.pdf
36 *
37 * This driver is inspired by the auich(4) and auixp(4) drivers, some
38 * of the hardware-independent functionality has been derived from them
39 * (e.g. memory allocation for the upper level, parameter setting).
40 */
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/device.h>
45 #include <sys/malloc.h>
46 #include <sys/audioio.h>
47
48 #include <machine/bus.h>
49
50 #include <dev/pci/pcireg.h>
51 #include <dev/pci/pcivar.h>
52 #include <dev/pci/pcidevs.h>
53 #include <dev/audio_if.h>
54
55 #include <dev/ic/ac97.h>
56
57 #define AUGLX_ACC_BAR 0x10
58
59 /* ACC Native Registers */
60 #define ACC_GPIO_STATUS 0x00
61 #define ACC_GPIO_CNTL 0x04
62 #define ACC_CODEC_STATUS 0x08
63 #define ACC_CODEC_CNTL 0x0c
64 #define ACC_IRQ_STATUS 0x12
65 #define ACC_ENGINE_CNTL 0x14
66 #define ACC_BM0_CMD 0x20 /* Bus Master 0 Command */
67 #define ACC_BM0_STATUS 0x21 /* Bus Master 0 IRQ Status */
68 #define ACC_BM0_PRD 0x24 /* BM0 PRD Table Address */
69 #define ACC_BM1_CMD 0x28 /* Bus Master 1 Command */
70 #define ACC_BM1_STATUS 0x29 /* Bus Master 1 IRQ Status */
71 #define ACC_BM1_PRD 0x2c /* BM1 PRD Table Address */
72 #define ACC_BM2_CMD 0x30 /* Bus Master 2 Command */
73 #define ACC_BM2_STATUS 0x31 /* Bus Master 2 IRQ Status */
74 #define ACC_BM2_PRD 0x34 /* BM2 PRD Table Address */
75 #define ACC_BM3_CMD 0x38 /* Bus Master 3 Command */
76 #define ACC_BM3_STATUS 0x39 /* Bus Master 3 IRQ Status */
77 #define ACC_BM3_PRD 0x3c /* BM3 PRD Table Address */
78 #define ACC_BM4_CMD 0x40 /* Bus Master 4 Command */
79 #define ACC_BM4_STATUS 0x41 /* Bus Master 4 IRQ Status */
80 #define ACC_BM4_PRD 0x44 /* BM4 PRD Table Address */
81 #define ACC_BM5_CMD 0x48 /* Bus Master 5 Command */
82 #define ACC_BM5_STATUS 0x49 /* Bus Master 5 IRQ Status */
83 #define ACC_BM5_PRD 0x4c /* BM5 PRD Table Address */
84 #define ACC_BM6_CMD 0x50 /* Bus Master 6 Command */
85 #define ACC_BM6_STATUS 0x51 /* Bus Master 6 IRQ Status */
86 #define ACC_BM6_PRD 0x54 /* BM6 PRD Table Address */
87 #define ACC_BM7_CMD 0x58 /* Bus Master 7 Command */
88 #define ACC_BM7_STATUS 0x59 /* Bus Master 7 IRQ Status */
89 #define ACC_BM7_PRD 0x5c /* BM7 PRD Table Address */
90 #define ACC_BM0_PNTR 0x60 /* Bus Master 0 DMA Pointer */
91 #define ACC_BM1_PNTR 0x64 /* Bus Master 1 DMA Pointer */
92 #define ACC_BM2_PNTR 0x68 /* Bus Master 2 DMA Pointer */
93 #define ACC_BM3_PNTR 0x6c /* Bus Master 3 DMA Pointer */
94 #define ACC_BM4_PNTR 0x70 /* Bus Master 4 DMA Pointer */
95 #define ACC_BM5_PNTR 0x74 /* Bus Master 5 DMA Pointer */
96 #define ACC_BM6_PNTR 0x78 /* Bus Master 6 DMA Pointer */
97 #define ACC_BM7_PNTR 0x7c /* Bus Master 7 DMA Pointer */
98
99 /* ACC_IRQ_STATUS Bit Definitions */
100 #define BM7_IRQ_STS 0x0200 /* Audio Bus Master 7 IRQ Status */
101 #define BM6_IRQ_STS 0x0100 /* Audio Bus Master 6 IRQ Status */
102 #define BM5_IRQ_STS 0x0080 /* Audio Bus Master 5 IRQ Status */
103 #define BM4_IRQ_STS 0x0040 /* Audio Bus Master 4 IRQ Status */
104 #define BM3_IRQ_STS 0x0020 /* Audio Bus Master 3 IRQ Status */
105 #define BM2_IRQ_STS 0x0010 /* Audio Bus Master 2 IRQ Status */
106 #define BM1_IRQ_STS 0x0008 /* Audio Bus Master 1 IRQ Status */
107 #define BM0_IRQ_STS 0x0004 /* Audio Bus Master 0 IRQ Status */
108 #define WU_IRQ_STS 0x0002 /* Codec GPIO Wakeup IRQ Status */
109 #define IRQ_STS 0x0001 /* Codec GPIO IRQ Status */
110
111 /* ACC_ENGINE_CNTL Bit Definitions */
112 #define SSND_MODE 0x00000001 /* Surround Sound (5.1) Sync. Mode */
113
114 /* ACC_BM[x]_CMD Bit Descriptions */
115 #define BMx_CMD_RW 0x08 /* 0: Mem to codec, 1: codec to mem */
116 #define BMx_CMD_BYTE_ORD 0x04 /* 0: Little Endian, 1: Big Endian */
117 #define BMx_CMD_BM_CTL_DIS 0x00 /* Disable bus master */
118 #define BMx_CMD_BM_CTL_EN 0x01 /* Enable bus master */
119 #define BMx_CMD_BM_CTL_PAUSE 0x03 /* Pause bus master */
120
121 /* ACC_BM[x]_STATUS Bit Definitions */
122 #define BMx_BM_EOP_ERR 0x02 /* Bus master error */
123 #define BMx_BM_EOP 0x01 /* End of page */
124
125 /* ACC_CODEC_CNTL Bit Definitions */
126 #define RW_CMD 0x80000000
127 #define PD_PRIM 0x00200000
128 #define PD_SEC 0x00100000
129 #define LNK_SHTDOWN 0x00040000
130 #define LNK_WRM_RST 0x00020000
131 #define CMD_NEW 0x00010000
132
133 /* ACC_CODEC_STATUS Bit Definitions */
134 #define PRM_RDY_STS 0x00800000
135 #define SEC_RDY_STS 0x00400000
136 #define SDATAIN2_EN 0x00200000
137 #define BM5_SEL 0x00100000
138 #define BM4_SEL 0x00080000
139 #define STS_NEW 0x00020000
140
141 #define AUGLX_TOUT 1000 /* uSec */
142
143 #define AUGLX_DMALIST_MAX 1
144 #define AUGLX_DMASEG_MAX 65536
145
146 struct auglx_prd {
147 u_int32_t base;
148 u_int32_t size;
149 #define AUGLX_PRD_EOT 0x80000000
150 #define AUGLX_PRD_EOP 0x40000000
151 #define AUGLX_PRD_JMP 0x20000000
152 };
153
154 #define AUGLX_FIXED_RATE 48000
155
156 struct auglx_dma {
157 bus_dmamap_t map;
158 caddr_t addr;
159 bus_dma_segment_t segs[AUGLX_DMALIST_MAX];
160 int nsegs;
161 size_t size;
162 struct auglx_dma *next;
163 };
164
165 struct auglx_softc {
166 struct device sc_dev;
167 void *sc_ih;
168
169 bus_space_tag_t sc_iot;
170 bus_space_handle_t sc_ioh;
171 bus_dma_tag_t sc_dmat;
172
173 /*
174 * The CS5536 ACC has eight bus masters to support 5.1 audio.
175 * This driver, however, only supports main playback and recording
176 * since I only have a Realtek ALC203 codec available for testing.
177 */
178 struct auglx_ring {
179 bus_dmamap_t sc_prd;
180 struct auglx_prd *sc_vprd;
181 int sc_nprd;
182
183 size_t sc_size;
184 int nsegs;
185 bus_dma_segment_t seg;
186
187 void (*intr)(void *);
188 void *arg;
189 } bm0, bm1; /* bm0: output, bm1: input */
190
191 struct auglx_dma *sc_dmas;
192
193 struct ac97_codec_if *codec_if;
194 struct ac97_host_if host_if;
195
196 int sc_dmamap_flags;
197 };
198
199 #ifdef AUGLX_DEBUG
200 #define DPRINTF(l,x) do { if (auglx_debug & (l)) printf x; } while(0)
201 int auglx_debug = 0;
202 #define AUGLX_DBG_ACC 0x0001
203 #define AUGLX_DBG_DMA 0x0002
204 #define AUGLX_DBG_IRQ 0x0004
205 #else
206 #define DPRINTF(x,y) /* nothing */
207 #endif
208
209 struct cfdriver auglx_cd = {
210 NULL, "auglx", DV_DULL
211 };
212
213 int auglx_open(void *, int);
214 void auglx_close(void *);
215 int auglx_set_params(void *, int, int, struct audio_params *,
216 struct audio_params *);
217 int auglx_round_blocksize(void *, int);
218 int auglx_halt_output(void *);
219 int auglx_halt_input(void *);
220 int auglx_set_port(void *, mixer_ctrl_t *);
221 int auglx_get_port(void *, mixer_ctrl_t *);
222 int auglx_query_devinfo(void *, mixer_devinfo_t *);
223 void *auglx_allocm(void *, int, size_t, int, int);
224 void auglx_freem(void *, void *, int);
225 size_t auglx_round_buffersize(void *, int, size_t);
226 int auglx_trigger_output(void *, void *, void *, int, void (*)(void *),
227 void *, struct audio_params *);
228 int auglx_trigger_input(void *, void *, void *, int, void (*)(void *),
229 void *, struct audio_params *);
230 int auglx_alloc_prd(struct auglx_softc *, size_t, struct auglx_ring *);
231 void auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm);
232 int auglx_allocmem(struct auglx_softc *, size_t, size_t, struct auglx_dma *);
233 void auglx_freemem(struct auglx_softc *, struct auglx_dma *);
234
235 const struct audio_hw_if auglx_hw_if = {
236 .open = auglx_open,
237 .close = auglx_close,
238 .set_params = auglx_set_params,
239 .round_blocksize = auglx_round_blocksize,
240 .halt_output = auglx_halt_output,
241 .halt_input = auglx_halt_input,
242 .set_port = auglx_set_port,
243 .get_port = auglx_get_port,
244 .query_devinfo = auglx_query_devinfo,
245 .allocm = auglx_allocm,
246 .freem = auglx_freem,
247 .round_buffersize = auglx_round_buffersize,
248 .trigger_output = auglx_trigger_output,
249 .trigger_input = auglx_trigger_input,
250 };
251
252 int auglx_match(struct device *, void *, void *);
253 void auglx_attach(struct device *, struct device *, void *);
254 int auglx_activate(struct device *, int);
255 int auglx_intr(void *);
256
257 int auglx_attach_codec(void *, struct ac97_codec_if *);
258 int auglx_read_codec(void *, u_int8_t, u_int16_t *);
259 int auglx_write_codec(void *, u_int8_t, u_int16_t);
260 void auglx_reset_codec(void *);
261 enum ac97_host_flags auglx_flags_codec(void *);
262
263 const struct cfattach auglx_ca = {
264 sizeof(struct auglx_softc), auglx_match, auglx_attach, NULL,
265 auglx_activate
266 };
267
268 const struct pci_matchid auglx_devices[] = {
269 { PCI_VENDOR_AMD, PCI_PRODUCT_AMD_CS5536_AUDIO }
270 };
271
272 int
auglx_match(struct device * parent,void * match,void * aux)273 auglx_match(struct device *parent, void *match, void *aux)
274 {
275 return (pci_matchbyid((struct pci_attach_args *)aux, auglx_devices,
276 sizeof(auglx_devices) / sizeof(auglx_devices[0])));
277 }
278
279 void
auglx_attach(struct device * parent,struct device * self,void * aux)280 auglx_attach(struct device *parent, struct device *self, void *aux)
281 {
282 struct auglx_softc *sc = (struct auglx_softc *)self;
283 struct pci_attach_args *pa = aux;
284 bus_size_t bar_size;
285 pci_intr_handle_t ih;
286 const char *intrstr;
287
288 if (pci_mapreg_map(pa, AUGLX_ACC_BAR, PCI_MAPREG_TYPE_IO, 0,
289 &sc->sc_iot, &sc->sc_ioh, NULL, &bar_size, 0)) {
290 printf(": can't map ACC I/O space\n");
291 return;
292 }
293
294 sc->sc_dmat = pa->pa_dmat;
295 sc->sc_dmamap_flags = BUS_DMA_COHERENT;
296
297 if (pci_intr_map(pa, &ih)) {
298 printf(": can't map interrupt");
299 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
300 return;
301 }
302 intrstr = pci_intr_string(pa->pa_pc, ih);
303 sc->sc_ih = pci_intr_establish(pa->pa_pc, ih, IPL_AUDIO | IPL_MPSAFE,
304 auglx_intr, sc, sc->sc_dev.dv_xname);
305 if (!sc->sc_ih) {
306 printf(": can't establish interrupt");
307 if (intrstr)
308 printf(" at %s", intrstr);
309 printf("\n");
310 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
311 return;
312 }
313
314 printf(": %s, %s\n", intrstr, "CS5536 AC97");
315
316 sc->host_if.arg = sc;
317 sc->host_if.attach = auglx_attach_codec;
318 sc->host_if.read = auglx_read_codec;
319 sc->host_if.write = auglx_write_codec;
320 sc->host_if.reset = auglx_reset_codec;
321 sc->host_if.flags = auglx_flags_codec;
322
323 if (ac97_attach(&sc->host_if) != 0) {
324 bus_space_unmap(sc->sc_iot, sc->sc_ioh, bar_size);
325 return;
326 }
327 audio_attach_mi(&auglx_hw_if, sc, NULL, &sc->sc_dev);
328 }
329
330 /* Functions to communicate with the AC97 Codec via the ACC */
331 int
auglx_read_codec(void * v,u_int8_t reg,u_int16_t * val)332 auglx_read_codec(void *v, u_int8_t reg, u_int16_t *val)
333 {
334 struct auglx_softc *sc = v;
335 u_int32_t codec_cntl, codec_status;
336 int i;
337
338 codec_cntl = RW_CMD | ((u_int32_t)reg << 24) | CMD_NEW;
339 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
340
341 for (i = AUGLX_TOUT; i; i--) {
342 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
343 ACC_CODEC_CNTL);
344 if (!(codec_cntl & CMD_NEW))
345 break;
346 delay(1);
347 }
348 if (codec_cntl & CMD_NEW) {
349 printf("%s: codec read timeout after write\n",
350 sc->sc_dev.dv_xname);
351 return -1;
352 }
353
354 for (i = AUGLX_TOUT; i; i--) {
355 codec_status = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
356 ACC_CODEC_STATUS);
357 if ((codec_status & STS_NEW) && (codec_status >> 24 == reg))
358 break;
359 delay(10);
360 }
361 if (i == 0) {
362 printf("%s: codec status read timeout, 0x%08x\n",
363 sc->sc_dev.dv_xname, codec_status);
364 return -1;
365 }
366
367 *val = codec_status & 0xffff;
368 DPRINTF(AUGLX_DBG_ACC, ("%s: read codec register 0x%02x: 0x%04x\n",
369 sc->sc_dev.dv_xname, reg, *val));
370 return 0;
371 }
372
373 int
auglx_write_codec(void * v,u_int8_t reg,u_int16_t val)374 auglx_write_codec(void *v, u_int8_t reg, u_int16_t val)
375 {
376 struct auglx_softc *sc = v;
377 u_int32_t codec_cntl;
378 int i;
379
380 DPRINTF(AUGLX_DBG_ACC, ("%s: write codec register 0x%02x: 0x%04x\n",
381 sc->sc_dev.dv_xname, reg, val));
382
383
384 codec_cntl = ((u_int32_t)reg << 24) | CMD_NEW | val;
385 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
386
387 for (i = AUGLX_TOUT; i; i--) {
388 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
389 ACC_CODEC_CNTL);
390 if (!(codec_cntl & CMD_NEW))
391 break;
392 delay(1);
393 }
394 if (codec_cntl & CMD_NEW) {
395 printf("%s: codec write timeout\n", sc->sc_dev.dv_xname);
396 return -1;
397 }
398
399 return 0;
400 }
401
402 int
auglx_attach_codec(void * v,struct ac97_codec_if * cif)403 auglx_attach_codec(void *v, struct ac97_codec_if *cif)
404 {
405 struct auglx_softc *sc = v;
406
407 sc->codec_if = cif;
408 return 0;
409 }
410
411 void
auglx_reset_codec(void * v)412 auglx_reset_codec(void *v)
413 {
414 struct auglx_softc *sc = v;
415 u_int32_t codec_cntl;
416 int i;
417
418 codec_cntl = LNK_WRM_RST | CMD_NEW;
419 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_CODEC_CNTL, codec_cntl);
420
421 for (i = AUGLX_TOUT; i; i--) {
422 codec_cntl = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
423 ACC_CODEC_CNTL);
424 if (!(codec_cntl & CMD_NEW))
425 continue;
426 delay(1);
427 }
428 if (codec_cntl & CMD_NEW)
429 printf("%s: codec reset timeout\n", sc->sc_dev.dv_xname);
430 }
431
432 enum ac97_host_flags
auglx_flags_codec(void * v)433 auglx_flags_codec(void *v)
434 {
435 return 0;
436 }
437
438 /*
439 * Audio functions
440 */
441 int
auglx_open(void * v,int flags)442 auglx_open(void *v, int flags)
443 {
444 return 0;
445 }
446
447 void
auglx_close(void * v)448 auglx_close(void *v)
449 {
450 }
451
452 int
auglx_set_params(void * v,int setmode,int usemode,struct audio_params * play,struct audio_params * rec)453 auglx_set_params(void *v, int setmode, int usemode, struct audio_params *play,
454 struct audio_params *rec)
455 {
456 struct auglx_softc *sc = v;
457 int error;
458 u_int orate;
459
460 if (setmode & AUMODE_PLAY) {
461 play->precision = 16;
462 play->channels = 2;
463 play->encoding = AUDIO_ENCODING_SLINEAR_LE;
464 play->bps = AUDIO_BPS(play->precision);
465 play->msb = 1;
466
467 orate = play->sample_rate;
468
469 play->sample_rate = orate;
470 error = ac97_set_rate(sc->codec_if,
471 AC97_REG_PCM_LFE_DAC_RATE, &play->sample_rate);
472 if (error)
473 return error;
474
475 play->sample_rate = orate;
476 error = ac97_set_rate(sc->codec_if,
477 AC97_REG_PCM_SURR_DAC_RATE, &play->sample_rate);
478 if (error)
479 return error;
480
481 play->sample_rate = orate;
482 error = ac97_set_rate(sc->codec_if,
483 AC97_REG_PCM_FRONT_DAC_RATE, &play->sample_rate);
484 if (error)
485 return error;
486 }
487
488 if (setmode & AUMODE_RECORD) {
489 rec->precision = 16;
490 rec->channels = 2;
491 rec->encoding = AUDIO_ENCODING_ULINEAR_LE;
492 rec->bps = AUDIO_BPS(rec->precision);
493 rec->msb = 1;
494
495 error = ac97_set_rate(sc->codec_if, AC97_REG_PCM_LR_ADC_RATE,
496 &rec->sample_rate);
497 if (error)
498 return error;
499 }
500
501 return 0;
502 }
503
504 int
auglx_round_blocksize(void * v,int blk)505 auglx_round_blocksize(void *v, int blk)
506 {
507 return (blk + 0x3f) & ~0x3f;
508 }
509
510 int
auglx_halt_output(void * v)511 auglx_halt_output(void *v)
512 {
513 struct auglx_softc *sc = v;
514
515 DPRINTF(AUGLX_DBG_DMA, ("%s: halt_output\n", sc->sc_dev.dv_xname));
516
517 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD, 0x00);
518 sc->bm0.intr = NULL;
519 return 0;
520 }
521
522 int
auglx_halt_input(void * v)523 auglx_halt_input(void *v)
524 {
525 struct auglx_softc *sc = v;
526
527 DPRINTF(AUGLX_DBG_DMA,
528 ("%s: halt_input\n", sc->sc_dev.dv_xname));
529
530 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD, 0x00);
531 sc->bm1.intr = NULL;
532 return 0;
533 }
534
535 int
auglx_set_port(void * v,mixer_ctrl_t * cp)536 auglx_set_port(void *v, mixer_ctrl_t *cp)
537 {
538 struct auglx_softc *sc = v;
539 return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp);
540 }
541
542 int
auglx_get_port(void * v,mixer_ctrl_t * cp)543 auglx_get_port(void *v, mixer_ctrl_t *cp)
544 {
545 struct auglx_softc *sc = v;
546 return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp);
547 }
548
549 int
auglx_query_devinfo(void * v,mixer_devinfo_t * dp)550 auglx_query_devinfo(void *v, mixer_devinfo_t *dp)
551 {
552 struct auglx_softc *sc = v;
553 return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dp);
554 }
555
556 void *
auglx_allocm(void * v,int direction,size_t size,int pool,int flags)557 auglx_allocm(void *v, int direction, size_t size, int pool, int flags)
558 {
559 struct auglx_softc *sc = v;
560 struct auglx_dma *p;
561 int error;
562
563 DPRINTF(AUGLX_DBG_DMA, ("%s: request buffer of size %ld, dir %d\n",
564 sc->sc_dev.dv_xname, size, direction));
565
566 /* can only use 1 segment */
567 if (size > AUGLX_DMASEG_MAX) {
568 DPRINTF(AUGLX_DBG_DMA,
569 ("%s: requested buffer size too large: %d", \
570 sc->sc_dev.dv_xname, size));
571 return NULL;
572 }
573
574 p = malloc(sizeof(*p), pool, flags | M_ZERO);
575 if (!p)
576 return NULL;
577
578 error = auglx_allocmem(sc, size, PAGE_SIZE, p);
579 if (error) {
580 free(p, pool, sizeof(*p));
581 return NULL;
582 }
583
584 p->next = sc->sc_dmas;
585 sc->sc_dmas = p;
586
587 return p->addr;
588 }
589
590 void
auglx_freem(void * v,void * ptr,int pool)591 auglx_freem(void *v, void *ptr, int pool)
592 {
593 struct auglx_softc *sc;
594 struct auglx_dma *p, **pp;
595
596 sc = v;
597 for (pp = &sc->sc_dmas; (p = *pp) != NULL; pp = &p->next) {
598 if (p->addr == ptr) {
599 auglx_freemem(sc, p);
600 *pp = p->next;
601 free(p, pool, sizeof(*p));
602 return;
603 }
604 }
605 }
606
607
608 size_t
auglx_round_buffersize(void * v,int direction,size_t size)609 auglx_round_buffersize(void *v, int direction, size_t size)
610 {
611 if (size > AUGLX_DMASEG_MAX)
612 size = AUGLX_DMASEG_MAX;
613
614 return size;
615 }
616
617 int
auglx_intr(void * v)618 auglx_intr(void *v)
619 {
620 struct auglx_softc *sc = v;
621 u_int16_t irq_sts;
622 u_int8_t bm_sts;
623
624 mtx_enter(&audio_lock);
625 irq_sts = bus_space_read_2(sc->sc_iot, sc->sc_ioh, ACC_IRQ_STATUS);
626 if (irq_sts == 0) {
627 mtx_leave(&audio_lock);
628 return 0;
629 }
630
631 if (irq_sts & BM0_IRQ_STS) {
632 bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
633 ACC_BM0_STATUS);
634 if (sc->bm0.intr) {
635 sc->bm0.intr(sc->bm0.arg);
636 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD,
637 BMx_CMD_BM_CTL_EN);
638 }
639 } else if (irq_sts & BM1_IRQ_STS) {
640 bm_sts = bus_space_read_1(sc->sc_iot, sc->sc_ioh,
641 ACC_BM1_STATUS);
642 if (sc->bm1.intr) {
643 sc->bm1.intr(sc->bm1.arg);
644 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD,
645 BMx_CMD_RW | BMx_CMD_BM_CTL_EN);
646 }
647 } else {
648 DPRINTF(AUGLX_DBG_IRQ, ("%s: stray intr, status = 0x%04x\n",
649 sc->sc_dev.dv_xname, irq_sts));
650 mtx_leave(&audio_lock);
651 return -1;
652 }
653 mtx_leave(&audio_lock);
654 return 1;
655 }
656
657 int
auglx_trigger_output(void * v,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)658 auglx_trigger_output(void *v, void *start, void *end, int blksize,
659 void (*intr)(void *), void *arg, struct audio_params *param)
660 {
661 struct auglx_softc *sc = v;
662 struct auglx_dma *p;
663 size_t size;
664 u_int32_t addr;
665 int i, nprd;
666
667 size = (size_t)((caddr_t)end - (caddr_t)start);
668 DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_output, %p 0x%08x bytes, "
669 "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize));
670
671 for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
672 ;
673 if (!p) {
674 DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n",
675 sc->sc_dev.dv_xname));
676 return -1;
677 }
678
679 /* set up the PRDs */
680 nprd = size / blksize;
681 if (sc->bm0.sc_nprd != nprd + 1) {
682 if (sc->bm0.sc_nprd > 0)
683 auglx_free_prd(sc, &sc->bm0);
684 sc->bm0.sc_nprd = nprd + 1;
685 auglx_alloc_prd(sc,
686 sc->bm0.sc_nprd * sizeof(struct auglx_prd), &sc->bm0);
687 }
688 DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname,
689 nprd));
690 addr = p->segs->ds_addr;
691 for (i = 0; i < nprd; i++) {
692 sc->bm0.sc_vprd[i].base = addr;
693 sc->bm0.sc_vprd[i].size = blksize | AUGLX_PRD_EOP;
694 addr += blksize;
695 }
696 sc->bm0.sc_vprd[i].base = sc->bm0.sc_prd->dm_segs[0].ds_addr;
697 sc->bm0.sc_vprd[i].size = AUGLX_PRD_JMP;
698
699 #ifdef AUGLX_DEBUG
700 for (i = 0; i < sc->bm0.sc_nprd; i++)
701 DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n",
702 sc->sc_dev.dv_xname, i, sc->bm0.sc_vprd[i].base,
703 sc->bm0.sc_vprd[i].size));
704 #endif
705 sc->bm0.intr = intr;
706 sc->bm0.arg = arg;
707
708 mtx_enter(&audio_lock);
709 /* Program the BM0 PRD register */
710 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM0_PRD,
711 sc->bm0.sc_prd->dm_segs[0].ds_addr);
712 /* Start Audio Bus Master 0 */
713 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM0_CMD,
714 BMx_CMD_BM_CTL_EN);
715 mtx_leave(&audio_lock);
716 return 0;
717 }
718
719 int
auglx_trigger_input(void * v,void * start,void * end,int blksize,void (* intr)(void *),void * arg,struct audio_params * param)720 auglx_trigger_input(void *v, void *start, void *end, int blksize,
721 void (*intr)(void *), void * arg, struct audio_params *param)
722 {
723 struct auglx_softc *sc = v;
724 struct auglx_dma *p;
725 size_t size;
726 u_int32_t addr;
727 int i, nprd;
728
729 size = (size_t)((caddr_t)end - (caddr_t)start);
730 DPRINTF(AUGLX_DBG_DMA, ("%s: trigger_input, %p 0x%08x bytes, "
731 "blksize 0x%04x\n", sc->sc_dev.dv_xname, start, size, blksize));
732
733 for (p = sc->sc_dmas; p && p->addr != start; p = p->next)
734 ;
735 if (!p) {
736 DPRINTF(AUGLX_DBG_DMA, ("%s dma reg not found\n",
737 sc->sc_dev.dv_xname));
738 return -1;
739 }
740
741 /* set up the PRDs */
742 nprd = size / blksize;
743 if (sc->bm1.sc_nprd != nprd + 1) {
744 if (sc->bm1.sc_nprd > 0)
745 auglx_free_prd(sc, &sc->bm1);
746 sc->bm1.sc_nprd = nprd + 1;
747 auglx_alloc_prd(sc,
748 sc->bm1.sc_nprd * sizeof(struct auglx_prd), &sc->bm1);
749 }
750 DPRINTF(AUGLX_DBG_DMA, ("%s: nprd = %d\n", sc->sc_dev.dv_xname,
751 nprd));
752 addr = p->segs->ds_addr;
753 for (i = 0; i < nprd; i++) {
754 sc->bm1.sc_vprd[i].base = addr;
755 sc->bm1.sc_vprd[i].size = blksize | AUGLX_PRD_EOP;
756 addr += blksize;
757 }
758 sc->bm1.sc_vprd[i].base = sc->bm1.sc_prd->dm_segs[0].ds_addr;
759 sc->bm1.sc_vprd[i].size = AUGLX_PRD_JMP;
760
761 #ifdef AUGLX_DEBUG
762 for (i = 0; i < sc->bm1.sc_nprd; i++)
763 DPRINTF(AUGLX_DBG_DMA, ("%s: PRD[%d].base = %p, size %p\n",
764 sc->sc_dev.dv_xname, i, sc->bm1.sc_vprd[i].base,
765 sc->bm1.sc_vprd[i].size));
766 #endif
767 sc->bm1.intr = intr;
768 sc->bm1.arg = arg;
769
770 mtx_enter(&audio_lock);
771 /* Program the BM1 PRD register */
772 bus_space_write_4(sc->sc_iot, sc->sc_ioh, ACC_BM1_PRD,
773 sc->bm1.sc_prd->dm_segs[0].ds_addr);
774 /* Start Audio Bus Master 0 */
775 bus_space_write_1(sc->sc_iot, sc->sc_ioh, ACC_BM1_CMD,
776 BMx_CMD_RW | BMx_CMD_BM_CTL_EN);
777 mtx_leave(&audio_lock);
778 return 0;
779 }
780
781 int
auglx_allocmem(struct auglx_softc * sc,size_t size,size_t align,struct auglx_dma * p)782 auglx_allocmem(struct auglx_softc *sc, size_t size, size_t align,
783 struct auglx_dma *p)
784 {
785 int error;
786
787 p->size = size;
788 error = bus_dmamem_alloc(sc->sc_dmat, p->size, align, 0, p->segs, 1,
789 &p->nsegs, BUS_DMA_NOWAIT);
790 if (error) {
791 DPRINTF(AUGLX_DBG_DMA,
792 ("%s: bus_dmamem_alloc failed: error %d\n",
793 sc->sc_dev.dv_xname, error));
794 return error;
795 }
796
797 error = bus_dmamem_map(sc->sc_dmat, p->segs, 1, p->size, &p->addr,
798 BUS_DMA_NOWAIT | sc->sc_dmamap_flags);
799 if (error) {
800 DPRINTF(AUGLX_DBG_DMA,
801 ("%s: bus_dmamem_map failed: error %d\n",
802 sc->sc_dev.dv_xname, error));
803 goto free;
804 }
805
806 error = bus_dmamap_create(sc->sc_dmat, p->size, 1, p->size, 0,
807 BUS_DMA_NOWAIT, &p->map);
808 if (error) {
809 DPRINTF(AUGLX_DBG_DMA,
810 ("%s: bus_dmamap_create failed: error %d\n",
811 sc->sc_dev.dv_xname, error));
812 goto unmap;
813 }
814
815 error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, p->size, NULL,
816 BUS_DMA_NOWAIT);
817 if (error) {
818 DPRINTF(AUGLX_DBG_DMA,
819 ("%s: bus_dmamap_load failed: error %d\n",
820 sc->sc_dev.dv_xname, error));
821 goto destroy;
822 }
823 return 0;
824
825 destroy:
826 bus_dmamap_destroy(sc->sc_dmat, p->map);
827 unmap:
828 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
829 free:
830 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
831 return error;
832 }
833
834 void
auglx_freemem(struct auglx_softc * sc,struct auglx_dma * p)835 auglx_freemem(struct auglx_softc *sc, struct auglx_dma *p)
836 {
837 bus_dmamap_unload(sc->sc_dmat, p->map);
838 bus_dmamap_destroy(sc->sc_dmat, p->map);
839 bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size);
840 bus_dmamem_free(sc->sc_dmat, p->segs, p->nsegs);
841 }
842
843 int
auglx_alloc_prd(struct auglx_softc * sc,size_t size,struct auglx_ring * bm)844 auglx_alloc_prd(struct auglx_softc *sc, size_t size, struct auglx_ring *bm)
845 {
846 int error, rseg;
847
848 /*
849 * Allocate PRD table structure, and create and load the
850 * DMA map for it.
851 */
852 if ((error = bus_dmamem_alloc(sc->sc_dmat, size,
853 PAGE_SIZE, 0, &bm->seg, 1, &rseg, 0)) != 0) {
854 printf("%s: unable to allocate PRD, error = %d\n",
855 sc->sc_dev.dv_xname, error);
856 goto fail_0;
857 }
858
859 if ((error = bus_dmamem_map(sc->sc_dmat, &bm->seg, rseg,
860 size, (caddr_t *)&bm->sc_vprd,
861 sc->sc_dmamap_flags)) != 0) {
862 printf("%s: unable to map PRD, error = %d\n",
863 sc->sc_dev.dv_xname, error);
864 goto fail_1;
865 }
866
867 if ((error = bus_dmamap_create(sc->sc_dmat, size,
868 1, size, 0, 0, &bm->sc_prd)) != 0) {
869 printf("%s: unable to create PRD DMA map, "
870 "error = %d\n", sc->sc_dev.dv_xname, error);
871 goto fail_2;
872 }
873
874 if ((error = bus_dmamap_load(sc->sc_dmat, bm->sc_prd, bm->sc_vprd,
875 size, NULL, 0)) != 0) {
876 printf("%s: unable tp load control data DMA map, "
877 "error = %d\n", sc->sc_dev.dv_xname, error);
878 goto fail_3;
879 }
880
881 return 0;
882
883 fail_3:
884 bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd);
885 fail_2:
886 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd,
887 sizeof(struct auglx_prd));
888 fail_1:
889 bus_dmamem_free(sc->sc_dmat, &bm->seg, rseg);
890 fail_0:
891 return error;
892 }
893
894 void
auglx_free_prd(struct auglx_softc * sc,struct auglx_ring * bm)895 auglx_free_prd(struct auglx_softc *sc, struct auglx_ring *bm)
896 {
897 bus_dmamap_unload(sc->sc_dmat, bm->sc_prd);
898 bus_dmamap_destroy(sc->sc_dmat, bm->sc_prd);
899 bus_dmamem_unmap(sc->sc_dmat, (caddr_t)bm->sc_vprd, bm->sc_size);
900 bus_dmamem_free(sc->sc_dmat, &bm->seg, bm->nsegs);
901 }
902
903 int
auglx_activate(struct device * self,int act)904 auglx_activate(struct device *self, int act)
905 {
906 struct auglx_softc *sc = (struct auglx_softc *)self;
907
908 if (act == DVACT_RESUME)
909 ac97_resume(&sc->host_if, sc->codec_if);
910 return (config_activate_children(self, act));
911 }
912