1*778f5600Sandvar /* $NetBSD: hdaudio.c,v 1.18 2022/04/07 19:33:37 andvar Exp $ */
27b8ad118Sjmcneill
37b8ad118Sjmcneill /*
47b8ad118Sjmcneill * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk>
57b8ad118Sjmcneill * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
67b8ad118Sjmcneill * All rights reserved.
77b8ad118Sjmcneill *
87b8ad118Sjmcneill * This code is derived from software contributed to The NetBSD Foundation
97b8ad118Sjmcneill * by Precedence Technologies Ltd
107b8ad118Sjmcneill *
117b8ad118Sjmcneill * Redistribution and use in source and binary forms, with or without
127b8ad118Sjmcneill * modification, are permitted provided that the following conditions
137b8ad118Sjmcneill * are met:
147b8ad118Sjmcneill * 1. Redistributions of source code must retain the above copyright
157b8ad118Sjmcneill * notice, this list of conditions and the following disclaimer.
167b8ad118Sjmcneill * 2. The name of the author may not be used to endorse or promote products
177b8ad118Sjmcneill * derived from this software without specific prior written permission.
187b8ad118Sjmcneill *
197b8ad118Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
207b8ad118Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
217b8ad118Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
227b8ad118Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
237b8ad118Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
247b8ad118Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
257b8ad118Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
267b8ad118Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
277b8ad118Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
287b8ad118Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
297b8ad118Sjmcneill * SUCH DAMAGE.
307b8ad118Sjmcneill */
317b8ad118Sjmcneill
327b8ad118Sjmcneill #include <sys/cdefs.h>
33*778f5600Sandvar __KERNEL_RCSID(0, "$NetBSD: hdaudio.c,v 1.18 2022/04/07 19:33:37 andvar Exp $");
347b8ad118Sjmcneill
357b8ad118Sjmcneill #include <sys/types.h>
367b8ad118Sjmcneill #include <sys/param.h>
377b8ad118Sjmcneill #include <sys/systm.h>
387b8ad118Sjmcneill #include <sys/device.h>
397b8ad118Sjmcneill #include <sys/conf.h>
407b8ad118Sjmcneill #include <sys/bus.h>
417b8ad118Sjmcneill #include <sys/kmem.h>
427b8ad118Sjmcneill #include <sys/module.h>
437b8ad118Sjmcneill
447b8ad118Sjmcneill #include "hdaudiovar.h"
457b8ad118Sjmcneill #include "hdaudioreg.h"
467b8ad118Sjmcneill #include "hdaudioio.h"
477b8ad118Sjmcneill #include "hdaudio_verbose.h"
48351ab7adSpgoyette #include "hdaudiodevs.h"
497b8ad118Sjmcneill
507b8ad118Sjmcneill /* #define HDAUDIO_DEBUG */
517b8ad118Sjmcneill
527b8ad118Sjmcneill #define HDAUDIO_RESET_TIMEOUT 5000
537b8ad118Sjmcneill #define HDAUDIO_CORB_TIMEOUT 1000
547b8ad118Sjmcneill #define HDAUDIO_RIRB_TIMEOUT 5000
557b8ad118Sjmcneill
567b8ad118Sjmcneill #define HDAUDIO_CODEC_DELAY 1000 /* spec calls for 250 */
577b8ad118Sjmcneill
587b8ad118Sjmcneill dev_type_open(hdaudioopen);
597b8ad118Sjmcneill dev_type_close(hdaudioclose);
607b8ad118Sjmcneill dev_type_ioctl(hdaudioioctl);
617b8ad118Sjmcneill
627b8ad118Sjmcneill const struct cdevsw hdaudio_cdevsw = {
637b8ad118Sjmcneill .d_open = hdaudioopen,
647b8ad118Sjmcneill .d_close = hdaudioclose,
657b8ad118Sjmcneill .d_read = noread,
667b8ad118Sjmcneill .d_write = nowrite,
677b8ad118Sjmcneill .d_ioctl = hdaudioioctl,
687b8ad118Sjmcneill .d_stop = nostop,
697b8ad118Sjmcneill .d_tty = notty,
707b8ad118Sjmcneill .d_poll = nopoll,
717b8ad118Sjmcneill .d_mmap = nommap,
727b8ad118Sjmcneill .d_kqfilter = nokqfilter,
737b8ad118Sjmcneill .d_discard = nodiscard,
747b8ad118Sjmcneill .d_flag = D_OTHER
757b8ad118Sjmcneill };
767b8ad118Sjmcneill
777b8ad118Sjmcneill extern struct cfdriver hdaudio_cd;
787b8ad118Sjmcneill
797b8ad118Sjmcneill #define HDAUDIOUNIT(x) minor((x))
807b8ad118Sjmcneill
817b8ad118Sjmcneill static void
hdaudio_stream_init(struct hdaudio_softc * sc,int nis,int nos,int nbidir)827b8ad118Sjmcneill hdaudio_stream_init(struct hdaudio_softc *sc, int nis, int nos, int nbidir)
837b8ad118Sjmcneill {
847b8ad118Sjmcneill int i, cnt = 0;
857b8ad118Sjmcneill
867b8ad118Sjmcneill for (i = 0; i < nis && cnt < HDAUDIO_MAX_STREAMS; i++) {
877b8ad118Sjmcneill sc->sc_stream[cnt].st_host = sc;
887b8ad118Sjmcneill sc->sc_stream[cnt].st_enable = true;
897b8ad118Sjmcneill sc->sc_stream[cnt].st_shift = cnt;
907b8ad118Sjmcneill sc->sc_stream[cnt++].st_type = HDAUDIO_STREAM_ISS;
917b8ad118Sjmcneill }
927b8ad118Sjmcneill for (i = 0; i < nos && cnt < HDAUDIO_MAX_STREAMS; i++) {
937b8ad118Sjmcneill sc->sc_stream[cnt].st_host = sc;
947b8ad118Sjmcneill sc->sc_stream[cnt].st_enable = true;
957b8ad118Sjmcneill sc->sc_stream[cnt].st_shift = cnt;
967b8ad118Sjmcneill sc->sc_stream[cnt++].st_type = HDAUDIO_STREAM_OSS;
977b8ad118Sjmcneill }
987b8ad118Sjmcneill for (i = 0; i < nbidir && cnt < HDAUDIO_MAX_STREAMS; i++) {
997b8ad118Sjmcneill sc->sc_stream[cnt].st_host = sc;
1007b8ad118Sjmcneill sc->sc_stream[cnt].st_enable = true;
1017b8ad118Sjmcneill sc->sc_stream[cnt].st_shift = cnt;
1027b8ad118Sjmcneill sc->sc_stream[cnt++].st_type = HDAUDIO_STREAM_BSS;
1037b8ad118Sjmcneill }
1047b8ad118Sjmcneill
1057b8ad118Sjmcneill for (i = 0; i < cnt; i++)
1067b8ad118Sjmcneill hdaudio_stream_stop(&sc->sc_stream[i]);
1077b8ad118Sjmcneill
1087b8ad118Sjmcneill sc->sc_stream_mask = 0;
1097b8ad118Sjmcneill }
1107b8ad118Sjmcneill
1117b8ad118Sjmcneill static void
hdaudio_codec_init(struct hdaudio_softc * sc)1127b8ad118Sjmcneill hdaudio_codec_init(struct hdaudio_softc *sc)
1137b8ad118Sjmcneill {
1147b8ad118Sjmcneill int i;
1157b8ad118Sjmcneill
1167b8ad118Sjmcneill for (i = 0; i < HDAUDIO_MAX_CODECS; i++) {
1177b8ad118Sjmcneill sc->sc_codec[i].co_addr = i;
1187b8ad118Sjmcneill sc->sc_codec[i].co_host = sc;
1197b8ad118Sjmcneill }
1207b8ad118Sjmcneill }
1217b8ad118Sjmcneill
1227b8ad118Sjmcneill static void
hdaudio_init(struct hdaudio_softc * sc)1237b8ad118Sjmcneill hdaudio_init(struct hdaudio_softc *sc)
1247b8ad118Sjmcneill {
125e1474beeSjmcneill const uint8_t vmaj = hda_read1(sc, HDAUDIO_MMIO_VMAJ);
126e1474beeSjmcneill const uint8_t vmin = hda_read1(sc, HDAUDIO_MMIO_VMIN);
127e1474beeSjmcneill const uint16_t gcap = hda_read2(sc, HDAUDIO_MMIO_GCAP);
128e1474beeSjmcneill const int nis = HDAUDIO_GCAP_ISS(gcap);
129e1474beeSjmcneill const int nos = HDAUDIO_GCAP_OSS(gcap);
130e1474beeSjmcneill const int nbidir = HDAUDIO_GCAP_BSS(gcap);
131e1474beeSjmcneill const int nsdo = HDAUDIO_GCAP_NSDO(gcap);
132e1474beeSjmcneill const int addr64 = HDAUDIO_GCAP_64OK(gcap);
1337b8ad118Sjmcneill
134e1474beeSjmcneill hda_print(sc, "HDA ver. %d.%d, OSS %d, ISS %d, BSS %d, SDO %d%s\n",
135e1474beeSjmcneill vmaj, vmin, nos, nis, nbidir, nsdo, addr64 ? ", 64-bit" : "");
1367b8ad118Sjmcneill
1377b8ad118Sjmcneill /* Initialize codecs and streams */
1387b8ad118Sjmcneill hdaudio_codec_init(sc);
1397b8ad118Sjmcneill hdaudio_stream_init(sc, nis, nos, nbidir);
1407b8ad118Sjmcneill }
1417b8ad118Sjmcneill
1427b8ad118Sjmcneill static int
hdaudio_codec_probe(struct hdaudio_softc * sc)1437b8ad118Sjmcneill hdaudio_codec_probe(struct hdaudio_softc *sc)
1447b8ad118Sjmcneill {
1457b8ad118Sjmcneill uint16_t statests;
1467b8ad118Sjmcneill int codecid;
1477b8ad118Sjmcneill
1487b8ad118Sjmcneill statests = hda_read2(sc, HDAUDIO_MMIO_STATESTS);
1497b8ad118Sjmcneill for (codecid = 0; codecid < HDAUDIO_MAX_CODECS; codecid++)
1507b8ad118Sjmcneill if (statests & (1 << codecid))
1517b8ad118Sjmcneill sc->sc_codec[codecid].co_valid = true;
1527b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS, statests);
1537b8ad118Sjmcneill
1547b8ad118Sjmcneill return statests;
1557b8ad118Sjmcneill }
1567b8ad118Sjmcneill
1577b8ad118Sjmcneill int
hdaudio_dma_alloc(struct hdaudio_softc * sc,struct hdaudio_dma * dma,int flags)1587b8ad118Sjmcneill hdaudio_dma_alloc(struct hdaudio_softc *sc, struct hdaudio_dma *dma,
1597b8ad118Sjmcneill int flags)
1607b8ad118Sjmcneill {
1617b8ad118Sjmcneill int err;
1627b8ad118Sjmcneill
1637b8ad118Sjmcneill KASSERT(dma->dma_size > 0);
1647b8ad118Sjmcneill
1657b8ad118Sjmcneill err = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, 128, 0,
1667b8ad118Sjmcneill dma->dma_segs, sizeof(dma->dma_segs) / sizeof(dma->dma_segs[0]),
1677b8ad118Sjmcneill &dma->dma_nsegs, BUS_DMA_WAITOK);
1687b8ad118Sjmcneill if (err)
1697b8ad118Sjmcneill return err;
1707b8ad118Sjmcneill err = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs,
1717b8ad118Sjmcneill dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | flags);
1727b8ad118Sjmcneill if (err)
1737b8ad118Sjmcneill goto free;
1747b8ad118Sjmcneill err = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs,
1757b8ad118Sjmcneill dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map);
1767b8ad118Sjmcneill if (err)
1777b8ad118Sjmcneill goto unmap;
1787b8ad118Sjmcneill err = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr,
1797b8ad118Sjmcneill dma->dma_size, NULL, BUS_DMA_WAITOK | flags);
1807b8ad118Sjmcneill if (err)
1817b8ad118Sjmcneill goto destroy;
1827b8ad118Sjmcneill
18351e195c1Sjmcneill memset(dma->dma_addr, 0, dma->dma_size);
18451e195c1Sjmcneill bus_dmamap_sync(sc->sc_dmat, dma->dma_map, 0, dma->dma_size,
18551e195c1Sjmcneill BUS_DMASYNC_PREWRITE);
18651e195c1Sjmcneill
1877b8ad118Sjmcneill dma->dma_valid = true;
1887b8ad118Sjmcneill return 0;
1897b8ad118Sjmcneill
1907b8ad118Sjmcneill destroy:
1917b8ad118Sjmcneill bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
1927b8ad118Sjmcneill unmap:
1937b8ad118Sjmcneill bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
1947b8ad118Sjmcneill free:
1957b8ad118Sjmcneill bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
1967b8ad118Sjmcneill
1977b8ad118Sjmcneill dma->dma_valid = false;
1987b8ad118Sjmcneill return err;
1997b8ad118Sjmcneill }
2007b8ad118Sjmcneill
2017b8ad118Sjmcneill void
hdaudio_dma_free(struct hdaudio_softc * sc,struct hdaudio_dma * dma)2027b8ad118Sjmcneill hdaudio_dma_free(struct hdaudio_softc *sc, struct hdaudio_dma *dma)
2037b8ad118Sjmcneill {
2047b8ad118Sjmcneill if (dma->dma_valid == false)
2057b8ad118Sjmcneill return;
2067b8ad118Sjmcneill bus_dmamap_unload(sc->sc_dmat, dma->dma_map);
2077b8ad118Sjmcneill bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
2087b8ad118Sjmcneill bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
2097b8ad118Sjmcneill bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
2107b8ad118Sjmcneill dma->dma_valid = false;
2117b8ad118Sjmcneill }
2127b8ad118Sjmcneill
2137b8ad118Sjmcneill static void
hdaudio_corb_enqueue(struct hdaudio_softc * sc,int addr,int nid,uint32_t control,uint32_t param)2147b8ad118Sjmcneill hdaudio_corb_enqueue(struct hdaudio_softc *sc, int addr, int nid,
2157b8ad118Sjmcneill uint32_t control, uint32_t param)
2167b8ad118Sjmcneill {
2177b8ad118Sjmcneill uint32_t *corb = DMA_KERNADDR(&sc->sc_corb);
2187b8ad118Sjmcneill uint32_t verb;
2197b8ad118Sjmcneill uint16_t corbrp;
2207b8ad118Sjmcneill int wp;
2217b8ad118Sjmcneill
2227b8ad118Sjmcneill /* Build command */
2237b8ad118Sjmcneill verb = (addr << 28) | (nid << 20) | (control << 8) | param;
2247b8ad118Sjmcneill
2257b8ad118Sjmcneill /* Fetch and update write pointer */
2267b8ad118Sjmcneill corbrp = hda_read2(sc, HDAUDIO_MMIO_CORBWP);
2277b8ad118Sjmcneill wp = (corbrp & 0xff) + 1;
2287b8ad118Sjmcneill if (wp >= (sc->sc_corb.dma_size / sizeof(*corb)))
2297b8ad118Sjmcneill wp = 0;
2307b8ad118Sjmcneill
2317b8ad118Sjmcneill /* Enqueue command */
2327b8ad118Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_corb.dma_map, 0,
2337b8ad118Sjmcneill sc->sc_corb.dma_size, BUS_DMASYNC_POSTWRITE);
2347b8ad118Sjmcneill corb[wp] = verb;
2357b8ad118Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_corb.dma_map, 0,
2367b8ad118Sjmcneill sc->sc_corb.dma_size, BUS_DMASYNC_PREWRITE);
2377b8ad118Sjmcneill
2387b8ad118Sjmcneill /* Commit updated write pointer */
2397b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBWP, wp);
2407b8ad118Sjmcneill }
2417b8ad118Sjmcneill
2427b8ad118Sjmcneill static void
hdaudio_rirb_unsol(struct hdaudio_softc * sc,struct rirb_entry * entry)2437b8ad118Sjmcneill hdaudio_rirb_unsol(struct hdaudio_softc *sc, struct rirb_entry *entry)
2447b8ad118Sjmcneill {
2457b8ad118Sjmcneill struct hdaudio_codec *co;
2467b8ad118Sjmcneill struct hdaudio_function_group *fg;
2477b8ad118Sjmcneill uint8_t codecid = RIRB_CODEC_ID(entry);
2487b8ad118Sjmcneill unsigned int i;
2497b8ad118Sjmcneill
2507b8ad118Sjmcneill if (codecid >= HDAUDIO_MAX_CODECS) {
2517b8ad118Sjmcneill hda_error(sc, "unsol: codec id 0x%02x out of range\n", codecid);
2527b8ad118Sjmcneill return;
2537b8ad118Sjmcneill }
2547b8ad118Sjmcneill co = &sc->sc_codec[codecid];
2557b8ad118Sjmcneill if (sc->sc_codec[codecid].co_valid == false) {
2567b8ad118Sjmcneill hda_error(sc, "unsol: codec id 0x%02x not valid\n", codecid);
2577b8ad118Sjmcneill return;
2587b8ad118Sjmcneill }
2597b8ad118Sjmcneill
2607b8ad118Sjmcneill for (i = 0; i < co->co_nfg; i++) {
2617b8ad118Sjmcneill fg = &co->co_fg[i];
2627b8ad118Sjmcneill if (fg->fg_device && fg->fg_unsol)
2637b8ad118Sjmcneill fg->fg_unsol(fg->fg_device, entry->resp);
2647b8ad118Sjmcneill }
2657b8ad118Sjmcneill }
2667b8ad118Sjmcneill
2677b8ad118Sjmcneill static uint32_t
hdaudio_rirb_dequeue(struct hdaudio_softc * sc,bool unsol)2687b8ad118Sjmcneill hdaudio_rirb_dequeue(struct hdaudio_softc *sc, bool unsol)
2697b8ad118Sjmcneill {
2707b8ad118Sjmcneill uint16_t rirbwp;
2717b8ad118Sjmcneill uint64_t *rirb = DMA_KERNADDR(&sc->sc_rirb);
2727b8ad118Sjmcneill struct rirb_entry entry;
2737b8ad118Sjmcneill int retry;
2747b8ad118Sjmcneill
2757b8ad118Sjmcneill for (;;) {
2767b8ad118Sjmcneill retry = HDAUDIO_RIRB_TIMEOUT;
2777b8ad118Sjmcneill
2787b8ad118Sjmcneill rirbwp = hda_read2(sc, HDAUDIO_MMIO_RIRBWP);
2797b8ad118Sjmcneill while (--retry > 0 && (rirbwp & 0xff) == sc->sc_rirbrp) {
2807b8ad118Sjmcneill if (unsol) {
2817b8ad118Sjmcneill /* don't wait for more unsol events */
2827b8ad118Sjmcneill hda_trace(sc, "unsol: rirb empty\n");
2837b8ad118Sjmcneill return 0xffffffff;
2847b8ad118Sjmcneill }
2857b8ad118Sjmcneill hda_delay(10);
2867b8ad118Sjmcneill rirbwp = hda_read2(sc, HDAUDIO_MMIO_RIRBWP);
2877b8ad118Sjmcneill }
2887b8ad118Sjmcneill if (retry == 0) {
2897b8ad118Sjmcneill hda_error(sc, "RIRB timeout\n");
2907b8ad118Sjmcneill return 0xffffffff;
2917b8ad118Sjmcneill }
2927b8ad118Sjmcneill
2937b8ad118Sjmcneill sc->sc_rirbrp++;
2947b8ad118Sjmcneill if (sc->sc_rirbrp >= (sc->sc_rirb.dma_size / sizeof(*rirb)))
2957b8ad118Sjmcneill sc->sc_rirbrp = 0;
2967b8ad118Sjmcneill
2977b8ad118Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_rirb.dma_map, 0,
2987b8ad118Sjmcneill sc->sc_rirb.dma_size, BUS_DMASYNC_POSTREAD);
2997b8ad118Sjmcneill entry = *(struct rirb_entry *)&rirb[sc->sc_rirbrp];
3007b8ad118Sjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_rirb.dma_map, 0,
3017b8ad118Sjmcneill sc->sc_rirb.dma_size, BUS_DMASYNC_PREREAD);
3027b8ad118Sjmcneill
3037b8ad118Sjmcneill hda_trace(sc, "%s: response %08X %08X\n",
3047b8ad118Sjmcneill unsol ? "unsol" : "cmd ",
3057b8ad118Sjmcneill entry.resp, entry.resp_ex);
3067b8ad118Sjmcneill
3077b8ad118Sjmcneill if (RIRB_UNSOL(&entry)) {
3087b8ad118Sjmcneill hdaudio_rirb_unsol(sc, &entry);
3097b8ad118Sjmcneill continue;
3107b8ad118Sjmcneill }
3117b8ad118Sjmcneill
3127b8ad118Sjmcneill return entry.resp;
3137b8ad118Sjmcneill }
3147b8ad118Sjmcneill }
3157b8ad118Sjmcneill
3167b8ad118Sjmcneill uint32_t
hdaudio_command(struct hdaudio_codec * co,int nid,uint32_t control,uint32_t param)3177b8ad118Sjmcneill hdaudio_command(struct hdaudio_codec *co, int nid, uint32_t control,
3187b8ad118Sjmcneill uint32_t param)
3197b8ad118Sjmcneill {
3207b8ad118Sjmcneill uint32_t result;
3217b8ad118Sjmcneill struct hdaudio_softc *sc = co->co_host;
3227b8ad118Sjmcneill mutex_enter(&sc->sc_corb_mtx);
3237b8ad118Sjmcneill result = hdaudio_command_unlocked(co, nid, control, param);
3247b8ad118Sjmcneill mutex_exit(&sc->sc_corb_mtx);
3257b8ad118Sjmcneill return result;
3267b8ad118Sjmcneill }
3277b8ad118Sjmcneill
3287b8ad118Sjmcneill uint32_t
hdaudio_command_unlocked(struct hdaudio_codec * co,int nid,uint32_t control,uint32_t param)3297b8ad118Sjmcneill hdaudio_command_unlocked(struct hdaudio_codec *co, int nid, uint32_t control,
3307b8ad118Sjmcneill uint32_t param)
3317b8ad118Sjmcneill {
3327b8ad118Sjmcneill struct hdaudio_softc *sc = co->co_host;
3337b8ad118Sjmcneill uint32_t result;
3347b8ad118Sjmcneill
3357b8ad118Sjmcneill hda_trace(sc, "cmd : request %08X %08X (%02X)\n",
3367b8ad118Sjmcneill control, param, nid);
3377b8ad118Sjmcneill hdaudio_corb_enqueue(sc, co->co_addr, nid, control, param);
3387b8ad118Sjmcneill result = hdaudio_rirb_dequeue(sc, false);
3397b8ad118Sjmcneill
340fbb8195bSjmcneill /* Clear response interrupt status */
341fbb8195bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS, hda_read1(sc, HDAUDIO_MMIO_RIRBSTS));
342fbb8195bSjmcneill
3437b8ad118Sjmcneill return result;
3447b8ad118Sjmcneill }
3457b8ad118Sjmcneill
3467b8ad118Sjmcneill static int
hdaudio_corb_setsize(struct hdaudio_softc * sc)3477b8ad118Sjmcneill hdaudio_corb_setsize(struct hdaudio_softc *sc)
3487b8ad118Sjmcneill {
3497b8ad118Sjmcneill uint8_t corbsize;
3507b8ad118Sjmcneill bus_size_t bufsize = 0;
3517b8ad118Sjmcneill
3527b8ad118Sjmcneill /*
3537b8ad118Sjmcneill * The size of the CORB is programmable to 2, 16, or 256 entries
3547b8ad118Sjmcneill * by using the CORBSIZE register. Choose a size based on the
3557b8ad118Sjmcneill * controller capabilities, preferring a larger size when possible.
3567b8ad118Sjmcneill */
3577b8ad118Sjmcneill corbsize = hda_read1(sc, HDAUDIO_MMIO_CORBSIZE);
3587b8ad118Sjmcneill corbsize &= ~0x3;
3597b8ad118Sjmcneill if ((corbsize >> 4) & 0x4) {
3607b8ad118Sjmcneill corbsize |= 0x2;
3617b8ad118Sjmcneill bufsize = 1024;
3627b8ad118Sjmcneill } else if ((corbsize >> 4) & 0x2) {
3637b8ad118Sjmcneill corbsize |= 0x1;
3647b8ad118Sjmcneill bufsize = 64;
3657b8ad118Sjmcneill } else if ((corbsize >> 4) & 0x1) {
3667b8ad118Sjmcneill corbsize |= 0x0;
3677b8ad118Sjmcneill bufsize = 8;
3687b8ad118Sjmcneill } else {
3697b8ad118Sjmcneill hda_error(sc, "couldn't configure CORB size\n");
3707b8ad118Sjmcneill return ENXIO;
3717b8ad118Sjmcneill }
3727b8ad118Sjmcneill
3737b8ad118Sjmcneill #if defined(HDAUDIO_DEBUG)
3747b8ad118Sjmcneill hda_print(sc, "using %d byte CORB (cap %X)\n",
3757b8ad118Sjmcneill (int)bufsize, corbsize >> 4);
3767b8ad118Sjmcneill #endif
3777b8ad118Sjmcneill
3787b8ad118Sjmcneill sc->sc_corb.dma_size = bufsize;
3797b8ad118Sjmcneill sc->sc_corb.dma_sizereg = corbsize;
3807b8ad118Sjmcneill
3817b8ad118Sjmcneill return 0;
3827b8ad118Sjmcneill }
3837b8ad118Sjmcneill
3847b8ad118Sjmcneill static int
hdaudio_corb_config(struct hdaudio_softc * sc)3857b8ad118Sjmcneill hdaudio_corb_config(struct hdaudio_softc *sc)
3867b8ad118Sjmcneill {
3877b8ad118Sjmcneill uint32_t corbubase, corblbase;
3887b8ad118Sjmcneill uint16_t corbrp;
3897b8ad118Sjmcneill int retry = HDAUDIO_CORB_TIMEOUT;
3907b8ad118Sjmcneill
3917b8ad118Sjmcneill /* Program command buffer base address and size */
3927b8ad118Sjmcneill corblbase = (uint32_t)DMA_DMAADDR(&sc->sc_corb);
3937b8ad118Sjmcneill corbubase = (uint32_t)(((uint64_t)DMA_DMAADDR(&sc->sc_corb)) >> 32);
3947b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_CORBLBASE, corblbase);
3957b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_CORBUBASE, corbubase);
3967b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_CORBSIZE, sc->sc_corb.dma_sizereg);
3977b8ad118Sjmcneill
3987b8ad118Sjmcneill /* Clear the read and write pointers */
3997b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBRP, HDAUDIO_CORBRP_RP_RESET);
4007b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBRP, 0);
4017b8ad118Sjmcneill do {
4027b8ad118Sjmcneill hda_delay(10);
4037b8ad118Sjmcneill corbrp = hda_read2(sc, HDAUDIO_MMIO_CORBRP);
4047b8ad118Sjmcneill } while (--retry > 0 && (corbrp & HDAUDIO_CORBRP_RP_RESET) != 0);
4057b8ad118Sjmcneill if (retry == 0) {
4067b8ad118Sjmcneill hda_error(sc, "timeout resetting CORB\n");
4077b8ad118Sjmcneill return ETIME;
4087b8ad118Sjmcneill }
4097b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBWP, 0);
4107b8ad118Sjmcneill
4117b8ad118Sjmcneill return 0;
4127b8ad118Sjmcneill }
4137b8ad118Sjmcneill
4147b8ad118Sjmcneill static int
hdaudio_corb_stop(struct hdaudio_softc * sc)4157b8ad118Sjmcneill hdaudio_corb_stop(struct hdaudio_softc *sc)
4167b8ad118Sjmcneill {
4177b8ad118Sjmcneill uint8_t corbctl;
4187b8ad118Sjmcneill int retry = HDAUDIO_CORB_TIMEOUT;
4197b8ad118Sjmcneill
4207b8ad118Sjmcneill /* Stop the CORB if necessary */
4217b8ad118Sjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4227b8ad118Sjmcneill if (corbctl & HDAUDIO_CORBCTL_RUN) {
4237b8ad118Sjmcneill corbctl &= ~HDAUDIO_CORBCTL_RUN;
42467d13b00Sjmcneill hda_write1(sc, HDAUDIO_MMIO_CORBCTL, corbctl);
4257b8ad118Sjmcneill do {
4267b8ad118Sjmcneill hda_delay(10);
42767d13b00Sjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4287b8ad118Sjmcneill } while (--retry > 0 && (corbctl & HDAUDIO_CORBCTL_RUN) != 0);
4297b8ad118Sjmcneill if (retry == 0) {
4307b8ad118Sjmcneill hda_error(sc, "timeout stopping CORB\n");
4317b8ad118Sjmcneill return ETIME;
4327b8ad118Sjmcneill }
4337b8ad118Sjmcneill }
4347b8ad118Sjmcneill
4357b8ad118Sjmcneill return 0;
4367b8ad118Sjmcneill }
4377b8ad118Sjmcneill
4387b8ad118Sjmcneill static int
hdaudio_corb_start(struct hdaudio_softc * sc)4397b8ad118Sjmcneill hdaudio_corb_start(struct hdaudio_softc *sc)
4407b8ad118Sjmcneill {
4417b8ad118Sjmcneill uint8_t corbctl;
4427b8ad118Sjmcneill int retry = HDAUDIO_CORB_TIMEOUT;
4437b8ad118Sjmcneill
4447b8ad118Sjmcneill /* Start the CORB if necessary */
4457b8ad118Sjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4467b8ad118Sjmcneill if ((corbctl & HDAUDIO_CORBCTL_RUN) == 0) {
4477b8ad118Sjmcneill corbctl |= HDAUDIO_CORBCTL_RUN;
44867d13b00Sjmcneill hda_write1(sc, HDAUDIO_MMIO_CORBCTL, corbctl);
4497b8ad118Sjmcneill do {
4507b8ad118Sjmcneill hda_delay(10);
45167d13b00Sjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4527b8ad118Sjmcneill } while (--retry > 0 && (corbctl & HDAUDIO_CORBCTL_RUN) == 0);
4537b8ad118Sjmcneill if (retry == 0) {
4547b8ad118Sjmcneill hda_error(sc, "timeout starting CORB\n");
4557b8ad118Sjmcneill return ETIME;
4567b8ad118Sjmcneill }
4577b8ad118Sjmcneill }
4587b8ad118Sjmcneill
4597b8ad118Sjmcneill return 0;
4607b8ad118Sjmcneill }
4617b8ad118Sjmcneill
4627b8ad118Sjmcneill static int
hdaudio_rirb_stop(struct hdaudio_softc * sc)4637b8ad118Sjmcneill hdaudio_rirb_stop(struct hdaudio_softc *sc)
4647b8ad118Sjmcneill {
4657b8ad118Sjmcneill uint8_t rirbctl;
4667b8ad118Sjmcneill int retry = HDAUDIO_RIRB_TIMEOUT;
4677b8ad118Sjmcneill
4687b8ad118Sjmcneill /* Stop the RIRB if necessary */
4697b8ad118Sjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
4707b8ad118Sjmcneill if (rirbctl & (HDAUDIO_RIRBCTL_RUN|HDAUDIO_RIRBCTL_ROI_EN)) {
4717b8ad118Sjmcneill rirbctl &= ~HDAUDIO_RIRBCTL_RUN;
4727b8ad118Sjmcneill rirbctl &= ~HDAUDIO_RIRBCTL_ROI_EN;
4737b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBCTL, rirbctl);
4747b8ad118Sjmcneill do {
4757b8ad118Sjmcneill hda_delay(10);
4767b8ad118Sjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
4777b8ad118Sjmcneill } while (--retry > 0 && (rirbctl & HDAUDIO_RIRBCTL_RUN) != 0);
4787b8ad118Sjmcneill if (retry == 0) {
4797b8ad118Sjmcneill hda_error(sc, "timeout stopping RIRB\n");
4807b8ad118Sjmcneill return ETIME;
4817b8ad118Sjmcneill }
4827b8ad118Sjmcneill }
4837b8ad118Sjmcneill
4847b8ad118Sjmcneill return 0;
4857b8ad118Sjmcneill }
4867b8ad118Sjmcneill
4877b8ad118Sjmcneill static int
hdaudio_rirb_start(struct hdaudio_softc * sc)4887b8ad118Sjmcneill hdaudio_rirb_start(struct hdaudio_softc *sc)
4897b8ad118Sjmcneill {
4907b8ad118Sjmcneill uint8_t rirbctl;
4917b8ad118Sjmcneill int retry = HDAUDIO_RIRB_TIMEOUT;
4927b8ad118Sjmcneill
493fbb8195bSjmcneill /* Set the RIRB interrupt count */
494fbb8195bSjmcneill hda_write2(sc, HDAUDIO_MMIO_RINTCNT, 1);
495fbb8195bSjmcneill
496fbb8195bSjmcneill /* Start the RIRB */
4977b8ad118Sjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
4987b8ad118Sjmcneill rirbctl |= HDAUDIO_RIRBCTL_RUN;
4997b8ad118Sjmcneill rirbctl |= HDAUDIO_RIRBCTL_INT_EN;
5007b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBCTL, rirbctl);
5017b8ad118Sjmcneill do {
5027b8ad118Sjmcneill hda_delay(10);
5037b8ad118Sjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
5047b8ad118Sjmcneill } while (--retry > 0 && (rirbctl & HDAUDIO_RIRBCTL_RUN) == 0);
5057b8ad118Sjmcneill if (retry == 0) {
5067b8ad118Sjmcneill hda_error(sc, "timeout starting RIRB\n");
5077b8ad118Sjmcneill return ETIME;
5087b8ad118Sjmcneill }
5097b8ad118Sjmcneill
5107b8ad118Sjmcneill return 0;
5117b8ad118Sjmcneill }
5127b8ad118Sjmcneill
5137b8ad118Sjmcneill static int
hdaudio_rirb_setsize(struct hdaudio_softc * sc)5147b8ad118Sjmcneill hdaudio_rirb_setsize(struct hdaudio_softc *sc)
5157b8ad118Sjmcneill {
5167b8ad118Sjmcneill uint8_t rirbsize;
5177b8ad118Sjmcneill bus_size_t bufsize = 0;
5187b8ad118Sjmcneill
5197b8ad118Sjmcneill /*
5207b8ad118Sjmcneill * The size of the RIRB is programmable to 2, 16, or 256 entries
5217b8ad118Sjmcneill * by using the RIRBSIZE register. Choose a size based on the
5227b8ad118Sjmcneill * controller capabilities, preferring a larger size when possible.
5237b8ad118Sjmcneill */
5247b8ad118Sjmcneill rirbsize = hda_read1(sc, HDAUDIO_MMIO_RIRBSIZE);
5257b8ad118Sjmcneill rirbsize &= ~0x3;
5267b8ad118Sjmcneill if ((rirbsize >> 4) & 0x4) {
5277b8ad118Sjmcneill rirbsize |= 0x2;
5287b8ad118Sjmcneill bufsize = 2048;
5297b8ad118Sjmcneill } else if ((rirbsize >> 4) & 0x2) {
5307b8ad118Sjmcneill rirbsize |= 0x1;
5317b8ad118Sjmcneill bufsize = 128;
5327b8ad118Sjmcneill } else if ((rirbsize >> 4) & 0x1) {
5337b8ad118Sjmcneill rirbsize |= 0x0;
5347b8ad118Sjmcneill bufsize = 16;
5357b8ad118Sjmcneill } else {
5367b8ad118Sjmcneill hda_error(sc, "couldn't configure RIRB size\n");
5377b8ad118Sjmcneill return ENXIO;
5387b8ad118Sjmcneill }
5397b8ad118Sjmcneill
5407b8ad118Sjmcneill #if defined(HDAUDIO_DEBUG)
5417b8ad118Sjmcneill hda_print(sc, "using %d byte RIRB (cap %X)\n",
5427b8ad118Sjmcneill (int)bufsize, rirbsize >> 4);
5437b8ad118Sjmcneill #endif
5447b8ad118Sjmcneill
5457b8ad118Sjmcneill sc->sc_rirb.dma_size = bufsize;
5467b8ad118Sjmcneill sc->sc_rirb.dma_sizereg = rirbsize;
5477b8ad118Sjmcneill
5487b8ad118Sjmcneill return 0;
5497b8ad118Sjmcneill }
5507b8ad118Sjmcneill
5517b8ad118Sjmcneill static int
hdaudio_rirb_config(struct hdaudio_softc * sc)5527b8ad118Sjmcneill hdaudio_rirb_config(struct hdaudio_softc *sc)
5537b8ad118Sjmcneill {
5547b8ad118Sjmcneill uint32_t rirbubase, rirblbase;
5557b8ad118Sjmcneill
5567b8ad118Sjmcneill /* Program command buffer base address and size */
5577b8ad118Sjmcneill rirblbase = (uint32_t)DMA_DMAADDR(&sc->sc_rirb);
5587b8ad118Sjmcneill rirbubase = (uint32_t)(((uint64_t)DMA_DMAADDR(&sc->sc_rirb)) >> 32);
5597b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_RIRBLBASE, rirblbase);
5607b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_RIRBUBASE, rirbubase);
5617b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSIZE, sc->sc_rirb.dma_sizereg);
5627b8ad118Sjmcneill
5637b8ad118Sjmcneill /* Clear the write pointer */
5647b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_RIRBWP, HDAUDIO_RIRBWP_WP_RESET);
5657b8ad118Sjmcneill sc->sc_rirbrp = 0;
5667b8ad118Sjmcneill
5677b8ad118Sjmcneill return 0;
5687b8ad118Sjmcneill }
5697b8ad118Sjmcneill
5707b8ad118Sjmcneill static int
hdaudio_reset(struct hdaudio_softc * sc)5717b8ad118Sjmcneill hdaudio_reset(struct hdaudio_softc *sc)
5727b8ad118Sjmcneill {
5737b8ad118Sjmcneill int retry = HDAUDIO_RESET_TIMEOUT;
5747b8ad118Sjmcneill uint32_t gctl;
5757b8ad118Sjmcneill int err;
5767b8ad118Sjmcneill
5777b8ad118Sjmcneill if ((err = hdaudio_rirb_stop(sc)) != 0) {
5787b8ad118Sjmcneill hda_error(sc, "couldn't reset because RIRB is busy\n");
5797b8ad118Sjmcneill return err;
5807b8ad118Sjmcneill }
5817b8ad118Sjmcneill if ((err = hdaudio_corb_stop(sc)) != 0) {
5827b8ad118Sjmcneill hda_error(sc, "couldn't reset because CORB is busy\n");
5837b8ad118Sjmcneill return err;
5847b8ad118Sjmcneill }
5857b8ad118Sjmcneill
5867b8ad118Sjmcneill /* Disable wake events */
5877b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_WAKEEN, 0);
5887b8ad118Sjmcneill
5897b8ad118Sjmcneill /* Disable interrupts */
5907b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, 0);
5917b8ad118Sjmcneill
5927b8ad118Sjmcneill /* Clear state change status register */
5937b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS,
5947b8ad118Sjmcneill hda_read2(sc, HDAUDIO_MMIO_STATESTS));
5957b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS,
5967b8ad118Sjmcneill hda_read1(sc, HDAUDIO_MMIO_RIRBSTS));
5977b8ad118Sjmcneill
5983935fef1Sjmcneill /* Put the controller into reset state */
5997b8ad118Sjmcneill gctl = hda_read4(sc, HDAUDIO_MMIO_GCTL);
6007b8ad118Sjmcneill gctl &= ~HDAUDIO_GCTL_CRST;
6017b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_GCTL, gctl);
6027b8ad118Sjmcneill do {
6037b8ad118Sjmcneill hda_delay(10);
6047b8ad118Sjmcneill gctl = hda_read4(sc, HDAUDIO_MMIO_GCTL);
6057b8ad118Sjmcneill } while (--retry > 0 && (gctl & HDAUDIO_GCTL_CRST) != 0);
6067b8ad118Sjmcneill if (retry == 0) {
6077b8ad118Sjmcneill hda_error(sc, "timeout entering reset state\n");
6087b8ad118Sjmcneill return ETIME;
6097b8ad118Sjmcneill }
6103935fef1Sjmcneill
6113935fef1Sjmcneill hda_delay(1000);
6127b8ad118Sjmcneill
6137b8ad118Sjmcneill /* Now the controller is in reset state, so bring it out */
6147b8ad118Sjmcneill retry = HDAUDIO_RESET_TIMEOUT;
6157b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_GCTL, gctl | HDAUDIO_GCTL_CRST);
6167b8ad118Sjmcneill do {
6177b8ad118Sjmcneill hda_delay(10);
6187b8ad118Sjmcneill gctl = hda_read4(sc, HDAUDIO_MMIO_GCTL);
6197b8ad118Sjmcneill } while (--retry > 0 && (gctl & HDAUDIO_GCTL_CRST) == 0);
6207b8ad118Sjmcneill if (retry == 0) {
6217b8ad118Sjmcneill hda_error(sc, "timeout leaving reset state\n");
6227b8ad118Sjmcneill return ETIME;
6237b8ad118Sjmcneill }
6247b8ad118Sjmcneill
6253935fef1Sjmcneill hda_delay(2000);
6263935fef1Sjmcneill
6277b8ad118Sjmcneill /* Accept unsolicited responses */
6287b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_GCTL, gctl | HDAUDIO_GCTL_UNSOL_EN);
6297b8ad118Sjmcneill
6307b8ad118Sjmcneill return 0;
6317b8ad118Sjmcneill }
6327b8ad118Sjmcneill
6337b8ad118Sjmcneill static void
hdaudio_intr_enable(struct hdaudio_softc * sc)6347b8ad118Sjmcneill hdaudio_intr_enable(struct hdaudio_softc *sc)
6357b8ad118Sjmcneill {
6367b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS,
6377b8ad118Sjmcneill hda_read4(sc, HDAUDIO_MMIO_INTSTS));
6387b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL,
6397b8ad118Sjmcneill HDAUDIO_INTCTL_GIE | HDAUDIO_INTCTL_CIE);
6407b8ad118Sjmcneill }
6417b8ad118Sjmcneill
6427b8ad118Sjmcneill static void
hdaudio_intr_disable(struct hdaudio_softc * sc)6437b8ad118Sjmcneill hdaudio_intr_disable(struct hdaudio_softc *sc)
6447b8ad118Sjmcneill {
6457b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, 0);
6467b8ad118Sjmcneill }
6477b8ad118Sjmcneill
6487b8ad118Sjmcneill static int
hdaudio_config_print(void * opaque,const char * pnp)6497b8ad118Sjmcneill hdaudio_config_print(void *opaque, const char *pnp)
6507b8ad118Sjmcneill {
6517b8ad118Sjmcneill prop_dictionary_t dict = opaque;
6527b8ad118Sjmcneill uint8_t fgtype, nid;
6537b8ad118Sjmcneill uint16_t vendor, product;
6547b8ad118Sjmcneill const char *type = "unknown";
6557b8ad118Sjmcneill
6567b8ad118Sjmcneill prop_dictionary_get_uint8(dict, "function-group-type", &fgtype);
6577b8ad118Sjmcneill prop_dictionary_get_uint8(dict, "node-id", &nid);
6587b8ad118Sjmcneill prop_dictionary_get_uint16(dict, "vendor-id", &vendor);
6597b8ad118Sjmcneill prop_dictionary_get_uint16(dict, "product-id", &product);
6607b8ad118Sjmcneill if (pnp) {
6617b8ad118Sjmcneill if (fgtype == HDAUDIO_GROUP_TYPE_AFG)
6627b8ad118Sjmcneill type = "hdafg";
6637b8ad118Sjmcneill else if (fgtype == HDAUDIO_GROUP_TYPE_VSM_FG)
6647b8ad118Sjmcneill type = "hdvsmfg";
6657b8ad118Sjmcneill
6667b8ad118Sjmcneill aprint_normal("%s at %s", type, pnp);
6677b8ad118Sjmcneill }
6687b8ad118Sjmcneill aprint_debug(" vendor 0x%04X product 0x%04X nid 0x%02X",
6697b8ad118Sjmcneill vendor, product, nid);
6707b8ad118Sjmcneill
6717b8ad118Sjmcneill return UNCONF;
6727b8ad118Sjmcneill }
6737b8ad118Sjmcneill
6747b8ad118Sjmcneill static void
hdaudio_attach_fg(struct hdaudio_function_group * fg,prop_array_t config)6757b8ad118Sjmcneill hdaudio_attach_fg(struct hdaudio_function_group *fg, prop_array_t config)
6767b8ad118Sjmcneill {
6777b8ad118Sjmcneill struct hdaudio_codec *co = fg->fg_codec;
6787b8ad118Sjmcneill struct hdaudio_softc *sc = co->co_host;
6797b8ad118Sjmcneill prop_dictionary_t args = prop_dictionary_create();
6807b8ad118Sjmcneill uint64_t fgptr = (vaddr_t)fg;
6817b8ad118Sjmcneill int locs[1];
6827b8ad118Sjmcneill
6837b8ad118Sjmcneill prop_dictionary_set_uint8(args, "function-group-type", fg->fg_type);
6847b8ad118Sjmcneill prop_dictionary_set_uint64(args, "function-group", fgptr);
6857b8ad118Sjmcneill prop_dictionary_set_uint8(args, "node-id", fg->fg_nid);
6867b8ad118Sjmcneill prop_dictionary_set_uint16(args, "vendor-id", fg->fg_vendor);
6877b8ad118Sjmcneill prop_dictionary_set_uint16(args, "product-id", fg->fg_product);
6887b8ad118Sjmcneill if (config)
6897b8ad118Sjmcneill prop_dictionary_set(args, "pin-config", config);
6907b8ad118Sjmcneill
6917b8ad118Sjmcneill locs[0] = fg->fg_nid;
6927b8ad118Sjmcneill
6933bee0c11Sthorpej fg->fg_device = config_found(sc->sc_dev, args, hdaudio_config_print,
694beecddb6Sthorpej CFARGS(.submatch = config_stdsubmatch,
695beecddb6Sthorpej .locators = locs));
6967b8ad118Sjmcneill
6977b8ad118Sjmcneill prop_object_release(args);
6987b8ad118Sjmcneill }
6997b8ad118Sjmcneill
7007b8ad118Sjmcneill static void
hdaudio_codec_attach(struct hdaudio_codec * co)7017b8ad118Sjmcneill hdaudio_codec_attach(struct hdaudio_codec *co)
7027b8ad118Sjmcneill {
703776dd837Sjmcneill struct hdaudio_softc *sc = co->co_host;
7047b8ad118Sjmcneill struct hdaudio_function_group *fg;
7057b8ad118Sjmcneill uint32_t vid, snc, fgrp;
7067b8ad118Sjmcneill int starting_node, num_nodes, nid;
7077b8ad118Sjmcneill
7087b8ad118Sjmcneill if (co->co_valid == false)
7097b8ad118Sjmcneill return;
7107b8ad118Sjmcneill
7117b8ad118Sjmcneill vid = hdaudio_command(co, 0, CORB_GET_PARAMETER, COP_VENDOR_ID);
7127b8ad118Sjmcneill snc = hdaudio_command(co, 0, CORB_GET_PARAMETER,
7137b8ad118Sjmcneill COP_SUBORDINATE_NODE_COUNT);
7147b8ad118Sjmcneill
7157b8ad118Sjmcneill /* make sure the vendor and product IDs are valid */
7167b8ad118Sjmcneill if (vid == 0xffffffff || vid == 0x00000000)
7177b8ad118Sjmcneill return;
7187b8ad118Sjmcneill
7197b8ad118Sjmcneill #ifdef HDAUDIO_DEBUG
7207b8ad118Sjmcneill uint32_t rid = hdaudio_command(co, 0, CORB_GET_PARAMETER,
7217b8ad118Sjmcneill COP_REVISION_ID);
7227b8ad118Sjmcneill hda_print(sc, "Codec%02X: %04X:%04X HDA %d.%d rev %d stepping %d\n",
7237b8ad118Sjmcneill co->co_addr, vid >> 16, vid & 0xffff,
7247b8ad118Sjmcneill (rid >> 20) & 0xf, (rid >> 16) & 0xf,
7257b8ad118Sjmcneill (rid >> 8) & 0xff, rid & 0xff);
7267b8ad118Sjmcneill #endif
7277b8ad118Sjmcneill starting_node = (snc >> 16) & 0xff;
7287b8ad118Sjmcneill num_nodes = snc & 0xff;
7297b8ad118Sjmcneill
730776dd837Sjmcneill /*
731776dd837Sjmcneill * If the total number of nodes is 0, there's nothing we can do.
732776dd837Sjmcneill * This shouldn't happen, so complain about it.
733776dd837Sjmcneill */
734776dd837Sjmcneill if (num_nodes == 0) {
735776dd837Sjmcneill hda_error(sc, "Codec%02X: No subordinate nodes found (%08x)\n",
736776dd837Sjmcneill co->co_addr, snc);
737776dd837Sjmcneill return;
738776dd837Sjmcneill }
739776dd837Sjmcneill
7407b8ad118Sjmcneill co->co_nfg = num_nodes;
7417b8ad118Sjmcneill co->co_fg = kmem_zalloc(co->co_nfg * sizeof(*co->co_fg), KM_SLEEP);
7427b8ad118Sjmcneill
7437b8ad118Sjmcneill for (nid = starting_node; nid < starting_node + num_nodes; nid++) {
7447b8ad118Sjmcneill fg = &co->co_fg[nid - starting_node];
7457b8ad118Sjmcneill fg->fg_codec = co;
7467b8ad118Sjmcneill fg->fg_nid = nid;
7477b8ad118Sjmcneill fg->fg_vendor = vid >> 16;
7487b8ad118Sjmcneill fg->fg_product = vid & 0xffff;
7497b8ad118Sjmcneill
7507b8ad118Sjmcneill fgrp = hdaudio_command(co, nid, CORB_GET_PARAMETER,
7517b8ad118Sjmcneill COP_FUNCTION_GROUP_TYPE);
7527b8ad118Sjmcneill switch (fgrp & 0xff) {
7537b8ad118Sjmcneill case 0x01: /* Audio Function Group */
7547b8ad118Sjmcneill fg->fg_type = HDAUDIO_GROUP_TYPE_AFG;
7557b8ad118Sjmcneill break;
7567b8ad118Sjmcneill case 0x02: /* Vendor Specific Modem Function Group */
7577b8ad118Sjmcneill fg->fg_type = HDAUDIO_GROUP_TYPE_VSM_FG;
7587b8ad118Sjmcneill break;
7597b8ad118Sjmcneill default:
7607b8ad118Sjmcneill /* Function group type not supported */
7617b8ad118Sjmcneill fg->fg_type = HDAUDIO_GROUP_TYPE_UNKNOWN;
7627b8ad118Sjmcneill break;
7637b8ad118Sjmcneill }
7647b8ad118Sjmcneill hdaudio_attach_fg(fg, NULL);
7657b8ad118Sjmcneill }
7667b8ad118Sjmcneill }
7677b8ad118Sjmcneill
7687b8ad118Sjmcneill int
hdaudio_stream_tag(struct hdaudio_stream * st)7697b8ad118Sjmcneill hdaudio_stream_tag(struct hdaudio_stream *st)
7707b8ad118Sjmcneill {
7717b8ad118Sjmcneill int ret = 0;
7727b8ad118Sjmcneill
7737b8ad118Sjmcneill switch (st->st_type) {
7747b8ad118Sjmcneill case HDAUDIO_STREAM_ISS:
7757b8ad118Sjmcneill ret = 1;
7767b8ad118Sjmcneill break;
7777b8ad118Sjmcneill case HDAUDIO_STREAM_OSS:
7787b8ad118Sjmcneill ret = 2;
7797b8ad118Sjmcneill break;
7807b8ad118Sjmcneill case HDAUDIO_STREAM_BSS:
7817b8ad118Sjmcneill ret = 3;
7827b8ad118Sjmcneill break;
7837b8ad118Sjmcneill }
7847b8ad118Sjmcneill
7857b8ad118Sjmcneill return ret;
7867b8ad118Sjmcneill }
7877b8ad118Sjmcneill
7887b8ad118Sjmcneill int
hdaudio_attach(device_t dev,struct hdaudio_softc * sc)7897b8ad118Sjmcneill hdaudio_attach(device_t dev, struct hdaudio_softc *sc)
7907b8ad118Sjmcneill {
7917b8ad118Sjmcneill int err, i;
7927b8ad118Sjmcneill
7937b8ad118Sjmcneill KASSERT(sc->sc_memvalid == true);
7947b8ad118Sjmcneill
7957b8ad118Sjmcneill sc->sc_dev = dev;
7967b8ad118Sjmcneill mutex_init(&sc->sc_corb_mtx, MUTEX_DEFAULT, IPL_AUDIO);
7977b8ad118Sjmcneill mutex_init(&sc->sc_stream_mtx, MUTEX_DEFAULT, IPL_AUDIO);
7987b8ad118Sjmcneill
7997b8ad118Sjmcneill /*
8007b8ad118Sjmcneill * Put the controller into a known state by entering and leaving
8017b8ad118Sjmcneill * CRST as necessary.
8027b8ad118Sjmcneill */
8037b8ad118Sjmcneill if ((err = hdaudio_reset(sc)) != 0)
8047b8ad118Sjmcneill goto fail;
8057b8ad118Sjmcneill
8067b8ad118Sjmcneill /*
8077b8ad118Sjmcneill * From the spec:
8087b8ad118Sjmcneill *
8097b8ad118Sjmcneill * Must wait 250us after reading CRST as a 1 before assuming that
8107b8ad118Sjmcneill * codecs have all made status change requests and have been
8117b8ad118Sjmcneill * registered by the controller.
8127b8ad118Sjmcneill *
8137b8ad118Sjmcneill * In reality, we need to wait longer than this.
8147b8ad118Sjmcneill */
8157b8ad118Sjmcneill hda_delay(HDAUDIO_CODEC_DELAY);
816e1474beeSjmcneill
817e1474beeSjmcneill /*
818e1474beeSjmcneill * Read device capabilities
819e1474beeSjmcneill */
820e1474beeSjmcneill hdaudio_init(sc);
821e1474beeSjmcneill
822e1474beeSjmcneill /*
823e1474beeSjmcneill * Detect codecs
824e1474beeSjmcneill */
8257b8ad118Sjmcneill if (hdaudio_codec_probe(sc) == 0) {
8267b8ad118Sjmcneill hda_error(sc, "no codecs found\n");
8277b8ad118Sjmcneill err = ENODEV;
8287b8ad118Sjmcneill goto fail;
8297b8ad118Sjmcneill }
8307b8ad118Sjmcneill
8317b8ad118Sjmcneill /*
8327b8ad118Sjmcneill * Ensure that the device is in a known state
8337b8ad118Sjmcneill */
8347b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS, HDAUDIO_STATESTS_SDIWAKE);
8357b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS,
8367b8ad118Sjmcneill HDAUDIO_RIRBSTS_RIRBOIS | HDAUDIO_RIRBSTS_RINTFL);
8377b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS,
8387b8ad118Sjmcneill hda_read4(sc, HDAUDIO_MMIO_INTSTS));
8397b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_DPLBASE, 0);
8407b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_DPUBASE, 0);
8417b8ad118Sjmcneill
8427b8ad118Sjmcneill /*
8437b8ad118Sjmcneill * Initialize the CORB. First negotiate a command buffer size,
8447b8ad118Sjmcneill * then allocate and configure it.
8457b8ad118Sjmcneill */
8467b8ad118Sjmcneill if ((err = hdaudio_corb_setsize(sc)) != 0)
8477b8ad118Sjmcneill goto fail;
8487b8ad118Sjmcneill if ((err = hdaudio_dma_alloc(sc, &sc->sc_corb, BUS_DMA_WRITE)) != 0)
8497b8ad118Sjmcneill goto fail;
8507b8ad118Sjmcneill if ((err = hdaudio_corb_config(sc)) != 0)
8517b8ad118Sjmcneill goto fail;
8527b8ad118Sjmcneill
8537b8ad118Sjmcneill /*
8547b8ad118Sjmcneill * Initialize the RIRB.
8557b8ad118Sjmcneill */
8567b8ad118Sjmcneill if ((err = hdaudio_rirb_setsize(sc)) != 0)
8577b8ad118Sjmcneill goto fail;
8587b8ad118Sjmcneill if ((err = hdaudio_dma_alloc(sc, &sc->sc_rirb, BUS_DMA_READ)) != 0)
8597b8ad118Sjmcneill goto fail;
8607b8ad118Sjmcneill if ((err = hdaudio_rirb_config(sc)) != 0)
8617b8ad118Sjmcneill goto fail;
8627b8ad118Sjmcneill
8637b8ad118Sjmcneill /*
8647b8ad118Sjmcneill * Start the CORB and RIRB
8657b8ad118Sjmcneill */
8667b8ad118Sjmcneill if ((err = hdaudio_corb_start(sc)) != 0)
8677b8ad118Sjmcneill goto fail;
8687b8ad118Sjmcneill if ((err = hdaudio_rirb_start(sc)) != 0)
8697b8ad118Sjmcneill goto fail;
8707b8ad118Sjmcneill
8717b8ad118Sjmcneill /*
8727b8ad118Sjmcneill * Identify and attach discovered codecs
8737b8ad118Sjmcneill */
8747b8ad118Sjmcneill for (i = 0; i < HDAUDIO_MAX_CODECS; i++)
8757b8ad118Sjmcneill hdaudio_codec_attach(&sc->sc_codec[i]);
8767b8ad118Sjmcneill
8777b8ad118Sjmcneill /*
8787b8ad118Sjmcneill * Enable interrupts
8797b8ad118Sjmcneill */
8807b8ad118Sjmcneill hdaudio_intr_enable(sc);
8817b8ad118Sjmcneill
8827b8ad118Sjmcneill fail:
8837b8ad118Sjmcneill if (err)
8847b8ad118Sjmcneill hda_error(sc, "device driver failed to attach\n");
8857b8ad118Sjmcneill return err;
8867b8ad118Sjmcneill }
8877b8ad118Sjmcneill
8887b8ad118Sjmcneill int
hdaudio_detach(struct hdaudio_softc * sc,int flags)8897b8ad118Sjmcneill hdaudio_detach(struct hdaudio_softc *sc, int flags)
8907b8ad118Sjmcneill {
8917b8ad118Sjmcneill int error;
8927b8ad118Sjmcneill
8937b8ad118Sjmcneill /* Disable interrupts */
8947b8ad118Sjmcneill hdaudio_intr_disable(sc);
8957b8ad118Sjmcneill
8967b8ad118Sjmcneill error = config_detach_children(sc->sc_dev, flags);
8977b8ad118Sjmcneill if (error != 0) {
8987b8ad118Sjmcneill hdaudio_intr_enable(sc);
8997b8ad118Sjmcneill return error;
9007b8ad118Sjmcneill }
9017b8ad118Sjmcneill
9027b8ad118Sjmcneill mutex_destroy(&sc->sc_corb_mtx);
9037b8ad118Sjmcneill mutex_destroy(&sc->sc_stream_mtx);
9047b8ad118Sjmcneill
9057b8ad118Sjmcneill hdaudio_dma_free(sc, &sc->sc_corb);
9067b8ad118Sjmcneill hdaudio_dma_free(sc, &sc->sc_rirb);
9077b8ad118Sjmcneill
9087b8ad118Sjmcneill return 0;
9097b8ad118Sjmcneill }
9107b8ad118Sjmcneill
9117b8ad118Sjmcneill bool
hdaudio_resume(struct hdaudio_softc * sc)9127b8ad118Sjmcneill hdaudio_resume(struct hdaudio_softc *sc)
9137b8ad118Sjmcneill {
9147b8ad118Sjmcneill if (hdaudio_reset(sc) != 0)
9157b8ad118Sjmcneill return false;
9167b8ad118Sjmcneill
9177b8ad118Sjmcneill hda_delay(HDAUDIO_CODEC_DELAY);
9187b8ad118Sjmcneill
9197b8ad118Sjmcneill /*
9207b8ad118Sjmcneill * Ensure that the device is in a known state
9217b8ad118Sjmcneill */
9227b8ad118Sjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS, HDAUDIO_STATESTS_SDIWAKE);
9237b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS,
9247b8ad118Sjmcneill HDAUDIO_RIRBSTS_RIRBOIS | HDAUDIO_RIRBSTS_RINTFL);
9257b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS,
9267b8ad118Sjmcneill hda_read4(sc, HDAUDIO_MMIO_INTSTS));
9277b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_DPLBASE, 0);
9287b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_DPUBASE, 0);
9297b8ad118Sjmcneill
9307b8ad118Sjmcneill if (hdaudio_corb_config(sc) != 0)
9317b8ad118Sjmcneill return false;
9327b8ad118Sjmcneill if (hdaudio_rirb_config(sc) != 0)
9337b8ad118Sjmcneill return false;
9347b8ad118Sjmcneill if (hdaudio_corb_start(sc) != 0)
9357b8ad118Sjmcneill return false;
9367b8ad118Sjmcneill if (hdaudio_rirb_start(sc) != 0)
9377b8ad118Sjmcneill return false;
9387b8ad118Sjmcneill
9397b8ad118Sjmcneill hdaudio_intr_enable(sc);
9407b8ad118Sjmcneill
9417b8ad118Sjmcneill return true;
9427b8ad118Sjmcneill }
9437b8ad118Sjmcneill
9447b8ad118Sjmcneill int
hdaudio_rescan(struct hdaudio_softc * sc,const char * ifattr,const int * locs)9457b8ad118Sjmcneill hdaudio_rescan(struct hdaudio_softc *sc, const char *ifattr, const int *locs)
9467b8ad118Sjmcneill {
9477b8ad118Sjmcneill struct hdaudio_codec *co;
9487b8ad118Sjmcneill struct hdaudio_function_group *fg;
9497b8ad118Sjmcneill unsigned int codec;
9507b8ad118Sjmcneill
9517b8ad118Sjmcneill for (codec = 0; codec < HDAUDIO_MAX_CODECS; codec++) {
9527b8ad118Sjmcneill co = &sc->sc_codec[codec];
9537b8ad118Sjmcneill fg = co->co_fg;
9547b8ad118Sjmcneill if (!co->co_valid || fg == NULL)
9557b8ad118Sjmcneill continue;
9567b8ad118Sjmcneill if (fg->fg_device)
9577b8ad118Sjmcneill continue;
9587b8ad118Sjmcneill hdaudio_attach_fg(fg, NULL);
9597b8ad118Sjmcneill }
9607b8ad118Sjmcneill
9617b8ad118Sjmcneill return 0;
9627b8ad118Sjmcneill }
9637b8ad118Sjmcneill
9647b8ad118Sjmcneill void
hdaudio_childdet(struct hdaudio_softc * sc,device_t child)9657b8ad118Sjmcneill hdaudio_childdet(struct hdaudio_softc *sc, device_t child)
9667b8ad118Sjmcneill {
9677b8ad118Sjmcneill struct hdaudio_codec *co;
9687b8ad118Sjmcneill struct hdaudio_function_group *fg;
9697b8ad118Sjmcneill unsigned int codec;
9707b8ad118Sjmcneill
9717b8ad118Sjmcneill for (codec = 0; codec < HDAUDIO_MAX_CODECS; codec++) {
9727b8ad118Sjmcneill co = &sc->sc_codec[codec];
9737b8ad118Sjmcneill fg = co->co_fg;
9747b8ad118Sjmcneill if (!co->co_valid || fg == NULL)
9757b8ad118Sjmcneill continue;
9767b8ad118Sjmcneill if (fg->fg_device == child)
9777b8ad118Sjmcneill fg->fg_device = NULL;
9787b8ad118Sjmcneill }
9797b8ad118Sjmcneill }
9807b8ad118Sjmcneill
9817b8ad118Sjmcneill int
hdaudio_intr(struct hdaudio_softc * sc)9827b8ad118Sjmcneill hdaudio_intr(struct hdaudio_softc *sc)
9837b8ad118Sjmcneill {
9847b8ad118Sjmcneill struct hdaudio_stream *st;
9857b8ad118Sjmcneill uint32_t intsts, stream_mask;
9867b8ad118Sjmcneill int streamid = 0;
9877b8ad118Sjmcneill uint8_t rirbsts;
9887b8ad118Sjmcneill
9897b8ad118Sjmcneill intsts = hda_read4(sc, HDAUDIO_MMIO_INTSTS);
9907b8ad118Sjmcneill if (!(intsts & HDAUDIO_INTSTS_GIS))
9917b8ad118Sjmcneill return 0;
9927b8ad118Sjmcneill
9937b8ad118Sjmcneill if (intsts & HDAUDIO_INTSTS_CIS) {
9947b8ad118Sjmcneill rirbsts = hda_read1(sc, HDAUDIO_MMIO_RIRBSTS);
9957b8ad118Sjmcneill if (rirbsts & HDAUDIO_RIRBSTS_RINTFL) {
9967b8ad118Sjmcneill mutex_enter(&sc->sc_corb_mtx);
9977b8ad118Sjmcneill hdaudio_rirb_dequeue(sc, true);
9987b8ad118Sjmcneill mutex_exit(&sc->sc_corb_mtx);
9997b8ad118Sjmcneill }
10007b8ad118Sjmcneill if (rirbsts & (HDAUDIO_RIRBSTS_RIRBOIS|HDAUDIO_RIRBSTS_RINTFL))
10017b8ad118Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS, rirbsts);
10027b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS, HDAUDIO_INTSTS_CIS);
10037b8ad118Sjmcneill }
10047b8ad118Sjmcneill if (intsts & HDAUDIO_INTSTS_SIS_MASK) {
10057b8ad118Sjmcneill mutex_enter(&sc->sc_stream_mtx);
10067b8ad118Sjmcneill stream_mask = intsts & sc->sc_stream_mask;
10077b8ad118Sjmcneill while (streamid < HDAUDIO_MAX_STREAMS && stream_mask != 0) {
10087b8ad118Sjmcneill st = &sc->sc_stream[streamid++];
10097b8ad118Sjmcneill if ((stream_mask & 1) != 0 && st->st_intr) {
10107b8ad118Sjmcneill st->st_intr(st);
10117b8ad118Sjmcneill }
10127b8ad118Sjmcneill stream_mask >>= 1;
10137b8ad118Sjmcneill }
10147b8ad118Sjmcneill mutex_exit(&sc->sc_stream_mtx);
10157b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS, HDAUDIO_INTSTS_SIS_MASK);
10167b8ad118Sjmcneill }
10177b8ad118Sjmcneill
10187b8ad118Sjmcneill return 1;
10197b8ad118Sjmcneill }
10207b8ad118Sjmcneill
10217b8ad118Sjmcneill struct hdaudio_stream *
hdaudio_stream_establish(struct hdaudio_softc * sc,enum hdaudio_stream_type type,int (* intr)(struct hdaudio_stream *),void * cookie)10227b8ad118Sjmcneill hdaudio_stream_establish(struct hdaudio_softc *sc,
10237b8ad118Sjmcneill enum hdaudio_stream_type type, int (*intr)(struct hdaudio_stream *),
10247b8ad118Sjmcneill void *cookie)
10257b8ad118Sjmcneill {
10267b8ad118Sjmcneill struct hdaudio_stream *st;
10277b8ad118Sjmcneill struct hdaudio_dma dma;
10287b8ad118Sjmcneill int i, err;
10297b8ad118Sjmcneill
10307b8ad118Sjmcneill dma.dma_size = sizeof(struct hdaudio_bdl_entry) * HDAUDIO_BDL_MAX;
1031ea2b5803Sriastradh dma.dma_sizereg = 0;
10327b8ad118Sjmcneill err = hdaudio_dma_alloc(sc, &dma, BUS_DMA_COHERENT | BUS_DMA_NOCACHE);
10337b8ad118Sjmcneill if (err)
10347b8ad118Sjmcneill return NULL;
10357b8ad118Sjmcneill
10367b8ad118Sjmcneill mutex_enter(&sc->sc_stream_mtx);
10377b8ad118Sjmcneill for (i = 0; i < HDAUDIO_MAX_STREAMS; i++) {
10387b8ad118Sjmcneill st = &sc->sc_stream[i];
10397b8ad118Sjmcneill if (st->st_enable == false)
10407b8ad118Sjmcneill break;
10417b8ad118Sjmcneill if (st->st_type != type)
10427b8ad118Sjmcneill continue;
10437b8ad118Sjmcneill if (sc->sc_stream_mask & (1 << i))
10447b8ad118Sjmcneill continue;
10457b8ad118Sjmcneill
10467b8ad118Sjmcneill /* Allocate stream */
10477b8ad118Sjmcneill st->st_bdl = dma;
10487b8ad118Sjmcneill st->st_intr = intr;
10497b8ad118Sjmcneill st->st_cookie = cookie;
10507b8ad118Sjmcneill sc->sc_stream_mask |= (1 << i);
10517b8ad118Sjmcneill mutex_exit(&sc->sc_stream_mtx);
10527b8ad118Sjmcneill return st;
10537b8ad118Sjmcneill }
10547b8ad118Sjmcneill mutex_exit(&sc->sc_stream_mtx);
10557b8ad118Sjmcneill
10567b8ad118Sjmcneill /* No streams of requested type available */
10577b8ad118Sjmcneill hdaudio_dma_free(sc, &dma);
10587b8ad118Sjmcneill return NULL;
10597b8ad118Sjmcneill }
10607b8ad118Sjmcneill
10617b8ad118Sjmcneill void
hdaudio_stream_disestablish(struct hdaudio_stream * st)10627b8ad118Sjmcneill hdaudio_stream_disestablish(struct hdaudio_stream *st)
10637b8ad118Sjmcneill {
10647b8ad118Sjmcneill struct hdaudio_softc *sc = st->st_host;
10657b8ad118Sjmcneill struct hdaudio_dma dma;
10667b8ad118Sjmcneill
10677b8ad118Sjmcneill KASSERT(sc->sc_stream_mask & (1 << st->st_shift));
10687b8ad118Sjmcneill
10697b8ad118Sjmcneill mutex_enter(&sc->sc_stream_mtx);
10707b8ad118Sjmcneill sc->sc_stream_mask &= ~(1 << st->st_shift);
10717b8ad118Sjmcneill st->st_intr = NULL;
10727b8ad118Sjmcneill st->st_cookie = NULL;
10737b8ad118Sjmcneill dma = st->st_bdl;
10747b8ad118Sjmcneill st->st_bdl.dma_valid = false;
10757b8ad118Sjmcneill mutex_exit(&sc->sc_stream_mtx);
10767b8ad118Sjmcneill
10777b8ad118Sjmcneill /* Can't bus_dmamem_unmap while holding a mutex. */
10787b8ad118Sjmcneill hdaudio_dma_free(sc, &dma);
10797b8ad118Sjmcneill }
10807b8ad118Sjmcneill
10817b8ad118Sjmcneill /*
1082*778f5600Sandvar * Convert most of audio_params_t to stream fmt descriptor; noticeably missing
10837b8ad118Sjmcneill * is the # channels bits, as this is encoded differently in codec and
10847b8ad118Sjmcneill * stream descriptors.
10857b8ad118Sjmcneill *
10867b8ad118Sjmcneill * TODO: validate that the stream and selected codecs can handle the fmt
10877b8ad118Sjmcneill */
10887b8ad118Sjmcneill uint16_t
hdaudio_stream_param(struct hdaudio_stream * st,const audio_params_t * param)10897b8ad118Sjmcneill hdaudio_stream_param(struct hdaudio_stream *st, const audio_params_t *param)
10907b8ad118Sjmcneill {
10917b8ad118Sjmcneill uint16_t fmt = 0;
10927b8ad118Sjmcneill
10937b8ad118Sjmcneill switch (param->encoding) {
10947b8ad118Sjmcneill case AUDIO_ENCODING_AC3:
10957b8ad118Sjmcneill fmt |= HDAUDIO_FMT_TYPE_NONPCM;
10967b8ad118Sjmcneill break;
10977b8ad118Sjmcneill default:
10987b8ad118Sjmcneill fmt |= HDAUDIO_FMT_TYPE_PCM;
10997b8ad118Sjmcneill break;
11007b8ad118Sjmcneill }
11017b8ad118Sjmcneill
11027b8ad118Sjmcneill switch (param->sample_rate) {
11037b8ad118Sjmcneill case 8000:
11047b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
11057b8ad118Sjmcneill HDAUDIO_FMT_DIV(6);
11067b8ad118Sjmcneill break;
11077b8ad118Sjmcneill case 11025:
11087b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1) |
11097b8ad118Sjmcneill HDAUDIO_FMT_DIV(4);
11107b8ad118Sjmcneill break;
11117b8ad118Sjmcneill case 16000:
11127b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
11137b8ad118Sjmcneill HDAUDIO_FMT_DIV(3);
11147b8ad118Sjmcneill break;
11157b8ad118Sjmcneill case 22050:
11167b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1) |
11177b8ad118Sjmcneill HDAUDIO_FMT_DIV(2);
11187b8ad118Sjmcneill break;
11197b8ad118Sjmcneill case 32000:
11207b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(2) |
11217b8ad118Sjmcneill HDAUDIO_FMT_DIV(3);
11227b8ad118Sjmcneill break;
11237b8ad118Sjmcneill case 44100:
11247b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1);
11257b8ad118Sjmcneill break;
11267b8ad118Sjmcneill case 48000:
11277b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1);
11287b8ad118Sjmcneill break;
11297b8ad118Sjmcneill case 88200:
11307b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(2);
11317b8ad118Sjmcneill break;
11327b8ad118Sjmcneill case 96000:
11337b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(2);
11347b8ad118Sjmcneill break;
11357b8ad118Sjmcneill case 176400:
11367b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(4);
11377b8ad118Sjmcneill break;
11387b8ad118Sjmcneill case 192000:
11397b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(4);
11407b8ad118Sjmcneill break;
11417b8ad118Sjmcneill default:
11427b8ad118Sjmcneill return 0;
11437b8ad118Sjmcneill }
11447b8ad118Sjmcneill
11457b8ad118Sjmcneill if (param->precision == 16 && param->validbits == 8)
11467b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BITS_8_16;
11477b8ad118Sjmcneill else if (param->precision == 16 && param->validbits == 16)
11487b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BITS_16_16;
11497b8ad118Sjmcneill else if (param->precision == 32 && param->validbits == 20)
11507b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BITS_20_32;
11517b8ad118Sjmcneill else if (param->precision == 32 && param->validbits == 24)
11527b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BITS_24_32;
11537b8ad118Sjmcneill else if (param->precision == 32 && param->validbits == 32)
11547b8ad118Sjmcneill fmt |= HDAUDIO_FMT_BITS_32_32;
11557b8ad118Sjmcneill else
11567b8ad118Sjmcneill return 0;
11577b8ad118Sjmcneill
11587b8ad118Sjmcneill return fmt;
11597b8ad118Sjmcneill }
11607b8ad118Sjmcneill
11617b8ad118Sjmcneill void
hdaudio_stream_reset(struct hdaudio_stream * st)11627b8ad118Sjmcneill hdaudio_stream_reset(struct hdaudio_stream *st)
11637b8ad118Sjmcneill {
11647b8ad118Sjmcneill struct hdaudio_softc *sc = st->st_host;
11657b8ad118Sjmcneill int snum = st->st_shift;
11667b8ad118Sjmcneill int retry;
11677b8ad118Sjmcneill uint8_t ctl0;
11687b8ad118Sjmcneill
11697b8ad118Sjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
11707b8ad118Sjmcneill ctl0 |= HDAUDIO_CTL_SRST;
11717b8ad118Sjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
11727b8ad118Sjmcneill
11737b8ad118Sjmcneill retry = HDAUDIO_RESET_TIMEOUT;
11747b8ad118Sjmcneill do {
11757b8ad118Sjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
11767b8ad118Sjmcneill if (ctl0 & HDAUDIO_CTL_SRST)
11777b8ad118Sjmcneill break;
11787b8ad118Sjmcneill hda_delay(10);
11797b8ad118Sjmcneill } while (--retry > 0);
11807b8ad118Sjmcneill
11817b8ad118Sjmcneill ctl0 &= ~HDAUDIO_CTL_SRST;
11827b8ad118Sjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
11837b8ad118Sjmcneill
11847b8ad118Sjmcneill retry = HDAUDIO_RESET_TIMEOUT;
11857b8ad118Sjmcneill do {
11867b8ad118Sjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
11877b8ad118Sjmcneill if (!(ctl0 & HDAUDIO_CTL_SRST))
11887b8ad118Sjmcneill break;
11897b8ad118Sjmcneill hda_delay(10);
11907b8ad118Sjmcneill } while (--retry > 0);
11917b8ad118Sjmcneill if (retry == 0) {
11927b8ad118Sjmcneill hda_error(sc, "timeout leaving stream reset state\n");
11937b8ad118Sjmcneill return;
11947b8ad118Sjmcneill }
11957b8ad118Sjmcneill }
11967b8ad118Sjmcneill
11977b8ad118Sjmcneill void
hdaudio_stream_start(struct hdaudio_stream * st,int blksize,bus_size_t dmasize,const audio_params_t * params)11987b8ad118Sjmcneill hdaudio_stream_start(struct hdaudio_stream *st, int blksize,
11997b8ad118Sjmcneill bus_size_t dmasize, const audio_params_t *params)
12007b8ad118Sjmcneill {
12017b8ad118Sjmcneill struct hdaudio_softc *sc = st->st_host;
12027b8ad118Sjmcneill struct hdaudio_bdl_entry *bdl;
12037b8ad118Sjmcneill uint64_t dmaaddr;
12047b8ad118Sjmcneill uint32_t intctl;
12057b8ad118Sjmcneill uint16_t fmt;
12067b8ad118Sjmcneill uint8_t ctl0, ctl2;
12077b8ad118Sjmcneill int cnt, snum = st->st_shift;
12087b8ad118Sjmcneill
12097b8ad118Sjmcneill KASSERT(sc->sc_stream_mask & (1 << st->st_shift));
12107b8ad118Sjmcneill KASSERT(st->st_data.dma_valid == true);
12117b8ad118Sjmcneill KASSERT(st->st_bdl.dma_valid == true);
12127b8ad118Sjmcneill
12137b8ad118Sjmcneill hdaudio_stream_stop(st);
12147b8ad118Sjmcneill hdaudio_stream_reset(st);
12157b8ad118Sjmcneill
12167b8ad118Sjmcneill /*
12177b8ad118Sjmcneill * Configure buffer descriptor list
12187b8ad118Sjmcneill */
12197b8ad118Sjmcneill dmaaddr = DMA_DMAADDR(&st->st_data);
12207b8ad118Sjmcneill bdl = DMA_KERNADDR(&st->st_bdl);
12217b8ad118Sjmcneill for (cnt = 0; cnt < HDAUDIO_BDL_MAX; cnt++) {
12227b8ad118Sjmcneill bdl[cnt].address_lo = (uint32_t)dmaaddr;
12237b8ad118Sjmcneill bdl[cnt].address_hi = dmaaddr >> 32;
12247b8ad118Sjmcneill bdl[cnt].length = blksize;
12257b8ad118Sjmcneill bdl[cnt].flags = HDAUDIO_BDL_ENTRY_IOC;
12267b8ad118Sjmcneill dmaaddr += blksize;
12277b8ad118Sjmcneill if (dmaaddr >= DMA_DMAADDR(&st->st_data) + dmasize) {
12287b8ad118Sjmcneill cnt++;
12297b8ad118Sjmcneill break;
12307b8ad118Sjmcneill }
12317b8ad118Sjmcneill }
12327b8ad118Sjmcneill
12337b8ad118Sjmcneill /*
12347b8ad118Sjmcneill * Program buffer descriptor list
12357b8ad118Sjmcneill */
12367b8ad118Sjmcneill dmaaddr = DMA_DMAADDR(&st->st_bdl);
12377b8ad118Sjmcneill hda_write4(sc, HDAUDIO_SD_BDPL(snum), (uint32_t)dmaaddr);
12387b8ad118Sjmcneill hda_write4(sc, HDAUDIO_SD_BDPU(snum), (uint32_t)(dmaaddr >> 32));
12397b8ad118Sjmcneill hda_write2(sc, HDAUDIO_SD_LVI(snum), (cnt - 1) & 0xff);
12407b8ad118Sjmcneill
12417b8ad118Sjmcneill /*
12427b8ad118Sjmcneill * Program cyclic buffer length
12437b8ad118Sjmcneill */
12447b8ad118Sjmcneill hda_write4(sc, HDAUDIO_SD_CBL(snum), dmasize);
12457b8ad118Sjmcneill
12467b8ad118Sjmcneill /*
12477b8ad118Sjmcneill * Program stream number (tag). Although controller hardware is
12487b8ad118Sjmcneill * capable of transmitting any stream number (0-15), by convention
12497b8ad118Sjmcneill * stream 0 is reserved as unused by software, so that converters
12507b8ad118Sjmcneill * whose stream numbers have been reset to 0 do not unintentionally
12517b8ad118Sjmcneill * decode data not intended for them.
12527b8ad118Sjmcneill */
12537b8ad118Sjmcneill ctl2 = hda_read1(sc, HDAUDIO_SD_CTL2(snum));
12547b8ad118Sjmcneill ctl2 &= ~0xf0;
12557b8ad118Sjmcneill ctl2 |= hdaudio_stream_tag(st) << 4;
12567b8ad118Sjmcneill hda_write1(sc, HDAUDIO_SD_CTL2(snum), ctl2);
12577b8ad118Sjmcneill
12587b8ad118Sjmcneill /*
12597b8ad118Sjmcneill * Program stream format
12607b8ad118Sjmcneill */
12617b8ad118Sjmcneill fmt = hdaudio_stream_param(st, params) |
12627b8ad118Sjmcneill HDAUDIO_FMT_CHAN(params->channels);
12637b8ad118Sjmcneill hda_write2(sc, HDAUDIO_SD_FMT(snum), fmt);
12647b8ad118Sjmcneill
12657b8ad118Sjmcneill /*
12667b8ad118Sjmcneill * Switch on interrupts for this stream
12677b8ad118Sjmcneill */
12687b8ad118Sjmcneill intctl = hda_read4(sc, HDAUDIO_MMIO_INTCTL);
12697b8ad118Sjmcneill intctl |= (1 << st->st_shift);
12707b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, intctl);
12717b8ad118Sjmcneill
12727b8ad118Sjmcneill /*
12737b8ad118Sjmcneill * Start running the stream
12747b8ad118Sjmcneill */
12757b8ad118Sjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
12767b8ad118Sjmcneill ctl0 |= HDAUDIO_CTL_DEIE | HDAUDIO_CTL_FEIE | HDAUDIO_CTL_IOCE |
12777b8ad118Sjmcneill HDAUDIO_CTL_RUN;
12787b8ad118Sjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
12797b8ad118Sjmcneill }
12807b8ad118Sjmcneill
12817b8ad118Sjmcneill void
hdaudio_stream_stop(struct hdaudio_stream * st)12827b8ad118Sjmcneill hdaudio_stream_stop(struct hdaudio_stream *st)
12837b8ad118Sjmcneill {
12847b8ad118Sjmcneill struct hdaudio_softc *sc = st->st_host;
12857b8ad118Sjmcneill uint32_t intctl;
12867b8ad118Sjmcneill uint8_t ctl0;
12877b8ad118Sjmcneill int snum = st->st_shift;
12887b8ad118Sjmcneill
12897b8ad118Sjmcneill /*
12907b8ad118Sjmcneill * Stop running the stream
12917b8ad118Sjmcneill */
12927b8ad118Sjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
12937b8ad118Sjmcneill ctl0 &= ~(HDAUDIO_CTL_DEIE | HDAUDIO_CTL_FEIE | HDAUDIO_CTL_IOCE |
12947b8ad118Sjmcneill HDAUDIO_CTL_RUN);
12957b8ad118Sjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
12967b8ad118Sjmcneill
12977b8ad118Sjmcneill /*
12987b8ad118Sjmcneill * Switch off interrupts for this stream
12997b8ad118Sjmcneill */
13007b8ad118Sjmcneill intctl = hda_read4(sc, HDAUDIO_MMIO_INTCTL);
13017b8ad118Sjmcneill intctl &= ~(1 << st->st_shift);
13027b8ad118Sjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, intctl);
13037b8ad118Sjmcneill }
13047b8ad118Sjmcneill
13057b8ad118Sjmcneill /*
13067b8ad118Sjmcneill * /dev/hdaudioN interface
13077b8ad118Sjmcneill */
13087b8ad118Sjmcneill
13097b8ad118Sjmcneill static const char *
hdaudioioctl_fgrp_to_cstr(enum function_group_type type)13107b8ad118Sjmcneill hdaudioioctl_fgrp_to_cstr(enum function_group_type type)
13117b8ad118Sjmcneill {
13127b8ad118Sjmcneill switch (type) {
13137b8ad118Sjmcneill case HDAUDIO_GROUP_TYPE_AFG:
13147b8ad118Sjmcneill return "afg";
13157b8ad118Sjmcneill case HDAUDIO_GROUP_TYPE_VSM_FG:
13167b8ad118Sjmcneill return "vsmfg";
13177b8ad118Sjmcneill default:
13187b8ad118Sjmcneill return "unknown";
13197b8ad118Sjmcneill }
13207b8ad118Sjmcneill }
13217b8ad118Sjmcneill
13227b8ad118Sjmcneill static struct hdaudio_function_group *
hdaudioioctl_fgrp_lookup(struct hdaudio_softc * sc,int codecid,int nid)13237b8ad118Sjmcneill hdaudioioctl_fgrp_lookup(struct hdaudio_softc *sc, int codecid, int nid)
13247b8ad118Sjmcneill {
13257b8ad118Sjmcneill struct hdaudio_codec *co;
13267b8ad118Sjmcneill struct hdaudio_function_group *fg = NULL;
13277b8ad118Sjmcneill int i;
13287b8ad118Sjmcneill
13297b8ad118Sjmcneill if (codecid < 0 || codecid >= HDAUDIO_MAX_CODECS)
13307b8ad118Sjmcneill return NULL;
13317b8ad118Sjmcneill co = &sc->sc_codec[codecid];
13327b8ad118Sjmcneill if (co->co_valid == false)
13337b8ad118Sjmcneill return NULL;
13347b8ad118Sjmcneill
13357b8ad118Sjmcneill for (i = 0; i < co->co_nfg; i++)
13367b8ad118Sjmcneill if (co->co_fg[i].fg_nid == nid) {
13377b8ad118Sjmcneill fg = &co->co_fg[i];
13387b8ad118Sjmcneill break;
13397b8ad118Sjmcneill }
13407b8ad118Sjmcneill
13417b8ad118Sjmcneill return fg;
13427b8ad118Sjmcneill }
13437b8ad118Sjmcneill
13447b8ad118Sjmcneill static int
hdaudioioctl_fgrp_info(struct hdaudio_softc * sc,prop_dictionary_t request,prop_dictionary_t response)13457b8ad118Sjmcneill hdaudioioctl_fgrp_info(struct hdaudio_softc *sc, prop_dictionary_t request,
13467b8ad118Sjmcneill prop_dictionary_t response)
13477b8ad118Sjmcneill {
13487b8ad118Sjmcneill struct hdaudio_codec *co;
13497b8ad118Sjmcneill struct hdaudio_function_group *fg;
13507b8ad118Sjmcneill prop_array_t array;
13517b8ad118Sjmcneill prop_dictionary_t dict;
13527b8ad118Sjmcneill int codecid, fgid;
13537b8ad118Sjmcneill
13547b8ad118Sjmcneill array = prop_array_create();
13557b8ad118Sjmcneill if (array == NULL)
13567b8ad118Sjmcneill return ENOMEM;
13577b8ad118Sjmcneill
13587b8ad118Sjmcneill for (codecid = 0; codecid < HDAUDIO_MAX_CODECS; codecid++) {
13597b8ad118Sjmcneill co = &sc->sc_codec[codecid];
13607b8ad118Sjmcneill if (co->co_valid == false)
13617b8ad118Sjmcneill continue;
13627b8ad118Sjmcneill for (fgid = 0; fgid < co->co_nfg; fgid++) {
13637b8ad118Sjmcneill fg = &co->co_fg[fgid];
13647b8ad118Sjmcneill dict = prop_dictionary_create();
13657b8ad118Sjmcneill if (dict == NULL)
13667b8ad118Sjmcneill return ENOMEM;
1367c4fd8ae4Sthorpej prop_dictionary_set_string_nocopy(dict,
13687b8ad118Sjmcneill "type", hdaudioioctl_fgrp_to_cstr(fg->fg_type));
13697b8ad118Sjmcneill prop_dictionary_set_int16(dict, "nid", fg->fg_nid);
13707b8ad118Sjmcneill prop_dictionary_set_int16(dict, "codecid", codecid);
13717b8ad118Sjmcneill prop_dictionary_set_uint16(dict, "vendor-id",
13727b8ad118Sjmcneill fg->fg_vendor);
13737b8ad118Sjmcneill prop_dictionary_set_uint16(dict, "product-id",
13747b8ad118Sjmcneill fg->fg_product);
13757b8ad118Sjmcneill prop_dictionary_set_uint32(dict, "subsystem-id",
13767b8ad118Sjmcneill sc->sc_subsystem);
13777b8ad118Sjmcneill if (fg->fg_device)
1378c4fd8ae4Sthorpej prop_dictionary_set_string(dict, "device",
13797b8ad118Sjmcneill device_xname(fg->fg_device));
13807b8ad118Sjmcneill else
1381c4fd8ae4Sthorpej prop_dictionary_set_string_nocopy(dict,
13827b8ad118Sjmcneill "device", "<none>");
13837b8ad118Sjmcneill prop_array_add(array, dict);
13847b8ad118Sjmcneill }
13857b8ad118Sjmcneill }
13867b8ad118Sjmcneill
13877b8ad118Sjmcneill prop_dictionary_set(response, "function-group-info", array);
13887b8ad118Sjmcneill return 0;
13897b8ad118Sjmcneill }
13907b8ad118Sjmcneill
13917b8ad118Sjmcneill static int
hdaudioioctl_fgrp_getconfig(struct hdaudio_softc * sc,prop_dictionary_t request,prop_dictionary_t response)13927b8ad118Sjmcneill hdaudioioctl_fgrp_getconfig(struct hdaudio_softc *sc,
13937b8ad118Sjmcneill prop_dictionary_t request, prop_dictionary_t response)
13947b8ad118Sjmcneill {
13957b8ad118Sjmcneill struct hdaudio_function_group *fg;
13967b8ad118Sjmcneill prop_dictionary_t dict;
13977b8ad118Sjmcneill prop_array_t array;
13987b8ad118Sjmcneill uint32_t nodecnt, wcap, config;
13997b8ad118Sjmcneill int16_t codecid, nid, i;
14007b8ad118Sjmcneill int startnode, endnode;
14017b8ad118Sjmcneill
14027b8ad118Sjmcneill if (!prop_dictionary_get_int16(request, "codecid", &codecid) ||
14037b8ad118Sjmcneill !prop_dictionary_get_int16(request, "nid", &nid))
14047b8ad118Sjmcneill return EINVAL;
14057b8ad118Sjmcneill
14067b8ad118Sjmcneill fg = hdaudioioctl_fgrp_lookup(sc, codecid, nid);
14077b8ad118Sjmcneill if (fg == NULL)
14087b8ad118Sjmcneill return ENODEV;
14097b8ad118Sjmcneill
14107b8ad118Sjmcneill array = prop_array_create();
14117b8ad118Sjmcneill if (array == NULL)
14127b8ad118Sjmcneill return ENOMEM;
14137b8ad118Sjmcneill
14147b8ad118Sjmcneill nodecnt = hdaudio_command(fg->fg_codec, fg->fg_nid,
14157b8ad118Sjmcneill CORB_GET_PARAMETER, COP_SUBORDINATE_NODE_COUNT);
14167b8ad118Sjmcneill startnode = COP_NODECNT_STARTNODE(nodecnt);
14177b8ad118Sjmcneill endnode = startnode + COP_NODECNT_NUMNODES(nodecnt);
14187b8ad118Sjmcneill
14197b8ad118Sjmcneill for (i = startnode; i < endnode; i++) {
14207b8ad118Sjmcneill wcap = hdaudio_command(fg->fg_codec, i,
14217b8ad118Sjmcneill CORB_GET_PARAMETER, COP_AUDIO_WIDGET_CAPABILITIES);
14227b8ad118Sjmcneill if (COP_AWCAP_TYPE(wcap) != COP_AWCAP_TYPE_PIN_COMPLEX)
14237b8ad118Sjmcneill continue;
14247b8ad118Sjmcneill config = hdaudio_command(fg->fg_codec, i,
14257b8ad118Sjmcneill CORB_GET_CONFIGURATION_DEFAULT, 0);
14267b8ad118Sjmcneill dict = prop_dictionary_create();
14277b8ad118Sjmcneill if (dict == NULL)
14287b8ad118Sjmcneill return ENOMEM;
14297b8ad118Sjmcneill prop_dictionary_set_int16(dict, "nid", i);
14307b8ad118Sjmcneill prop_dictionary_set_uint32(dict, "config", config);
14317b8ad118Sjmcneill prop_array_add(array, dict);
14327b8ad118Sjmcneill }
14337b8ad118Sjmcneill
14347b8ad118Sjmcneill prop_dictionary_set(response, "pin-config", array);
14357b8ad118Sjmcneill
14367b8ad118Sjmcneill return 0;
14377b8ad118Sjmcneill }
14387b8ad118Sjmcneill
14397b8ad118Sjmcneill static int
hdaudioioctl_fgrp_setconfig(struct hdaudio_softc * sc,prop_dictionary_t request,prop_dictionary_t response)14407b8ad118Sjmcneill hdaudioioctl_fgrp_setconfig(struct hdaudio_softc *sc,
14417b8ad118Sjmcneill prop_dictionary_t request, prop_dictionary_t response)
14427b8ad118Sjmcneill {
14437b8ad118Sjmcneill struct hdaudio_function_group *fg;
14447b8ad118Sjmcneill prop_array_t config;
14457b8ad118Sjmcneill int16_t codecid, nid;
14467b8ad118Sjmcneill int err;
14477b8ad118Sjmcneill
14487b8ad118Sjmcneill if (!prop_dictionary_get_int16(request, "codecid", &codecid) ||
14497b8ad118Sjmcneill !prop_dictionary_get_int16(request, "nid", &nid))
14507b8ad118Sjmcneill return EINVAL;
14517b8ad118Sjmcneill
14527b8ad118Sjmcneill fg = hdaudioioctl_fgrp_lookup(sc, codecid, nid);
14537b8ad118Sjmcneill if (fg == NULL)
14547b8ad118Sjmcneill return ENODEV;
14557b8ad118Sjmcneill
14567b8ad118Sjmcneill if (fg->fg_device) {
14577b8ad118Sjmcneill err = config_detach(fg->fg_device, 0);
14587b8ad118Sjmcneill if (err)
14597b8ad118Sjmcneill return err;
14607b8ad118Sjmcneill fg->fg_device = NULL;
14617b8ad118Sjmcneill }
14627b8ad118Sjmcneill
14637b8ad118Sjmcneill /* "pin-config" may be NULL, this means "use BIOS configuration" */
14647b8ad118Sjmcneill config = prop_dictionary_get(request, "pin-config");
14657b8ad118Sjmcneill if (config && prop_object_type(config) != PROP_TYPE_ARRAY) {
14667b8ad118Sjmcneill prop_object_release(config);
14677b8ad118Sjmcneill return EINVAL;
14687b8ad118Sjmcneill }
14697b8ad118Sjmcneill hdaudio_attach_fg(fg, config);
14707b8ad118Sjmcneill if (config)
14717b8ad118Sjmcneill prop_object_release(config);
14727b8ad118Sjmcneill
14737b8ad118Sjmcneill return 0;
14747b8ad118Sjmcneill }
14757b8ad118Sjmcneill
14767b8ad118Sjmcneill static int
hdaudio_dispatch_fgrp_ioctl(struct hdaudio_softc * sc,u_long cmd,prop_dictionary_t request,prop_dictionary_t response)14777b8ad118Sjmcneill hdaudio_dispatch_fgrp_ioctl(struct hdaudio_softc *sc, u_long cmd,
14787b8ad118Sjmcneill prop_dictionary_t request, prop_dictionary_t response)
14797b8ad118Sjmcneill {
14807b8ad118Sjmcneill struct hdaudio_function_group *fg;
14817b8ad118Sjmcneill int (*infocb)(void *, prop_dictionary_t, prop_dictionary_t);
14827b8ad118Sjmcneill prop_dictionary_t fgrp_dict;
14837b8ad118Sjmcneill uint64_t info_fn;
14847b8ad118Sjmcneill int16_t codecid, nid;
14857b8ad118Sjmcneill void *fgrp_sc;
14867b8ad118Sjmcneill bool rv;
14877b8ad118Sjmcneill int err;
14887b8ad118Sjmcneill
14897b8ad118Sjmcneill if (!prop_dictionary_get_int16(request, "codecid", &codecid) ||
14907b8ad118Sjmcneill !prop_dictionary_get_int16(request, "nid", &nid))
14917b8ad118Sjmcneill return EINVAL;
14927b8ad118Sjmcneill
14937b8ad118Sjmcneill fg = hdaudioioctl_fgrp_lookup(sc, codecid, nid);
14947b8ad118Sjmcneill if (fg == NULL)
14957b8ad118Sjmcneill return ENODEV;
14967b8ad118Sjmcneill if (fg->fg_device == NULL)
14977b8ad118Sjmcneill return ENXIO;
14987b8ad118Sjmcneill fgrp_sc = device_private(fg->fg_device);
14997b8ad118Sjmcneill fgrp_dict = device_properties(fg->fg_device);
15007b8ad118Sjmcneill
15017b8ad118Sjmcneill switch (fg->fg_type) {
15027b8ad118Sjmcneill case HDAUDIO_GROUP_TYPE_AFG:
15037b8ad118Sjmcneill switch (cmd) {
15047b8ad118Sjmcneill case HDAUDIO_FGRP_CODEC_INFO:
15057b8ad118Sjmcneill rv = prop_dictionary_get_uint64(fgrp_dict,
15067b8ad118Sjmcneill "codecinfo-callback", &info_fn);
15077b8ad118Sjmcneill if (!rv)
15087b8ad118Sjmcneill return ENXIO;
15097b8ad118Sjmcneill infocb = (void *)(uintptr_t)info_fn;
15107b8ad118Sjmcneill err = infocb(fgrp_sc, request, response);
15117b8ad118Sjmcneill break;
15127b8ad118Sjmcneill case HDAUDIO_FGRP_WIDGET_INFO:
15137b8ad118Sjmcneill rv = prop_dictionary_get_uint64(fgrp_dict,
15147b8ad118Sjmcneill "widgetinfo-callback", &info_fn);
15157b8ad118Sjmcneill if (!rv)
15167b8ad118Sjmcneill return ENXIO;
15177b8ad118Sjmcneill infocb = (void *)(uintptr_t)info_fn;
15187b8ad118Sjmcneill err = infocb(fgrp_sc, request, response);
15197b8ad118Sjmcneill break;
15207b8ad118Sjmcneill default:
15217b8ad118Sjmcneill err = EINVAL;
15227b8ad118Sjmcneill break;
15237b8ad118Sjmcneill }
15247b8ad118Sjmcneill break;
15257b8ad118Sjmcneill
15267b8ad118Sjmcneill default:
15277b8ad118Sjmcneill err = EINVAL;
15287b8ad118Sjmcneill break;
15297b8ad118Sjmcneill }
15307b8ad118Sjmcneill return err;
15317b8ad118Sjmcneill }
15327b8ad118Sjmcneill
15337b8ad118Sjmcneill int
hdaudioopen(dev_t dev,int flag,int mode,struct lwp * l)15347b8ad118Sjmcneill hdaudioopen(dev_t dev, int flag, int mode, struct lwp *l)
15357b8ad118Sjmcneill {
15367b8ad118Sjmcneill device_t self;
15377b8ad118Sjmcneill
15387b8ad118Sjmcneill self = device_lookup(&hdaudio_cd, HDAUDIOUNIT(dev));
15397b8ad118Sjmcneill if (self == NULL)
15407b8ad118Sjmcneill return ENXIO;
15417b8ad118Sjmcneill
15427b8ad118Sjmcneill return 0;
15437b8ad118Sjmcneill }
15447b8ad118Sjmcneill
15457b8ad118Sjmcneill int
hdaudioclose(dev_t dev,int flag,int mode,struct lwp * l)15467b8ad118Sjmcneill hdaudioclose(dev_t dev, int flag, int mode, struct lwp *l)
15477b8ad118Sjmcneill {
15487b8ad118Sjmcneill return 0;
15497b8ad118Sjmcneill }
15507b8ad118Sjmcneill
15517b8ad118Sjmcneill int
hdaudioioctl(dev_t dev,u_long cmd,void * addr,int flag,struct lwp * l)15527b8ad118Sjmcneill hdaudioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
15537b8ad118Sjmcneill {
15547b8ad118Sjmcneill struct hdaudio_softc *sc;
15557b8ad118Sjmcneill struct plistref *pref = addr;
15567b8ad118Sjmcneill prop_dictionary_t request, response;
15577b8ad118Sjmcneill int err;
15587b8ad118Sjmcneill
15597b8ad118Sjmcneill sc = device_lookup_private(&hdaudio_cd, HDAUDIOUNIT(dev));
15607b8ad118Sjmcneill if (sc == NULL)
15617b8ad118Sjmcneill return ENXIO;
15627b8ad118Sjmcneill
15637b8ad118Sjmcneill response = prop_dictionary_create();
15647b8ad118Sjmcneill if (response == NULL)
15657b8ad118Sjmcneill return ENOMEM;
15667b8ad118Sjmcneill
15677b8ad118Sjmcneill err = prop_dictionary_copyin_ioctl(pref, cmd, &request);
15687b8ad118Sjmcneill if (err) {
15697b8ad118Sjmcneill prop_object_release(response);
15707b8ad118Sjmcneill return err;
15717b8ad118Sjmcneill }
15727b8ad118Sjmcneill
15737b8ad118Sjmcneill switch (cmd) {
15747b8ad118Sjmcneill case HDAUDIO_FGRP_INFO:
15757b8ad118Sjmcneill err = hdaudioioctl_fgrp_info(sc, request, response);
15767b8ad118Sjmcneill break;
15777b8ad118Sjmcneill case HDAUDIO_FGRP_GETCONFIG:
15787b8ad118Sjmcneill err = hdaudioioctl_fgrp_getconfig(sc, request, response);
15797b8ad118Sjmcneill break;
15807b8ad118Sjmcneill case HDAUDIO_FGRP_SETCONFIG:
15817b8ad118Sjmcneill err = hdaudioioctl_fgrp_setconfig(sc, request, response);
15827b8ad118Sjmcneill break;
15837b8ad118Sjmcneill case HDAUDIO_FGRP_CODEC_INFO:
15847b8ad118Sjmcneill case HDAUDIO_FGRP_WIDGET_INFO:
15857b8ad118Sjmcneill err = hdaudio_dispatch_fgrp_ioctl(sc, cmd, request, response);
15867b8ad118Sjmcneill break;
15877b8ad118Sjmcneill default:
15887b8ad118Sjmcneill err = EINVAL;
15897b8ad118Sjmcneill break;
15907b8ad118Sjmcneill }
15917b8ad118Sjmcneill
15927b8ad118Sjmcneill if (!err)
15937b8ad118Sjmcneill err = prop_dictionary_copyout_ioctl(pref, cmd, response);
15947b8ad118Sjmcneill
15957b8ad118Sjmcneill if (response)
15967b8ad118Sjmcneill prop_object_release(response);
15977b8ad118Sjmcneill prop_object_release(request);
15987b8ad118Sjmcneill return err;
15997b8ad118Sjmcneill }
16007b8ad118Sjmcneill
16015b6ebe47Spgoyette MODULE(MODULE_CLASS_DRIVER, hdaudio, "audio");
16025b6ebe47Spgoyette #ifdef _MODULE
16035b6ebe47Spgoyette static const struct cfiattrdata hdaudiobuscf_iattrdata = {
16045b6ebe47Spgoyette "hdaudiobus", 1, {
16055b6ebe47Spgoyette { "nid", "-1", -1 },
16065b6ebe47Spgoyette }
16075b6ebe47Spgoyette };
16085b6ebe47Spgoyette static const struct cfiattrdata * const hdaudio_attrs[] = {
16095b6ebe47Spgoyette &hdaudiobuscf_iattrdata, NULL
16105b6ebe47Spgoyette };
16115b6ebe47Spgoyette CFDRIVER_DECL(hdaudio, DV_AUDIODEV, hdaudio_attrs);
16125b6ebe47Spgoyette #endif
16137b8ad118Sjmcneill
16147b8ad118Sjmcneill static int
hdaudio_modcmd(modcmd_t cmd,void * opaque)16157b8ad118Sjmcneill hdaudio_modcmd(modcmd_t cmd, void *opaque)
16167b8ad118Sjmcneill {
16177b8ad118Sjmcneill int error = 0;
16187b8ad118Sjmcneill #ifdef _MODULE
16197b8ad118Sjmcneill int bmaj = -1, cmaj = -1;
16207b8ad118Sjmcneill #endif
16217b8ad118Sjmcneill
16227b8ad118Sjmcneill switch (cmd) {
16237b8ad118Sjmcneill case MODULE_CMD_INIT:
16247b8ad118Sjmcneill #ifdef _MODULE
16257b8ad118Sjmcneill error = devsw_attach("hdaudio", NULL, &bmaj,
16267b8ad118Sjmcneill &hdaudio_cdevsw, &cmaj);
16275b6ebe47Spgoyette if (error)
16285b6ebe47Spgoyette break;
16295b6ebe47Spgoyette error = config_cfdriver_attach(&hdaudio_cd);
16305b6ebe47Spgoyette if (error)
16317b8ad118Sjmcneill devsw_detach(NULL, &hdaudio_cdevsw);
16327b8ad118Sjmcneill #endif
16335b6ebe47Spgoyette break;
16345b6ebe47Spgoyette case MODULE_CMD_FINI:
16355b6ebe47Spgoyette #ifdef _MODULE
16365b6ebe47Spgoyette error = config_cfdriver_detach(&hdaudio_cd);
16375b6ebe47Spgoyette if (error)
16385b6ebe47Spgoyette break;
1639ad9f1588Sriastradh devsw_detach(NULL, &hdaudio_cdevsw);
16405b6ebe47Spgoyette #endif
16415b6ebe47Spgoyette break;
16425b6ebe47Spgoyette default:
16435b6ebe47Spgoyette error = ENOTTY;
16445b6ebe47Spgoyette break;
16455b6ebe47Spgoyette }
16465b6ebe47Spgoyette return error;
16477b8ad118Sjmcneill }
16487b8ad118Sjmcneill
16497b8ad118Sjmcneill DEV_VERBOSE_DEFINE(hdaudio);
1650