184659b2Michael Zeller/*-
284659b2Michael Zeller * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
384659b2Michael Zeller *
484659b2Michael Zeller * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
584659b2Michael Zeller * All rights reserved.
684659b2Michael Zeller *
784659b2Michael Zeller * Redistribution and use in source and binary forms, with or without
884659b2Michael Zeller * modification, are permitted provided that the following conditions
984659b2Michael Zeller * are met:
1084659b2Michael Zeller * 1. Redistributions of source code must retain the above copyright
1184659b2Michael Zeller *    notice, this list of conditions and the following disclaimer.
1284659b2Michael Zeller * 2. Redistributions in binary form must reproduce the above copyright
1384659b2Michael Zeller *    notice, this list of conditions and the following disclaimer in the
1484659b2Michael Zeller *    documentation and/or other materials provided with the distribution.
1584659b2Michael Zeller *
1684659b2Michael Zeller * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
1784659b2Michael Zeller * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1884659b2Michael Zeller * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1984659b2Michael Zeller * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2084659b2Michael Zeller * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2184659b2Michael Zeller * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2284659b2Michael Zeller * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2384659b2Michael Zeller * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2484659b2Michael Zeller * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2584659b2Michael Zeller * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2684659b2Michael Zeller * SUCH DAMAGE.
2784659b2Michael Zeller *
2884659b2Michael Zeller */
2984659b2Michael Zeller
3084659b2Michael Zeller#include <sys/cdefs.h>
3184659b2Michael Zeller__FBSDID("$FreeBSD$");
3284659b2Michael Zeller
3384659b2Michael Zeller#include <time.h>
3484659b2Michael Zeller
3584659b2Michael Zeller#include "pci_hda.h"
3684659b2Michael Zeller#include "bhyverun.h"
3784659b2Michael Zeller#include "pci_emul.h"
3884659b2Michael Zeller#include "hdac_reg.h"
3984659b2Michael Zeller
4084659b2Michael Zeller/*
4184659b2Michael Zeller * HDA defines
4284659b2Michael Zeller */
4384659b2Michael Zeller#define PCIR_HDCTL		0x40
4484659b2Michael Zeller#define INTEL_VENDORID		0x8086
4584659b2Michael Zeller#define HDA_INTEL_82801G	0x27d8
4684659b2Michael Zeller
4784659b2Michael Zeller#define HDA_IOSS_NO		0x08
4884659b2Michael Zeller#define HDA_OSS_NO		0x04
4984659b2Michael Zeller#define HDA_ISS_NO		0x04
5084659b2Michael Zeller#define HDA_CODEC_MAX		0x0f
5184659b2Michael Zeller#define HDA_LAST_OFFSET						\
5284659b2Michael Zeller	(0x2084 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20))
5384659b2Michael Zeller#define HDA_SET_REG_TABLE_SZ					\
5484659b2Michael Zeller	(0x80 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20))
5584659b2Michael Zeller#define HDA_CORB_ENTRY_LEN	0x04
5684659b2Michael Zeller#define HDA_RIRB_ENTRY_LEN	0x08
5784659b2Michael Zeller#define HDA_BDL_ENTRY_LEN	0x10
5884659b2Michael Zeller#define HDA_DMA_PIB_ENTRY_LEN	0x08
5984659b2Michael Zeller#define HDA_STREAM_TAGS_CNT	0x10
6084659b2Michael Zeller#define HDA_STREAM_REGS_BASE	0x80
6184659b2Michael Zeller#define HDA_STREAM_REGS_LEN	0x20
6284659b2Michael Zeller
6384659b2Michael Zeller#define HDA_DMA_ACCESS_LEN	(sizeof(uint32_t))
6484659b2Michael Zeller#define HDA_BDL_MAX_LEN		0x0100
6584659b2Michael Zeller
6684659b2Michael Zeller#define HDAC_SDSTS_FIFORDY	(1 << 5)
6784659b2Michael Zeller
6884659b2Michael Zeller#define HDA_RIRBSTS_IRQ_MASK	(HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS)
6984659b2Michael Zeller#define HDA_STATESTS_IRQ_MASK	((1 << HDA_CODEC_MAX) - 1)
7084659b2Michael Zeller#define HDA_SDSTS_IRQ_MASK					\
7184659b2Michael Zeller	(HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS)
7284659b2Michael Zeller
7384659b2Michael Zeller/*
7484659b2Michael Zeller * HDA data structures
7584659b2Michael Zeller */
7684659b2Michael Zeller
7784659b2Michael Zellerstruct hda_softc;
7884659b2Michael Zeller
7984659b2Michael Zellertypedef void (*hda_set_reg_handler)(struct hda_softc *sc, uint32_t offset,
8084659b2Michael Zeller		uint32_t old);
8184659b2Michael Zeller
8284659b2Michael Zellerstruct hda_bdle {
8384659b2Michael Zeller	uint32_t addrl;
8484659b2Michael Zeller	uint32_t addrh;
8584659b2Michael Zeller	uint32_t len;
8684659b2Michael Zeller	uint32_t ioc;
8784659b2Michael Zeller} __packed;
8884659b2Michael Zeller
8984659b2Michael Zellerstruct hda_bdle_desc {
9084659b2Michael Zeller	void *addr;
9184659b2Michael Zeller	uint8_t ioc;
9284659b2Michael Zeller	uint32_t len;
9384659b2Michael Zeller};
9484659b2Michael Zeller
9584659b2Michael Zellerstruct hda_codec_cmd_ctl {
9684659b2Michael Zeller	char *name;
9784659b2Michael Zeller	void *dma_vaddr;
9884659b2Michael Zeller	uint8_t run;
9984659b2Michael Zeller	uint16_t rp;
10084659b2Michael Zeller	uint16_t size;
10184659b2Michael Zeller	uint16_t wp;
10284659b2Michael Zeller};
10384659b2Michael Zeller
10484659b2Michael Zellerstruct hda_stream_desc {
10584659b2Michael Zeller	uint8_t dir;
10684659b2Michael Zeller	uint8_t run;
10784659b2Michael Zeller	uint8_t stream;
10884659b2Michael Zeller
10984659b2Michael Zeller	/* bp is the no. of bytes transferred in the current bdle */
11084659b2Michael Zeller	uint32_t bp;
11184659b2Michael Zeller	/* be is the no. of bdles transferred in the bdl */
11284659b2Michael Zeller	uint32_t be;
11384659b2Michael Zeller
11484659b2Michael Zeller	uint32_t bdl_cnt;
11584659b2Michael Zeller	struct hda_bdle_desc bdl[HDA_BDL_MAX_LEN];
11684659b2Michael Zeller};
11784659b2Michael Zeller
11884659b2Michael Zellerstruct hda_softc {
11984659b2Michael Zeller	struct pci_devinst *pci_dev;
12084659b2Michael Zeller	uint32_t regs[HDA_LAST_OFFSET];
12184659b2Michael Zeller
12284659b2Michael Zeller	uint8_t lintr;
12384659b2Michael Zeller	uint8_t rirb_cnt;
12484659b2Michael Zeller	uint64_t wall_clock_start;
12584659b2Michael Zeller
12684659b2Michael Zeller	struct hda_codec_cmd_ctl corb;
12784659b2Michael Zeller	struct hda_codec_cmd_ctl rirb;
12884659b2Michael Zeller
12984659b2Michael Zeller	uint8_t codecs_no;
13084659b2Michael Zeller	struct hda_codec_inst *codecs[HDA_CODEC_MAX];
13184659b2Michael Zeller
13284659b2Michael Zeller	/* Base Address of the DMA Position Buffer */
13384659b2Michael Zeller	void *dma_pib_vaddr;
13484659b2Michael Zeller
13584659b2Michael Zeller	struct hda_stream_desc streams[HDA_IOSS_NO];
13684659b2Michael Zeller	/* 2 tables for output and input */
13784659b2Michael Zeller	uint8_t stream_map[2][HDA_STREAM_TAGS_CNT];
13884659b2Michael Zeller};
13984659b2Michael Zeller
14084659b2Michael Zeller/*
14184659b2Michael Zeller * HDA module function declarations
14284659b2Michael Zeller */
14384659b2Michael Zellerstatic inline void hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset,
14484659b2Michael Zeller    uint32_t value);
14584659b2Michael Zellerstatic inline uint32_t hda_get_reg_by_offset(struct hda_softc *sc,
14684659b2Michael Zeller    uint32_t offset);
14784659b2Michael Zellerstatic inline void hda_set_field_by_offset(struct hda_softc *sc,
14884659b2Michael Zeller    uint32_t offset, uint32_t mask, uint32_t value);
14984659b2Michael Zeller
15084659b2Michael Zellerstatic uint8_t hda_parse_config(const char *opts, const char *key, char *val);
15184659b2Michael Zellerstatic struct hda_softc *hda_init(const char *opts);
15284659b2Michael Zellerstatic void hda_update_intr(struct hda_softc *sc);
15384659b2Michael Zellerstatic void hda_response_interrupt(struct hda_softc *sc);
15484659b2Michael Zellerstatic int hda_codec_constructor(struct hda_softc *sc,
15584659b2Michael Zeller    struct hda_codec_class *codec, const char *play, const char *rec,
15684659b2Michael Zeller    const char *opts);
15784659b2Michael Zellerstatic struct hda_codec_class *hda_find_codec_class(const char *name);
15884659b2Michael Zeller
15984659b2Michael Zellerstatic int hda_send_command(struct hda_softc *sc, uint32_t verb);
16084659b2Michael Zellerstatic int hda_notify_codecs(struct hda_softc *sc, uint8_t run,
16184659b2Michael Zeller    uint8_t stream, uint8_t dir);
16284659b2Michael Zellerstatic void hda_reset(struct hda_softc *sc);
16384659b2Michael Zellerstatic void hda_reset_regs(struct hda_softc *sc);
16484659b2Michael Zellerstatic void hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind);
16584659b2Michael Zellerstatic int hda_stream_start(struct hda_softc *sc, uint8_t stream_ind);
16684659b2Michael Zellerstatic int hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind);
16784659b2Michael Zellerstatic uint32_t hda_read(struct hda_softc *sc, uint32_t offset);
16884659b2Michael Zellerstatic int hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size,
16984659b2Michael Zeller    uint32_t value);
17084659b2Michael Zeller
17184659b2Michael Zellerstatic inline void hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p);
17284659b2Michael Zellerstatic int hda_corb_start(struct hda_softc *sc);
17384659b2Michael Zellerstatic int hda_corb_run(struct hda_softc *sc);
17484659b2Michael Zellerstatic int hda_rirb_start(struct hda_softc *sc);
17584659b2Michael Zeller
17684659b2Michael Zellerstatic void *hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr,
17784659b2Michael Zeller    size_t len);
17884659b2Michael Zellerstatic void hda_dma_st_dword(void *dma_vaddr, uint32_t data);
17984659b2Michael Zellerstatic uint32_t hda_dma_ld_dword(void *dma_vaddr);
18084659b2Michael Zeller
18184659b2Michael Zellerstatic inline uint8_t hda_get_stream_by_offsets(uint32_t offset,
18284659b2Michael Zeller    uint8_t reg_offset);
18384659b2Michael Zellerstatic inline uint32_t hda_get_offset_stream(uint8_t stream_ind);
18484659b2Michael Zeller
18584659b2Michael Zellerstatic void hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
18684659b2Michael Zellerstatic void hda_set_statests(struct hda_softc *sc, uint32_t offset,
18784659b2Michael Zeller    uint32_t old);
18884659b2Michael Zellerstatic void hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old);
18984659b2Michael Zellerstatic void hda_set_corbctl(struct hda_softc *sc, uint32_t offset,
19084659b2Michael Zeller    uint32_t old);
19184659b2Michael Zellerstatic void hda_set_rirbctl(struct hda_softc *sc, uint32_t offset,
19284659b2Michael Zeller    uint32_t old);
19384659b2Michael Zellerstatic void hda_set_rirbsts(struct hda_softc *sc, uint32_t offset,
19484659b2Michael Zeller    uint32_t old);
19584659b2Michael Zellerstatic void hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset,
19684659b2Michael Zeller    uint32_t old);
19784659b2Michael Zellerstatic void hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
19884659b2Michael Zellerstatic void hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old);
19984659b2Michael Zellerstatic void hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old);
20084659b2Michael Zeller
20184659b2Michael Zellerstatic int hda_signal_state_change(struct hda_codec_inst *hci);
20284659b2Michael Zellerstatic int hda_response(struct hda_codec_inst *hci, uint32_t response,
20384659b2Michael Zeller    uint8_t unsol);
20484659b2Michael Zellerstatic int hda_transfer(struct hda_codec_inst *hci, uint8_t stream,
20584659b2Michael Zeller    uint8_t dir, void *buf, size_t count);
20684659b2Michael Zeller
20784659b2Michael Zellerstatic void hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib);
20884659b2Michael Zellerstatic uint64_t hda_get_clock_ns(void);
20984659b2Michael Zeller
21084659b2Michael Zeller/*
21184659b2Michael Zeller * PCI HDA function declarations
21284659b2Michael Zeller */
21384659b2Michael Zellerstatic int pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts);
21484659b2Michael Zellerstatic void pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
21584659b2Michael Zeller    int baridx, uint64_t offset, int size, uint64_t value);
21684659b2Michael Zellerstatic uint64_t pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi,
21784659b2Michael Zeller    int baridx, uint64_t offset, int size);
21884659b2Michael Zeller/*
21984659b2Michael Zeller * HDA global data
22084659b2Michael Zeller */
22184659b2Michael Zeller
22284659b2Michael Zellerstatic const hda_set_reg_handler hda_set_reg_table[] = {
22384659b2Michael Zeller	[HDAC_GCTL] = hda_set_gctl,
22484659b2Michael Zeller	[HDAC_STATESTS] = hda_set_statests,
22584659b2Michael Zeller	[HDAC_CORBWP] = hda_set_corbwp,
22684659b2Michael Zeller	[HDAC_CORBCTL] = hda_set_corbctl,
22784659b2Michael Zeller	[HDAC_RIRBCTL] = hda_set_rirbctl,
22884659b2Michael Zeller	[HDAC_RIRBSTS] = hda_set_rirbsts,
22984659b2Michael Zeller	[HDAC_DPIBLBASE] = hda_set_dpiblbase,
23084659b2Michael Zeller
23184659b2Michael Zeller#define HDAC_ISTREAM(n, iss, oss)				\
23284659b2Michael Zeller	[_HDAC_ISDCTL(n, iss, oss)] = hda_set_sdctl,		\
23384659b2Michael Zeller	[_HDAC_ISDCTL(n, iss, oss) + 2] = hda_set_sdctl2,	\
23484659b2Michael Zeller	[_HDAC_ISDSTS(n, iss, oss)] = hda_set_sdsts,		\
23584659b2Michael Zeller
23684659b2Michael Zeller#define HDAC_OSTREAM(n, iss, oss)				\
23784659b2Michael Zeller	[_HDAC_OSDCTL(n, iss, oss)] = hda_set_sdctl,		\
23884659b2Michael Zeller	[_HDAC_OSDCTL(n, iss, oss) + 2] = hda_set_sdctl2,	\
23984659b2Michael Zeller	[_HDAC_OSDSTS(n, iss, oss)] = hda_set_sdsts,		\
24084659b2Michael Zeller
24184659b2Michael Zeller	HDAC_ISTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
24284659b2Michael Zeller	HDAC_ISTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
24384659b2Michael Zeller	HDAC_ISTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
24484659b2Michael Zeller	HDAC_ISTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
24584659b2Michael Zeller
24684659b2Michael Zeller	HDAC_OSTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
24784659b2Michael Zeller	HDAC_OSTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
24884659b2Michael Zeller	HDAC_OSTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
24984659b2Michael Zeller	HDAC_OSTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
25084659b2Michael Zeller
25184659b2Michael Zeller	[HDA_SET_REG_TABLE_SZ] = NULL,
25284659b2Michael Zeller};
25384659b2Michael Zeller
25484659b2Michael Zellerstatic const uint16_t hda_corb_sizes[] = {
25584659b2Michael Zeller	[HDAC_CORBSIZE_CORBSIZE_2]	= 2,
25684659b2Michael Zeller	[HDAC_CORBSIZE_CORBSIZE_16]	= 16,
25784659b2Michael Zeller	[HDAC_CORBSIZE_CORBSIZE_256]	= 256,
25884659b2Michael Zeller	[HDAC_CORBSIZE_CORBSIZE_MASK]	= 0,
25984659b2Michael Zeller};
26084659b2Michael Zeller
26184659b2Michael Zellerstatic const uint16_t hda_rirb_sizes[] = {
26284659b2Michael Zeller	[HDAC_RIRBSIZE_RIRBSIZE_2]	= 2,
26384659b2Michael Zeller	[HDAC_RIRBSIZE_RIRBSIZE_16]	= 16,
26484659b2Michael Zeller	[HDAC_RIRBSIZE_RIRBSIZE_256]	= 256,
26584659b2Michael Zeller	[HDAC_RIRBSIZE_RIRBSIZE_MASK]	= 0,
26684659b2Michael Zeller};
26784659b2Michael Zeller
26884659b2Michael Zellerstatic struct hda_ops hops = {
26984659b2Michael Zeller	.signal		= hda_signal_state_change,
27084659b2Michael Zeller	.response	= hda_response,
27184659b2Michael Zeller	.transfer	= hda_transfer,
27284659b2Michael Zeller};
27384659b2Michael Zeller
27484659b2Michael Zellerstruct pci_devemu pci_de_hda = {
27584659b2Michael Zeller	.pe_emu		= "hda",
27684659b2Michael Zeller	.pe_init	= pci_hda_init,
27784659b2Michael Zeller	.pe_barwrite	= pci_hda_write,
27884659b2Michael Zeller	.pe_barread	= pci_hda_read
27984659b2Michael Zeller};
28084659b2Michael Zeller
28184659b2Michael ZellerPCI_EMUL_SET(pci_de_hda);
28284659b2Michael Zeller
28384659b2Michael ZellerSET_DECLARE(hda_codec_class_set, struct hda_codec_class);
28484659b2Michael Zeller
28584659b2Michael Zeller#if DEBUG_HDA == 1
28684659b2Michael ZellerFILE *dbg;
28784659b2Michael Zeller#endif
28884659b2Michael Zeller
28984659b2Michael Zeller/*
29084659b2Michael Zeller * HDA module function definitions
29184659b2Michael Zeller */
29284659b2Michael Zeller
29384659b2Michael Zellerstatic inline void
29484659b2Michael Zellerhda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t value)
29584659b2Michael Zeller{
29684659b2Michael Zeller	assert(offset < HDA_LAST_OFFSET);
29784659b2Michael Zeller	sc->regs[offset] = value;
29884659b2Michael Zeller}
29984659b2Michael Zeller
30084659b2Michael Zellerstatic inline uint32_t
30184659b2Michael Zellerhda_get_reg_by_offset(struct hda_softc *sc, uint32_t offset)
30284659b2Michael Zeller{
30384659b2Michael Zeller	assert(offset < HDA_LAST_OFFSET);
30484659b2Michael Zeller	return sc->regs[offset];
30584659b2Michael Zeller}
30684659b2Michael Zeller
30784659b2Michael Zellerstatic inline void
30884659b2Michael Zellerhda_set_field_by_offset(struct hda_softc *sc, uint32_t offset,
30984659b2Michael Zeller    uint32_t mask, uint32_t value)
31084659b2Michael Zeller{
31184659b2Michael Zeller	uint32_t reg_value = 0;
31284659b2Michael Zeller
31384659b2Michael Zeller	reg_value = hda_get_reg_by_offset(sc, offset);
31484659b2Michael Zeller
31584659b2Michael Zeller	reg_value &= ~mask;
31684659b2Michael Zeller	reg_value |= (value & mask);
31784659b2Michael Zeller
31884659b2Michael Zeller	hda_set_reg_by_offset(sc, offset, reg_value);
31984659b2Michael Zeller}
32084659b2Michael Zeller
32184659b2Michael Zellerstatic uint8_t
32284659b2Michael Zellerhda_parse_config(const char *opts, const char *key, char *val)
32384659b2Michael Zeller{
32484659b2Michael Zeller	char buf[64];
32584659b2Michael Zeller	char *s = buf;
32684659b2Michael Zeller	char *tmp = NULL;
32784659b2Michael Zeller	size_t len;
32884659b2Michael Zeller	int i;
32984659b2Michael Zeller
33084659b2Michael Zeller	if (!opts)
33184659b2Michael Zeller		return (0);
33284659b2Michael Zeller
33384659b2Michael Zeller	len = strlen(opts);
33484659b2Michael Zeller	if (len >= sizeof(buf)) {
335154972aPatrick Mooney		DPRINTF("Opts too big");
33684659b2Michael Zeller		return (0);
33784659b2Michael Zeller	}
33884659b2Michael Zeller
339154972aPatrick Mooney	DPRINTF("opts: %s", opts);
34084659b2Michael Zeller
34184659b2Michael Zeller	strcpy(buf, opts);
34284659b2Michael Zeller
34384659b2Michael Zeller	for (i = 0; i < len; i++)
34484659b2Michael Zeller		if (buf[i] == ',') {
34584659b2Michael Zeller			buf[i] = 0;
34684659b2Michael Zeller			tmp = buf + i + 1;
34784659b2Michael Zeller			break;
34884659b2Michael Zeller		}
34984659b2Michael Zeller
35084659b2Michael Zeller	if (!memcmp(s, key, strlen(key))) {
35184659b2Michael Zeller		strncpy(val, s + strlen(key), 64);
35284659b2Michael Zeller		return (1);
35384659b2Michael Zeller	}
35484659b2Michael Zeller
35584659b2Michael Zeller	if (!tmp)
35684659b2Michael Zeller		return (0);
35784659b2Michael Zeller
35884659b2Michael Zeller	s = tmp;
35984659b2Michael Zeller	if (!memcmp(s, key, strlen(key))) {
36084659b2Michael Zeller		strncpy(val, s + strlen(key), 64);
36184659b2Michael Zeller		return (1);
36284659b2Michael Zeller	}
36384659b2Michael Zeller
36484659b2Michael Zeller	return (0);
36584659b2Michael Zeller}
36684659b2Michael Zeller
36784659b2Michael Zellerstatic struct hda_softc *
36884659b2Michael Zellerhda_init(const char *opts)
36984659b2Michael Zeller{
37084659b2Michael Zeller	struct hda_softc *sc = NULL;
37184659b2Michael Zeller	struct hda_codec_class *codec = NULL;
37284659b2Michael Zeller	char play[64];
37384659b2Michael Zeller	char rec[64];
37484659b2Michael Zeller	int err, p, r;
37584659b2Michael Zeller
37684659b2Michael Zeller#if DEBUG_HDA == 1
37784659b2Michael Zeller	dbg = fopen("/tmp/bhyve_hda.log", "w+");
37884659b2Michael Zeller#endif
37984659b2Michael Zeller
380154972aPatrick Mooney	DPRINTF("opts: %s", opts);
38184659b2Michael Zeller
38284659b2Michael Zeller	sc = calloc(1, sizeof(*sc));
38384659b2Michael Zeller	if (!sc)
38484659b2Michael Zeller		return (NULL);
38584659b2Michael Zeller
38684659b2Michael Zeller	hda_reset_regs(sc);
38784659b2Michael Zeller
38884659b2Michael Zeller	/*
38984659b2Michael Zeller	 * TODO search all the codecs declared in opts
39084659b2Michael Zeller	 * For now we play with one single codec
39184659b2Michael Zeller	 */
39284659b2Michael Zeller	codec = hda_find_codec_class("hda_codec");
39384659b2Michael Zeller	if (codec) {
39484659b2Michael Zeller		p = hda_parse_config(opts, "play=", play);
39584659b2Michael Zeller		r = hda_parse_config(opts, "rec=", rec);
396154972aPatrick Mooney		DPRINTF("play: %s rec: %s", play, rec);
39784659b2Michael Zeller		if (p | r) {
39884659b2Michael Zeller			err = hda_codec_constructor(sc, codec, p ?	\
39984659b2Michael Zeller				play : NULL, r ? rec : NULL, NULL);
40084659b2Michael Zeller			assert(!err);
40184659b2Michael Zeller		}
40284659b2Michael Zeller	}
40384659b2Michael Zeller
40484659b2Michael Zeller	return (sc);
40584659b2Michael Zeller}
40684659b2Michael Zeller
40784659b2Michael Zellerstatic void
40884659b2Michael Zellerhda_update_intr(struct hda_softc *sc)
40984659b2Michael Zeller{
41084659b2Michael Zeller	struct pci_devinst *pi = sc->pci_dev;
41184659b2Michael Zeller	uint32_t intctl = hda_get_reg_by_offset(sc, HDAC_INTCTL);
41284659b2Michael Zeller	uint32_t intsts = 0;
41384659b2Michael Zeller	uint32_t sdsts = 0;
41484659b2Michael Zeller	uint32_t rirbsts = 0;
41584659b2Michael Zeller	uint32_t wakeen = 0;
41684659b2Michael Zeller	uint32_t statests = 0;
41784659b2Michael Zeller	uint32_t off = 0;
41884659b2Michael Zeller	int i;
41984659b2Michael Zeller
42084659b2Michael Zeller	/* update the CIS bits */
42184659b2Michael Zeller	rirbsts = hda_get_reg_by_offset(sc, HDAC_RIRBSTS);
42284659b2Michael Zeller	if (rirbsts & (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS))
42384659b2Michael Zeller		intsts |= HDAC_INTSTS_CIS;
42484659b2Michael Zeller
42584659b2Michael Zeller	wakeen = hda_get_reg_by_offset(sc, HDAC_WAKEEN);
42684659b2Michael Zeller	statests = hda_get_reg_by_offset(sc, HDAC_STATESTS);
42784659b2Michael Zeller	if (statests & wakeen)
42884659b2Michael Zeller		intsts |= HDAC_INTSTS_CIS;
42984659b2Michael Zeller
43084659b2Michael Zeller	/* update the SIS bits */
43184659b2Michael Zeller	for (i = 0; i < HDA_IOSS_NO; i++) {
43284659b2Michael Zeller		off = hda_get_offset_stream(i);
43384659b2Michael Zeller		sdsts = hda_get_reg_by_offset(sc, off + HDAC_SDSTS);
43484659b2Michael Zeller		if (sdsts & HDAC_SDSTS_BCIS)
43584659b2Michael Zeller			intsts |= (1 << i);
43684659b2Michael Zeller	}
43784659b2Michael Zeller
43884659b2Michael Zeller	/* update the GIS bit */
43984659b2Michael Zeller	if (intsts)
44084659b2Michael Zeller		intsts |= HDAC_INTSTS_GIS;
44184659b2Michael Zeller
44284659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_INTSTS, intsts);
44384659b2Michael Zeller
44484659b2Michael Zeller	if ((intctl & HDAC_INTCTL_GIE) && ((intsts &			\
44584659b2Michael Zeller		~HDAC_INTSTS_GIS) & intctl)) {
44684659b2Michael Zeller		if (!sc->lintr) {
44784659b2Michael Zeller			pci_lintr_assert(pi);
44884659b2Michael Zeller			sc->lintr = 1;
44984659b2Michael Zeller		}
45084659b2Michael Zeller	} else {
45184659b2Michael Zeller		if (sc->lintr) {
45284659b2Michael Zeller			pci_lintr_deassert(pi);
45384659b2Michael Zeller			sc->lintr = 0;
45484659b2Michael Zeller		}
45584659b2Michael Zeller	}
45684659b2Michael Zeller}
45784659b2Michael Zeller
45884659b2Michael Zellerstatic void
45984659b2Michael Zellerhda_response_interrupt(struct hda_softc *sc)
46084659b2Michael Zeller{
46184659b2Michael Zeller	uint8_t rirbctl = hda_get_reg_by_offset(sc, HDAC_RIRBCTL);
46284659b2Michael Zeller
46384659b2Michael Zeller	if ((rirbctl & HDAC_RIRBCTL_RINTCTL) && sc->rirb_cnt) {
46484659b2Michael Zeller		sc->rirb_cnt = 0;
46584659b2Michael Zeller		hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL,
46684659b2Michael Zeller				HDAC_RIRBSTS_RINTFL);
46784659b2Michael Zeller		hda_update_intr(sc);
46884659b2Michael Zeller	}
46984659b2Michael Zeller}
47084659b2Michael Zeller
47184659b2Michael Zellerstatic int
47284659b2Michael Zellerhda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec,
47384659b2Michael Zeller    const char *play, const char *rec, const char *opts)
47484659b2Michael Zeller{
47584659b2Michael Zeller	struct hda_codec_inst *hci = NULL;
47684659b2Michael Zeller
47784659b2Michael Zeller	if (sc->codecs_no >= HDA_CODEC_MAX)
47884659b2Michael Zeller		return (-1);
47984659b2Michael Zeller
48084659b2Michael Zeller	hci = calloc(1, sizeof(struct hda_codec_inst));
48184659b2Michael Zeller	if (!hci)
48284659b2Michael Zeller		return (-1);
48384659b2Michael Zeller
48484659b2Michael Zeller	hci->hda = sc;
48584659b2Michael Zeller	hci->hops = &hops;
48684659b2Michael Zeller	hci->cad = sc->codecs_no;
48784659b2Michael Zeller	hci->codec = codec;
48884659b2Michael Zeller
48984659b2Michael Zeller	sc->codecs[sc->codecs_no++] = hci;
49084659b2Michael Zeller
49184659b2Michael Zeller	if (!codec->init) {
492154972aPatrick Mooney		DPRINTF("This codec does not implement the init function");
49384659b2Michael Zeller		return (-1);
49484659b2Michael Zeller	}
49584659b2Michael Zeller
49684659b2Michael Zeller	return (codec->init(hci, play, rec, opts));
49784659b2Michael Zeller}
49884659b2Michael Zeller
49984659b2Michael Zellerstatic struct hda_codec_class *
50084659b2Michael Zellerhda_find_codec_class(const char *name)
50184659b2Michael Zeller{
50284659b2Michael Zeller	struct hda_codec_class **pdpp = NULL, *pdp = NULL;
50384659b2Michael Zeller
50484659b2Michael Zeller	SET_FOREACH(pdpp, hda_codec_class_set) {
50584659b2Michael Zeller		pdp = *pdpp;
50684659b2Michael Zeller		if (!strcmp(pdp->name, name)) {
50784659b2Michael Zeller			return (pdp);
50884659b2Michael Zeller		}
50984659b2Michael Zeller	}
51084659b2Michael Zeller
51184659b2Michael Zeller	return (NULL);
51284659b2Michael Zeller}
51384659b2Michael Zeller
51484659b2Michael Zellerstatic int
51584659b2Michael Zellerhda_send_command(struct hda_softc *sc, uint32_t verb)
51684659b2Michael Zeller{
51784659b2Michael Zeller	struct hda_codec_inst *hci = NULL;
51884659b2Michael Zeller	struct hda_codec_class *codec = NULL;
51984659b2Michael Zeller	uint8_t cad = (verb >> HDA_CMD_CAD_SHIFT) & 0x0f;
52084659b2Michael Zeller
52184659b2Michael Zeller	hci = sc->codecs[cad];
52284659b2Michael Zeller	if (!hci)
52384659b2Michael Zeller		return (-1);
52484659b2Michael Zeller
525154972aPatrick Mooney	DPRINTF("cad: 0x%x verb: 0x%x", cad, verb);
52684659b2Michael Zeller
52784659b2Michael Zeller	codec = hci->codec;
52884659b2Michael Zeller	assert(codec);
52984659b2Michael Zeller
53084659b2Michael Zeller	if (!codec->command) {
531154972aPatrick Mooney		DPRINTF("This codec does not implement the command function");
53284659b2Michael Zeller		return (-1);
53384659b2Michael Zeller	}
53484659b2Michael Zeller
53584659b2Michael Zeller	return (codec->command(hci, verb));
53684659b2Michael Zeller}
53784659b2Michael Zeller
53884659b2Michael Zellerstatic int
53984659b2Michael Zellerhda_notify_codecs(struct hda_softc *sc, uint8_t run, uint8_t stream,
54084659b2Michael Zeller    uint8_t dir)
54184659b2Michael Zeller{
54284659b2Michael Zeller	struct hda_codec_inst *hci = NULL;
54384659b2Michael Zeller	struct hda_codec_class *codec = NULL;
54484659b2Michael Zeller	int err;
54584659b2Michael Zeller	int i;
54684659b2Michael Zeller
54784659b2Michael Zeller	/* Notify each codec */
54884659b2Michael Zeller	for (i = 0; i < sc->codecs_no; i++) {
54984659b2Michael Zeller		hci = sc->codecs[i];
55084659b2Michael Zeller		assert(hci);
55184659b2Michael Zeller
55284659b2Michael Zeller		codec = hci->codec;
55384659b2Michael Zeller		assert(codec);
55484659b2Michael Zeller
55584659b2Michael Zeller		if (codec->notify) {
55684659b2Michael Zeller			err = codec->notify(hci, run, stream, dir);
55784659b2Michael Zeller			if (!err)
55884659b2Michael Zeller				break;
55984659b2Michael Zeller		}
56084659b2Michael Zeller	}
56184659b2Michael Zeller
56284659b2Michael Zeller	return (i == sc->codecs_no ? (-1) : 0);
56384659b2Michael Zeller}
56484659b2Michael Zeller
56584659b2Michael Zellerstatic void
56684659b2Michael Zellerhda_reset(struct hda_softc *sc)
56784659b2Michael Zeller{
56884659b2Michael Zeller	int i;
56984659b2Michael Zeller	struct hda_codec_inst *hci = NULL;
57084659b2Michael Zeller	struct hda_codec_class *codec = NULL;
57184659b2Michael Zeller
57284659b2Michael Zeller	hda_reset_regs(sc);
57384659b2Michael Zeller
57484659b2Michael Zeller	/* Reset each codec */
57584659b2Michael Zeller	for (i = 0; i < sc->codecs_no; i++) {
57684659b2Michael Zeller		hci = sc->codecs[i];
57784659b2Michael Zeller		assert(hci);
57884659b2Michael Zeller
57984659b2Michael Zeller		codec = hci->codec;
58084659b2Michael Zeller		assert(codec);
58184659b2Michael Zeller
58284659b2Michael Zeller		if (codec->reset)
58384659b2Michael Zeller			codec->reset(hci);
58484659b2Michael Zeller	}
58584659b2Michael Zeller
58684659b2Michael Zeller	sc->wall_clock_start = hda_get_clock_ns();
58784659b2Michael Zeller}
58884659b2Michael Zeller
58984659b2Michael Zellerstatic void
59084659b2Michael Zellerhda_reset_regs(struct hda_softc *sc)
59184659b2Michael Zeller{
59284659b2Michael Zeller	uint32_t off = 0;
59384659b2Michael Zeller	uint8_t i;
59484659b2Michael Zeller
595154972aPatrick Mooney	DPRINTF("Reset the HDA controller registers ...");
59684659b2Michael Zeller
59784659b2Michael Zeller	memset(sc->regs, 0, sizeof(sc->regs));
59884659b2Michael Zeller
59984659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_GCAP,
60084659b2Michael Zeller			HDAC_GCAP_64OK |
60184659b2Michael Zeller			(HDA_ISS_NO << HDAC_GCAP_ISS_SHIFT) |
60284659b2Michael Zeller			(HDA_OSS_NO << HDAC_GCAP_OSS_SHIFT));
60384659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_VMAJ, 0x01);
60484659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_OUTPAY, 0x3c);
60584659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_INPAY, 0x1d);
60684659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_CORBSIZE,
60784659b2Michael Zeller	    HDAC_CORBSIZE_CORBSZCAP_256 | HDAC_CORBSIZE_CORBSIZE_256);
60884659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_RIRBSIZE,
60984659b2Michael Zeller	    HDAC_RIRBSIZE_RIRBSZCAP_256 | HDAC_RIRBSIZE_RIRBSIZE_256);
61084659b2Michael Zeller
61184659b2Michael Zeller	for (i = 0; i < HDA_IOSS_NO; i++) {
61284659b2Michael Zeller		off = hda_get_offset_stream(i);
61384659b2Michael Zeller		hda_set_reg_by_offset(sc, off + HDAC_SDFIFOS, HDA_FIFO_SIZE);
61484659b2Michael Zeller	}
61584659b2Michael Zeller}
61684659b2Michael Zeller
61784659b2Michael Zellerstatic void
61884659b2Michael Zellerhda_stream_reset(struct hda_softc *sc, uint8_t stream_ind)
61984659b2Michael Zeller{
62084659b2Michael Zeller	struct hda_stream_desc *st = &sc->streams[stream_ind];
62184659b2Michael Zeller	uint32_t off = hda_get_offset_stream(stream_ind);
62284659b2Michael Zeller
623154972aPatrick Mooney	DPRINTF("Reset the HDA stream: 0x%x", stream_ind);
62484659b2Michael Zeller
62584659b2Michael Zeller	/* Reset the Stream Descriptor registers */
62684659b2Michael Zeller	memset(sc->regs + HDA_STREAM_REGS_BASE + off, 0, HDA_STREAM_REGS_LEN);
62784659b2Michael Zeller
62884659b2Michael Zeller	/* Reset the Stream Descriptor */
62984659b2Michael Zeller	memset(st, 0, sizeof(*st));
63084659b2Michael Zeller
63184659b2Michael Zeller	hda_set_field_by_offset(sc, off + HDAC_SDSTS,
63284659b2Michael Zeller	    HDAC_SDSTS_FIFORDY, HDAC_SDSTS_FIFORDY);
63384659b2Michael Zeller	hda_set_field_by_offset(sc, off + HDAC_SDCTL0,
63484659b2Michael Zeller	    HDAC_SDCTL_SRST, HDAC_SDCTL_SRST);
63584659b2Michael Zeller}
63684659b2Michael Zeller
63784659b2Michael Zellerstatic int
63884659b2Michael Zellerhda_stream_start(struct hda_softc *sc, uint8_t stream_ind)
63984659b2Michael Zeller{
64084659b2Michael Zeller	struct hda_stream_desc *st = &sc->streams[stream_ind];
64184659b2Michael Zeller	struct hda_bdle_desc *bdle_desc = NULL;
64284659b2Michael Zeller	struct hda_bdle *bdle = NULL;
64384659b2Michael Zeller	uint32_t lvi = 0;
64484659b2Michael Zeller	uint32_t bdl_cnt = 0;
64584659b2Michael Zeller	uint64_t bdpl = 0;
64684659b2Michael Zeller	uint64_t bdpu = 0;
64784659b2Michael Zeller	uint64_t bdl_paddr = 0;
64884659b2Michael Zeller	void *bdl_vaddr = NULL;
64984659b2Michael Zeller	uint32_t bdle_sz = 0;
65084659b2Michael Zeller	uint64_t bdle_addrl = 0;
65184659b2Michael Zeller	uint64_t bdle_addrh = 0;
65284659b2Michael Zeller	uint64_t bdle_paddr = 0;
65384659b2Michael Zeller	void *bdle_vaddr = NULL;
65484659b2Michael Zeller	uint32_t off = hda_get_offset_stream(stream_ind);
65584659b2Michael Zeller	uint32_t sdctl = 0;
65684659b2Michael Zeller	uint8_t strm = 0;
65784659b2Michael Zeller	uint8_t dir = 0;
65884659b2Michael Zeller	int i;
65984659b2Michael Zeller
66084659b2Michael Zeller	assert(!st->run);
66184659b2Michael Zeller
66284659b2Michael Zeller	lvi = hda_get_reg_by_offset(sc, off + HDAC_SDLVI);
66384659b2Michael Zeller	bdpl = hda_get_reg_by_offset(sc, off + HDAC_SDBDPL);
66484659b2Michael Zeller	bdpu = hda_get_reg_by_offset(sc, off + HDAC_SDBDPU);
66584659b2Michael Zeller
66684659b2Michael Zeller	bdl_cnt = lvi + 1;
66784659b2Michael Zeller	assert(bdl_cnt <= HDA_BDL_MAX_LEN);
66884659b2Michael Zeller
66984659b2Michael Zeller	bdl_paddr = bdpl | (bdpu << 32);
67084659b2Michael Zeller	bdl_vaddr = hda_dma_get_vaddr(sc, bdl_paddr,
67184659b2Michael Zeller	    HDA_BDL_ENTRY_LEN * bdl_cnt);
67284659b2Michael Zeller	if (!bdl_vaddr) {
673154972aPatrick Mooney		DPRINTF("Fail to get the guest virtual address");
67484659b2Michael Zeller		return (-1);
67584659b2Michael Zeller	}
67684659b2Michael Zeller
677154972aPatrick Mooney	DPRINTF("stream: 0x%x bdl_cnt: 0x%x bdl_paddr: 0x%lx",
67884659b2Michael Zeller	    stream_ind, bdl_cnt, bdl_paddr);
67984659b2Michael Zeller
68084659b2Michael Zeller	st->bdl_cnt = bdl_cnt;
68184659b2Michael Zeller
68284659b2Michael Zeller	bdle = (struct hda_bdle *)bdl_vaddr;
68384659b2Michael Zeller	for (i = 0; i < bdl_cnt; i++, bdle++) {
68484659b2Michael Zeller		bdle_sz = bdle->len;
68584659b2Michael Zeller		assert(!(bdle_sz % HDA_DMA_ACCESS_LEN));
68684659b2Michael Zeller
68784659b2Michael Zeller		bdle_addrl = bdle->addrl;
68884659b2Michael Zeller		bdle_addrh = bdle->addrh;
68984659b2Michael Zeller
69084659b2Michael Zeller		bdle_paddr = bdle_addrl | (bdle_addrh << 32);
69184659b2Michael Zeller		bdle_vaddr = hda_dma_get_vaddr(sc, bdle_paddr, bdle_sz);
69284659b2Michael Zeller		if (!bdle_vaddr) {
693154972aPatrick Mooney			DPRINTF("Fail to get the guest virtual address");
69484659b2Michael Zeller			return (-1);
69584659b2Michael Zeller		}
69684659b2Michael Zeller
69784659b2Michael Zeller		bdle_desc = &st->bdl[i];
69884659b2Michael Zeller		bdle_desc->addr = bdle_vaddr;
69984659b2Michael Zeller		bdle_desc->len = bdle_sz;
70084659b2Michael Zeller		bdle_desc->ioc = bdle->ioc;
70184659b2Michael Zeller
702154972aPatrick Mooney		DPRINTF("bdle: 0x%x bdle_sz: 0x%x", i, bdle_sz);
70384659b2Michael Zeller	}
70484659b2Michael Zeller
70584659b2Michael Zeller	sdctl = hda_get_reg_by_offset(sc, off + HDAC_SDCTL0);
70684659b2Michael Zeller	strm = (sdctl >> 20) & 0x0f;
70784659b2Michael Zeller	dir = stream_ind >= HDA_ISS_NO;
70884659b2Michael Zeller
709154972aPatrick Mooney	DPRINTF("strm: 0x%x, dir: 0x%x", strm, dir);
71084659b2Michael Zeller
71184659b2Michael Zeller	sc->stream_map[dir][strm] = stream_ind;
71284659b2Michael Zeller	st->stream = strm;
71384659b2Michael Zeller	st->dir = dir;
71484659b2Michael Zeller	st->bp = 0;
71584659b2Michael Zeller	st->be = 0;
71684659b2Michael Zeller
71784659b2Michael Zeller	hda_set_pib(sc, stream_ind, 0);
71884659b2Michael Zeller
71984659b2Michael Zeller	st->run = 1;
72084659b2Michael Zeller
72184659b2Michael Zeller	hda_notify_codecs(sc, 1, strm, dir);
72284659b2Michael Zeller
72384659b2Michael Zeller	return (0);
72484659b2Michael Zeller}
72584659b2Michael Zeller
72684659b2Michael Zellerstatic int
72784659b2Michael Zellerhda_stream_stop(struct hda_softc *sc, uint8_t stream_ind)
72884659b2Michael Zeller{
72984659b2Michael Zeller	struct hda_stream_desc *st = &sc->streams[stream_ind];
73084659b2Michael Zeller	uint8_t strm = st->stream;
73184659b2Michael Zeller	uint8_t dir = st->dir;
73284659b2Michael Zeller
733154972aPatrick Mooney	DPRINTF("stream: 0x%x, strm: 0x%x, dir: 0x%x", stream_ind, strm, dir);
73484659b2Michael Zeller
73584659b2Michael Zeller	st->run = 0;
73684659b2Michael Zeller
73784659b2Michael Zeller	hda_notify_codecs(sc, 0, strm, dir);
73884659b2Michael Zeller
73984659b2Michael Zeller	return (0);
74084659b2Michael Zeller}
74184659b2Michael Zeller
74284659b2Michael Zellerstatic uint32_t
74384659b2Michael Zellerhda_read(struct hda_softc *sc, uint32_t offset)
74484659b2Michael Zeller{
74584659b2Michael Zeller	if (offset == HDAC_WALCLK)
74684659b2Michael Zeller		return (24 * (hda_get_clock_ns() -			\
74784659b2Michael Zeller			sc->wall_clock_start) / 1000);
74884659b2Michael Zeller
74984659b2Michael Zeller	return (hda_get_reg_by_offset(sc, offset));
75084659b2Michael Zeller}
75184659b2Michael Zeller
75284659b2Michael Zellerstatic int
75384659b2Michael Zellerhda_write(struct hda_softc *sc, uint32_t offset, uint8_t size, uint32_t value)
75484659b2Michael Zeller{
75584659b2Michael Zeller	uint32_t old = hda_get_reg_by_offset(sc, offset);
75684659b2Michael Zeller	uint32_t masks[] = {0x00000000, 0x000000ff, 0x0000ffff,
75784659b2Michael Zeller			0x00ffffff, 0xffffffff};
75884659b2Michael Zeller	hda_set_reg_handler set_reg_handler = hda_set_reg_table[offset];
75984659b2Michael Zeller
76084659b2Michael Zeller	hda_set_field_by_offset(sc, offset, masks[size], value);
76184659b2Michael Zeller
76284659b2Michael Zeller	if (set_reg_handler)
76384659b2Michael Zeller		set_reg_handler(sc, offset, old);
76484659b2Michael Zeller
76584659b2Michael Zeller	return (0);
76684659b2Michael Zeller}
76784659b2Michael Zeller
76884659b2Michael Zellerstatic inline void
76984659b2Michael Zellerhda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p)
77084659b2Michael Zeller{
77184659b2Michael Zeller#if DEBUG_HDA == 1
77284659b2Michael Zeller	char *name = p->name;
77384659b2Michael Zeller#endif
774154972aPatrick Mooney	DPRINTF("%s size: %d", name, p->size);
775154972aPatrick Mooney	DPRINTF("%s dma_vaddr: %p", name, p->dma_vaddr);
776154972aPatrick Mooney	DPRINTF("%s wp: 0x%x", name, p->wp);
777154972aPatrick Mooney	DPRINTF("%s rp: 0x%x", name, p->rp);
77884659b2Michael Zeller}
77984659b2Michael Zeller
78084659b2Michael Zellerstatic int
78184659b2Michael Zellerhda_corb_start(struct hda_softc *sc)
78284659b2Michael Zeller{
78384659b2Michael Zeller	struct hda_codec_cmd_ctl *corb = &sc->corb;
78484659b2Michael Zeller	uint8_t corbsize = 0;
78584659b2Michael Zeller	uint64_t corblbase = 0;
78684659b2Michael Zeller	uint64_t corbubase = 0;
78784659b2Michael Zeller	uint64_t corbpaddr = 0;
78884659b2Michael Zeller
78984659b2Michael Zeller	corb->name = "CORB";
79084659b2Michael Zeller
79184659b2Michael Zeller	corbsize = hda_get_reg_by_offset(sc, HDAC_CORBSIZE) &		\
79284659b2Michael Zeller		   HDAC_CORBSIZE_CORBSIZE_MASK;
79384659b2Michael Zeller	corb->size = hda_corb_sizes[corbsize];
79484659b2Michael Zeller
79584659b2Michael Zeller	if (!corb->size) {
796154972aPatrick Mooney		DPRINTF("Invalid corb size");
79784659b2Michael Zeller		return (-1);
79884659b2Michael Zeller	}
79984659b2Michael Zeller
80084659b2Michael Zeller	corblbase = hda_get_reg_by_offset(sc, HDAC_CORBLBASE);
80184659b2Michael Zeller	corbubase = hda_get_reg_by_offset(sc, HDAC_CORBUBASE);
80284659b2Michael Zeller
80384659b2Michael Zeller	corbpaddr = corblbase | (corbubase << 32);
804154972aPatrick Mooney	DPRINTF("CORB dma_paddr: %p", (void *)corbpaddr);
80584659b2Michael Zeller
80684659b2Michael Zeller	corb->dma_vaddr = hda_dma_get_vaddr(sc, corbpaddr,
80784659b2Michael Zeller			HDA_CORB_ENTRY_LEN * corb->size);
80884659b2Michael Zeller	if (!corb->dma_vaddr) {
809154972aPatrick Mooney		DPRINTF("Fail to get the guest virtual address");
81084659b2Michael Zeller		return (-1);
81184659b2Michael Zeller	}
81284659b2Michael Zeller
81384659b2Michael Zeller	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
81484659b2Michael Zeller	corb->rp = hda_get_reg_by_offset(sc, HDAC_CORBRP);
81584659b2Michael Zeller
81684659b2Michael Zeller	corb->run = 1;
81784659b2Michael Zeller
81884659b2Michael Zeller	hda_print_cmd_ctl_data(corb);
81984659b2Michael Zeller
82084659b2Michael Zeller	return (0);
82184659b2Michael Zeller}
82284659b2Michael Zeller
82384659b2Michael Zellerstatic int
82484659b2Michael Zellerhda_corb_run(struct hda_softc *sc)
82584659b2Michael Zeller{
82684659b2Michael Zeller	struct hda_codec_cmd_ctl *corb = &sc->corb;
82784659b2Michael Zeller	uint32_t verb = 0;
82884659b2Michael Zeller	int err;
82984659b2Michael Zeller
83084659b2Michael Zeller	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
83184659b2Michael Zeller
83284659b2Michael Zeller	while (corb->rp != corb->wp && corb->run) {
83384659b2Michael Zeller		corb->rp++;
83484659b2Michael Zeller		corb->rp %= corb->size;
83584659b2Michael Zeller
83684659b2Michael Zeller		verb = hda_dma_ld_dword(corb->dma_vaddr +		\
83784659b2Michael Zeller				HDA_CORB_ENTRY_LEN * corb->rp);
83884659b2Michael Zeller
83984659b2Michael Zeller		err = hda_send_command(sc, verb);
84084659b2Michael Zeller		assert(!err);
84184659b2Michael Zeller	}
84284659b2Michael Zeller
84384659b2Michael Zeller	hda_set_reg_by_offset(sc, HDAC_CORBRP, corb->rp);
84484659b2Michael Zeller
84584659b2Michael Zeller	if (corb->run)
84684659b2Michael Zeller		hda_response_interrupt(sc);
84784659b2Michael Zeller
84884659b2Michael Zeller	return (0);
84984659b2Michael Zeller}
85084659b2Michael Zeller
85184659b2Michael Zellerstatic int
85284659b2Michael Zellerhda_rirb_start(struct hda_softc *sc)
85384659b2Michael Zeller{
85484659b2Michael Zeller	struct hda_codec_cmd_ctl *rirb = &sc->rirb;
85584659b2Michael Zeller	uint8_t rirbsize = 0;
85684659b2Michael Zeller	uint64_t rirblbase = 0;
85784659b2Michael Zeller	uint64_t rirbubase = 0;
85884659b2Michael Zeller	uint64_t rirbpaddr = 0;
85984659b2Michael Zeller
86084659b2Michael Zeller	rirb->name = "RIRB";
86184659b2Michael Zeller
86284659b2Michael Zeller	rirbsize = hda_get_reg_by_offset(sc, HDAC_RIRBSIZE) &		\
86384659b2Michael Zeller		   HDAC_RIRBSIZE_RIRBSIZE_MASK;
86484659b2Michael Zeller	rirb->size = hda_rirb_sizes[rirbsize];
86584659b2Michael Zeller
86684659b2Michael Zeller	if (!rirb->size) {
867154972aPatrick Mooney		DPRINTF("Invalid rirb size");
86884659b2Michael Zeller		return (-1);
86984659b2Michael Zeller	}
87084659b2Michael Zeller
87184659b2Michael Zeller	rirblbase = hda_get_reg_by_offset(sc, HDAC_RIRBLBASE);
87284659b2Michael Zeller	rirbubase = hda_get_reg_by_offset(sc, HDAC_RIRBUBASE);
87384659b2Michael Zeller
87484659b2Michael Zeller	rirbpaddr = rirblbase | (rirbubase << 32);
875154972aPatrick Mooney	DPRINTF("RIRB dma_paddr: %p", (void *)rirbpaddr);
87684659b2Michael Zeller
87784659b2Michael Zeller	rirb->dma_vaddr = hda_dma_get_vaddr(sc, rirbpaddr,
87884659b2Michael Zeller			HDA_RIRB_ENTRY_LEN * rirb->size);
87984659b2Michael Zeller	if (!rirb->dma_vaddr) {
880154972aPatrick Mooney		DPRINTF("Fail to get the guest virtual address");
88184659b2Michael Zeller		return (-1);
88284659b2Michael Zeller	}
88384659b2Michael Zeller
88484659b2Michael Zeller	rirb->wp = hda_get_reg_by_offset(sc, HDAC_RIRBWP);
88584659b2Michael Zeller	rirb->rp = 0x0000;
88684659b2Michael Zeller
88784659b2Michael Zeller	rirb->run = 1;
88884659b2Michael Zeller
88984659b2Michael Zeller	hda_print_cmd_ctl_data(rirb);
89084659b2Michael Zeller
89184659b2Michael Zeller	return (0);
89284659b2Michael Zeller}
89384659b2Michael Zeller
89484659b2Michael Zellerstatic void *
89584659b2Michael Zellerhda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr, size_t len)
89684659b2Michael Zeller{
89784659b2Michael Zeller	struct pci_devinst *pi = sc->pci_dev;
89884659b2Michael Zeller
89984659b2Michael Zeller	assert(pi);
90084659b2Michael Zeller
90184659b2Michael Zeller	return (paddr_guest2host(pi->pi_vmctx, (uintptr_t)dma_paddr, len));
90284659b2Michael Zeller}
90384659b2Michael Zeller
90484659b2Michael Zellerstatic void
90584659b2Michael Zellerhda_dma_st_dword(void *dma_vaddr, uint32_t data)
90684659b2Michael Zeller{
90784659b2Michael Zeller	*(uint32_t*)dma_vaddr = data;
90884659b2Michael Zeller}
90984659b2Michael Zeller
91084659b2Michael Zellerstatic uint32_t
91184659b2Michael Zellerhda_dma_ld_dword(void *dma_vaddr)
91284659b2Michael Zeller{
91384659b2Michael Zeller	return (*(uint32_t*)dma_vaddr);
91484659b2Michael Zeller}
91584659b2Michael Zeller
91684659b2Michael Zellerstatic inline uint8_t
91784659b2Michael Zellerhda_get_stream_by_offsets(uint32_t offset, uint8_t reg_offset)
91884659b2Michael Zeller{
91984659b2Michael Zeller	uint8_t stream_ind = (offset - reg_offset) >> 5;
92084659b2Michael Zeller
92184659b2Michael Zeller	assert(stream_ind < HDA_IOSS_NO);
92284659b2Michael Zeller
92384659b2Michael Zeller	return (stream_ind);
92484659b2Michael Zeller}
925