1 | /* $NetBSD: fms.c,v 1.42 2014/03/29 19:28:24 christos Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1999, 2008 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Witold J. Wnuk. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Forte Media FM801 Audio Device Driver |
34 | */ |
35 | |
36 | #include <sys/cdefs.h> |
37 | __KERNEL_RCSID(0, "$NetBSD: fms.c,v 1.42 2014/03/29 19:28:24 christos Exp $" ); |
38 | |
39 | #include "mpu.h" |
40 | |
41 | #include <sys/param.h> |
42 | #include <sys/systm.h> |
43 | #include <sys/kernel.h> |
44 | #include <sys/kmem.h> |
45 | #include <sys/device.h> |
46 | #include <sys/audioio.h> |
47 | |
48 | #include <sys/bus.h> |
49 | #include <sys/cpu.h> |
50 | |
51 | #include <dev/pci/pcidevs.h> |
52 | #include <dev/pci/pcivar.h> |
53 | |
54 | #include <dev/audio_if.h> |
55 | #include <dev/mulaw.h> |
56 | #include <dev/auconv.h> |
57 | |
58 | #include <dev/ic/ac97var.h> |
59 | #include <dev/ic/mpuvar.h> |
60 | |
61 | #include <dev/pci/fmsvar.h> |
62 | |
63 | |
64 | struct fms_dma { |
65 | struct fms_dma *next; |
66 | void *addr; |
67 | size_t size; |
68 | bus_dmamap_t map; |
69 | bus_dma_segment_t seg; |
70 | }; |
71 | |
72 | |
73 | |
74 | static int fms_match(device_t, cfdata_t, void *); |
75 | static void fms_attach(device_t, device_t, void *); |
76 | static int fms_intr(void *); |
77 | |
78 | static int fms_query_encoding(void *, struct audio_encoding *); |
79 | static int fms_set_params(void *, int, int, audio_params_t *, |
80 | audio_params_t *, stream_filter_list_t *, |
81 | stream_filter_list_t *); |
82 | static int fms_round_blocksize(void *, int, int, const audio_params_t *); |
83 | static int fms_halt_output(void *); |
84 | static int fms_halt_input(void *); |
85 | static int fms_getdev(void *, struct audio_device *); |
86 | static int fms_set_port(void *, mixer_ctrl_t *); |
87 | static int fms_get_port(void *, mixer_ctrl_t *); |
88 | static int fms_query_devinfo(void *, mixer_devinfo_t *); |
89 | static void *fms_malloc(void *, int, size_t); |
90 | static void fms_free(void *, void *, size_t); |
91 | static size_t fms_round_buffersize(void *, int, size_t); |
92 | static paddr_t fms_mappage(void *, void *, off_t, int); |
93 | static int fms_get_props(void *); |
94 | static int fms_trigger_output(void *, void *, void *, int, |
95 | void (*)(void *), void *, |
96 | const audio_params_t *); |
97 | static int fms_trigger_input(void *, void *, void *, int, |
98 | void (*)(void *), void *, |
99 | const audio_params_t *); |
100 | static void fms_get_locks(void *, kmutex_t **, kmutex_t **); |
101 | |
102 | CFATTACH_DECL_NEW(fms, sizeof (struct fms_softc), |
103 | fms_match, fms_attach, NULL, NULL); |
104 | |
105 | static struct audio_device fms_device = { |
106 | "Forte Media 801" , |
107 | "1.0" , |
108 | "fms" |
109 | }; |
110 | |
111 | |
112 | static const struct audio_hw_if fms_hw_if = { |
113 | NULL, /* open */ |
114 | NULL, /* close */ |
115 | NULL, |
116 | fms_query_encoding, |
117 | fms_set_params, |
118 | fms_round_blocksize, |
119 | NULL, |
120 | NULL, |
121 | NULL, |
122 | NULL, |
123 | NULL, |
124 | fms_halt_output, |
125 | fms_halt_input, |
126 | NULL, |
127 | fms_getdev, |
128 | NULL, |
129 | fms_set_port, |
130 | fms_get_port, |
131 | fms_query_devinfo, |
132 | fms_malloc, |
133 | fms_free, |
134 | fms_round_buffersize, |
135 | fms_mappage, |
136 | fms_get_props, |
137 | fms_trigger_output, |
138 | fms_trigger_input, |
139 | NULL, |
140 | fms_get_locks, |
141 | }; |
142 | |
143 | static int fms_attach_codec(void *, struct ac97_codec_if *); |
144 | static int fms_read_codec(void *, uint8_t, uint16_t *); |
145 | static int fms_write_codec(void *, uint8_t, uint16_t); |
146 | static int fms_reset_codec(void *); |
147 | |
148 | #define FM_PCM_VOLUME 0x00 |
149 | #define FM_FM_VOLUME 0x02 |
150 | #define FM_I2S_VOLUME 0x04 |
151 | #define FM_RECORD_SOURCE 0x06 |
152 | |
153 | #define FM_PLAY_CTL 0x08 |
154 | #define FM_PLAY_RATE_MASK 0x0f00 |
155 | #define FM_PLAY_BUF1_LAST 0x0001 |
156 | #define FM_PLAY_BUF2_LAST 0x0002 |
157 | #define FM_PLAY_START 0x0020 |
158 | #define FM_PLAY_PAUSE 0x0040 |
159 | #define FM_PLAY_STOPNOW 0x0080 |
160 | #define FM_PLAY_16BIT 0x4000 |
161 | #define FM_PLAY_STEREO 0x8000 |
162 | |
163 | #define FM_PLAY_DMALEN 0x0a |
164 | #define FM_PLAY_DMABUF1 0x0c |
165 | #define FM_PLAY_DMABUF2 0x10 |
166 | |
167 | |
168 | #define FM_REC_CTL 0x14 |
169 | #define FM_REC_RATE_MASK 0x0f00 |
170 | #define FM_REC_BUF1_LAST 0x0001 |
171 | #define FM_REC_BUF2_LAST 0x0002 |
172 | #define FM_REC_START 0x0020 |
173 | #define FM_REC_PAUSE 0x0040 |
174 | #define FM_REC_STOPNOW 0x0080 |
175 | #define FM_REC_16BIT 0x4000 |
176 | #define FM_REC_STEREO 0x8000 |
177 | |
178 | |
179 | #define FM_REC_DMALEN 0x16 |
180 | #define FM_REC_DMABUF1 0x18 |
181 | #define FM_REC_DMABUF2 0x1c |
182 | |
183 | #define FM_CODEC_CTL 0x22 |
184 | #define FM_VOLUME 0x26 |
185 | #define FM_VOLUME_MUTE 0x8000 |
186 | |
187 | #define FM_CODEC_CMD 0x2a |
188 | #define FM_CODEC_CMD_READ 0x0080 |
189 | #define FM_CODEC_CMD_VALID 0x0100 |
190 | #define FM_CODEC_CMD_BUSY 0x0200 |
191 | |
192 | #define FM_CODEC_DATA 0x2c |
193 | |
194 | #define FM_IO_CTL 0x52 |
195 | #define FM_CARD_CTL 0x54 |
196 | |
197 | #define FM_INTMASK 0x56 |
198 | #define FM_INTMASK_PLAY 0x0001 |
199 | #define FM_INTMASK_REC 0x0002 |
200 | #define FM_INTMASK_VOL 0x0040 |
201 | #define FM_INTMASK_MPU 0x0080 |
202 | |
203 | #define FM_INTSTATUS 0x5a |
204 | #define FM_INTSTATUS_PLAY 0x0100 |
205 | #define FM_INTSTATUS_REC 0x0200 |
206 | #define FM_INTSTATUS_VOL 0x4000 |
207 | #define FM_INTSTATUS_MPU 0x8000 |
208 | |
209 | |
210 | static int |
211 | fms_match(device_t parent, cfdata_t match, void *aux) |
212 | { |
213 | struct pci_attach_args *pa; |
214 | |
215 | pa = (struct pci_attach_args *)aux; |
216 | if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_FORTEMEDIA) |
217 | return 0; |
218 | if (PCI_PRODUCT(pa->pa_id) != PCI_PRODUCT_FORTEMEDIA_FM801) |
219 | return 0; |
220 | |
221 | return 1; |
222 | } |
223 | |
224 | static void |
225 | fms_attach(device_t parent, device_t self, void *aux) |
226 | { |
227 | struct pci_attach_args *pa; |
228 | struct fms_softc *sc; |
229 | struct audio_attach_args aa; |
230 | const char *intrstr; |
231 | pci_chipset_tag_t pc; |
232 | pcitag_t pt; |
233 | pci_intr_handle_t ih; |
234 | uint16_t k1; |
235 | char intrbuf[PCI_INTRSTR_LEN]; |
236 | |
237 | pa = aux; |
238 | sc = device_private(self); |
239 | sc->sc_dev = self; |
240 | intrstr = NULL; |
241 | pc = pa->pa_pc; |
242 | pt = pa->pa_tag; |
243 | aprint_naive(": Audio controller\n" ); |
244 | aprint_normal(": Forte Media FM-801\n" ); |
245 | |
246 | if (pci_mapreg_map(pa, 0x10, PCI_MAPREG_TYPE_IO, 0, &sc->sc_iot, |
247 | &sc->sc_ioh, &sc->sc_ioaddr, &sc->sc_iosize)) { |
248 | aprint_error_dev(sc->sc_dev, "can't map i/o space\n" ); |
249 | return; |
250 | } |
251 | if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x30, 2, |
252 | &sc->sc_mpu_ioh)) |
253 | panic("fms_attach: can't get mpu subregion handle" ); |
254 | if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, 0x68, 4, |
255 | &sc->sc_opl_ioh)) |
256 | panic("fms_attach: can't get opl subregion handle" ); |
257 | |
258 | if (pci_intr_map(pa, &ih)) { |
259 | aprint_error_dev(sc->sc_dev, "couldn't map interrupt\n" ); |
260 | return; |
261 | } |
262 | intrstr = pci_intr_string(pc, ih, intrbuf, sizeof(intrbuf)); |
263 | |
264 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE); |
265 | mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_AUDIO); |
266 | |
267 | sc->sc_ih = pci_intr_establish(pc, ih, IPL_AUDIO, fms_intr, sc); |
268 | if (sc->sc_ih == NULL) { |
269 | aprint_error_dev(sc->sc_dev, "couldn't establish interrupt" ); |
270 | if (intrstr != NULL) |
271 | aprint_error(" at %s" , intrstr); |
272 | aprint_error("\n" ); |
273 | mutex_destroy(&sc->sc_lock); |
274 | mutex_destroy(&sc->sc_intr_lock); |
275 | return; |
276 | } |
277 | |
278 | sc->sc_dmat = pa->pa_dmat; |
279 | |
280 | aprint_normal_dev(sc->sc_dev, "interrupting at %s\n" , intrstr); |
281 | |
282 | /* Disable legacy audio (SBPro compatibility) */ |
283 | pci_conf_write(pc, pt, 0x40, 0); |
284 | |
285 | /* Reset codec and AC'97 */ |
286 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020); |
287 | delay(2); /* > 1us according to AC'97 documentation */ |
288 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000); |
289 | delay(1); /* > 168.2ns according to AC'97 documentation */ |
290 | |
291 | /* Set up volume */ |
292 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PCM_VOLUME, 0x0808); |
293 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_FM_VOLUME, 0x0808); |
294 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_I2S_VOLUME, 0x0808); |
295 | |
296 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_RECORD_SOURCE, 0x0000); |
297 | |
298 | /* Unmask playback, record and mpu interrupts, mask the rest */ |
299 | k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK); |
300 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTMASK, |
301 | (k1 & ~(FM_INTMASK_PLAY | FM_INTMASK_REC | FM_INTMASK_MPU)) | |
302 | FM_INTMASK_VOL); |
303 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS, |
304 | FM_INTSTATUS_PLAY | FM_INTSTATUS_REC | FM_INTSTATUS_MPU | |
305 | FM_INTSTATUS_VOL); |
306 | |
307 | sc->host_if.arg = sc; |
308 | sc->host_if.attach = fms_attach_codec; |
309 | sc->host_if.read = fms_read_codec; |
310 | sc->host_if.write = fms_write_codec; |
311 | sc->host_if.reset = fms_reset_codec; |
312 | |
313 | if (ac97_attach(&sc->host_if, self, &sc->sc_lock) != 0) { |
314 | mutex_destroy(&sc->sc_intr_lock); |
315 | mutex_destroy(&sc->sc_lock); |
316 | return; |
317 | } |
318 | |
319 | audio_attach_mi(&fms_hw_if, sc, sc->sc_dev); |
320 | |
321 | aa.type = AUDIODEV_TYPE_OPL; |
322 | aa.hwif = NULL; |
323 | aa.hdl = NULL; |
324 | config_found(sc->sc_dev, &aa, audioprint); |
325 | |
326 | aa.type = AUDIODEV_TYPE_MPU; |
327 | aa.hwif = NULL; |
328 | aa.hdl = NULL; |
329 | sc->sc_mpu_dev = config_found(sc->sc_dev, &aa, audioprint); |
330 | } |
331 | |
332 | /* |
333 | * Each AC-link frame takes 20.8us, data should be ready in next frame, |
334 | * we allow more than two. |
335 | */ |
336 | #define TIMO 50 |
337 | static int |
338 | fms_read_codec(void *addr, uint8_t reg, uint16_t *val) |
339 | { |
340 | struct fms_softc *sc; |
341 | int i; |
342 | |
343 | sc = addr; |
344 | /* Poll until codec is ready */ |
345 | for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh, |
346 | FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++) |
347 | delay(1); |
348 | if (i >= TIMO) { |
349 | printf("fms: codec busy\n" ); |
350 | return 1; |
351 | } |
352 | |
353 | /* Write register index, read access */ |
354 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD, |
355 | reg | FM_CODEC_CMD_READ); |
356 | |
357 | /* Poll until we have valid data */ |
358 | for (i = 0; i < TIMO && !(bus_space_read_2(sc->sc_iot, sc->sc_ioh, |
359 | FM_CODEC_CMD) & FM_CODEC_CMD_VALID); i++) |
360 | delay(1); |
361 | if (i >= TIMO) { |
362 | printf("fms: no data from codec\n" ); |
363 | return 1; |
364 | } |
365 | |
366 | /* Read data */ |
367 | *val = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA); |
368 | return 0; |
369 | } |
370 | |
371 | static int |
372 | fms_write_codec(void *addr, uint8_t reg, uint16_t val) |
373 | { |
374 | struct fms_softc *sc = addr; |
375 | int i; |
376 | |
377 | /* Poll until codec is ready */ |
378 | for (i = 0; i < TIMO && bus_space_read_2(sc->sc_iot, sc->sc_ioh, |
379 | FM_CODEC_CMD) & FM_CODEC_CMD_BUSY; i++) |
380 | delay(1); |
381 | if (i >= TIMO) { |
382 | printf("fms: codec busy\n" ); |
383 | return 1; |
384 | } |
385 | |
386 | /* Write data */ |
387 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_DATA, val); |
388 | /* Write index register, write access */ |
389 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CMD, reg); |
390 | return 0; |
391 | } |
392 | #undef TIMO |
393 | |
394 | static int |
395 | fms_attach_codec(void *addr, struct ac97_codec_if *cif) |
396 | { |
397 | struct fms_softc *sc; |
398 | |
399 | sc = addr; |
400 | sc->codec_if = cif; |
401 | return 0; |
402 | } |
403 | |
404 | /* Cold Reset */ |
405 | static int |
406 | fms_reset_codec(void *addr) |
407 | { |
408 | struct fms_softc *sc; |
409 | |
410 | sc = addr; |
411 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0020); |
412 | delay(2); |
413 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_CODEC_CTL, 0x0000); |
414 | delay(1); |
415 | return 0; |
416 | } |
417 | |
418 | static int |
419 | fms_intr(void *arg) |
420 | { |
421 | struct fms_softc *sc = arg; |
422 | #if NMPU > 0 |
423 | struct mpu_softc *sc_mpu = device_private(sc->sc_mpu_dev); |
424 | #endif |
425 | uint16_t istat; |
426 | |
427 | mutex_spin_enter(&sc->sc_intr_lock); |
428 | |
429 | istat = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS); |
430 | |
431 | if (istat & FM_INTSTATUS_PLAY) { |
432 | if ((sc->sc_play_nextblk += sc->sc_play_blksize) >= |
433 | sc->sc_play_end) |
434 | sc->sc_play_nextblk = sc->sc_play_start; |
435 | |
436 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, |
437 | sc->sc_play_flip++ & 1 ? |
438 | FM_PLAY_DMABUF2 : FM_PLAY_DMABUF1, sc->sc_play_nextblk); |
439 | |
440 | if (sc->sc_pintr) |
441 | sc->sc_pintr(sc->sc_parg); |
442 | else |
443 | printf("unexpected play intr\n" ); |
444 | } |
445 | |
446 | if (istat & FM_INTSTATUS_REC) { |
447 | if ((sc->sc_rec_nextblk += sc->sc_rec_blksize) >= |
448 | sc->sc_rec_end) |
449 | sc->sc_rec_nextblk = sc->sc_rec_start; |
450 | |
451 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, |
452 | sc->sc_rec_flip++ & 1 ? |
453 | FM_REC_DMABUF2 : FM_REC_DMABUF1, sc->sc_rec_nextblk); |
454 | |
455 | if (sc->sc_rintr) |
456 | sc->sc_rintr(sc->sc_rarg); |
457 | else |
458 | printf("unexpected rec intr\n" ); |
459 | } |
460 | |
461 | #if NMPU > 0 |
462 | if (istat & FM_INTSTATUS_MPU) |
463 | mpu_intr(sc_mpu); |
464 | #endif |
465 | |
466 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_INTSTATUS, |
467 | istat & (FM_INTSTATUS_PLAY | FM_INTSTATUS_REC)); |
468 | |
469 | mutex_spin_exit(&sc->sc_intr_lock); |
470 | |
471 | return 1; |
472 | } |
473 | |
474 | static int |
475 | fms_query_encoding(void *addr, struct audio_encoding *fp) |
476 | { |
477 | |
478 | switch (fp->index) { |
479 | case 0: |
480 | strcpy(fp->name, AudioEmulaw); |
481 | fp->encoding = AUDIO_ENCODING_ULAW; |
482 | fp->precision = 8; |
483 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
484 | return 0; |
485 | case 1: |
486 | strcpy(fp->name, AudioEslinear_le); |
487 | fp->encoding = AUDIO_ENCODING_SLINEAR_LE; |
488 | fp->precision = 16; |
489 | fp->flags = 0; |
490 | return 0; |
491 | case 2: |
492 | strcpy(fp->name, AudioEulinear); |
493 | fp->encoding = AUDIO_ENCODING_ULINEAR; |
494 | fp->precision = 8; |
495 | fp->flags = 0; |
496 | return 0; |
497 | case 3: |
498 | strcpy(fp->name, AudioEalaw); |
499 | fp->encoding = AUDIO_ENCODING_ALAW; |
500 | fp->precision = 8; |
501 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
502 | return 0; |
503 | case 4: |
504 | strcpy(fp->name, AudioEulinear_le); |
505 | fp->encoding = AUDIO_ENCODING_ULINEAR_LE; |
506 | fp->precision = 16; |
507 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
508 | return 0; |
509 | case 5: |
510 | strcpy(fp->name, AudioEslinear); |
511 | fp->encoding = AUDIO_ENCODING_SLINEAR; |
512 | fp->precision = 8; |
513 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
514 | return 0; |
515 | case 6: |
516 | strcpy(fp->name, AudioEulinear_be); |
517 | fp->encoding = AUDIO_ENCODING_ULINEAR_BE; |
518 | fp->precision = 16; |
519 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
520 | return 0; |
521 | case 7: |
522 | strcpy(fp->name, AudioEslinear_be); |
523 | fp->encoding = AUDIO_ENCODING_SLINEAR_BE; |
524 | fp->precision = 16; |
525 | fp->flags = AUDIO_ENCODINGFLAG_EMULATED; |
526 | return 0; |
527 | default: |
528 | return EINVAL; |
529 | } |
530 | } |
531 | |
532 | /* |
533 | * Range below -limit- is set to -rate- |
534 | * What a pity FM801 does not have 24000 |
535 | * 24000 -> 22050 sounds rather poor |
536 | */ |
537 | static struct { |
538 | int limit; |
539 | int rate; |
540 | } const fms_rates[11] = { |
541 | { 6600, 5500 }, |
542 | { 8750, 8000 }, |
543 | { 10250, 9600 }, |
544 | { 13200, 11025 }, |
545 | { 17500, 16000 }, |
546 | { 20500, 19200 }, |
547 | { 26500, 22050 }, |
548 | { 35000, 32000 }, |
549 | { 41000, 38400 }, |
550 | { 46000, 44100 }, |
551 | { 48000, 48000 }, |
552 | /* anything above -> 48000 */ |
553 | }; |
554 | |
555 | #define FMS_NFORMATS 4 |
556 | static const struct audio_format fms_formats[FMS_NFORMATS] = { |
557 | {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, |
558 | 2, AUFMT_STEREO, 11, {5500, 8000, 9600, 11025, 16000, 19200, 22050, |
559 | 32000, 38400, 44100, 48000}}, |
560 | {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16, |
561 | 1, AUFMT_MONAURAL, 11, {5500, 8000, 9600, 11025, 16000, 19200, 22050, |
562 | 32000, 38400, 44100, 48000}}, |
563 | {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULINEAR_LE, 8, 8, |
564 | 2, AUFMT_STEREO, 11, {5500, 8000, 9600, 11025, 16000, 19200, 22050, |
565 | 32000, 38400, 44100, 48000}}, |
566 | {NULL, AUMODE_PLAY | AUMODE_RECORD, AUDIO_ENCODING_ULINEAR_LE, 8, 8, |
567 | 1, AUFMT_MONAURAL, 11, {5500, 8000, 9600, 11025, 16000, 19200, 22050, |
568 | 32000, 38400, 44100, 48000}}, |
569 | }; |
570 | |
571 | static int |
572 | fms_set_params(void *addr, int setmode, int usemode, |
573 | audio_params_t *play, audio_params_t *rec, stream_filter_list_t *pfil, |
574 | stream_filter_list_t *rfil) |
575 | { |
576 | struct fms_softc *sc; |
577 | int i, index; |
578 | |
579 | sc = addr; |
580 | if (setmode & AUMODE_PLAY) { |
581 | for (i = 0; i < 10 && play->sample_rate > fms_rates[i].limit; |
582 | i++) |
583 | continue; |
584 | play->sample_rate = fms_rates[i].rate; |
585 | index = auconv_set_converter(fms_formats, FMS_NFORMATS, |
586 | AUMODE_PLAY, play, FALSE, pfil); |
587 | if (index < 0) |
588 | return EINVAL; |
589 | sc->sc_play_reg = i << 8; |
590 | if (fms_formats[index].channels == 2) |
591 | sc->sc_play_reg |= FM_PLAY_STEREO; |
592 | if (fms_formats[index].precision == 16) |
593 | sc->sc_play_reg |= FM_PLAY_16BIT; |
594 | } |
595 | |
596 | if (setmode & AUMODE_RECORD) { |
597 | for (i = 0; i < 10 && rec->sample_rate > fms_rates[i].limit; |
598 | i++) |
599 | continue; |
600 | rec->sample_rate = fms_rates[i].rate; |
601 | index = auconv_set_converter(fms_formats, FMS_NFORMATS, |
602 | AUMODE_RECORD, rec, FALSE, rfil); |
603 | if (index < 0) |
604 | return EINVAL; |
605 | sc->sc_rec_reg = i << 8; |
606 | if (fms_formats[index].channels == 2) |
607 | sc->sc_rec_reg |= FM_REC_STEREO; |
608 | if (fms_formats[index].precision == 16) |
609 | sc->sc_rec_reg |= FM_REC_16BIT; |
610 | } |
611 | |
612 | return 0; |
613 | } |
614 | |
615 | static int |
616 | fms_round_blocksize(void *addr, int blk, int mode, |
617 | const audio_params_t *param) |
618 | { |
619 | |
620 | return blk & ~0xf; |
621 | } |
622 | |
623 | static int |
624 | fms_halt_output(void *addr) |
625 | { |
626 | struct fms_softc *sc; |
627 | uint16_t k1; |
628 | |
629 | sc = addr; |
630 | k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL); |
631 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL, |
632 | (k1 & ~(FM_PLAY_STOPNOW | FM_PLAY_START)) | |
633 | FM_PLAY_BUF1_LAST | FM_PLAY_BUF2_LAST); |
634 | |
635 | return 0; |
636 | } |
637 | |
638 | static int |
639 | fms_halt_input(void *addr) |
640 | { |
641 | struct fms_softc *sc; |
642 | uint16_t k1; |
643 | |
644 | sc = addr; |
645 | k1 = bus_space_read_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL); |
646 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL, |
647 | (k1 & ~(FM_REC_STOPNOW | FM_REC_START)) | |
648 | FM_REC_BUF1_LAST | FM_REC_BUF2_LAST); |
649 | |
650 | return 0; |
651 | } |
652 | |
653 | static int |
654 | fms_getdev(void *addr, struct audio_device *retp) |
655 | { |
656 | |
657 | *retp = fms_device; |
658 | return 0; |
659 | } |
660 | |
661 | static int |
662 | fms_set_port(void *addr, mixer_ctrl_t *cp) |
663 | { |
664 | struct fms_softc *sc; |
665 | |
666 | sc = addr; |
667 | return sc->codec_if->vtbl->mixer_set_port(sc->codec_if, cp); |
668 | } |
669 | |
670 | static int |
671 | fms_get_port(void *addr, mixer_ctrl_t *cp) |
672 | { |
673 | struct fms_softc *sc; |
674 | |
675 | sc = addr; |
676 | return sc->codec_if->vtbl->mixer_get_port(sc->codec_if, cp); |
677 | } |
678 | |
679 | static void * |
680 | fms_malloc(void *addr, int direction, size_t size) |
681 | { |
682 | struct fms_softc *sc; |
683 | struct fms_dma *p; |
684 | int error; |
685 | int rseg; |
686 | |
687 | sc = addr; |
688 | p = kmem_alloc(sizeof(*p), KM_SLEEP); |
689 | if (p == NULL) |
690 | return NULL; |
691 | |
692 | p->size = size; |
693 | if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &p->seg, |
694 | 1, &rseg, BUS_DMA_WAITOK)) != 0) { |
695 | aprint_error_dev(sc->sc_dev, "unable to allocate DMA, error = %d\n" , error); |
696 | goto fail_alloc; |
697 | } |
698 | |
699 | if ((error = bus_dmamem_map(sc->sc_dmat, &p->seg, rseg, size, &p->addr, |
700 | BUS_DMA_WAITOK | BUS_DMA_COHERENT)) != 0) { |
701 | aprint_error_dev(sc->sc_dev, "unable to map DMA, error = %d\n" , |
702 | error); |
703 | goto fail_map; |
704 | } |
705 | |
706 | if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, |
707 | BUS_DMA_WAITOK, &p->map)) != 0) { |
708 | aprint_error_dev(sc->sc_dev, "unable to create DMA map, error = %d\n" , |
709 | error); |
710 | goto fail_create; |
711 | } |
712 | |
713 | if ((error = bus_dmamap_load(sc->sc_dmat, p->map, p->addr, size, NULL, |
714 | BUS_DMA_WAITOK)) != 0) { |
715 | aprint_error_dev(sc->sc_dev, "unable to load DMA map, error = %d\n" , |
716 | error); |
717 | goto fail_load; |
718 | } |
719 | |
720 | p->next = sc->sc_dmas; |
721 | sc->sc_dmas = p; |
722 | |
723 | return p->addr; |
724 | |
725 | |
726 | fail_load: |
727 | bus_dmamap_destroy(sc->sc_dmat, p->map); |
728 | fail_create: |
729 | bus_dmamem_unmap(sc->sc_dmat, p->addr, size); |
730 | fail_map: |
731 | bus_dmamem_free(sc->sc_dmat, &p->seg, 1); |
732 | fail_alloc: |
733 | kmem_free(p, sizeof(*p)); |
734 | return NULL; |
735 | } |
736 | |
737 | static void |
738 | fms_free(void *addr, void *ptr, size_t size) |
739 | { |
740 | struct fms_softc *sc; |
741 | struct fms_dma **pp, *p; |
742 | |
743 | sc = addr; |
744 | for (pp = &(sc->sc_dmas); (p = *pp) != NULL; pp = &p->next) |
745 | if (p->addr == ptr) { |
746 | bus_dmamap_unload(sc->sc_dmat, p->map); |
747 | bus_dmamap_destroy(sc->sc_dmat, p->map); |
748 | bus_dmamem_unmap(sc->sc_dmat, p->addr, p->size); |
749 | bus_dmamem_free(sc->sc_dmat, &p->seg, 1); |
750 | |
751 | *pp = p->next; |
752 | kmem_free(p, sizeof(*p)); |
753 | return; |
754 | } |
755 | |
756 | panic("fms_free: trying to free unallocated memory" ); |
757 | } |
758 | |
759 | static size_t |
760 | fms_round_buffersize(void *addr, int direction, size_t size) |
761 | { |
762 | |
763 | return size; |
764 | } |
765 | |
766 | static paddr_t |
767 | fms_mappage(void *addr, void *mem, off_t off, int prot) |
768 | { |
769 | struct fms_softc *sc; |
770 | struct fms_dma *p; |
771 | |
772 | sc = addr; |
773 | if (off < 0) |
774 | return -1; |
775 | |
776 | for (p = sc->sc_dmas; p && p->addr != mem; p = p->next) |
777 | continue; |
778 | if (p == NULL) |
779 | return -1; |
780 | |
781 | return bus_dmamem_mmap(sc->sc_dmat, &p->seg, 1, off, prot, |
782 | BUS_DMA_WAITOK); |
783 | } |
784 | |
785 | static int |
786 | fms_get_props(void *addr) |
787 | { |
788 | return AUDIO_PROP_MMAP | AUDIO_PROP_INDEPENDENT | |
789 | AUDIO_PROP_FULLDUPLEX; |
790 | } |
791 | |
792 | static int |
793 | fms_query_devinfo(void *addr, mixer_devinfo_t *dip) |
794 | { |
795 | struct fms_softc *sc; |
796 | |
797 | sc = addr; |
798 | return sc->codec_if->vtbl->query_devinfo(sc->codec_if, dip); |
799 | } |
800 | |
801 | static int |
802 | fms_trigger_output(void *addr, void *start, void *end, int blksize, |
803 | void (*intr)(void *), void *arg, const audio_params_t *param) |
804 | { |
805 | struct fms_softc *sc; |
806 | struct fms_dma *p; |
807 | |
808 | sc = addr; |
809 | sc->sc_pintr = intr; |
810 | sc->sc_parg = arg; |
811 | |
812 | for (p = sc->sc_dmas; p && p->addr != start; p = p->next) |
813 | continue; |
814 | |
815 | if (p == NULL) |
816 | panic("fms_trigger_output: request with bad start " |
817 | "address (%p)" , start); |
818 | |
819 | sc->sc_play_start = p->map->dm_segs[0].ds_addr; |
820 | sc->sc_play_end = sc->sc_play_start + ((char *)end - (char *)start); |
821 | sc->sc_play_blksize = blksize; |
822 | sc->sc_play_nextblk = sc->sc_play_start + sc->sc_play_blksize; |
823 | sc->sc_play_flip = 0; |
824 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMALEN, blksize - 1); |
825 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF1, |
826 | sc->sc_play_start); |
827 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_PLAY_DMABUF2, |
828 | sc->sc_play_nextblk); |
829 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_PLAY_CTL, |
830 | FM_PLAY_START | FM_PLAY_STOPNOW | sc->sc_play_reg); |
831 | return 0; |
832 | } |
833 | |
834 | |
835 | static int |
836 | fms_trigger_input(void *addr, void *start, void *end, int blksize, |
837 | void (*intr)(void *), void *arg, const audio_params_t *param) |
838 | { |
839 | struct fms_softc *sc; |
840 | struct fms_dma *p; |
841 | |
842 | sc = addr; |
843 | sc->sc_rintr = intr; |
844 | sc->sc_rarg = arg; |
845 | |
846 | for (p = sc->sc_dmas; p && p->addr != start; p = p->next) |
847 | continue; |
848 | |
849 | if (p == NULL) |
850 | panic("fms_trigger_input: request with bad start " |
851 | "address (%p)" , start); |
852 | |
853 | sc->sc_rec_start = p->map->dm_segs[0].ds_addr; |
854 | sc->sc_rec_end = sc->sc_rec_start + ((char *)end - (char *)start); |
855 | sc->sc_rec_blksize = blksize; |
856 | sc->sc_rec_nextblk = sc->sc_rec_start + sc->sc_rec_blksize; |
857 | sc->sc_rec_flip = 0; |
858 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_DMALEN, blksize - 1); |
859 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF1, |
860 | sc->sc_rec_start); |
861 | bus_space_write_4(sc->sc_iot, sc->sc_ioh, FM_REC_DMABUF2, |
862 | sc->sc_rec_nextblk); |
863 | bus_space_write_2(sc->sc_iot, sc->sc_ioh, FM_REC_CTL, |
864 | FM_REC_START | FM_REC_STOPNOW | sc->sc_rec_reg); |
865 | return 0; |
866 | } |
867 | |
868 | static void |
869 | fms_get_locks(void *addr, kmutex_t **intr, kmutex_t **thread) |
870 | { |
871 | struct fms_softc *sc; |
872 | |
873 | sc = addr; |
874 | *intr = &sc->sc_intr_lock; |
875 | *thread = &sc->sc_lock; |
876 | } |
877 | |