1*32ffafadSjsg /* $OpenBSD: harmony.c,v 1.41 2024/05/22 14:25:47 jsg Exp $ */
242aaa9baSjason
342aaa9baSjason /*
442aaa9baSjason * Copyright (c) 2003 Jason L. Wright (jason@thought.net)
542aaa9baSjason * All rights reserved.
642aaa9baSjason *
742aaa9baSjason * Redistribution and use in source and binary forms, with or without
842aaa9baSjason * modification, are permitted provided that the following conditions
942aaa9baSjason * are met:
1042aaa9baSjason * 1. Redistributions of source code must retain the above copyright
1142aaa9baSjason * notice, this list of conditions and the following disclaimer.
1242aaa9baSjason * 2. Redistributions in binary form must reproduce the above copyright
1342aaa9baSjason * notice, this list of conditions and the following disclaimer in the
1442aaa9baSjason * documentation and/or other materials provided with the distribution.
1542aaa9baSjason *
1642aaa9baSjason * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1742aaa9baSjason * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
1842aaa9baSjason * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
1942aaa9baSjason * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
2042aaa9baSjason * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2142aaa9baSjason * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2242aaa9baSjason * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2342aaa9baSjason * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
2442aaa9baSjason * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
2542aaa9baSjason * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2642aaa9baSjason * POSSIBILITY OF SUCH DAMAGE.
2742aaa9baSjason */
2842aaa9baSjason
29b88c9c3fSjason /*
30b88c9c3fSjason * Harmony (CS4215/AD1849 LASI) audio interface.
31b88c9c3fSjason */
32b88c9c3fSjason
3342aaa9baSjason #include <sys/param.h>
347a877ae9Sjason #include <sys/kernel.h>
3542aaa9baSjason #include <sys/systm.h>
3642aaa9baSjason #include <sys/errno.h>
3742aaa9baSjason #include <sys/ioctl.h>
3842aaa9baSjason #include <sys/device.h>
3942aaa9baSjason #include <sys/proc.h>
4042aaa9baSjason #include <sys/malloc.h>
4142aaa9baSjason
4242aaa9baSjason #include <sys/audioio.h>
4342aaa9baSjason #include <dev/audio_if.h>
4442aaa9baSjason
4542aaa9baSjason #include <machine/cpu.h>
4642aaa9baSjason #include <machine/intr.h>
4742aaa9baSjason #include <machine/iomod.h>
4842aaa9baSjason #include <machine/autoconf.h>
4942aaa9baSjason #include <machine/bus.h>
5042aaa9baSjason
5142aaa9baSjason #include <hppa/dev/cpudevs.h>
5242aaa9baSjason #include <hppa/gsc/gscbusvar.h>
53b88c9c3fSjason #include <hppa/gsc/harmonyreg.h>
54591687a2Sjason #include <hppa/gsc/harmonyvar.h>
557282f66cSjason
5642aaa9baSjason int harmony_open(void *, int);
5742aaa9baSjason void harmony_close(void *);
5842aaa9baSjason int harmony_set_params(void *, int, int, struct audio_params *,
5942aaa9baSjason struct audio_params *);
6042aaa9baSjason int harmony_round_blocksize(void *, int);
6142aaa9baSjason int harmony_commit_settings(void *);
6242aaa9baSjason int harmony_halt_output(void *);
6342aaa9baSjason int harmony_halt_input(void *);
6442aaa9baSjason int harmony_set_port(void *, mixer_ctrl_t *);
6542aaa9baSjason int harmony_get_port(void *, mixer_ctrl_t *);
6642aaa9baSjason int harmony_query_devinfo(void *addr, mixer_devinfo_t *);
67673bd31fSjason void * harmony_allocm(void *, int, size_t, int, int);
68673bd31fSjason void harmony_freem(void *, void *, int);
6942aaa9baSjason size_t harmony_round_buffersize(void *, int, size_t);
7042aaa9baSjason int harmony_trigger_output(void *, void *, void *, int,
717a877ae9Sjason void (*intr)(void *), void *, struct audio_params *);
7242aaa9baSjason int harmony_trigger_input(void *, void *, void *, int,
737a877ae9Sjason void (*intr)(void *), void *, struct audio_params *);
7442aaa9baSjason
750d6a2fdeSmiod const struct audio_hw_if harmony_sa_hw_if = {
765a8d990aSkn .open = harmony_open,
775a8d990aSkn .close = harmony_close,
785a8d990aSkn .set_params = harmony_set_params,
795a8d990aSkn .round_blocksize = harmony_round_blocksize,
805a8d990aSkn .commit_settings = harmony_commit_settings,
815a8d990aSkn .halt_output = harmony_halt_output,
825a8d990aSkn .halt_input = harmony_halt_input,
835a8d990aSkn .set_port = harmony_set_port,
845a8d990aSkn .get_port = harmony_get_port,
855a8d990aSkn .query_devinfo = harmony_query_devinfo,
865a8d990aSkn .allocm = harmony_allocm,
875a8d990aSkn .freem = harmony_freem,
885a8d990aSkn .round_buffersize = harmony_round_buffersize,
895a8d990aSkn .trigger_output = harmony_trigger_output,
905a8d990aSkn .trigger_input = harmony_trigger_input,
9142aaa9baSjason };
9242aaa9baSjason
9342aaa9baSjason int harmony_match(struct device *, void *, void *);
9442aaa9baSjason void harmony_attach(struct device *, struct device *, void *);
9542aaa9baSjason int harmony_intr(void *);
9642aaa9baSjason void harmony_intr_enable(struct harmony_softc *);
9742aaa9baSjason void harmony_intr_disable(struct harmony_softc *);
98673bd31fSjason u_int32_t harmony_speed_bits(struct harmony_softc *, u_long *);
99fe6bde3cSjason int harmony_set_gainctl(struct harmony_softc *);
1007282f66cSjason void harmony_reset_codec(struct harmony_softc *);
101167728ffSjason void harmony_start_cp(struct harmony_softc *);
1027a877ae9Sjason void harmony_try_more(struct harmony_softc *);
10342aaa9baSjason
1047180d4afSmickey void harmony_acc_tmo(void *);
1057180d4afSmickey #define ADD_CLKALLICA(sc) do { \
1067180d4afSmickey (sc)->sc_acc <<= 1; \
1077180d4afSmickey (sc)->sc_acc |= READ_REG((sc), HARMONY_DIAG) & DIAG_CO; \
1087180d4afSmickey if ((sc)->sc_acc_cnt++ && !((sc)->sc_acc_cnt % 32)) \
1099e9abf5bSjasper enqueue_randomness((sc)->sc_acc_num ^= (sc)->sc_acc); \
1107180d4afSmickey } while(0)
1117180d4afSmickey
11242aaa9baSjason int
harmony_match(parent,match,aux)11342aaa9baSjason harmony_match(parent, match, aux)
11442aaa9baSjason struct device *parent;
11542aaa9baSjason void *match, *aux;
11642aaa9baSjason {
11742aaa9baSjason struct gsc_attach_args *ga = aux;
11814fe4800Smickey bus_space_handle_t bh;
11914fe4800Smickey u_int32_t cntl;
12042aaa9baSjason
12142aaa9baSjason if (ga->ga_type.iodc_type == HPPA_TYPE_FIO) {
12242aaa9baSjason if (ga->ga_type.iodc_sv_model == HPPA_FIO_A1 ||
12342aaa9baSjason ga->ga_type.iodc_sv_model == HPPA_FIO_A2NB ||
12442aaa9baSjason ga->ga_type.iodc_sv_model == HPPA_FIO_A1NB ||
12514fe4800Smickey ga->ga_type.iodc_sv_model == HPPA_FIO_A2) {
12614fe4800Smickey if (bus_space_map(ga->ga_iot, ga->ga_hpa,
12714fe4800Smickey HARMONY_NREGS, 0, &bh) != 0)
12814fe4800Smickey return (0);
12914fe4800Smickey cntl = bus_space_read_4(ga->ga_iot, bh, HARMONY_ID) &
13014fe4800Smickey ID_REV_MASK;
13114fe4800Smickey bus_space_unmap(ga->ga_iot, bh, HARMONY_NREGS);
13214fe4800Smickey if (cntl == ID_REV_TS || cntl == ID_REV_NOTS)
13342aaa9baSjason return (1);
13442aaa9baSjason }
13514fe4800Smickey }
13642aaa9baSjason return (0);
13742aaa9baSjason }
13842aaa9baSjason
13942aaa9baSjason void
harmony_attach(parent,self,aux)14042aaa9baSjason harmony_attach(parent, self, aux)
14142aaa9baSjason struct device *parent, *self;
14242aaa9baSjason void *aux;
14342aaa9baSjason {
14442aaa9baSjason struct harmony_softc *sc = (struct harmony_softc *)self;
14542aaa9baSjason struct gsc_attach_args *ga = aux;
1464de9670dSjason u_int8_t rev;
147f182043cSmickey u_int32_t cntl;
14842aaa9baSjason int i;
14942aaa9baSjason
15042aaa9baSjason sc->sc_bt = ga->ga_iot;
15142aaa9baSjason sc->sc_dmat = ga->ga_dmatag;
15242aaa9baSjason
15342aaa9baSjason if (bus_space_map(sc->sc_bt, ga->ga_hpa, HARMONY_NREGS, 0,
15442aaa9baSjason &sc->sc_bh) != 0) {
15542aaa9baSjason printf(": couldn't map registers\n");
15642aaa9baSjason return;
15742aaa9baSjason }
15842aaa9baSjason
15927b54b8bSmickey cntl = READ_REG(sc, HARMONY_ID);
16014fe4800Smickey sc->sc_teleshare = (cntl & ID_REV_MASK) == ID_REV_TS;
16127b54b8bSmickey
16242aaa9baSjason if (bus_dmamem_alloc(sc->sc_dmat, sizeof(struct harmony_empty),
16342aaa9baSjason PAGE_SIZE, 0, &sc->sc_empty_seg, 1, &sc->sc_empty_rseg,
16442aaa9baSjason BUS_DMA_NOWAIT) != 0) {
16527b54b8bSmickey printf(": couldn't alloc DMA memory\n");
16627b54b8bSmickey bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
16742aaa9baSjason return;
16842aaa9baSjason }
16942aaa9baSjason if (bus_dmamem_map(sc->sc_dmat, &sc->sc_empty_seg, 1,
17042aaa9baSjason sizeof(struct harmony_empty), (caddr_t *)&sc->sc_empty_kva,
17142aaa9baSjason BUS_DMA_NOWAIT) != 0) {
17227b54b8bSmickey printf(": couldn't map DMA memory\n");
17342aaa9baSjason bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
17442aaa9baSjason sc->sc_empty_rseg);
17527b54b8bSmickey bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
17642aaa9baSjason return;
17742aaa9baSjason }
17842aaa9baSjason if (bus_dmamap_create(sc->sc_dmat, sizeof(struct harmony_empty), 1,
17942aaa9baSjason sizeof(struct harmony_empty), 0, BUS_DMA_NOWAIT,
18042aaa9baSjason &sc->sc_empty_map) != 0) {
18127b54b8bSmickey printf(": can't create DMA map\n");
18242aaa9baSjason bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
18342aaa9baSjason sizeof(struct harmony_empty));
18442aaa9baSjason bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
18542aaa9baSjason sc->sc_empty_rseg);
18627b54b8bSmickey bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
18742aaa9baSjason return;
18842aaa9baSjason }
18942aaa9baSjason if (bus_dmamap_load(sc->sc_dmat, sc->sc_empty_map, sc->sc_empty_kva,
19042aaa9baSjason sizeof(struct harmony_empty), NULL, BUS_DMA_NOWAIT) != 0) {
19127b54b8bSmickey printf(": can't load DMA map\n");
19242aaa9baSjason bus_dmamap_destroy(sc->sc_dmat, sc->sc_empty_map);
19342aaa9baSjason bus_dmamem_unmap(sc->sc_dmat, (caddr_t)sc->sc_empty_kva,
19442aaa9baSjason sizeof(struct harmony_empty));
19542aaa9baSjason bus_dmamem_free(sc->sc_dmat, &sc->sc_empty_seg,
19642aaa9baSjason sc->sc_empty_rseg);
19727b54b8bSmickey bus_space_unmap(sc->sc_bt, sc->sc_bh, HARMONY_NREGS);
19842aaa9baSjason return;
19942aaa9baSjason }
20042aaa9baSjason
20142aaa9baSjason sc->sc_playback_empty = 0;
20242aaa9baSjason for (i = 0; i < PLAYBACK_EMPTYS; i++)
20342aaa9baSjason sc->sc_playback_paddrs[i] =
20442aaa9baSjason sc->sc_empty_map->dm_segs[0].ds_addr +
20542aaa9baSjason offsetof(struct harmony_empty, playback[i][0]);
20642aaa9baSjason
20742aaa9baSjason sc->sc_capture_empty = 0;
20842aaa9baSjason for (i = 0; i < CAPTURE_EMPTYS; i++)
20942aaa9baSjason sc->sc_capture_paddrs[i] =
21042aaa9baSjason sc->sc_empty_map->dm_segs[0].ds_addr +
21142aaa9baSjason offsetof(struct harmony_empty, playback[i][0]);
21242aaa9baSjason
21342aaa9baSjason bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
21442aaa9baSjason offsetof(struct harmony_empty, playback[0][0]),
21542aaa9baSjason PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);
21642aaa9baSjason
2177c4844bbSmickey (void)gsc_intr_establish((struct gsc_softc *)parent, ga->ga_irq,
2187c4844bbSmickey IPL_AUDIO, harmony_intr, sc, sc->sc_dv.dv_xname);
21942aaa9baSjason
2207282f66cSjason /* set defaults */
2217282f66cSjason sc->sc_in_port = HARMONY_IN_LINE;
222591687a2Sjason sc->sc_out_port = HARMONY_OUT_SPEAKER;
223b88c9c3fSjason sc->sc_input_lvl.left = sc->sc_input_lvl.right = 240;
224b88c9c3fSjason sc->sc_output_lvl.left = sc->sc_output_lvl.right = 244;
225b88c9c3fSjason sc->sc_monitor_lvl.left = sc->sc_monitor_lvl.right = 208;
2267c84e6fbSjason sc->sc_outputgain = 0;
22742aaa9baSjason
2287282f66cSjason /* reset chip, and push default gain controls */
2297282f66cSjason harmony_reset_codec(sc);
230532b98aeSjason
231f182043cSmickey cntl = READ_REG(sc, HARMONY_CNTL);
2324de9670dSjason rev = (cntl & CNTL_CODEC_REV_MASK) >> CNTL_CODEC_REV_SHIFT;
23327b54b8bSmickey printf(": rev %u", rev);
23427b54b8bSmickey
23527b54b8bSmickey if (sc->sc_teleshare)
23627b54b8bSmickey printf(", teleshare");
23727b54b8bSmickey printf("\n");
2384de9670dSjason
2394de9670dSjason if ((rev & CS4215_REV_VER) >= CS4215_REV_VER_E)
2404de9670dSjason sc->sc_hasulinear8 = 1;
24142aaa9baSjason
2422baa08e2Santon audio_attach_mi(&harmony_sa_hw_if, sc, NULL, &sc->sc_dv);
2437180d4afSmickey
2447180d4afSmickey timeout_set(&sc->sc_acc_tmo, harmony_acc_tmo, sc);
2457180d4afSmickey sc->sc_acc_num = 0xa5a5a5a5;
24642aaa9baSjason }
24742aaa9baSjason
2487282f66cSjason void
harmony_reset_codec(struct harmony_softc * sc)2497282f66cSjason harmony_reset_codec(struct harmony_softc *sc)
2507282f66cSjason {
2517282f66cSjason /* silence */
2527282f66cSjason WRITE_REG(sc, HARMONY_GAINCTL, GAINCTL_OUTPUT_LEFT_M |
2537282f66cSjason GAINCTL_OUTPUT_RIGHT_M | GAINCTL_MONITOR_M);
2547282f66cSjason
2557282f66cSjason /* start reset */
2567282f66cSjason WRITE_REG(sc, HARMONY_RESET, RESET_RST);
2577282f66cSjason
2587282f66cSjason DELAY(100000); /* wait at least 0.05 sec */
2597282f66cSjason
2607282f66cSjason harmony_set_gainctl(sc);
2617282f66cSjason WRITE_REG(sc, HARMONY_RESET, 0);
2627282f66cSjason }
2637282f66cSjason
2647180d4afSmickey void
harmony_acc_tmo(void * v)2657180d4afSmickey harmony_acc_tmo(void *v)
2667180d4afSmickey {
2677180d4afSmickey struct harmony_softc *sc = v;
2687180d4afSmickey
2697180d4afSmickey ADD_CLKALLICA(sc);
2707180d4afSmickey timeout_add(&sc->sc_acc_tmo, 1);
2717180d4afSmickey }
2727180d4afSmickey
27342aaa9baSjason /*
27442aaa9baSjason * interrupt handler
27542aaa9baSjason */
27642aaa9baSjason int
harmony_intr(vsc)27742aaa9baSjason harmony_intr(vsc)
27842aaa9baSjason void *vsc;
27942aaa9baSjason {
28042aaa9baSjason struct harmony_softc *sc = vsc;
281167728ffSjason struct harmony_channel *c;
28242aaa9baSjason u_int32_t dstatus;
283673bd31fSjason int r = 0;
284673bd31fSjason
285886882aaSratchov mtx_enter(&audio_lock);
2867180d4afSmickey ADD_CLKALLICA(sc);
2877180d4afSmickey
28842aaa9baSjason harmony_intr_disable(sc);
289532b98aeSjason
2907282f66cSjason dstatus = READ_REG(sc, HARMONY_DSTATUS);
29142aaa9baSjason
292b88c9c3fSjason if (dstatus & DSTATUS_PN) {
2937a877ae9Sjason struct harmony_dma *d;
2947a877ae9Sjason bus_addr_t nextaddr;
2957a877ae9Sjason bus_size_t togo;
2967a877ae9Sjason
297673bd31fSjason r = 1;
2987a877ae9Sjason c = &sc->sc_playback;
2997a877ae9Sjason d = c->c_current;
3007a877ae9Sjason togo = c->c_segsz - c->c_cnt;
3017a877ae9Sjason if (togo == 0) {
3027a877ae9Sjason nextaddr = d->d_map->dm_segs[0].ds_addr;
3037a877ae9Sjason c->c_cnt = togo = c->c_blksz;
3047a877ae9Sjason } else {
3057a877ae9Sjason nextaddr = c->c_lastaddr;
3067a877ae9Sjason if (togo > c->c_blksz)
3077a877ae9Sjason togo = c->c_blksz;
3087a877ae9Sjason c->c_cnt += togo;
3097a877ae9Sjason }
3107a877ae9Sjason
3117a877ae9Sjason bus_dmamap_sync(sc->sc_dmat, d->d_map,
3127a877ae9Sjason nextaddr - d->d_map->dm_segs[0].ds_addr,
3137a877ae9Sjason c->c_blksz, BUS_DMASYNC_PREWRITE);
3147a877ae9Sjason
3157a877ae9Sjason WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
3167a877ae9Sjason SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
3177a877ae9Sjason c->c_lastaddr = nextaddr + togo;
3187a877ae9Sjason harmony_try_more(sc);
319673bd31fSjason }
32042aaa9baSjason
321c20c6401Smickey dstatus = READ_REG(sc, HARMONY_DSTATUS);
322c20c6401Smickey
323b88c9c3fSjason if (dstatus & DSTATUS_RN) {
324167728ffSjason c = &sc->sc_capture;
325673bd31fSjason r = 1;
326167728ffSjason harmony_start_cp(sc);
327167728ffSjason if (sc->sc_capturing && c->c_intr != NULL)
328b88c9c3fSjason (*c->c_intr)(c->c_intrarg);
329b88c9c3fSjason }
33042aaa9baSjason
33127b54b8bSmickey if (READ_REG(sc, HARMONY_OV) & OV_OV) {
33227b54b8bSmickey sc->sc_ov = 1;
33327b54b8bSmickey WRITE_REG(sc, HARMONY_OV, 0);
33427b54b8bSmickey } else
33527b54b8bSmickey sc->sc_ov = 0;
33627b54b8bSmickey
33742aaa9baSjason harmony_intr_enable(sc);
338886882aaSratchov mtx_leave(&audio_lock);
339673bd31fSjason return (r);
34042aaa9baSjason }
34142aaa9baSjason
34242aaa9baSjason void
harmony_intr_enable(struct harmony_softc * sc)34342aaa9baSjason harmony_intr_enable(struct harmony_softc *sc)
34442aaa9baSjason {
3457282f66cSjason WRITE_REG(sc, HARMONY_DSTATUS, DSTATUS_IE);
3467282f66cSjason SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
34742aaa9baSjason }
34842aaa9baSjason
34942aaa9baSjason void
harmony_intr_disable(struct harmony_softc * sc)35042aaa9baSjason harmony_intr_disable(struct harmony_softc *sc)
35142aaa9baSjason {
3527282f66cSjason WRITE_REG(sc, HARMONY_DSTATUS, 0);
3537282f66cSjason SYNC_REG(sc, HARMONY_DSTATUS, BUS_SPACE_BARRIER_WRITE);
35442aaa9baSjason }
35542aaa9baSjason
35642aaa9baSjason int
harmony_open(void * vsc,int flags)35742aaa9baSjason harmony_open(void *vsc, int flags)
35842aaa9baSjason {
35942aaa9baSjason struct harmony_softc *sc = vsc;
36042aaa9baSjason
36142aaa9baSjason if (sc->sc_open)
36242aaa9baSjason return (EBUSY);
36342aaa9baSjason sc->sc_open = 1;
36442aaa9baSjason return (0);
36542aaa9baSjason }
36642aaa9baSjason
36742aaa9baSjason void
harmony_close(void * vsc)36842aaa9baSjason harmony_close(void *vsc)
36942aaa9baSjason {
37042aaa9baSjason struct harmony_softc *sc = vsc;
37142aaa9baSjason
372886882aaSratchov /* XXX: not useful, halt_*() already called */
37342aaa9baSjason harmony_halt_input(sc);
37442aaa9baSjason harmony_halt_output(sc);
3757282f66cSjason harmony_intr_disable(sc);
37642aaa9baSjason sc->sc_open = 0;
37742aaa9baSjason }
37842aaa9baSjason
37942aaa9baSjason int
harmony_set_params(void * vsc,int setmode,int usemode,struct audio_params * p,struct audio_params * r)38042aaa9baSjason harmony_set_params(void *vsc, int setmode, int usemode,
38142aaa9baSjason struct audio_params *p, struct audio_params *r)
38242aaa9baSjason {
38342aaa9baSjason struct harmony_softc *sc = vsc;
38442aaa9baSjason u_int32_t bits;
38542aaa9baSjason
38642aaa9baSjason switch (p->encoding) {
38742aaa9baSjason case AUDIO_ENCODING_ULAW:
38842aaa9baSjason bits = CNTL_FORMAT_ULAW;
389ac2bb4f3Sratchov p->precision = 8;
39042aaa9baSjason break;
39142aaa9baSjason case AUDIO_ENCODING_ALAW:
39242aaa9baSjason bits = CNTL_FORMAT_ALAW;
393ac2bb4f3Sratchov p->precision = 8;
39442aaa9baSjason break;
39542aaa9baSjason case AUDIO_ENCODING_SLINEAR_BE:
3964de9670dSjason if (p->precision == 16) {
39742aaa9baSjason bits = CNTL_FORMAT_SLINEAR16BE;
39842aaa9baSjason break;
3994de9670dSjason }
40042aaa9baSjason return (EINVAL);
4014de9670dSjason case AUDIO_ENCODING_ULINEAR_LE:
402ac2bb4f3Sratchov case AUDIO_ENCODING_ULINEAR_BE:
4034de9670dSjason if (p->precision == 8) {
4044de9670dSjason bits = CNTL_FORMAT_ULINEAR8;
4054de9670dSjason break;
4064de9670dSjason }
4074de9670dSjason return (EINVAL);
40842aaa9baSjason default:
40942aaa9baSjason return (EINVAL);
41042aaa9baSjason }
41142aaa9baSjason
4127c84e6fbSjason if (sc->sc_outputgain)
4137c84e6fbSjason bits |= CNTL_OLB;
4147c84e6fbSjason
41542aaa9baSjason if (p->channels == 1)
41642aaa9baSjason bits |= CNTL_CHANS_MONO;
41742aaa9baSjason else if (p->channels == 2)
41842aaa9baSjason bits |= CNTL_CHANS_STEREO;
41942aaa9baSjason else
42042aaa9baSjason return (EINVAL);
42142aaa9baSjason
422ac2bb4f3Sratchov r->sample_rate = p->sample_rate;
423ac2bb4f3Sratchov r->encoding = p->encoding;
424ac2bb4f3Sratchov r->precision = p->precision;
42516579317Sjakemsr p->bps = AUDIO_BPS(p->precision);
42616579317Sjakemsr r->bps = AUDIO_BPS(r->precision);
42716579317Sjakemsr p->msb = r->msb = 1;
42816579317Sjakemsr
42916579317Sjakemsr bits |= harmony_speed_bits(sc, &p->sample_rate);
43042aaa9baSjason sc->sc_cntlbits = bits;
43142aaa9baSjason sc->sc_need_commit = 1;
43242aaa9baSjason
43342aaa9baSjason return (0);
43442aaa9baSjason }
43542aaa9baSjason
43642aaa9baSjason int
harmony_round_blocksize(void * vsc,int blk)43742aaa9baSjason harmony_round_blocksize(void *vsc, int blk)
43842aaa9baSjason {
439673bd31fSjason return (HARMONY_BUFSIZE);
44042aaa9baSjason }
44142aaa9baSjason
44242aaa9baSjason int
harmony_commit_settings(void * vsc)44342aaa9baSjason harmony_commit_settings(void *vsc)
44442aaa9baSjason {
44542aaa9baSjason struct harmony_softc *sc = vsc;
4467282f66cSjason u_int32_t reg;
44742aaa9baSjason u_int8_t quietchar;
44842aaa9baSjason int i;
44942aaa9baSjason
45042aaa9baSjason if (sc->sc_need_commit == 0)
45142aaa9baSjason return (0);
45242aaa9baSjason
4537282f66cSjason harmony_intr_disable(sc);
4547282f66cSjason
4557282f66cSjason for (;;) {
4567282f66cSjason reg = READ_REG(sc, HARMONY_DSTATUS);
4577282f66cSjason if ((reg & (DSTATUS_PC | DSTATUS_RC)) == 0)
4587282f66cSjason break;
4597282f66cSjason }
4607282f66cSjason
461fe6bde3cSjason /* Setting some bits in gainctl requires a reset */
462fe6bde3cSjason harmony_reset_codec(sc);
46342aaa9baSjason
46442aaa9baSjason /* set the silence character based on the encoding type */
46542aaa9baSjason bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
46642aaa9baSjason offsetof(struct harmony_empty, playback[0][0]),
46742aaa9baSjason PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_POSTWRITE);
46842aaa9baSjason switch (sc->sc_cntlbits & CNTL_FORMAT_MASK) {
46942aaa9baSjason case CNTL_FORMAT_ULAW:
47042aaa9baSjason quietchar = 0x7f;
47142aaa9baSjason break;
47242aaa9baSjason case CNTL_FORMAT_ALAW:
47342aaa9baSjason quietchar = 0x55;
47442aaa9baSjason break;
47542aaa9baSjason case CNTL_FORMAT_SLINEAR16BE:
4764de9670dSjason case CNTL_FORMAT_ULINEAR8:
47742aaa9baSjason default:
47842aaa9baSjason quietchar = 0;
47942aaa9baSjason break;
48042aaa9baSjason }
48142aaa9baSjason for (i = 0; i < PLAYBACK_EMPTYS; i++)
48242aaa9baSjason memset(&sc->sc_empty_kva->playback[i][0],
48342aaa9baSjason quietchar, HARMONY_BUFSIZE);
48442aaa9baSjason bus_dmamap_sync(sc->sc_dmat, sc->sc_empty_map,
48542aaa9baSjason offsetof(struct harmony_empty, playback[0][0]),
48642aaa9baSjason PLAYBACK_EMPTYS * HARMONY_BUFSIZE, BUS_DMASYNC_PREWRITE);
487b88c9c3fSjason
4887282f66cSjason for (;;) {
4897282f66cSjason /* Wait for it to come out of control mode */
4907282f66cSjason reg = READ_REG(sc, HARMONY_CNTL);
4917282f66cSjason if ((reg & CNTL_C) == 0)
4927282f66cSjason break;
4937282f66cSjason }
4947282f66cSjason
4957282f66cSjason bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_CNTL,
4967282f66cSjason sc->sc_cntlbits | CNTL_C);
4977282f66cSjason
4987282f66cSjason for (;;) {
4997282f66cSjason /* Wait for it to come out of control mode */
5007282f66cSjason reg = READ_REG(sc, HARMONY_CNTL);
5017282f66cSjason if ((reg & CNTL_C) == 0)
5027282f66cSjason break;
5037282f66cSjason }
5047282f66cSjason
50542aaa9baSjason sc->sc_need_commit = 0;
50642aaa9baSjason
5077282f66cSjason if (sc->sc_playing || sc->sc_capturing)
5087282f66cSjason harmony_intr_enable(sc);
5097282f66cSjason
51042aaa9baSjason return (0);
51142aaa9baSjason }
51242aaa9baSjason
51342aaa9baSjason int
harmony_halt_output(void * vsc)51442aaa9baSjason harmony_halt_output(void *vsc)
51542aaa9baSjason {
51642aaa9baSjason struct harmony_softc *sc = vsc;
51742aaa9baSjason
518886882aaSratchov /* XXX: disable interrupts */
519673bd31fSjason sc->sc_playing = 0;
52042aaa9baSjason return (0);
52142aaa9baSjason }
52242aaa9baSjason
52342aaa9baSjason int
harmony_halt_input(void * vsc)52442aaa9baSjason harmony_halt_input(void *vsc)
52542aaa9baSjason {
52642aaa9baSjason struct harmony_softc *sc = vsc;
52742aaa9baSjason
528886882aaSratchov /* XXX: disable interrupts */
529673bd31fSjason sc->sc_capturing = 0;
53042aaa9baSjason return (0);
53142aaa9baSjason }
53242aaa9baSjason
53342aaa9baSjason int
harmony_set_port(void * vsc,mixer_ctrl_t * cp)53442aaa9baSjason harmony_set_port(void *vsc, mixer_ctrl_t *cp)
53542aaa9baSjason {
53642aaa9baSjason struct harmony_softc *sc = vsc;
53742aaa9baSjason int err = EINVAL;
53842aaa9baSjason
53942aaa9baSjason switch (cp->dev) {
54042aaa9baSjason case HARMONY_PORT_INPUT_LVL:
54142aaa9baSjason if (cp->type != AUDIO_MIXER_VALUE)
54242aaa9baSjason break;
543b88c9c3fSjason if (cp->un.value.num_channels == 1)
544b88c9c3fSjason sc->sc_input_lvl.left = sc->sc_input_lvl.right =
545b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
546b88c9c3fSjason else if (cp->un.value.num_channels == 2) {
547b88c9c3fSjason sc->sc_input_lvl.left =
548b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
549b88c9c3fSjason sc->sc_input_lvl.right =
550b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
55142aaa9baSjason } else
55242aaa9baSjason break;
553b88c9c3fSjason sc->sc_need_commit = 1;
55442aaa9baSjason err = 0;
55542aaa9baSjason break;
55642aaa9baSjason case HARMONY_PORT_OUTPUT_LVL:
55742aaa9baSjason if (cp->type != AUDIO_MIXER_VALUE)
55842aaa9baSjason break;
559b88c9c3fSjason if (cp->un.value.num_channels == 1)
560b88c9c3fSjason sc->sc_output_lvl.left = sc->sc_output_lvl.right =
561b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
562b88c9c3fSjason else if (cp->un.value.num_channels == 2) {
563b88c9c3fSjason sc->sc_output_lvl.left =
564b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT];
565b88c9c3fSjason sc->sc_output_lvl.right =
566b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
56742aaa9baSjason } else
56842aaa9baSjason break;
569b88c9c3fSjason sc->sc_need_commit = 1;
57042aaa9baSjason err = 0;
57142aaa9baSjason break;
5727c84e6fbSjason case HARMONY_PORT_OUTPUT_GAIN:
5737c84e6fbSjason if (cp->type != AUDIO_MIXER_ENUM)
5747c84e6fbSjason break;
5757c84e6fbSjason sc->sc_outputgain = cp->un.ord ? 1 : 0;
5767c84e6fbSjason err = 0;
5777c84e6fbSjason break;
57842aaa9baSjason case HARMONY_PORT_MONITOR_LVL:
57942aaa9baSjason if (cp->type != AUDIO_MIXER_VALUE)
58042aaa9baSjason break;
58142aaa9baSjason if (cp->un.value.num_channels != 1)
58242aaa9baSjason break;
583b88c9c3fSjason sc->sc_monitor_lvl.left = sc->sc_input_lvl.right =
584b88c9c3fSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO];
585b88c9c3fSjason sc->sc_need_commit = 1;
58642aaa9baSjason err = 0;
58742aaa9baSjason break;
5887282f66cSjason case HARMONY_PORT_RECORD_SOURCE:
5897282f66cSjason if (cp->type != AUDIO_MIXER_ENUM)
5907282f66cSjason break;
5917282f66cSjason if (cp->un.ord != HARMONY_IN_LINE &&
5927282f66cSjason cp->un.ord != HARMONY_IN_MIC)
5937282f66cSjason break;
5947282f66cSjason sc->sc_in_port = cp->un.ord;
5957282f66cSjason err = 0;
5967282f66cSjason sc->sc_need_commit = 1;
5977282f66cSjason break;
598591687a2Sjason case HARMONY_PORT_OUTPUT_SOURCE:
599591687a2Sjason if (cp->type != AUDIO_MIXER_ENUM)
600591687a2Sjason break;
601591687a2Sjason if (cp->un.ord != HARMONY_OUT_LINE &&
602591687a2Sjason cp->un.ord != HARMONY_OUT_SPEAKER &&
603591687a2Sjason cp->un.ord != HARMONY_OUT_HEADPHONE)
604591687a2Sjason break;
605591687a2Sjason sc->sc_out_port = cp->un.ord;
606591687a2Sjason err = 0;
607591687a2Sjason sc->sc_need_commit = 1;
608591687a2Sjason break;
60942aaa9baSjason }
61042aaa9baSjason
61142aaa9baSjason return (err);
61242aaa9baSjason }
61342aaa9baSjason
61442aaa9baSjason int
harmony_get_port(void * vsc,mixer_ctrl_t * cp)61542aaa9baSjason harmony_get_port(void *vsc, mixer_ctrl_t *cp)
61642aaa9baSjason {
61742aaa9baSjason struct harmony_softc *sc = vsc;
61842aaa9baSjason int err = EINVAL;
61942aaa9baSjason
62042aaa9baSjason switch (cp->dev) {
62142aaa9baSjason case HARMONY_PORT_INPUT_LVL:
62242aaa9baSjason if (cp->type != AUDIO_MIXER_VALUE)
62342aaa9baSjason break;
62442aaa9baSjason if (cp->un.value.num_channels == 1) {
62542aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
626b88c9c3fSjason sc->sc_input_lvl.left;
62742aaa9baSjason } else if (cp->un.value.num_channels == 2) {
62842aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
629b88c9c3fSjason sc->sc_input_lvl.left;
63042aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
631b88c9c3fSjason sc->sc_input_lvl.right;
63242aaa9baSjason } else
63342aaa9baSjason break;
63442aaa9baSjason err = 0;
63542aaa9baSjason break;
63627b54b8bSmickey case HARMONY_PORT_INPUT_OV:
63727b54b8bSmickey if (cp->type != AUDIO_MIXER_ENUM)
63827b54b8bSmickey break;
63927b54b8bSmickey cp->un.ord = sc->sc_ov ? 1 : 0;
64027b54b8bSmickey err = 0;
64127b54b8bSmickey break;
64242aaa9baSjason case HARMONY_PORT_OUTPUT_LVL:
64342aaa9baSjason if (cp->type != AUDIO_MIXER_VALUE)
64442aaa9baSjason break;
64542aaa9baSjason if (cp->un.value.num_channels == 1) {
64642aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
647b88c9c3fSjason sc->sc_output_lvl.left;
64842aaa9baSjason } else if (cp->un.value.num_channels == 2) {
64942aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_LEFT] =
650b88c9c3fSjason sc->sc_output_lvl.left;
65142aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_RIGHT] =
652b88c9c3fSjason sc->sc_output_lvl.right;
65342aaa9baSjason } else
65442aaa9baSjason break;
65542aaa9baSjason err = 0;
65642aaa9baSjason break;
6577c84e6fbSjason case HARMONY_PORT_OUTPUT_GAIN:
6587c84e6fbSjason if (cp->type != AUDIO_MIXER_ENUM)
6597c84e6fbSjason break;
6607c84e6fbSjason cp->un.ord = sc->sc_outputgain ? 1 : 0;
6617c84e6fbSjason err = 0;
6627c84e6fbSjason break;
66342aaa9baSjason case HARMONY_PORT_MONITOR_LVL:
66442aaa9baSjason if (cp->type != AUDIO_MIXER_VALUE)
66542aaa9baSjason break;
66642aaa9baSjason if (cp->un.value.num_channels != 1)
66742aaa9baSjason break;
66842aaa9baSjason cp->un.value.level[AUDIO_MIXER_LEVEL_MONO] =
669b88c9c3fSjason sc->sc_monitor_lvl.left;
67042aaa9baSjason err = 0;
67142aaa9baSjason break;
6727282f66cSjason case HARMONY_PORT_RECORD_SOURCE:
6737282f66cSjason if (cp->type != AUDIO_MIXER_ENUM)
6747282f66cSjason break;
6757282f66cSjason cp->un.ord = sc->sc_in_port;
6767282f66cSjason err = 0;
6777282f66cSjason break;
678591687a2Sjason case HARMONY_PORT_OUTPUT_SOURCE:
679591687a2Sjason if (cp->type != AUDIO_MIXER_ENUM)
680591687a2Sjason break;
681591687a2Sjason cp->un.ord = sc->sc_out_port;
682591687a2Sjason err = 0;
683591687a2Sjason break;
68442aaa9baSjason }
68542aaa9baSjason return (0);
68642aaa9baSjason }
68742aaa9baSjason
68842aaa9baSjason int
harmony_query_devinfo(void * vsc,mixer_devinfo_t * dip)68942aaa9baSjason harmony_query_devinfo(void *vsc, mixer_devinfo_t *dip)
69042aaa9baSjason {
69142aaa9baSjason int err = 0;
69242aaa9baSjason
69342aaa9baSjason switch (dip->index) {
69442aaa9baSjason case HARMONY_PORT_INPUT_LVL:
69542aaa9baSjason dip->type = AUDIO_MIXER_VALUE;
69642aaa9baSjason dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
69742aaa9baSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
698094fa01fSderaadt strlcpy(dip->label.name, AudioNinput, sizeof dip->label.name);
69942aaa9baSjason dip->un.v.num_channels = 2;
700094fa01fSderaadt strlcpy(dip->un.v.units.name, AudioNvolume,
701094fa01fSderaadt sizeof dip->un.v.units.name);
70242aaa9baSjason break;
70327b54b8bSmickey case HARMONY_PORT_INPUT_OV:
70427b54b8bSmickey dip->type = AUDIO_MIXER_ENUM;
70527b54b8bSmickey dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
70627b54b8bSmickey dip->prev = dip->next = AUDIO_MIXER_LAST;
707094fa01fSderaadt strlcpy(dip->label.name, "overrange", sizeof dip->label.name);
70827b54b8bSmickey dip->un.e.num_mem = 2;
709094fa01fSderaadt strlcpy(dip->un.e.member[0].label.name, AudioNoff,
710094fa01fSderaadt sizeof dip->un.e.member[0].label.name);
71127b54b8bSmickey dip->un.e.member[0].ord = 0;
712094fa01fSderaadt strlcpy(dip->un.e.member[1].label.name, AudioNon,
713094fa01fSderaadt sizeof dip->un.e.member[1].label.name);
71427b54b8bSmickey dip->un.e.member[1].ord = 1;
71527b54b8bSmickey break;
71642aaa9baSjason case HARMONY_PORT_OUTPUT_LVL:
71742aaa9baSjason dip->type = AUDIO_MIXER_VALUE;
71842aaa9baSjason dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
71942aaa9baSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
720094fa01fSderaadt strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
72142aaa9baSjason dip->un.v.num_channels = 2;
722094fa01fSderaadt strlcpy(dip->un.v.units.name, AudioNvolume,
723094fa01fSderaadt sizeof dip->un.v.units.name);
72442aaa9baSjason break;
7257c84e6fbSjason case HARMONY_PORT_OUTPUT_GAIN:
7267c84e6fbSjason dip->type = AUDIO_MIXER_ENUM;
7277c84e6fbSjason dip->mixer_class = HARMONY_PORT_OUTPUT_CLASS;
7287c84e6fbSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
729094fa01fSderaadt strlcpy(dip->label.name, "gain", sizeof dip->label.name);
7307c84e6fbSjason dip->un.e.num_mem = 2;
731094fa01fSderaadt strlcpy(dip->un.e.member[0].label.name, AudioNoff,
732094fa01fSderaadt sizeof dip->un.e.member[0].label.name);
7337c84e6fbSjason dip->un.e.member[0].ord = 0;
734094fa01fSderaadt strlcpy(dip->un.e.member[1].label.name, AudioNon,
735094fa01fSderaadt sizeof dip->un.e.member[1].label.name);
7367c84e6fbSjason dip->un.e.member[1].ord = 1;
7377c84e6fbSjason break;
73842aaa9baSjason case HARMONY_PORT_MONITOR_LVL:
73942aaa9baSjason dip->type = AUDIO_MIXER_VALUE;
74042aaa9baSjason dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
74142aaa9baSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
742094fa01fSderaadt strlcpy(dip->label.name, AudioNmonitor, sizeof dip->label.name);
74342aaa9baSjason dip->un.v.num_channels = 1;
744094fa01fSderaadt strlcpy(dip->un.v.units.name, AudioNvolume,
745094fa01fSderaadt sizeof dip->un.v.units.name);
74642aaa9baSjason break;
7477282f66cSjason case HARMONY_PORT_RECORD_SOURCE:
7487282f66cSjason dip->type = AUDIO_MIXER_ENUM;
7497282f66cSjason dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
7507282f66cSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
751094fa01fSderaadt strlcpy(dip->label.name, AudioNsource, sizeof dip->label.name);
7527282f66cSjason dip->un.e.num_mem = 2;
753094fa01fSderaadt strlcpy(dip->un.e.member[0].label.name, AudioNmicrophone,
754094fa01fSderaadt sizeof dip->un.e.member[0].label.name);
7557282f66cSjason dip->un.e.member[0].ord = HARMONY_IN_MIC;
756094fa01fSderaadt strlcpy(dip->un.e.member[1].label.name, AudioNline,
757094fa01fSderaadt sizeof dip->un.e.member[1].label.name);
7587282f66cSjason dip->un.e.member[1].ord = HARMONY_IN_LINE;
7597282f66cSjason break;
760591687a2Sjason case HARMONY_PORT_OUTPUT_SOURCE:
761591687a2Sjason dip->type = AUDIO_MIXER_ENUM;
762591687a2Sjason dip->mixer_class = HARMONY_PORT_MONITOR_CLASS;
763591687a2Sjason dip->prev = dip->next = AUDIO_MIXER_LAST;
764094fa01fSderaadt strlcpy(dip->label.name, AudioNoutput, sizeof dip->label.name);
765591687a2Sjason dip->un.e.num_mem = 3;
766094fa01fSderaadt strlcpy(dip->un.e.member[0].label.name, AudioNline,
767094fa01fSderaadt sizeof dip->un.e.member[0].label.name);
768591687a2Sjason dip->un.e.member[0].ord = HARMONY_OUT_LINE;
769094fa01fSderaadt strlcpy(dip->un.e.member[1].label.name, AudioNspeaker,
770094fa01fSderaadt sizeof dip->un.e.member[1].label.name);
771591687a2Sjason dip->un.e.member[1].ord = HARMONY_OUT_SPEAKER;
772094fa01fSderaadt strlcpy(dip->un.e.member[2].label.name, AudioNheadphone,
773094fa01fSderaadt sizeof dip->un.e.member[2].label.name);
774591687a2Sjason dip->un.e.member[2].ord = HARMONY_OUT_HEADPHONE;
775591687a2Sjason break;
77642aaa9baSjason case HARMONY_PORT_INPUT_CLASS:
77742aaa9baSjason dip->type = AUDIO_MIXER_CLASS;
77842aaa9baSjason dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
77942aaa9baSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
780094fa01fSderaadt strlcpy(dip->label.name, AudioCinputs, sizeof dip->label.name);
78142aaa9baSjason break;
78242aaa9baSjason case HARMONY_PORT_OUTPUT_CLASS:
78342aaa9baSjason dip->type = AUDIO_MIXER_CLASS;
78442aaa9baSjason dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
78542aaa9baSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
786094fa01fSderaadt strlcpy(dip->label.name, AudioCoutputs, sizeof dip->label.name);
78742aaa9baSjason break;
78842aaa9baSjason case HARMONY_PORT_MONITOR_CLASS:
78942aaa9baSjason dip->type = AUDIO_MIXER_CLASS;
79042aaa9baSjason dip->mixer_class = HARMONY_PORT_INPUT_CLASS;
79142aaa9baSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
792094fa01fSderaadt strlcpy(dip->label.name, AudioCmonitor, sizeof dip->label.name);
79342aaa9baSjason break;
7947282f66cSjason case HARMONY_PORT_RECORD_CLASS:
7957282f66cSjason dip->type = AUDIO_MIXER_CLASS;
7967282f66cSjason dip->mixer_class = HARMONY_PORT_RECORD_CLASS;
7977282f66cSjason dip->prev = dip->next = AUDIO_MIXER_LAST;
798094fa01fSderaadt strlcpy(dip->label.name, AudioCrecord, sizeof dip->label.name);
7997282f66cSjason break;
80042aaa9baSjason default:
80142aaa9baSjason err = ENXIO;
80242aaa9baSjason break;
80342aaa9baSjason }
80442aaa9baSjason
80542aaa9baSjason return (err);
80642aaa9baSjason }
80742aaa9baSjason
808673bd31fSjason void *
harmony_allocm(void * vsc,int dir,size_t size,int pool,int flags)809673bd31fSjason harmony_allocm(void *vsc, int dir, size_t size, int pool, int flags)
810673bd31fSjason {
811673bd31fSjason struct harmony_softc *sc = vsc;
812673bd31fSjason struct harmony_dma *d;
813673bd31fSjason int rseg;
814673bd31fSjason
815673bd31fSjason d = (struct harmony_dma *)malloc(sizeof(struct harmony_dma), pool, flags);
816673bd31fSjason if (d == NULL)
817673bd31fSjason goto fail;
818673bd31fSjason
819673bd31fSjason if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT,
820673bd31fSjason &d->d_map) != 0)
821673bd31fSjason goto fail1;
822673bd31fSjason
823673bd31fSjason if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &d->d_seg, 1,
824673bd31fSjason &rseg, BUS_DMA_NOWAIT) != 0)
825673bd31fSjason goto fail2;
826673bd31fSjason
827673bd31fSjason if (bus_dmamem_map(sc->sc_dmat, &d->d_seg, 1, size, &d->d_kva,
828673bd31fSjason BUS_DMA_NOWAIT) != 0)
829673bd31fSjason goto fail3;
830673bd31fSjason
831673bd31fSjason if (bus_dmamap_load(sc->sc_dmat, d->d_map, d->d_kva, size, NULL,
832673bd31fSjason BUS_DMA_NOWAIT) != 0)
833673bd31fSjason goto fail4;
834673bd31fSjason
835673bd31fSjason d->d_next = sc->sc_dmas;
836673bd31fSjason sc->sc_dmas = d;
837673bd31fSjason d->d_size = size;
838673bd31fSjason return (d->d_kva);
839673bd31fSjason
840673bd31fSjason fail4:
841673bd31fSjason bus_dmamem_unmap(sc->sc_dmat, d->d_kva, size);
842673bd31fSjason fail3:
843673bd31fSjason bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
844673bd31fSjason fail2:
845673bd31fSjason bus_dmamap_destroy(sc->sc_dmat, d->d_map);
846673bd31fSjason fail1:
847367b04a3Sderaadt free(d, pool, sizeof *d);
848673bd31fSjason fail:
849673bd31fSjason return (NULL);
850673bd31fSjason }
851673bd31fSjason
852673bd31fSjason void
harmony_freem(void * vsc,void * ptr,int pool)853673bd31fSjason harmony_freem(void *vsc, void *ptr, int pool)
854673bd31fSjason {
855673bd31fSjason struct harmony_softc *sc = vsc;
856673bd31fSjason struct harmony_dma *d, **dd;
857673bd31fSjason
858673bd31fSjason for (dd = &sc->sc_dmas; (d = *dd) != NULL; dd = &(*dd)->d_next) {
859673bd31fSjason if (d->d_kva != ptr)
860673bd31fSjason continue;
861673bd31fSjason bus_dmamap_unload(sc->sc_dmat, d->d_map);
862673bd31fSjason bus_dmamem_unmap(sc->sc_dmat, d->d_kva, d->d_size);
863673bd31fSjason bus_dmamem_free(sc->sc_dmat, &d->d_seg, 1);
864673bd31fSjason bus_dmamap_destroy(sc->sc_dmat, d->d_map);
865367b04a3Sderaadt free(d, pool, sizeof *d);
866673bd31fSjason return;
867673bd31fSjason }
868673bd31fSjason printf("%s: free rogue pointer\n", sc->sc_dv.dv_xname);
869673bd31fSjason }
870673bd31fSjason
87142aaa9baSjason size_t
harmony_round_buffersize(void * vsc,int direction,size_t size)87242aaa9baSjason harmony_round_buffersize(void *vsc, int direction, size_t size)
87342aaa9baSjason {
874b0d03aaaSmickey return ((size + HARMONY_BUFSIZE - 1) & (size_t)(-HARMONY_BUFSIZE));
87542aaa9baSjason }
87642aaa9baSjason
87742aaa9baSjason int
harmony_trigger_output(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * param)87842aaa9baSjason harmony_trigger_output(void *vsc, void *start, void *end, int blksize,
879c9f617c0Sjason void (*intr)(void *), void *intrarg, struct audio_params *param)
88042aaa9baSjason {
88142aaa9baSjason struct harmony_softc *sc = vsc;
882673bd31fSjason struct harmony_channel *c = &sc->sc_playback;
883673bd31fSjason struct harmony_dma *d;
8847a877ae9Sjason bus_addr_t nextaddr;
8857a877ae9Sjason bus_size_t togo;
88642aaa9baSjason
887673bd31fSjason for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
888673bd31fSjason /*EMPTY*/;
889673bd31fSjason if (d == NULL) {
890673bd31fSjason printf("%s: trigger_output: bad addr: %p\n",
891673bd31fSjason sc->sc_dv.dv_xname, start);
892673bd31fSjason return (EINVAL);
893673bd31fSjason }
894673bd31fSjason
895c9f617c0Sjason c->c_intr = intr;
896c9f617c0Sjason c->c_intrarg = intrarg;
897673bd31fSjason c->c_blksz = blksize;
898673bd31fSjason c->c_current = d;
8997282f66cSjason c->c_segsz = (caddr_t)end - (caddr_t)start;
9007282f66cSjason c->c_cnt = 0;
9017282f66cSjason c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
902673bd31fSjason
903532b98aeSjason sc->sc_playing = 1;
9047282f66cSjason
905167728ffSjason togo = c->c_segsz - c->c_cnt;
906167728ffSjason if (togo == 0) {
907167728ffSjason nextaddr = d->d_map->dm_segs[0].ds_addr;
908167728ffSjason c->c_cnt = togo = c->c_blksz;
909167728ffSjason } else {
910167728ffSjason nextaddr = c->c_lastaddr;
911167728ffSjason if (togo > c->c_blksz)
912167728ffSjason togo = c->c_blksz;
913167728ffSjason c->c_cnt += togo;
914167728ffSjason }
915167728ffSjason
916167728ffSjason bus_dmamap_sync(sc->sc_dmat, d->d_map,
917167728ffSjason nextaddr - d->d_map->dm_segs[0].ds_addr,
918167728ffSjason c->c_blksz, BUS_DMASYNC_PREWRITE);
919167728ffSjason
920886882aaSratchov mtx_enter(&audio_lock);
921167728ffSjason WRITE_REG(sc, HARMONY_PNXTADD, nextaddr);
9227a877ae9Sjason c->c_theaddr = nextaddr;
923167728ffSjason SYNC_REG(sc, HARMONY_PNXTADD, BUS_SPACE_BARRIER_WRITE);
924167728ffSjason c->c_lastaddr = nextaddr + togo;
9257a877ae9Sjason
9267a877ae9Sjason harmony_start_cp(sc);
9277a877ae9Sjason harmony_intr_enable(sc);
928886882aaSratchov mtx_leave(&audio_lock);
9297a877ae9Sjason return (0);
930167728ffSjason }
931167728ffSjason
932167728ffSjason void
harmony_start_cp(struct harmony_softc * sc)933167728ffSjason harmony_start_cp(struct harmony_softc *sc)
934167728ffSjason {
935167728ffSjason struct harmony_channel *c = &sc->sc_capture;
936167728ffSjason struct harmony_dma *d;
937167728ffSjason bus_addr_t nextaddr;
938167728ffSjason bus_size_t togo;
939167728ffSjason
940167728ffSjason if (sc->sc_capturing == 0) {
941167728ffSjason WRITE_REG(sc, HARMONY_RNXTADD,
942167728ffSjason sc->sc_capture_paddrs[sc->sc_capture_empty]);
943167728ffSjason if (++sc->sc_capture_empty == CAPTURE_EMPTYS)
944167728ffSjason sc->sc_capture_empty = 0;
945167728ffSjason } else {
946167728ffSjason d = c->c_current;
947167728ffSjason togo = c->c_segsz - c->c_cnt;
948167728ffSjason if (togo == 0) {
949167728ffSjason nextaddr = d->d_map->dm_segs[0].ds_addr;
950167728ffSjason c->c_cnt = togo = c->c_blksz;
951167728ffSjason } else {
952167728ffSjason nextaddr = c->c_lastaddr;
953167728ffSjason if (togo > c->c_blksz)
954167728ffSjason togo = c->c_blksz;
955167728ffSjason c->c_cnt += togo;
956167728ffSjason }
957167728ffSjason
958167728ffSjason bus_dmamap_sync(sc->sc_dmat, d->d_map,
959167728ffSjason nextaddr - d->d_map->dm_segs[0].ds_addr,
960167728ffSjason c->c_blksz, BUS_DMASYNC_PREWRITE);
961167728ffSjason
962167728ffSjason WRITE_REG(sc, HARMONY_RNXTADD, nextaddr);
963591687a2Sjason SYNC_REG(sc, HARMONY_RNXTADD, BUS_SPACE_BARRIER_WRITE);
964167728ffSjason c->c_lastaddr = nextaddr + togo;
965167728ffSjason }
9667180d4afSmickey
9677180d4afSmickey timeout_add(&sc->sc_acc_tmo, 1);
968167728ffSjason }
969167728ffSjason
97042aaa9baSjason int
harmony_trigger_input(void * vsc,void * start,void * end,int blksize,void (* intr)(void *),void * intrarg,struct audio_params * param)97142aaa9baSjason harmony_trigger_input(void *vsc, void *start, void *end, int blksize,
972b88c9c3fSjason void (*intr)(void *), void *intrarg, struct audio_params *param)
97342aaa9baSjason {
97442aaa9baSjason struct harmony_softc *sc = vsc;
975b88c9c3fSjason struct harmony_channel *c = &sc->sc_capture;
976b88c9c3fSjason struct harmony_dma *d;
97742aaa9baSjason
978b88c9c3fSjason for (d = sc->sc_dmas; d->d_kva != start; d = d->d_next)
979b88c9c3fSjason /*EMPTY*/;
980b88c9c3fSjason if (d == NULL) {
981b88c9c3fSjason printf("%s: trigger_input: bad addr: %p\n",
982b88c9c3fSjason sc->sc_dv.dv_xname, start);
983b88c9c3fSjason return (EINVAL);
984b88c9c3fSjason }
985b88c9c3fSjason
986b88c9c3fSjason c->c_intr = intr;
987b88c9c3fSjason c->c_intrarg = intrarg;
988b88c9c3fSjason c->c_blksz = blksize;
989b88c9c3fSjason c->c_current = d;
9907282f66cSjason c->c_segsz = (caddr_t)end - (caddr_t)start;
9917282f66cSjason c->c_cnt = 0;
9927282f66cSjason c->c_lastaddr = d->d_map->dm_segs[0].ds_addr;
993886882aaSratchov mtx_enter(&audio_lock);
994673bd31fSjason sc->sc_capturing = 1;
995167728ffSjason harmony_start_cp(sc);
99642aaa9baSjason harmony_intr_enable(sc);
997886882aaSratchov mtx_leave(&audio_lock);
99842aaa9baSjason return (0);
99942aaa9baSjason }
100042aaa9baSjason
100142aaa9baSjason static const struct speed_struct {
100242aaa9baSjason u_int32_t speed;
100342aaa9baSjason u_int32_t bits;
100442aaa9baSjason } harmony_speeds[] = {
100542aaa9baSjason { 5125, CNTL_RATE_5125 },
100642aaa9baSjason { 6615, CNTL_RATE_6615 },
100742aaa9baSjason { 8000, CNTL_RATE_8000 },
100842aaa9baSjason { 9600, CNTL_RATE_9600 },
100942aaa9baSjason { 11025, CNTL_RATE_11025 },
101042aaa9baSjason { 16000, CNTL_RATE_16000 },
101142aaa9baSjason { 18900, CNTL_RATE_18900 },
101242aaa9baSjason { 22050, CNTL_RATE_22050 },
101342aaa9baSjason { 27428, CNTL_RATE_27428 },
101442aaa9baSjason { 32000, CNTL_RATE_32000 },
101542aaa9baSjason { 33075, CNTL_RATE_33075 },
101642aaa9baSjason { 37800, CNTL_RATE_37800 },
101742aaa9baSjason { 44100, CNTL_RATE_44100 },
101842aaa9baSjason { 48000, CNTL_RATE_48000 },
101942aaa9baSjason };
102042aaa9baSjason
102142aaa9baSjason u_int32_t
harmony_speed_bits(struct harmony_softc * sc,u_long * speedp)102242aaa9baSjason harmony_speed_bits(struct harmony_softc *sc, u_long *speedp)
102342aaa9baSjason {
102442aaa9baSjason int i, n, selected = -1;
102542aaa9baSjason
102642aaa9baSjason n = sizeof(harmony_speeds) / sizeof(harmony_speeds[0]);
102742aaa9baSjason
102842aaa9baSjason if ((*speedp) <= harmony_speeds[0].speed)
102942aaa9baSjason selected = 0;
103042aaa9baSjason else if ((*speedp) >= harmony_speeds[n - 1].speed)
103142aaa9baSjason selected = n - 1;
103242aaa9baSjason else {
103342aaa9baSjason for (i = 1; selected == -1 && i < n; i++) {
103442aaa9baSjason if ((*speedp) == harmony_speeds[i].speed)
103542aaa9baSjason selected = i;
103642aaa9baSjason else if ((*speedp) < harmony_speeds[i].speed) {
103742aaa9baSjason int diff1, diff2;
103842aaa9baSjason
103942aaa9baSjason diff1 = (*speedp) - harmony_speeds[i - 1].speed;
104042aaa9baSjason diff2 = harmony_speeds[i].speed - (*speedp);
104142aaa9baSjason if (diff1 < diff2)
104242aaa9baSjason selected = i - 1;
104342aaa9baSjason else
104442aaa9baSjason selected = i;
104542aaa9baSjason }
104642aaa9baSjason }
104742aaa9baSjason }
104842aaa9baSjason
104942aaa9baSjason if (selected == -1)
105042aaa9baSjason selected = 2;
105142aaa9baSjason
105242aaa9baSjason *speedp = harmony_speeds[selected].speed;
105342aaa9baSjason return (harmony_speeds[selected].bits);
105442aaa9baSjason }
105542aaa9baSjason
1056fe6bde3cSjason int
harmony_set_gainctl(struct harmony_softc * sc)10577282f66cSjason harmony_set_gainctl(struct harmony_softc *sc)
1058b88c9c3fSjason {
1059fe6bde3cSjason u_int32_t bits, mask, val, old;
1060b88c9c3fSjason
10617282f66cSjason /* XXX leave these bits alone or the chip will not come out of CNTL */
1062591687a2Sjason bits = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
1063b88c9c3fSjason
1064b88c9c3fSjason /* input level */
1065b88c9c3fSjason bits |= ((sc->sc_input_lvl.left >> (8 - GAINCTL_INPUT_BITS)) <<
1066b88c9c3fSjason GAINCTL_INPUT_LEFT_S) & GAINCTL_INPUT_LEFT_M;
1067b88c9c3fSjason bits |= ((sc->sc_input_lvl.right >> (8 - GAINCTL_INPUT_BITS)) <<
1068b88c9c3fSjason GAINCTL_INPUT_RIGHT_S) & GAINCTL_INPUT_RIGHT_M;
1069b88c9c3fSjason
1070b88c9c3fSjason /* output level (inverted) */
1071b88c9c3fSjason mask = (1 << GAINCTL_OUTPUT_BITS) - 1;
1072b88c9c3fSjason val = mask - (sc->sc_output_lvl.left >> (8 - GAINCTL_OUTPUT_BITS));
1073b88c9c3fSjason bits |= (val << GAINCTL_OUTPUT_LEFT_S) & GAINCTL_OUTPUT_LEFT_M;
1074b88c9c3fSjason val = mask - (sc->sc_output_lvl.right >> (8 - GAINCTL_OUTPUT_BITS));
1075b88c9c3fSjason bits |= (val << GAINCTL_OUTPUT_RIGHT_S) & GAINCTL_OUTPUT_RIGHT_M;
1076b88c9c3fSjason
1077b88c9c3fSjason /* monitor level (inverted) */
1078b88c9c3fSjason mask = (1 << GAINCTL_MONITOR_BITS) - 1;
1079b88c9c3fSjason val = mask - (sc->sc_monitor_lvl.left >> (8 - GAINCTL_MONITOR_BITS));
1080b88c9c3fSjason bits |= (val << GAINCTL_MONITOR_S) & GAINCTL_MONITOR_M;
1081b88c9c3fSjason
1082591687a2Sjason /* XXX messing with these causes CNTL_C to get stuck... grr. */
1083591687a2Sjason bits &= ~GAINCTL_IS_MASK;
1084591687a2Sjason if (sc->sc_in_port == HARMONY_IN_MIC)
1085591687a2Sjason bits |= GAINCTL_IS_LINE;
1086591687a2Sjason else
1087fe6bde3cSjason bits |= GAINCTL_IS_MICROPHONE;
1088591687a2Sjason
1089591687a2Sjason /* XXX messing with these causes CNTL_C to get stuck... grr. */
1090fe6bde3cSjason bits &= ~(GAINCTL_LE | GAINCTL_HE | GAINCTL_SE);
1091591687a2Sjason if (sc->sc_out_port == HARMONY_OUT_LINE)
1092591687a2Sjason bits |= GAINCTL_LE;
1093591687a2Sjason else if (sc->sc_out_port == HARMONY_OUT_SPEAKER)
1094591687a2Sjason bits |= GAINCTL_SE;
1095591687a2Sjason else
1096591687a2Sjason bits |= GAINCTL_HE;
1097591687a2Sjason
1098fe6bde3cSjason mask = GAINCTL_LE | GAINCTL_HE | GAINCTL_SE | GAINCTL_IS_MASK;
1099fe6bde3cSjason old = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL);
11007282f66cSjason bus_space_write_4(sc->sc_bt, sc->sc_bh, HARMONY_GAINCTL, bits);
1101fe6bde3cSjason if ((old & mask) != (bits & mask))
1102fe6bde3cSjason return (1);
1103fe6bde3cSjason return (0);
1104b88c9c3fSjason }
1105b88c9c3fSjason
11067a877ae9Sjason void
harmony_try_more(struct harmony_softc * sc)11077a877ae9Sjason harmony_try_more(struct harmony_softc *sc)
11087a877ae9Sjason {
11097a877ae9Sjason struct harmony_channel *c = &sc->sc_playback;
11107a877ae9Sjason struct harmony_dma *d = c->c_current;
11117a877ae9Sjason u_int32_t cur;
11127a877ae9Sjason int i, nsegs;
11137a877ae9Sjason
11147a877ae9Sjason cur = bus_space_read_4(sc->sc_bt, sc->sc_bh, HARMONY_PCURADD);
11157a877ae9Sjason cur &= PCURADD_BUFMASK;
11167a877ae9Sjason nsegs = 0;
11177a877ae9Sjason
11187a877ae9Sjason #ifdef DIAGNOSTIC
11197a877ae9Sjason if (cur < d->d_map->dm_segs[0].ds_addr ||
11207a877ae9Sjason cur >= (d->d_map->dm_segs[0].ds_addr + c->c_segsz))
112156f69dffSmiod panic("%s: bad current %x < %lx || %x > %lx",
112256f69dffSmiod sc->sc_dv.dv_xname, cur, d->d_map->dm_segs[0].ds_addr, cur,
11237a877ae9Sjason d->d_map->dm_segs[0].ds_addr + c->c_segsz);
11247a877ae9Sjason #endif /* DIAGNOSTIC */
11257a877ae9Sjason
11267a877ae9Sjason if (cur > c->c_theaddr) {
11277a877ae9Sjason nsegs = (cur - c->c_theaddr) / HARMONY_BUFSIZE;
11287a877ae9Sjason } else if (cur < c->c_theaddr) {
11297a877ae9Sjason nsegs = (d->d_map->dm_segs[0].ds_addr + c->c_segsz -
11307a877ae9Sjason c->c_theaddr) / HARMONY_BUFSIZE;
11317a877ae9Sjason nsegs += (cur - d->d_map->dm_segs[0].ds_addr) /
11327a877ae9Sjason HARMONY_BUFSIZE;
11337a877ae9Sjason }
11347a877ae9Sjason
11357a877ae9Sjason if (nsegs != 0 && c->c_intr != NULL) {
11367a877ae9Sjason for (i = 0; i < nsegs; i++)
11377a877ae9Sjason (*c->c_intr)(c->c_intrarg);
11387a877ae9Sjason c->c_theaddr = cur;
11397a877ae9Sjason }
11407a877ae9Sjason }
11417a877ae9Sjason
114242aaa9baSjason struct cfdriver harmony_cd = {
114342aaa9baSjason NULL, "harmony", DV_DULL
114442aaa9baSjason };
114542aaa9baSjason
114678d5ff0eSmpi const struct cfattach harmony_ca = {
114742aaa9baSjason sizeof(struct harmony_softc), harmony_match, harmony_attach
114842aaa9baSjason };
1149