1*2e9df72eSandvar /* $NetBSD: hdaudio.c,v 1.18 2022/04/07 19:33:37 andvar Exp $ */
28a9ff04bSjmcneill
38a9ff04bSjmcneill /*
48a9ff04bSjmcneill * Copyright (c) 2009 Precedence Technologies Ltd <support@precedence.co.uk>
58a9ff04bSjmcneill * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
68a9ff04bSjmcneill * All rights reserved.
78a9ff04bSjmcneill *
88a9ff04bSjmcneill * This code is derived from software contributed to The NetBSD Foundation
98a9ff04bSjmcneill * by Precedence Technologies Ltd
108a9ff04bSjmcneill *
118a9ff04bSjmcneill * Redistribution and use in source and binary forms, with or without
128a9ff04bSjmcneill * modification, are permitted provided that the following conditions
138a9ff04bSjmcneill * are met:
148a9ff04bSjmcneill * 1. Redistributions of source code must retain the above copyright
158a9ff04bSjmcneill * notice, this list of conditions and the following disclaimer.
168a9ff04bSjmcneill * 2. The name of the author may not be used to endorse or promote products
178a9ff04bSjmcneill * derived from this software without specific prior written permission.
188a9ff04bSjmcneill *
198a9ff04bSjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
208a9ff04bSjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
218a9ff04bSjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
228a9ff04bSjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
238a9ff04bSjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
248a9ff04bSjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
258a9ff04bSjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
268a9ff04bSjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
278a9ff04bSjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
288a9ff04bSjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
298a9ff04bSjmcneill * SUCH DAMAGE.
308a9ff04bSjmcneill */
318a9ff04bSjmcneill
328a9ff04bSjmcneill #include <sys/cdefs.h>
33*2e9df72eSandvar __KERNEL_RCSID(0, "$NetBSD: hdaudio.c,v 1.18 2022/04/07 19:33:37 andvar Exp $");
348a9ff04bSjmcneill
358a9ff04bSjmcneill #include <sys/types.h>
368a9ff04bSjmcneill #include <sys/param.h>
378a9ff04bSjmcneill #include <sys/systm.h>
388a9ff04bSjmcneill #include <sys/device.h>
398a9ff04bSjmcneill #include <sys/conf.h>
408a9ff04bSjmcneill #include <sys/bus.h>
418a9ff04bSjmcneill #include <sys/kmem.h>
428a9ff04bSjmcneill #include <sys/module.h>
438a9ff04bSjmcneill
448a9ff04bSjmcneill #include "hdaudiovar.h"
458a9ff04bSjmcneill #include "hdaudioreg.h"
468a9ff04bSjmcneill #include "hdaudioio.h"
478a9ff04bSjmcneill #include "hdaudio_verbose.h"
4874007e33Spgoyette #include "hdaudiodevs.h"
498a9ff04bSjmcneill
508a9ff04bSjmcneill /* #define HDAUDIO_DEBUG */
518a9ff04bSjmcneill
528a9ff04bSjmcneill #define HDAUDIO_RESET_TIMEOUT 5000
538a9ff04bSjmcneill #define HDAUDIO_CORB_TIMEOUT 1000
548a9ff04bSjmcneill #define HDAUDIO_RIRB_TIMEOUT 5000
558a9ff04bSjmcneill
568a9ff04bSjmcneill #define HDAUDIO_CODEC_DELAY 1000 /* spec calls for 250 */
578a9ff04bSjmcneill
588a9ff04bSjmcneill dev_type_open(hdaudioopen);
598a9ff04bSjmcneill dev_type_close(hdaudioclose);
608a9ff04bSjmcneill dev_type_ioctl(hdaudioioctl);
618a9ff04bSjmcneill
628a9ff04bSjmcneill const struct cdevsw hdaudio_cdevsw = {
638a9ff04bSjmcneill .d_open = hdaudioopen,
648a9ff04bSjmcneill .d_close = hdaudioclose,
658a9ff04bSjmcneill .d_read = noread,
668a9ff04bSjmcneill .d_write = nowrite,
678a9ff04bSjmcneill .d_ioctl = hdaudioioctl,
688a9ff04bSjmcneill .d_stop = nostop,
698a9ff04bSjmcneill .d_tty = notty,
708a9ff04bSjmcneill .d_poll = nopoll,
718a9ff04bSjmcneill .d_mmap = nommap,
728a9ff04bSjmcneill .d_kqfilter = nokqfilter,
738a9ff04bSjmcneill .d_discard = nodiscard,
748a9ff04bSjmcneill .d_flag = D_OTHER
758a9ff04bSjmcneill };
768a9ff04bSjmcneill
778a9ff04bSjmcneill extern struct cfdriver hdaudio_cd;
788a9ff04bSjmcneill
798a9ff04bSjmcneill #define HDAUDIOUNIT(x) minor((x))
808a9ff04bSjmcneill
818a9ff04bSjmcneill static void
hdaudio_stream_init(struct hdaudio_softc * sc,int nis,int nos,int nbidir)828a9ff04bSjmcneill hdaudio_stream_init(struct hdaudio_softc *sc, int nis, int nos, int nbidir)
838a9ff04bSjmcneill {
848a9ff04bSjmcneill int i, cnt = 0;
858a9ff04bSjmcneill
868a9ff04bSjmcneill for (i = 0; i < nis && cnt < HDAUDIO_MAX_STREAMS; i++) {
878a9ff04bSjmcneill sc->sc_stream[cnt].st_host = sc;
888a9ff04bSjmcneill sc->sc_stream[cnt].st_enable = true;
898a9ff04bSjmcneill sc->sc_stream[cnt].st_shift = cnt;
908a9ff04bSjmcneill sc->sc_stream[cnt++].st_type = HDAUDIO_STREAM_ISS;
918a9ff04bSjmcneill }
928a9ff04bSjmcneill for (i = 0; i < nos && cnt < HDAUDIO_MAX_STREAMS; i++) {
938a9ff04bSjmcneill sc->sc_stream[cnt].st_host = sc;
948a9ff04bSjmcneill sc->sc_stream[cnt].st_enable = true;
958a9ff04bSjmcneill sc->sc_stream[cnt].st_shift = cnt;
968a9ff04bSjmcneill sc->sc_stream[cnt++].st_type = HDAUDIO_STREAM_OSS;
978a9ff04bSjmcneill }
988a9ff04bSjmcneill for (i = 0; i < nbidir && cnt < HDAUDIO_MAX_STREAMS; i++) {
998a9ff04bSjmcneill sc->sc_stream[cnt].st_host = sc;
1008a9ff04bSjmcneill sc->sc_stream[cnt].st_enable = true;
1018a9ff04bSjmcneill sc->sc_stream[cnt].st_shift = cnt;
1028a9ff04bSjmcneill sc->sc_stream[cnt++].st_type = HDAUDIO_STREAM_BSS;
1038a9ff04bSjmcneill }
1048a9ff04bSjmcneill
1058a9ff04bSjmcneill for (i = 0; i < cnt; i++)
1068a9ff04bSjmcneill hdaudio_stream_stop(&sc->sc_stream[i]);
1078a9ff04bSjmcneill
1088a9ff04bSjmcneill sc->sc_stream_mask = 0;
1098a9ff04bSjmcneill }
1108a9ff04bSjmcneill
1118a9ff04bSjmcneill static void
hdaudio_codec_init(struct hdaudio_softc * sc)1128a9ff04bSjmcneill hdaudio_codec_init(struct hdaudio_softc *sc)
1138a9ff04bSjmcneill {
1148a9ff04bSjmcneill int i;
1158a9ff04bSjmcneill
1168a9ff04bSjmcneill for (i = 0; i < HDAUDIO_MAX_CODECS; i++) {
1178a9ff04bSjmcneill sc->sc_codec[i].co_addr = i;
1188a9ff04bSjmcneill sc->sc_codec[i].co_host = sc;
1198a9ff04bSjmcneill }
1208a9ff04bSjmcneill }
1218a9ff04bSjmcneill
1228a9ff04bSjmcneill static void
hdaudio_init(struct hdaudio_softc * sc)1238a9ff04bSjmcneill hdaudio_init(struct hdaudio_softc *sc)
1248a9ff04bSjmcneill {
125e83803dfSjmcneill const uint8_t vmaj = hda_read1(sc, HDAUDIO_MMIO_VMAJ);
126e83803dfSjmcneill const uint8_t vmin = hda_read1(sc, HDAUDIO_MMIO_VMIN);
127e83803dfSjmcneill const uint16_t gcap = hda_read2(sc, HDAUDIO_MMIO_GCAP);
128e83803dfSjmcneill const int nis = HDAUDIO_GCAP_ISS(gcap);
129e83803dfSjmcneill const int nos = HDAUDIO_GCAP_OSS(gcap);
130e83803dfSjmcneill const int nbidir = HDAUDIO_GCAP_BSS(gcap);
131e83803dfSjmcneill const int nsdo = HDAUDIO_GCAP_NSDO(gcap);
132e83803dfSjmcneill const int addr64 = HDAUDIO_GCAP_64OK(gcap);
1338a9ff04bSjmcneill
134e83803dfSjmcneill hda_print(sc, "HDA ver. %d.%d, OSS %d, ISS %d, BSS %d, SDO %d%s\n",
135e83803dfSjmcneill vmaj, vmin, nos, nis, nbidir, nsdo, addr64 ? ", 64-bit" : "");
1368a9ff04bSjmcneill
1378a9ff04bSjmcneill /* Initialize codecs and streams */
1388a9ff04bSjmcneill hdaudio_codec_init(sc);
1398a9ff04bSjmcneill hdaudio_stream_init(sc, nis, nos, nbidir);
1408a9ff04bSjmcneill }
1418a9ff04bSjmcneill
1428a9ff04bSjmcneill static int
hdaudio_codec_probe(struct hdaudio_softc * sc)1438a9ff04bSjmcneill hdaudio_codec_probe(struct hdaudio_softc *sc)
1448a9ff04bSjmcneill {
1458a9ff04bSjmcneill uint16_t statests;
1468a9ff04bSjmcneill int codecid;
1478a9ff04bSjmcneill
1488a9ff04bSjmcneill statests = hda_read2(sc, HDAUDIO_MMIO_STATESTS);
1498a9ff04bSjmcneill for (codecid = 0; codecid < HDAUDIO_MAX_CODECS; codecid++)
1508a9ff04bSjmcneill if (statests & (1 << codecid))
1518a9ff04bSjmcneill sc->sc_codec[codecid].co_valid = true;
1528a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS, statests);
1538a9ff04bSjmcneill
1548a9ff04bSjmcneill return statests;
1558a9ff04bSjmcneill }
1568a9ff04bSjmcneill
1578a9ff04bSjmcneill int
hdaudio_dma_alloc(struct hdaudio_softc * sc,struct hdaudio_dma * dma,int flags)1588a9ff04bSjmcneill hdaudio_dma_alloc(struct hdaudio_softc *sc, struct hdaudio_dma *dma,
1598a9ff04bSjmcneill int flags)
1608a9ff04bSjmcneill {
1618a9ff04bSjmcneill int err;
1628a9ff04bSjmcneill
1638a9ff04bSjmcneill KASSERT(dma->dma_size > 0);
1648a9ff04bSjmcneill
1658a9ff04bSjmcneill err = bus_dmamem_alloc(sc->sc_dmat, dma->dma_size, 128, 0,
1668a9ff04bSjmcneill dma->dma_segs, sizeof(dma->dma_segs) / sizeof(dma->dma_segs[0]),
1678a9ff04bSjmcneill &dma->dma_nsegs, BUS_DMA_WAITOK);
1688a9ff04bSjmcneill if (err)
1698a9ff04bSjmcneill return err;
1708a9ff04bSjmcneill err = bus_dmamem_map(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs,
1718a9ff04bSjmcneill dma->dma_size, &dma->dma_addr, BUS_DMA_WAITOK | flags);
1728a9ff04bSjmcneill if (err)
1738a9ff04bSjmcneill goto free;
1748a9ff04bSjmcneill err = bus_dmamap_create(sc->sc_dmat, dma->dma_size, dma->dma_nsegs,
1758a9ff04bSjmcneill dma->dma_size, 0, BUS_DMA_WAITOK, &dma->dma_map);
1768a9ff04bSjmcneill if (err)
1778a9ff04bSjmcneill goto unmap;
1788a9ff04bSjmcneill err = bus_dmamap_load(sc->sc_dmat, dma->dma_map, dma->dma_addr,
1798a9ff04bSjmcneill dma->dma_size, NULL, BUS_DMA_WAITOK | flags);
1808a9ff04bSjmcneill if (err)
1818a9ff04bSjmcneill goto destroy;
1828a9ff04bSjmcneill
18341f3aaf4Sjmcneill memset(dma->dma_addr, 0, dma->dma_size);
18441f3aaf4Sjmcneill bus_dmamap_sync(sc->sc_dmat, dma->dma_map, 0, dma->dma_size,
18541f3aaf4Sjmcneill BUS_DMASYNC_PREWRITE);
18641f3aaf4Sjmcneill
1878a9ff04bSjmcneill dma->dma_valid = true;
1888a9ff04bSjmcneill return 0;
1898a9ff04bSjmcneill
1908a9ff04bSjmcneill destroy:
1918a9ff04bSjmcneill bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
1928a9ff04bSjmcneill unmap:
1938a9ff04bSjmcneill bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
1948a9ff04bSjmcneill free:
1958a9ff04bSjmcneill bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
1968a9ff04bSjmcneill
1978a9ff04bSjmcneill dma->dma_valid = false;
1988a9ff04bSjmcneill return err;
1998a9ff04bSjmcneill }
2008a9ff04bSjmcneill
2018a9ff04bSjmcneill void
hdaudio_dma_free(struct hdaudio_softc * sc,struct hdaudio_dma * dma)2028a9ff04bSjmcneill hdaudio_dma_free(struct hdaudio_softc *sc, struct hdaudio_dma *dma)
2038a9ff04bSjmcneill {
2048a9ff04bSjmcneill if (dma->dma_valid == false)
2058a9ff04bSjmcneill return;
2068a9ff04bSjmcneill bus_dmamap_unload(sc->sc_dmat, dma->dma_map);
2078a9ff04bSjmcneill bus_dmamap_destroy(sc->sc_dmat, dma->dma_map);
2088a9ff04bSjmcneill bus_dmamem_unmap(sc->sc_dmat, dma->dma_addr, dma->dma_size);
2098a9ff04bSjmcneill bus_dmamem_free(sc->sc_dmat, dma->dma_segs, dma->dma_nsegs);
2108a9ff04bSjmcneill dma->dma_valid = false;
2118a9ff04bSjmcneill }
2128a9ff04bSjmcneill
2138a9ff04bSjmcneill static void
hdaudio_corb_enqueue(struct hdaudio_softc * sc,int addr,int nid,uint32_t control,uint32_t param)2148a9ff04bSjmcneill hdaudio_corb_enqueue(struct hdaudio_softc *sc, int addr, int nid,
2158a9ff04bSjmcneill uint32_t control, uint32_t param)
2168a9ff04bSjmcneill {
2178a9ff04bSjmcneill uint32_t *corb = DMA_KERNADDR(&sc->sc_corb);
2188a9ff04bSjmcneill uint32_t verb;
2198a9ff04bSjmcneill uint16_t corbrp;
2208a9ff04bSjmcneill int wp;
2218a9ff04bSjmcneill
2228a9ff04bSjmcneill /* Build command */
2238a9ff04bSjmcneill verb = (addr << 28) | (nid << 20) | (control << 8) | param;
2248a9ff04bSjmcneill
2258a9ff04bSjmcneill /* Fetch and update write pointer */
2268a9ff04bSjmcneill corbrp = hda_read2(sc, HDAUDIO_MMIO_CORBWP);
2278a9ff04bSjmcneill wp = (corbrp & 0xff) + 1;
2288a9ff04bSjmcneill if (wp >= (sc->sc_corb.dma_size / sizeof(*corb)))
2298a9ff04bSjmcneill wp = 0;
2308a9ff04bSjmcneill
2318a9ff04bSjmcneill /* Enqueue command */
2328a9ff04bSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_corb.dma_map, 0,
2338a9ff04bSjmcneill sc->sc_corb.dma_size, BUS_DMASYNC_POSTWRITE);
2348a9ff04bSjmcneill corb[wp] = verb;
2358a9ff04bSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_corb.dma_map, 0,
2368a9ff04bSjmcneill sc->sc_corb.dma_size, BUS_DMASYNC_PREWRITE);
2378a9ff04bSjmcneill
2388a9ff04bSjmcneill /* Commit updated write pointer */
2398a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBWP, wp);
2408a9ff04bSjmcneill }
2418a9ff04bSjmcneill
2428a9ff04bSjmcneill static void
hdaudio_rirb_unsol(struct hdaudio_softc * sc,struct rirb_entry * entry)2438a9ff04bSjmcneill hdaudio_rirb_unsol(struct hdaudio_softc *sc, struct rirb_entry *entry)
2448a9ff04bSjmcneill {
2458a9ff04bSjmcneill struct hdaudio_codec *co;
2468a9ff04bSjmcneill struct hdaudio_function_group *fg;
2478a9ff04bSjmcneill uint8_t codecid = RIRB_CODEC_ID(entry);
2488a9ff04bSjmcneill unsigned int i;
2498a9ff04bSjmcneill
2508a9ff04bSjmcneill if (codecid >= HDAUDIO_MAX_CODECS) {
2518a9ff04bSjmcneill hda_error(sc, "unsol: codec id 0x%02x out of range\n", codecid);
2528a9ff04bSjmcneill return;
2538a9ff04bSjmcneill }
2548a9ff04bSjmcneill co = &sc->sc_codec[codecid];
2558a9ff04bSjmcneill if (sc->sc_codec[codecid].co_valid == false) {
2568a9ff04bSjmcneill hda_error(sc, "unsol: codec id 0x%02x not valid\n", codecid);
2578a9ff04bSjmcneill return;
2588a9ff04bSjmcneill }
2598a9ff04bSjmcneill
2608a9ff04bSjmcneill for (i = 0; i < co->co_nfg; i++) {
2618a9ff04bSjmcneill fg = &co->co_fg[i];
2628a9ff04bSjmcneill if (fg->fg_device && fg->fg_unsol)
2638a9ff04bSjmcneill fg->fg_unsol(fg->fg_device, entry->resp);
2648a9ff04bSjmcneill }
2658a9ff04bSjmcneill }
2668a9ff04bSjmcneill
2678a9ff04bSjmcneill static uint32_t
hdaudio_rirb_dequeue(struct hdaudio_softc * sc,bool unsol)2688a9ff04bSjmcneill hdaudio_rirb_dequeue(struct hdaudio_softc *sc, bool unsol)
2698a9ff04bSjmcneill {
2708a9ff04bSjmcneill uint16_t rirbwp;
2718a9ff04bSjmcneill uint64_t *rirb = DMA_KERNADDR(&sc->sc_rirb);
2728a9ff04bSjmcneill struct rirb_entry entry;
2738a9ff04bSjmcneill int retry;
2748a9ff04bSjmcneill
2758a9ff04bSjmcneill for (;;) {
2768a9ff04bSjmcneill retry = HDAUDIO_RIRB_TIMEOUT;
2778a9ff04bSjmcneill
2788a9ff04bSjmcneill rirbwp = hda_read2(sc, HDAUDIO_MMIO_RIRBWP);
2798a9ff04bSjmcneill while (--retry > 0 && (rirbwp & 0xff) == sc->sc_rirbrp) {
2808a9ff04bSjmcneill if (unsol) {
2818a9ff04bSjmcneill /* don't wait for more unsol events */
2828a9ff04bSjmcneill hda_trace(sc, "unsol: rirb empty\n");
2838a9ff04bSjmcneill return 0xffffffff;
2848a9ff04bSjmcneill }
2858a9ff04bSjmcneill hda_delay(10);
2868a9ff04bSjmcneill rirbwp = hda_read2(sc, HDAUDIO_MMIO_RIRBWP);
2878a9ff04bSjmcneill }
2888a9ff04bSjmcneill if (retry == 0) {
2898a9ff04bSjmcneill hda_error(sc, "RIRB timeout\n");
2908a9ff04bSjmcneill return 0xffffffff;
2918a9ff04bSjmcneill }
2928a9ff04bSjmcneill
2938a9ff04bSjmcneill sc->sc_rirbrp++;
2948a9ff04bSjmcneill if (sc->sc_rirbrp >= (sc->sc_rirb.dma_size / sizeof(*rirb)))
2958a9ff04bSjmcneill sc->sc_rirbrp = 0;
2968a9ff04bSjmcneill
2978a9ff04bSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_rirb.dma_map, 0,
2988a9ff04bSjmcneill sc->sc_rirb.dma_size, BUS_DMASYNC_POSTREAD);
2998a9ff04bSjmcneill entry = *(struct rirb_entry *)&rirb[sc->sc_rirbrp];
3008a9ff04bSjmcneill bus_dmamap_sync(sc->sc_dmat, sc->sc_rirb.dma_map, 0,
3018a9ff04bSjmcneill sc->sc_rirb.dma_size, BUS_DMASYNC_PREREAD);
3028a9ff04bSjmcneill
3038a9ff04bSjmcneill hda_trace(sc, "%s: response %08X %08X\n",
3048a9ff04bSjmcneill unsol ? "unsol" : "cmd ",
3058a9ff04bSjmcneill entry.resp, entry.resp_ex);
3068a9ff04bSjmcneill
3078a9ff04bSjmcneill if (RIRB_UNSOL(&entry)) {
3088a9ff04bSjmcneill hdaudio_rirb_unsol(sc, &entry);
3098a9ff04bSjmcneill continue;
3108a9ff04bSjmcneill }
3118a9ff04bSjmcneill
3128a9ff04bSjmcneill return entry.resp;
3138a9ff04bSjmcneill }
3148a9ff04bSjmcneill }
3158a9ff04bSjmcneill
3168a9ff04bSjmcneill uint32_t
hdaudio_command(struct hdaudio_codec * co,int nid,uint32_t control,uint32_t param)3178a9ff04bSjmcneill hdaudio_command(struct hdaudio_codec *co, int nid, uint32_t control,
3188a9ff04bSjmcneill uint32_t param)
3198a9ff04bSjmcneill {
3208a9ff04bSjmcneill uint32_t result;
3218a9ff04bSjmcneill struct hdaudio_softc *sc = co->co_host;
3228a9ff04bSjmcneill mutex_enter(&sc->sc_corb_mtx);
3238a9ff04bSjmcneill result = hdaudio_command_unlocked(co, nid, control, param);
3248a9ff04bSjmcneill mutex_exit(&sc->sc_corb_mtx);
3258a9ff04bSjmcneill return result;
3268a9ff04bSjmcneill }
3278a9ff04bSjmcneill
3288a9ff04bSjmcneill uint32_t
hdaudio_command_unlocked(struct hdaudio_codec * co,int nid,uint32_t control,uint32_t param)3298a9ff04bSjmcneill hdaudio_command_unlocked(struct hdaudio_codec *co, int nid, uint32_t control,
3308a9ff04bSjmcneill uint32_t param)
3318a9ff04bSjmcneill {
3328a9ff04bSjmcneill struct hdaudio_softc *sc = co->co_host;
3338a9ff04bSjmcneill uint32_t result;
3348a9ff04bSjmcneill
3358a9ff04bSjmcneill hda_trace(sc, "cmd : request %08X %08X (%02X)\n",
3368a9ff04bSjmcneill control, param, nid);
3378a9ff04bSjmcneill hdaudio_corb_enqueue(sc, co->co_addr, nid, control, param);
3388a9ff04bSjmcneill result = hdaudio_rirb_dequeue(sc, false);
3398a9ff04bSjmcneill
340ccc79c82Sjmcneill /* Clear response interrupt status */
341ccc79c82Sjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS, hda_read1(sc, HDAUDIO_MMIO_RIRBSTS));
342ccc79c82Sjmcneill
3438a9ff04bSjmcneill return result;
3448a9ff04bSjmcneill }
3458a9ff04bSjmcneill
3468a9ff04bSjmcneill static int
hdaudio_corb_setsize(struct hdaudio_softc * sc)3478a9ff04bSjmcneill hdaudio_corb_setsize(struct hdaudio_softc *sc)
3488a9ff04bSjmcneill {
3498a9ff04bSjmcneill uint8_t corbsize;
3508a9ff04bSjmcneill bus_size_t bufsize = 0;
3518a9ff04bSjmcneill
3528a9ff04bSjmcneill /*
3538a9ff04bSjmcneill * The size of the CORB is programmable to 2, 16, or 256 entries
3548a9ff04bSjmcneill * by using the CORBSIZE register. Choose a size based on the
3558a9ff04bSjmcneill * controller capabilities, preferring a larger size when possible.
3568a9ff04bSjmcneill */
3578a9ff04bSjmcneill corbsize = hda_read1(sc, HDAUDIO_MMIO_CORBSIZE);
3588a9ff04bSjmcneill corbsize &= ~0x3;
3598a9ff04bSjmcneill if ((corbsize >> 4) & 0x4) {
3608a9ff04bSjmcneill corbsize |= 0x2;
3618a9ff04bSjmcneill bufsize = 1024;
3628a9ff04bSjmcneill } else if ((corbsize >> 4) & 0x2) {
3638a9ff04bSjmcneill corbsize |= 0x1;
3648a9ff04bSjmcneill bufsize = 64;
3658a9ff04bSjmcneill } else if ((corbsize >> 4) & 0x1) {
3668a9ff04bSjmcneill corbsize |= 0x0;
3678a9ff04bSjmcneill bufsize = 8;
3688a9ff04bSjmcneill } else {
3698a9ff04bSjmcneill hda_error(sc, "couldn't configure CORB size\n");
3708a9ff04bSjmcneill return ENXIO;
3718a9ff04bSjmcneill }
3728a9ff04bSjmcneill
3738a9ff04bSjmcneill #if defined(HDAUDIO_DEBUG)
3748a9ff04bSjmcneill hda_print(sc, "using %d byte CORB (cap %X)\n",
3758a9ff04bSjmcneill (int)bufsize, corbsize >> 4);
3768a9ff04bSjmcneill #endif
3778a9ff04bSjmcneill
3788a9ff04bSjmcneill sc->sc_corb.dma_size = bufsize;
3798a9ff04bSjmcneill sc->sc_corb.dma_sizereg = corbsize;
3808a9ff04bSjmcneill
3818a9ff04bSjmcneill return 0;
3828a9ff04bSjmcneill }
3838a9ff04bSjmcneill
3848a9ff04bSjmcneill static int
hdaudio_corb_config(struct hdaudio_softc * sc)3858a9ff04bSjmcneill hdaudio_corb_config(struct hdaudio_softc *sc)
3868a9ff04bSjmcneill {
3878a9ff04bSjmcneill uint32_t corbubase, corblbase;
3888a9ff04bSjmcneill uint16_t corbrp;
3898a9ff04bSjmcneill int retry = HDAUDIO_CORB_TIMEOUT;
3908a9ff04bSjmcneill
3918a9ff04bSjmcneill /* Program command buffer base address and size */
3928a9ff04bSjmcneill corblbase = (uint32_t)DMA_DMAADDR(&sc->sc_corb);
3938a9ff04bSjmcneill corbubase = (uint32_t)(((uint64_t)DMA_DMAADDR(&sc->sc_corb)) >> 32);
3948a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_CORBLBASE, corblbase);
3958a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_CORBUBASE, corbubase);
3968a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_CORBSIZE, sc->sc_corb.dma_sizereg);
3978a9ff04bSjmcneill
3988a9ff04bSjmcneill /* Clear the read and write pointers */
3998a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBRP, HDAUDIO_CORBRP_RP_RESET);
4008a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBRP, 0);
4018a9ff04bSjmcneill do {
4028a9ff04bSjmcneill hda_delay(10);
4038a9ff04bSjmcneill corbrp = hda_read2(sc, HDAUDIO_MMIO_CORBRP);
4048a9ff04bSjmcneill } while (--retry > 0 && (corbrp & HDAUDIO_CORBRP_RP_RESET) != 0);
4058a9ff04bSjmcneill if (retry == 0) {
4068a9ff04bSjmcneill hda_error(sc, "timeout resetting CORB\n");
4078a9ff04bSjmcneill return ETIME;
4088a9ff04bSjmcneill }
4098a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_CORBWP, 0);
4108a9ff04bSjmcneill
4118a9ff04bSjmcneill return 0;
4128a9ff04bSjmcneill }
4138a9ff04bSjmcneill
4148a9ff04bSjmcneill static int
hdaudio_corb_stop(struct hdaudio_softc * sc)4158a9ff04bSjmcneill hdaudio_corb_stop(struct hdaudio_softc *sc)
4168a9ff04bSjmcneill {
4178a9ff04bSjmcneill uint8_t corbctl;
4188a9ff04bSjmcneill int retry = HDAUDIO_CORB_TIMEOUT;
4198a9ff04bSjmcneill
4208a9ff04bSjmcneill /* Stop the CORB if necessary */
4218a9ff04bSjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4228a9ff04bSjmcneill if (corbctl & HDAUDIO_CORBCTL_RUN) {
4238a9ff04bSjmcneill corbctl &= ~HDAUDIO_CORBCTL_RUN;
42452e61030Sjmcneill hda_write1(sc, HDAUDIO_MMIO_CORBCTL, corbctl);
4258a9ff04bSjmcneill do {
4268a9ff04bSjmcneill hda_delay(10);
42752e61030Sjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4288a9ff04bSjmcneill } while (--retry > 0 && (corbctl & HDAUDIO_CORBCTL_RUN) != 0);
4298a9ff04bSjmcneill if (retry == 0) {
4308a9ff04bSjmcneill hda_error(sc, "timeout stopping CORB\n");
4318a9ff04bSjmcneill return ETIME;
4328a9ff04bSjmcneill }
4338a9ff04bSjmcneill }
4348a9ff04bSjmcneill
4358a9ff04bSjmcneill return 0;
4368a9ff04bSjmcneill }
4378a9ff04bSjmcneill
4388a9ff04bSjmcneill static int
hdaudio_corb_start(struct hdaudio_softc * sc)4398a9ff04bSjmcneill hdaudio_corb_start(struct hdaudio_softc *sc)
4408a9ff04bSjmcneill {
4418a9ff04bSjmcneill uint8_t corbctl;
4428a9ff04bSjmcneill int retry = HDAUDIO_CORB_TIMEOUT;
4438a9ff04bSjmcneill
4448a9ff04bSjmcneill /* Start the CORB if necessary */
4458a9ff04bSjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4468a9ff04bSjmcneill if ((corbctl & HDAUDIO_CORBCTL_RUN) == 0) {
4478a9ff04bSjmcneill corbctl |= HDAUDIO_CORBCTL_RUN;
44852e61030Sjmcneill hda_write1(sc, HDAUDIO_MMIO_CORBCTL, corbctl);
4498a9ff04bSjmcneill do {
4508a9ff04bSjmcneill hda_delay(10);
45152e61030Sjmcneill corbctl = hda_read1(sc, HDAUDIO_MMIO_CORBCTL);
4528a9ff04bSjmcneill } while (--retry > 0 && (corbctl & HDAUDIO_CORBCTL_RUN) == 0);
4538a9ff04bSjmcneill if (retry == 0) {
4548a9ff04bSjmcneill hda_error(sc, "timeout starting CORB\n");
4558a9ff04bSjmcneill return ETIME;
4568a9ff04bSjmcneill }
4578a9ff04bSjmcneill }
4588a9ff04bSjmcneill
4598a9ff04bSjmcneill return 0;
4608a9ff04bSjmcneill }
4618a9ff04bSjmcneill
4628a9ff04bSjmcneill static int
hdaudio_rirb_stop(struct hdaudio_softc * sc)4638a9ff04bSjmcneill hdaudio_rirb_stop(struct hdaudio_softc *sc)
4648a9ff04bSjmcneill {
4658a9ff04bSjmcneill uint8_t rirbctl;
4668a9ff04bSjmcneill int retry = HDAUDIO_RIRB_TIMEOUT;
4678a9ff04bSjmcneill
4688a9ff04bSjmcneill /* Stop the RIRB if necessary */
4698a9ff04bSjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
4708a9ff04bSjmcneill if (rirbctl & (HDAUDIO_RIRBCTL_RUN|HDAUDIO_RIRBCTL_ROI_EN)) {
4718a9ff04bSjmcneill rirbctl &= ~HDAUDIO_RIRBCTL_RUN;
4728a9ff04bSjmcneill rirbctl &= ~HDAUDIO_RIRBCTL_ROI_EN;
4738a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBCTL, rirbctl);
4748a9ff04bSjmcneill do {
4758a9ff04bSjmcneill hda_delay(10);
4768a9ff04bSjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
4778a9ff04bSjmcneill } while (--retry > 0 && (rirbctl & HDAUDIO_RIRBCTL_RUN) != 0);
4788a9ff04bSjmcneill if (retry == 0) {
4798a9ff04bSjmcneill hda_error(sc, "timeout stopping RIRB\n");
4808a9ff04bSjmcneill return ETIME;
4818a9ff04bSjmcneill }
4828a9ff04bSjmcneill }
4838a9ff04bSjmcneill
4848a9ff04bSjmcneill return 0;
4858a9ff04bSjmcneill }
4868a9ff04bSjmcneill
4878a9ff04bSjmcneill static int
hdaudio_rirb_start(struct hdaudio_softc * sc)4888a9ff04bSjmcneill hdaudio_rirb_start(struct hdaudio_softc *sc)
4898a9ff04bSjmcneill {
4908a9ff04bSjmcneill uint8_t rirbctl;
4918a9ff04bSjmcneill int retry = HDAUDIO_RIRB_TIMEOUT;
4928a9ff04bSjmcneill
493ccc79c82Sjmcneill /* Set the RIRB interrupt count */
494ccc79c82Sjmcneill hda_write2(sc, HDAUDIO_MMIO_RINTCNT, 1);
495ccc79c82Sjmcneill
496ccc79c82Sjmcneill /* Start the RIRB */
4978a9ff04bSjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
4988a9ff04bSjmcneill rirbctl |= HDAUDIO_RIRBCTL_RUN;
4998a9ff04bSjmcneill rirbctl |= HDAUDIO_RIRBCTL_INT_EN;
5008a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBCTL, rirbctl);
5018a9ff04bSjmcneill do {
5028a9ff04bSjmcneill hda_delay(10);
5038a9ff04bSjmcneill rirbctl = hda_read1(sc, HDAUDIO_MMIO_RIRBCTL);
5048a9ff04bSjmcneill } while (--retry > 0 && (rirbctl & HDAUDIO_RIRBCTL_RUN) == 0);
5058a9ff04bSjmcneill if (retry == 0) {
5068a9ff04bSjmcneill hda_error(sc, "timeout starting RIRB\n");
5078a9ff04bSjmcneill return ETIME;
5088a9ff04bSjmcneill }
5098a9ff04bSjmcneill
5108a9ff04bSjmcneill return 0;
5118a9ff04bSjmcneill }
5128a9ff04bSjmcneill
5138a9ff04bSjmcneill static int
hdaudio_rirb_setsize(struct hdaudio_softc * sc)5148a9ff04bSjmcneill hdaudio_rirb_setsize(struct hdaudio_softc *sc)
5158a9ff04bSjmcneill {
5168a9ff04bSjmcneill uint8_t rirbsize;
5178a9ff04bSjmcneill bus_size_t bufsize = 0;
5188a9ff04bSjmcneill
5198a9ff04bSjmcneill /*
5208a9ff04bSjmcneill * The size of the RIRB is programmable to 2, 16, or 256 entries
5218a9ff04bSjmcneill * by using the RIRBSIZE register. Choose a size based on the
5228a9ff04bSjmcneill * controller capabilities, preferring a larger size when possible.
5238a9ff04bSjmcneill */
5248a9ff04bSjmcneill rirbsize = hda_read1(sc, HDAUDIO_MMIO_RIRBSIZE);
5258a9ff04bSjmcneill rirbsize &= ~0x3;
5268a9ff04bSjmcneill if ((rirbsize >> 4) & 0x4) {
5278a9ff04bSjmcneill rirbsize |= 0x2;
5288a9ff04bSjmcneill bufsize = 2048;
5298a9ff04bSjmcneill } else if ((rirbsize >> 4) & 0x2) {
5308a9ff04bSjmcneill rirbsize |= 0x1;
5318a9ff04bSjmcneill bufsize = 128;
5328a9ff04bSjmcneill } else if ((rirbsize >> 4) & 0x1) {
5338a9ff04bSjmcneill rirbsize |= 0x0;
5348a9ff04bSjmcneill bufsize = 16;
5358a9ff04bSjmcneill } else {
5368a9ff04bSjmcneill hda_error(sc, "couldn't configure RIRB size\n");
5378a9ff04bSjmcneill return ENXIO;
5388a9ff04bSjmcneill }
5398a9ff04bSjmcneill
5408a9ff04bSjmcneill #if defined(HDAUDIO_DEBUG)
5418a9ff04bSjmcneill hda_print(sc, "using %d byte RIRB (cap %X)\n",
5428a9ff04bSjmcneill (int)bufsize, rirbsize >> 4);
5438a9ff04bSjmcneill #endif
5448a9ff04bSjmcneill
5458a9ff04bSjmcneill sc->sc_rirb.dma_size = bufsize;
5468a9ff04bSjmcneill sc->sc_rirb.dma_sizereg = rirbsize;
5478a9ff04bSjmcneill
5488a9ff04bSjmcneill return 0;
5498a9ff04bSjmcneill }
5508a9ff04bSjmcneill
5518a9ff04bSjmcneill static int
hdaudio_rirb_config(struct hdaudio_softc * sc)5528a9ff04bSjmcneill hdaudio_rirb_config(struct hdaudio_softc *sc)
5538a9ff04bSjmcneill {
5548a9ff04bSjmcneill uint32_t rirbubase, rirblbase;
5558a9ff04bSjmcneill
5568a9ff04bSjmcneill /* Program command buffer base address and size */
5578a9ff04bSjmcneill rirblbase = (uint32_t)DMA_DMAADDR(&sc->sc_rirb);
5588a9ff04bSjmcneill rirbubase = (uint32_t)(((uint64_t)DMA_DMAADDR(&sc->sc_rirb)) >> 32);
5598a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_RIRBLBASE, rirblbase);
5608a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_RIRBUBASE, rirbubase);
5618a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSIZE, sc->sc_rirb.dma_sizereg);
5628a9ff04bSjmcneill
5638a9ff04bSjmcneill /* Clear the write pointer */
5648a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_RIRBWP, HDAUDIO_RIRBWP_WP_RESET);
5658a9ff04bSjmcneill sc->sc_rirbrp = 0;
5668a9ff04bSjmcneill
5678a9ff04bSjmcneill return 0;
5688a9ff04bSjmcneill }
5698a9ff04bSjmcneill
5708a9ff04bSjmcneill static int
hdaudio_reset(struct hdaudio_softc * sc)5718a9ff04bSjmcneill hdaudio_reset(struct hdaudio_softc *sc)
5728a9ff04bSjmcneill {
5738a9ff04bSjmcneill int retry = HDAUDIO_RESET_TIMEOUT;
5748a9ff04bSjmcneill uint32_t gctl;
5758a9ff04bSjmcneill int err;
5768a9ff04bSjmcneill
5778a9ff04bSjmcneill if ((err = hdaudio_rirb_stop(sc)) != 0) {
5788a9ff04bSjmcneill hda_error(sc, "couldn't reset because RIRB is busy\n");
5798a9ff04bSjmcneill return err;
5808a9ff04bSjmcneill }
5818a9ff04bSjmcneill if ((err = hdaudio_corb_stop(sc)) != 0) {
5828a9ff04bSjmcneill hda_error(sc, "couldn't reset because CORB is busy\n");
5838a9ff04bSjmcneill return err;
5848a9ff04bSjmcneill }
5858a9ff04bSjmcneill
5868a9ff04bSjmcneill /* Disable wake events */
5878a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_WAKEEN, 0);
5888a9ff04bSjmcneill
5898a9ff04bSjmcneill /* Disable interrupts */
5908a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, 0);
5918a9ff04bSjmcneill
5928a9ff04bSjmcneill /* Clear state change status register */
5938a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS,
5948a9ff04bSjmcneill hda_read2(sc, HDAUDIO_MMIO_STATESTS));
5958a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS,
5968a9ff04bSjmcneill hda_read1(sc, HDAUDIO_MMIO_RIRBSTS));
5978a9ff04bSjmcneill
59827292242Sjmcneill /* Put the controller into reset state */
5998a9ff04bSjmcneill gctl = hda_read4(sc, HDAUDIO_MMIO_GCTL);
6008a9ff04bSjmcneill gctl &= ~HDAUDIO_GCTL_CRST;
6018a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_GCTL, gctl);
6028a9ff04bSjmcneill do {
6038a9ff04bSjmcneill hda_delay(10);
6048a9ff04bSjmcneill gctl = hda_read4(sc, HDAUDIO_MMIO_GCTL);
6058a9ff04bSjmcneill } while (--retry > 0 && (gctl & HDAUDIO_GCTL_CRST) != 0);
6068a9ff04bSjmcneill if (retry == 0) {
6078a9ff04bSjmcneill hda_error(sc, "timeout entering reset state\n");
6088a9ff04bSjmcneill return ETIME;
6098a9ff04bSjmcneill }
61027292242Sjmcneill
61127292242Sjmcneill hda_delay(1000);
6128a9ff04bSjmcneill
6138a9ff04bSjmcneill /* Now the controller is in reset state, so bring it out */
6148a9ff04bSjmcneill retry = HDAUDIO_RESET_TIMEOUT;
6158a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_GCTL, gctl | HDAUDIO_GCTL_CRST);
6168a9ff04bSjmcneill do {
6178a9ff04bSjmcneill hda_delay(10);
6188a9ff04bSjmcneill gctl = hda_read4(sc, HDAUDIO_MMIO_GCTL);
6198a9ff04bSjmcneill } while (--retry > 0 && (gctl & HDAUDIO_GCTL_CRST) == 0);
6208a9ff04bSjmcneill if (retry == 0) {
6218a9ff04bSjmcneill hda_error(sc, "timeout leaving reset state\n");
6228a9ff04bSjmcneill return ETIME;
6238a9ff04bSjmcneill }
6248a9ff04bSjmcneill
62527292242Sjmcneill hda_delay(2000);
62627292242Sjmcneill
6278a9ff04bSjmcneill /* Accept unsolicited responses */
6288a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_GCTL, gctl | HDAUDIO_GCTL_UNSOL_EN);
6298a9ff04bSjmcneill
6308a9ff04bSjmcneill return 0;
6318a9ff04bSjmcneill }
6328a9ff04bSjmcneill
6338a9ff04bSjmcneill static void
hdaudio_intr_enable(struct hdaudio_softc * sc)6348a9ff04bSjmcneill hdaudio_intr_enable(struct hdaudio_softc *sc)
6358a9ff04bSjmcneill {
6368a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS,
6378a9ff04bSjmcneill hda_read4(sc, HDAUDIO_MMIO_INTSTS));
6388a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL,
6398a9ff04bSjmcneill HDAUDIO_INTCTL_GIE | HDAUDIO_INTCTL_CIE);
6408a9ff04bSjmcneill }
6418a9ff04bSjmcneill
6428a9ff04bSjmcneill static void
hdaudio_intr_disable(struct hdaudio_softc * sc)6438a9ff04bSjmcneill hdaudio_intr_disable(struct hdaudio_softc *sc)
6448a9ff04bSjmcneill {
6458a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, 0);
6468a9ff04bSjmcneill }
6478a9ff04bSjmcneill
6488a9ff04bSjmcneill static int
hdaudio_config_print(void * opaque,const char * pnp)6498a9ff04bSjmcneill hdaudio_config_print(void *opaque, const char *pnp)
6508a9ff04bSjmcneill {
6518a9ff04bSjmcneill prop_dictionary_t dict = opaque;
6528a9ff04bSjmcneill uint8_t fgtype, nid;
6538a9ff04bSjmcneill uint16_t vendor, product;
6548a9ff04bSjmcneill const char *type = "unknown";
6558a9ff04bSjmcneill
6568a9ff04bSjmcneill prop_dictionary_get_uint8(dict, "function-group-type", &fgtype);
6578a9ff04bSjmcneill prop_dictionary_get_uint8(dict, "node-id", &nid);
6588a9ff04bSjmcneill prop_dictionary_get_uint16(dict, "vendor-id", &vendor);
6598a9ff04bSjmcneill prop_dictionary_get_uint16(dict, "product-id", &product);
6608a9ff04bSjmcneill if (pnp) {
6618a9ff04bSjmcneill if (fgtype == HDAUDIO_GROUP_TYPE_AFG)
6628a9ff04bSjmcneill type = "hdafg";
6638a9ff04bSjmcneill else if (fgtype == HDAUDIO_GROUP_TYPE_VSM_FG)
6648a9ff04bSjmcneill type = "hdvsmfg";
6658a9ff04bSjmcneill
6668a9ff04bSjmcneill aprint_normal("%s at %s", type, pnp);
6678a9ff04bSjmcneill }
6688a9ff04bSjmcneill aprint_debug(" vendor 0x%04X product 0x%04X nid 0x%02X",
6698a9ff04bSjmcneill vendor, product, nid);
6708a9ff04bSjmcneill
6718a9ff04bSjmcneill return UNCONF;
6728a9ff04bSjmcneill }
6738a9ff04bSjmcneill
6748a9ff04bSjmcneill static void
hdaudio_attach_fg(struct hdaudio_function_group * fg,prop_array_t config)6758a9ff04bSjmcneill hdaudio_attach_fg(struct hdaudio_function_group *fg, prop_array_t config)
6768a9ff04bSjmcneill {
6778a9ff04bSjmcneill struct hdaudio_codec *co = fg->fg_codec;
6788a9ff04bSjmcneill struct hdaudio_softc *sc = co->co_host;
6798a9ff04bSjmcneill prop_dictionary_t args = prop_dictionary_create();
6808a9ff04bSjmcneill uint64_t fgptr = (vaddr_t)fg;
6818a9ff04bSjmcneill int locs[1];
6828a9ff04bSjmcneill
6838a9ff04bSjmcneill prop_dictionary_set_uint8(args, "function-group-type", fg->fg_type);
6848a9ff04bSjmcneill prop_dictionary_set_uint64(args, "function-group", fgptr);
6858a9ff04bSjmcneill prop_dictionary_set_uint8(args, "node-id", fg->fg_nid);
6868a9ff04bSjmcneill prop_dictionary_set_uint16(args, "vendor-id", fg->fg_vendor);
6878a9ff04bSjmcneill prop_dictionary_set_uint16(args, "product-id", fg->fg_product);
6888a9ff04bSjmcneill if (config)
6898a9ff04bSjmcneill prop_dictionary_set(args, "pin-config", config);
6908a9ff04bSjmcneill
6918a9ff04bSjmcneill locs[0] = fg->fg_nid;
6928a9ff04bSjmcneill
6932685996bSthorpej fg->fg_device = config_found(sc->sc_dev, args, hdaudio_config_print,
694c7fb772bSthorpej CFARGS(.submatch = config_stdsubmatch,
695c7fb772bSthorpej .locators = locs));
6968a9ff04bSjmcneill
6978a9ff04bSjmcneill prop_object_release(args);
6988a9ff04bSjmcneill }
6998a9ff04bSjmcneill
7008a9ff04bSjmcneill static void
hdaudio_codec_attach(struct hdaudio_codec * co)7018a9ff04bSjmcneill hdaudio_codec_attach(struct hdaudio_codec *co)
7028a9ff04bSjmcneill {
703b547d839Sjmcneill struct hdaudio_softc *sc = co->co_host;
7048a9ff04bSjmcneill struct hdaudio_function_group *fg;
7058a9ff04bSjmcneill uint32_t vid, snc, fgrp;
7068a9ff04bSjmcneill int starting_node, num_nodes, nid;
7078a9ff04bSjmcneill
7088a9ff04bSjmcneill if (co->co_valid == false)
7098a9ff04bSjmcneill return;
7108a9ff04bSjmcneill
7118a9ff04bSjmcneill vid = hdaudio_command(co, 0, CORB_GET_PARAMETER, COP_VENDOR_ID);
7128a9ff04bSjmcneill snc = hdaudio_command(co, 0, CORB_GET_PARAMETER,
7138a9ff04bSjmcneill COP_SUBORDINATE_NODE_COUNT);
7148a9ff04bSjmcneill
7158a9ff04bSjmcneill /* make sure the vendor and product IDs are valid */
7168a9ff04bSjmcneill if (vid == 0xffffffff || vid == 0x00000000)
7178a9ff04bSjmcneill return;
7188a9ff04bSjmcneill
7198a9ff04bSjmcneill #ifdef HDAUDIO_DEBUG
7208a9ff04bSjmcneill uint32_t rid = hdaudio_command(co, 0, CORB_GET_PARAMETER,
7218a9ff04bSjmcneill COP_REVISION_ID);
7228a9ff04bSjmcneill hda_print(sc, "Codec%02X: %04X:%04X HDA %d.%d rev %d stepping %d\n",
7238a9ff04bSjmcneill co->co_addr, vid >> 16, vid & 0xffff,
7248a9ff04bSjmcneill (rid >> 20) & 0xf, (rid >> 16) & 0xf,
7258a9ff04bSjmcneill (rid >> 8) & 0xff, rid & 0xff);
7268a9ff04bSjmcneill #endif
7278a9ff04bSjmcneill starting_node = (snc >> 16) & 0xff;
7288a9ff04bSjmcneill num_nodes = snc & 0xff;
7298a9ff04bSjmcneill
730b547d839Sjmcneill /*
731b547d839Sjmcneill * If the total number of nodes is 0, there's nothing we can do.
732b547d839Sjmcneill * This shouldn't happen, so complain about it.
733b547d839Sjmcneill */
734b547d839Sjmcneill if (num_nodes == 0) {
735b547d839Sjmcneill hda_error(sc, "Codec%02X: No subordinate nodes found (%08x)\n",
736b547d839Sjmcneill co->co_addr, snc);
737b547d839Sjmcneill return;
738b547d839Sjmcneill }
739b547d839Sjmcneill
7408a9ff04bSjmcneill co->co_nfg = num_nodes;
7418a9ff04bSjmcneill co->co_fg = kmem_zalloc(co->co_nfg * sizeof(*co->co_fg), KM_SLEEP);
7428a9ff04bSjmcneill
7438a9ff04bSjmcneill for (nid = starting_node; nid < starting_node + num_nodes; nid++) {
7448a9ff04bSjmcneill fg = &co->co_fg[nid - starting_node];
7458a9ff04bSjmcneill fg->fg_codec = co;
7468a9ff04bSjmcneill fg->fg_nid = nid;
7478a9ff04bSjmcneill fg->fg_vendor = vid >> 16;
7488a9ff04bSjmcneill fg->fg_product = vid & 0xffff;
7498a9ff04bSjmcneill
7508a9ff04bSjmcneill fgrp = hdaudio_command(co, nid, CORB_GET_PARAMETER,
7518a9ff04bSjmcneill COP_FUNCTION_GROUP_TYPE);
7528a9ff04bSjmcneill switch (fgrp & 0xff) {
7538a9ff04bSjmcneill case 0x01: /* Audio Function Group */
7548a9ff04bSjmcneill fg->fg_type = HDAUDIO_GROUP_TYPE_AFG;
7558a9ff04bSjmcneill break;
7568a9ff04bSjmcneill case 0x02: /* Vendor Specific Modem Function Group */
7578a9ff04bSjmcneill fg->fg_type = HDAUDIO_GROUP_TYPE_VSM_FG;
7588a9ff04bSjmcneill break;
7598a9ff04bSjmcneill default:
7608a9ff04bSjmcneill /* Function group type not supported */
7618a9ff04bSjmcneill fg->fg_type = HDAUDIO_GROUP_TYPE_UNKNOWN;
7628a9ff04bSjmcneill break;
7638a9ff04bSjmcneill }
7648a9ff04bSjmcneill hdaudio_attach_fg(fg, NULL);
7658a9ff04bSjmcneill }
7668a9ff04bSjmcneill }
7678a9ff04bSjmcneill
7688a9ff04bSjmcneill int
hdaudio_stream_tag(struct hdaudio_stream * st)7698a9ff04bSjmcneill hdaudio_stream_tag(struct hdaudio_stream *st)
7708a9ff04bSjmcneill {
7718a9ff04bSjmcneill int ret = 0;
7728a9ff04bSjmcneill
7738a9ff04bSjmcneill switch (st->st_type) {
7748a9ff04bSjmcneill case HDAUDIO_STREAM_ISS:
7758a9ff04bSjmcneill ret = 1;
7768a9ff04bSjmcneill break;
7778a9ff04bSjmcneill case HDAUDIO_STREAM_OSS:
7788a9ff04bSjmcneill ret = 2;
7798a9ff04bSjmcneill break;
7808a9ff04bSjmcneill case HDAUDIO_STREAM_BSS:
7818a9ff04bSjmcneill ret = 3;
7828a9ff04bSjmcneill break;
7838a9ff04bSjmcneill }
7848a9ff04bSjmcneill
7858a9ff04bSjmcneill return ret;
7868a9ff04bSjmcneill }
7878a9ff04bSjmcneill
7888a9ff04bSjmcneill int
hdaudio_attach(device_t dev,struct hdaudio_softc * sc)7898a9ff04bSjmcneill hdaudio_attach(device_t dev, struct hdaudio_softc *sc)
7908a9ff04bSjmcneill {
7918a9ff04bSjmcneill int err, i;
7928a9ff04bSjmcneill
7938a9ff04bSjmcneill KASSERT(sc->sc_memvalid == true);
7948a9ff04bSjmcneill
7958a9ff04bSjmcneill sc->sc_dev = dev;
7968a9ff04bSjmcneill mutex_init(&sc->sc_corb_mtx, MUTEX_DEFAULT, IPL_AUDIO);
7978a9ff04bSjmcneill mutex_init(&sc->sc_stream_mtx, MUTEX_DEFAULT, IPL_AUDIO);
7988a9ff04bSjmcneill
7998a9ff04bSjmcneill /*
8008a9ff04bSjmcneill * Put the controller into a known state by entering and leaving
8018a9ff04bSjmcneill * CRST as necessary.
8028a9ff04bSjmcneill */
8038a9ff04bSjmcneill if ((err = hdaudio_reset(sc)) != 0)
8048a9ff04bSjmcneill goto fail;
8058a9ff04bSjmcneill
8068a9ff04bSjmcneill /*
8078a9ff04bSjmcneill * From the spec:
8088a9ff04bSjmcneill *
8098a9ff04bSjmcneill * Must wait 250us after reading CRST as a 1 before assuming that
8108a9ff04bSjmcneill * codecs have all made status change requests and have been
8118a9ff04bSjmcneill * registered by the controller.
8128a9ff04bSjmcneill *
8138a9ff04bSjmcneill * In reality, we need to wait longer than this.
8148a9ff04bSjmcneill */
8158a9ff04bSjmcneill hda_delay(HDAUDIO_CODEC_DELAY);
816e83803dfSjmcneill
817e83803dfSjmcneill /*
818e83803dfSjmcneill * Read device capabilities
819e83803dfSjmcneill */
820e83803dfSjmcneill hdaudio_init(sc);
821e83803dfSjmcneill
822e83803dfSjmcneill /*
823e83803dfSjmcneill * Detect codecs
824e83803dfSjmcneill */
8258a9ff04bSjmcneill if (hdaudio_codec_probe(sc) == 0) {
8268a9ff04bSjmcneill hda_error(sc, "no codecs found\n");
8278a9ff04bSjmcneill err = ENODEV;
8288a9ff04bSjmcneill goto fail;
8298a9ff04bSjmcneill }
8308a9ff04bSjmcneill
8318a9ff04bSjmcneill /*
8328a9ff04bSjmcneill * Ensure that the device is in a known state
8338a9ff04bSjmcneill */
8348a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS, HDAUDIO_STATESTS_SDIWAKE);
8358a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS,
8368a9ff04bSjmcneill HDAUDIO_RIRBSTS_RIRBOIS | HDAUDIO_RIRBSTS_RINTFL);
8378a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS,
8388a9ff04bSjmcneill hda_read4(sc, HDAUDIO_MMIO_INTSTS));
8398a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_DPLBASE, 0);
8408a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_DPUBASE, 0);
8418a9ff04bSjmcneill
8428a9ff04bSjmcneill /*
8438a9ff04bSjmcneill * Initialize the CORB. First negotiate a command buffer size,
8448a9ff04bSjmcneill * then allocate and configure it.
8458a9ff04bSjmcneill */
8468a9ff04bSjmcneill if ((err = hdaudio_corb_setsize(sc)) != 0)
8478a9ff04bSjmcneill goto fail;
8488a9ff04bSjmcneill if ((err = hdaudio_dma_alloc(sc, &sc->sc_corb, BUS_DMA_WRITE)) != 0)
8498a9ff04bSjmcneill goto fail;
8508a9ff04bSjmcneill if ((err = hdaudio_corb_config(sc)) != 0)
8518a9ff04bSjmcneill goto fail;
8528a9ff04bSjmcneill
8538a9ff04bSjmcneill /*
8548a9ff04bSjmcneill * Initialize the RIRB.
8558a9ff04bSjmcneill */
8568a9ff04bSjmcneill if ((err = hdaudio_rirb_setsize(sc)) != 0)
8578a9ff04bSjmcneill goto fail;
8588a9ff04bSjmcneill if ((err = hdaudio_dma_alloc(sc, &sc->sc_rirb, BUS_DMA_READ)) != 0)
8598a9ff04bSjmcneill goto fail;
8608a9ff04bSjmcneill if ((err = hdaudio_rirb_config(sc)) != 0)
8618a9ff04bSjmcneill goto fail;
8628a9ff04bSjmcneill
8638a9ff04bSjmcneill /*
8648a9ff04bSjmcneill * Start the CORB and RIRB
8658a9ff04bSjmcneill */
8668a9ff04bSjmcneill if ((err = hdaudio_corb_start(sc)) != 0)
8678a9ff04bSjmcneill goto fail;
8688a9ff04bSjmcneill if ((err = hdaudio_rirb_start(sc)) != 0)
8698a9ff04bSjmcneill goto fail;
8708a9ff04bSjmcneill
8718a9ff04bSjmcneill /*
8728a9ff04bSjmcneill * Identify and attach discovered codecs
8738a9ff04bSjmcneill */
8748a9ff04bSjmcneill for (i = 0; i < HDAUDIO_MAX_CODECS; i++)
8758a9ff04bSjmcneill hdaudio_codec_attach(&sc->sc_codec[i]);
8768a9ff04bSjmcneill
8778a9ff04bSjmcneill /*
8788a9ff04bSjmcneill * Enable interrupts
8798a9ff04bSjmcneill */
8808a9ff04bSjmcneill hdaudio_intr_enable(sc);
8818a9ff04bSjmcneill
8828a9ff04bSjmcneill fail:
8838a9ff04bSjmcneill if (err)
8848a9ff04bSjmcneill hda_error(sc, "device driver failed to attach\n");
8858a9ff04bSjmcneill return err;
8868a9ff04bSjmcneill }
8878a9ff04bSjmcneill
8888a9ff04bSjmcneill int
hdaudio_detach(struct hdaudio_softc * sc,int flags)8898a9ff04bSjmcneill hdaudio_detach(struct hdaudio_softc *sc, int flags)
8908a9ff04bSjmcneill {
8918a9ff04bSjmcneill int error;
8928a9ff04bSjmcneill
8938a9ff04bSjmcneill /* Disable interrupts */
8948a9ff04bSjmcneill hdaudio_intr_disable(sc);
8958a9ff04bSjmcneill
8968a9ff04bSjmcneill error = config_detach_children(sc->sc_dev, flags);
8978a9ff04bSjmcneill if (error != 0) {
8988a9ff04bSjmcneill hdaudio_intr_enable(sc);
8998a9ff04bSjmcneill return error;
9008a9ff04bSjmcneill }
9018a9ff04bSjmcneill
9028a9ff04bSjmcneill mutex_destroy(&sc->sc_corb_mtx);
9038a9ff04bSjmcneill mutex_destroy(&sc->sc_stream_mtx);
9048a9ff04bSjmcneill
9058a9ff04bSjmcneill hdaudio_dma_free(sc, &sc->sc_corb);
9068a9ff04bSjmcneill hdaudio_dma_free(sc, &sc->sc_rirb);
9078a9ff04bSjmcneill
9088a9ff04bSjmcneill return 0;
9098a9ff04bSjmcneill }
9108a9ff04bSjmcneill
9118a9ff04bSjmcneill bool
hdaudio_resume(struct hdaudio_softc * sc)9128a9ff04bSjmcneill hdaudio_resume(struct hdaudio_softc *sc)
9138a9ff04bSjmcneill {
9148a9ff04bSjmcneill if (hdaudio_reset(sc) != 0)
9158a9ff04bSjmcneill return false;
9168a9ff04bSjmcneill
9178a9ff04bSjmcneill hda_delay(HDAUDIO_CODEC_DELAY);
9188a9ff04bSjmcneill
9198a9ff04bSjmcneill /*
9208a9ff04bSjmcneill * Ensure that the device is in a known state
9218a9ff04bSjmcneill */
9228a9ff04bSjmcneill hda_write2(sc, HDAUDIO_MMIO_STATESTS, HDAUDIO_STATESTS_SDIWAKE);
9238a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS,
9248a9ff04bSjmcneill HDAUDIO_RIRBSTS_RIRBOIS | HDAUDIO_RIRBSTS_RINTFL);
9258a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS,
9268a9ff04bSjmcneill hda_read4(sc, HDAUDIO_MMIO_INTSTS));
9278a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_DPLBASE, 0);
9288a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_DPUBASE, 0);
9298a9ff04bSjmcneill
9308a9ff04bSjmcneill if (hdaudio_corb_config(sc) != 0)
9318a9ff04bSjmcneill return false;
9328a9ff04bSjmcneill if (hdaudio_rirb_config(sc) != 0)
9338a9ff04bSjmcneill return false;
9348a9ff04bSjmcneill if (hdaudio_corb_start(sc) != 0)
9358a9ff04bSjmcneill return false;
9368a9ff04bSjmcneill if (hdaudio_rirb_start(sc) != 0)
9378a9ff04bSjmcneill return false;
9388a9ff04bSjmcneill
9398a9ff04bSjmcneill hdaudio_intr_enable(sc);
9408a9ff04bSjmcneill
9418a9ff04bSjmcneill return true;
9428a9ff04bSjmcneill }
9438a9ff04bSjmcneill
9448a9ff04bSjmcneill int
hdaudio_rescan(struct hdaudio_softc * sc,const char * ifattr,const int * locs)9458a9ff04bSjmcneill hdaudio_rescan(struct hdaudio_softc *sc, const char *ifattr, const int *locs)
9468a9ff04bSjmcneill {
9478a9ff04bSjmcneill struct hdaudio_codec *co;
9488a9ff04bSjmcneill struct hdaudio_function_group *fg;
9498a9ff04bSjmcneill unsigned int codec;
9508a9ff04bSjmcneill
9518a9ff04bSjmcneill for (codec = 0; codec < HDAUDIO_MAX_CODECS; codec++) {
9528a9ff04bSjmcneill co = &sc->sc_codec[codec];
9538a9ff04bSjmcneill fg = co->co_fg;
9548a9ff04bSjmcneill if (!co->co_valid || fg == NULL)
9558a9ff04bSjmcneill continue;
9568a9ff04bSjmcneill if (fg->fg_device)
9578a9ff04bSjmcneill continue;
9588a9ff04bSjmcneill hdaudio_attach_fg(fg, NULL);
9598a9ff04bSjmcneill }
9608a9ff04bSjmcneill
9618a9ff04bSjmcneill return 0;
9628a9ff04bSjmcneill }
9638a9ff04bSjmcneill
9648a9ff04bSjmcneill void
hdaudio_childdet(struct hdaudio_softc * sc,device_t child)9658a9ff04bSjmcneill hdaudio_childdet(struct hdaudio_softc *sc, device_t child)
9668a9ff04bSjmcneill {
9678a9ff04bSjmcneill struct hdaudio_codec *co;
9688a9ff04bSjmcneill struct hdaudio_function_group *fg;
9698a9ff04bSjmcneill unsigned int codec;
9708a9ff04bSjmcneill
9718a9ff04bSjmcneill for (codec = 0; codec < HDAUDIO_MAX_CODECS; codec++) {
9728a9ff04bSjmcneill co = &sc->sc_codec[codec];
9738a9ff04bSjmcneill fg = co->co_fg;
9748a9ff04bSjmcneill if (!co->co_valid || fg == NULL)
9758a9ff04bSjmcneill continue;
9768a9ff04bSjmcneill if (fg->fg_device == child)
9778a9ff04bSjmcneill fg->fg_device = NULL;
9788a9ff04bSjmcneill }
9798a9ff04bSjmcneill }
9808a9ff04bSjmcneill
9818a9ff04bSjmcneill int
hdaudio_intr(struct hdaudio_softc * sc)9828a9ff04bSjmcneill hdaudio_intr(struct hdaudio_softc *sc)
9838a9ff04bSjmcneill {
9848a9ff04bSjmcneill struct hdaudio_stream *st;
9858a9ff04bSjmcneill uint32_t intsts, stream_mask;
9868a9ff04bSjmcneill int streamid = 0;
9878a9ff04bSjmcneill uint8_t rirbsts;
9888a9ff04bSjmcneill
9898a9ff04bSjmcneill intsts = hda_read4(sc, HDAUDIO_MMIO_INTSTS);
9908a9ff04bSjmcneill if (!(intsts & HDAUDIO_INTSTS_GIS))
9918a9ff04bSjmcneill return 0;
9928a9ff04bSjmcneill
9938a9ff04bSjmcneill if (intsts & HDAUDIO_INTSTS_CIS) {
9948a9ff04bSjmcneill rirbsts = hda_read1(sc, HDAUDIO_MMIO_RIRBSTS);
9958a9ff04bSjmcneill if (rirbsts & HDAUDIO_RIRBSTS_RINTFL) {
9968a9ff04bSjmcneill mutex_enter(&sc->sc_corb_mtx);
9978a9ff04bSjmcneill hdaudio_rirb_dequeue(sc, true);
9988a9ff04bSjmcneill mutex_exit(&sc->sc_corb_mtx);
9998a9ff04bSjmcneill }
10008a9ff04bSjmcneill if (rirbsts & (HDAUDIO_RIRBSTS_RIRBOIS|HDAUDIO_RIRBSTS_RINTFL))
10018a9ff04bSjmcneill hda_write1(sc, HDAUDIO_MMIO_RIRBSTS, rirbsts);
10028a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS, HDAUDIO_INTSTS_CIS);
10038a9ff04bSjmcneill }
10048a9ff04bSjmcneill if (intsts & HDAUDIO_INTSTS_SIS_MASK) {
10058a9ff04bSjmcneill mutex_enter(&sc->sc_stream_mtx);
10068a9ff04bSjmcneill stream_mask = intsts & sc->sc_stream_mask;
10078a9ff04bSjmcneill while (streamid < HDAUDIO_MAX_STREAMS && stream_mask != 0) {
10088a9ff04bSjmcneill st = &sc->sc_stream[streamid++];
10098a9ff04bSjmcneill if ((stream_mask & 1) != 0 && st->st_intr) {
10108a9ff04bSjmcneill st->st_intr(st);
10118a9ff04bSjmcneill }
10128a9ff04bSjmcneill stream_mask >>= 1;
10138a9ff04bSjmcneill }
10148a9ff04bSjmcneill mutex_exit(&sc->sc_stream_mtx);
10158a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTSTS, HDAUDIO_INTSTS_SIS_MASK);
10168a9ff04bSjmcneill }
10178a9ff04bSjmcneill
10188a9ff04bSjmcneill return 1;
10198a9ff04bSjmcneill }
10208a9ff04bSjmcneill
10218a9ff04bSjmcneill struct hdaudio_stream *
hdaudio_stream_establish(struct hdaudio_softc * sc,enum hdaudio_stream_type type,int (* intr)(struct hdaudio_stream *),void * cookie)10228a9ff04bSjmcneill hdaudio_stream_establish(struct hdaudio_softc *sc,
10238a9ff04bSjmcneill enum hdaudio_stream_type type, int (*intr)(struct hdaudio_stream *),
10248a9ff04bSjmcneill void *cookie)
10258a9ff04bSjmcneill {
10268a9ff04bSjmcneill struct hdaudio_stream *st;
10278a9ff04bSjmcneill struct hdaudio_dma dma;
10288a9ff04bSjmcneill int i, err;
10298a9ff04bSjmcneill
10308a9ff04bSjmcneill dma.dma_size = sizeof(struct hdaudio_bdl_entry) * HDAUDIO_BDL_MAX;
103116adabdcSriastradh dma.dma_sizereg = 0;
10328a9ff04bSjmcneill err = hdaudio_dma_alloc(sc, &dma, BUS_DMA_COHERENT | BUS_DMA_NOCACHE);
10338a9ff04bSjmcneill if (err)
10348a9ff04bSjmcneill return NULL;
10358a9ff04bSjmcneill
10368a9ff04bSjmcneill mutex_enter(&sc->sc_stream_mtx);
10378a9ff04bSjmcneill for (i = 0; i < HDAUDIO_MAX_STREAMS; i++) {
10388a9ff04bSjmcneill st = &sc->sc_stream[i];
10398a9ff04bSjmcneill if (st->st_enable == false)
10408a9ff04bSjmcneill break;
10418a9ff04bSjmcneill if (st->st_type != type)
10428a9ff04bSjmcneill continue;
10438a9ff04bSjmcneill if (sc->sc_stream_mask & (1 << i))
10448a9ff04bSjmcneill continue;
10458a9ff04bSjmcneill
10468a9ff04bSjmcneill /* Allocate stream */
10478a9ff04bSjmcneill st->st_bdl = dma;
10488a9ff04bSjmcneill st->st_intr = intr;
10498a9ff04bSjmcneill st->st_cookie = cookie;
10508a9ff04bSjmcneill sc->sc_stream_mask |= (1 << i);
10518a9ff04bSjmcneill mutex_exit(&sc->sc_stream_mtx);
10528a9ff04bSjmcneill return st;
10538a9ff04bSjmcneill }
10548a9ff04bSjmcneill mutex_exit(&sc->sc_stream_mtx);
10558a9ff04bSjmcneill
10568a9ff04bSjmcneill /* No streams of requested type available */
10578a9ff04bSjmcneill hdaudio_dma_free(sc, &dma);
10588a9ff04bSjmcneill return NULL;
10598a9ff04bSjmcneill }
10608a9ff04bSjmcneill
10618a9ff04bSjmcneill void
hdaudio_stream_disestablish(struct hdaudio_stream * st)10628a9ff04bSjmcneill hdaudio_stream_disestablish(struct hdaudio_stream *st)
10638a9ff04bSjmcneill {
10648a9ff04bSjmcneill struct hdaudio_softc *sc = st->st_host;
10658a9ff04bSjmcneill struct hdaudio_dma dma;
10668a9ff04bSjmcneill
10678a9ff04bSjmcneill KASSERT(sc->sc_stream_mask & (1 << st->st_shift));
10688a9ff04bSjmcneill
10698a9ff04bSjmcneill mutex_enter(&sc->sc_stream_mtx);
10708a9ff04bSjmcneill sc->sc_stream_mask &= ~(1 << st->st_shift);
10718a9ff04bSjmcneill st->st_intr = NULL;
10728a9ff04bSjmcneill st->st_cookie = NULL;
10738a9ff04bSjmcneill dma = st->st_bdl;
10748a9ff04bSjmcneill st->st_bdl.dma_valid = false;
10758a9ff04bSjmcneill mutex_exit(&sc->sc_stream_mtx);
10768a9ff04bSjmcneill
10778a9ff04bSjmcneill /* Can't bus_dmamem_unmap while holding a mutex. */
10788a9ff04bSjmcneill hdaudio_dma_free(sc, &dma);
10798a9ff04bSjmcneill }
10808a9ff04bSjmcneill
10818a9ff04bSjmcneill /*
1082*2e9df72eSandvar * Convert most of audio_params_t to stream fmt descriptor; noticeably missing
10838a9ff04bSjmcneill * is the # channels bits, as this is encoded differently in codec and
10848a9ff04bSjmcneill * stream descriptors.
10858a9ff04bSjmcneill *
10868a9ff04bSjmcneill * TODO: validate that the stream and selected codecs can handle the fmt
10878a9ff04bSjmcneill */
10888a9ff04bSjmcneill uint16_t
hdaudio_stream_param(struct hdaudio_stream * st,const audio_params_t * param)10898a9ff04bSjmcneill hdaudio_stream_param(struct hdaudio_stream *st, const audio_params_t *param)
10908a9ff04bSjmcneill {
10918a9ff04bSjmcneill uint16_t fmt = 0;
10928a9ff04bSjmcneill
10938a9ff04bSjmcneill switch (param->encoding) {
10948a9ff04bSjmcneill case AUDIO_ENCODING_AC3:
10958a9ff04bSjmcneill fmt |= HDAUDIO_FMT_TYPE_NONPCM;
10968a9ff04bSjmcneill break;
10978a9ff04bSjmcneill default:
10988a9ff04bSjmcneill fmt |= HDAUDIO_FMT_TYPE_PCM;
10998a9ff04bSjmcneill break;
11008a9ff04bSjmcneill }
11018a9ff04bSjmcneill
11028a9ff04bSjmcneill switch (param->sample_rate) {
11038a9ff04bSjmcneill case 8000:
11048a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
11058a9ff04bSjmcneill HDAUDIO_FMT_DIV(6);
11068a9ff04bSjmcneill break;
11078a9ff04bSjmcneill case 11025:
11088a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1) |
11098a9ff04bSjmcneill HDAUDIO_FMT_DIV(4);
11108a9ff04bSjmcneill break;
11118a9ff04bSjmcneill case 16000:
11128a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1) |
11138a9ff04bSjmcneill HDAUDIO_FMT_DIV(3);
11148a9ff04bSjmcneill break;
11158a9ff04bSjmcneill case 22050:
11168a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1) |
11178a9ff04bSjmcneill HDAUDIO_FMT_DIV(2);
11188a9ff04bSjmcneill break;
11198a9ff04bSjmcneill case 32000:
11208a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(2) |
11218a9ff04bSjmcneill HDAUDIO_FMT_DIV(3);
11228a9ff04bSjmcneill break;
11238a9ff04bSjmcneill case 44100:
11248a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(1);
11258a9ff04bSjmcneill break;
11268a9ff04bSjmcneill case 48000:
11278a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(1);
11288a9ff04bSjmcneill break;
11298a9ff04bSjmcneill case 88200:
11308a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(2);
11318a9ff04bSjmcneill break;
11328a9ff04bSjmcneill case 96000:
11338a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(2);
11348a9ff04bSjmcneill break;
11358a9ff04bSjmcneill case 176400:
11368a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_44 | HDAUDIO_FMT_MULT(4);
11378a9ff04bSjmcneill break;
11388a9ff04bSjmcneill case 192000:
11398a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BASE_48 | HDAUDIO_FMT_MULT(4);
11408a9ff04bSjmcneill break;
11418a9ff04bSjmcneill default:
11428a9ff04bSjmcneill return 0;
11438a9ff04bSjmcneill }
11448a9ff04bSjmcneill
11458a9ff04bSjmcneill if (param->precision == 16 && param->validbits == 8)
11468a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BITS_8_16;
11478a9ff04bSjmcneill else if (param->precision == 16 && param->validbits == 16)
11488a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BITS_16_16;
11498a9ff04bSjmcneill else if (param->precision == 32 && param->validbits == 20)
11508a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BITS_20_32;
11518a9ff04bSjmcneill else if (param->precision == 32 && param->validbits == 24)
11528a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BITS_24_32;
11538a9ff04bSjmcneill else if (param->precision == 32 && param->validbits == 32)
11548a9ff04bSjmcneill fmt |= HDAUDIO_FMT_BITS_32_32;
11558a9ff04bSjmcneill else
11568a9ff04bSjmcneill return 0;
11578a9ff04bSjmcneill
11588a9ff04bSjmcneill return fmt;
11598a9ff04bSjmcneill }
11608a9ff04bSjmcneill
11618a9ff04bSjmcneill void
hdaudio_stream_reset(struct hdaudio_stream * st)11628a9ff04bSjmcneill hdaudio_stream_reset(struct hdaudio_stream *st)
11638a9ff04bSjmcneill {
11648a9ff04bSjmcneill struct hdaudio_softc *sc = st->st_host;
11658a9ff04bSjmcneill int snum = st->st_shift;
11668a9ff04bSjmcneill int retry;
11678a9ff04bSjmcneill uint8_t ctl0;
11688a9ff04bSjmcneill
11698a9ff04bSjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
11708a9ff04bSjmcneill ctl0 |= HDAUDIO_CTL_SRST;
11718a9ff04bSjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
11728a9ff04bSjmcneill
11738a9ff04bSjmcneill retry = HDAUDIO_RESET_TIMEOUT;
11748a9ff04bSjmcneill do {
11758a9ff04bSjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
11768a9ff04bSjmcneill if (ctl0 & HDAUDIO_CTL_SRST)
11778a9ff04bSjmcneill break;
11788a9ff04bSjmcneill hda_delay(10);
11798a9ff04bSjmcneill } while (--retry > 0);
11808a9ff04bSjmcneill
11818a9ff04bSjmcneill ctl0 &= ~HDAUDIO_CTL_SRST;
11828a9ff04bSjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
11838a9ff04bSjmcneill
11848a9ff04bSjmcneill retry = HDAUDIO_RESET_TIMEOUT;
11858a9ff04bSjmcneill do {
11868a9ff04bSjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
11878a9ff04bSjmcneill if (!(ctl0 & HDAUDIO_CTL_SRST))
11888a9ff04bSjmcneill break;
11898a9ff04bSjmcneill hda_delay(10);
11908a9ff04bSjmcneill } while (--retry > 0);
11918a9ff04bSjmcneill if (retry == 0) {
11928a9ff04bSjmcneill hda_error(sc, "timeout leaving stream reset state\n");
11938a9ff04bSjmcneill return;
11948a9ff04bSjmcneill }
11958a9ff04bSjmcneill }
11968a9ff04bSjmcneill
11978a9ff04bSjmcneill void
hdaudio_stream_start(struct hdaudio_stream * st,int blksize,bus_size_t dmasize,const audio_params_t * params)11988a9ff04bSjmcneill hdaudio_stream_start(struct hdaudio_stream *st, int blksize,
11998a9ff04bSjmcneill bus_size_t dmasize, const audio_params_t *params)
12008a9ff04bSjmcneill {
12018a9ff04bSjmcneill struct hdaudio_softc *sc = st->st_host;
12028a9ff04bSjmcneill struct hdaudio_bdl_entry *bdl;
12038a9ff04bSjmcneill uint64_t dmaaddr;
12048a9ff04bSjmcneill uint32_t intctl;
12058a9ff04bSjmcneill uint16_t fmt;
12068a9ff04bSjmcneill uint8_t ctl0, ctl2;
12078a9ff04bSjmcneill int cnt, snum = st->st_shift;
12088a9ff04bSjmcneill
12098a9ff04bSjmcneill KASSERT(sc->sc_stream_mask & (1 << st->st_shift));
12108a9ff04bSjmcneill KASSERT(st->st_data.dma_valid == true);
12118a9ff04bSjmcneill KASSERT(st->st_bdl.dma_valid == true);
12128a9ff04bSjmcneill
12138a9ff04bSjmcneill hdaudio_stream_stop(st);
12148a9ff04bSjmcneill hdaudio_stream_reset(st);
12158a9ff04bSjmcneill
12168a9ff04bSjmcneill /*
12178a9ff04bSjmcneill * Configure buffer descriptor list
12188a9ff04bSjmcneill */
12198a9ff04bSjmcneill dmaaddr = DMA_DMAADDR(&st->st_data);
12208a9ff04bSjmcneill bdl = DMA_KERNADDR(&st->st_bdl);
12218a9ff04bSjmcneill for (cnt = 0; cnt < HDAUDIO_BDL_MAX; cnt++) {
12228a9ff04bSjmcneill bdl[cnt].address_lo = (uint32_t)dmaaddr;
12238a9ff04bSjmcneill bdl[cnt].address_hi = dmaaddr >> 32;
12248a9ff04bSjmcneill bdl[cnt].length = blksize;
12258a9ff04bSjmcneill bdl[cnt].flags = HDAUDIO_BDL_ENTRY_IOC;
12268a9ff04bSjmcneill dmaaddr += blksize;
12278a9ff04bSjmcneill if (dmaaddr >= DMA_DMAADDR(&st->st_data) + dmasize) {
12288a9ff04bSjmcneill cnt++;
12298a9ff04bSjmcneill break;
12308a9ff04bSjmcneill }
12318a9ff04bSjmcneill }
12328a9ff04bSjmcneill
12338a9ff04bSjmcneill /*
12348a9ff04bSjmcneill * Program buffer descriptor list
12358a9ff04bSjmcneill */
12368a9ff04bSjmcneill dmaaddr = DMA_DMAADDR(&st->st_bdl);
12378a9ff04bSjmcneill hda_write4(sc, HDAUDIO_SD_BDPL(snum), (uint32_t)dmaaddr);
12388a9ff04bSjmcneill hda_write4(sc, HDAUDIO_SD_BDPU(snum), (uint32_t)(dmaaddr >> 32));
12398a9ff04bSjmcneill hda_write2(sc, HDAUDIO_SD_LVI(snum), (cnt - 1) & 0xff);
12408a9ff04bSjmcneill
12418a9ff04bSjmcneill /*
12428a9ff04bSjmcneill * Program cyclic buffer length
12438a9ff04bSjmcneill */
12448a9ff04bSjmcneill hda_write4(sc, HDAUDIO_SD_CBL(snum), dmasize);
12458a9ff04bSjmcneill
12468a9ff04bSjmcneill /*
12478a9ff04bSjmcneill * Program stream number (tag). Although controller hardware is
12488a9ff04bSjmcneill * capable of transmitting any stream number (0-15), by convention
12498a9ff04bSjmcneill * stream 0 is reserved as unused by software, so that converters
12508a9ff04bSjmcneill * whose stream numbers have been reset to 0 do not unintentionally
12518a9ff04bSjmcneill * decode data not intended for them.
12528a9ff04bSjmcneill */
12538a9ff04bSjmcneill ctl2 = hda_read1(sc, HDAUDIO_SD_CTL2(snum));
12548a9ff04bSjmcneill ctl2 &= ~0xf0;
12558a9ff04bSjmcneill ctl2 |= hdaudio_stream_tag(st) << 4;
12568a9ff04bSjmcneill hda_write1(sc, HDAUDIO_SD_CTL2(snum), ctl2);
12578a9ff04bSjmcneill
12588a9ff04bSjmcneill /*
12598a9ff04bSjmcneill * Program stream format
12608a9ff04bSjmcneill */
12618a9ff04bSjmcneill fmt = hdaudio_stream_param(st, params) |
12628a9ff04bSjmcneill HDAUDIO_FMT_CHAN(params->channels);
12638a9ff04bSjmcneill hda_write2(sc, HDAUDIO_SD_FMT(snum), fmt);
12648a9ff04bSjmcneill
12658a9ff04bSjmcneill /*
12668a9ff04bSjmcneill * Switch on interrupts for this stream
12678a9ff04bSjmcneill */
12688a9ff04bSjmcneill intctl = hda_read4(sc, HDAUDIO_MMIO_INTCTL);
12698a9ff04bSjmcneill intctl |= (1 << st->st_shift);
12708a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, intctl);
12718a9ff04bSjmcneill
12728a9ff04bSjmcneill /*
12738a9ff04bSjmcneill * Start running the stream
12748a9ff04bSjmcneill */
12758a9ff04bSjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
12768a9ff04bSjmcneill ctl0 |= HDAUDIO_CTL_DEIE | HDAUDIO_CTL_FEIE | HDAUDIO_CTL_IOCE |
12778a9ff04bSjmcneill HDAUDIO_CTL_RUN;
12788a9ff04bSjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
12798a9ff04bSjmcneill }
12808a9ff04bSjmcneill
12818a9ff04bSjmcneill void
hdaudio_stream_stop(struct hdaudio_stream * st)12828a9ff04bSjmcneill hdaudio_stream_stop(struct hdaudio_stream *st)
12838a9ff04bSjmcneill {
12848a9ff04bSjmcneill struct hdaudio_softc *sc = st->st_host;
12858a9ff04bSjmcneill uint32_t intctl;
12868a9ff04bSjmcneill uint8_t ctl0;
12878a9ff04bSjmcneill int snum = st->st_shift;
12888a9ff04bSjmcneill
12898a9ff04bSjmcneill /*
12908a9ff04bSjmcneill * Stop running the stream
12918a9ff04bSjmcneill */
12928a9ff04bSjmcneill ctl0 = hda_read1(sc, HDAUDIO_SD_CTL0(snum));
12938a9ff04bSjmcneill ctl0 &= ~(HDAUDIO_CTL_DEIE | HDAUDIO_CTL_FEIE | HDAUDIO_CTL_IOCE |
12948a9ff04bSjmcneill HDAUDIO_CTL_RUN);
12958a9ff04bSjmcneill hda_write1(sc, HDAUDIO_SD_CTL0(snum), ctl0);
12968a9ff04bSjmcneill
12978a9ff04bSjmcneill /*
12988a9ff04bSjmcneill * Switch off interrupts for this stream
12998a9ff04bSjmcneill */
13008a9ff04bSjmcneill intctl = hda_read4(sc, HDAUDIO_MMIO_INTCTL);
13018a9ff04bSjmcneill intctl &= ~(1 << st->st_shift);
13028a9ff04bSjmcneill hda_write4(sc, HDAUDIO_MMIO_INTCTL, intctl);
13038a9ff04bSjmcneill }
13048a9ff04bSjmcneill
13058a9ff04bSjmcneill /*
13068a9ff04bSjmcneill * /dev/hdaudioN interface
13078a9ff04bSjmcneill */
13088a9ff04bSjmcneill
13098a9ff04bSjmcneill static const char *
hdaudioioctl_fgrp_to_cstr(enum function_group_type type)13108a9ff04bSjmcneill hdaudioioctl_fgrp_to_cstr(enum function_group_type type)
13118a9ff04bSjmcneill {
13128a9ff04bSjmcneill switch (type) {
13138a9ff04bSjmcneill case HDAUDIO_GROUP_TYPE_AFG:
13148a9ff04bSjmcneill return "afg";
13158a9ff04bSjmcneill case HDAUDIO_GROUP_TYPE_VSM_FG:
13168a9ff04bSjmcneill return "vsmfg";
13178a9ff04bSjmcneill default:
13188a9ff04bSjmcneill return "unknown";
13198a9ff04bSjmcneill }
13208a9ff04bSjmcneill }
13218a9ff04bSjmcneill
13228a9ff04bSjmcneill static struct hdaudio_function_group *
hdaudioioctl_fgrp_lookup(struct hdaudio_softc * sc,int codecid,int nid)13238a9ff04bSjmcneill hdaudioioctl_fgrp_lookup(struct hdaudio_softc *sc, int codecid, int nid)
13248a9ff04bSjmcneill {
13258a9ff04bSjmcneill struct hdaudio_codec *co;
13268a9ff04bSjmcneill struct hdaudio_function_group *fg = NULL;
13278a9ff04bSjmcneill int i;
13288a9ff04bSjmcneill
13298a9ff04bSjmcneill if (codecid < 0 || codecid >= HDAUDIO_MAX_CODECS)
13308a9ff04bSjmcneill return NULL;
13318a9ff04bSjmcneill co = &sc->sc_codec[codecid];
13328a9ff04bSjmcneill if (co->co_valid == false)
13338a9ff04bSjmcneill return NULL;
13348a9ff04bSjmcneill
13358a9ff04bSjmcneill for (i = 0; i < co->co_nfg; i++)
13368a9ff04bSjmcneill if (co->co_fg[i].fg_nid == nid) {
13378a9ff04bSjmcneill fg = &co->co_fg[i];
13388a9ff04bSjmcneill break;
13398a9ff04bSjmcneill }
13408a9ff04bSjmcneill
13418a9ff04bSjmcneill return fg;
13428a9ff04bSjmcneill }
13438a9ff04bSjmcneill
13448a9ff04bSjmcneill static int
hdaudioioctl_fgrp_info(struct hdaudio_softc * sc,prop_dictionary_t request,prop_dictionary_t response)13458a9ff04bSjmcneill hdaudioioctl_fgrp_info(struct hdaudio_softc *sc, prop_dictionary_t request,
13468a9ff04bSjmcneill prop_dictionary_t response)
13478a9ff04bSjmcneill {
13488a9ff04bSjmcneill struct hdaudio_codec *co;
13498a9ff04bSjmcneill struct hdaudio_function_group *fg;
13508a9ff04bSjmcneill prop_array_t array;
13518a9ff04bSjmcneill prop_dictionary_t dict;
13528a9ff04bSjmcneill int codecid, fgid;
13538a9ff04bSjmcneill
13548a9ff04bSjmcneill array = prop_array_create();
13558a9ff04bSjmcneill if (array == NULL)
13568a9ff04bSjmcneill return ENOMEM;
13578a9ff04bSjmcneill
13588a9ff04bSjmcneill for (codecid = 0; codecid < HDAUDIO_MAX_CODECS; codecid++) {
13598a9ff04bSjmcneill co = &sc->sc_codec[codecid];
13608a9ff04bSjmcneill if (co->co_valid == false)
13618a9ff04bSjmcneill continue;
13628a9ff04bSjmcneill for (fgid = 0; fgid < co->co_nfg; fgid++) {
13638a9ff04bSjmcneill fg = &co->co_fg[fgid];
13648a9ff04bSjmcneill dict = prop_dictionary_create();
13658a9ff04bSjmcneill if (dict == NULL)
13668a9ff04bSjmcneill return ENOMEM;
1367814a7798Sthorpej prop_dictionary_set_string_nocopy(dict,
13688a9ff04bSjmcneill "type", hdaudioioctl_fgrp_to_cstr(fg->fg_type));
13698a9ff04bSjmcneill prop_dictionary_set_int16(dict, "nid", fg->fg_nid);
13708a9ff04bSjmcneill prop_dictionary_set_int16(dict, "codecid", codecid);
13718a9ff04bSjmcneill prop_dictionary_set_uint16(dict, "vendor-id",
13728a9ff04bSjmcneill fg->fg_vendor);
13738a9ff04bSjmcneill prop_dictionary_set_uint16(dict, "product-id",
13748a9ff04bSjmcneill fg->fg_product);
13758a9ff04bSjmcneill prop_dictionary_set_uint32(dict, "subsystem-id",
13768a9ff04bSjmcneill sc->sc_subsystem);
13778a9ff04bSjmcneill if (fg->fg_device)
1378814a7798Sthorpej prop_dictionary_set_string(dict, "device",
13798a9ff04bSjmcneill device_xname(fg->fg_device));
13808a9ff04bSjmcneill else
1381814a7798Sthorpej prop_dictionary_set_string_nocopy(dict,
13828a9ff04bSjmcneill "device", "<none>");
13838a9ff04bSjmcneill prop_array_add(array, dict);
13848a9ff04bSjmcneill }
13858a9ff04bSjmcneill }
13868a9ff04bSjmcneill
13878a9ff04bSjmcneill prop_dictionary_set(response, "function-group-info", array);
13888a9ff04bSjmcneill return 0;
13898a9ff04bSjmcneill }
13908a9ff04bSjmcneill
13918a9ff04bSjmcneill static int
hdaudioioctl_fgrp_getconfig(struct hdaudio_softc * sc,prop_dictionary_t request,prop_dictionary_t response)13928a9ff04bSjmcneill hdaudioioctl_fgrp_getconfig(struct hdaudio_softc *sc,
13938a9ff04bSjmcneill prop_dictionary_t request, prop_dictionary_t response)
13948a9ff04bSjmcneill {
13958a9ff04bSjmcneill struct hdaudio_function_group *fg;
13968a9ff04bSjmcneill prop_dictionary_t dict;
13978a9ff04bSjmcneill prop_array_t array;
13988a9ff04bSjmcneill uint32_t nodecnt, wcap, config;
13998a9ff04bSjmcneill int16_t codecid, nid, i;
14008a9ff04bSjmcneill int startnode, endnode;
14018a9ff04bSjmcneill
14028a9ff04bSjmcneill if (!prop_dictionary_get_int16(request, "codecid", &codecid) ||
14038a9ff04bSjmcneill !prop_dictionary_get_int16(request, "nid", &nid))
14048a9ff04bSjmcneill return EINVAL;
14058a9ff04bSjmcneill
14068a9ff04bSjmcneill fg = hdaudioioctl_fgrp_lookup(sc, codecid, nid);
14078a9ff04bSjmcneill if (fg == NULL)
14088a9ff04bSjmcneill return ENODEV;
14098a9ff04bSjmcneill
14108a9ff04bSjmcneill array = prop_array_create();
14118a9ff04bSjmcneill if (array == NULL)
14128a9ff04bSjmcneill return ENOMEM;
14138a9ff04bSjmcneill
14148a9ff04bSjmcneill nodecnt = hdaudio_command(fg->fg_codec, fg->fg_nid,
14158a9ff04bSjmcneill CORB_GET_PARAMETER, COP_SUBORDINATE_NODE_COUNT);
14168a9ff04bSjmcneill startnode = COP_NODECNT_STARTNODE(nodecnt);
14178a9ff04bSjmcneill endnode = startnode + COP_NODECNT_NUMNODES(nodecnt);
14188a9ff04bSjmcneill
14198a9ff04bSjmcneill for (i = startnode; i < endnode; i++) {
14208a9ff04bSjmcneill wcap = hdaudio_command(fg->fg_codec, i,
14218a9ff04bSjmcneill CORB_GET_PARAMETER, COP_AUDIO_WIDGET_CAPABILITIES);
14228a9ff04bSjmcneill if (COP_AWCAP_TYPE(wcap) != COP_AWCAP_TYPE_PIN_COMPLEX)
14238a9ff04bSjmcneill continue;
14248a9ff04bSjmcneill config = hdaudio_command(fg->fg_codec, i,
14258a9ff04bSjmcneill CORB_GET_CONFIGURATION_DEFAULT, 0);
14268a9ff04bSjmcneill dict = prop_dictionary_create();
14278a9ff04bSjmcneill if (dict == NULL)
14288a9ff04bSjmcneill return ENOMEM;
14298a9ff04bSjmcneill prop_dictionary_set_int16(dict, "nid", i);
14308a9ff04bSjmcneill prop_dictionary_set_uint32(dict, "config", config);
14318a9ff04bSjmcneill prop_array_add(array, dict);
14328a9ff04bSjmcneill }
14338a9ff04bSjmcneill
14348a9ff04bSjmcneill prop_dictionary_set(response, "pin-config", array);
14358a9ff04bSjmcneill
14368a9ff04bSjmcneill return 0;
14378a9ff04bSjmcneill }
14388a9ff04bSjmcneill
14398a9ff04bSjmcneill static int
hdaudioioctl_fgrp_setconfig(struct hdaudio_softc * sc,prop_dictionary_t request,prop_dictionary_t response)14408a9ff04bSjmcneill hdaudioioctl_fgrp_setconfig(struct hdaudio_softc *sc,
14418a9ff04bSjmcneill prop_dictionary_t request, prop_dictionary_t response)
14428a9ff04bSjmcneill {
14438a9ff04bSjmcneill struct hdaudio_function_group *fg;
14448a9ff04bSjmcneill prop_array_t config;
14458a9ff04bSjmcneill int16_t codecid, nid;
14468a9ff04bSjmcneill int err;
14478a9ff04bSjmcneill
14488a9ff04bSjmcneill if (!prop_dictionary_get_int16(request, "codecid", &codecid) ||
14498a9ff04bSjmcneill !prop_dictionary_get_int16(request, "nid", &nid))
14508a9ff04bSjmcneill return EINVAL;
14518a9ff04bSjmcneill
14528a9ff04bSjmcneill fg = hdaudioioctl_fgrp_lookup(sc, codecid, nid);
14538a9ff04bSjmcneill if (fg == NULL)
14548a9ff04bSjmcneill return ENODEV;
14558a9ff04bSjmcneill
14568a9ff04bSjmcneill if (fg->fg_device) {
14578a9ff04bSjmcneill err = config_detach(fg->fg_device, 0);
14588a9ff04bSjmcneill if (err)
14598a9ff04bSjmcneill return err;
14608a9ff04bSjmcneill fg->fg_device = NULL;
14618a9ff04bSjmcneill }
14628a9ff04bSjmcneill
14638a9ff04bSjmcneill /* "pin-config" may be NULL, this means "use BIOS configuration" */
14648a9ff04bSjmcneill config = prop_dictionary_get(request, "pin-config");
14658a9ff04bSjmcneill if (config && prop_object_type(config) != PROP_TYPE_ARRAY) {
14668a9ff04bSjmcneill prop_object_release(config);
14678a9ff04bSjmcneill return EINVAL;
14688a9ff04bSjmcneill }
14698a9ff04bSjmcneill hdaudio_attach_fg(fg, config);
14708a9ff04bSjmcneill if (config)
14718a9ff04bSjmcneill prop_object_release(config);
14728a9ff04bSjmcneill
14738a9ff04bSjmcneill return 0;
14748a9ff04bSjmcneill }
14758a9ff04bSjmcneill
14768a9ff04bSjmcneill static int
hdaudio_dispatch_fgrp_ioctl(struct hdaudio_softc * sc,u_long cmd,prop_dictionary_t request,prop_dictionary_t response)14778a9ff04bSjmcneill hdaudio_dispatch_fgrp_ioctl(struct hdaudio_softc *sc, u_long cmd,
14788a9ff04bSjmcneill prop_dictionary_t request, prop_dictionary_t response)
14798a9ff04bSjmcneill {
14808a9ff04bSjmcneill struct hdaudio_function_group *fg;
14818a9ff04bSjmcneill int (*infocb)(void *, prop_dictionary_t, prop_dictionary_t);
14828a9ff04bSjmcneill prop_dictionary_t fgrp_dict;
14838a9ff04bSjmcneill uint64_t info_fn;
14848a9ff04bSjmcneill int16_t codecid, nid;
14858a9ff04bSjmcneill void *fgrp_sc;
14868a9ff04bSjmcneill bool rv;
14878a9ff04bSjmcneill int err;
14888a9ff04bSjmcneill
14898a9ff04bSjmcneill if (!prop_dictionary_get_int16(request, "codecid", &codecid) ||
14908a9ff04bSjmcneill !prop_dictionary_get_int16(request, "nid", &nid))
14918a9ff04bSjmcneill return EINVAL;
14928a9ff04bSjmcneill
14938a9ff04bSjmcneill fg = hdaudioioctl_fgrp_lookup(sc, codecid, nid);
14948a9ff04bSjmcneill if (fg == NULL)
14958a9ff04bSjmcneill return ENODEV;
14968a9ff04bSjmcneill if (fg->fg_device == NULL)
14978a9ff04bSjmcneill return ENXIO;
14988a9ff04bSjmcneill fgrp_sc = device_private(fg->fg_device);
14998a9ff04bSjmcneill fgrp_dict = device_properties(fg->fg_device);
15008a9ff04bSjmcneill
15018a9ff04bSjmcneill switch (fg->fg_type) {
15028a9ff04bSjmcneill case HDAUDIO_GROUP_TYPE_AFG:
15038a9ff04bSjmcneill switch (cmd) {
15048a9ff04bSjmcneill case HDAUDIO_FGRP_CODEC_INFO:
15058a9ff04bSjmcneill rv = prop_dictionary_get_uint64(fgrp_dict,
15068a9ff04bSjmcneill "codecinfo-callback", &info_fn);
15078a9ff04bSjmcneill if (!rv)
15088a9ff04bSjmcneill return ENXIO;
15098a9ff04bSjmcneill infocb = (void *)(uintptr_t)info_fn;
15108a9ff04bSjmcneill err = infocb(fgrp_sc, request, response);
15118a9ff04bSjmcneill break;
15128a9ff04bSjmcneill case HDAUDIO_FGRP_WIDGET_INFO:
15138a9ff04bSjmcneill rv = prop_dictionary_get_uint64(fgrp_dict,
15148a9ff04bSjmcneill "widgetinfo-callback", &info_fn);
15158a9ff04bSjmcneill if (!rv)
15168a9ff04bSjmcneill return ENXIO;
15178a9ff04bSjmcneill infocb = (void *)(uintptr_t)info_fn;
15188a9ff04bSjmcneill err = infocb(fgrp_sc, request, response);
15198a9ff04bSjmcneill break;
15208a9ff04bSjmcneill default:
15218a9ff04bSjmcneill err = EINVAL;
15228a9ff04bSjmcneill break;
15238a9ff04bSjmcneill }
15248a9ff04bSjmcneill break;
15258a9ff04bSjmcneill
15268a9ff04bSjmcneill default:
15278a9ff04bSjmcneill err = EINVAL;
15288a9ff04bSjmcneill break;
15298a9ff04bSjmcneill }
15308a9ff04bSjmcneill return err;
15318a9ff04bSjmcneill }
15328a9ff04bSjmcneill
15338a9ff04bSjmcneill int
hdaudioopen(dev_t dev,int flag,int mode,struct lwp * l)15348a9ff04bSjmcneill hdaudioopen(dev_t dev, int flag, int mode, struct lwp *l)
15358a9ff04bSjmcneill {
15368a9ff04bSjmcneill device_t self;
15378a9ff04bSjmcneill
15388a9ff04bSjmcneill self = device_lookup(&hdaudio_cd, HDAUDIOUNIT(dev));
15398a9ff04bSjmcneill if (self == NULL)
15408a9ff04bSjmcneill return ENXIO;
15418a9ff04bSjmcneill
15428a9ff04bSjmcneill return 0;
15438a9ff04bSjmcneill }
15448a9ff04bSjmcneill
15458a9ff04bSjmcneill int
hdaudioclose(dev_t dev,int flag,int mode,struct lwp * l)15468a9ff04bSjmcneill hdaudioclose(dev_t dev, int flag, int mode, struct lwp *l)
15478a9ff04bSjmcneill {
15488a9ff04bSjmcneill return 0;
15498a9ff04bSjmcneill }
15508a9ff04bSjmcneill
15518a9ff04bSjmcneill int
hdaudioioctl(dev_t dev,u_long cmd,void * addr,int flag,struct lwp * l)15528a9ff04bSjmcneill hdaudioioctl(dev_t dev, u_long cmd, void *addr, int flag, struct lwp *l)
15538a9ff04bSjmcneill {
15548a9ff04bSjmcneill struct hdaudio_softc *sc;
15558a9ff04bSjmcneill struct plistref *pref = addr;
15568a9ff04bSjmcneill prop_dictionary_t request, response;
15578a9ff04bSjmcneill int err;
15588a9ff04bSjmcneill
15598a9ff04bSjmcneill sc = device_lookup_private(&hdaudio_cd, HDAUDIOUNIT(dev));
15608a9ff04bSjmcneill if (sc == NULL)
15618a9ff04bSjmcneill return ENXIO;
15628a9ff04bSjmcneill
15638a9ff04bSjmcneill response = prop_dictionary_create();
15648a9ff04bSjmcneill if (response == NULL)
15658a9ff04bSjmcneill return ENOMEM;
15668a9ff04bSjmcneill
15678a9ff04bSjmcneill err = prop_dictionary_copyin_ioctl(pref, cmd, &request);
15688a9ff04bSjmcneill if (err) {
15698a9ff04bSjmcneill prop_object_release(response);
15708a9ff04bSjmcneill return err;
15718a9ff04bSjmcneill }
15728a9ff04bSjmcneill
15738a9ff04bSjmcneill switch (cmd) {
15748a9ff04bSjmcneill case HDAUDIO_FGRP_INFO:
15758a9ff04bSjmcneill err = hdaudioioctl_fgrp_info(sc, request, response);
15768a9ff04bSjmcneill break;
15778a9ff04bSjmcneill case HDAUDIO_FGRP_GETCONFIG:
15788a9ff04bSjmcneill err = hdaudioioctl_fgrp_getconfig(sc, request, response);
15798a9ff04bSjmcneill break;
15808a9ff04bSjmcneill case HDAUDIO_FGRP_SETCONFIG:
15818a9ff04bSjmcneill err = hdaudioioctl_fgrp_setconfig(sc, request, response);
15828a9ff04bSjmcneill break;
15838a9ff04bSjmcneill case HDAUDIO_FGRP_CODEC_INFO:
15848a9ff04bSjmcneill case HDAUDIO_FGRP_WIDGET_INFO:
15858a9ff04bSjmcneill err = hdaudio_dispatch_fgrp_ioctl(sc, cmd, request, response);
15868a9ff04bSjmcneill break;
15878a9ff04bSjmcneill default:
15888a9ff04bSjmcneill err = EINVAL;
15898a9ff04bSjmcneill break;
15908a9ff04bSjmcneill }
15918a9ff04bSjmcneill
15928a9ff04bSjmcneill if (!err)
15938a9ff04bSjmcneill err = prop_dictionary_copyout_ioctl(pref, cmd, response);
15948a9ff04bSjmcneill
15958a9ff04bSjmcneill if (response)
15968a9ff04bSjmcneill prop_object_release(response);
15978a9ff04bSjmcneill prop_object_release(request);
15988a9ff04bSjmcneill return err;
15998a9ff04bSjmcneill }
16008a9ff04bSjmcneill
160152b02286Spgoyette MODULE(MODULE_CLASS_DRIVER, hdaudio, "audio");
160252b02286Spgoyette #ifdef _MODULE
160352b02286Spgoyette static const struct cfiattrdata hdaudiobuscf_iattrdata = {
160452b02286Spgoyette "hdaudiobus", 1, {
160552b02286Spgoyette { "nid", "-1", -1 },
160652b02286Spgoyette }
160752b02286Spgoyette };
160852b02286Spgoyette static const struct cfiattrdata * const hdaudio_attrs[] = {
160952b02286Spgoyette &hdaudiobuscf_iattrdata, NULL
161052b02286Spgoyette };
161152b02286Spgoyette CFDRIVER_DECL(hdaudio, DV_AUDIODEV, hdaudio_attrs);
161252b02286Spgoyette #endif
16138a9ff04bSjmcneill
16148a9ff04bSjmcneill static int
hdaudio_modcmd(modcmd_t cmd,void * opaque)16158a9ff04bSjmcneill hdaudio_modcmd(modcmd_t cmd, void *opaque)
16168a9ff04bSjmcneill {
16178a9ff04bSjmcneill int error = 0;
16188a9ff04bSjmcneill #ifdef _MODULE
16198a9ff04bSjmcneill int bmaj = -1, cmaj = -1;
16208a9ff04bSjmcneill #endif
16218a9ff04bSjmcneill
16228a9ff04bSjmcneill switch (cmd) {
16238a9ff04bSjmcneill case MODULE_CMD_INIT:
16248a9ff04bSjmcneill #ifdef _MODULE
16258a9ff04bSjmcneill error = devsw_attach("hdaudio", NULL, &bmaj,
16268a9ff04bSjmcneill &hdaudio_cdevsw, &cmaj);
162752b02286Spgoyette if (error)
162852b02286Spgoyette break;
162952b02286Spgoyette error = config_cfdriver_attach(&hdaudio_cd);
163052b02286Spgoyette if (error)
16318a9ff04bSjmcneill devsw_detach(NULL, &hdaudio_cdevsw);
16328a9ff04bSjmcneill #endif
163352b02286Spgoyette break;
163452b02286Spgoyette case MODULE_CMD_FINI:
163552b02286Spgoyette #ifdef _MODULE
163652b02286Spgoyette error = config_cfdriver_detach(&hdaudio_cd);
163752b02286Spgoyette if (error)
163852b02286Spgoyette break;
1639e7bed289Sriastradh devsw_detach(NULL, &hdaudio_cdevsw);
164052b02286Spgoyette #endif
164152b02286Spgoyette break;
164252b02286Spgoyette default:
164352b02286Spgoyette error = ENOTTY;
164452b02286Spgoyette break;
164552b02286Spgoyette }
164652b02286Spgoyette return error;
16478a9ff04bSjmcneill }
16488a9ff04bSjmcneill
16498a9ff04bSjmcneill DEV_VERBOSE_DEFINE(hdaudio);
1650