xref: /freebsd/usr.sbin/bhyve/pci_hda.c (revision 4d65a7c6)
16b021cc2SWarner Losh /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
33e21da8aSMarcelo Araujo  *
46b021cc2SWarner Losh  * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
56b021cc2SWarner Losh  * All rights reserved.
66b021cc2SWarner Losh  *
76b021cc2SWarner Losh  * Redistribution and use in source and binary forms, with or without
86b021cc2SWarner Losh  * modification, are permitted provided that the following conditions
96b021cc2SWarner Losh  * are met:
106b021cc2SWarner Losh  * 1. Redistributions of source code must retain the above copyright
116b021cc2SWarner Losh  *    notice, this list of conditions and the following disclaimer.
126b021cc2SWarner Losh  * 2. Redistributions in binary form must reproduce the above copyright
136b021cc2SWarner Losh  *    notice, this list of conditions and the following disclaimer in the
146b021cc2SWarner Losh  *    documentation and/or other materials provided with the distribution.
156b021cc2SWarner Losh  *
166b021cc2SWarner Losh  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
176b021cc2SWarner Losh  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
186b021cc2SWarner Losh  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
196b021cc2SWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
206b021cc2SWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
216b021cc2SWarner Losh  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
226b021cc2SWarner Losh  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
236b021cc2SWarner Losh  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
246b021cc2SWarner Losh  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
256b021cc2SWarner Losh  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
266b021cc2SWarner Losh  * SUCH DAMAGE.
276b021cc2SWarner Losh  *
286b021cc2SWarner Losh  */
296b021cc2SWarner Losh 
30bfe8e339SJohn Baldwin #include <sys/param.h>
316b021cc2SWarner Losh #include <time.h>
326b021cc2SWarner Losh 
336b021cc2SWarner Losh #include "pci_hda.h"
346b021cc2SWarner Losh #include "bhyverun.h"
35621b5090SJohn Baldwin #include "config.h"
366b021cc2SWarner Losh #include "pci_emul.h"
376b021cc2SWarner Losh #include "hdac_reg.h"
386b021cc2SWarner Losh 
396b021cc2SWarner Losh /*
406b021cc2SWarner Losh  * HDA defines
416b021cc2SWarner Losh  */
426b021cc2SWarner Losh #define PCIR_HDCTL		0x40
436b021cc2SWarner Losh #define INTEL_VENDORID		0x8086
446b021cc2SWarner Losh #define HDA_INTEL_82801G	0x27d8
456b021cc2SWarner Losh 
466b021cc2SWarner Losh #define HDA_IOSS_NO		0x08
476b021cc2SWarner Losh #define HDA_OSS_NO		0x04
486b021cc2SWarner Losh #define HDA_ISS_NO		0x04
496b021cc2SWarner Losh #define HDA_CODEC_MAX		0x0f
506b021cc2SWarner Losh #define HDA_LAST_OFFSET						\
516b021cc2SWarner Losh 	(0x2084 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20))
526b021cc2SWarner Losh #define HDA_CORB_ENTRY_LEN	0x04
536b021cc2SWarner Losh #define HDA_RIRB_ENTRY_LEN	0x08
546b021cc2SWarner Losh #define HDA_BDL_ENTRY_LEN	0x10
556b021cc2SWarner Losh #define HDA_DMA_PIB_ENTRY_LEN	0x08
566b021cc2SWarner Losh #define HDA_STREAM_TAGS_CNT	0x10
576b021cc2SWarner Losh #define HDA_STREAM_REGS_BASE	0x80
586b021cc2SWarner Losh #define HDA_STREAM_REGS_LEN	0x20
596b021cc2SWarner Losh 
606b021cc2SWarner Losh #define HDA_DMA_ACCESS_LEN	(sizeof(uint32_t))
616b021cc2SWarner Losh #define HDA_BDL_MAX_LEN		0x0100
626b021cc2SWarner Losh 
636b021cc2SWarner Losh #define HDAC_SDSTS_FIFORDY	(1 << 5)
646b021cc2SWarner Losh 
656b021cc2SWarner Losh #define HDA_RIRBSTS_IRQ_MASK	(HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS)
666b021cc2SWarner Losh #define HDA_STATESTS_IRQ_MASK	((1 << HDA_CODEC_MAX) - 1)
676b021cc2SWarner Losh #define HDA_SDSTS_IRQ_MASK					\
686b021cc2SWarner Losh 	(HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS)
696b021cc2SWarner Losh 
706b021cc2SWarner Losh /*
716b021cc2SWarner Losh  * HDA data structures
726b021cc2SWarner Losh  */
736b021cc2SWarner Losh 
746b021cc2SWarner Losh struct hda_softc;
756b021cc2SWarner Losh 
766b021cc2SWarner Losh typedef void (*hda_set_reg_handler)(struct hda_softc *sc, uint32_t offset,
776b021cc2SWarner Losh 		uint32_t old);
786b021cc2SWarner Losh 
796b021cc2SWarner Losh struct hda_bdle {
806b021cc2SWarner Losh 	uint32_t addrl;
8188880fd4SScott Long 	uint32_t addrh;
826b021cc2SWarner Losh 	uint32_t len;
8388880fd4SScott Long 	uint32_t ioc;
846b021cc2SWarner Losh } __packed;
856b021cc2SWarner Losh 
866b021cc2SWarner Losh struct hda_bdle_desc {
876b021cc2SWarner Losh 	void *addr;
886b021cc2SWarner Losh 	uint8_t ioc;
896b021cc2SWarner Losh 	uint32_t len;
906b021cc2SWarner Losh };
916b021cc2SWarner Losh 
926b021cc2SWarner Losh struct hda_codec_cmd_ctl {
93d06bf11cSMark Johnston 	const char *name;
946b021cc2SWarner Losh 	void *dma_vaddr;
956b021cc2SWarner Losh 	uint8_t run;
966b021cc2SWarner Losh 	uint16_t rp;
976b021cc2SWarner Losh 	uint16_t size;
986b021cc2SWarner Losh 	uint16_t wp;
996b021cc2SWarner Losh };
1006b021cc2SWarner Losh 
1016b021cc2SWarner Losh struct hda_stream_desc {
1026b021cc2SWarner Losh 	uint8_t dir;
1036b021cc2SWarner Losh 	uint8_t run;
1046b021cc2SWarner Losh 	uint8_t stream;
1056b021cc2SWarner Losh 
1066b021cc2SWarner Losh 	/* bp is the no. of bytes transferred in the current bdle */
1076b021cc2SWarner Losh 	uint32_t bp;
1086b021cc2SWarner Losh 	/* be is the no. of bdles transferred in the bdl */
1096b021cc2SWarner Losh 	uint32_t be;
1106b021cc2SWarner Losh 
1116b021cc2SWarner Losh 	uint32_t bdl_cnt;
1126b021cc2SWarner Losh 	struct hda_bdle_desc bdl[HDA_BDL_MAX_LEN];
1136b021cc2SWarner Losh };
1146b021cc2SWarner Losh 
1156b021cc2SWarner Losh struct hda_softc {
1166b021cc2SWarner Losh 	struct pci_devinst *pci_dev;
1176b021cc2SWarner Losh 	uint32_t regs[HDA_LAST_OFFSET];
1186b021cc2SWarner Losh 
1196b021cc2SWarner Losh 	uint8_t lintr;
1206b021cc2SWarner Losh 	uint8_t rirb_cnt;
1216b021cc2SWarner Losh 	uint64_t wall_clock_start;
1226b021cc2SWarner Losh 
1236b021cc2SWarner Losh 	struct hda_codec_cmd_ctl corb;
1246b021cc2SWarner Losh 	struct hda_codec_cmd_ctl rirb;
1256b021cc2SWarner Losh 
1266b021cc2SWarner Losh 	uint8_t codecs_no;
1276b021cc2SWarner Losh 	struct hda_codec_inst *codecs[HDA_CODEC_MAX];
1286b021cc2SWarner Losh 
1296b021cc2SWarner Losh 	/* Base Address of the DMA Position Buffer */
1306b021cc2SWarner Losh 	void *dma_pib_vaddr;
1316b021cc2SWarner Losh 
1326b021cc2SWarner Losh 	struct hda_stream_desc streams[HDA_IOSS_NO];
1336b021cc2SWarner Losh 	/* 2 tables for output and input */
1346b021cc2SWarner Losh 	uint8_t stream_map[2][HDA_STREAM_TAGS_CNT];
1356b021cc2SWarner Losh };
1366b021cc2SWarner Losh 
1376b021cc2SWarner Losh /*
1386b021cc2SWarner Losh  * HDA module function declarations
1396b021cc2SWarner Losh  */
1406b021cc2SWarner Losh static inline void hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset,
1416b021cc2SWarner Losh     uint32_t value);
1426b021cc2SWarner Losh static inline uint32_t hda_get_reg_by_offset(struct hda_softc *sc,
1436b021cc2SWarner Losh     uint32_t offset);
1446b021cc2SWarner Losh static inline void hda_set_field_by_offset(struct hda_softc *sc,
1456b021cc2SWarner Losh     uint32_t offset, uint32_t mask, uint32_t value);
1466b021cc2SWarner Losh 
147621b5090SJohn Baldwin static struct hda_softc *hda_init(nvlist_t *nvl);
1486b021cc2SWarner Losh static void hda_update_intr(struct hda_softc *sc);
1496b021cc2SWarner Losh static void hda_response_interrupt(struct hda_softc *sc);
1506b021cc2SWarner Losh static int hda_codec_constructor(struct hda_softc *sc,
151621b5090SJohn Baldwin     struct hda_codec_class *codec, const char *play, const char *rec);
1526b021cc2SWarner Losh static struct hda_codec_class *hda_find_codec_class(const char *name);
1536b021cc2SWarner Losh 
1546b021cc2SWarner Losh static int hda_send_command(struct hda_softc *sc, uint32_t verb);
1556b021cc2SWarner Losh static int hda_notify_codecs(struct hda_softc *sc, uint8_t run,
1566b021cc2SWarner Losh     uint8_t stream, uint8_t dir);
1576b021cc2SWarner Losh static void hda_reset(struct hda_softc *sc);
1586b021cc2SWarner Losh static void hda_reset_regs(struct hda_softc *sc);
1596b021cc2SWarner Losh static void hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind);
1606b021cc2SWarner Losh static int hda_stream_start(struct hda_softc *sc, uint8_t stream_ind);
1616b021cc2SWarner Losh static int hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind);
1626b021cc2SWarner Losh static uint32_t hda_read(struct hda_softc *sc, uint32_t offset);
1636b021cc2SWarner Losh static int hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size,
1646b021cc2SWarner Losh     uint32_t value);
1656b021cc2SWarner Losh 
1666b021cc2SWarner Losh static inline void hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p);
1676b021cc2SWarner Losh static int hda_corb_start(struct hda_softc *sc);
1686b021cc2SWarner Losh static int hda_corb_run(struct hda_softc *sc);
1696b021cc2SWarner Losh static int hda_rirb_start(struct hda_softc *sc);
1706b021cc2SWarner Losh 
1716b021cc2SWarner Losh static void *hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr,
1726b021cc2SWarner Losh     size_t len);
1736b021cc2SWarner Losh static void hda_dma_st_dword(void *dma_vaddr, uint32_t data);
1746b021cc2SWarner Losh static uint32_t hda_dma_ld_dword(void *dma_vaddr);
1756b021cc2SWarner Losh 
1766b021cc2SWarner Losh static inline uint8_t hda_get_stream_by_offsets(uint32_t offset,
1776b021cc2SWarner Losh     uint8_t reg_offset);
1786b021cc2SWarner Losh static inline uint32_t hda_get_offset_stream(uint8_t stream_ind);
1796b021cc2SWarner Losh 
1806b021cc2SWarner Losh static void hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
1816b021cc2SWarner Losh static void hda_set_statests(struct hda_softc *sc, uint32_t offset,
1826b021cc2SWarner Losh     uint32_t old);
1836b021cc2SWarner Losh static void hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old);
1846b021cc2SWarner Losh static void hda_set_corbctl(struct hda_softc *sc, uint32_t offset,
1856b021cc2SWarner Losh     uint32_t old);
1866b021cc2SWarner Losh static void hda_set_rirbctl(struct hda_softc *sc, uint32_t offset,
1876b021cc2SWarner Losh     uint32_t old);
1886b021cc2SWarner Losh static void hda_set_rirbsts(struct hda_softc *sc, uint32_t offset,
1896b021cc2SWarner Losh     uint32_t old);
1906b021cc2SWarner Losh static void hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset,
1916b021cc2SWarner Losh     uint32_t old);
1926b021cc2SWarner Losh static void hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
1936b021cc2SWarner Losh static void hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old);
1946b021cc2SWarner Losh static void hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old);
1956b021cc2SWarner Losh 
1966b021cc2SWarner Losh static int hda_signal_state_change(struct hda_codec_inst *hci);
1976b021cc2SWarner Losh static int hda_response(struct hda_codec_inst *hci, uint32_t response,
1986b021cc2SWarner Losh     uint8_t unsol);
1996b021cc2SWarner Losh static int hda_transfer(struct hda_codec_inst *hci, uint8_t stream,
20063898728SMark Johnston     uint8_t dir, uint8_t *buf, size_t count);
2016b021cc2SWarner Losh 
2026b021cc2SWarner Losh static void hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib);
2036b021cc2SWarner Losh static uint64_t hda_get_clock_ns(void);
2046b021cc2SWarner Losh 
2056b021cc2SWarner Losh /*
2066b021cc2SWarner Losh  * PCI HDA function declarations
2076b021cc2SWarner Losh  */
2086a284cacSJohn Baldwin static int pci_hda_init(struct pci_devinst *pi, nvlist_t *nvl);
2096a284cacSJohn Baldwin static void pci_hda_write(struct pci_devinst *pi, int baridx, uint64_t offset,
2106a284cacSJohn Baldwin     int size, uint64_t value);
2116a284cacSJohn Baldwin static uint64_t pci_hda_read(struct pci_devinst *pi, int baridx,
2126a284cacSJohn Baldwin     uint64_t offset, int size);
2136b021cc2SWarner Losh /*
2146b021cc2SWarner Losh  * HDA global data
2156b021cc2SWarner Losh  */
2166b021cc2SWarner Losh 
2176b021cc2SWarner Losh static const hda_set_reg_handler hda_set_reg_table[] = {
2186b021cc2SWarner Losh 	[HDAC_GCTL] = hda_set_gctl,
2196b021cc2SWarner Losh 	[HDAC_STATESTS] = hda_set_statests,
2206b021cc2SWarner Losh 	[HDAC_CORBWP] = hda_set_corbwp,
2216b021cc2SWarner Losh 	[HDAC_CORBCTL] = hda_set_corbctl,
2226b021cc2SWarner Losh 	[HDAC_RIRBCTL] = hda_set_rirbctl,
2236b021cc2SWarner Losh 	[HDAC_RIRBSTS] = hda_set_rirbsts,
2246b021cc2SWarner Losh 	[HDAC_DPIBLBASE] = hda_set_dpiblbase,
2256b021cc2SWarner Losh 
2266b021cc2SWarner Losh #define HDAC_ISTREAM(n, iss, oss)				\
2276b021cc2SWarner Losh 	[_HDAC_ISDCTL(n, iss, oss)] = hda_set_sdctl,		\
2286b021cc2SWarner Losh 	[_HDAC_ISDCTL(n, iss, oss) + 2] = hda_set_sdctl2,	\
2296b021cc2SWarner Losh 	[_HDAC_ISDSTS(n, iss, oss)] = hda_set_sdsts,		\
2306b021cc2SWarner Losh 
2316b021cc2SWarner Losh #define HDAC_OSTREAM(n, iss, oss)				\
2326b021cc2SWarner Losh 	[_HDAC_OSDCTL(n, iss, oss)] = hda_set_sdctl,		\
2336b021cc2SWarner Losh 	[_HDAC_OSDCTL(n, iss, oss) + 2] = hda_set_sdctl2,	\
2346b021cc2SWarner Losh 	[_HDAC_OSDSTS(n, iss, oss)] = hda_set_sdsts,		\
2356b021cc2SWarner Losh 
2366b021cc2SWarner Losh 	HDAC_ISTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
2376b021cc2SWarner Losh 	HDAC_ISTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
2386b021cc2SWarner Losh 	HDAC_ISTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
2396b021cc2SWarner Losh 	HDAC_ISTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
2406b021cc2SWarner Losh 
2416b021cc2SWarner Losh 	HDAC_OSTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
2426b021cc2SWarner Losh 	HDAC_OSTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
2436b021cc2SWarner Losh 	HDAC_OSTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
2446b021cc2SWarner Losh 	HDAC_OSTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
2456b021cc2SWarner Losh };
2466b021cc2SWarner Losh 
2476b021cc2SWarner Losh static const uint16_t hda_corb_sizes[] = {
2486b021cc2SWarner Losh 	[HDAC_CORBSIZE_CORBSIZE_2]	= 2,
2496b021cc2SWarner Losh 	[HDAC_CORBSIZE_CORBSIZE_16]	= 16,
2506b021cc2SWarner Losh 	[HDAC_CORBSIZE_CORBSIZE_256]	= 256,
2516b021cc2SWarner Losh 	[HDAC_CORBSIZE_CORBSIZE_MASK]	= 0,
2526b021cc2SWarner Losh };
2536b021cc2SWarner Losh 
2546b021cc2SWarner Losh static const uint16_t hda_rirb_sizes[] = {
2556b021cc2SWarner Losh 	[HDAC_RIRBSIZE_RIRBSIZE_2]	= 2,
2566b021cc2SWarner Losh 	[HDAC_RIRBSIZE_RIRBSIZE_16]	= 16,
2576b021cc2SWarner Losh 	[HDAC_RIRBSIZE_RIRBSIZE_256]	= 256,
2586b021cc2SWarner Losh 	[HDAC_RIRBSIZE_RIRBSIZE_MASK]	= 0,
2596b021cc2SWarner Losh };
2606b021cc2SWarner Losh 
261489392feSMark Johnston static const struct hda_ops hops = {
2626b021cc2SWarner Losh 	.signal		= hda_signal_state_change,
2636b021cc2SWarner Losh 	.response	= hda_response,
2646b021cc2SWarner Losh 	.transfer	= hda_transfer,
2656b021cc2SWarner Losh };
2666b021cc2SWarner Losh 
26737045dfaSMark Johnston static const struct pci_devemu pci_de_hda = {
2686b021cc2SWarner Losh 	.pe_emu		= "hda",
2696b021cc2SWarner Losh 	.pe_init	= pci_hda_init,
2706b021cc2SWarner Losh 	.pe_barwrite	= pci_hda_write,
2716b021cc2SWarner Losh 	.pe_barread	= pci_hda_read
2726b021cc2SWarner Losh };
2736b021cc2SWarner Losh PCI_EMUL_SET(pci_de_hda);
2746b021cc2SWarner Losh 
2756b021cc2SWarner Losh SET_DECLARE(hda_codec_class_set, struct hda_codec_class);
2766b021cc2SWarner Losh 
2776b021cc2SWarner Losh #if DEBUG_HDA == 1
2786b021cc2SWarner Losh FILE *dbg;
2796b021cc2SWarner Losh #endif
2806b021cc2SWarner Losh 
2816b021cc2SWarner Losh /*
2826b021cc2SWarner Losh  * HDA module function definitions
2836b021cc2SWarner Losh  */
2846b021cc2SWarner Losh 
2856b021cc2SWarner Losh static inline void
hda_set_reg_by_offset(struct hda_softc * sc,uint32_t offset,uint32_t value)2866b021cc2SWarner Losh hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t value)
2876b021cc2SWarner Losh {
2886b021cc2SWarner Losh 	assert(offset < HDA_LAST_OFFSET);
2896b021cc2SWarner Losh 	sc->regs[offset] = value;
2906b021cc2SWarner Losh }
2916b021cc2SWarner Losh 
2926b021cc2SWarner Losh static inline uint32_t
hda_get_reg_by_offset(struct hda_softc * sc,uint32_t offset)2936b021cc2SWarner Losh hda_get_reg_by_offset(struct hda_softc *sc, uint32_t offset)
2946b021cc2SWarner Losh {
2956b021cc2SWarner Losh 	assert(offset < HDA_LAST_OFFSET);
2966b021cc2SWarner Losh 	return sc->regs[offset];
2976b021cc2SWarner Losh }
2986b021cc2SWarner Losh 
2996b021cc2SWarner Losh static inline void
hda_set_field_by_offset(struct hda_softc * sc,uint32_t offset,uint32_t mask,uint32_t value)3006b021cc2SWarner Losh hda_set_field_by_offset(struct hda_softc *sc, uint32_t offset,
3016b021cc2SWarner Losh     uint32_t mask, uint32_t value)
3026b021cc2SWarner Losh {
3036b021cc2SWarner Losh 	uint32_t reg_value = 0;
3046b021cc2SWarner Losh 
3056b021cc2SWarner Losh 	reg_value = hda_get_reg_by_offset(sc, offset);
3066b021cc2SWarner Losh 
3076b021cc2SWarner Losh 	reg_value &= ~mask;
3086b021cc2SWarner Losh 	reg_value |= (value & mask);
3096b021cc2SWarner Losh 
3106b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, offset, reg_value);
3116b021cc2SWarner Losh }
3126b021cc2SWarner Losh 
3136b021cc2SWarner Losh static struct hda_softc *
hda_init(nvlist_t * nvl)314621b5090SJohn Baldwin hda_init(nvlist_t *nvl)
3156b021cc2SWarner Losh {
3166b021cc2SWarner Losh 	struct hda_softc *sc = NULL;
3176b021cc2SWarner Losh 	struct hda_codec_class *codec = NULL;
318621b5090SJohn Baldwin 	const char *value;
319621b5090SJohn Baldwin 	char *play;
320621b5090SJohn Baldwin 	char *rec;
321621b5090SJohn Baldwin 	int err;
3226b021cc2SWarner Losh 
3236b021cc2SWarner Losh #if DEBUG_HDA == 1
324014e0341SGuido Falsi 	dbg = fopen(DEBUG_HDA_FILE, "w+");
3256b021cc2SWarner Losh #endif
3266b021cc2SWarner Losh 
3276b021cc2SWarner Losh 	sc = calloc(1, sizeof(*sc));
3286b021cc2SWarner Losh 	if (!sc)
3296b021cc2SWarner Losh 		return (NULL);
3306b021cc2SWarner Losh 
3316b021cc2SWarner Losh 	hda_reset_regs(sc);
3326b021cc2SWarner Losh 
3336b021cc2SWarner Losh 	/*
334621b5090SJohn Baldwin 	 * TODO search all configured codecs
3356b021cc2SWarner Losh 	 * For now we play with one single codec
3366b021cc2SWarner Losh 	 */
3376b021cc2SWarner Losh 	codec = hda_find_codec_class("hda_codec");
3386b021cc2SWarner Losh 	if (codec) {
339621b5090SJohn Baldwin 		value = get_config_value_node(nvl, "play");
340621b5090SJohn Baldwin 		if (value == NULL)
341621b5090SJohn Baldwin 			play = NULL;
342621b5090SJohn Baldwin 		else
343621b5090SJohn Baldwin 			play = strdup(value);
344621b5090SJohn Baldwin 		value = get_config_value_node(nvl, "rec");
345621b5090SJohn Baldwin 		if (value == NULL)
346621b5090SJohn Baldwin 			rec = NULL;
347621b5090SJohn Baldwin 		else
348621b5090SJohn Baldwin 			rec = strdup(value);
349332eff95SVincenzo Maffione 		DPRINTF("play: %s rec: %s", play, rec);
350621b5090SJohn Baldwin 		if (play != NULL || rec != NULL) {
351621b5090SJohn Baldwin 			err = hda_codec_constructor(sc, codec, play, rec);
3526b021cc2SWarner Losh 			assert(!err);
3536b021cc2SWarner Losh 		}
354621b5090SJohn Baldwin 		free(play);
355621b5090SJohn Baldwin 		free(rec);
3566b021cc2SWarner Losh 	}
3576b021cc2SWarner Losh 
3586b021cc2SWarner Losh 	return (sc);
3596b021cc2SWarner Losh }
3606b021cc2SWarner Losh 
3616b021cc2SWarner Losh static void
hda_update_intr(struct hda_softc * sc)3626b021cc2SWarner Losh hda_update_intr(struct hda_softc *sc)
3636b021cc2SWarner Losh {
3646b021cc2SWarner Losh 	struct pci_devinst *pi = sc->pci_dev;
3656b021cc2SWarner Losh 	uint32_t intctl = hda_get_reg_by_offset(sc, HDAC_INTCTL);
3666b021cc2SWarner Losh 	uint32_t intsts = 0;
3676b021cc2SWarner Losh 	uint32_t sdsts = 0;
3686b021cc2SWarner Losh 	uint32_t rirbsts = 0;
3696b021cc2SWarner Losh 	uint32_t wakeen = 0;
3706b021cc2SWarner Losh 	uint32_t statests = 0;
3716b021cc2SWarner Losh 	uint32_t off = 0;
3726b021cc2SWarner Losh 	int i;
3736b021cc2SWarner Losh 
3746b021cc2SWarner Losh 	/* update the CIS bits */
3756b021cc2SWarner Losh 	rirbsts = hda_get_reg_by_offset(sc, HDAC_RIRBSTS);
3766b021cc2SWarner Losh 	if (rirbsts & (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS))
3776b021cc2SWarner Losh 		intsts |= HDAC_INTSTS_CIS;
3786b021cc2SWarner Losh 
3796b021cc2SWarner Losh 	wakeen = hda_get_reg_by_offset(sc, HDAC_WAKEEN);
3806b021cc2SWarner Losh 	statests = hda_get_reg_by_offset(sc, HDAC_STATESTS);
3816b021cc2SWarner Losh 	if (statests & wakeen)
3826b021cc2SWarner Losh 		intsts |= HDAC_INTSTS_CIS;
3836b021cc2SWarner Losh 
3846b021cc2SWarner Losh 	/* update the SIS bits */
3856b021cc2SWarner Losh 	for (i = 0; i < HDA_IOSS_NO; i++) {
3866b021cc2SWarner Losh 		off = hda_get_offset_stream(i);
3876b021cc2SWarner Losh 		sdsts = hda_get_reg_by_offset(sc, off + HDAC_SDSTS);
3886b021cc2SWarner Losh 		if (sdsts & HDAC_SDSTS_BCIS)
3896b021cc2SWarner Losh 			intsts |= (1 << i);
3906b021cc2SWarner Losh 	}
3916b021cc2SWarner Losh 
3926b021cc2SWarner Losh 	/* update the GIS bit */
3936b021cc2SWarner Losh 	if (intsts)
3946b021cc2SWarner Losh 		intsts |= HDAC_INTSTS_GIS;
3956b021cc2SWarner Losh 
3966b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_INTSTS, intsts);
3976b021cc2SWarner Losh 
3986b021cc2SWarner Losh 	if ((intctl & HDAC_INTCTL_GIE) && ((intsts &			\
3996b021cc2SWarner Losh 		~HDAC_INTSTS_GIS) & intctl)) {
4006b021cc2SWarner Losh 		if (!sc->lintr) {
4016b021cc2SWarner Losh 			pci_lintr_assert(pi);
4026b021cc2SWarner Losh 			sc->lintr = 1;
4036b021cc2SWarner Losh 		}
4046b021cc2SWarner Losh 	} else {
4056b021cc2SWarner Losh 		if (sc->lintr) {
4066b021cc2SWarner Losh 			pci_lintr_deassert(pi);
4076b021cc2SWarner Losh 			sc->lintr = 0;
4086b021cc2SWarner Losh 		}
4096b021cc2SWarner Losh 	}
4106b021cc2SWarner Losh }
4116b021cc2SWarner Losh 
4126b021cc2SWarner Losh static void
hda_response_interrupt(struct hda_softc * sc)4136b021cc2SWarner Losh hda_response_interrupt(struct hda_softc *sc)
4146b021cc2SWarner Losh {
4156b021cc2SWarner Losh 	uint8_t rirbctl = hda_get_reg_by_offset(sc, HDAC_RIRBCTL);
4166b021cc2SWarner Losh 
4176b021cc2SWarner Losh 	if ((rirbctl & HDAC_RIRBCTL_RINTCTL) && sc->rirb_cnt) {
4186b021cc2SWarner Losh 		sc->rirb_cnt = 0;
4196b021cc2SWarner Losh 		hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL,
4206b021cc2SWarner Losh 				HDAC_RIRBSTS_RINTFL);
4216b021cc2SWarner Losh 		hda_update_intr(sc);
4226b021cc2SWarner Losh 	}
4236b021cc2SWarner Losh }
4246b021cc2SWarner Losh 
4256b021cc2SWarner Losh static int
hda_codec_constructor(struct hda_softc * sc,struct hda_codec_class * codec,const char * play,const char * rec)4266b021cc2SWarner Losh hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec,
427621b5090SJohn Baldwin     const char *play, const char *rec)
4286b021cc2SWarner Losh {
4296b021cc2SWarner Losh 	struct hda_codec_inst *hci = NULL;
4306b021cc2SWarner Losh 
4316b021cc2SWarner Losh 	if (sc->codecs_no >= HDA_CODEC_MAX)
4326b021cc2SWarner Losh 		return (-1);
4336b021cc2SWarner Losh 
4346b021cc2SWarner Losh 	hci = calloc(1, sizeof(struct hda_codec_inst));
4356b021cc2SWarner Losh 	if (!hci)
4366b021cc2SWarner Losh 		return (-1);
4376b021cc2SWarner Losh 
4386b021cc2SWarner Losh 	hci->hda = sc;
4396b021cc2SWarner Losh 	hci->hops = &hops;
4406b021cc2SWarner Losh 	hci->cad = sc->codecs_no;
4416b021cc2SWarner Losh 	hci->codec = codec;
4426b021cc2SWarner Losh 
4436b021cc2SWarner Losh 	sc->codecs[sc->codecs_no++] = hci;
4446b021cc2SWarner Losh 
4456b021cc2SWarner Losh 	if (!codec->init) {
446332eff95SVincenzo Maffione 		DPRINTF("This codec does not implement the init function");
4476b021cc2SWarner Losh 		return (-1);
4486b021cc2SWarner Losh 	}
4496b021cc2SWarner Losh 
450621b5090SJohn Baldwin 	return (codec->init(hci, play, rec));
4516b021cc2SWarner Losh }
4526b021cc2SWarner Losh 
4536b021cc2SWarner Losh static struct hda_codec_class *
hda_find_codec_class(const char * name)4546b021cc2SWarner Losh hda_find_codec_class(const char *name)
4556b021cc2SWarner Losh {
4566b021cc2SWarner Losh 	struct hda_codec_class **pdpp = NULL, *pdp = NULL;
4576b021cc2SWarner Losh 
4586b021cc2SWarner Losh 	SET_FOREACH(pdpp, hda_codec_class_set) {
4596b021cc2SWarner Losh 		pdp = *pdpp;
4606b021cc2SWarner Losh 		if (!strcmp(pdp->name, name)) {
4616b021cc2SWarner Losh 			return (pdp);
4626b021cc2SWarner Losh 		}
4636b021cc2SWarner Losh 	}
4646b021cc2SWarner Losh 
4656b021cc2SWarner Losh 	return (NULL);
4666b021cc2SWarner Losh }
4676b021cc2SWarner Losh 
4686b021cc2SWarner Losh static int
hda_send_command(struct hda_softc * sc,uint32_t verb)4696b021cc2SWarner Losh hda_send_command(struct hda_softc *sc, uint32_t verb)
4706b021cc2SWarner Losh {
4716b021cc2SWarner Losh 	struct hda_codec_inst *hci = NULL;
4726b021cc2SWarner Losh 	struct hda_codec_class *codec = NULL;
4736b021cc2SWarner Losh 	uint8_t cad = (verb >> HDA_CMD_CAD_SHIFT) & 0x0f;
4746b021cc2SWarner Losh 
475cf57f20eSJohn Baldwin 	if (cad >= sc->codecs_no)
4766b021cc2SWarner Losh 		return (-1);
4776b021cc2SWarner Losh 
478332eff95SVincenzo Maffione 	DPRINTF("cad: 0x%x verb: 0x%x", cad, verb);
4796b021cc2SWarner Losh 
480cf57f20eSJohn Baldwin 	hci = sc->codecs[cad];
481cf57f20eSJohn Baldwin 	assert(hci);
482cf57f20eSJohn Baldwin 
4836b021cc2SWarner Losh 	codec = hci->codec;
4846b021cc2SWarner Losh 	assert(codec);
4856b021cc2SWarner Losh 
4866b021cc2SWarner Losh 	if (!codec->command) {
487332eff95SVincenzo Maffione 		DPRINTF("This codec does not implement the command function");
4886b021cc2SWarner Losh 		return (-1);
4896b021cc2SWarner Losh 	}
4906b021cc2SWarner Losh 
4916b021cc2SWarner Losh 	return (codec->command(hci, verb));
4926b021cc2SWarner Losh }
4936b021cc2SWarner Losh 
4946b021cc2SWarner Losh static int
hda_notify_codecs(struct hda_softc * sc,uint8_t run,uint8_t stream,uint8_t dir)4956b021cc2SWarner Losh hda_notify_codecs(struct hda_softc *sc, uint8_t run, uint8_t stream,
4966b021cc2SWarner Losh     uint8_t dir)
4976b021cc2SWarner Losh {
4986b021cc2SWarner Losh 	struct hda_codec_inst *hci = NULL;
4996b021cc2SWarner Losh 	struct hda_codec_class *codec = NULL;
5006b021cc2SWarner Losh 	int err;
5016b021cc2SWarner Losh 	int i;
5026b021cc2SWarner Losh 
5036b021cc2SWarner Losh 	/* Notify each codec */
5046b021cc2SWarner Losh 	for (i = 0; i < sc->codecs_no; i++) {
5056b021cc2SWarner Losh 		hci = sc->codecs[i];
5066b021cc2SWarner Losh 		assert(hci);
5076b021cc2SWarner Losh 
5086b021cc2SWarner Losh 		codec = hci->codec;
5096b021cc2SWarner Losh 		assert(codec);
5106b021cc2SWarner Losh 
5116b021cc2SWarner Losh 		if (codec->notify) {
5126b021cc2SWarner Losh 			err = codec->notify(hci, run, stream, dir);
5136b021cc2SWarner Losh 			if (!err)
5146b021cc2SWarner Losh 				break;
5156b021cc2SWarner Losh 		}
5166b021cc2SWarner Losh 	}
5176b021cc2SWarner Losh 
5186b021cc2SWarner Losh 	return (i == sc->codecs_no ? (-1) : 0);
5196b021cc2SWarner Losh }
5206b021cc2SWarner Losh 
5216b021cc2SWarner Losh static void
hda_reset(struct hda_softc * sc)5226b021cc2SWarner Losh hda_reset(struct hda_softc *sc)
5236b021cc2SWarner Losh {
5246b021cc2SWarner Losh 	int i;
5256b021cc2SWarner Losh 	struct hda_codec_inst *hci = NULL;
5266b021cc2SWarner Losh 	struct hda_codec_class *codec = NULL;
5276b021cc2SWarner Losh 
5286b021cc2SWarner Losh 	hda_reset_regs(sc);
5296b021cc2SWarner Losh 
5306b021cc2SWarner Losh 	/* Reset each codec */
5316b021cc2SWarner Losh 	for (i = 0; i < sc->codecs_no; i++) {
5326b021cc2SWarner Losh 		hci = sc->codecs[i];
5336b021cc2SWarner Losh 		assert(hci);
5346b021cc2SWarner Losh 
5356b021cc2SWarner Losh 		codec = hci->codec;
5366b021cc2SWarner Losh 		assert(codec);
5376b021cc2SWarner Losh 
5386b021cc2SWarner Losh 		if (codec->reset)
5396b021cc2SWarner Losh 			codec->reset(hci);
5406b021cc2SWarner Losh 	}
5416b021cc2SWarner Losh 
5426b021cc2SWarner Losh 	sc->wall_clock_start = hda_get_clock_ns();
5436b021cc2SWarner Losh }
5446b021cc2SWarner Losh 
5456b021cc2SWarner Losh static void
hda_reset_regs(struct hda_softc * sc)5466b021cc2SWarner Losh hda_reset_regs(struct hda_softc *sc)
5476b021cc2SWarner Losh {
5486b021cc2SWarner Losh 	uint32_t off = 0;
5496b021cc2SWarner Losh 	uint8_t i;
5506b021cc2SWarner Losh 
551332eff95SVincenzo Maffione 	DPRINTF("Reset the HDA controller registers ...");
5526b021cc2SWarner Losh 
5536b021cc2SWarner Losh 	memset(sc->regs, 0, sizeof(sc->regs));
5546b021cc2SWarner Losh 
5556b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_GCAP,
5566b021cc2SWarner Losh 			HDAC_GCAP_64OK |
5576b021cc2SWarner Losh 			(HDA_ISS_NO << HDAC_GCAP_ISS_SHIFT) |
5586b021cc2SWarner Losh 			(HDA_OSS_NO << HDAC_GCAP_OSS_SHIFT));
5596b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_VMAJ, 0x01);
5606b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_OUTPAY, 0x3c);
5616b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_INPAY, 0x1d);
5626b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_CORBSIZE,
5636b021cc2SWarner Losh 	    HDAC_CORBSIZE_CORBSZCAP_256 | HDAC_CORBSIZE_CORBSIZE_256);
5646b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_RIRBSIZE,
5656b021cc2SWarner Losh 	    HDAC_RIRBSIZE_RIRBSZCAP_256 | HDAC_RIRBSIZE_RIRBSIZE_256);
5666b021cc2SWarner Losh 
5676b021cc2SWarner Losh 	for (i = 0; i < HDA_IOSS_NO; i++) {
5686b021cc2SWarner Losh 		off = hda_get_offset_stream(i);
5696b021cc2SWarner Losh 		hda_set_reg_by_offset(sc, off + HDAC_SDFIFOS, HDA_FIFO_SIZE);
5706b021cc2SWarner Losh 	}
5716b021cc2SWarner Losh }
5726b021cc2SWarner Losh 
5736b021cc2SWarner Losh static void
hda_stream_reset(struct hda_softc * sc,uint8_t stream_ind)5746b021cc2SWarner Losh hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind)
5756b021cc2SWarner Losh {
5766b021cc2SWarner Losh 	struct hda_stream_desc *st = &sc->streams[stream_ind];
5776b021cc2SWarner Losh 	uint32_t off = hda_get_offset_stream(stream_ind);
5786b021cc2SWarner Losh 
579332eff95SVincenzo Maffione 	DPRINTF("Reset the HDA stream: 0x%x", stream_ind);
5806b021cc2SWarner Losh 
5816b021cc2SWarner Losh 	/* Reset the Stream Descriptor registers */
5826b021cc2SWarner Losh 	memset(sc->regs + HDA_STREAM_REGS_BASE + off, 0, HDA_STREAM_REGS_LEN);
5836b021cc2SWarner Losh 
5846b021cc2SWarner Losh 	/* Reset the Stream Descriptor */
5856b021cc2SWarner Losh 	memset(st, 0, sizeof(*st));
5866b021cc2SWarner Losh 
5876b021cc2SWarner Losh 	hda_set_field_by_offset(sc, off + HDAC_SDSTS,
5886b021cc2SWarner Losh 	    HDAC_SDSTS_FIFORDY, HDAC_SDSTS_FIFORDY);
5896b021cc2SWarner Losh 	hda_set_field_by_offset(sc, off + HDAC_SDCTL0,
5906b021cc2SWarner Losh 	    HDAC_SDCTL_SRST, HDAC_SDCTL_SRST);
5916b021cc2SWarner Losh }
5926b021cc2SWarner Losh 
5936b021cc2SWarner Losh static int
hda_stream_start(struct hda_softc * sc,uint8_t stream_ind)5946b021cc2SWarner Losh hda_stream_start(struct hda_softc *sc, uint8_t stream_ind)
5956b021cc2SWarner Losh {
5966b021cc2SWarner Losh 	struct hda_stream_desc *st = &sc->streams[stream_ind];
5976b021cc2SWarner Losh 	struct hda_bdle_desc *bdle_desc = NULL;
5986b021cc2SWarner Losh 	struct hda_bdle *bdle = NULL;
5996b021cc2SWarner Losh 	uint32_t lvi = 0;
6006b021cc2SWarner Losh 	uint32_t bdl_cnt = 0;
6016b021cc2SWarner Losh 	uint64_t bdpl = 0;
6026b021cc2SWarner Losh 	uint64_t bdpu = 0;
6036b021cc2SWarner Losh 	uint64_t bdl_paddr = 0;
6046b021cc2SWarner Losh 	void *bdl_vaddr = NULL;
6056b021cc2SWarner Losh 	uint32_t bdle_sz = 0;
6066b021cc2SWarner Losh 	uint64_t bdle_addrl = 0;
6076b021cc2SWarner Losh 	uint64_t bdle_addrh = 0;
6086b021cc2SWarner Losh 	uint64_t bdle_paddr = 0;
6096b021cc2SWarner Losh 	void *bdle_vaddr = NULL;
6106b021cc2SWarner Losh 	uint32_t off = hda_get_offset_stream(stream_ind);
6116b021cc2SWarner Losh 	uint32_t sdctl = 0;
6126b021cc2SWarner Losh 	uint8_t strm = 0;
6136b021cc2SWarner Losh 	uint8_t dir = 0;
6146b021cc2SWarner Losh 
6156b021cc2SWarner Losh 	assert(!st->run);
6166b021cc2SWarner Losh 
6176b021cc2SWarner Losh 	lvi = hda_get_reg_by_offset(sc, off + HDAC_SDLVI);
6186b021cc2SWarner Losh 	bdpl = hda_get_reg_by_offset(sc, off + HDAC_SDBDPL);
6196b021cc2SWarner Losh 	bdpu = hda_get_reg_by_offset(sc, off + HDAC_SDBDPU);
6206b021cc2SWarner Losh 
6216b021cc2SWarner Losh 	bdl_cnt = lvi + 1;
6226b021cc2SWarner Losh 	assert(bdl_cnt <= HDA_BDL_MAX_LEN);
6236b021cc2SWarner Losh 
6246b021cc2SWarner Losh 	bdl_paddr = bdpl | (bdpu << 32);
6256b021cc2SWarner Losh 	bdl_vaddr = hda_dma_get_vaddr(sc, bdl_paddr,
6266b021cc2SWarner Losh 	    HDA_BDL_ENTRY_LEN * bdl_cnt);
6276b021cc2SWarner Losh 	if (!bdl_vaddr) {
628332eff95SVincenzo Maffione 		DPRINTF("Fail to get the guest virtual address");
6296b021cc2SWarner Losh 		return (-1);
6306b021cc2SWarner Losh 	}
6316b021cc2SWarner Losh 
632332eff95SVincenzo Maffione 	DPRINTF("stream: 0x%x bdl_cnt: 0x%x bdl_paddr: 0x%lx",
6336b021cc2SWarner Losh 	    stream_ind, bdl_cnt, bdl_paddr);
6346b021cc2SWarner Losh 
6356b021cc2SWarner Losh 	st->bdl_cnt = bdl_cnt;
6366b021cc2SWarner Losh 
6376b021cc2SWarner Losh 	bdle = (struct hda_bdle *)bdl_vaddr;
638ed721684SMark Johnston 	for (size_t i = 0; i < bdl_cnt; i++, bdle++) {
6396b021cc2SWarner Losh 		bdle_sz = bdle->len;
6406b021cc2SWarner Losh 		assert(!(bdle_sz % HDA_DMA_ACCESS_LEN));
6416b021cc2SWarner Losh 
6426b021cc2SWarner Losh 		bdle_addrl = bdle->addrl;
6436b021cc2SWarner Losh 		bdle_addrh = bdle->addrh;
6446b021cc2SWarner Losh 
6456b021cc2SWarner Losh 		bdle_paddr = bdle_addrl | (bdle_addrh << 32);
6466b021cc2SWarner Losh 		bdle_vaddr = hda_dma_get_vaddr(sc, bdle_paddr, bdle_sz);
6476b021cc2SWarner Losh 		if (!bdle_vaddr) {
648332eff95SVincenzo Maffione 			DPRINTF("Fail to get the guest virtual address");
6496b021cc2SWarner Losh 			return (-1);
6506b021cc2SWarner Losh 		}
6516b021cc2SWarner Losh 
6526b021cc2SWarner Losh 		bdle_desc = &st->bdl[i];
6536b021cc2SWarner Losh 		bdle_desc->addr = bdle_vaddr;
6546b021cc2SWarner Losh 		bdle_desc->len = bdle_sz;
6556b021cc2SWarner Losh 		bdle_desc->ioc = bdle->ioc;
6566b021cc2SWarner Losh 
657ed721684SMark Johnston 		DPRINTF("bdle: 0x%zx bdle_sz: 0x%x", i, bdle_sz);
6586b021cc2SWarner Losh 	}
6596b021cc2SWarner Losh 
6606b021cc2SWarner Losh 	sdctl = hda_get_reg_by_offset(sc, off + HDAC_SDCTL0);
6616b021cc2SWarner Losh 	strm = (sdctl >> 20) & 0x0f;
6626b021cc2SWarner Losh 	dir = stream_ind >= HDA_ISS_NO;
6636b021cc2SWarner Losh 
664332eff95SVincenzo Maffione 	DPRINTF("strm: 0x%x, dir: 0x%x", strm, dir);
6656b021cc2SWarner Losh 
6666b021cc2SWarner Losh 	sc->stream_map[dir][strm] = stream_ind;
6676b021cc2SWarner Losh 	st->stream = strm;
6686b021cc2SWarner Losh 	st->dir = dir;
6696b021cc2SWarner Losh 	st->bp = 0;
6706b021cc2SWarner Losh 	st->be = 0;
6716b021cc2SWarner Losh 
6726b021cc2SWarner Losh 	hda_set_pib(sc, stream_ind, 0);
6736b021cc2SWarner Losh 
6746b021cc2SWarner Losh 	st->run = 1;
6756b021cc2SWarner Losh 
6766b021cc2SWarner Losh 	hda_notify_codecs(sc, 1, strm, dir);
6776b021cc2SWarner Losh 
6786b021cc2SWarner Losh 	return (0);
6796b021cc2SWarner Losh }
6806b021cc2SWarner Losh 
6816b021cc2SWarner Losh static int
hda_stream_stop(struct hda_softc * sc,uint8_t stream_ind)6826b021cc2SWarner Losh hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind)
6836b021cc2SWarner Losh {
6846b021cc2SWarner Losh 	struct hda_stream_desc *st = &sc->streams[stream_ind];
6856b021cc2SWarner Losh 	uint8_t strm = st->stream;
6866b021cc2SWarner Losh 	uint8_t dir = st->dir;
6876b021cc2SWarner Losh 
688332eff95SVincenzo Maffione 	DPRINTF("stream: 0x%x, strm: 0x%x, dir: 0x%x", stream_ind, strm, dir);
6896b021cc2SWarner Losh 
6906b021cc2SWarner Losh 	st->run = 0;
6916b021cc2SWarner Losh 
6926b021cc2SWarner Losh 	hda_notify_codecs(sc, 0, strm, dir);
6936b021cc2SWarner Losh 
6946b021cc2SWarner Losh 	return (0);
6956b021cc2SWarner Losh }
6966b021cc2SWarner Losh 
6976b021cc2SWarner Losh static uint32_t
hda_read(struct hda_softc * sc,uint32_t offset)6986b021cc2SWarner Losh hda_read(struct hda_softc *sc, uint32_t offset)
6996b021cc2SWarner Losh {
7006b021cc2SWarner Losh 	if (offset == HDAC_WALCLK)
7016b021cc2SWarner Losh 		return (24 * (hda_get_clock_ns() -			\
7026b021cc2SWarner Losh 			sc->wall_clock_start) / 1000);
7036b021cc2SWarner Losh 
7046b021cc2SWarner Losh 	return (hda_get_reg_by_offset(sc, offset));
7056b021cc2SWarner Losh }
7066b021cc2SWarner Losh 
7076b021cc2SWarner Losh static int
hda_write(struct hda_softc * sc,uint32_t offset,uint8_t size,uint32_t value)7086b021cc2SWarner Losh hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size, uint32_t value)
7096b021cc2SWarner Losh {
7106b021cc2SWarner Losh 	uint32_t old = hda_get_reg_by_offset(sc, offset);
7116b021cc2SWarner Losh 	uint32_t masks[] = {0x00000000, 0x000000ff, 0x0000ffff,
7126b021cc2SWarner Losh 			0x00ffffff, 0xffffffff};
713bfe8e339SJohn Baldwin 	hda_set_reg_handler set_reg_handler = NULL;
714bfe8e339SJohn Baldwin 
715bfe8e339SJohn Baldwin 	if (offset < nitems(hda_set_reg_table))
716bfe8e339SJohn Baldwin 		set_reg_handler = hda_set_reg_table[offset];
7176b021cc2SWarner Losh 
7186b021cc2SWarner Losh 	hda_set_field_by_offset(sc, offset, masks[size], value);
7196b021cc2SWarner Losh 
7206b021cc2SWarner Losh 	if (set_reg_handler)
7216b021cc2SWarner Losh 		set_reg_handler(sc, offset, old);
7226b021cc2SWarner Losh 
7236b021cc2SWarner Losh 	return (0);
7246b021cc2SWarner Losh }
7256b021cc2SWarner Losh 
72603d31246SGuido Falsi #if DEBUG_HDA == 1
7276b021cc2SWarner Losh static inline void
hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl * p)7286b021cc2SWarner Losh hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p)
7296b021cc2SWarner Losh {
73003d31246SGuido Falsi 	DPRINTF("%s size: %d", p->name, p->size);
73103d31246SGuido Falsi 	DPRINTF("%s dma_vaddr: %p", p->name, p->dma_vaddr);
73203d31246SGuido Falsi 	DPRINTF("%s wp: 0x%x", p->name, p->wp);
73303d31246SGuido Falsi 	DPRINTF("%s rp: 0x%x", p->name, p->rp);
7346b021cc2SWarner Losh }
73503d31246SGuido Falsi #else
73603d31246SGuido Falsi static inline void
hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl * p __unused)73703d31246SGuido Falsi hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p __unused) {}
73803d31246SGuido Falsi #endif
7396b021cc2SWarner Losh 
7406b021cc2SWarner Losh static int
hda_corb_start(struct hda_softc * sc)7416b021cc2SWarner Losh hda_corb_start(struct hda_softc *sc)
7426b021cc2SWarner Losh {
7436b021cc2SWarner Losh 	struct hda_codec_cmd_ctl *corb = &sc->corb;
7446b021cc2SWarner Losh 	uint8_t corbsize = 0;
7456b021cc2SWarner Losh 	uint64_t corblbase = 0;
7466b021cc2SWarner Losh 	uint64_t corbubase = 0;
7476b021cc2SWarner Losh 	uint64_t corbpaddr = 0;
7486b021cc2SWarner Losh 
7496b021cc2SWarner Losh 	corb->name = "CORB";
7506b021cc2SWarner Losh 
7516b021cc2SWarner Losh 	corbsize = hda_get_reg_by_offset(sc, HDAC_CORBSIZE) &		\
7526b021cc2SWarner Losh 		   HDAC_CORBSIZE_CORBSIZE_MASK;
7536b021cc2SWarner Losh 	corb->size = hda_corb_sizes[corbsize];
7546b021cc2SWarner Losh 
7556b021cc2SWarner Losh 	if (!corb->size) {
756332eff95SVincenzo Maffione 		DPRINTF("Invalid corb size");
7576b021cc2SWarner Losh 		return (-1);
7586b021cc2SWarner Losh 	}
7596b021cc2SWarner Losh 
7606b021cc2SWarner Losh 	corblbase = hda_get_reg_by_offset(sc, HDAC_CORBLBASE);
7616b021cc2SWarner Losh 	corbubase = hda_get_reg_by_offset(sc, HDAC_CORBUBASE);
7626b021cc2SWarner Losh 
7636b021cc2SWarner Losh 	corbpaddr = corblbase | (corbubase << 32);
764332eff95SVincenzo Maffione 	DPRINTF("CORB dma_paddr: %p", (void *)corbpaddr);
7656b021cc2SWarner Losh 
7666b021cc2SWarner Losh 	corb->dma_vaddr = hda_dma_get_vaddr(sc, corbpaddr,
7676b021cc2SWarner Losh 			HDA_CORB_ENTRY_LEN * corb->size);
7686b021cc2SWarner Losh 	if (!corb->dma_vaddr) {
769332eff95SVincenzo Maffione 		DPRINTF("Fail to get the guest virtual address");
7706b021cc2SWarner Losh 		return (-1);
7716b021cc2SWarner Losh 	}
7726b021cc2SWarner Losh 
7736b021cc2SWarner Losh 	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
7746b021cc2SWarner Losh 	corb->rp = hda_get_reg_by_offset(sc, HDAC_CORBRP);
7756b021cc2SWarner Losh 
7766b021cc2SWarner Losh 	corb->run = 1;
7776b021cc2SWarner Losh 
7786b021cc2SWarner Losh 	hda_print_cmd_ctl_data(corb);
7796b021cc2SWarner Losh 
7806b021cc2SWarner Losh 	return (0);
7816b021cc2SWarner Losh }
7826b021cc2SWarner Losh 
7836b021cc2SWarner Losh static int
hda_corb_run(struct hda_softc * sc)7846b021cc2SWarner Losh hda_corb_run(struct hda_softc *sc)
7856b021cc2SWarner Losh {
7866b021cc2SWarner Losh 	struct hda_codec_cmd_ctl *corb = &sc->corb;
7876b021cc2SWarner Losh 	uint32_t verb = 0;
7886b021cc2SWarner Losh 	int err;
7896b021cc2SWarner Losh 
7906b021cc2SWarner Losh 	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
7916b021cc2SWarner Losh 
7926b021cc2SWarner Losh 	while (corb->rp != corb->wp && corb->run) {
7936b021cc2SWarner Losh 		corb->rp++;
7946b021cc2SWarner Losh 		corb->rp %= corb->size;
7956b021cc2SWarner Losh 
79663898728SMark Johnston 		verb = hda_dma_ld_dword((uint8_t *)corb->dma_vaddr +
7976b021cc2SWarner Losh 		    HDA_CORB_ENTRY_LEN * corb->rp);
7986b021cc2SWarner Losh 
7996b021cc2SWarner Losh 		err = hda_send_command(sc, verb);
8006b021cc2SWarner Losh 		assert(!err);
8016b021cc2SWarner Losh 	}
8026b021cc2SWarner Losh 
8036b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, HDAC_CORBRP, corb->rp);
8046b021cc2SWarner Losh 
8056b021cc2SWarner Losh 	if (corb->run)
8066b021cc2SWarner Losh 		hda_response_interrupt(sc);
8076b021cc2SWarner Losh 
8086b021cc2SWarner Losh 	return (0);
8096b021cc2SWarner Losh }
8106b021cc2SWarner Losh 
8116b021cc2SWarner Losh static int
hda_rirb_start(struct hda_softc * sc)8126b021cc2SWarner Losh hda_rirb_start(struct hda_softc *sc)
8136b021cc2SWarner Losh {
8146b021cc2SWarner Losh 	struct hda_codec_cmd_ctl *rirb = &sc->rirb;
8156b021cc2SWarner Losh 	uint8_t rirbsize = 0;
8166b021cc2SWarner Losh 	uint64_t rirblbase = 0;
8176b021cc2SWarner Losh 	uint64_t rirbubase = 0;
8186b021cc2SWarner Losh 	uint64_t rirbpaddr = 0;
8196b021cc2SWarner Losh 
8206b021cc2SWarner Losh 	rirb->name = "RIRB";
8216b021cc2SWarner Losh 
8226b021cc2SWarner Losh 	rirbsize = hda_get_reg_by_offset(sc, HDAC_RIRBSIZE) &		\
8236b021cc2SWarner Losh 		   HDAC_RIRBSIZE_RIRBSIZE_MASK;
8246b021cc2SWarner Losh 	rirb->size = hda_rirb_sizes[rirbsize];
8256b021cc2SWarner Losh 
8266b021cc2SWarner Losh 	if (!rirb->size) {
827332eff95SVincenzo Maffione 		DPRINTF("Invalid rirb size");
8286b021cc2SWarner Losh 		return (-1);
8296b021cc2SWarner Losh 	}
8306b021cc2SWarner Losh 
8316b021cc2SWarner Losh 	rirblbase = hda_get_reg_by_offset(sc, HDAC_RIRBLBASE);
8326b021cc2SWarner Losh 	rirbubase = hda_get_reg_by_offset(sc, HDAC_RIRBUBASE);
8336b021cc2SWarner Losh 
8346b021cc2SWarner Losh 	rirbpaddr = rirblbase | (rirbubase << 32);
835332eff95SVincenzo Maffione 	DPRINTF("RIRB dma_paddr: %p", (void *)rirbpaddr);
8366b021cc2SWarner Losh 
8376b021cc2SWarner Losh 	rirb->dma_vaddr = hda_dma_get_vaddr(sc, rirbpaddr,
8386b021cc2SWarner Losh 			HDA_RIRB_ENTRY_LEN * rirb->size);
8396b021cc2SWarner Losh 	if (!rirb->dma_vaddr) {
840332eff95SVincenzo Maffione 		DPRINTF("Fail to get the guest virtual address");
8416b021cc2SWarner Losh 		return (-1);
8426b021cc2SWarner Losh 	}
8436b021cc2SWarner Losh 
8446b021cc2SWarner Losh 	rirb->wp = hda_get_reg_by_offset(sc, HDAC_RIRBWP);
8456b021cc2SWarner Losh 	rirb->rp = 0x0000;
8466b021cc2SWarner Losh 
8476b021cc2SWarner Losh 	rirb->run = 1;
8486b021cc2SWarner Losh 
8496b021cc2SWarner Losh 	hda_print_cmd_ctl_data(rirb);
8506b021cc2SWarner Losh 
8516b021cc2SWarner Losh 	return (0);
8526b021cc2SWarner Losh }
8536b021cc2SWarner Losh 
8546b021cc2SWarner Losh static void *
hda_dma_get_vaddr(struct hda_softc * sc,uint64_t dma_paddr,size_t len)8556b021cc2SWarner Losh hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr, size_t len)
8566b021cc2SWarner Losh {
8576b021cc2SWarner Losh 	struct pci_devinst *pi = sc->pci_dev;
8586b021cc2SWarner Losh 
8596b021cc2SWarner Losh 	assert(pi);
8606b021cc2SWarner Losh 
8616b021cc2SWarner Losh 	return (paddr_guest2host(pi->pi_vmctx, (uintptr_t)dma_paddr, len));
8626b021cc2SWarner Losh }
8636b021cc2SWarner Losh 
8646b021cc2SWarner Losh static void
hda_dma_st_dword(void * dma_vaddr,uint32_t data)8656b021cc2SWarner Losh hda_dma_st_dword(void *dma_vaddr, uint32_t data)
8666b021cc2SWarner Losh {
8676b021cc2SWarner Losh 	*(uint32_t*)dma_vaddr = data;
8686b021cc2SWarner Losh }
8696b021cc2SWarner Losh 
8706b021cc2SWarner Losh static uint32_t
hda_dma_ld_dword(void * dma_vaddr)8716b021cc2SWarner Losh hda_dma_ld_dword(void *dma_vaddr)
8726b021cc2SWarner Losh {
8736b021cc2SWarner Losh 	return (*(uint32_t*)dma_vaddr);
8746b021cc2SWarner Losh }
8756b021cc2SWarner Losh 
8766b021cc2SWarner Losh static inline uint8_t
hda_get_stream_by_offsets(uint32_t offset,uint8_t reg_offset)8776b021cc2SWarner Losh hda_get_stream_by_offsets(uint32_t offset, uint8_t reg_offset)
8786b021cc2SWarner Losh {
8796b021cc2SWarner Losh 	uint8_t stream_ind = (offset - reg_offset) >> 5;
8806b021cc2SWarner Losh 
8816b021cc2SWarner Losh 	assert(stream_ind < HDA_IOSS_NO);
8826b021cc2SWarner Losh 
8836b021cc2SWarner Losh 	return (stream_ind);
8846b021cc2SWarner Losh }
8856b021cc2SWarner Losh 
8866b021cc2SWarner Losh static inline uint32_t
hda_get_offset_stream(uint8_t stream_ind)8876b021cc2SWarner Losh hda_get_offset_stream(uint8_t stream_ind)
8886b021cc2SWarner Losh {
8896b021cc2SWarner Losh 	return (stream_ind << 5);
8906b021cc2SWarner Losh }
8916b021cc2SWarner Losh 
8926b021cc2SWarner Losh static void
hda_set_gctl(struct hda_softc * sc,uint32_t offset,uint32_t old __unused)89398d920d9SMark Johnston hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
8946b021cc2SWarner Losh {
8956b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
8966b021cc2SWarner Losh 
8976b021cc2SWarner Losh 	if (!(value & HDAC_GCTL_CRST)) {
8986b021cc2SWarner Losh 		hda_reset(sc);
8996b021cc2SWarner Losh 	}
9006b021cc2SWarner Losh }
9016b021cc2SWarner Losh 
9026b021cc2SWarner Losh static void
hda_set_statests(struct hda_softc * sc,uint32_t offset,uint32_t old)9036b021cc2SWarner Losh hda_set_statests(struct hda_softc *sc, uint32_t offset, uint32_t old)
9046b021cc2SWarner Losh {
9056b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
9066b021cc2SWarner Losh 
9076b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, offset, old);
9086b021cc2SWarner Losh 
9096b021cc2SWarner Losh 	/* clear the corresponding bits written by the software (guest) */
9106b021cc2SWarner Losh 	hda_set_field_by_offset(sc, offset, value & HDA_STATESTS_IRQ_MASK, 0);
9116b021cc2SWarner Losh 
9126b021cc2SWarner Losh 	hda_update_intr(sc);
9136b021cc2SWarner Losh }
9146b021cc2SWarner Losh 
9156b021cc2SWarner Losh static void
hda_set_corbwp(struct hda_softc * sc,uint32_t offset __unused,uint32_t old __unused)91698d920d9SMark Johnston hda_set_corbwp(struct hda_softc *sc, uint32_t offset __unused,
91798d920d9SMark Johnston     uint32_t old __unused)
9186b021cc2SWarner Losh {
9196b021cc2SWarner Losh 	hda_corb_run(sc);
9206b021cc2SWarner Losh }
9216b021cc2SWarner Losh 
9226b021cc2SWarner Losh static void
hda_set_corbctl(struct hda_softc * sc,uint32_t offset,uint32_t old)9236b021cc2SWarner Losh hda_set_corbctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
9246b021cc2SWarner Losh {
9256b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
9266b021cc2SWarner Losh 	int err;
9276b021cc2SWarner Losh 	struct hda_codec_cmd_ctl *corb = NULL;
9286b021cc2SWarner Losh 
9296b021cc2SWarner Losh 	if (value & HDAC_CORBCTL_CORBRUN) {
9306b021cc2SWarner Losh 		if (!(old & HDAC_CORBCTL_CORBRUN)) {
9316b021cc2SWarner Losh 			err = hda_corb_start(sc);
9326b021cc2SWarner Losh 			assert(!err);
9336b021cc2SWarner Losh 		}
9346b021cc2SWarner Losh 	} else {
9356b021cc2SWarner Losh 		corb = &sc->corb;
9366b021cc2SWarner Losh 		memset(corb, 0, sizeof(*corb));
9376b021cc2SWarner Losh 	}
9386b021cc2SWarner Losh 
9396b021cc2SWarner Losh 	hda_corb_run(sc);
9406b021cc2SWarner Losh }
9416b021cc2SWarner Losh 
9426b021cc2SWarner Losh static void
hda_set_rirbctl(struct hda_softc * sc,uint32_t offset,uint32_t old __unused)94398d920d9SMark Johnston hda_set_rirbctl(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
9446b021cc2SWarner Losh {
9456b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
9466b021cc2SWarner Losh 	int err;
9476b021cc2SWarner Losh 	struct hda_codec_cmd_ctl *rirb = NULL;
9486b021cc2SWarner Losh 
9496b021cc2SWarner Losh 	if (value & HDAC_RIRBCTL_RIRBDMAEN) {
9506b021cc2SWarner Losh 		err = hda_rirb_start(sc);
9516b021cc2SWarner Losh 		assert(!err);
9526b021cc2SWarner Losh 	} else {
9536b021cc2SWarner Losh 		rirb = &sc->rirb;
9546b021cc2SWarner Losh 		memset(rirb, 0, sizeof(*rirb));
9556b021cc2SWarner Losh 	}
9566b021cc2SWarner Losh }
9576b021cc2SWarner Losh 
9586b021cc2SWarner Losh static void
hda_set_rirbsts(struct hda_softc * sc,uint32_t offset,uint32_t old)9596b021cc2SWarner Losh hda_set_rirbsts(struct hda_softc *sc, uint32_t offset, uint32_t old)
9606b021cc2SWarner Losh {
9616b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
9626b021cc2SWarner Losh 
9636b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, offset, old);
9646b021cc2SWarner Losh 
9656b021cc2SWarner Losh 	/* clear the corresponding bits written by the software (guest) */
9666b021cc2SWarner Losh 	hda_set_field_by_offset(sc, offset, value & HDA_RIRBSTS_IRQ_MASK, 0);
9676b021cc2SWarner Losh 
9686b021cc2SWarner Losh 	hda_update_intr(sc);
9696b021cc2SWarner Losh }
9706b021cc2SWarner Losh 
9716b021cc2SWarner Losh static void
hda_set_dpiblbase(struct hda_softc * sc,uint32_t offset,uint32_t old)9726b021cc2SWarner Losh hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset, uint32_t old)
9736b021cc2SWarner Losh {
9746b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
9756b021cc2SWarner Losh 	uint64_t dpiblbase = 0;
9766b021cc2SWarner Losh 	uint64_t dpibubase = 0;
9776b021cc2SWarner Losh 	uint64_t dpibpaddr = 0;
9786b021cc2SWarner Losh 
9796b021cc2SWarner Losh 	if ((value & HDAC_DPLBASE_DPLBASE_DMAPBE) != (old &		\
9806b021cc2SWarner Losh 				HDAC_DPLBASE_DPLBASE_DMAPBE)) {
9816b021cc2SWarner Losh 		if (value & HDAC_DPLBASE_DPLBASE_DMAPBE) {
9826b021cc2SWarner Losh 			dpiblbase = value & HDAC_DPLBASE_DPLBASE_MASK;
9836b021cc2SWarner Losh 			dpibubase = hda_get_reg_by_offset(sc, HDAC_DPIBUBASE);
9846b021cc2SWarner Losh 
9856b021cc2SWarner Losh 			dpibpaddr = dpiblbase | (dpibubase << 32);
986332eff95SVincenzo Maffione 			DPRINTF("DMA Position In Buffer dma_paddr: %p",
9876b021cc2SWarner Losh 			    (void *)dpibpaddr);
9886b021cc2SWarner Losh 
9896b021cc2SWarner Losh 			sc->dma_pib_vaddr = hda_dma_get_vaddr(sc, dpibpaddr,
9906b021cc2SWarner Losh 					HDA_DMA_PIB_ENTRY_LEN * HDA_IOSS_NO);
9916b021cc2SWarner Losh 			if (!sc->dma_pib_vaddr) {
9926b021cc2SWarner Losh 				DPRINTF("Fail to get the guest \
993332eff95SVincenzo Maffione 					 virtual address");
9946b021cc2SWarner Losh 				assert(0);
9956b021cc2SWarner Losh 			}
9966b021cc2SWarner Losh 		} else {
997332eff95SVincenzo Maffione 			DPRINTF("DMA Position In Buffer Reset");
9986b021cc2SWarner Losh 			sc->dma_pib_vaddr = NULL;
9996b021cc2SWarner Losh 		}
10006b021cc2SWarner Losh 	}
10016b021cc2SWarner Losh }
10026b021cc2SWarner Losh 
10036b021cc2SWarner Losh static void
hda_set_sdctl(struct hda_softc * sc,uint32_t offset,uint32_t old)10046b021cc2SWarner Losh hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
10056b021cc2SWarner Losh {
10066b021cc2SWarner Losh 	uint8_t stream_ind = hda_get_stream_by_offsets(offset, HDAC_SDCTL0);
10076b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
10086b021cc2SWarner Losh 	int err;
10096b021cc2SWarner Losh 
1010332eff95SVincenzo Maffione 	DPRINTF("stream_ind: 0x%x old: 0x%x value: 0x%x",
10116b021cc2SWarner Losh 	    stream_ind, old, value);
10126b021cc2SWarner Losh 
10136b021cc2SWarner Losh 	if (value & HDAC_SDCTL_SRST) {
10146b021cc2SWarner Losh 		hda_stream_reset(sc, stream_ind);
10156b021cc2SWarner Losh 	}
10166b021cc2SWarner Losh 
10176b021cc2SWarner Losh 	if ((value & HDAC_SDCTL_RUN) != (old & HDAC_SDCTL_RUN)) {
10186b021cc2SWarner Losh 		if (value & HDAC_SDCTL_RUN) {
10196b021cc2SWarner Losh 			err = hda_stream_start(sc, stream_ind);
10206b021cc2SWarner Losh 			assert(!err);
10216b021cc2SWarner Losh 		} else {
10226b021cc2SWarner Losh 			err = hda_stream_stop(sc, stream_ind);
10236b021cc2SWarner Losh 			assert(!err);
10246b021cc2SWarner Losh 		}
10256b021cc2SWarner Losh 	}
10266b021cc2SWarner Losh }
10276b021cc2SWarner Losh 
10286b021cc2SWarner Losh static void
hda_set_sdctl2(struct hda_softc * sc,uint32_t offset,uint32_t old __unused)102998d920d9SMark Johnston hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
10306b021cc2SWarner Losh {
10316b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
10326b021cc2SWarner Losh 
10336b021cc2SWarner Losh 	hda_set_field_by_offset(sc, offset - 2, 0x00ff0000, value << 16);
10346b021cc2SWarner Losh }
10356b021cc2SWarner Losh 
10366b021cc2SWarner Losh static void
hda_set_sdsts(struct hda_softc * sc,uint32_t offset,uint32_t old)10376b021cc2SWarner Losh hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old)
10386b021cc2SWarner Losh {
10396b021cc2SWarner Losh 	uint32_t value = hda_get_reg_by_offset(sc, offset);
10406b021cc2SWarner Losh 
10416b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, offset, old);
10426b021cc2SWarner Losh 
10436b021cc2SWarner Losh 	/* clear the corresponding bits written by the software (guest) */
10446b021cc2SWarner Losh 	hda_set_field_by_offset(sc, offset, value & HDA_SDSTS_IRQ_MASK, 0);
10456b021cc2SWarner Losh 
10466b021cc2SWarner Losh 	hda_update_intr(sc);
10476b021cc2SWarner Losh }
10486b021cc2SWarner Losh 
10496b021cc2SWarner Losh static int
hda_signal_state_change(struct hda_codec_inst * hci)10506b021cc2SWarner Losh hda_signal_state_change(struct hda_codec_inst *hci)
10516b021cc2SWarner Losh {
10526b021cc2SWarner Losh 	struct hda_softc *sc = NULL;
10536b021cc2SWarner Losh 	uint32_t sdiwake = 0;
10546b021cc2SWarner Losh 
10556b021cc2SWarner Losh 	assert(hci);
10566b021cc2SWarner Losh 	assert(hci->hda);
10576b021cc2SWarner Losh 
1058332eff95SVincenzo Maffione 	DPRINTF("cad: 0x%x", hci->cad);
10596b021cc2SWarner Losh 
10606b021cc2SWarner Losh 	sc = hci->hda;
10616b021cc2SWarner Losh 	sdiwake = 1 << hci->cad;
10626b021cc2SWarner Losh 
10636b021cc2SWarner Losh 	hda_set_field_by_offset(sc, HDAC_STATESTS, sdiwake, sdiwake);
10646b021cc2SWarner Losh 	hda_update_intr(sc);
10656b021cc2SWarner Losh 
10666b021cc2SWarner Losh 	return (0);
10676b021cc2SWarner Losh }
10686b021cc2SWarner Losh 
10696b021cc2SWarner Losh static int
hda_response(struct hda_codec_inst * hci,uint32_t response,uint8_t unsol)10706b021cc2SWarner Losh hda_response(struct hda_codec_inst *hci, uint32_t response, uint8_t unsol)
10716b021cc2SWarner Losh {
10726b021cc2SWarner Losh 	struct hda_softc *sc = NULL;
10736b021cc2SWarner Losh 	struct hda_codec_cmd_ctl *rirb = NULL;
10746b021cc2SWarner Losh 	uint32_t response_ex = 0;
10756b021cc2SWarner Losh 	uint8_t rintcnt = 0;
10766b021cc2SWarner Losh 
10776b021cc2SWarner Losh 	assert(hci);
10786b021cc2SWarner Losh 	assert(hci->cad <= HDA_CODEC_MAX);
10796b021cc2SWarner Losh 
10806b021cc2SWarner Losh 	response_ex = hci->cad | unsol;
10816b021cc2SWarner Losh 
10826b021cc2SWarner Losh 	sc = hci->hda;
10836b021cc2SWarner Losh 	assert(sc);
10846b021cc2SWarner Losh 
10856b021cc2SWarner Losh 	rirb = &sc->rirb;
10866b021cc2SWarner Losh 
10876b021cc2SWarner Losh 	if (rirb->run) {
10886b021cc2SWarner Losh 		rirb->wp++;
10896b021cc2SWarner Losh 		rirb->wp %= rirb->size;
10906b021cc2SWarner Losh 
109163898728SMark Johnston 		hda_dma_st_dword((uint8_t *)rirb->dma_vaddr +
109263898728SMark Johnston 		    HDA_RIRB_ENTRY_LEN * rirb->wp, response);
109363898728SMark Johnston 		hda_dma_st_dword((uint8_t *)rirb->dma_vaddr +
109463898728SMark Johnston 		    HDA_RIRB_ENTRY_LEN * rirb->wp + 0x04, response_ex);
10956b021cc2SWarner Losh 
10966b021cc2SWarner Losh 		hda_set_reg_by_offset(sc, HDAC_RIRBWP, rirb->wp);
10976b021cc2SWarner Losh 
10986b021cc2SWarner Losh 		sc->rirb_cnt++;
10996b021cc2SWarner Losh 	}
11006b021cc2SWarner Losh 
11016b021cc2SWarner Losh 	rintcnt = hda_get_reg_by_offset(sc, HDAC_RINTCNT);
11026b021cc2SWarner Losh 	if (sc->rirb_cnt == rintcnt)
11036b021cc2SWarner Losh 		hda_response_interrupt(sc);
11046b021cc2SWarner Losh 
11056b021cc2SWarner Losh 	return (0);
11066b021cc2SWarner Losh }
11076b021cc2SWarner Losh 
11086b021cc2SWarner Losh static int
hda_transfer(struct hda_codec_inst * hci,uint8_t stream,uint8_t dir,uint8_t * buf,size_t count)11096b021cc2SWarner Losh hda_transfer(struct hda_codec_inst *hci, uint8_t stream, uint8_t dir,
111063898728SMark Johnston     uint8_t *buf, size_t count)
11116b021cc2SWarner Losh {
11126b021cc2SWarner Losh 	struct hda_softc *sc = NULL;
11136b021cc2SWarner Losh 	struct hda_stream_desc *st = NULL;
11146b021cc2SWarner Losh 	struct hda_bdle_desc *bdl = NULL;
11156b021cc2SWarner Losh 	struct hda_bdle_desc *bdle_desc = NULL;
11166b021cc2SWarner Losh 	uint8_t stream_ind = 0;
11176b021cc2SWarner Losh 	uint32_t lpib = 0;
11186b021cc2SWarner Losh 	uint32_t off = 0;
11196b021cc2SWarner Losh 	size_t left = 0;
11206b021cc2SWarner Losh 	uint8_t irq = 0;
11216b021cc2SWarner Losh 
11226b021cc2SWarner Losh 	assert(hci);
11236b021cc2SWarner Losh 	assert(hci->hda);
11246b021cc2SWarner Losh 	assert(buf);
11256b021cc2SWarner Losh 	assert(!(count % HDA_DMA_ACCESS_LEN));
11266b021cc2SWarner Losh 
11276b021cc2SWarner Losh 	if (!stream) {
1128332eff95SVincenzo Maffione 		DPRINTF("Invalid stream");
11296b021cc2SWarner Losh 		return (-1);
11306b021cc2SWarner Losh 	}
11316b021cc2SWarner Losh 
11326b021cc2SWarner Losh 	sc = hci->hda;
11336b021cc2SWarner Losh 
11346b021cc2SWarner Losh 	assert(stream < HDA_STREAM_TAGS_CNT);
11356b021cc2SWarner Losh 	stream_ind = sc->stream_map[dir][stream];
11366b021cc2SWarner Losh 
11376b021cc2SWarner Losh 	if (!dir)
11386b021cc2SWarner Losh 		assert(stream_ind < HDA_ISS_NO);
11396b021cc2SWarner Losh 	else
11406b021cc2SWarner Losh 		assert(stream_ind >= HDA_ISS_NO && stream_ind < HDA_IOSS_NO);
11416b021cc2SWarner Losh 
11426b021cc2SWarner Losh 	st = &sc->streams[stream_ind];
11436b021cc2SWarner Losh 	if (!st->run) {
1144332eff95SVincenzo Maffione 		DPRINTF("Stream 0x%x stopped", stream);
11456b021cc2SWarner Losh 		return (-1);
11466b021cc2SWarner Losh 	}
11476b021cc2SWarner Losh 
11486b021cc2SWarner Losh 	assert(st->stream == stream);
11496b021cc2SWarner Losh 
11506b021cc2SWarner Losh 	off = hda_get_offset_stream(stream_ind);
11516b021cc2SWarner Losh 
11526b021cc2SWarner Losh 	lpib = hda_get_reg_by_offset(sc, off + HDAC_SDLPIB);
11536b021cc2SWarner Losh 
11546b021cc2SWarner Losh 	bdl = st->bdl;
11556b021cc2SWarner Losh 
11566b021cc2SWarner Losh 	assert(st->be < st->bdl_cnt);
11576b021cc2SWarner Losh 	assert(st->bp < bdl[st->be].len);
11586b021cc2SWarner Losh 
11596b021cc2SWarner Losh 	left = count;
11606b021cc2SWarner Losh 	while (left) {
11616b021cc2SWarner Losh 		bdle_desc = &bdl[st->be];
11626b021cc2SWarner Losh 
11636b021cc2SWarner Losh 		if (dir)
116463898728SMark Johnston 			*(uint32_t *)buf = hda_dma_ld_dword(
116563898728SMark Johnston 			    (uint8_t *)bdle_desc->addr + st->bp);
11666b021cc2SWarner Losh 		else
116763898728SMark Johnston 			hda_dma_st_dword((uint8_t *)bdle_desc->addr +
116863898728SMark Johnston 			    st->bp, *(uint32_t *)buf);
11696b021cc2SWarner Losh 
11706b021cc2SWarner Losh 		buf += HDA_DMA_ACCESS_LEN;
11716b021cc2SWarner Losh 		st->bp += HDA_DMA_ACCESS_LEN;
11726b021cc2SWarner Losh 		lpib += HDA_DMA_ACCESS_LEN;
11736b021cc2SWarner Losh 		left -= HDA_DMA_ACCESS_LEN;
11746b021cc2SWarner Losh 
11756b021cc2SWarner Losh 		if (st->bp == bdle_desc->len) {
11766b021cc2SWarner Losh 			st->bp = 0;
11776b021cc2SWarner Losh 			if (bdle_desc->ioc)
11786b021cc2SWarner Losh 				irq = 1;
11796b021cc2SWarner Losh 			st->be++;
11806b021cc2SWarner Losh 			if (st->be == st->bdl_cnt) {
11816b021cc2SWarner Losh 				st->be = 0;
11826b021cc2SWarner Losh 				lpib = 0;
11836b021cc2SWarner Losh 			}
11846b021cc2SWarner Losh 			bdle_desc = &bdl[st->be];
11856b021cc2SWarner Losh 		}
11866b021cc2SWarner Losh 	}
11876b021cc2SWarner Losh 
11886b021cc2SWarner Losh 	hda_set_pib(sc, stream_ind, lpib);
11896b021cc2SWarner Losh 
11906b021cc2SWarner Losh 	if (irq) {
11916b021cc2SWarner Losh 		hda_set_field_by_offset(sc, off + HDAC_SDSTS,
11926b021cc2SWarner Losh 				HDAC_SDSTS_BCIS, HDAC_SDSTS_BCIS);
11936b021cc2SWarner Losh 		hda_update_intr(sc);
11946b021cc2SWarner Losh 	}
11956b021cc2SWarner Losh 
11966b021cc2SWarner Losh 	return (0);
11976b021cc2SWarner Losh }
11986b021cc2SWarner Losh 
11996b021cc2SWarner Losh static void
hda_set_pib(struct hda_softc * sc,uint8_t stream_ind,uint32_t pib)12006b021cc2SWarner Losh hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib)
12016b021cc2SWarner Losh {
12026b021cc2SWarner Losh 	uint32_t off = hda_get_offset_stream(stream_ind);
12036b021cc2SWarner Losh 
12046b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, off + HDAC_SDLPIB, pib);
12056b021cc2SWarner Losh 	/* LPIB Alias */
12066b021cc2SWarner Losh 	hda_set_reg_by_offset(sc, 0x2000 + off + HDAC_SDLPIB, pib);
12076b021cc2SWarner Losh 	if (sc->dma_pib_vaddr)
120863898728SMark Johnston 		*(uint32_t *)((uint8_t *)sc->dma_pib_vaddr + stream_ind *
12096b021cc2SWarner Losh 		    HDA_DMA_PIB_ENTRY_LEN) = pib;
12106b021cc2SWarner Losh }
12116b021cc2SWarner Losh 
hda_get_clock_ns(void)12126b021cc2SWarner Losh static uint64_t hda_get_clock_ns(void)
12136b021cc2SWarner Losh {
12146b021cc2SWarner Losh 	struct timespec ts;
12156b021cc2SWarner Losh 	int err;
12166b021cc2SWarner Losh 
12176b021cc2SWarner Losh 	err = clock_gettime(CLOCK_MONOTONIC, &ts);
12186b021cc2SWarner Losh 	assert(!err);
12196b021cc2SWarner Losh 
12206b021cc2SWarner Losh 	return (ts.tv_sec * 1000000000LL + ts.tv_nsec);
12216b021cc2SWarner Losh }
12226b021cc2SWarner Losh 
12236b021cc2SWarner Losh /*
12246b021cc2SWarner Losh  * PCI HDA function definitions
12256b021cc2SWarner Losh  */
12266b021cc2SWarner Losh static int
pci_hda_init(struct pci_devinst * pi,nvlist_t * nvl)12276a284cacSJohn Baldwin pci_hda_init(struct pci_devinst *pi, nvlist_t *nvl)
12286b021cc2SWarner Losh {
12296b021cc2SWarner Losh 	struct hda_softc *sc = NULL;
12306b021cc2SWarner Losh 
12316b021cc2SWarner Losh 	assert(pi != NULL);
12326b021cc2SWarner Losh 
12336b021cc2SWarner Losh 	pci_set_cfgdata16(pi, PCIR_VENDOR, INTEL_VENDORID);
12346b021cc2SWarner Losh 	pci_set_cfgdata16(pi, PCIR_DEVICE, HDA_INTEL_82801G);
12356b021cc2SWarner Losh 
12366b021cc2SWarner Losh 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_MULTIMEDIA_HDA);
12376b021cc2SWarner Losh 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_MULTIMEDIA);
12386b021cc2SWarner Losh 
12396b021cc2SWarner Losh 	/* select the Intel HDA mode */
12406b021cc2SWarner Losh 	pci_set_cfgdata8(pi, PCIR_HDCTL, 0x01);
12416b021cc2SWarner Losh 
12426b021cc2SWarner Losh 	/* allocate one BAR register for the Memory address offsets */
12436b021cc2SWarner Losh 	pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, HDA_LAST_OFFSET);
12446b021cc2SWarner Losh 
12456b021cc2SWarner Losh 	/* allocate an IRQ pin for our slot */
12466b021cc2SWarner Losh 	pci_lintr_request(pi);
12476b021cc2SWarner Losh 
1248621b5090SJohn Baldwin 	sc = hda_init(nvl);
12496b021cc2SWarner Losh 	if (!sc)
12506b021cc2SWarner Losh 		return (-1);
12516b021cc2SWarner Losh 
12526b021cc2SWarner Losh 	sc->pci_dev = pi;
12536b021cc2SWarner Losh 	pi->pi_arg = sc;
12546b021cc2SWarner Losh 
12556b021cc2SWarner Losh 	return (0);
12566b021cc2SWarner Losh }
12576b021cc2SWarner Losh 
12586b021cc2SWarner Losh static void
pci_hda_write(struct pci_devinst * pi,int baridx,uint64_t offset,int size,uint64_t value)12596a284cacSJohn Baldwin pci_hda_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
126098d920d9SMark Johnston     uint64_t value)
12616b021cc2SWarner Losh {
12626b021cc2SWarner Losh 	struct hda_softc *sc = pi->pi_arg;
12636b021cc2SWarner Losh 	int err;
12646b021cc2SWarner Losh 
12656b021cc2SWarner Losh 	assert(sc);
12666b021cc2SWarner Losh 	assert(baridx == 0);
12676b021cc2SWarner Losh 	assert(size <= 4);
12686b021cc2SWarner Losh 
1269332eff95SVincenzo Maffione 	DPRINTF("offset: 0x%lx value: 0x%lx", offset, value);
12706b021cc2SWarner Losh 
12716b021cc2SWarner Losh 	err = hda_write(sc, offset, size, value);
12726b021cc2SWarner Losh 	assert(!err);
12736b021cc2SWarner Losh }
12746b021cc2SWarner Losh 
12756b021cc2SWarner Losh static uint64_t
pci_hda_read(struct pci_devinst * pi,int baridx,uint64_t offset,int size)12766a284cacSJohn Baldwin pci_hda_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
12776b021cc2SWarner Losh {
12786b021cc2SWarner Losh 	struct hda_softc *sc = pi->pi_arg;
12796b021cc2SWarner Losh 	uint64_t value = 0;
12806b021cc2SWarner Losh 
12816b021cc2SWarner Losh 	assert(sc);
12826b021cc2SWarner Losh 	assert(baridx == 0);
12836b021cc2SWarner Losh 	assert(size <= 4);
12846b021cc2SWarner Losh 
12856b021cc2SWarner Losh 	value = hda_read(sc, offset);
12866b021cc2SWarner Losh 
1287332eff95SVincenzo Maffione 	DPRINTF("offset: 0x%lx value: 0x%lx", offset, value);
12886b021cc2SWarner Losh 
12896b021cc2SWarner Losh 	return (value);
12906b021cc2SWarner Losh }
1291