xref: /illumos-gate/usr/src/cmd/bhyve/pci_hda.c (revision 32640292339b07090f10ce34d455f98711077343)
184659b24SMichael Zeller /*-
2*32640292SAndy Fiddaman  * SPDX-License-Identifier: BSD-2-Clause
384659b24SMichael Zeller  *
484659b24SMichael Zeller  * Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
584659b24SMichael Zeller  * All rights reserved.
684659b24SMichael Zeller  *
784659b24SMichael Zeller  * Redistribution and use in source and binary forms, with or without
884659b24SMichael Zeller  * modification, are permitted provided that the following conditions
984659b24SMichael Zeller  * are met:
1084659b24SMichael Zeller  * 1. Redistributions of source code must retain the above copyright
1184659b24SMichael Zeller  *    notice, this list of conditions and the following disclaimer.
1284659b24SMichael Zeller  * 2. Redistributions in binary form must reproduce the above copyright
1384659b24SMichael Zeller  *    notice, this list of conditions and the following disclaimer in the
1484659b24SMichael Zeller  *    documentation and/or other materials provided with the distribution.
1584659b24SMichael Zeller  *
1684659b24SMichael Zeller  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
1784659b24SMichael Zeller  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1884659b24SMichael Zeller  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1984659b24SMichael Zeller  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2084659b24SMichael Zeller  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2184659b24SMichael Zeller  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2284659b24SMichael Zeller  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2384659b24SMichael Zeller  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2484659b24SMichael Zeller  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2584659b24SMichael Zeller  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2684659b24SMichael Zeller  * SUCH DAMAGE.
2784659b24SMichael Zeller  *
2884659b24SMichael Zeller  */
2984659b24SMichael Zeller 
3084659b24SMichael Zeller #include <sys/cdefs.h>
3184659b24SMichael Zeller 
32*32640292SAndy Fiddaman #include <sys/param.h>
3384659b24SMichael Zeller #include <time.h>
3484659b24SMichael Zeller 
3584659b24SMichael Zeller #include "pci_hda.h"
3684659b24SMichael Zeller #include "bhyverun.h"
372b948146SAndy Fiddaman #include "config.h"
3884659b24SMichael Zeller #include "pci_emul.h"
3984659b24SMichael Zeller #include "hdac_reg.h"
4084659b24SMichael Zeller 
4184659b24SMichael Zeller /*
4284659b24SMichael Zeller  * HDA defines
4384659b24SMichael Zeller  */
4484659b24SMichael Zeller #define PCIR_HDCTL		0x40
4584659b24SMichael Zeller #define INTEL_VENDORID		0x8086
4684659b24SMichael Zeller #define HDA_INTEL_82801G	0x27d8
4784659b24SMichael Zeller 
4884659b24SMichael Zeller #define HDA_IOSS_NO		0x08
4984659b24SMichael Zeller #define HDA_OSS_NO		0x04
5084659b24SMichael Zeller #define HDA_ISS_NO		0x04
5184659b24SMichael Zeller #define HDA_CODEC_MAX		0x0f
5284659b24SMichael Zeller #define HDA_LAST_OFFSET						\
5384659b24SMichael Zeller 	(0x2084 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20))
5484659b24SMichael Zeller #define HDA_CORB_ENTRY_LEN	0x04
5584659b24SMichael Zeller #define HDA_RIRB_ENTRY_LEN	0x08
5684659b24SMichael Zeller #define HDA_BDL_ENTRY_LEN	0x10
5784659b24SMichael Zeller #define HDA_DMA_PIB_ENTRY_LEN	0x08
5884659b24SMichael Zeller #define HDA_STREAM_TAGS_CNT	0x10
5984659b24SMichael Zeller #define HDA_STREAM_REGS_BASE	0x80
6084659b24SMichael Zeller #define HDA_STREAM_REGS_LEN	0x20
6184659b24SMichael Zeller 
6284659b24SMichael Zeller #define HDA_DMA_ACCESS_LEN	(sizeof(uint32_t))
6384659b24SMichael Zeller #define HDA_BDL_MAX_LEN		0x0100
6484659b24SMichael Zeller 
6584659b24SMichael Zeller #define HDAC_SDSTS_FIFORDY	(1 << 5)
6684659b24SMichael Zeller 
6784659b24SMichael Zeller #define HDA_RIRBSTS_IRQ_MASK	(HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS)
6884659b24SMichael Zeller #define HDA_STATESTS_IRQ_MASK	((1 << HDA_CODEC_MAX) - 1)
6984659b24SMichael Zeller #define HDA_SDSTS_IRQ_MASK					\
7084659b24SMichael Zeller 	(HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS)
7184659b24SMichael Zeller 
7284659b24SMichael Zeller /*
7384659b24SMichael Zeller  * HDA data structures
7484659b24SMichael Zeller  */
7584659b24SMichael Zeller 
7684659b24SMichael Zeller struct hda_softc;
7784659b24SMichael Zeller 
7884659b24SMichael Zeller typedef void (*hda_set_reg_handler)(struct hda_softc *sc, uint32_t offset,
7984659b24SMichael Zeller 		uint32_t old);
8084659b24SMichael Zeller 
8184659b24SMichael Zeller struct hda_bdle {
8284659b24SMichael Zeller 	uint32_t addrl;
8384659b24SMichael Zeller 	uint32_t addrh;
8484659b24SMichael Zeller 	uint32_t len;
8584659b24SMichael Zeller 	uint32_t ioc;
8684659b24SMichael Zeller } __packed;
8784659b24SMichael Zeller 
8884659b24SMichael Zeller struct hda_bdle_desc {
8984659b24SMichael Zeller 	void *addr;
9084659b24SMichael Zeller 	uint8_t ioc;
9184659b24SMichael Zeller 	uint32_t len;
9284659b24SMichael Zeller };
9384659b24SMichael Zeller 
9484659b24SMichael Zeller struct hda_codec_cmd_ctl {
954f3f3e9aSAndy Fiddaman 	const char *name;
9684659b24SMichael Zeller 	void *dma_vaddr;
9784659b24SMichael Zeller 	uint8_t run;
9884659b24SMichael Zeller 	uint16_t rp;
9984659b24SMichael Zeller 	uint16_t size;
10084659b24SMichael Zeller 	uint16_t wp;
10184659b24SMichael Zeller };
10284659b24SMichael Zeller 
10384659b24SMichael Zeller struct hda_stream_desc {
10484659b24SMichael Zeller 	uint8_t dir;
10584659b24SMichael Zeller 	uint8_t run;
10684659b24SMichael Zeller 	uint8_t stream;
10784659b24SMichael Zeller 
10884659b24SMichael Zeller 	/* bp is the no. of bytes transferred in the current bdle */
10984659b24SMichael Zeller 	uint32_t bp;
11084659b24SMichael Zeller 	/* be is the no. of bdles transferred in the bdl */
11184659b24SMichael Zeller 	uint32_t be;
11284659b24SMichael Zeller 
11384659b24SMichael Zeller 	uint32_t bdl_cnt;
11484659b24SMichael Zeller 	struct hda_bdle_desc bdl[HDA_BDL_MAX_LEN];
11584659b24SMichael Zeller };
11684659b24SMichael Zeller 
11784659b24SMichael Zeller struct hda_softc {
11884659b24SMichael Zeller 	struct pci_devinst *pci_dev;
11984659b24SMichael Zeller 	uint32_t regs[HDA_LAST_OFFSET];
12084659b24SMichael Zeller 
12184659b24SMichael Zeller 	uint8_t lintr;
12284659b24SMichael Zeller 	uint8_t rirb_cnt;
12384659b24SMichael Zeller 	uint64_t wall_clock_start;
12484659b24SMichael Zeller 
12584659b24SMichael Zeller 	struct hda_codec_cmd_ctl corb;
12684659b24SMichael Zeller 	struct hda_codec_cmd_ctl rirb;
12784659b24SMichael Zeller 
12884659b24SMichael Zeller 	uint8_t codecs_no;
12984659b24SMichael Zeller 	struct hda_codec_inst *codecs[HDA_CODEC_MAX];
13084659b24SMichael Zeller 
13184659b24SMichael Zeller 	/* Base Address of the DMA Position Buffer */
13284659b24SMichael Zeller 	void *dma_pib_vaddr;
13384659b24SMichael Zeller 
13484659b24SMichael Zeller 	struct hda_stream_desc streams[HDA_IOSS_NO];
13584659b24SMichael Zeller 	/* 2 tables for output and input */
13684659b24SMichael Zeller 	uint8_t stream_map[2][HDA_STREAM_TAGS_CNT];
13784659b24SMichael Zeller };
13884659b24SMichael Zeller 
13984659b24SMichael Zeller /*
14084659b24SMichael Zeller  * HDA module function declarations
14184659b24SMichael Zeller  */
14284659b24SMichael Zeller static inline void hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset,
14384659b24SMichael Zeller     uint32_t value);
14484659b24SMichael Zeller static inline uint32_t hda_get_reg_by_offset(struct hda_softc *sc,
14584659b24SMichael Zeller     uint32_t offset);
14684659b24SMichael Zeller static inline void hda_set_field_by_offset(struct hda_softc *sc,
14784659b24SMichael Zeller     uint32_t offset, uint32_t mask, uint32_t value);
14884659b24SMichael Zeller 
1492b948146SAndy Fiddaman static struct hda_softc *hda_init(nvlist_t *nvl);
15084659b24SMichael Zeller static void hda_update_intr(struct hda_softc *sc);
15184659b24SMichael Zeller static void hda_response_interrupt(struct hda_softc *sc);
15284659b24SMichael Zeller static int hda_codec_constructor(struct hda_softc *sc,
1532b948146SAndy Fiddaman     struct hda_codec_class *codec, const char *play, const char *rec);
15484659b24SMichael Zeller static struct hda_codec_class *hda_find_codec_class(const char *name);
15584659b24SMichael Zeller 
15684659b24SMichael Zeller static int hda_send_command(struct hda_softc *sc, uint32_t verb);
15784659b24SMichael Zeller static int hda_notify_codecs(struct hda_softc *sc, uint8_t run,
15884659b24SMichael Zeller     uint8_t stream, uint8_t dir);
15984659b24SMichael Zeller static void hda_reset(struct hda_softc *sc);
16084659b24SMichael Zeller static void hda_reset_regs(struct hda_softc *sc);
16184659b24SMichael Zeller static void hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind);
16284659b24SMichael Zeller static int hda_stream_start(struct hda_softc *sc, uint8_t stream_ind);
16384659b24SMichael Zeller static int hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind);
16484659b24SMichael Zeller static uint32_t hda_read(struct hda_softc *sc, uint32_t offset);
16584659b24SMichael Zeller static int hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size,
16684659b24SMichael Zeller     uint32_t value);
16784659b24SMichael Zeller 
16884659b24SMichael Zeller static inline void hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p);
16984659b24SMichael Zeller static int hda_corb_start(struct hda_softc *sc);
17084659b24SMichael Zeller static int hda_corb_run(struct hda_softc *sc);
17184659b24SMichael Zeller static int hda_rirb_start(struct hda_softc *sc);
17284659b24SMichael Zeller 
17384659b24SMichael Zeller static void *hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr,
17484659b24SMichael Zeller     size_t len);
17584659b24SMichael Zeller static void hda_dma_st_dword(void *dma_vaddr, uint32_t data);
17684659b24SMichael Zeller static uint32_t hda_dma_ld_dword(void *dma_vaddr);
17784659b24SMichael Zeller 
17884659b24SMichael Zeller static inline uint8_t hda_get_stream_by_offsets(uint32_t offset,
17984659b24SMichael Zeller     uint8_t reg_offset);
18084659b24SMichael Zeller static inline uint32_t hda_get_offset_stream(uint8_t stream_ind);
18184659b24SMichael Zeller 
18284659b24SMichael Zeller static void hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
18384659b24SMichael Zeller static void hda_set_statests(struct hda_softc *sc, uint32_t offset,
18484659b24SMichael Zeller     uint32_t old);
18584659b24SMichael Zeller static void hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old);
18684659b24SMichael Zeller static void hda_set_corbctl(struct hda_softc *sc, uint32_t offset,
18784659b24SMichael Zeller     uint32_t old);
18884659b24SMichael Zeller static void hda_set_rirbctl(struct hda_softc *sc, uint32_t offset,
18984659b24SMichael Zeller     uint32_t old);
19084659b24SMichael Zeller static void hda_set_rirbsts(struct hda_softc *sc, uint32_t offset,
19184659b24SMichael Zeller     uint32_t old);
19284659b24SMichael Zeller static void hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset,
19384659b24SMichael Zeller     uint32_t old);
19484659b24SMichael Zeller static void hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old);
19584659b24SMichael Zeller static void hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old);
19684659b24SMichael Zeller static void hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old);
19784659b24SMichael Zeller 
19884659b24SMichael Zeller static int hda_signal_state_change(struct hda_codec_inst *hci);
19984659b24SMichael Zeller static int hda_response(struct hda_codec_inst *hci, uint32_t response,
20084659b24SMichael Zeller     uint8_t unsol);
20184659b24SMichael Zeller static int hda_transfer(struct hda_codec_inst *hci, uint8_t stream,
20259d65d31SAndy Fiddaman     uint8_t dir, uint8_t *buf, size_t count);
20384659b24SMichael Zeller 
20484659b24SMichael Zeller static void hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib);
20584659b24SMichael Zeller static uint64_t hda_get_clock_ns(void);
20684659b24SMichael Zeller 
20784659b24SMichael Zeller /*
20884659b24SMichael Zeller  * PCI HDA function declarations
20984659b24SMichael Zeller  */
210*32640292SAndy Fiddaman static int pci_hda_init(struct pci_devinst *pi, nvlist_t *nvl);
211*32640292SAndy Fiddaman static void pci_hda_write(struct pci_devinst *pi, int baridx, uint64_t offset,
212*32640292SAndy Fiddaman     int size, uint64_t value);
213*32640292SAndy Fiddaman static uint64_t pci_hda_read(struct pci_devinst *pi, int baridx,
214*32640292SAndy Fiddaman     uint64_t offset, int size);
21584659b24SMichael Zeller /*
21684659b24SMichael Zeller  * HDA global data
21784659b24SMichael Zeller  */
21884659b24SMichael Zeller 
21984659b24SMichael Zeller static const hda_set_reg_handler hda_set_reg_table[] = {
22084659b24SMichael Zeller 	[HDAC_GCTL] = hda_set_gctl,
22184659b24SMichael Zeller 	[HDAC_STATESTS] = hda_set_statests,
22284659b24SMichael Zeller 	[HDAC_CORBWP] = hda_set_corbwp,
22384659b24SMichael Zeller 	[HDAC_CORBCTL] = hda_set_corbctl,
22484659b24SMichael Zeller 	[HDAC_RIRBCTL] = hda_set_rirbctl,
22584659b24SMichael Zeller 	[HDAC_RIRBSTS] = hda_set_rirbsts,
22684659b24SMichael Zeller 	[HDAC_DPIBLBASE] = hda_set_dpiblbase,
22784659b24SMichael Zeller 
22884659b24SMichael Zeller #define HDAC_ISTREAM(n, iss, oss)				\
22984659b24SMichael Zeller 	[_HDAC_ISDCTL(n, iss, oss)] = hda_set_sdctl,		\
23084659b24SMichael Zeller 	[_HDAC_ISDCTL(n, iss, oss) + 2] = hda_set_sdctl2,	\
23184659b24SMichael Zeller 	[_HDAC_ISDSTS(n, iss, oss)] = hda_set_sdsts,		\
23284659b24SMichael Zeller 
23384659b24SMichael Zeller #define HDAC_OSTREAM(n, iss, oss)				\
23484659b24SMichael Zeller 	[_HDAC_OSDCTL(n, iss, oss)] = hda_set_sdctl,		\
23584659b24SMichael Zeller 	[_HDAC_OSDCTL(n, iss, oss) + 2] = hda_set_sdctl2,	\
23684659b24SMichael Zeller 	[_HDAC_OSDSTS(n, iss, oss)] = hda_set_sdsts,		\
23784659b24SMichael Zeller 
23884659b24SMichael Zeller 	HDAC_ISTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
23984659b24SMichael Zeller 	HDAC_ISTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
24084659b24SMichael Zeller 	HDAC_ISTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
24184659b24SMichael Zeller 	HDAC_ISTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
24284659b24SMichael Zeller 
24384659b24SMichael Zeller 	HDAC_OSTREAM(0, HDA_ISS_NO, HDA_OSS_NO)
24484659b24SMichael Zeller 	HDAC_OSTREAM(1, HDA_ISS_NO, HDA_OSS_NO)
24584659b24SMichael Zeller 	HDAC_OSTREAM(2, HDA_ISS_NO, HDA_OSS_NO)
24684659b24SMichael Zeller 	HDAC_OSTREAM(3, HDA_ISS_NO, HDA_OSS_NO)
24784659b24SMichael Zeller };
24884659b24SMichael Zeller 
24984659b24SMichael Zeller static const uint16_t hda_corb_sizes[] = {
25084659b24SMichael Zeller 	[HDAC_CORBSIZE_CORBSIZE_2]	= 2,
25184659b24SMichael Zeller 	[HDAC_CORBSIZE_CORBSIZE_16]	= 16,
25284659b24SMichael Zeller 	[HDAC_CORBSIZE_CORBSIZE_256]	= 256,
25384659b24SMichael Zeller 	[HDAC_CORBSIZE_CORBSIZE_MASK]	= 0,
25484659b24SMichael Zeller };
25584659b24SMichael Zeller 
25684659b24SMichael Zeller static const uint16_t hda_rirb_sizes[] = {
25784659b24SMichael Zeller 	[HDAC_RIRBSIZE_RIRBSIZE_2]	= 2,
25884659b24SMichael Zeller 	[HDAC_RIRBSIZE_RIRBSIZE_16]	= 16,
25984659b24SMichael Zeller 	[HDAC_RIRBSIZE_RIRBSIZE_256]	= 256,
26084659b24SMichael Zeller 	[HDAC_RIRBSIZE_RIRBSIZE_MASK]	= 0,
26184659b24SMichael Zeller };
26284659b24SMichael Zeller 
26359d65d31SAndy Fiddaman static const struct hda_ops hops = {
26484659b24SMichael Zeller 	.signal		= hda_signal_state_change,
26584659b24SMichael Zeller 	.response	= hda_response,
26684659b24SMichael Zeller 	.transfer	= hda_transfer,
26784659b24SMichael Zeller };
26884659b24SMichael Zeller 
2694f3f3e9aSAndy Fiddaman static const struct pci_devemu pci_de_hda = {
27084659b24SMichael Zeller 	.pe_emu		= "hda",
27184659b24SMichael Zeller 	.pe_init	= pci_hda_init,
27284659b24SMichael Zeller 	.pe_barwrite	= pci_hda_write,
27384659b24SMichael Zeller 	.pe_barread	= pci_hda_read
27484659b24SMichael Zeller };
27584659b24SMichael Zeller PCI_EMUL_SET(pci_de_hda);
27684659b24SMichael Zeller 
27784659b24SMichael Zeller SET_DECLARE(hda_codec_class_set, struct hda_codec_class);
27884659b24SMichael Zeller 
27984659b24SMichael Zeller #if DEBUG_HDA == 1
28084659b24SMichael Zeller FILE *dbg;
28184659b24SMichael Zeller #endif
28284659b24SMichael Zeller 
28384659b24SMichael Zeller /*
28484659b24SMichael Zeller  * HDA module function definitions
28584659b24SMichael Zeller  */
28684659b24SMichael Zeller 
28784659b24SMichael Zeller static inline void
hda_set_reg_by_offset(struct hda_softc * sc,uint32_t offset,uint32_t value)28884659b24SMichael Zeller hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t value)
28984659b24SMichael Zeller {
29084659b24SMichael Zeller 	assert(offset < HDA_LAST_OFFSET);
29184659b24SMichael Zeller 	sc->regs[offset] = value;
29284659b24SMichael Zeller }
29384659b24SMichael Zeller 
29484659b24SMichael Zeller static inline uint32_t
hda_get_reg_by_offset(struct hda_softc * sc,uint32_t offset)29584659b24SMichael Zeller hda_get_reg_by_offset(struct hda_softc *sc, uint32_t offset)
29684659b24SMichael Zeller {
29784659b24SMichael Zeller 	assert(offset < HDA_LAST_OFFSET);
29884659b24SMichael Zeller 	return sc->regs[offset];
29984659b24SMichael Zeller }
30084659b24SMichael Zeller 
30184659b24SMichael Zeller static inline void
hda_set_field_by_offset(struct hda_softc * sc,uint32_t offset,uint32_t mask,uint32_t value)30284659b24SMichael Zeller hda_set_field_by_offset(struct hda_softc *sc, uint32_t offset,
30384659b24SMichael Zeller     uint32_t mask, uint32_t value)
30484659b24SMichael Zeller {
30584659b24SMichael Zeller 	uint32_t reg_value = 0;
30684659b24SMichael Zeller 
30784659b24SMichael Zeller 	reg_value = hda_get_reg_by_offset(sc, offset);
30884659b24SMichael Zeller 
30984659b24SMichael Zeller 	reg_value &= ~mask;
31084659b24SMichael Zeller 	reg_value |= (value & mask);
31184659b24SMichael Zeller 
31284659b24SMichael Zeller 	hda_set_reg_by_offset(sc, offset, reg_value);
31384659b24SMichael Zeller }
31484659b24SMichael Zeller 
31584659b24SMichael Zeller static struct hda_softc *
hda_init(nvlist_t * nvl)3162b948146SAndy Fiddaman hda_init(nvlist_t *nvl)
31784659b24SMichael Zeller {
31884659b24SMichael Zeller 	struct hda_softc *sc = NULL;
31984659b24SMichael Zeller 	struct hda_codec_class *codec = NULL;
3202b948146SAndy Fiddaman 	const char *value;
3212b948146SAndy Fiddaman 	char *play;
3222b948146SAndy Fiddaman 	char *rec;
3232b948146SAndy Fiddaman 	int err;
32484659b24SMichael Zeller 
32584659b24SMichael Zeller #if DEBUG_HDA == 1
326*32640292SAndy Fiddaman 	dbg = fopen(DEBUG_HDA_FILE, "w+");
32784659b24SMichael Zeller #endif
32884659b24SMichael Zeller 
32984659b24SMichael Zeller 	sc = calloc(1, sizeof(*sc));
33084659b24SMichael Zeller 	if (!sc)
33184659b24SMichael Zeller 		return (NULL);
33284659b24SMichael Zeller 
33384659b24SMichael Zeller 	hda_reset_regs(sc);
33484659b24SMichael Zeller 
33584659b24SMichael Zeller 	/*
3362b948146SAndy Fiddaman 	 * TODO search all configured codecs
33784659b24SMichael Zeller 	 * For now we play with one single codec
33884659b24SMichael Zeller 	 */
33984659b24SMichael Zeller 	codec = hda_find_codec_class("hda_codec");
34084659b24SMichael Zeller 	if (codec) {
3412b948146SAndy Fiddaman 		value = get_config_value_node(nvl, "play");
3422b948146SAndy Fiddaman 		if (value == NULL)
3432b948146SAndy Fiddaman 			play = NULL;
3442b948146SAndy Fiddaman 		else
3452b948146SAndy Fiddaman 			play = strdup(value);
3462b948146SAndy Fiddaman 		value = get_config_value_node(nvl, "rec");
3472b948146SAndy Fiddaman 		if (value == NULL)
3482b948146SAndy Fiddaman 			rec = NULL;
3492b948146SAndy Fiddaman 		else
3502b948146SAndy Fiddaman 			rec = strdup(value);
351154972afSPatrick Mooney 		DPRINTF("play: %s rec: %s", play, rec);
3522b948146SAndy Fiddaman 		if (play != NULL || rec != NULL) {
3532b948146SAndy Fiddaman 			err = hda_codec_constructor(sc, codec, play, rec);
35484659b24SMichael Zeller 			assert(!err);
35584659b24SMichael Zeller 		}
3562b948146SAndy Fiddaman 		free(play);
3572b948146SAndy Fiddaman 		free(rec);
35884659b24SMichael Zeller 	}
35984659b24SMichael Zeller 
36084659b24SMichael Zeller 	return (sc);
36184659b24SMichael Zeller }
36284659b24SMichael Zeller 
36384659b24SMichael Zeller static void
hda_update_intr(struct hda_softc * sc)36484659b24SMichael Zeller hda_update_intr(struct hda_softc *sc)
36584659b24SMichael Zeller {
36684659b24SMichael Zeller 	struct pci_devinst *pi = sc->pci_dev;
36784659b24SMichael Zeller 	uint32_t intctl = hda_get_reg_by_offset(sc, HDAC_INTCTL);
36884659b24SMichael Zeller 	uint32_t intsts = 0;
36984659b24SMichael Zeller 	uint32_t sdsts = 0;
37084659b24SMichael Zeller 	uint32_t rirbsts = 0;
37184659b24SMichael Zeller 	uint32_t wakeen = 0;
37284659b24SMichael Zeller 	uint32_t statests = 0;
37384659b24SMichael Zeller 	uint32_t off = 0;
37484659b24SMichael Zeller 	int i;
37584659b24SMichael Zeller 
37684659b24SMichael Zeller 	/* update the CIS bits */
37784659b24SMichael Zeller 	rirbsts = hda_get_reg_by_offset(sc, HDAC_RIRBSTS);
37884659b24SMichael Zeller 	if (rirbsts & (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS))
37984659b24SMichael Zeller 		intsts |= HDAC_INTSTS_CIS;
38084659b24SMichael Zeller 
38184659b24SMichael Zeller 	wakeen = hda_get_reg_by_offset(sc, HDAC_WAKEEN);
38284659b24SMichael Zeller 	statests = hda_get_reg_by_offset(sc, HDAC_STATESTS);
38384659b24SMichael Zeller 	if (statests & wakeen)
38484659b24SMichael Zeller 		intsts |= HDAC_INTSTS_CIS;
38584659b24SMichael Zeller 
38684659b24SMichael Zeller 	/* update the SIS bits */
38784659b24SMichael Zeller 	for (i = 0; i < HDA_IOSS_NO; i++) {
38884659b24SMichael Zeller 		off = hda_get_offset_stream(i);
38984659b24SMichael Zeller 		sdsts = hda_get_reg_by_offset(sc, off + HDAC_SDSTS);
39084659b24SMichael Zeller 		if (sdsts & HDAC_SDSTS_BCIS)
39184659b24SMichael Zeller 			intsts |= (1 << i);
39284659b24SMichael Zeller 	}
39384659b24SMichael Zeller 
39484659b24SMichael Zeller 	/* update the GIS bit */
39584659b24SMichael Zeller 	if (intsts)
39684659b24SMichael Zeller 		intsts |= HDAC_INTSTS_GIS;
39784659b24SMichael Zeller 
39884659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_INTSTS, intsts);
39984659b24SMichael Zeller 
40084659b24SMichael Zeller 	if ((intctl & HDAC_INTCTL_GIE) && ((intsts &			\
40184659b24SMichael Zeller 		~HDAC_INTSTS_GIS) & intctl)) {
40284659b24SMichael Zeller 		if (!sc->lintr) {
40384659b24SMichael Zeller 			pci_lintr_assert(pi);
40484659b24SMichael Zeller 			sc->lintr = 1;
40584659b24SMichael Zeller 		}
40684659b24SMichael Zeller 	} else {
40784659b24SMichael Zeller 		if (sc->lintr) {
40884659b24SMichael Zeller 			pci_lintr_deassert(pi);
40984659b24SMichael Zeller 			sc->lintr = 0;
41084659b24SMichael Zeller 		}
41184659b24SMichael Zeller 	}
41284659b24SMichael Zeller }
41384659b24SMichael Zeller 
41484659b24SMichael Zeller static void
hda_response_interrupt(struct hda_softc * sc)41584659b24SMichael Zeller hda_response_interrupt(struct hda_softc *sc)
41684659b24SMichael Zeller {
41784659b24SMichael Zeller 	uint8_t rirbctl = hda_get_reg_by_offset(sc, HDAC_RIRBCTL);
41884659b24SMichael Zeller 
41984659b24SMichael Zeller 	if ((rirbctl & HDAC_RIRBCTL_RINTCTL) && sc->rirb_cnt) {
42084659b24SMichael Zeller 		sc->rirb_cnt = 0;
42184659b24SMichael Zeller 		hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL,
42284659b24SMichael Zeller 				HDAC_RIRBSTS_RINTFL);
42384659b24SMichael Zeller 		hda_update_intr(sc);
42484659b24SMichael Zeller 	}
42584659b24SMichael Zeller }
42684659b24SMichael Zeller 
42784659b24SMichael Zeller static int
hda_codec_constructor(struct hda_softc * sc,struct hda_codec_class * codec,const char * play,const char * rec)42884659b24SMichael Zeller hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec,
4292b948146SAndy Fiddaman     const char *play, const char *rec)
43084659b24SMichael Zeller {
43184659b24SMichael Zeller 	struct hda_codec_inst *hci = NULL;
43284659b24SMichael Zeller 
43384659b24SMichael Zeller 	if (sc->codecs_no >= HDA_CODEC_MAX)
43484659b24SMichael Zeller 		return (-1);
43584659b24SMichael Zeller 
43684659b24SMichael Zeller 	hci = calloc(1, sizeof(struct hda_codec_inst));
43784659b24SMichael Zeller 	if (!hci)
43884659b24SMichael Zeller 		return (-1);
43984659b24SMichael Zeller 
44084659b24SMichael Zeller 	hci->hda = sc;
44184659b24SMichael Zeller 	hci->hops = &hops;
44284659b24SMichael Zeller 	hci->cad = sc->codecs_no;
44384659b24SMichael Zeller 	hci->codec = codec;
44484659b24SMichael Zeller 
44584659b24SMichael Zeller 	sc->codecs[sc->codecs_no++] = hci;
44684659b24SMichael Zeller 
44784659b24SMichael Zeller 	if (!codec->init) {
448154972afSPatrick Mooney 		DPRINTF("This codec does not implement the init function");
44984659b24SMichael Zeller 		return (-1);
45084659b24SMichael Zeller 	}
45184659b24SMichael Zeller 
4522b948146SAndy Fiddaman 	return (codec->init(hci, play, rec));
45384659b24SMichael Zeller }
45484659b24SMichael Zeller 
45584659b24SMichael Zeller static struct hda_codec_class *
hda_find_codec_class(const char * name)45684659b24SMichael Zeller hda_find_codec_class(const char *name)
45784659b24SMichael Zeller {
45884659b24SMichael Zeller 	struct hda_codec_class **pdpp = NULL, *pdp = NULL;
45984659b24SMichael Zeller 
46084659b24SMichael Zeller 	SET_FOREACH(pdpp, hda_codec_class_set) {
46184659b24SMichael Zeller 		pdp = *pdpp;
46284659b24SMichael Zeller 		if (!strcmp(pdp->name, name)) {
46384659b24SMichael Zeller 			return (pdp);
46484659b24SMichael Zeller 		}
46584659b24SMichael Zeller 	}
46684659b24SMichael Zeller 
46784659b24SMichael Zeller 	return (NULL);
46884659b24SMichael Zeller }
46984659b24SMichael Zeller 
47084659b24SMichael Zeller static int
hda_send_command(struct hda_softc * sc,uint32_t verb)47184659b24SMichael Zeller hda_send_command(struct hda_softc *sc, uint32_t verb)
47284659b24SMichael Zeller {
47384659b24SMichael Zeller 	struct hda_codec_inst *hci = NULL;
47484659b24SMichael Zeller 	struct hda_codec_class *codec = NULL;
47584659b24SMichael Zeller 	uint8_t cad = (verb >> HDA_CMD_CAD_SHIFT) & 0x0f;
47684659b24SMichael Zeller 
477*32640292SAndy Fiddaman 	if (cad >= sc->codecs_no)
47884659b24SMichael Zeller 		return (-1);
47984659b24SMichael Zeller 
480154972afSPatrick Mooney 	DPRINTF("cad: 0x%x verb: 0x%x", cad, verb);
48184659b24SMichael Zeller 
482*32640292SAndy Fiddaman 	hci = sc->codecs[cad];
483*32640292SAndy Fiddaman 	assert(hci);
484*32640292SAndy Fiddaman 
48584659b24SMichael Zeller 	codec = hci->codec;
48684659b24SMichael Zeller 	assert(codec);
48784659b24SMichael Zeller 
48884659b24SMichael Zeller 	if (!codec->command) {
489154972afSPatrick Mooney 		DPRINTF("This codec does not implement the command function");
49084659b24SMichael Zeller 		return (-1);
49184659b24SMichael Zeller 	}
49284659b24SMichael Zeller 
49384659b24SMichael Zeller 	return (codec->command(hci, verb));
49484659b24SMichael Zeller }
49584659b24SMichael Zeller 
49684659b24SMichael Zeller static int
hda_notify_codecs(struct hda_softc * sc,uint8_t run,uint8_t stream,uint8_t dir)49784659b24SMichael Zeller hda_notify_codecs(struct hda_softc *sc, uint8_t run, uint8_t stream,
49884659b24SMichael Zeller     uint8_t dir)
49984659b24SMichael Zeller {
50084659b24SMichael Zeller 	struct hda_codec_inst *hci = NULL;
50184659b24SMichael Zeller 	struct hda_codec_class *codec = NULL;
50284659b24SMichael Zeller 	int err;
50384659b24SMichael Zeller 	int i;
50484659b24SMichael Zeller 
50584659b24SMichael Zeller 	/* Notify each codec */
50684659b24SMichael Zeller 	for (i = 0; i < sc->codecs_no; i++) {
50784659b24SMichael Zeller 		hci = sc->codecs[i];
50884659b24SMichael Zeller 		assert(hci);
50984659b24SMichael Zeller 
51084659b24SMichael Zeller 		codec = hci->codec;
51184659b24SMichael Zeller 		assert(codec);
51284659b24SMichael Zeller 
51384659b24SMichael Zeller 		if (codec->notify) {
51484659b24SMichael Zeller 			err = codec->notify(hci, run, stream, dir);
51584659b24SMichael Zeller 			if (!err)
51684659b24SMichael Zeller 				break;
51784659b24SMichael Zeller 		}
51884659b24SMichael Zeller 	}
51984659b24SMichael Zeller 
52084659b24SMichael Zeller 	return (i == sc->codecs_no ? (-1) : 0);
52184659b24SMichael Zeller }
52284659b24SMichael Zeller 
52384659b24SMichael Zeller static void
hda_reset(struct hda_softc * sc)52484659b24SMichael Zeller hda_reset(struct hda_softc *sc)
52584659b24SMichael Zeller {
52684659b24SMichael Zeller 	int i;
52784659b24SMichael Zeller 	struct hda_codec_inst *hci = NULL;
52884659b24SMichael Zeller 	struct hda_codec_class *codec = NULL;
52984659b24SMichael Zeller 
53084659b24SMichael Zeller 	hda_reset_regs(sc);
53184659b24SMichael Zeller 
53284659b24SMichael Zeller 	/* Reset each codec */
53384659b24SMichael Zeller 	for (i = 0; i < sc->codecs_no; i++) {
53484659b24SMichael Zeller 		hci = sc->codecs[i];
53584659b24SMichael Zeller 		assert(hci);
53684659b24SMichael Zeller 
53784659b24SMichael Zeller 		codec = hci->codec;
53884659b24SMichael Zeller 		assert(codec);
53984659b24SMichael Zeller 
54084659b24SMichael Zeller 		if (codec->reset)
54184659b24SMichael Zeller 			codec->reset(hci);
54284659b24SMichael Zeller 	}
54384659b24SMichael Zeller 
54484659b24SMichael Zeller 	sc->wall_clock_start = hda_get_clock_ns();
54584659b24SMichael Zeller }
54684659b24SMichael Zeller 
54784659b24SMichael Zeller static void
hda_reset_regs(struct hda_softc * sc)54884659b24SMichael Zeller hda_reset_regs(struct hda_softc *sc)
54984659b24SMichael Zeller {
55084659b24SMichael Zeller 	uint32_t off = 0;
55184659b24SMichael Zeller 	uint8_t i;
55284659b24SMichael Zeller 
553154972afSPatrick Mooney 	DPRINTF("Reset the HDA controller registers ...");
55484659b24SMichael Zeller 
55584659b24SMichael Zeller 	memset(sc->regs, 0, sizeof(sc->regs));
55684659b24SMichael Zeller 
55784659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_GCAP,
55884659b24SMichael Zeller 			HDAC_GCAP_64OK |
55984659b24SMichael Zeller 			(HDA_ISS_NO << HDAC_GCAP_ISS_SHIFT) |
56084659b24SMichael Zeller 			(HDA_OSS_NO << HDAC_GCAP_OSS_SHIFT));
56184659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_VMAJ, 0x01);
56284659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_OUTPAY, 0x3c);
56384659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_INPAY, 0x1d);
56484659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_CORBSIZE,
56584659b24SMichael Zeller 	    HDAC_CORBSIZE_CORBSZCAP_256 | HDAC_CORBSIZE_CORBSIZE_256);
56684659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_RIRBSIZE,
56784659b24SMichael Zeller 	    HDAC_RIRBSIZE_RIRBSZCAP_256 | HDAC_RIRBSIZE_RIRBSIZE_256);
56884659b24SMichael Zeller 
56984659b24SMichael Zeller 	for (i = 0; i < HDA_IOSS_NO; i++) {
57084659b24SMichael Zeller 		off = hda_get_offset_stream(i);
57184659b24SMichael Zeller 		hda_set_reg_by_offset(sc, off + HDAC_SDFIFOS, HDA_FIFO_SIZE);
57284659b24SMichael Zeller 	}
57384659b24SMichael Zeller }
57484659b24SMichael Zeller 
57584659b24SMichael Zeller static void
hda_stream_reset(struct hda_softc * sc,uint8_t stream_ind)57684659b24SMichael Zeller hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind)
57784659b24SMichael Zeller {
57884659b24SMichael Zeller 	struct hda_stream_desc *st = &sc->streams[stream_ind];
57984659b24SMichael Zeller 	uint32_t off = hda_get_offset_stream(stream_ind);
58084659b24SMichael Zeller 
581154972afSPatrick Mooney 	DPRINTF("Reset the HDA stream: 0x%x", stream_ind);
58284659b24SMichael Zeller 
58384659b24SMichael Zeller 	/* Reset the Stream Descriptor registers */
58484659b24SMichael Zeller 	memset(sc->regs + HDA_STREAM_REGS_BASE + off, 0, HDA_STREAM_REGS_LEN);
58584659b24SMichael Zeller 
58684659b24SMichael Zeller 	/* Reset the Stream Descriptor */
58784659b24SMichael Zeller 	memset(st, 0, sizeof(*st));
58884659b24SMichael Zeller 
58984659b24SMichael Zeller 	hda_set_field_by_offset(sc, off + HDAC_SDSTS,
59084659b24SMichael Zeller 	    HDAC_SDSTS_FIFORDY, HDAC_SDSTS_FIFORDY);
59184659b24SMichael Zeller 	hda_set_field_by_offset(sc, off + HDAC_SDCTL0,
59284659b24SMichael Zeller 	    HDAC_SDCTL_SRST, HDAC_SDCTL_SRST);
59384659b24SMichael Zeller }
59484659b24SMichael Zeller 
59584659b24SMichael Zeller static int
hda_stream_start(struct hda_softc * sc,uint8_t stream_ind)59684659b24SMichael Zeller hda_stream_start(struct hda_softc *sc, uint8_t stream_ind)
59784659b24SMichael Zeller {
59884659b24SMichael Zeller 	struct hda_stream_desc *st = &sc->streams[stream_ind];
59984659b24SMichael Zeller 	struct hda_bdle_desc *bdle_desc = NULL;
60084659b24SMichael Zeller 	struct hda_bdle *bdle = NULL;
60184659b24SMichael Zeller 	uint32_t lvi = 0;
60284659b24SMichael Zeller 	uint32_t bdl_cnt = 0;
60384659b24SMichael Zeller 	uint64_t bdpl = 0;
60484659b24SMichael Zeller 	uint64_t bdpu = 0;
60584659b24SMichael Zeller 	uint64_t bdl_paddr = 0;
60684659b24SMichael Zeller 	void *bdl_vaddr = NULL;
60784659b24SMichael Zeller 	uint32_t bdle_sz = 0;
60884659b24SMichael Zeller 	uint64_t bdle_addrl = 0;
60984659b24SMichael Zeller 	uint64_t bdle_addrh = 0;
61084659b24SMichael Zeller 	uint64_t bdle_paddr = 0;
61184659b24SMichael Zeller 	void *bdle_vaddr = NULL;
61284659b24SMichael Zeller 	uint32_t off = hda_get_offset_stream(stream_ind);
61384659b24SMichael Zeller 	uint32_t sdctl = 0;
61484659b24SMichael Zeller 	uint8_t strm = 0;
61584659b24SMichael Zeller 	uint8_t dir = 0;
61684659b24SMichael Zeller 
61784659b24SMichael Zeller 	assert(!st->run);
61884659b24SMichael Zeller 
61984659b24SMichael Zeller 	lvi = hda_get_reg_by_offset(sc, off + HDAC_SDLVI);
62084659b24SMichael Zeller 	bdpl = hda_get_reg_by_offset(sc, off + HDAC_SDBDPL);
62184659b24SMichael Zeller 	bdpu = hda_get_reg_by_offset(sc, off + HDAC_SDBDPU);
62284659b24SMichael Zeller 
62384659b24SMichael Zeller 	bdl_cnt = lvi + 1;
62484659b24SMichael Zeller 	assert(bdl_cnt <= HDA_BDL_MAX_LEN);
62584659b24SMichael Zeller 
62684659b24SMichael Zeller 	bdl_paddr = bdpl | (bdpu << 32);
62784659b24SMichael Zeller 	bdl_vaddr = hda_dma_get_vaddr(sc, bdl_paddr,
62884659b24SMichael Zeller 	    HDA_BDL_ENTRY_LEN * bdl_cnt);
62984659b24SMichael Zeller 	if (!bdl_vaddr) {
630154972afSPatrick Mooney 		DPRINTF("Fail to get the guest virtual address");
63184659b24SMichael Zeller 		return (-1);
63284659b24SMichael Zeller 	}
63384659b24SMichael Zeller 
634154972afSPatrick Mooney 	DPRINTF("stream: 0x%x bdl_cnt: 0x%x bdl_paddr: 0x%lx",
63584659b24SMichael Zeller 	    stream_ind, bdl_cnt, bdl_paddr);
63684659b24SMichael Zeller 
63784659b24SMichael Zeller 	st->bdl_cnt = bdl_cnt;
63884659b24SMichael Zeller 
63984659b24SMichael Zeller 	bdle = (struct hda_bdle *)bdl_vaddr;
64059d65d31SAndy Fiddaman 	for (size_t i = 0; i < bdl_cnt; i++, bdle++) {
64184659b24SMichael Zeller 		bdle_sz = bdle->len;
64284659b24SMichael Zeller 		assert(!(bdle_sz % HDA_DMA_ACCESS_LEN));
64384659b24SMichael Zeller 
64484659b24SMichael Zeller 		bdle_addrl = bdle->addrl;
64584659b24SMichael Zeller 		bdle_addrh = bdle->addrh;
64684659b24SMichael Zeller 
64784659b24SMichael Zeller 		bdle_paddr = bdle_addrl | (bdle_addrh << 32);
64884659b24SMichael Zeller 		bdle_vaddr = hda_dma_get_vaddr(sc, bdle_paddr, bdle_sz);
64984659b24SMichael Zeller 		if (!bdle_vaddr) {
650154972afSPatrick Mooney 			DPRINTF("Fail to get the guest virtual address");
65184659b24SMichael Zeller 			return (-1);
65284659b24SMichael Zeller 		}
65384659b24SMichael Zeller 
65484659b24SMichael Zeller 		bdle_desc = &st->bdl[i];
65584659b24SMichael Zeller 		bdle_desc->addr = bdle_vaddr;
65684659b24SMichael Zeller 		bdle_desc->len = bdle_sz;
65784659b24SMichael Zeller 		bdle_desc->ioc = bdle->ioc;
65884659b24SMichael Zeller 
65959d65d31SAndy Fiddaman 		DPRINTF("bdle: 0x%zx bdle_sz: 0x%x", i, bdle_sz);
66084659b24SMichael Zeller 	}
66184659b24SMichael Zeller 
66284659b24SMichael Zeller 	sdctl = hda_get_reg_by_offset(sc, off + HDAC_SDCTL0);
66384659b24SMichael Zeller 	strm = (sdctl >> 20) & 0x0f;
66484659b24SMichael Zeller 	dir = stream_ind >= HDA_ISS_NO;
66584659b24SMichael Zeller 
666154972afSPatrick Mooney 	DPRINTF("strm: 0x%x, dir: 0x%x", strm, dir);
66784659b24SMichael Zeller 
66884659b24SMichael Zeller 	sc->stream_map[dir][strm] = stream_ind;
66984659b24SMichael Zeller 	st->stream = strm;
67084659b24SMichael Zeller 	st->dir = dir;
67184659b24SMichael Zeller 	st->bp = 0;
67284659b24SMichael Zeller 	st->be = 0;
67384659b24SMichael Zeller 
67484659b24SMichael Zeller 	hda_set_pib(sc, stream_ind, 0);
67584659b24SMichael Zeller 
67684659b24SMichael Zeller 	st->run = 1;
67784659b24SMichael Zeller 
67884659b24SMichael Zeller 	hda_notify_codecs(sc, 1, strm, dir);
67984659b24SMichael Zeller 
68084659b24SMichael Zeller 	return (0);
68184659b24SMichael Zeller }
68284659b24SMichael Zeller 
68384659b24SMichael Zeller static int
hda_stream_stop(struct hda_softc * sc,uint8_t stream_ind)68484659b24SMichael Zeller hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind)
68584659b24SMichael Zeller {
68684659b24SMichael Zeller 	struct hda_stream_desc *st = &sc->streams[stream_ind];
68784659b24SMichael Zeller 	uint8_t strm = st->stream;
68884659b24SMichael Zeller 	uint8_t dir = st->dir;
68984659b24SMichael Zeller 
690154972afSPatrick Mooney 	DPRINTF("stream: 0x%x, strm: 0x%x, dir: 0x%x", stream_ind, strm, dir);
69184659b24SMichael Zeller 
69284659b24SMichael Zeller 	st->run = 0;
69384659b24SMichael Zeller 
69484659b24SMichael Zeller 	hda_notify_codecs(sc, 0, strm, dir);
69584659b24SMichael Zeller 
69684659b24SMichael Zeller 	return (0);
69784659b24SMichael Zeller }
69884659b24SMichael Zeller 
69984659b24SMichael Zeller static uint32_t
hda_read(struct hda_softc * sc,uint32_t offset)70084659b24SMichael Zeller hda_read(struct hda_softc *sc, uint32_t offset)
70184659b24SMichael Zeller {
70284659b24SMichael Zeller 	if (offset == HDAC_WALCLK)
70384659b24SMichael Zeller 		return (24 * (hda_get_clock_ns() -			\
70484659b24SMichael Zeller 			sc->wall_clock_start) / 1000);
70584659b24SMichael Zeller 
70684659b24SMichael Zeller 	return (hda_get_reg_by_offset(sc, offset));
70784659b24SMichael Zeller }
70884659b24SMichael Zeller 
70984659b24SMichael Zeller static int
hda_write(struct hda_softc * sc,uint32_t offset,uint8_t size,uint32_t value)71084659b24SMichael Zeller hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size, uint32_t value)
71184659b24SMichael Zeller {
71284659b24SMichael Zeller 	uint32_t old = hda_get_reg_by_offset(sc, offset);
71384659b24SMichael Zeller 	uint32_t masks[] = {0x00000000, 0x000000ff, 0x0000ffff,
71484659b24SMichael Zeller 			0x00ffffff, 0xffffffff};
715*32640292SAndy Fiddaman 	hda_set_reg_handler set_reg_handler = NULL;
716*32640292SAndy Fiddaman 
717*32640292SAndy Fiddaman 	if (offset < nitems(hda_set_reg_table))
718*32640292SAndy Fiddaman 		set_reg_handler = hda_set_reg_table[offset];
71984659b24SMichael Zeller 
72084659b24SMichael Zeller 	hda_set_field_by_offset(sc, offset, masks[size], value);
72184659b24SMichael Zeller 
72284659b24SMichael Zeller 	if (set_reg_handler)
72384659b24SMichael Zeller 		set_reg_handler(sc, offset, old);
72484659b24SMichael Zeller 
72584659b24SMichael Zeller 	return (0);
72684659b24SMichael Zeller }
72784659b24SMichael Zeller 
728*32640292SAndy Fiddaman #if DEBUG_HDA == 1
72984659b24SMichael Zeller static inline void
hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl * p)73084659b24SMichael Zeller hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p)
73184659b24SMichael Zeller {
732*32640292SAndy Fiddaman 	DPRINTF("%s size: %d", p->name, p->size);
733*32640292SAndy Fiddaman 	DPRINTF("%s dma_vaddr: %p", p->name, p->dma_vaddr);
734*32640292SAndy Fiddaman 	DPRINTF("%s wp: 0x%x", p->name, p->wp);
735*32640292SAndy Fiddaman 	DPRINTF("%s rp: 0x%x", p->name, p->rp);
73684659b24SMichael Zeller }
737*32640292SAndy Fiddaman #else
738*32640292SAndy Fiddaman static inline void
hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl * p __unused)739*32640292SAndy Fiddaman hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p __unused) {}
740*32640292SAndy Fiddaman #endif
74184659b24SMichael Zeller 
74284659b24SMichael Zeller static int
hda_corb_start(struct hda_softc * sc)74384659b24SMichael Zeller hda_corb_start(struct hda_softc *sc)
74484659b24SMichael Zeller {
74584659b24SMichael Zeller 	struct hda_codec_cmd_ctl *corb = &sc->corb;
74684659b24SMichael Zeller 	uint8_t corbsize = 0;
74784659b24SMichael Zeller 	uint64_t corblbase = 0;
74884659b24SMichael Zeller 	uint64_t corbubase = 0;
74984659b24SMichael Zeller 	uint64_t corbpaddr = 0;
75084659b24SMichael Zeller 
75184659b24SMichael Zeller 	corb->name = "CORB";
75284659b24SMichael Zeller 
75384659b24SMichael Zeller 	corbsize = hda_get_reg_by_offset(sc, HDAC_CORBSIZE) &		\
75484659b24SMichael Zeller 		   HDAC_CORBSIZE_CORBSIZE_MASK;
75584659b24SMichael Zeller 	corb->size = hda_corb_sizes[corbsize];
75684659b24SMichael Zeller 
75784659b24SMichael Zeller 	if (!corb->size) {
758154972afSPatrick Mooney 		DPRINTF("Invalid corb size");
75984659b24SMichael Zeller 		return (-1);
76084659b24SMichael Zeller 	}
76184659b24SMichael Zeller 
76284659b24SMichael Zeller 	corblbase = hda_get_reg_by_offset(sc, HDAC_CORBLBASE);
76384659b24SMichael Zeller 	corbubase = hda_get_reg_by_offset(sc, HDAC_CORBUBASE);
76484659b24SMichael Zeller 
76584659b24SMichael Zeller 	corbpaddr = corblbase | (corbubase << 32);
766154972afSPatrick Mooney 	DPRINTF("CORB dma_paddr: %p", (void *)corbpaddr);
76784659b24SMichael Zeller 
76884659b24SMichael Zeller 	corb->dma_vaddr = hda_dma_get_vaddr(sc, corbpaddr,
76984659b24SMichael Zeller 			HDA_CORB_ENTRY_LEN * corb->size);
77084659b24SMichael Zeller 	if (!corb->dma_vaddr) {
771154972afSPatrick Mooney 		DPRINTF("Fail to get the guest virtual address");
77284659b24SMichael Zeller 		return (-1);
77384659b24SMichael Zeller 	}
77484659b24SMichael Zeller 
77584659b24SMichael Zeller 	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
77684659b24SMichael Zeller 	corb->rp = hda_get_reg_by_offset(sc, HDAC_CORBRP);
77784659b24SMichael Zeller 
77884659b24SMichael Zeller 	corb->run = 1;
77984659b24SMichael Zeller 
78084659b24SMichael Zeller 	hda_print_cmd_ctl_data(corb);
78184659b24SMichael Zeller 
78284659b24SMichael Zeller 	return (0);
78384659b24SMichael Zeller }
78484659b24SMichael Zeller 
78584659b24SMichael Zeller static int
hda_corb_run(struct hda_softc * sc)78684659b24SMichael Zeller hda_corb_run(struct hda_softc *sc)
78784659b24SMichael Zeller {
78884659b24SMichael Zeller 	struct hda_codec_cmd_ctl *corb = &sc->corb;
78984659b24SMichael Zeller 	uint32_t verb = 0;
79084659b24SMichael Zeller 	int err;
79184659b24SMichael Zeller 
79284659b24SMichael Zeller 	corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP);
79384659b24SMichael Zeller 
79484659b24SMichael Zeller 	while (corb->rp != corb->wp && corb->run) {
79584659b24SMichael Zeller 		corb->rp++;
79684659b24SMichael Zeller 		corb->rp %= corb->size;
79784659b24SMichael Zeller 
79859d65d31SAndy Fiddaman 		verb = hda_dma_ld_dword((uint8_t *)corb->dma_vaddr +
79984659b24SMichael Zeller 		    HDA_CORB_ENTRY_LEN * corb->rp);
80084659b24SMichael Zeller 
80184659b24SMichael Zeller 		err = hda_send_command(sc, verb);
80284659b24SMichael Zeller 		assert(!err);
80384659b24SMichael Zeller 	}
80484659b24SMichael Zeller 
80584659b24SMichael Zeller 	hda_set_reg_by_offset(sc, HDAC_CORBRP, corb->rp);
80684659b24SMichael Zeller 
80784659b24SMichael Zeller 	if (corb->run)
80884659b24SMichael Zeller 		hda_response_interrupt(sc);
80984659b24SMichael Zeller 
81084659b24SMichael Zeller 	return (0);
81184659b24SMichael Zeller }
81284659b24SMichael Zeller 
81384659b24SMichael Zeller static int
hda_rirb_start(struct hda_softc * sc)81484659b24SMichael Zeller hda_rirb_start(struct hda_softc *sc)
81584659b24SMichael Zeller {
81684659b24SMichael Zeller 	struct hda_codec_cmd_ctl *rirb = &sc->rirb;
81784659b24SMichael Zeller 	uint8_t rirbsize = 0;
81884659b24SMichael Zeller 	uint64_t rirblbase = 0;
81984659b24SMichael Zeller 	uint64_t rirbubase = 0;
82084659b24SMichael Zeller 	uint64_t rirbpaddr = 0;
82184659b24SMichael Zeller 
82284659b24SMichael Zeller 	rirb->name = "RIRB";
82384659b24SMichael Zeller 
82484659b24SMichael Zeller 	rirbsize = hda_get_reg_by_offset(sc, HDAC_RIRBSIZE) &		\
82584659b24SMichael Zeller 		   HDAC_RIRBSIZE_RIRBSIZE_MASK;
82684659b24SMichael Zeller 	rirb->size = hda_rirb_sizes[rirbsize];
82784659b24SMichael Zeller 
82884659b24SMichael Zeller 	if (!rirb->size) {
829154972afSPatrick Mooney 		DPRINTF("Invalid rirb size");
83084659b24SMichael Zeller 		return (-1);
83184659b24SMichael Zeller 	}
83284659b24SMichael Zeller 
83384659b24SMichael Zeller 	rirblbase = hda_get_reg_by_offset(sc, HDAC_RIRBLBASE);
83484659b24SMichael Zeller 	rirbubase = hda_get_reg_by_offset(sc, HDAC_RIRBUBASE);
83584659b24SMichael Zeller 
83684659b24SMichael Zeller 	rirbpaddr = rirblbase | (rirbubase << 32);
837154972afSPatrick Mooney 	DPRINTF("RIRB dma_paddr: %p", (void *)rirbpaddr);
83884659b24SMichael Zeller 
83984659b24SMichael Zeller 	rirb->dma_vaddr = hda_dma_get_vaddr(sc, rirbpaddr,
84084659b24SMichael Zeller 			HDA_RIRB_ENTRY_LEN * rirb->size);
84184659b24SMichael Zeller 	if (!rirb->dma_vaddr) {
842154972afSPatrick Mooney 		DPRINTF("Fail to get the guest virtual address");
84384659b24SMichael Zeller 		return (-1);
84484659b24SMichael Zeller 	}
84584659b24SMichael Zeller 
84684659b24SMichael Zeller 	rirb->wp = hda_get_reg_by_offset(sc, HDAC_RIRBWP);
84784659b24SMichael Zeller 	rirb->rp = 0x0000;
84884659b24SMichael Zeller 
84984659b24SMichael Zeller 	rirb->run = 1;
85084659b24SMichael Zeller 
85184659b24SMichael Zeller 	hda_print_cmd_ctl_data(rirb);
85284659b24SMichael Zeller 
85384659b24SMichael Zeller 	return (0);
85484659b24SMichael Zeller }
85584659b24SMichael Zeller 
85684659b24SMichael Zeller static void *
hda_dma_get_vaddr(struct hda_softc * sc,uint64_t dma_paddr,size_t len)85784659b24SMichael Zeller hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr, size_t len)
85884659b24SMichael Zeller {
85984659b24SMichael Zeller 	struct pci_devinst *pi = sc->pci_dev;
86084659b24SMichael Zeller 
86184659b24SMichael Zeller 	assert(pi);
86284659b24SMichael Zeller 
86384659b24SMichael Zeller 	return (paddr_guest2host(pi->pi_vmctx, (uintptr_t)dma_paddr, len));
86484659b24SMichael Zeller }
86584659b24SMichael Zeller 
86684659b24SMichael Zeller static void
hda_dma_st_dword(void * dma_vaddr,uint32_t data)86784659b24SMichael Zeller hda_dma_st_dword(void *dma_vaddr, uint32_t data)
86884659b24SMichael Zeller {
86984659b24SMichael Zeller 	*(uint32_t*)dma_vaddr = data;
87084659b24SMichael Zeller }
87184659b24SMichael Zeller 
87284659b24SMichael Zeller static uint32_t
hda_dma_ld_dword(void * dma_vaddr)87384659b24SMichael Zeller hda_dma_ld_dword(void *dma_vaddr)
87484659b24SMichael Zeller {
87584659b24SMichael Zeller 	return (*(uint32_t*)dma_vaddr);
87684659b24SMichael Zeller }
87784659b24SMichael Zeller 
87884659b24SMichael Zeller static inline uint8_t
hda_get_stream_by_offsets(uint32_t offset,uint8_t reg_offset)87984659b24SMichael Zeller hda_get_stream_by_offsets(uint32_t offset, uint8_t reg_offset)
88084659b24SMichael Zeller {
88184659b24SMichael Zeller 	uint8_t stream_ind = (offset - reg_offset) >> 5;
88284659b24SMichael Zeller 
88384659b24SMichael Zeller 	assert(stream_ind < HDA_IOSS_NO);
88484659b24SMichael Zeller 
88584659b24SMichael Zeller 	return (stream_ind);
88684659b24SMichael Zeller }
88784659b24SMichael Zeller 
88884659b24SMichael Zeller static inline uint32_t
hda_get_offset_stream(uint8_t stream_ind)88984659b24SMichael Zeller hda_get_offset_stream(uint8_t stream_ind)
89084659b24SMichael Zeller {
89184659b24SMichael Zeller 	return (stream_ind << 5);
89284659b24SMichael Zeller }
89384659b24SMichael Zeller 
89484659b24SMichael Zeller static void
hda_set_gctl(struct hda_softc * sc,uint32_t offset,uint32_t old __unused)89559d65d31SAndy Fiddaman hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
89684659b24SMichael Zeller {
89784659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
89884659b24SMichael Zeller 
89984659b24SMichael Zeller 	if (!(value & HDAC_GCTL_CRST)) {
90084659b24SMichael Zeller 		hda_reset(sc);
90184659b24SMichael Zeller 	}
90284659b24SMichael Zeller }
90384659b24SMichael Zeller 
90484659b24SMichael Zeller static void
hda_set_statests(struct hda_softc * sc,uint32_t offset,uint32_t old)90584659b24SMichael Zeller hda_set_statests(struct hda_softc *sc, uint32_t offset, uint32_t old)
90684659b24SMichael Zeller {
90784659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
90884659b24SMichael Zeller 
90984659b24SMichael Zeller 	hda_set_reg_by_offset(sc, offset, old);
91084659b24SMichael Zeller 
91184659b24SMichael Zeller 	/* clear the corresponding bits written by the software (guest) */
91284659b24SMichael Zeller 	hda_set_field_by_offset(sc, offset, value & HDA_STATESTS_IRQ_MASK, 0);
91384659b24SMichael Zeller 
91484659b24SMichael Zeller 	hda_update_intr(sc);
91584659b24SMichael Zeller }
91684659b24SMichael Zeller 
91784659b24SMichael Zeller static void
hda_set_corbwp(struct hda_softc * sc,uint32_t offset __unused,uint32_t old __unused)91859d65d31SAndy Fiddaman hda_set_corbwp(struct hda_softc *sc, uint32_t offset __unused,
91959d65d31SAndy Fiddaman     uint32_t old __unused)
92084659b24SMichael Zeller {
92184659b24SMichael Zeller 	hda_corb_run(sc);
92284659b24SMichael Zeller }
92384659b24SMichael Zeller 
92484659b24SMichael Zeller static void
hda_set_corbctl(struct hda_softc * sc,uint32_t offset,uint32_t old)92584659b24SMichael Zeller hda_set_corbctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
92684659b24SMichael Zeller {
92784659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
92884659b24SMichael Zeller 	int err;
92984659b24SMichael Zeller 	struct hda_codec_cmd_ctl *corb = NULL;
93084659b24SMichael Zeller 
93184659b24SMichael Zeller 	if (value & HDAC_CORBCTL_CORBRUN) {
93284659b24SMichael Zeller 		if (!(old & HDAC_CORBCTL_CORBRUN)) {
93384659b24SMichael Zeller 			err = hda_corb_start(sc);
93484659b24SMichael Zeller 			assert(!err);
93584659b24SMichael Zeller 		}
93684659b24SMichael Zeller 	} else {
93784659b24SMichael Zeller 		corb = &sc->corb;
93884659b24SMichael Zeller 		memset(corb, 0, sizeof(*corb));
93984659b24SMichael Zeller 	}
94084659b24SMichael Zeller 
94184659b24SMichael Zeller 	hda_corb_run(sc);
94284659b24SMichael Zeller }
94384659b24SMichael Zeller 
94484659b24SMichael Zeller static void
hda_set_rirbctl(struct hda_softc * sc,uint32_t offset,uint32_t old __unused)94559d65d31SAndy Fiddaman hda_set_rirbctl(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
94684659b24SMichael Zeller {
94784659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
94884659b24SMichael Zeller 	int err;
94984659b24SMichael Zeller 	struct hda_codec_cmd_ctl *rirb = NULL;
95084659b24SMichael Zeller 
95184659b24SMichael Zeller 	if (value & HDAC_RIRBCTL_RIRBDMAEN) {
95284659b24SMichael Zeller 		err = hda_rirb_start(sc);
95384659b24SMichael Zeller 		assert(!err);
95484659b24SMichael Zeller 	} else {
95584659b24SMichael Zeller 		rirb = &sc->rirb;
95684659b24SMichael Zeller 		memset(rirb, 0, sizeof(*rirb));
95784659b24SMichael Zeller 	}
95884659b24SMichael Zeller }
95984659b24SMichael Zeller 
96084659b24SMichael Zeller static void
hda_set_rirbsts(struct hda_softc * sc,uint32_t offset,uint32_t old)96184659b24SMichael Zeller hda_set_rirbsts(struct hda_softc *sc, uint32_t offset, uint32_t old)
96284659b24SMichael Zeller {
96384659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
96484659b24SMichael Zeller 
96584659b24SMichael Zeller 	hda_set_reg_by_offset(sc, offset, old);
96684659b24SMichael Zeller 
96784659b24SMichael Zeller 	/* clear the corresponding bits written by the software (guest) */
96884659b24SMichael Zeller 	hda_set_field_by_offset(sc, offset, value & HDA_RIRBSTS_IRQ_MASK, 0);
96984659b24SMichael Zeller 
97084659b24SMichael Zeller 	hda_update_intr(sc);
97184659b24SMichael Zeller }
97284659b24SMichael Zeller 
97384659b24SMichael Zeller static void
hda_set_dpiblbase(struct hda_softc * sc,uint32_t offset,uint32_t old)97484659b24SMichael Zeller hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset, uint32_t old)
97584659b24SMichael Zeller {
97684659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
97784659b24SMichael Zeller 	uint64_t dpiblbase = 0;
97884659b24SMichael Zeller 	uint64_t dpibubase = 0;
97984659b24SMichael Zeller 	uint64_t dpibpaddr = 0;
98084659b24SMichael Zeller 
98184659b24SMichael Zeller 	if ((value & HDAC_DPLBASE_DPLBASE_DMAPBE) != (old &		\
98284659b24SMichael Zeller 				HDAC_DPLBASE_DPLBASE_DMAPBE)) {
98384659b24SMichael Zeller 		if (value & HDAC_DPLBASE_DPLBASE_DMAPBE) {
98484659b24SMichael Zeller 			dpiblbase = value & HDAC_DPLBASE_DPLBASE_MASK;
98584659b24SMichael Zeller 			dpibubase = hda_get_reg_by_offset(sc, HDAC_DPIBUBASE);
98684659b24SMichael Zeller 
98784659b24SMichael Zeller 			dpibpaddr = dpiblbase | (dpibubase << 32);
988154972afSPatrick Mooney 			DPRINTF("DMA Position In Buffer dma_paddr: %p",
98984659b24SMichael Zeller 			    (void *)dpibpaddr);
99084659b24SMichael Zeller 
99184659b24SMichael Zeller 			sc->dma_pib_vaddr = hda_dma_get_vaddr(sc, dpibpaddr,
99284659b24SMichael Zeller 					HDA_DMA_PIB_ENTRY_LEN * HDA_IOSS_NO);
99384659b24SMichael Zeller 			if (!sc->dma_pib_vaddr) {
99484659b24SMichael Zeller 				DPRINTF("Fail to get the guest \
995154972afSPatrick Mooney 					 virtual address");
99684659b24SMichael Zeller 				assert(0);
99784659b24SMichael Zeller 			}
99884659b24SMichael Zeller 		} else {
999154972afSPatrick Mooney 			DPRINTF("DMA Position In Buffer Reset");
100084659b24SMichael Zeller 			sc->dma_pib_vaddr = NULL;
100184659b24SMichael Zeller 		}
100284659b24SMichael Zeller 	}
100384659b24SMichael Zeller }
100484659b24SMichael Zeller 
100584659b24SMichael Zeller static void
hda_set_sdctl(struct hda_softc * sc,uint32_t offset,uint32_t old)100684659b24SMichael Zeller hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old)
100784659b24SMichael Zeller {
100884659b24SMichael Zeller 	uint8_t stream_ind = hda_get_stream_by_offsets(offset, HDAC_SDCTL0);
100984659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
101084659b24SMichael Zeller 	int err;
101184659b24SMichael Zeller 
1012154972afSPatrick Mooney 	DPRINTF("stream_ind: 0x%x old: 0x%x value: 0x%x",
101384659b24SMichael Zeller 	    stream_ind, old, value);
101484659b24SMichael Zeller 
101584659b24SMichael Zeller 	if (value & HDAC_SDCTL_SRST) {
101684659b24SMichael Zeller 		hda_stream_reset(sc, stream_ind);
101784659b24SMichael Zeller 	}
101884659b24SMichael Zeller 
101984659b24SMichael Zeller 	if ((value & HDAC_SDCTL_RUN) != (old & HDAC_SDCTL_RUN)) {
102084659b24SMichael Zeller 		if (value & HDAC_SDCTL_RUN) {
102184659b24SMichael Zeller 			err = hda_stream_start(sc, stream_ind);
102284659b24SMichael Zeller 			assert(!err);
102384659b24SMichael Zeller 		} else {
102484659b24SMichael Zeller 			err = hda_stream_stop(sc, stream_ind);
102584659b24SMichael Zeller 			assert(!err);
102684659b24SMichael Zeller 		}
102784659b24SMichael Zeller 	}
102884659b24SMichael Zeller }
102984659b24SMichael Zeller 
103084659b24SMichael Zeller static void
hda_set_sdctl2(struct hda_softc * sc,uint32_t offset,uint32_t old __unused)103159d65d31SAndy Fiddaman hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old __unused)
103284659b24SMichael Zeller {
103384659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
103484659b24SMichael Zeller 
103584659b24SMichael Zeller 	hda_set_field_by_offset(sc, offset - 2, 0x00ff0000, value << 16);
103684659b24SMichael Zeller }
103784659b24SMichael Zeller 
103884659b24SMichael Zeller static void
hda_set_sdsts(struct hda_softc * sc,uint32_t offset,uint32_t old)103984659b24SMichael Zeller hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old)
104084659b24SMichael Zeller {
104184659b24SMichael Zeller 	uint32_t value = hda_get_reg_by_offset(sc, offset);
104284659b24SMichael Zeller 
104384659b24SMichael Zeller 	hda_set_reg_by_offset(sc, offset, old);
104484659b24SMichael Zeller 
104584659b24SMichael Zeller 	/* clear the corresponding bits written by the software (guest) */
104684659b24SMichael Zeller 	hda_set_field_by_offset(sc, offset, value & HDA_SDSTS_IRQ_MASK, 0);
104784659b24SMichael Zeller 
104884659b24SMichael Zeller 	hda_update_intr(sc);
104984659b24SMichael Zeller }
105084659b24SMichael Zeller 
105184659b24SMichael Zeller static int
hda_signal_state_change(struct hda_codec_inst * hci)105284659b24SMichael Zeller hda_signal_state_change(struct hda_codec_inst *hci)
105384659b24SMichael Zeller {
105484659b24SMichael Zeller 	struct hda_softc *sc = NULL;
105584659b24SMichael Zeller 	uint32_t sdiwake = 0;
105684659b24SMichael Zeller 
105784659b24SMichael Zeller 	assert(hci);
105884659b24SMichael Zeller 	assert(hci->hda);
105984659b24SMichael Zeller 
1060154972afSPatrick Mooney 	DPRINTF("cad: 0x%x", hci->cad);
106184659b24SMichael Zeller 
106284659b24SMichael Zeller 	sc = hci->hda;
106384659b24SMichael Zeller 	sdiwake = 1 << hci->cad;
106484659b24SMichael Zeller 
106584659b24SMichael Zeller 	hda_set_field_by_offset(sc, HDAC_STATESTS, sdiwake, sdiwake);
106684659b24SMichael Zeller 	hda_update_intr(sc);
106784659b24SMichael Zeller 
106884659b24SMichael Zeller 	return (0);
106984659b24SMichael Zeller }
107084659b24SMichael Zeller 
107184659b24SMichael Zeller static int
hda_response(struct hda_codec_inst * hci,uint32_t response,uint8_t unsol)107284659b24SMichael Zeller hda_response(struct hda_codec_inst *hci, uint32_t response, uint8_t unsol)
107384659b24SMichael Zeller {
107484659b24SMichael Zeller 	struct hda_softc *sc = NULL;
107584659b24SMichael Zeller 	struct hda_codec_cmd_ctl *rirb = NULL;
107684659b24SMichael Zeller 	uint32_t response_ex = 0;
107784659b24SMichael Zeller 	uint8_t rintcnt = 0;
107884659b24SMichael Zeller 
107984659b24SMichael Zeller 	assert(hci);
108084659b24SMichael Zeller 	assert(hci->cad <= HDA_CODEC_MAX);
108184659b24SMichael Zeller 
108284659b24SMichael Zeller 	response_ex = hci->cad | unsol;
108384659b24SMichael Zeller 
108484659b24SMichael Zeller 	sc = hci->hda;
108584659b24SMichael Zeller 	assert(sc);
108684659b24SMichael Zeller 
108784659b24SMichael Zeller 	rirb = &sc->rirb;
108884659b24SMichael Zeller 
108984659b24SMichael Zeller 	if (rirb->run) {
109084659b24SMichael Zeller 		rirb->wp++;
109184659b24SMichael Zeller 		rirb->wp %= rirb->size;
109284659b24SMichael Zeller 
109359d65d31SAndy Fiddaman 		hda_dma_st_dword((uint8_t *)rirb->dma_vaddr +
109459d65d31SAndy Fiddaman 		    HDA_RIRB_ENTRY_LEN * rirb->wp, response);
109559d65d31SAndy Fiddaman 		hda_dma_st_dword((uint8_t *)rirb->dma_vaddr +
109659d65d31SAndy Fiddaman 		    HDA_RIRB_ENTRY_LEN * rirb->wp + 0x04, response_ex);
109784659b24SMichael Zeller 
109884659b24SMichael Zeller 		hda_set_reg_by_offset(sc, HDAC_RIRBWP, rirb->wp);
109984659b24SMichael Zeller 
110084659b24SMichael Zeller 		sc->rirb_cnt++;
110184659b24SMichael Zeller 	}
110284659b24SMichael Zeller 
110384659b24SMichael Zeller 	rintcnt = hda_get_reg_by_offset(sc, HDAC_RINTCNT);
110484659b24SMichael Zeller 	if (sc->rirb_cnt == rintcnt)
110584659b24SMichael Zeller 		hda_response_interrupt(sc);
110684659b24SMichael Zeller 
110784659b24SMichael Zeller 	return (0);
110884659b24SMichael Zeller }
110984659b24SMichael Zeller 
111084659b24SMichael Zeller static int
hda_transfer(struct hda_codec_inst * hci,uint8_t stream,uint8_t dir,uint8_t * buf,size_t count)111184659b24SMichael Zeller hda_transfer(struct hda_codec_inst *hci, uint8_t stream, uint8_t dir,
111259d65d31SAndy Fiddaman     uint8_t *buf, size_t count)
111384659b24SMichael Zeller {
111484659b24SMichael Zeller 	struct hda_softc *sc = NULL;
111584659b24SMichael Zeller 	struct hda_stream_desc *st = NULL;
111684659b24SMichael Zeller 	struct hda_bdle_desc *bdl = NULL;
111784659b24SMichael Zeller 	struct hda_bdle_desc *bdle_desc = NULL;
111884659b24SMichael Zeller 	uint8_t stream_ind = 0;
111984659b24SMichael Zeller 	uint32_t lpib = 0;
112084659b24SMichael Zeller 	uint32_t off = 0;
112184659b24SMichael Zeller 	size_t left = 0;
112284659b24SMichael Zeller 	uint8_t irq = 0;
112384659b24SMichael Zeller 
112484659b24SMichael Zeller 	assert(hci);
112584659b24SMichael Zeller 	assert(hci->hda);
112684659b24SMichael Zeller 	assert(buf);
112784659b24SMichael Zeller 	assert(!(count % HDA_DMA_ACCESS_LEN));
112884659b24SMichael Zeller 
112984659b24SMichael Zeller 	if (!stream) {
1130154972afSPatrick Mooney 		DPRINTF("Invalid stream");
113184659b24SMichael Zeller 		return (-1);
113284659b24SMichael Zeller 	}
113384659b24SMichael Zeller 
113484659b24SMichael Zeller 	sc = hci->hda;
113584659b24SMichael Zeller 
113684659b24SMichael Zeller 	assert(stream < HDA_STREAM_TAGS_CNT);
113784659b24SMichael Zeller 	stream_ind = sc->stream_map[dir][stream];
113884659b24SMichael Zeller 
113984659b24SMichael Zeller 	if (!dir)
114084659b24SMichael Zeller 		assert(stream_ind < HDA_ISS_NO);
114184659b24SMichael Zeller 	else
114284659b24SMichael Zeller 		assert(stream_ind >= HDA_ISS_NO && stream_ind < HDA_IOSS_NO);
114384659b24SMichael Zeller 
114484659b24SMichael Zeller 	st = &sc->streams[stream_ind];
114584659b24SMichael Zeller 	if (!st->run) {
1146154972afSPatrick Mooney 		DPRINTF("Stream 0x%x stopped", stream);
114784659b24SMichael Zeller 		return (-1);
114884659b24SMichael Zeller 	}
114984659b24SMichael Zeller 
115084659b24SMichael Zeller 	assert(st->stream == stream);
115184659b24SMichael Zeller 
115284659b24SMichael Zeller 	off = hda_get_offset_stream(stream_ind);
115384659b24SMichael Zeller 
115484659b24SMichael Zeller 	lpib = hda_get_reg_by_offset(sc, off + HDAC_SDLPIB);
115584659b24SMichael Zeller 
115684659b24SMichael Zeller 	bdl = st->bdl;
115784659b24SMichael Zeller 
115884659b24SMichael Zeller 	assert(st->be < st->bdl_cnt);
115984659b24SMichael Zeller 	assert(st->bp < bdl[st->be].len);
116084659b24SMichael Zeller 
116184659b24SMichael Zeller 	left = count;
116284659b24SMichael Zeller 	while (left) {
116384659b24SMichael Zeller 		bdle_desc = &bdl[st->be];
116484659b24SMichael Zeller 
116584659b24SMichael Zeller 		if (dir)
116659d65d31SAndy Fiddaman 			*(uint32_t *)buf = hda_dma_ld_dword(
116759d65d31SAndy Fiddaman 			    (uint8_t *)bdle_desc->addr + st->bp);
116884659b24SMichael Zeller 		else
116959d65d31SAndy Fiddaman 			hda_dma_st_dword((uint8_t *)bdle_desc->addr +
117059d65d31SAndy Fiddaman 			    st->bp, *(uint32_t *)buf);
117184659b24SMichael Zeller 
117284659b24SMichael Zeller 		buf += HDA_DMA_ACCESS_LEN;
117384659b24SMichael Zeller 		st->bp += HDA_DMA_ACCESS_LEN;
117484659b24SMichael Zeller 		lpib += HDA_DMA_ACCESS_LEN;
117584659b24SMichael Zeller 		left -= HDA_DMA_ACCESS_LEN;
117684659b24SMichael Zeller 
117784659b24SMichael Zeller 		if (st->bp == bdle_desc->len) {
117884659b24SMichael Zeller 			st->bp = 0;
117984659b24SMichael Zeller 			if (bdle_desc->ioc)
118084659b24SMichael Zeller 				irq = 1;
118184659b24SMichael Zeller 			st->be++;
118284659b24SMichael Zeller 			if (st->be == st->bdl_cnt) {
118384659b24SMichael Zeller 				st->be = 0;
118484659b24SMichael Zeller 				lpib = 0;
118584659b24SMichael Zeller 			}
118684659b24SMichael Zeller 			bdle_desc = &bdl[st->be];
118784659b24SMichael Zeller 		}
118884659b24SMichael Zeller 	}
118984659b24SMichael Zeller 
119084659b24SMichael Zeller 	hda_set_pib(sc, stream_ind, lpib);
119184659b24SMichael Zeller 
119284659b24SMichael Zeller 	if (irq) {
119384659b24SMichael Zeller 		hda_set_field_by_offset(sc, off + HDAC_SDSTS,
119484659b24SMichael Zeller 				HDAC_SDSTS_BCIS, HDAC_SDSTS_BCIS);
119584659b24SMichael Zeller 		hda_update_intr(sc);
119684659b24SMichael Zeller 	}
119784659b24SMichael Zeller 
119884659b24SMichael Zeller 	return (0);
119984659b24SMichael Zeller }
120084659b24SMichael Zeller 
120184659b24SMichael Zeller static void
hda_set_pib(struct hda_softc * sc,uint8_t stream_ind,uint32_t pib)120284659b24SMichael Zeller hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib)
120384659b24SMichael Zeller {
120484659b24SMichael Zeller 	uint32_t off = hda_get_offset_stream(stream_ind);
120584659b24SMichael Zeller 
120684659b24SMichael Zeller 	hda_set_reg_by_offset(sc, off + HDAC_SDLPIB, pib);
120784659b24SMichael Zeller 	/* LPIB Alias */
120884659b24SMichael Zeller 	hda_set_reg_by_offset(sc, 0x2000 + off + HDAC_SDLPIB, pib);
120984659b24SMichael Zeller 	if (sc->dma_pib_vaddr)
121059d65d31SAndy Fiddaman 		*(uint32_t *)((uint8_t *)sc->dma_pib_vaddr + stream_ind *
121184659b24SMichael Zeller 		    HDA_DMA_PIB_ENTRY_LEN) = pib;
121284659b24SMichael Zeller }
121384659b24SMichael Zeller 
hda_get_clock_ns(void)121484659b24SMichael Zeller static uint64_t hda_get_clock_ns(void)
121584659b24SMichael Zeller {
121684659b24SMichael Zeller 	struct timespec ts;
121784659b24SMichael Zeller 	int err;
121884659b24SMichael Zeller 
121984659b24SMichael Zeller 	err = clock_gettime(CLOCK_MONOTONIC, &ts);
122084659b24SMichael Zeller 	assert(!err);
122184659b24SMichael Zeller 
122284659b24SMichael Zeller 	return (ts.tv_sec * 1000000000LL + ts.tv_nsec);
122384659b24SMichael Zeller }
122484659b24SMichael Zeller 
122584659b24SMichael Zeller /*
122684659b24SMichael Zeller  * PCI HDA function definitions
122784659b24SMichael Zeller  */
122884659b24SMichael Zeller static int
pci_hda_init(struct pci_devinst * pi,nvlist_t * nvl)1229*32640292SAndy Fiddaman pci_hda_init(struct pci_devinst *pi, nvlist_t *nvl)
123084659b24SMichael Zeller {
123184659b24SMichael Zeller 	struct hda_softc *sc = NULL;
123284659b24SMichael Zeller 
123384659b24SMichael Zeller 	assert(pi != NULL);
123484659b24SMichael Zeller 
123584659b24SMichael Zeller 	pci_set_cfgdata16(pi, PCIR_VENDOR, INTEL_VENDORID);
123684659b24SMichael Zeller 	pci_set_cfgdata16(pi, PCIR_DEVICE, HDA_INTEL_82801G);
123784659b24SMichael Zeller 
123884659b24SMichael Zeller 	pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_MULTIMEDIA_HDA);
123984659b24SMichael Zeller 	pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_MULTIMEDIA);
124084659b24SMichael Zeller 
124184659b24SMichael Zeller 	/* select the Intel HDA mode */
124284659b24SMichael Zeller 	pci_set_cfgdata8(pi, PCIR_HDCTL, 0x01);
124384659b24SMichael Zeller 
124484659b24SMichael Zeller 	/* allocate one BAR register for the Memory address offsets */
124584659b24SMichael Zeller 	pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, HDA_LAST_OFFSET);
124684659b24SMichael Zeller 
124784659b24SMichael Zeller 	/* allocate an IRQ pin for our slot */
124884659b24SMichael Zeller 	pci_lintr_request(pi);
124984659b24SMichael Zeller 
12502b948146SAndy Fiddaman 	sc = hda_init(nvl);
125184659b24SMichael Zeller 	if (!sc)
125284659b24SMichael Zeller 		return (-1);
125384659b24SMichael Zeller 
125484659b24SMichael Zeller 	sc->pci_dev = pi;
125584659b24SMichael Zeller 	pi->pi_arg = sc;
125684659b24SMichael Zeller 
125784659b24SMichael Zeller 	return (0);
125884659b24SMichael Zeller }
125984659b24SMichael Zeller 
126084659b24SMichael Zeller static void
pci_hda_write(struct pci_devinst * pi,int baridx,uint64_t offset,int size,uint64_t value)1261*32640292SAndy Fiddaman pci_hda_write(struct pci_devinst *pi, int baridx, uint64_t offset, int size,
126259d65d31SAndy Fiddaman     uint64_t value)
126384659b24SMichael Zeller {
126484659b24SMichael Zeller 	struct hda_softc *sc = pi->pi_arg;
126584659b24SMichael Zeller 	int err;
126684659b24SMichael Zeller 
126784659b24SMichael Zeller 	assert(sc);
126884659b24SMichael Zeller 	assert(baridx == 0);
126984659b24SMichael Zeller 	assert(size <= 4);
127084659b24SMichael Zeller 
1271154972afSPatrick Mooney 	DPRINTF("offset: 0x%lx value: 0x%lx", offset, value);
127284659b24SMichael Zeller 
127384659b24SMichael Zeller 	err = hda_write(sc, offset, size, value);
127484659b24SMichael Zeller 	assert(!err);
127584659b24SMichael Zeller }
127684659b24SMichael Zeller 
127784659b24SMichael Zeller static uint64_t
pci_hda_read(struct pci_devinst * pi,int baridx,uint64_t offset,int size)1278*32640292SAndy Fiddaman pci_hda_read(struct pci_devinst *pi, int baridx, uint64_t offset, int size)
127984659b24SMichael Zeller {
128084659b24SMichael Zeller 	struct hda_softc *sc = pi->pi_arg;
128184659b24SMichael Zeller 	uint64_t value = 0;
128284659b24SMichael Zeller 
128384659b24SMichael Zeller 	assert(sc);
128484659b24SMichael Zeller 	assert(baridx == 0);
128584659b24SMichael Zeller 	assert(size <= 4);
128684659b24SMichael Zeller 
128784659b24SMichael Zeller 	value = hda_read(sc, offset);
128884659b24SMichael Zeller 
1289154972afSPatrick Mooney 	DPRINTF("offset: 0x%lx value: 0x%lx", offset, value);
129084659b24SMichael Zeller 
129184659b24SMichael Zeller 	return (value);
129284659b24SMichael Zeller }
1293