Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/pci_hda.c
Show All 28 Lines | |||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include <time.h> | #include <time.h> | ||||
#include "pci_hda.h" | #include "pci_hda.h" | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
#include "config.h" | |||||
#include "pci_emul.h" | #include "pci_emul.h" | ||||
#include "hdac_reg.h" | #include "hdac_reg.h" | ||||
/* | /* | ||||
* HDA defines | * HDA defines | ||||
*/ | */ | ||||
#define PCIR_HDCTL 0x40 | #define PCIR_HDCTL 0x40 | ||||
#define INTEL_VENDORID 0x8086 | #define INTEL_VENDORID 0x8086 | ||||
▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static inline void hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, | static inline void hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, | ||||
uint32_t value); | uint32_t value); | ||||
static inline uint32_t hda_get_reg_by_offset(struct hda_softc *sc, | static inline uint32_t hda_get_reg_by_offset(struct hda_softc *sc, | ||||
uint32_t offset); | uint32_t offset); | ||||
static inline void hda_set_field_by_offset(struct hda_softc *sc, | static inline void hda_set_field_by_offset(struct hda_softc *sc, | ||||
uint32_t offset, uint32_t mask, uint32_t value); | uint32_t offset, uint32_t mask, uint32_t value); | ||||
static uint8_t hda_parse_config(const char *opts, const char *key, char *val); | static struct hda_softc *hda_init(nvlist_t *nvl); | ||||
static struct hda_softc *hda_init(const char *opts); | |||||
static void hda_update_intr(struct hda_softc *sc); | static void hda_update_intr(struct hda_softc *sc); | ||||
static void hda_response_interrupt(struct hda_softc *sc); | static void hda_response_interrupt(struct hda_softc *sc); | ||||
static int hda_codec_constructor(struct hda_softc *sc, | static int hda_codec_constructor(struct hda_softc *sc, | ||||
struct hda_codec_class *codec, const char *play, const char *rec, | struct hda_codec_class *codec, const char *play, const char *rec); | ||||
const char *opts); | |||||
static struct hda_codec_class *hda_find_codec_class(const char *name); | static struct hda_codec_class *hda_find_codec_class(const char *name); | ||||
static int hda_send_command(struct hda_softc *sc, uint32_t verb); | static int hda_send_command(struct hda_softc *sc, uint32_t verb); | ||||
static int hda_notify_codecs(struct hda_softc *sc, uint8_t run, | static int hda_notify_codecs(struct hda_softc *sc, uint8_t run, | ||||
uint8_t stream, uint8_t dir); | uint8_t stream, uint8_t dir); | ||||
static void hda_reset(struct hda_softc *sc); | static void hda_reset(struct hda_softc *sc); | ||||
static void hda_reset_regs(struct hda_softc *sc); | static void hda_reset_regs(struct hda_softc *sc); | ||||
static void hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind); | static void hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind); | ||||
Show All 40 Lines | static int hda_transfer(struct hda_codec_inst *hci, uint8_t stream, | ||||
uint8_t dir, void *buf, size_t count); | uint8_t dir, void *buf, size_t count); | ||||
static void hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib); | static void hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib); | ||||
static uint64_t hda_get_clock_ns(void); | static uint64_t hda_get_clock_ns(void); | ||||
/* | /* | ||||
* PCI HDA function declarations | * PCI HDA function declarations | ||||
*/ | */ | ||||
static int pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts); | static int pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl); | ||||
static void pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | static void pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
int baridx, uint64_t offset, int size, uint64_t value); | int baridx, uint64_t offset, int size, uint64_t value); | ||||
static uint64_t pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | static uint64_t pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, | ||||
int baridx, uint64_t offset, int size); | int baridx, uint64_t offset, int size); | ||||
/* | /* | ||||
* HDA global data | * HDA global data | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 91 Lines • ▼ Show 20 Lines | hda_set_field_by_offset(struct hda_softc *sc, uint32_t offset, | ||||
reg_value = hda_get_reg_by_offset(sc, offset); | reg_value = hda_get_reg_by_offset(sc, offset); | ||||
reg_value &= ~mask; | reg_value &= ~mask; | ||||
reg_value |= (value & mask); | reg_value |= (value & mask); | ||||
hda_set_reg_by_offset(sc, offset, reg_value); | hda_set_reg_by_offset(sc, offset, reg_value); | ||||
} | } | ||||
static uint8_t | |||||
hda_parse_config(const char *opts, const char *key, char *val) | |||||
{ | |||||
char buf[64]; | |||||
char *s = buf; | |||||
char *tmp = NULL; | |||||
size_t len; | |||||
int i; | |||||
if (!opts) | |||||
return (0); | |||||
len = strlen(opts); | |||||
if (len >= sizeof(buf)) { | |||||
DPRINTF("Opts too big"); | |||||
return (0); | |||||
} | |||||
DPRINTF("opts: %s", opts); | |||||
strcpy(buf, opts); | |||||
for (i = 0; i < len; i++) | |||||
if (buf[i] == ',') { | |||||
buf[i] = 0; | |||||
tmp = buf + i + 1; | |||||
break; | |||||
} | |||||
if (!memcmp(s, key, strlen(key))) { | |||||
strncpy(val, s + strlen(key), 64); | |||||
return (1); | |||||
} | |||||
if (!tmp) | |||||
return (0); | |||||
s = tmp; | |||||
if (!memcmp(s, key, strlen(key))) { | |||||
strncpy(val, s + strlen(key), 64); | |||||
return (1); | |||||
} | |||||
return (0); | |||||
} | |||||
static struct hda_softc * | static struct hda_softc * | ||||
hda_init(const char *opts) | hda_init(nvlist_t *nvl) | ||||
{ | { | ||||
struct hda_softc *sc = NULL; | struct hda_softc *sc = NULL; | ||||
struct hda_codec_class *codec = NULL; | struct hda_codec_class *codec = NULL; | ||||
char play[64]; | const char *value; | ||||
char rec[64]; | char *play; | ||||
int err, p, r; | char *rec; | ||||
int err; | |||||
#if DEBUG_HDA == 1 | #if DEBUG_HDA == 1 | ||||
dbg = fopen("/tmp/bhyve_hda.log", "w+"); | dbg = fopen("/tmp/bhyve_hda.log", "w+"); | ||||
#endif | #endif | ||||
DPRINTF("opts: %s", opts); | |||||
sc = calloc(1, sizeof(*sc)); | sc = calloc(1, sizeof(*sc)); | ||||
if (!sc) | if (!sc) | ||||
return (NULL); | return (NULL); | ||||
hda_reset_regs(sc); | hda_reset_regs(sc); | ||||
/* | /* | ||||
* TODO search all the codecs declared in opts | * TODO search all configured codecs | ||||
* For now we play with one single codec | * For now we play with one single codec | ||||
*/ | */ | ||||
codec = hda_find_codec_class("hda_codec"); | codec = hda_find_codec_class("hda_codec"); | ||||
if (codec) { | if (codec) { | ||||
p = hda_parse_config(opts, "play=", play); | value = get_config_value_node(nvl, "play"); | ||||
r = hda_parse_config(opts, "rec=", rec); | if (value == NULL) | ||||
play = NULL; | |||||
else | |||||
play = strdup(value); | |||||
value = get_config_value_node(nvl, "rec"); | |||||
if (value == NULL) | |||||
rec = NULL; | |||||
else | |||||
rec = strdup(value); | |||||
DPRINTF("play: %s rec: %s", play, rec); | DPRINTF("play: %s rec: %s", play, rec); | ||||
if (p | r) { | if (play != NULL || rec != NULL) { | ||||
err = hda_codec_constructor(sc, codec, p ? \ | err = hda_codec_constructor(sc, codec, play, rec); | ||||
play : NULL, r ? rec : NULL, NULL); | |||||
assert(!err); | assert(!err); | ||||
} | } | ||||
free(play); | |||||
free(rec); | |||||
} | } | ||||
return (sc); | return (sc); | ||||
} | } | ||||
static void | static void | ||||
hda_update_intr(struct hda_softc *sc) | hda_update_intr(struct hda_softc *sc) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | if ((rirbctl & HDAC_RIRBCTL_RINTCTL) && sc->rirb_cnt) { | ||||
hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL, | hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL, | ||||
HDAC_RIRBSTS_RINTFL); | HDAC_RIRBSTS_RINTFL); | ||||
hda_update_intr(sc); | hda_update_intr(sc); | ||||
} | } | ||||
} | } | ||||
static int | static int | ||||
hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec, | hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec, | ||||
const char *play, const char *rec, const char *opts) | const char *play, const char *rec) | ||||
{ | { | ||||
struct hda_codec_inst *hci = NULL; | struct hda_codec_inst *hci = NULL; | ||||
if (sc->codecs_no >= HDA_CODEC_MAX) | if (sc->codecs_no >= HDA_CODEC_MAX) | ||||
return (-1); | return (-1); | ||||
hci = calloc(1, sizeof(struct hda_codec_inst)); | hci = calloc(1, sizeof(struct hda_codec_inst)); | ||||
if (!hci) | if (!hci) | ||||
return (-1); | return (-1); | ||||
hci->hda = sc; | hci->hda = sc; | ||||
hci->hops = &hops; | hci->hops = &hops; | ||||
hci->cad = sc->codecs_no; | hci->cad = sc->codecs_no; | ||||
hci->codec = codec; | hci->codec = codec; | ||||
sc->codecs[sc->codecs_no++] = hci; | sc->codecs[sc->codecs_no++] = hci; | ||||
if (!codec->init) { | if (!codec->init) { | ||||
DPRINTF("This codec does not implement the init function"); | DPRINTF("This codec does not implement the init function"); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
return (codec->init(hci, play, rec, opts)); | return (codec->init(hci, play, rec)); | ||||
} | } | ||||
static struct hda_codec_class * | static struct hda_codec_class * | ||||
hda_find_codec_class(const char *name) | hda_find_codec_class(const char *name) | ||||
{ | { | ||||
struct hda_codec_class **pdpp = NULL, *pdp = NULL; | struct hda_codec_class **pdpp = NULL, *pdp = NULL; | ||||
SET_FOREACH(pdpp, hda_codec_class_set) { | SET_FOREACH(pdpp, hda_codec_class_set) { | ||||
▲ Show 20 Lines • Show All 753 Lines • ▼ Show 20 Lines | static uint64_t hda_get_clock_ns(void) | ||||
return (ts.tv_sec * 1000000000LL + ts.tv_nsec); | return (ts.tv_sec * 1000000000LL + ts.tv_nsec); | ||||
} | } | ||||
/* | /* | ||||
* PCI HDA function definitions | * PCI HDA function definitions | ||||
*/ | */ | ||||
static int | static int | ||||
pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) | pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, nvlist_t *nvl) | ||||
{ | { | ||||
struct hda_softc *sc = NULL; | struct hda_softc *sc = NULL; | ||||
assert(ctx != NULL); | assert(ctx != NULL); | ||||
assert(pi != NULL); | assert(pi != NULL); | ||||
pci_set_cfgdata16(pi, PCIR_VENDOR, INTEL_VENDORID); | pci_set_cfgdata16(pi, PCIR_VENDOR, INTEL_VENDORID); | ||||
pci_set_cfgdata16(pi, PCIR_DEVICE, HDA_INTEL_82801G); | pci_set_cfgdata16(pi, PCIR_DEVICE, HDA_INTEL_82801G); | ||||
pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_MULTIMEDIA_HDA); | pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_MULTIMEDIA_HDA); | ||||
pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_MULTIMEDIA); | pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_MULTIMEDIA); | ||||
/* select the Intel HDA mode */ | /* select the Intel HDA mode */ | ||||
pci_set_cfgdata8(pi, PCIR_HDCTL, 0x01); | pci_set_cfgdata8(pi, PCIR_HDCTL, 0x01); | ||||
/* allocate one BAR register for the Memory address offsets */ | /* allocate one BAR register for the Memory address offsets */ | ||||
pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, HDA_LAST_OFFSET); | pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, HDA_LAST_OFFSET); | ||||
/* allocate an IRQ pin for our slot */ | /* allocate an IRQ pin for our slot */ | ||||
pci_lintr_request(pi); | pci_lintr_request(pi); | ||||
sc = hda_init(opts); | sc = hda_init(nvl); | ||||
if (!sc) | if (!sc) | ||||
return (-1); | return (-1); | ||||
sc->pci_dev = pi; | sc->pci_dev = pi; | ||||
pi->pi_arg = sc; | pi->pi_arg = sc; | ||||
return (0); | return (0); | ||||
} | } | ||||
Show All 35 Lines |