1 /* $NetBSD: cs4231.c,v 1.22 2008/04/28 18:49:27 garbled Exp $ */
4 * Copyright (c) 1998, 1999 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: cs4231.c,v 1.22 2008/04/28 18:49:27 garbled Exp $");
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/errno.h>
41 #include <sys/device.h>
42 #include <sys/malloc.h>
44 #include <machine/autoconf.h>
45 #include <machine/bus.h>
48 #include <sys/audioio.h>
49 #include <dev/audio_if.h>
51 #include <dev/ic/ad1848reg.h>
52 #include <dev/ic/cs4231reg.h>
53 #include <dev/ic/ad1848var.h>
54 #include <dev/ic/cs4231var.h>
57 #define CSAUDIO_DAC_LVL 0
58 #define CSAUDIO_LINE_IN_LVL 1
59 #define CSAUDIO_MONO_LVL 2
60 #define CSAUDIO_CD_LVL 3
61 #define CSAUDIO_OUTPUT_LVL 4
62 #define CSAUDIO_OUT_LVL 5
63 #define CSAUDIO_LINE_IN_MUTE 6
64 #define CSAUDIO_DAC_MUTE 7
65 #define CSAUDIO_CD_MUTE 8
66 #define CSAUDIO_MONO_MUTE 9
67 #define CSAUDIO_OUTPUT_MUTE 10
68 #define CSAUDIO_OUT_MUTE 11
69 #define CSAUDIO_REC_LVL 12
70 #define CSAUDIO_RECORD_SOURCE 13
72 #define CSAUDIO_INPUT_CLASS 14
73 #define CSAUDIO_MONITOR_CLASS 15
74 #define CSAUDIO_RECORD_CLASS 16
78 #define DPRINTF(x) if (cs4231_debug) printf x
83 struct audio_device cs4231_device
= {
90 /* ad1848 sc_{read,write}reg */
91 static int cs4231_read(struct ad1848_softc
*, int);
92 static void cs4231_write(struct ad1848_softc
*, int, int);
95 cs4231_read(struct ad1848_softc
*sc
, int index
)
98 return bus_space_read_1(sc
->sc_iot
, sc
->sc_ioh
, (index
<< 2));
102 cs4231_write(struct ad1848_softc
*sc
, int index
, int value
)
105 bus_space_write_1(sc
->sc_iot
, sc
->sc_ioh
, (index
<< 2), value
);
110 cs4231_common_attach(struct cs4231_softc
*sc
, bus_space_handle_t ioh
)
115 sc
->sc_ad1848
.parent
= sc
;
116 sc
->sc_ad1848
.sc_iot
= sc
->sc_bustag
;
117 sc
->sc_ad1848
.sc_ioh
= ioh
;
118 sc
->sc_ad1848
.sc_readreg
= cs4231_read
;
119 sc
->sc_ad1848
.sc_writereg
= cs4231_write
;
121 sc
->sc_playback
.t_name
= "playback";
122 sc
->sc_capture
.t_name
= "capture";
124 evcnt_attach_dynamic(&sc
->sc_intrcnt
, EVCNT_TYPE_INTR
,
126 device_xname(&sc
->sc_ad1848
.sc_dev
), "total");
128 evcnt_attach_dynamic(&sc
->sc_playback
.t_intrcnt
, EVCNT_TYPE_INTR
,
130 device_xname(&sc
->sc_ad1848
.sc_dev
), "playback");
132 evcnt_attach_dynamic(&sc
->sc_playback
.t_ierrcnt
, EVCNT_TYPE_INTR
,
134 device_xname(&sc
->sc_ad1848
.sc_dev
), "perrors");
136 evcnt_attach_dynamic(&sc
->sc_capture
.t_intrcnt
, EVCNT_TYPE_INTR
,
138 device_xname(&sc
->sc_ad1848
.sc_dev
), "capture");
140 evcnt_attach_dynamic(&sc
->sc_capture
.t_ierrcnt
, EVCNT_TYPE_INTR
,
142 device_xname(&sc
->sc_ad1848
.sc_dev
), "cerrors");
144 /* put chip in native mode to access (extended) ID register */
145 reg
= ad_read(&sc
->sc_ad1848
, SP_MISC_INFO
);
146 ad_write(&sc
->sc_ad1848
, SP_MISC_INFO
, reg
| MODE2
);
148 /* read version numbers from I25 */
149 reg
= ad_read(&sc
->sc_ad1848
, CS_VERSION_ID
);
150 switch (reg
& (CS_VERSION_NUMBER
| CS_VERSION_CHIPID
)) {
152 sc
->sc_ad1848
.chip_name
= "CS4231A";
155 sc
->sc_ad1848
.chip_name
= "CS4231";
158 sc
->sc_ad1848
.chip_name
= "CS4232";
161 sc
->sc_ad1848
.chip_name
= "CS4232C";
164 if ((buf
= malloc(32, M_TEMP
, M_NOWAIT
)) != NULL
) {
165 snprintf(buf
, 32, "unknown rev: %x/%x",
167 sc
->sc_ad1848
.chip_name
= buf
;
171 sc
->sc_ad1848
.mode
= 2; /* put ad1848 driver in `MODE 2' mode */
172 ad1848_attach(&sc
->sc_ad1848
);
176 cs4231_malloc(void *addr
, int direction
, size_t size
,
177 struct malloc_type
*pool
, int flags
)
179 struct cs4231_softc
*sc
;
180 bus_dma_tag_t dmatag
;
184 dmatag
= sc
->sc_dmatag
;
185 p
= malloc(sizeof(*p
), pool
, flags
);
189 /* Allocate a DMA map */
190 if (bus_dmamap_create(dmatag
, size
, 1, size
, 0,
191 BUS_DMA_NOWAIT
, &p
->dmamap
) != 0)
194 /* Allocate DMA memory */
196 if (bus_dmamem_alloc(dmatag
, size
, 64*1024, 0,
197 p
->segs
, sizeof(p
->segs
)/sizeof(p
->segs
[0]),
198 &p
->nsegs
, BUS_DMA_NOWAIT
) != 0)
201 /* Map DMA memory into kernel space */
202 if (bus_dmamem_map(dmatag
, p
->segs
, p
->nsegs
, p
->size
,
203 &p
->addr
, BUS_DMA_NOWAIT
|BUS_DMA_COHERENT
) != 0)
206 /* Load the buffer */
207 if (bus_dmamap_load(dmatag
, p
->dmamap
,
208 p
->addr
, size
, NULL
, BUS_DMA_NOWAIT
) != 0)
211 p
->next
= sc
->sc_dmas
;
216 bus_dmamem_unmap(dmatag
, p
->addr
, p
->size
);
218 bus_dmamem_free(dmatag
, p
->segs
, p
->nsegs
);
220 bus_dmamap_destroy(dmatag
, p
->dmamap
);
227 cs4231_free(void *addr
, void *ptr
, struct malloc_type
*pool
)
229 struct cs4231_softc
*sc
;
230 bus_dma_tag_t dmatag
;
231 struct cs_dma
*p
, **pp
;
234 dmatag
= sc
->sc_dmatag
;
235 for (pp
= &sc
->sc_dmas
; (p
= *pp
) != NULL
; pp
= &(*pp
)->next
) {
238 bus_dmamap_unload(dmatag
, p
->dmamap
);
239 bus_dmamem_unmap(dmatag
, p
->addr
, p
->size
);
240 bus_dmamem_free(dmatag
, p
->segs
, p
->nsegs
);
241 bus_dmamap_destroy(dmatag
, p
->dmamap
);
246 printf("cs4231_free: rogue pointer\n");
251 * Set up transfer and return DMA address and byte count in paddr and psize
252 * for bus dependent trigger_{in,out}put to load into the DMA controller.
255 cs4231_transfer_init(
256 struct cs4231_softc
*sc
,
257 struct cs_transfer
*t
,
260 void *start
, void *end
,
262 void (*intr
)(void *),
269 printf("%s: %s already running\n",
270 device_xname(&sc
->sc_ad1848
.sc_dev
), t
->t_name
);
277 for (p
= sc
->sc_dmas
; p
!= NULL
&& p
->addr
!= start
; p
= p
->next
)
280 printf("%s: bad %s addr %p\n",
281 device_xname(&sc
->sc_ad1848
.sc_dev
), t
->t_name
, start
);
285 n
= (char *)end
- (char *)start
;
287 t
->t_dma
= p
; /* the DMA memory segment */
288 t
->t_segsz
= n
; /* size of DMA segment */
289 t
->t_blksz
= blksize
; /* do transfers in blksize chunks */
296 /* for caller to load into DMA controller */
297 *paddr
= t
->t_dma
->dmamap
->dm_segs
[0].ds_addr
;
300 DPRINTF(("%s: init %s: [%p..%p] %lu bytes %lu blocks;"
301 " DMA at 0x%lx count %lu\n",
302 device_xname(&sc
->sc_ad1848
.sc_dev
), t
->t_name
,
303 start
, end
, (u_long
)t
->t_segsz
, (u_long
)t
->t_blksz
,
304 (u_long
)*paddr
, (u_long
)*psize
));
311 * Compute next DMA address/counter, update transfer status.
314 cs4231_transfer_advance(struct cs_transfer
*t
, bus_addr_t
*paddr
,
317 bus_addr_t dmabase
, nextaddr
;
320 dmabase
= t
->t_dma
->dmamap
->dm_segs
[0].ds_addr
;
322 togo
= t
->t_segsz
- t
->t_cnt
;
323 if (togo
== 0) { /* roll over */
325 t
->t_cnt
= togo
= t
->t_blksz
;
327 nextaddr
= dmabase
+ t
->t_cnt
;
328 if (togo
> t
->t_blksz
)
333 /* for caller to load into DMA controller */
340 cs4231_open(void *addr
, int flags
)
342 struct cs4231_softc
*sc
;
345 DPRINTF(("sa_open: unit %p\n", sc
));
347 sc
->sc_playback
.t_active
= 0;
348 sc
->sc_playback
.t_intr
= NULL
;
349 sc
->sc_playback
.t_arg
= NULL
;
351 sc
->sc_capture
.t_active
= 0;
352 sc
->sc_capture
.t_intr
= NULL
;
353 sc
->sc_capture
.t_arg
= NULL
;
355 /* no interrupts from ad1848 */
356 ad_write(&sc
->sc_ad1848
, SP_PIN_CONTROL
, 0);
357 ad1848_reset(&sc
->sc_ad1848
);
359 DPRINTF(("sa_open: ok -> sc=%p\n", sc
));
364 cs4231_close(void *addr
)
367 DPRINTF(("sa_close: sc=%p\n", addr
));
369 /* audio(9) already called halt methods */
371 DPRINTF(("sa_close: closed.\n"));
375 cs4231_getdev(void *addr
, struct audio_device
*retp
)
378 *retp
= cs4231_device
;
382 static const ad1848_devmap_t csmapping
[] = {
383 { CSAUDIO_DAC_LVL
, AD1848_KIND_LVL
, AD1848_AUX1_CHANNEL
},
384 { CSAUDIO_LINE_IN_LVL
, AD1848_KIND_LVL
, AD1848_LINE_CHANNEL
},
385 { CSAUDIO_MONO_LVL
, AD1848_KIND_LVL
, AD1848_MONO_CHANNEL
},
386 { CSAUDIO_CD_LVL
, AD1848_KIND_LVL
, AD1848_AUX2_CHANNEL
},
387 { CSAUDIO_OUTPUT_LVL
, AD1848_KIND_LVL
, AD1848_MONITOR_CHANNEL
},
388 { CSAUDIO_OUT_LVL
, AD1848_KIND_LVL
, AD1848_DAC_CHANNEL
},
389 { CSAUDIO_DAC_MUTE
, AD1848_KIND_MUTE
, AD1848_AUX1_CHANNEL
},
390 { CSAUDIO_LINE_IN_MUTE
, AD1848_KIND_MUTE
, AD1848_LINE_CHANNEL
},
391 { CSAUDIO_MONO_MUTE
, AD1848_KIND_MUTE
, AD1848_MONO_CHANNEL
},
392 { CSAUDIO_CD_MUTE
, AD1848_KIND_MUTE
, AD1848_AUX2_CHANNEL
},
393 { CSAUDIO_OUTPUT_MUTE
, AD1848_KIND_MUTE
, AD1848_MONITOR_CHANNEL
},
394 { CSAUDIO_OUT_MUTE
, AD1848_KIND_MUTE
, AD1848_OUT_CHANNEL
},
395 { CSAUDIO_REC_LVL
, AD1848_KIND_RECORDGAIN
, -1 },
396 { CSAUDIO_RECORD_SOURCE
, AD1848_KIND_RECORDSOURCE
, -1 }
399 static int nummap
= sizeof(csmapping
) / sizeof(csmapping
[0]);
403 cs4231_set_port(void *addr
, mixer_ctrl_t
*cp
)
405 struct ad1848_softc
*ac
;
407 DPRINTF(("cs4231_set_port: port=%d", cp
->dev
));
409 return ad1848_mixer_set_port(ac
, csmapping
, nummap
, cp
);
413 cs4231_get_port(void *addr
, mixer_ctrl_t
*cp
)
415 struct ad1848_softc
*ac
;
417 DPRINTF(("cs4231_get_port: port=%d", cp
->dev
));
419 return ad1848_mixer_get_port(ac
, csmapping
, nummap
, cp
);
423 cs4231_get_props(void *addr
)
426 return AUDIO_PROP_FULLDUPLEX
;
430 cs4231_query_devinfo(void *addr
, mixer_devinfo_t
*dip
)
435 case CSAUDIO_DAC_LVL
: /* dacout */
436 dip
->type
= AUDIO_MIXER_VALUE
;
437 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
438 dip
->prev
= AUDIO_MIXER_LAST
;
439 dip
->next
= CSAUDIO_DAC_MUTE
;
440 strcpy(dip
->label
.name
, AudioNdac
);
441 dip
->un
.v
.num_channels
= 2;
442 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
445 case CSAUDIO_LINE_IN_LVL
: /* line */
446 dip
->type
= AUDIO_MIXER_VALUE
;
447 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
448 dip
->prev
= AUDIO_MIXER_LAST
;
449 dip
->next
= CSAUDIO_LINE_IN_MUTE
;
450 strcpy(dip
->label
.name
, AudioNline
);
451 dip
->un
.v
.num_channels
= 2;
452 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
455 case CSAUDIO_MONO_LVL
: /* mono/microphone mixer */
456 dip
->type
= AUDIO_MIXER_VALUE
;
457 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
458 dip
->prev
= AUDIO_MIXER_LAST
;
459 dip
->next
= CSAUDIO_MONO_MUTE
;
460 strcpy(dip
->label
.name
, AudioNmicrophone
);
461 dip
->un
.v
.num_channels
= 1;
462 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
465 case CSAUDIO_CD_LVL
: /* cd */
466 dip
->type
= AUDIO_MIXER_VALUE
;
467 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
468 dip
->prev
= AUDIO_MIXER_LAST
;
469 dip
->next
= CSAUDIO_CD_MUTE
;
470 strcpy(dip
->label
.name
, AudioNcd
);
471 dip
->un
.v
.num_channels
= 2;
472 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
476 case CSAUDIO_OUTPUT_LVL
: /* monitor level */
477 dip
->type
= AUDIO_MIXER_VALUE
;
478 dip
->mixer_class
= CSAUDIO_MONITOR_CLASS
;
479 dip
->next
= CSAUDIO_OUTPUT_MUTE
;
480 dip
->prev
= AUDIO_MIXER_LAST
;
481 strcpy(dip
->label
.name
, AudioNmonitor
);
482 dip
->un
.v
.num_channels
= 1;
483 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
486 case CSAUDIO_OUT_LVL
: /* cs4231 output volume */
487 dip
->type
= AUDIO_MIXER_VALUE
;
488 dip
->mixer_class
= CSAUDIO_MONITOR_CLASS
;
489 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
490 strcpy(dip
->label
.name
, AudioNmaster
);
491 dip
->un
.v
.num_channels
= 2;
492 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
495 case CSAUDIO_OUT_MUTE
: /* mute built-in speaker */
496 dip
->mixer_class
= CSAUDIO_MONITOR_CLASS
;
497 dip
->type
= AUDIO_MIXER_ENUM
;
498 dip
->prev
= CSAUDIO_MONITOR_CLASS
;
499 dip
->next
= AUDIO_MIXER_LAST
;
500 strcpy(dip
->label
.name
, AudioNmono
);
501 /* names reversed, this is a "mute" value used as "mono enabled" */
502 dip
->un
.e
.num_mem
= 2;
503 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNon
);
504 dip
->un
.e
.member
[0].ord
= 0;
505 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNoff
);
506 dip
->un
.e
.member
[1].ord
= 1;
509 case CSAUDIO_LINE_IN_MUTE
:
510 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
511 dip
->type
= AUDIO_MIXER_ENUM
;
512 dip
->prev
= CSAUDIO_LINE_IN_LVL
;
513 dip
->next
= AUDIO_MIXER_LAST
;
516 case CSAUDIO_DAC_MUTE
:
517 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
518 dip
->type
= AUDIO_MIXER_ENUM
;
519 dip
->prev
= CSAUDIO_DAC_LVL
;
520 dip
->next
= AUDIO_MIXER_LAST
;
523 case CSAUDIO_CD_MUTE
:
524 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
525 dip
->type
= AUDIO_MIXER_ENUM
;
526 dip
->prev
= CSAUDIO_CD_LVL
;
527 dip
->next
= AUDIO_MIXER_LAST
;
530 case CSAUDIO_MONO_MUTE
:
531 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
532 dip
->type
= AUDIO_MIXER_ENUM
;
533 dip
->prev
= CSAUDIO_MONO_LVL
;
534 dip
->next
= AUDIO_MIXER_LAST
;
537 case CSAUDIO_OUTPUT_MUTE
:
538 dip
->mixer_class
= CSAUDIO_MONITOR_CLASS
;
539 dip
->type
= AUDIO_MIXER_ENUM
;
540 dip
->prev
= CSAUDIO_OUTPUT_LVL
;
541 dip
->next
= AUDIO_MIXER_LAST
;
543 strcpy(dip
->label
.name
, AudioNmute
);
544 dip
->un
.e
.num_mem
= 2;
545 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNoff
);
546 dip
->un
.e
.member
[0].ord
= 0;
547 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNon
);
548 dip
->un
.e
.member
[1].ord
= 1;
551 case CSAUDIO_REC_LVL
: /* record level */
552 dip
->type
= AUDIO_MIXER_VALUE
;
553 dip
->mixer_class
= CSAUDIO_RECORD_CLASS
;
554 dip
->prev
= AUDIO_MIXER_LAST
;
555 dip
->next
= CSAUDIO_RECORD_SOURCE
;
556 strcpy(dip
->label
.name
, AudioNrecord
);
557 dip
->un
.v
.num_channels
= 2;
558 strcpy(dip
->un
.v
.units
.name
, AudioNvolume
);
561 case CSAUDIO_RECORD_SOURCE
:
562 dip
->mixer_class
= CSAUDIO_RECORD_CLASS
;
563 dip
->type
= AUDIO_MIXER_ENUM
;
564 dip
->prev
= CSAUDIO_REC_LVL
;
565 dip
->next
= AUDIO_MIXER_LAST
;
566 strcpy(dip
->label
.name
, AudioNsource
);
567 dip
->un
.e
.num_mem
= 4;
568 strcpy(dip
->un
.e
.member
[0].label
.name
, AudioNoutput
);
569 dip
->un
.e
.member
[0].ord
= DAC_IN_PORT
;
570 strcpy(dip
->un
.e
.member
[1].label
.name
, AudioNmicrophone
);
571 dip
->un
.e
.member
[1].ord
= MIC_IN_PORT
;
572 strcpy(dip
->un
.e
.member
[2].label
.name
, AudioNdac
);
573 dip
->un
.e
.member
[2].ord
= AUX1_IN_PORT
;
574 strcpy(dip
->un
.e
.member
[3].label
.name
, AudioNline
);
575 dip
->un
.e
.member
[3].ord
= LINE_IN_PORT
;
578 case CSAUDIO_INPUT_CLASS
: /* input class descriptor */
579 dip
->type
= AUDIO_MIXER_CLASS
;
580 dip
->mixer_class
= CSAUDIO_INPUT_CLASS
;
581 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
582 strcpy(dip
->label
.name
, AudioCinputs
);
585 case CSAUDIO_MONITOR_CLASS
: /* output class descriptor */
586 dip
->type
= AUDIO_MIXER_CLASS
;
587 dip
->mixer_class
= CSAUDIO_MONITOR_CLASS
;
588 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
589 strcpy(dip
->label
.name
, AudioCmonitor
);
592 case CSAUDIO_RECORD_CLASS
: /* record source class */
593 dip
->type
= AUDIO_MIXER_CLASS
;
594 dip
->mixer_class
= CSAUDIO_RECORD_CLASS
;
595 dip
->next
= dip
->prev
= AUDIO_MIXER_LAST
;
596 strcpy(dip
->label
.name
, AudioCrecord
);
603 DPRINTF(("AUDIO_MIXER_DEVINFO: name=%s\n", dip
->label
.name
));
608 #endif /* NAUDIO > 0 */