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 | |||||