Index: Makefile =================================================================== --- Makefile +++ Makefile @@ -14,12 +14,14 @@ SRCS= \ atkbdc.c \ acpi.c \ + audio.c \ bhyverun.c \ block_if.c \ bootrom.c \ consport.c \ dbgport.c \ fwctl.c \ + hda_codec.c \ inout.c \ ioapic.c \ mem.c \ @@ -27,6 +29,7 @@ mptbl.c \ pci_ahci.c \ pci_emul.c \ + pci_hda.c \ pci_hostbridge.c \ pci_irq.c \ pci_lpc.c \ Index: audio.h =================================================================== --- audio.h +++ audio.h @@ -0,0 +1,58 @@ + +#ifndef _AUDIO_EMUL_H_ +#define _AUDIO_EMUL_H_ + +#include +#include + +/* + * Audio Player data structures + */ + +struct audio; + +struct audio_params { + int format; + int channels; + int rate; +}; + +/* + * Audio Player API + */ + +/* + * audio_init - initialize an instance of audio player + * @dev_name - the backend sound device used to play / capture + * @dir - dir = 1 for write mode, dir = 0 for read mode + * Returns NULL on error and the address of the audio player instance + */ +struct audio *audio_init(const char *dev_name, uint8_t dir); + +/* + * audio_set_params - reset the sound device and set the audio params + * @aud - the audio player to be configured + * @params - the audio parameters to be set + * Returns -1 on error and 0 on success + */ +int audio_set_params(struct audio *aud, struct audio_params *params); + +/* + * audio_playback - plays samples to the sound device using blocking operations + * @aud - the audio player used to play the samples + * @buf - the buffer containing the samples + * @count - the number of bytes in buffer + * Returns -1 on error and 0 on success + */ +int audio_playback(struct audio *aud, const void *buf, size_t count); + +/* + * audio_record - records samples from the sound device using blocking operations + * @aud - the audio player used to capture the samples + * @buf - the buffer to receive the samples + * @count - the number of bytes to capture in buffer + * Returns -1 on error and 0 on success + */ +int audio_record(struct audio *aud, void *buf, size_t count); + +#endif /* _AUDIO_EMUL_H_ */ Index: audio.c =================================================================== --- audio.c +++ audio.c @@ -0,0 +1,207 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "audio.h" +#include "pci_hda.h" + +/* + * Audio Player internal data structures + */ + +struct audio { + int fd; + uint8_t dir; + char dev_name[64]; +}; + +/* + * Audio Player module function definitions + */ + +/* + * audio_init - initialize an instance of audio player + * @dev_name - the backend sound device used to play / capture + * @dir - dir = 1 for write mode, dir = 0 for read mode + */ +struct audio *audio_init(const char *dev_name, uint8_t dir) +{ + struct audio *aud = NULL; + + assert(dev_name); + + aud = calloc(1, sizeof(*aud)); + if (!aud) + return NULL; + + if (strlen(dev_name) < sizeof(aud->dev_name)) + memcpy(aud->dev_name, dev_name, strlen(dev_name) + 1); + else { + DPRINTF("dev_name too big\n"); + free(aud); + return NULL; + } + + aud->fd = -1; + aud->dir = dir; + + return aud; +} + +/* + * audio_set_params - reset the sound device and set the audio params + * @aud - the audio player to be configured + * @params - the audio parameters to be set + */ +int audio_set_params(struct audio *aud, struct audio_params *params) +{ + int audio_fd = -1; + int format, channels, rate; + int err; +#if DEBUG_HDA == 1 + audio_buf_info info; +#endif + + assert(aud); + assert(params); + + /* Close the device if was opened before */ + if (aud->fd != -1) { + err = close(aud->fd); + if (err == -1) { + DPRINTF("Fail to close fd: %d, errno: %d\n", aud->fd, errno); + return -1; + } + } + + audio_fd = open(aud->dev_name, aud->dir ? O_WRONLY : O_RDONLY, 0); + if (audio_fd == -1) { + DPRINTF("Fail to open dev: %s, errno: %d\n", aud->dev_name, errno); + return -1; + } + + aud->fd = audio_fd; + + /* Set the Format (Bits per Sample) */ + format = params->format; + err = ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format); + if (err == -1) { + DPRINTF("Fail to set fmt: 0x%x errno: %d\n", params->format, errno); + return -1; + } + + /* The device does not support the requested audio format */ + if (format != params->format) { + DPRINTF("Mismatch format: 0x%x params->format: 0x%x\n", format, params->format); + return -1; + } + + /* Set the Number of Channels */ + channels = params->channels; + err = ioctl(audio_fd, SNDCTL_DSP_CHANNELS, &channels); + if (err == -1) { + DPRINTF("Fail to set channels: %d errno: %d\n", params->channels, errno); + return -1; + } + + /* The device does not support the requested no. of channels */ + if (channels != params->channels) { + DPRINTF("Mismatch channels: %d params->channels: %d\n", channels, params->channels); + return -1; + } + + /* Set the Sample Rate / Speed */ + rate = params->rate; + err = ioctl(audio_fd, SNDCTL_DSP_SPEED, &rate); + if (err == -1) { + DPRINTF("Fail to set speed: %d errno: %d\n", params->rate, errno); + return -1; + } + + /* The device does not support the requested rate / speed */ + if (rate != params->rate) { + DPRINTF("Mismatch rate: %d params->rate: %d\n", rate, params->rate); + return -1; + } + +#if DEBUG_HDA == 1 + err = ioctl(audio_fd, aud->dir ? SNDCTL_DSP_GETOSPACE : SNDCTL_DSP_GETISPACE, &info); + if (err == -1) { + DPRINTF("Fail to get audio buf info errno: %d\n", errno); + return -1; + } + DPRINTF("fragstotal: 0x%x fragsize: 0x%x\n", info.fragstotal, info.fragsize); +#endif + return 0; +} + +/* + * audio_playback - plays samples to the sound device using blocking operations + * @aud - the audio player used to play the samples + * @buf - the buffer containing the samples + * @count - the number of bytes in buffer + */ +int audio_playback(struct audio *aud, const void *buf, size_t count) +{ + int audio_fd = -1; + ssize_t len = 0, total = 0; + + assert(aud); + assert(aud->dir); + assert(buf); + + audio_fd = aud->fd; + assert(audio_fd != -1); + + total = 0; + while (total < count) { + len = write(audio_fd, buf + total, count - total); + if (len == -1) { + DPRINTF("Fail to write to fd: %d, errno: %d\n", audio_fd, errno); + return -1; + } + + total += len; + } + + return 0; +} + +/* + * audio_record - records samples from the sound device using blocking operations + * @aud - the audio player used to capture the samples + * @buf - the buffer to receive the samples + * @count - the number of bytes to capture in buffer + * Returns -1 on error and 0 on success + */ +int audio_record(struct audio *aud, void *buf, size_t count) +{ + int audio_fd = -1; + ssize_t len = 0, total = 0; + + assert(aud); + assert(!aud->dir); + assert(buf); + + audio_fd = aud->fd; + assert(audio_fd != -1); + + total = 0; + while (total < count) { + len = read(audio_fd, buf + total, count - total); + if (len == -1) { + DPRINTF("Fail to write to fd: %d, errno: %d\n", audio_fd, errno); + return -1; + } + + total += len; + } + + return 0; +} + Index: hda_codec.c =================================================================== --- hda_codec.c +++ hda_codec.c @@ -0,0 +1,868 @@ + +#include +#include +#include + +#include "pci_hda.h" +#include "audio.h" + +/* + * HDA Codec defines + */ +#define INTEL_VENDORID 0x8086 + +#define HDA_CODEC_SUBSYSTEM_ID ((INTEL_VENDORID << 16) | 0x01) +#define HDA_CODEC_ROOT_NID 0x00 +#define HDA_CODEC_FG_NID 0x01 +#define HDA_CODEC_AUDIO_OUTPUT_NID 0x02 +#define HDA_CODEC_PIN_OUTPUT_NID 0x03 +#define HDA_CODEC_AUDIO_INPUT_NID 0x04 +#define HDA_CODEC_PIN_INPUT_NID 0x05 + +#define HDA_CODEC_STREAMS_COUNT 0x02 +#define HDA_CODEC_STREAM_OUTPUT 0x00 +#define HDA_CODEC_STREAM_INPUT 0x01 + +#define HDA_CODEC_PARAMS_COUNT 0x14 +#define HDA_CODEC_CONN_LIST_COUNT 0x01 +#define HDA_CODEC_RESPONSE_EX_UNSOL 0x10 +#define HDA_CODEC_RESPONSE_EX_SOL 0x00 +#define HDA_CODEC_AMP_NUMSTEPS 0x4a + +#define HDA_CODEC_SUPP_STREAM_FORMATS_PCM (1 << HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT) + +#define HDA_CODEC_FMT_BASE_MASK (0x01 << 14) + +#define HDA_CODEC_FMT_MULT_MASK (0x07 << 11) +#define HDA_CODEC_FMT_MULT_2 (0x01 << 11) +#define HDA_CODEC_FMT_MULT_3 (0x02 << 11) +#define HDA_CODEC_FMT_MULT_4 (0x03 << 11) + +#define HDA_CODEC_FMT_DIV_MASK 0x07 +#define HDA_CODEC_FMT_DIV_SHIFT 8 + +#define HDA_CODEC_FMT_BITS_MASK (0x07 << 4) +#define HDA_CODEC_FMT_BITS_8 (0x00 << 4) +#define HDA_CODEC_FMT_BITS_16 (0x01 << 4) +#define HDA_CODEC_FMT_BITS_24 (0x03 << 4) +#define HDA_CODEC_FMT_BITS_32 (0x04 << 4) + +#define HDA_CODEC_FMT_CHAN_MASK (0x0f << 0) + +#define HDA_CODEC_AUDIO_WCAP_OUTPUT (0x00 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_INPUT (0x01 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_PIN (0x04 << HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_CONN_LIST (1 << HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_FORMAT_OVR (1 << HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_AMP_OVR (1 << HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_OUT_AMP (1 << HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_IN_AMP (1 << HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT) +#define HDA_CODEC_AUDIO_WCAP_STEREO (1 << HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT) + +#define HDA_CODEC_PIN_CAP_OUTPUT (1 << HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT) +#define HDA_CODEC_PIN_CAP_INPUT (1 << HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT) +#define HDA_CODEC_PIN_CAP_PRESENCE_DETECT (1 << HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT) + +#define HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP (1 << HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE (0x03 << HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS (HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_CODEC_OUTPUT_AMP_CAP_OFFSET (HDA_CODEC_AMP_NUMSTEPS << HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT) + +#define HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE 0x80 +#define HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK 0x7f + +#define HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED (1 << 31) +#define HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE (1 << HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT) +#define HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE (1 << HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT) + +#define HDA_CONFIG_DEFAULTCONF_COLOR_BLACK (0x01 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_COLOR_RED (0x05 << HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) + +#define HDA_CODEC_BUF_SIZE HDA_FIFO_SIZE + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) + + +/* + * HDA Audio Context data structures + */ + +typedef void (*transfer_func_t)(void *arg); +typedef int (*setup_func_t)(void *arg); + +struct hda_audio_ctxt { + uint8_t started; + char name[64]; + uint8_t run; + pthread_t tid; + pthread_mutex_t mtx; + pthread_cond_t cond; + transfer_func_t do_transfer; + setup_func_t do_setup; + void *priv; +}; + +/* + * HDA Audio Context module function declarations + */ + +static void * +hda_audio_ctxt_thr(void *arg); +static int +hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname, transfer_func_t do_transfer, setup_func_t do_setup, void *priv); +static int +hda_audio_ctxt_start(struct hda_audio_ctxt *actx); +static int +hda_audio_ctxt_stop(struct hda_audio_ctxt *actx); + +/* + * HDA Codec data structures + */ + +struct hda_codec_softc; + +typedef uint32_t (*verb_func_t)(struct hda_codec_softc *sc, uint16_t verb, uint16_t payload); + +struct hda_codec_stream { + uint8_t buf[HDA_CODEC_BUF_SIZE]; + uint8_t stream; + uint8_t channel; + uint16_t fmt; + + uint8_t left_gain; + uint8_t right_gain; + uint8_t left_mute; + uint8_t right_mute; + + struct hda_audio_ctxt actx; + struct audio *aud; +}; + +struct hda_codec_softc { + struct hda_codec_inst *hci; + uint32_t subsystem_id; + uint32_t no_nodes; + const uint32_t (*get_parameters)[HDA_CODEC_PARAMS_COUNT]; + const uint8_t (*conn_list)[HDA_CODEC_CONN_LIST_COUNT]; + const uint32_t *conf_default; + const uint8_t *pin_ctrl_default; + const verb_func_t *verb_handlers; + + struct hda_codec_stream streams[HDA_CODEC_STREAMS_COUNT]; +}; + +/* + * HDA Codec module function declarations + */ +static int +hda_codec_init(struct hda_codec_inst *hci, const char *play, const char *rec, const char *opts); +static int +hda_codec_reset(struct hda_codec_inst *hci); +static int +hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data); +static int +hda_codec_notify(struct hda_codec_inst *hci, uint8_t run, uint8_t stream, uint8_t dir); + +static int hda_codec_parse_format(uint16_t fmt, struct audio_params *params); + +static uint32_t +hda_codec_audio_output_nid(struct hda_codec_softc *sc, uint16_t verb, uint16_t payload); +static void +hda_codec_audio_output_do_transfer(void *arg); +static int +hda_codec_audio_output_do_setup(void *arg); +static uint32_t +hda_codec_audio_input_nid(struct hda_codec_softc *sc, uint16_t verb, uint16_t payload); +static void +hda_codec_audio_input_do_transfer(void *arg); +static int +hda_codec_audio_input_do_setup(void *arg); + +static uint32_t +hda_codec_audio_inout_nid(struct hda_codec_stream *st, uint16_t verb, uint16_t payload); + +/* + * HDA Codec global data + */ + +#define HDA_CODEC_ROOT_DESC \ + [HDA_CODEC_ROOT_NID] = { \ + [HDA_PARAM_VENDOR_ID] = INTEL_VENDORID, \ + [HDA_PARAM_REVISION_ID] = 0xffff, \ + [HDA_PARAM_SUB_NODE_COUNT] = 0x00010001, /* 1 Subnode, StartNid = 1 */ \ + }, \ + +#define HDA_CODEC_FG_COMMON_DESC \ + [HDA_PARAM_FCT_GRP_TYPE] = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO, \ + [HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x1f << 16) | 0x7ff, /* B8 - B32, 8.0 - 192.0kHz */ \ + [HDA_PARAM_SUPP_STREAM_FORMATS] = HDA_CODEC_SUPP_STREAM_FORMATS_PCM, \ + [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \ + [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \ + [HDA_PARAM_GPIO_COUNT] = 0x00, \ + +#define HDA_CODEC_FG_OUTPUT_DESC \ + [HDA_CODEC_FG_NID] = { \ + [HDA_PARAM_SUB_NODE_COUNT] = 0x00020002, /* 2 Subnodes, StartNid = 2 */ \ + HDA_CODEC_FG_COMMON_DESC \ + }, \ + +#define HDA_CODEC_FG_INPUT_DESC \ + [HDA_CODEC_FG_NID] = { \ + [HDA_PARAM_SUB_NODE_COUNT] = 0x00040002, /* 2 Subnodes, StartNid = 4 */ \ + HDA_CODEC_FG_COMMON_DESC \ + }, \ + +#define HDA_CODEC_FG_DUPLEX_DESC \ + [HDA_CODEC_FG_NID] = { \ + [HDA_PARAM_SUB_NODE_COUNT] = 0x00020004, /* 4 Subnodes, StartNid = 2 */ \ + HDA_CODEC_FG_COMMON_DESC \ + }, \ + +#define HDA_CODEC_OUTPUT_DESC \ + [HDA_CODEC_AUDIO_OUTPUT_NID] = { \ + [HDA_PARAM_AUDIO_WIDGET_CAP] = HDA_CODEC_AUDIO_WCAP_OUTPUT | \ + HDA_CODEC_AUDIO_WCAP_FORMAT_OVR | \ + HDA_CODEC_AUDIO_WCAP_AMP_OVR | \ + HDA_CODEC_AUDIO_WCAP_OUT_AMP | \ + HDA_CODEC_AUDIO_WCAP_STEREO, \ + [HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc, /* B16, 16.0 - 192.0kHz */ \ + [HDA_PARAM_SUPP_STREAM_FORMATS] = HDA_CODEC_SUPP_STREAM_FORMATS_PCM, \ + [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \ + [HDA_PARAM_CONN_LIST_LENGTH] = 0x00, \ + [HDA_PARAM_OUTPUT_AMP_CAP] = HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP | \ + HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE | \ + HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS | \ + HDA_CODEC_OUTPUT_AMP_CAP_OFFSET, \ + }, \ + [HDA_CODEC_PIN_OUTPUT_NID] = { \ + [HDA_PARAM_AUDIO_WIDGET_CAP] = HDA_CODEC_AUDIO_WCAP_PIN | \ + HDA_CODEC_AUDIO_WCAP_CONN_LIST | \ + HDA_CODEC_AUDIO_WCAP_STEREO, \ + [HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_OUTPUT | \ + HDA_CODEC_PIN_CAP_PRESENCE_DETECT, \ + [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \ + [HDA_PARAM_CONN_LIST_LENGTH] = 0x01, \ + [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \ + }, \ + +#define HDA_CODEC_INPUT_DESC \ + [HDA_CODEC_AUDIO_INPUT_NID] = { \ + [HDA_PARAM_AUDIO_WIDGET_CAP] = HDA_CODEC_AUDIO_WCAP_INPUT | \ + HDA_CODEC_AUDIO_WCAP_CONN_LIST | \ + HDA_CODEC_AUDIO_WCAP_FORMAT_OVR | \ + HDA_CODEC_AUDIO_WCAP_AMP_OVR | \ + HDA_CODEC_AUDIO_WCAP_IN_AMP | \ + HDA_CODEC_AUDIO_WCAP_STEREO, \ + [HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc, /* B16, 16.0 - 192.0kHz */ \ + [HDA_PARAM_SUPP_STREAM_FORMATS] = HDA_CODEC_SUPP_STREAM_FORMATS_PCM, \ + [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \ + [HDA_PARAM_CONN_LIST_LENGTH] = 0x01, \ + [HDA_PARAM_INPUT_AMP_CAP] = HDA_CODEC_OUTPUT_AMP_CAP_MUTE_CAP | \ + HDA_CODEC_OUTPUT_AMP_CAP_STEPSIZE | \ + HDA_CODEC_OUTPUT_AMP_CAP_NUMSTEPS | \ + HDA_CODEC_OUTPUT_AMP_CAP_OFFSET, \ + }, \ + [HDA_CODEC_PIN_INPUT_NID] = { \ + [HDA_PARAM_AUDIO_WIDGET_CAP] = HDA_CODEC_AUDIO_WCAP_PIN | \ + HDA_CODEC_AUDIO_WCAP_STEREO, \ + [HDA_PARAM_PIN_CAP] = HDA_CODEC_PIN_CAP_INPUT | \ + HDA_CODEC_PIN_CAP_PRESENCE_DETECT, \ + [HDA_PARAM_INPUT_AMP_CAP] = 0x00, /* None */ \ + [HDA_PARAM_OUTPUT_AMP_CAP] = 0x00, /* None */ \ + }, \ + +static const uint32_t hda_codec_output_parameters[][HDA_CODEC_PARAMS_COUNT] = { + HDA_CODEC_ROOT_DESC + HDA_CODEC_FG_OUTPUT_DESC + HDA_CODEC_OUTPUT_DESC +}; + +static const uint32_t hda_codec_input_parameters[][HDA_CODEC_PARAMS_COUNT] = { + HDA_CODEC_ROOT_DESC + HDA_CODEC_FG_INPUT_DESC + HDA_CODEC_INPUT_DESC +}; + +static const uint32_t hda_codec_duplex_parameters[][HDA_CODEC_PARAMS_COUNT] = { + HDA_CODEC_ROOT_DESC + HDA_CODEC_FG_DUPLEX_DESC + HDA_CODEC_OUTPUT_DESC + HDA_CODEC_INPUT_DESC +}; + +#define HDA_CODEC_NODES_COUNT (ARRAY_SIZE(hda_codec_duplex_parameters)) + +static const uint8_t hda_codec_conn_list[HDA_CODEC_NODES_COUNT][HDA_CODEC_CONN_LIST_COUNT] = { + [HDA_CODEC_PIN_OUTPUT_NID] = {HDA_CODEC_AUDIO_OUTPUT_NID}, + [HDA_CODEC_AUDIO_INPUT_NID] = {HDA_CODEC_PIN_INPUT_NID}, +}; + +static const uint32_t hda_codec_conf_default[HDA_CODEC_NODES_COUNT] = { + [HDA_CODEC_PIN_OUTPUT_NID] = HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK | + HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT | + HDA_CONFIG_DEFAULTCONF_COLOR_BLACK | + (0x01 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT), + [HDA_CODEC_PIN_INPUT_NID] = HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK | + HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN | + HDA_CONFIG_DEFAULTCONF_COLOR_RED | + (0x02 << HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT), +}; + +static const uint8_t hda_codec_pin_ctrl_default[HDA_CODEC_NODES_COUNT] = { + [HDA_CODEC_PIN_OUTPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_OUT_ENABLE, + [HDA_CODEC_PIN_INPUT_NID] = HDA_CODEC_PIN_WIDGET_CTRL_IN_ENABLE, +}; + +static const verb_func_t hda_codec_verb_handlers[HDA_CODEC_NODES_COUNT] = { + [HDA_CODEC_AUDIO_OUTPUT_NID] = hda_codec_audio_output_nid, + [HDA_CODEC_AUDIO_INPUT_NID] = hda_codec_audio_input_nid, +}; + +/* + * HDA Codec module function definitions + */ + +static int +hda_codec_init(struct hda_codec_inst *hci, const char *play, const char *rec, const char *opts) +{ + struct hda_codec_softc *sc = NULL; + struct hda_codec_stream *st = NULL; + int err; + + if (!(play || rec)) + return -1; + + DPRINTF("cad: 0x%x opts: %s\n", hci->cad, opts); + + sc = calloc(1, sizeof(*sc)); + if (!sc) + return -1; + + if (play && rec) + sc->get_parameters = hda_codec_duplex_parameters; + else { + if (play) + sc->get_parameters = hda_codec_output_parameters; + else + sc->get_parameters = hda_codec_input_parameters; + } + sc->subsystem_id = HDA_CODEC_SUBSYSTEM_ID; + sc->no_nodes = HDA_CODEC_NODES_COUNT; + sc->conn_list = hda_codec_conn_list; + sc->conf_default = hda_codec_conf_default; + sc->pin_ctrl_default = hda_codec_pin_ctrl_default; + sc->verb_handlers = hda_codec_verb_handlers; + DPRINTF("HDA Codec nodes: %d\n", sc->no_nodes); + + /* + * Initialize the Audio Output stream + */ + if (play) { + st = &sc->streams[HDA_CODEC_STREAM_OUTPUT]; + + err = hda_audio_ctxt_init(&st->actx, "hda-audio-output", + hda_codec_audio_output_do_transfer, hda_codec_audio_output_do_setup, sc); + assert(!err); + + st->aud = audio_init(play, 1); + if (!st->aud) { + DPRINTF("Fail to init the output audio player\n"); + return -1; + } + } + + /* + * Initialize the Audio Input stream + */ + if (rec) { + st = &sc->streams[HDA_CODEC_STREAM_INPUT]; + + err = hda_audio_ctxt_init(&st->actx, "hda-audio-input", + hda_codec_audio_input_do_transfer, hda_codec_audio_input_do_setup, sc); + assert(!err); + + st->aud = audio_init(rec, 0); + if (!st->aud) { + DPRINTF("Fail to init the input audio player\n"); + return -1; + } + } + + sc->hci = hci; + hci->priv = sc; + + return 0; +} + +static int +hda_codec_reset(struct hda_codec_inst *hci) +{ + struct hda_ops *hops = NULL; + struct hda_codec_softc *sc = NULL; + struct hda_codec_stream *st = NULL; + int i; + + assert(hci); + + hops = hci->hops; + assert(hops); + + sc = (struct hda_codec_softc *)hci->priv; + assert(sc); + + for (i = 0; i < HDA_CODEC_STREAMS_COUNT; i++) { + st = &sc->streams[i]; + st->left_gain = HDA_CODEC_AMP_NUMSTEPS; + st->right_gain = HDA_CODEC_AMP_NUMSTEPS; + st->left_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE; + st->right_mute = HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE; + } + + DPRINTF("cad: 0x%x\n", hci->cad); + + if (!hops->signal) { + DPRINTF("The controller ops does not implement the signal function\n"); + return -1; + } + + return hops->signal(hci); +} + +static int +hda_codec_command(struct hda_codec_inst *hci, uint32_t cmd_data) +{ + struct hda_codec_softc *sc = NULL; + struct hda_ops *hops = NULL; + uint8_t cad = 0, nid = 0; + uint16_t verb = 0, payload = 0; + uint32_t res = 0; + + cad = (cmd_data >> HDA_CMD_CAD_SHIFT) & 0x0f; // 4 bits + nid = (cmd_data >> HDA_CMD_NID_SHIFT) & 0xff; // 8 bits + + if ((cmd_data & 0x70000) == 0x70000) { + verb = (cmd_data >> HDA_CMD_VERB_12BIT_SHIFT) & 0x0fff; // 12 bits + payload = cmd_data & 0xff; // 8 bits + } else { + verb = (cmd_data >> HDA_CMD_VERB_4BIT_SHIFT) & 0x0f; // 4 bits + payload = cmd_data & 0xffff; // 16 bits + } + + assert(cad == hci->cad); + assert(hci); + + hops = hci->hops; + assert(hops); + + sc = (struct hda_codec_softc *)hci->priv; + assert(sc); + + assert(nid < sc->no_nodes); + + if (!hops->response) { + DPRINTF("The controller ops does not implement the response function\n"); + return -1; + } + + switch (verb) { + case HDA_CMD_VERB_GET_PARAMETER: + res = sc->get_parameters[nid][payload]; + break; + case HDA_CMD_VERB_GET_CONN_LIST_ENTRY: + res = sc->conn_list[nid][0]; + break; + case HDA_CMD_VERB_GET_PIN_WIDGET_CTRL: + res = sc->pin_ctrl_default[nid]; + break; + case HDA_CMD_VERB_GET_PIN_SENSE: + res = HDA_CODEC_PIN_SENSE_PRESENCE_PLUGGED; + break; + case HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT: + res = sc->conf_default[nid]; + break; + case HDA_CMD_VERB_GET_SUBSYSTEM_ID: + res = sc->subsystem_id; + break; + default: + assert(sc->verb_handlers); + if (sc->verb_handlers[nid]) + res = sc->verb_handlers[nid](sc, verb, payload); + else + DPRINTF("Unknown VERB: 0x%x\n", verb); + break; + } + + DPRINTF("cad: 0x%x nid: 0x%x verb: 0x%x payload: 0x%x response: 0x%x\n", + cad, nid, verb, payload, res); + + return hops->response(hci, res, HDA_CODEC_RESPONSE_EX_SOL); +} + +static int +hda_codec_notify(struct hda_codec_inst *hci, uint8_t run, uint8_t stream, uint8_t dir) +{ + struct hda_codec_softc *sc = NULL; + struct hda_codec_stream *st = NULL; + struct hda_audio_ctxt *actx = NULL; + int i; + int err; + + assert(hci); + assert(stream); + + sc = (struct hda_codec_softc *)hci->priv; + assert(sc); + + i = dir ? HDA_CODEC_STREAM_OUTPUT : HDA_CODEC_STREAM_INPUT; + st = &sc->streams[i]; + + DPRINTF("run: %d, stream: 0x%x, st->stream: 0x%x dir: %d\n", run, stream, st->stream, dir); + + if (stream != st->stream) { + DPRINTF("Stream not found\n"); + return 0; + } + + actx = &st->actx; + + if (run) + err = hda_audio_ctxt_start(actx); + else + err = hda_audio_ctxt_stop(actx); + + return err; +} + +static int hda_codec_parse_format(uint16_t fmt, struct audio_params *params) +{ + uint8_t div = 0; + + assert(params); + + /* Compute the Sample Rate */ + params->rate = (fmt & HDA_CODEC_FMT_BASE_MASK) ? 44100 : 48000; + + switch (fmt & HDA_CODEC_FMT_MULT_MASK) { + case HDA_CODEC_FMT_MULT_2: + params->rate *= 2; + break; + case HDA_CODEC_FMT_MULT_3: + params->rate *= 3; + break; + case HDA_CODEC_FMT_MULT_4: + params->rate *= 4; + break; + } + + div = (fmt >> HDA_CODEC_FMT_DIV_SHIFT) & HDA_CODEC_FMT_DIV_MASK; + params->rate /= (div + 1); + + /* Compute the Bits per Sample */ + switch (fmt & HDA_CODEC_FMT_BITS_MASK) { + case HDA_CODEC_FMT_BITS_8: + params->format = AFMT_U8; + break; + case HDA_CODEC_FMT_BITS_16: + params->format = AFMT_S16_LE; + break; + case HDA_CODEC_FMT_BITS_24: + params->format = AFMT_S24_LE; + break; + case HDA_CODEC_FMT_BITS_32: + params->format = AFMT_S32_LE; + break; + default: + DPRINTF("Unknown format bits: 0x%x\n", fmt & HDA_CODEC_FMT_BITS_MASK); + return -1; + } + + /* Compute the Number of Channels */ + params->channels = (fmt & HDA_CODEC_FMT_CHAN_MASK) + 1; + + return 0; +} + +static uint32_t +hda_codec_audio_output_nid(struct hda_codec_softc *sc, uint16_t verb, uint16_t payload) +{ + struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_OUTPUT]; + int res; + + res = hda_codec_audio_inout_nid(st, verb, payload); + + return res; +} + +static void +hda_codec_audio_output_do_transfer(void *arg) +{ + struct hda_codec_softc *sc = (struct hda_codec_softc *)arg; + struct hda_codec_inst *hci = NULL; + struct hda_ops *hops = NULL; + struct hda_codec_stream *st = NULL; + struct audio *aud = NULL; + int err; + + hci = sc->hci; + assert(hci); + + hops = hci->hops; + assert(hops); + + st = &sc->streams[HDA_CODEC_STREAM_OUTPUT]; + aud = st->aud; + + err = hops->transfer(hci, st->stream, 1, st->buf, sizeof(st->buf)); + if (err) + return; + + err = audio_playback(aud, st->buf, sizeof(st->buf)); + assert(!err); + + return; +} + +static int +hda_codec_audio_output_do_setup(void *arg) +{ + struct hda_codec_softc *sc = (struct hda_codec_softc *)arg; + struct hda_codec_stream *st = NULL; + struct audio *aud = NULL; + struct audio_params params; + int err; + + st = &sc->streams[HDA_CODEC_STREAM_OUTPUT]; + aud = st->aud; + + err = hda_codec_parse_format(st->fmt, ¶ms); + if (err) + return -1; + + DPRINTF("rate: %d, channels: %d, format: 0x%x\n", params.rate, params.channels, params.format); + + return audio_set_params(aud, ¶ms); +} + +static uint32_t +hda_codec_audio_input_nid(struct hda_codec_softc *sc, uint16_t verb, uint16_t payload) +{ + struct hda_codec_stream *st = &sc->streams[HDA_CODEC_STREAM_INPUT]; + int res; + + res = hda_codec_audio_inout_nid(st, verb, payload); + + return res; +} + +static void +hda_codec_audio_input_do_transfer(void *arg) +{ + struct hda_codec_softc *sc = (struct hda_codec_softc *)arg; + struct hda_codec_inst *hci = NULL; + struct hda_ops *hops = NULL; + struct hda_codec_stream *st = NULL; + struct audio *aud = NULL; + int err; + + hci = sc->hci; + assert(hci); + + hops = hci->hops; + assert(hops); + + st = &sc->streams[HDA_CODEC_STREAM_INPUT]; + aud = st->aud; + + err = audio_record(aud, st->buf, sizeof(st->buf)); + assert(!err); + + hops->transfer(hci, st->stream, 0, st->buf, sizeof(st->buf)); + + return; +} + +static int +hda_codec_audio_input_do_setup(void *arg) +{ + struct hda_codec_softc *sc = (struct hda_codec_softc *)arg; + struct hda_codec_stream *st = NULL; + struct audio *aud = NULL; + struct audio_params params; + int err; + + st = &sc->streams[HDA_CODEC_STREAM_INPUT]; + aud = st->aud; + + err = hda_codec_parse_format(st->fmt, ¶ms); + if (err) + return -1; + + DPRINTF("rate: %d, channels: %d, format: 0x%x\n", params.rate, params.channels, params.format); + + return audio_set_params(aud, ¶ms); +} + +static uint32_t +hda_codec_audio_inout_nid(struct hda_codec_stream *st, uint16_t verb, uint16_t payload) +{ + uint32_t res = 0; + uint8_t mute = 0; + uint8_t gain = 0; + + DPRINTF("%s verb: 0x%x, payload, 0x%x\n", st->actx.name, verb, payload); + + switch (verb) { + case HDA_CMD_VERB_GET_CONV_FMT: + res = st->fmt; + break; + case HDA_CMD_VERB_SET_CONV_FMT: + st->fmt = payload; + break; + case HDA_CMD_VERB_GET_AMP_GAIN_MUTE: + if (payload & HDA_CMD_GET_AMP_GAIN_MUTE_LEFT) { + res = st->left_gain | st->left_mute; + DPRINTF("GET_AMP_GAIN_MUTE_LEFT: 0x%x\n", res); + } else { + res = st->right_gain | st->right_mute; + DPRINTF("GET_AMP_GAIN_MUTE_RIGHT: 0x%x\n", res); + } + break; + case HDA_CMD_VERB_SET_AMP_GAIN_MUTE: + mute = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_MUTE; + gain = payload & HDA_CODEC_SET_AMP_GAIN_MUTE_GAIN_MASK; + + if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_LEFT) { + st->left_mute = mute; + st->left_gain = gain; + DPRINTF("SET_AMP_GAIN_MUTE_LEFT: mute: 0x%x gain: 0x%x\n", mute, gain); + } + + if (payload & HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT) { + st->right_mute = mute; + st->right_gain = gain; + DPRINTF("SET_AMP_GAIN_MUTE_RIGHT: mute: 0x%x gain: 0x%x\n", mute, gain); + } + break; + case HDA_CMD_VERB_GET_CONV_STREAM_CHAN: + res = (st->stream << 4) | st->channel; + break; + case HDA_CMD_VERB_SET_CONV_STREAM_CHAN: + st->channel = payload & 0x0f; + st->stream = (payload >> 4) & 0x0f; + DPRINTF("st->channel: 0x%x st->stream: 0x%x\n", st->channel, st->stream); + if (!st->stream) + hda_audio_ctxt_stop(&st->actx); + break; + default: + DPRINTF("Unknown VERB: 0x%x\n", verb); + break; + } + + return res; +} + +struct hda_codec_class hda_codec = { + .name = "hda_codec", + .init = hda_codec_init, + .reset = hda_codec_reset, + .command = hda_codec_command, + .notify = hda_codec_notify, +}; + +HDA_EMUL_SET(hda_codec); + + +/* + * HDA Audio Context module function definitions + */ + +static void * +hda_audio_ctxt_thr(void *arg) +{ + struct hda_audio_ctxt *actx = (struct hda_audio_ctxt *)arg; + + DPRINTF("Start Thread: %s\n", actx->name); + + pthread_mutex_lock(&actx->mtx); + while (1) { + while (!actx->run) + pthread_cond_wait(&actx->cond, &actx->mtx); + + actx->do_transfer(actx->priv); + } + pthread_mutex_unlock(&actx->mtx); + + pthread_exit(NULL); + + return NULL; +} + +static int +hda_audio_ctxt_init(struct hda_audio_ctxt *actx, const char *tname, transfer_func_t do_transfer, setup_func_t do_setup, void *priv) +{ + int err; + + assert(actx); + assert(tname); + assert(do_transfer); + assert(do_setup); + assert(priv); + + memset(actx, 0, sizeof(*actx)); + + actx->run = 0; + actx->do_transfer = do_transfer; + actx->do_setup = do_setup; + actx->priv = priv; + if (strlen(tname) < sizeof(actx->name)) + memcpy(actx->name, tname, strlen(tname) + 1); + else + strcpy(actx->name, "unknown"); + + err = pthread_mutex_init(&actx->mtx, NULL); + assert(!err); + + err = pthread_cond_init(&actx->cond, NULL); + assert(!err); + + err = pthread_create(&actx->tid, NULL, hda_audio_ctxt_thr, actx); + assert(!err); + + pthread_set_name_np(actx->tid, tname); + assert(!err); + + actx->started = 1; + + return 0; +} + +static int +hda_audio_ctxt_start(struct hda_audio_ctxt *actx) +{ + int err = 0; + + assert(actx); + assert(actx->started); + + /* The stream is supposed to be stopped */ + if (actx->run) + return -1; + + pthread_mutex_lock(&actx->mtx); + err = actx->do_setup(actx->priv); + if (!err) { + actx->run = 1; + pthread_cond_signal(&actx->cond); + } + pthread_mutex_unlock(&actx->mtx); + + return err; +} + +static int +hda_audio_ctxt_stop(struct hda_audio_ctxt *actx) +{ + actx->run = 0; + return 0; +} + Index: hda_reg.h =================================================================== --- hda_reg.h +++ hda_reg.h @@ -0,0 +1,1367 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _HDA_REG_H_ +#define _HDA_REG_H_ + +/**************************************************************************** + * HDA Device Verbs + ****************************************************************************/ + +/* HDA Command */ +#define HDA_CMD_VERB_MASK 0x000fffff +#define HDA_CMD_VERB_SHIFT 0 +#define HDA_CMD_NID_MASK 0x0ff00000 +#define HDA_CMD_NID_SHIFT 20 +#define HDA_CMD_CAD_MASK 0xf0000000 +#define HDA_CMD_CAD_SHIFT 28 + +#define HDA_CMD_VERB_4BIT_SHIFT 16 +#define HDA_CMD_VERB_12BIT_SHIFT 8 + +#define HDA_CMD_VERB_4BIT(verb, payload) \ + (((verb) << HDA_CMD_VERB_4BIT_SHIFT) | (payload)) +#define HDA_CMD_4BIT(cad, nid, verb, payload) \ + (((cad) << HDA_CMD_CAD_SHIFT) | \ + ((nid) << HDA_CMD_NID_SHIFT) | \ + (HDA_CMD_VERB_4BIT((verb), (payload)))) + +#define HDA_CMD_VERB_12BIT(verb, payload) \ + (((verb) << HDA_CMD_VERB_12BIT_SHIFT) | (payload)) +#define HDA_CMD_12BIT(cad, nid, verb, payload) \ + (((cad) << HDA_CMD_CAD_SHIFT) | \ + ((nid) << HDA_CMD_NID_SHIFT) | \ + (HDA_CMD_VERB_12BIT((verb), (payload)))) + +/* Get Parameter */ +#define HDA_CMD_VERB_GET_PARAMETER 0xf00 + +#define HDA_CMD_GET_PARAMETER(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PARAMETER, (payload))) + +/* Connection Select Control */ +#define HDA_CMD_VERB_GET_CONN_SELECT_CONTROL 0xf01 +#define HDA_CMD_VERB_SET_CONN_SELECT_CONTROL 0x701 + +#define HDA_CMD_GET_CONN_SELECT_CONTROL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONN_SELECT_CONTROL, 0x0)) +#define HDA_CMD_SET_CONNECTION_SELECT_CONTROL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONN_SELECT_CONTROL, (payload))) + +/* Connection List Entry */ +#define HDA_CMD_VERB_GET_CONN_LIST_ENTRY 0xf02 + +#define HDA_CMD_GET_CONN_LIST_ENTRY(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONN_LIST_ENTRY, (payload))) + +#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_SHORT 1 +#define HDA_CMD_GET_CONN_LIST_ENTRY_SIZE_LONG 2 + +/* Processing State */ +#define HDA_CMD_VERB_GET_PROCESSING_STATE 0xf03 +#define HDA_CMD_VERB_SET_PROCESSING_STATE 0x703 + +#define HDA_CMD_GET_PROCESSING_STATE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PROCESSING_STATE, 0x0)) +#define HDA_CMD_SET_PROCESSING_STATE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PROCESSING_STATE, (payload))) + +#define HDA_CMD_GET_PROCESSING_STATE_STATE_OFF 0x00 +#define HDA_CMD_GET_PROCESSING_STATE_STATE_ON 0x01 +#define HDA_CMD_GET_PROCESSING_STATE_STATE_BENIGN 0x02 + +/* Coefficient Index */ +#define HDA_CMD_VERB_GET_COEFF_INDEX 0xd +#define HDA_CMD_VERB_SET_COEFF_INDEX 0x5 + +#define HDA_CMD_GET_COEFF_INDEX(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_COEFF_INDEX, 0x0)) +#define HDA_CMD_SET_COEFF_INDEX(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_COEFF_INDEX, (payload))) + +/* Processing Coefficient */ +#define HDA_CMD_VERB_GET_PROCESSING_COEFF 0xc +#define HDA_CMD_VERB_SET_PROCESSING_COEFF 0x4 + +#define HDA_CMD_GET_PROCESSING_COEFF(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PROCESSING_COEFF, 0x0)) +#define HDA_CMD_SET_PROCESSING_COEFF(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PROCESSING_COEFF, (payload))) + +/* Amplifier Gain/Mute */ +#define HDA_CMD_VERB_GET_AMP_GAIN_MUTE 0xb +#define HDA_CMD_VERB_SET_AMP_GAIN_MUTE 0x3 + +#define HDA_CMD_GET_AMP_GAIN_MUTE(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_AMP_GAIN_MUTE, (payload))) +#define HDA_CMD_SET_AMP_GAIN_MUTE(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_AMP_GAIN_MUTE, (payload))) + +#define HDA_CMD_GET_AMP_GAIN_MUTE_INPUT 0x0000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_OUTPUT 0x8000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_RIGHT 0x0000 +#define HDA_CMD_GET_AMP_GAIN_MUTE_LEFT 0x2000 + +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK 0x00000008 +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT 7 +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK 0x00000007 +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT 0 + +#define HDA_CMD_GET_AMP_GAIN_MUTE_MUTE(rsp) \ + (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_MASK) >> \ + HDA_CMD_GET_AMP_GAIN_MUTE_MUTE_SHIFT) +#define HDA_CMD_GET_AMP_GAIN_MUTE_GAIN(rsp) \ + (((rsp) & HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_MASK) >> \ + HDA_CMD_GET_AMP_GAIN_MUTE_GAIN_SHIFT) + +#define HDA_CMD_SET_AMP_GAIN_MUTE_OUTPUT 0x8000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INPUT 0x4000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_LEFT 0x2000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_RIGHT 0x1000 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK 0x0f00 +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT 8 +#define HDA_CMD_SET_AMP_GAIN_MUTE_MUTE 0x0080 +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK 0x0007 +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT 0 + +#define HDA_CMD_SET_AMP_GAIN_MUTE_INDEX(index) \ + (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_SHIFT) & \ + HDA_CMD_SET_AMP_GAIN_MUTE_INDEX_MASK) +#define HDA_CMD_SET_AMP_GAIN_MUTE_GAIN(index) \ + (((index) << HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_SHIFT) & \ + HDA_CMD_SET_AMP_GAIN_MUTE_GAIN_MASK) + +/* Converter format */ +#define HDA_CMD_VERB_GET_CONV_FMT 0xa +#define HDA_CMD_VERB_SET_CONV_FMT 0x2 + +#define HDA_CMD_GET_CONV_FMT(cad, nid) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_FMT, 0x0)) +#define HDA_CMD_SET_CONV_FMT(cad, nid, payload) \ + (HDA_CMD_4BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_FMT, (payload))) + +/* Digital Converter Control */ +#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT1 0xf0d +#define HDA_CMD_VERB_GET_DIGITAL_CONV_FMT2 0xf0e +#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1 0x70d +#define HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2 0x70e + +#define HDA_CMD_GET_DIGITAL_CONV_FMT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_DIGITAL_CONV_FMT1, 0x0)) +#define HDA_CMD_SET_DIGITAL_CONV_FMT1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_DIGITAL_CONV_FMT1, (payload))) +#define HDA_CMD_SET_DIGITAL_CONV_FMT2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_DIGITAL_CONV_FMT2, (payload))) + +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK 0x7f00 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT 8 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK 0x0080 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT 7 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK 0x0040 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT 6 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK 0x0020 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT 5 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK 0x0010 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT 4 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK 0x0008 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT 3 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK 0x0004 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT 2 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK 0x0002 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT 1 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK 0x0001 +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT 0 + +#define HDA_CMD_GET_DIGITAL_CONV_FMT_CC(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_CC_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_CC_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_L(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_L_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_L_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRO(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_PRO_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_NAUDIO_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_COPY(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_COPY_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_PRE(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_PRE_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_VCFG_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_V(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_V_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_V_SHIFT) +#define HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN(rsp) \ + (((rsp) & HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_MASK) >> \ + HDA_CMD_GET_DIGITAL_CONV_FMT_DIGEN_SHIFT) + +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_L 0x80 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRO 0x40 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_NAUDIO 0x20 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_COPY 0x10 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_PRE 0x08 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_VCFG 0x04 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_V 0x02 +#define HDA_CMD_SET_DIGITAL_CONV_FMT1_DIGEN 0x01 + +/* Power State */ +#define HDA_CMD_VERB_GET_POWER_STATE 0xf05 +#define HDA_CMD_VERB_SET_POWER_STATE 0x705 + +#define HDA_CMD_GET_POWER_STATE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_POWER_STATE, 0x0)) +#define HDA_CMD_SET_POWER_STATE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_POWER_STATE, (payload))) + +#define HDA_CMD_POWER_STATE_D0 0x00 +#define HDA_CMD_POWER_STATE_D1 0x01 +#define HDA_CMD_POWER_STATE_D2 0x02 +#define HDA_CMD_POWER_STATE_D3 0x03 + +#define HDA_CMD_POWER_STATE_ACT_MASK 0x000000f0 +#define HDA_CMD_POWER_STATE_ACT_SHIFT 4 +#define HDA_CMD_POWER_STATE_SET_MASK 0x0000000f +#define HDA_CMD_POWER_STATE_SET_SHIFT 0 + +#define HDA_CMD_GET_POWER_STATE_ACT(rsp) \ + (((rsp) & HDA_CMD_POWER_STATE_ACT_MASK) >> \ + HDA_CMD_POWER_STATE_ACT_SHIFT) +#define HDA_CMD_GET_POWER_STATE_SET(rsp) \ + (((rsp) & HDA_CMD_POWER_STATE_SET_MASK) >> \ + HDA_CMD_POWER_STATE_SET_SHIFT) + +#define HDA_CMD_SET_POWER_STATE_ACT(ps) \ + (((ps) << HDA_CMD_POWER_STATE_ACT_SHIFT) & \ + HDA_CMD_POWER_STATE_ACT_MASK) +#define HDA_CMD_SET_POWER_STATE_SET(ps) \ + (((ps) << HDA_CMD_POWER_STATE_SET_SHIFT) & \ + HDA_CMD_POWER_STATE_ACT_MASK) + +/* Converter Stream, Channel */ +#define HDA_CMD_VERB_GET_CONV_STREAM_CHAN 0xf06 +#define HDA_CMD_VERB_SET_CONV_STREAM_CHAN 0x706 + +#define HDA_CMD_GET_CONV_STREAM_CHAN(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_STREAM_CHAN, 0x0)) +#define HDA_CMD_SET_CONV_STREAM_CHAN(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_STREAM_CHAN, (payload))) + +#define HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK 0x000000f0 +#define HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT 4 +#define HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK 0x0000000f +#define HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT 0 + +#define HDA_CMD_GET_CONV_STREAM_CHAN_STREAM(rsp) \ + (((rsp) & HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) >> \ + HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) +#define HDA_CMD_GET_CONV_STREAM_CHAN_CHAN(rsp) \ + (((rsp) & HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) >> \ + HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) + +#define HDA_CMD_SET_CONV_STREAM_CHAN_STREAM(param) \ + (((param) << HDA_CMD_CONV_STREAM_CHAN_STREAM_SHIFT) & \ + HDA_CMD_CONV_STREAM_CHAN_STREAM_MASK) +#define HDA_CMD_SET_CONV_STREAM_CHAN_CHAN(param) \ + (((param) << HDA_CMD_CONV_STREAM_CHAN_CHAN_SHIFT) & \ + HDA_CMD_CONV_STREAM_CHAN_CHAN_MASK) + +/* Input Converter SDI Select */ +#define HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT 0xf04 +#define HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT 0x704 + +#define HDA_CMD_GET_INPUT_CONVERTER_SDI_SELECT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_INPUT_CONVERTER_SDI_SELECT, 0x0)) +#define HDA_CMD_SET_INPUT_CONVERTER_SDI_SELECT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_INPUT_CONVERTER_SDI_SELECT, (payload))) + +/* Pin Widget Control */ +#define HDA_CMD_VERB_GET_PIN_WIDGET_CTRL 0xf07 +#define HDA_CMD_VERB_SET_PIN_WIDGET_CTRL 0x707 + +#define HDA_CMD_GET_PIN_WIDGET_CTRL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PIN_WIDGET_CTRL, 0x0)) +#define HDA_CMD_SET_PIN_WIDGET_CTRL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PIN_WIDGET_CTRL, (payload))) + +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK 0x00000080 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT 7 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK 0x00000040 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT 6 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK 0x00000020 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT 5 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x00000007 +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0 + +#define HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_HPHN_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_OUT_ENABLE_MASK) >> \ + HDA_GET_CMD_PIN_WIDGET_CTRL_OUT_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_IN_ENABLE_SHIFT) +#define HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) >> \ + HDA_CMD_GET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) + +#define HDA_CMD_SET_PIN_WIDGET_CTRL_HPHN_ENABLE 0x80 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_OUT_ENABLE 0x40 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_IN_ENABLE 0x20 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK 0x07 +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT 0 + +#define HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE(param) \ + (((param) << HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_SHIFT) & \ + HDA_CMD_SET_PIN_WIDGET_CTRL_VREF_ENABLE_MASK) + +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_HIZ 0 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_50 1 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_GROUND 2 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_80 4 +#define HDA_CMD_PIN_WIDGET_CTRL_VREF_ENABLE_100 5 + +/* Unsolicited Response */ +#define HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE 0xf08 +#define HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE 0x708 + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_UNSOLICITED_RESPONSE, 0x0)) +#define HDA_CMD_SET_UNSOLICITED_RESPONSE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_UNSOLICITED_RESPONSE, (payload))) + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK 0x00000080 +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT 7 +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK 0x0000001f +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 + +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE(rsp) \ + (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_MASK) >> \ + HDA_CMD_GET_UNSOLICITED_RESPONSE_ENABLE_SHIFT) +#define HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG(rsp) \ + (((rsp) & HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_MASK) >> \ + HDA_CMD_GET_UNSOLICITED_RESPONSE_TAG_SHIFT) + +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_ENABLE 0x80 +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK 0x3f +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT 0 + +#define HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG(param) \ + (((param) << HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_SHIFT) & \ + HDA_CMD_SET_UNSOLICITED_RESPONSE_TAG_MASK) + +/* Pin Sense */ +#define HDA_CMD_VERB_GET_PIN_SENSE 0xf09 +#define HDA_CMD_VERB_SET_PIN_SENSE 0x709 + +#define HDA_CMD_GET_PIN_SENSE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_PIN_SENSE, 0x0)) +#define HDA_CMD_SET_PIN_SENSE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_PIN_SENSE, (payload))) + +#define HDA_CMD_GET_PIN_SENSE_PRESENCE_DETECT 0x80000000 +#define HDA_CMD_GET_PIN_SENSE_ELD_VALID 0x40000000 +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK 0x7fffffff +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT 0 + +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE(rsp) \ + (((rsp) & HDA_CMD_GET_PIN_SENSE_IMP_SENSE_MASK) >> \ + HDA_CMD_GET_PIN_SENSE_IMP_SENSE_SHIFT) + +#define HDA_CMD_GET_PIN_SENSE_IMP_SENSE_INVALID 0x7fffffff + +#define HDA_CMD_SET_PIN_SENSE_LEFT_CHANNEL 0x00 +#define HDA_CMD_SET_PIN_SENSE_RIGHT_CHANNEL 0x01 + +/* EAPD/BTL Enable */ +#define HDA_CMD_VERB_GET_EAPD_BTL_ENABLE 0xf0c +#define HDA_CMD_VERB_SET_EAPD_BTL_ENABLE 0x70c + +#define HDA_CMD_GET_EAPD_BTL_ENABLE(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_EAPD_BTL_ENABLE, 0x0)) +#define HDA_CMD_SET_EAPD_BTL_ENABLE(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_EAPD_BTL_ENABLE, (payload))) + +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK 0x00000004 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT 2 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK 0x00000002 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT 1 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK 0x00000001 +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT 0 + +#define HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_LR_SWAP_SHIFT) +#define HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_EAPD_SHIFT) +#define HDA_CMD_GET_EAPD_BTL_ENABLE_BTL(rsp) \ + (((rsp) & HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_MASK) >> \ + HDA_CMD_GET_EAPD_BTL_ENABLE_BTL_SHIFT) + +#define HDA_CMD_SET_EAPD_BTL_ENABLE_LR_SWAP 0x04 +#define HDA_CMD_SET_EAPD_BTL_ENABLE_EAPD 0x02 +#define HDA_CMD_SET_EAPD_BTL_ENABLE_BTL 0x01 + +/* GPI Data */ +#define HDA_CMD_VERB_GET_GPI_DATA 0xf10 +#define HDA_CMD_VERB_SET_GPI_DATA 0x710 + +#define HDA_CMD_GET_GPI_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_DATA, 0x0)) +#define HDA_CMD_SET_GPI_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_DATA, (payload))) + +/* GPI Wake Enable Mask */ +#define HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK 0xf11 +#define HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK 0x711 + +#define HDA_CMD_GET_GPI_WAKE_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_WAKE_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPI_WAKE_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_WAKE_ENABLE_MASK, (payload))) + +/* GPI Unsolicited Enable Mask */ +#define HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK 0xf12 +#define HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK 0x712 + +#define HDA_CMD_GET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_UNSOLICITED_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPI_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_UNSOLICITED_ENABLE_MASK, (payload))) + +/* GPI Sticky Mask */ +#define HDA_CMD_VERB_GET_GPI_STICKY_MASK 0xf13 +#define HDA_CMD_VERB_SET_GPI_STICKY_MASK 0x713 + +#define HDA_CMD_GET_GPI_STICKY_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPI_STICKY_MASK, 0x0)) +#define HDA_CMD_SET_GPI_STICKY_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPI_STICKY_MASK, (payload))) + +/* GPO Data */ +#define HDA_CMD_VERB_GET_GPO_DATA 0xf14 +#define HDA_CMD_VERB_SET_GPO_DATA 0x714 + +#define HDA_CMD_GET_GPO_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPO_DATA, 0x0)) +#define HDA_CMD_SET_GPO_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPO_DATA, (payload))) + +/* GPIO Data */ +#define HDA_CMD_VERB_GET_GPIO_DATA 0xf15 +#define HDA_CMD_VERB_SET_GPIO_DATA 0x715 + +#define HDA_CMD_GET_GPIO_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_DATA, 0x0)) +#define HDA_CMD_SET_GPIO_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_DATA, (payload))) + +/* GPIO Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_ENABLE_MASK 0xf16 +#define HDA_CMD_VERB_SET_GPIO_ENABLE_MASK 0x716 + +#define HDA_CMD_GET_GPIO_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_ENABLE_MASK, (payload))) + +/* GPIO Direction */ +#define HDA_CMD_VERB_GET_GPIO_DIRECTION 0xf17 +#define HDA_CMD_VERB_SET_GPIO_DIRECTION 0x717 + +#define HDA_CMD_GET_GPIO_DIRECTION(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_DIRECTION, 0x0)) +#define HDA_CMD_SET_GPIO_DIRECTION(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_DIRECTION, (payload))) + +/* GPIO Wake Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK 0xf18 +#define HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK 0x718 + +#define HDA_CMD_GET_GPIO_WAKE_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_WAKE_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_WAKE_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_WAKE_ENABLE_MASK, (payload))) + +/* GPIO Unsolicited Enable Mask */ +#define HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK 0xf19 +#define HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK 0x719 + +#define HDA_CMD_GET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_UNSOLICITED_ENABLE_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_UNSOLICITED_ENABLE_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_UNSOLICITED_ENABLE_MASK, (payload))) + +/* GPIO_STICKY_MASK */ +#define HDA_CMD_VERB_GET_GPIO_STICKY_MASK 0xf1a +#define HDA_CMD_VERB_SET_GPIO_STICKY_MASK 0x71a + +#define HDA_CMD_GET_GPIO_STICKY_MASK(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_GPIO_STICKY_MASK, 0x0)) +#define HDA_CMD_SET_GPIO_STICKY_MASK(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_GPIO_STICKY_MASK, (payload))) + +/* Beep Generation */ +#define HDA_CMD_VERB_GET_BEEP_GENERATION 0xf0a +#define HDA_CMD_VERB_SET_BEEP_GENERATION 0x70a + +#define HDA_CMD_GET_BEEP_GENERATION(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_BEEP_GENERATION, 0x0)) +#define HDA_CMD_SET_BEEP_GENERATION(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_BEEP_GENERATION, (payload))) + +/* Volume Knob */ +#define HDA_CMD_VERB_GET_VOLUME_KNOB 0xf0f +#define HDA_CMD_VERB_SET_VOLUME_KNOB 0x70f + +#define HDA_CMD_GET_VOLUME_KNOB(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_VOLUME_KNOB, 0x0)) +#define HDA_CMD_SET_VOLUME_KNOB(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_VOLUME_KNOB, (payload))) + +/* Subsystem ID */ +#define HDA_CMD_VERB_GET_SUBSYSTEM_ID 0xf20 +#define HDA_CMD_VERB_SET_SUSBYSTEM_ID1 0x720 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID2 0x721 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID3 0x722 +#define HDA_CMD_VERB_SET_SUBSYSTEM_ID4 0x723 + +#define HDA_CMD_GET_SUBSYSTEM_ID(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_SUBSYSTEM_ID, 0x0)) +#define HDA_CMD_SET_SUBSYSTEM_ID1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID1, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID2, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID3(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID3, (payload))) +#define HDA_CMD_SET_SUBSYSTEM_ID4(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_SUSBYSTEM_ID4, (payload))) + +/* Configuration Default */ +#define HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT 0xf1c +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1 0x71c +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2 0x71d +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3 0x71e +#define HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4 0x71f + +#define HDA_CMD_GET_CONFIGURATION_DEFAULT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONFIGURATION_DEFAULT, 0x0)) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT1(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT1, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT2(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT2, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT3(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT3, (payload))) +#define HDA_CMD_SET_CONFIGURATION_DEFAULT4(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONFIGURATION_DEFAULT4, (payload))) + +/* Stripe Control */ +#define HDA_CMD_VERB_GET_STRIPE_CONTROL 0xf24 +#define HDA_CMD_VERB_SET_STRIPE_CONTROL 0x724 + +#define HDA_CMD_GET_STRIPE_CONTROL(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_STRIPE_CONTROL, 0x0)) +#define HDA_CMD_SET_STRIPE_CONTROL(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_STRIPE_CONTROL, (payload))) + +/* Channel Count Control */ +#define HDA_CMD_VERB_GET_CONV_CHAN_COUNT 0xf2d +#define HDA_CMD_VERB_SET_CONV_CHAN_COUNT 0x72d + +#define HDA_CMD_GET_CONV_CHAN_COUNT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_CONV_CHAN_COUNT, 0x0)) +#define HDA_CMD_SET_CONV_CHAN_COUNT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_CONV_CHAN_COUNT, (payload))) + +#define HDA_CMD_VERB_GET_HDMI_DIP_SIZE 0xf2e + +#define HDA_CMD_GET_HDMI_DIP_SIZE(cad, nid, arg) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_SIZE, (arg))) + +#define HDA_CMD_VERB_GET_HDMI_ELDD 0xf2f + +#define HDA_CMD_GET_HDMI_ELDD(cad, nid, off) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_ELDD, (off))) + +#define HDA_CMD_VERB_GET_HDMI_DIP_INDEX 0xf30 +#define HDA_CMD_VERB_SET_HDMI_DIP_INDEX 0x730 + +#define HDA_CMD_GET_HDMI_DIP_INDEX(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_INDEX, 0x0)) +#define HDA_CMD_SET_HDMI_DIP_INDEX(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_DIP_INDEX, (payload))) + +#define HDA_CMD_VERB_GET_HDMI_DIP_DATA 0xf31 +#define HDA_CMD_VERB_SET_HDMI_DIP_DATA 0x731 + +#define HDA_CMD_GET_HDMI_DIP_DATA(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_DATA, 0x0)) +#define HDA_CMD_SET_HDMI_DIP_DATA(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_DIP_DATA, (payload))) + +#define HDA_CMD_VERB_GET_HDMI_DIP_XMIT 0xf32 +#define HDA_CMD_VERB_SET_HDMI_DIP_XMIT 0x732 + +#define HDA_CMD_GET_HDMI_DIP_XMIT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_DIP_XMIT, 0x0)) +#define HDA_CMD_SET_HDMI_DIP_XMIT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_DIP_XMIT, (payload))) + +#define HDA_CMD_VERB_GET_HDMI_CP_CTRL 0xf33 +#define HDA_CMD_VERB_SET_HDMI_CP_CTRL 0x733 + +#define HDA_CMD_VERB_GET_HDMI_CHAN_SLOT 0xf34 +#define HDA_CMD_VERB_SET_HDMI_CHAN_SLOT 0x734 + +#define HDA_CMD_GET_HDMI_CHAN_SLOT(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_GET_HDMI_CHAN_SLOT, 0x0)) +#define HDA_CMD_SET_HDMI_CHAN_SLOT(cad, nid, payload) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_SET_HDMI_CHAN_SLOT, (payload))) + +#define HDA_HDMI_CODING_TYPE_REF_STREAM_HEADER 0 +#define HDA_HDMI_CODING_TYPE_LPCM 1 +#define HDA_HDMI_CODING_TYPE_AC3 2 +#define HDA_HDMI_CODING_TYPE_MPEG1 3 +#define HDA_HDMI_CODING_TYPE_MP3 4 +#define HDA_HDMI_CODING_TYPE_MPEG2 5 +#define HDA_HDMI_CODING_TYPE_AACLC 6 +#define HDA_HDMI_CODING_TYPE_DTS 7 +#define HDA_HDMI_CODING_TYPE_ATRAC 8 +#define HDA_HDMI_CODING_TYPE_SACD 9 +#define HDA_HDMI_CODING_TYPE_EAC3 10 +#define HDA_HDMI_CODING_TYPE_DTS_HD 11 +#define HDA_HDMI_CODING_TYPE_MLP 12 +#define HDA_HDMI_CODING_TYPE_DST 13 +#define HDA_HDMI_CODING_TYPE_WMAPRO 14 +#define HDA_HDMI_CODING_TYPE_REF_CTX 15 + +/* Function Reset */ +#define HDA_CMD_VERB_FUNCTION_RESET 0x7ff + +#define HDA_CMD_FUNCTION_RESET(cad, nid) \ + (HDA_CMD_12BIT((cad), (nid), \ + HDA_CMD_VERB_FUNCTION_RESET, 0x0)) + + +/**************************************************************************** + * HDA Device Parameters + ****************************************************************************/ + +/* Vendor ID */ +#define HDA_PARAM_VENDOR_ID 0x00 + +#define HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK 0xffff0000 +#define HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT 16 +#define HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK 0x0000ffff +#define HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT 0 + +#define HDA_PARAM_VENDOR_ID_VENDOR_ID(param) \ + (((param) & HDA_PARAM_VENDOR_ID_VENDOR_ID_MASK) >> \ + HDA_PARAM_VENDOR_ID_VENDOR_ID_SHIFT) +#define HDA_PARAM_VENDOR_ID_DEVICE_ID(param) \ + (((param) & HDA_PARAM_VENDOR_ID_DEVICE_ID_MASK) >> \ + HDA_PARAM_VENDOR_ID_DEVICE_ID_SHIFT) + +/* Revision ID */ +#define HDA_PARAM_REVISION_ID 0x02 + +#define HDA_PARAM_REVISION_ID_MAJREV_MASK 0x00f00000 +#define HDA_PARAM_REVISION_ID_MAJREV_SHIFT 20 +#define HDA_PARAM_REVISION_ID_MINREV_MASK 0x000f0000 +#define HDA_PARAM_REVISION_ID_MINREV_SHIFT 16 +#define HDA_PARAM_REVISION_ID_REVISION_ID_MASK 0x0000ff00 +#define HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT 8 +#define HDA_PARAM_REVISION_ID_STEPPING_ID_MASK 0x000000ff +#define HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT 0 + +#define HDA_PARAM_REVISION_ID_MAJREV(param) \ + (((param) & HDA_PARAM_REVISION_ID_MAJREV_MASK) >> \ + HDA_PARAM_REVISION_ID_MAJREV_SHIFT) +#define HDA_PARAM_REVISION_ID_MINREV(param) \ + (((param) & HDA_PARAM_REVISION_ID_MINREV_MASK) >> \ + HDA_PARAM_REVISION_ID_MINREV_SHIFT) +#define HDA_PARAM_REVISION_ID_REVISION_ID(param) \ + (((param) & HDA_PARAM_REVISION_ID_REVISION_ID_MASK) >> \ + HDA_PARAM_REVISION_ID_REVISION_ID_SHIFT) +#define HDA_PARAM_REVISION_ID_STEPPING_ID(param) \ + (((param) & HDA_PARAM_REVISION_ID_STEPPING_ID_MASK) >> \ + HDA_PARAM_REVISION_ID_STEPPING_ID_SHIFT) + +/* Subordinate Node Cound */ +#define HDA_PARAM_SUB_NODE_COUNT 0x04 + +#define HDA_PARAM_SUB_NODE_COUNT_START_MASK 0x00ff0000 +#define HDA_PARAM_SUB_NODE_COUNT_START_SHIFT 16 +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK 0x000000ff +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT 0 + +#define HDA_PARAM_SUB_NODE_COUNT_START(param) \ + (((param) & HDA_PARAM_SUB_NODE_COUNT_START_MASK) >> \ + HDA_PARAM_SUB_NODE_COUNT_START_SHIFT) +#define HDA_PARAM_SUB_NODE_COUNT_TOTAL(param) \ + (((param) & HDA_PARAM_SUB_NODE_COUNT_TOTAL_MASK) >> \ + HDA_PARAM_SUB_NODE_COUNT_TOTAL_SHIFT) + +/* Function Group Type */ +#define HDA_PARAM_FCT_GRP_TYPE 0x05 + +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK 0x00000100 +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL_SHIFT 8 +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK 0x000000ff +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT 0 + +#define HDA_PARAM_FCT_GRP_TYPE_UNSOL(param) \ + (((param) & HDA_PARAM_FCT_GRP_TYPE_UNSOL_MASK) >> \ + HDA_PARAM_FCT_GROUP_TYPE_UNSOL_SHIFT) +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE(param) \ + (((param) & HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MASK) >> \ + HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_SHIFT) + +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO 0x01 +#define HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_MODEM 0x02 + +/* Audio Function Group Capabilities */ +#define HDA_PARAM_AUDIO_FCT_GRP_CAP 0x08 + +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK 0x00010000 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT 16 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK 0x00000f00 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT 8 +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK 0x0000000f +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT 0 + +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_BEEP_GEN_SHIFT) +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_INPUT_DELAY_SHIFT) +#define HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_FCT_GRP_CAP_OUTPUT_DELAY_SHIFT) + +/* Audio Widget Capabilities */ +#define HDA_PARAM_AUDIO_WIDGET_CAP 0x09 + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK 0x00f00000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT 20 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK 0x000f0000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT 16 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_MASK 0x0000e000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_SHIFT 13 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CP_MASK 0x00001000 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CP_SHIFT 12 +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK 0x00000800 +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT 11 +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK 0x00000400 +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT 10 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK 0x00000200 +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT 9 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK 0x00000100 +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT 8 +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK 0x00000080 +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT 7 +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK 0x00000040 +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT 6 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK 0x00000020 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT 5 +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK 0x00000010 +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT 4 +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK 0x00000008 +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT 3 +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK 0x00000004 +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT 2 +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK 0x00000002 +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT 1 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK 0x00000001 +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT 0 + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_DELAY(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_DELAY_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_CC(param) \ + ((((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_MASK) >> \ + (HDA_PARAM_AUDIO_WIDGET_CAP_CC_EXT_SHIFT - 1)) | \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT)) +#define HDA_PARAM_AUDIO_WIDGET_CAP_CP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_CP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_LR_SWAP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_POWER_CTRL_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_DIGITAL_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_CONN_LIST_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_UNSOL_CAP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_PROC_WIDGET_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STRIPE_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_FORMAT_OVR_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_AMP_OVR_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_OUT_AMP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_IN_AMP_SHIFT) +#define HDA_PARAM_AUDIO_WIDGET_CAP_STEREO(param) \ + (((param) & HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_MASK) >> \ + HDA_PARAM_AUDIO_WIDGET_CAP_STEREO_SHIFT) + +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_OUTPUT 0x0 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_INPUT 0x1 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_MIXER 0x2 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_AUDIO_SELECTOR 0x3 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_PIN_COMPLEX 0x4 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_POWER_WIDGET 0x5 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VOLUME_WIDGET 0x6 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_BEEP_WIDGET 0x7 +#define HDA_PARAM_AUDIO_WIDGET_CAP_TYPE_VENDOR_WIDGET 0xf + +/* Supported PCM Size, Rates */ + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE 0x0a + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK 0x00100000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT 20 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK 0x00080000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT 19 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK 0x00040000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT 18 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK 0x00020000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT 17 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK 0x00010000 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT 16 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK 0x00000001 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT 0 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK 0x00000002 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT 1 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK 0x00000004 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT 2 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK 0x00000008 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT 3 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK 0x00000010 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT 4 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK 0x00000020 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT 5 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK 0x00000040 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT 6 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK 0x00000080 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT 7 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK 0x00000100 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT 8 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK 0x00000200 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT 9 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK 0x00000400 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT 10 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK 0x00000800 +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT 11 + +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_32BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_24BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_16BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_8BIT_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_8KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_11KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_16KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_22KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_32KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_44KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_48KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_88KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_96KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_176KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_192KHZ_SHIFT) +#define HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ(param) \ + (((param) & HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_MASK) >> \ + HDA_PARAM_SUPP_PCM_SIZE_RATE_384KHZ_SHIFT) + +/* Supported Stream Formats */ +#define HDA_PARAM_SUPP_STREAM_FORMATS 0x0b + +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK 0x00000004 +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT 2 +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK 0x00000002 +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT 1 +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK 0x00000001 +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT 0 + +#define HDA_PARAM_SUPP_STREAM_FORMATS_AC3(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_AC3_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_AC3_SHIFT) +#define HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_FLOAT32_SHIFT) +#define HDA_PARAM_SUPP_STREAM_FORMATS_PCM(param) \ + (((param) & HDA_PARAM_SUPP_STREAM_FORMATS_PCM_MASK) >> \ + HDA_PARAM_SUPP_STREAM_FORMATS_PCM_SHIFT) + +/* Pin Capabilities */ +#define HDA_PARAM_PIN_CAP 0x0c + +#define HDA_PARAM_PIN_CAP_HBR_MASK 0x08000000 +#define HDA_PARAM_PIN_CAP_HBR_SHIFT 27 +#define HDA_PARAM_PIN_CAP_DP_MASK 0x01000000 +#define HDA_PARAM_PIN_CAP_DP_SHIFT 24 +#define HDA_PARAM_PIN_CAP_EAPD_CAP_MASK 0x00010000 +#define HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT 16 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_MASK 0x0000ff00 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT 8 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK 0x00002000 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT 13 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK 0x00001000 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT 12 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK 0x00000400 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT 10 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK 0x00000200 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT 9 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK 0x00000100 +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT 8 +#define HDA_PARAM_PIN_CAP_HDMI_MASK 0x00000080 +#define HDA_PARAM_PIN_CAP_HDMI_SHIFT 7 +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK 0x00000040 +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT 6 +#define HDA_PARAM_PIN_CAP_INPUT_CAP_MASK 0x00000020 +#define HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT 5 +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK 0x00000010 +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT 4 +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK 0x00000008 +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT 3 +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK 0x00000004 +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT 2 +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK 0x00000002 +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT 1 +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK 0x00000001 +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT 0 + +#define HDA_PARAM_PIN_CAP_HBR(param) \ + (((param) & HDA_PARAM_PIN_CAP_HBR_MASK) >> \ + HDA_PARAM_PIN_CAP_HBR_SHIFT) +#define HDA_PARAM_PIN_CAP_DP(param) \ + (((param) & HDA_PARAM_PIN_CAP_DP_MASK) >> \ + HDA_PARAM_PIN_CAP_DP_SHIFT) +#define HDA_PARAM_PIN_CAP_EAPD_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_EAPD_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_EAPD_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_100(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_100_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_100_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_80(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_80_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_80_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_GROUND_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_50(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_50_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_50_SHIFT) +#define HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ(param) \ + (((param) & HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_MASK) >> \ + HDA_PARAM_PIN_CAP_VREF_CTRL_HIZ_SHIFT) +#define HDA_PARAM_PIN_CAP_HDMI(param) \ + (((param) & HDA_PARAM_PIN_CAP_HDMI_MASK) >> \ + HDA_PARAM_PIN_CAP_HDMI_SHIFT) +#define HDA_PARAM_PIN_CAP_BALANCED_IO_PINS(param) \ + (((param) & HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_MASK) >> \ + HDA_PARAM_PIN_CAP_BALANCED_IO_PINS_SHIFT) +#define HDA_PARAM_PIN_CAP_INPUT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_INPUT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_INPUT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_OUTPUT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_OUTPUT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_OUTPUT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_HEADPHONE_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_HEADPHONE_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_HEADPHONE_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_PRESENCE_DETECT_CAP_SHIFT) +#define HDA_PARAM_PIN_CAP_TRIGGER_REQD(param) \ + (((param) & HDA_PARAM_PIN_CAP_TRIGGER_REQD_MASK) >> \ + HDA_PARAM_PIN_CAP_TRIGGER_REQD_SHIFT) +#define HDA_PARAM_PIN_CAP_IMP_SENSE_CAP(param) \ + (((param) & HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_MASK) >> \ + HDA_PARAM_PIN_CAP_IMP_SENSE_CAP_SHIFT) + +/* Input Amplifier Capabilities */ +#define HDA_PARAM_INPUT_AMP_CAP 0x0d + +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000 +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT 31 +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000 +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT 16 +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00 +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT 8 +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK 0x0000007f +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT 0 + +#define HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_STEPSIZE(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_PARAM_INPUT_AMP_CAP_OFFSET(param) \ + (((param) & HDA_PARAM_INPUT_AMP_CAP_OFFSET_MASK) >> \ + HDA_PARAM_INPUT_AMP_CAP_OFFSET_SHIFT) + +/* Output Amplifier Capabilities */ +#define HDA_PARAM_OUTPUT_AMP_CAP 0x12 + +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK 0x80000000 +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT 31 +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK 0x007f0000 +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT 16 +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK 0x00007f00 +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT 8 +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK 0x0000007f +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT 0 + +#define HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_MUTE_CAP_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_STEPSIZE_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_NUMSTEPS_SHIFT) +#define HDA_PARAM_OUTPUT_AMP_CAP_OFFSET(param) \ + (((param) & HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_MASK) >> \ + HDA_PARAM_OUTPUT_AMP_CAP_OFFSET_SHIFT) + +/* Connection List Length */ +#define HDA_PARAM_CONN_LIST_LENGTH 0x0e + +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK 0x00000080 +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT 7 +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK 0x0000007f +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT 0 + +#define HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM(param) \ + (((param) & HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_MASK) >> \ + HDA_PARAM_CONN_LIST_LENGTH_LONG_FORM_SHIFT) +#define HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH(param) \ + (((param) & HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_MASK) >> \ + HDA_PARAM_CONN_LIST_LENGTH_LIST_LENGTH_SHIFT) + +/* Supported Power States */ +#define HDA_PARAM_SUPP_POWER_STATES 0x0f + +#define HDA_PARAM_SUPP_POWER_STATES_D3_MASK 0x00000008 +#define HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT 3 +#define HDA_PARAM_SUPP_POWER_STATES_D2_MASK 0x00000004 +#define HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT 2 +#define HDA_PARAM_SUPP_POWER_STATES_D1_MASK 0x00000002 +#define HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT 1 +#define HDA_PARAM_SUPP_POWER_STATES_D0_MASK 0x00000001 +#define HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT 0 + +#define HDA_PARAM_SUPP_POWER_STATES_D3(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D3_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D3_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D2(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D2_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D2_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D1(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D1_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D1_SHIFT) +#define HDA_PARAM_SUPP_POWER_STATES_D0(param) \ + (((param) & HDA_PARAM_SUPP_POWER_STATES_D0_MASK) >> \ + HDA_PARAM_SUPP_POWER_STATES_D0_SHIFT) + +/* Processing Capabilities */ +#define HDA_PARAM_PROCESSING_CAP 0x10 + +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK 0x0000ff00 +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT 8 +#define HDA_PARAM_PROCESSING_CAP_BENIGN_MASK 0x00000001 +#define HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT 0 + +#define HDA_PARAM_PROCESSING_CAP_NUMCOEFF(param) \ + (((param) & HDA_PARAM_PROCESSING_CAP_NUMCOEFF_MASK) >> \ + HDA_PARAM_PROCESSING_CAP_NUMCOEFF_SHIFT) +#define HDA_PARAM_PROCESSING_CAP_BENIGN(param) \ + (((param) & HDA_PARAM_PROCESSING_CAP_BENIGN_MASK) >> \ + HDA_PARAM_PROCESSING_CAP_BENIGN_SHIFT) + +/* GPIO Count */ +#define HDA_PARAM_GPIO_COUNT 0x11 + +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK 0x80000000 +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT 31 +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK 0x40000000 +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT 30 +#define HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK 0x00ff0000 +#define HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT 16 +#define HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK 0x0000ff00 +#define HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT 8 +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK 0x000000ff +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT 0 + +#define HDA_PARAM_GPIO_COUNT_GPI_WAKE(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_GPI_WAKE_MASK) >> \ + HDA_PARAM_GPIO_COUNT_GPI_WAKE_SHIFT) +#define HDA_PARAM_GPIO_COUNT_GPI_UNSOL(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_GPI_UNSOL_MASK) >> \ + HDA_PARAM_GPIO_COUNT_GPI_UNSOL_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPI(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPI_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPI_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPO(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPO_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPO_SHIFT) +#define HDA_PARAM_GPIO_COUNT_NUM_GPIO(param) \ + (((param) & HDA_PARAM_GPIO_COUNT_NUM_GPIO_MASK) >> \ + HDA_PARAM_GPIO_COUNT_NUM_GPIO_SHIFT) + +/* Volume Knob Capabilities */ +#define HDA_PARAM_VOLUME_KNOB_CAP 0x13 + +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK 0x00000080 +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT 7 +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK 0x0000007f +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT 0 + +#define HDA_PARAM_VOLUME_KNOB_CAP_DELTA(param) \ + (((param) & HDA_PARAM_VOLUME_KNOB_CAP_DELTA_MASK) >> \ + HDA_PARAM_VOLUME_KNOB_CAP_DELTA_SHIFT) +#define HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS(param) \ + (((param) & HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_MASK) >> \ + HDA_PARAM_VOLUME_KNOB_CAP_NUM_STEPS_SHIFT) + + +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK 0x0000000f +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT 0 +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK 0x000000f0 +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT 4 +#define HDA_CONFIG_DEFAULTCONF_MISC_MASK 0x00000f00 +#define HDA_CONFIG_DEFAULTCONF_MISC_SHIFT 8 +#define HDA_CONFIG_DEFAULTCONF_COLOR_MASK 0x0000f000 +#define HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT 12 +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK 0x000f0000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT 16 +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MASK 0x00f00000 +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT 20 +#define HDA_CONFIG_DEFAULTCONF_LOCATION_MASK 0x3f000000 +#define HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT 24 +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK 0xc0000000 +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT 30 + +#define HDA_CONFIG_DEFAULTCONF_SEQUENCE(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_SEQUENCE_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_SEQUENCE_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_ASSOCIATION(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_ASSOCIATION_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_ASSOCIATION_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_MISC(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_MISC_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_MISC_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_COLOR(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_COLOR_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_COLOR_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_CONNECTION_TYPE_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_DEVICE(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_DEVICE_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_DEVICE_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_LOCATION(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_LOCATION_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_LOCATION_SHIFT) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY(conf) \ + (((conf) & HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_MASK) >> \ + HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_SHIFT) + +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_JACK (0<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_NONE (1<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_FIXED (2<<30) +#define HDA_CONFIG_DEFAULTCONF_CONNECTIVITY_BOTH (3<<30) + +#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_OUT (0<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPEAKER (1<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_HP_OUT (2<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_CD (3<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_OUT (4<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_OUT (5<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_LINE (6<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MODEM_HANDSET (7<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_LINE_IN (8<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_AUX (9<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_MIC_IN (10<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_TELEPHONY (11<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_SPDIF_IN (12<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_DIGITAL_OTHER_IN (13<<20) +#define HDA_CONFIG_DEFAULTCONF_DEVICE_OTHER (15<<20) + +#endif Index: hdac_reg.h =================================================================== --- hdac_reg.h +++ hdac_reg.h @@ -0,0 +1,268 @@ +/*- + * Copyright (c) 2006 Stephane E. Potvin + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _HDAC_REG_H_ +#define _HDAC_REG_H_ + +/**************************************************************************** + * HDA Controller Register Set + ****************************************************************************/ +#define HDAC_GCAP 0x00 /* 2 - Global Capabilities*/ +#define HDAC_VMIN 0x02 /* 1 - Minor Version */ +#define HDAC_VMAJ 0x03 /* 1 - Major Version */ +#define HDAC_OUTPAY 0x04 /* 2 - Output Payload Capability */ +#define HDAC_INPAY 0x06 /* 2 - Input Payload Capability */ +#define HDAC_GCTL 0x08 /* 4 - Global Control */ +#define HDAC_WAKEEN 0x0c /* 2 - Wake Enable */ +#define HDAC_STATESTS 0x0e /* 2 - State Change Status */ +#define HDAC_GSTS 0x10 /* 2 - Global Status */ +#define HDAC_OUTSTRMPAY 0x18 /* 2 - Output Stream Payload Capability */ +#define HDAC_INSTRMPAY 0x1a /* 2 - Input Stream Payload Capability */ +#define HDAC_INTCTL 0x20 /* 4 - Interrupt Control */ +#define HDAC_INTSTS 0x24 /* 4 - Interrupt Status */ +#define HDAC_WALCLK 0x30 /* 4 - Wall Clock Counter */ +#define HDAC_SSYNC 0x38 /* 4 - Stream Synchronization */ +#define HDAC_CORBLBASE 0x40 /* 4 - CORB Lower Base Address */ +#define HDAC_CORBUBASE 0x44 /* 4 - CORB Upper Base Address */ +#define HDAC_CORBWP 0x48 /* 2 - CORB Write Pointer */ +#define HDAC_CORBRP 0x4a /* 2 - CORB Read Pointer */ +#define HDAC_CORBCTL 0x4c /* 1 - CORB Control */ +#define HDAC_CORBSTS 0x4d /* 1 - CORB Status */ +#define HDAC_CORBSIZE 0x4e /* 1 - CORB Size */ +#define HDAC_RIRBLBASE 0x50 /* 4 - RIRB Lower Base Address */ +#define HDAC_RIRBUBASE 0x54 /* 4 - RIRB Upper Base Address */ +#define HDAC_RIRBWP 0x58 /* 2 - RIRB Write Pointer */ +#define HDAC_RINTCNT 0x5a /* 2 - Response Interrupt Count */ +#define HDAC_RIRBCTL 0x5c /* 1 - RIRB Control */ +#define HDAC_RIRBSTS 0x5d /* 1 - RIRB Status */ +#define HDAC_RIRBSIZE 0x5e /* 1 - RIRB Size */ +#define HDAC_ICOI 0x60 /* 4 - Immediate Command Output Interface */ +#define HDAC_ICII 0x64 /* 4 - Immediate Command Input Interface */ +#define HDAC_ICIS 0x68 /* 2 - Immediate Command Status */ +#define HDAC_DPIBLBASE 0x70 /* 4 - DMA Position Buffer Lower Base */ +#define HDAC_DPIBUBASE 0x74 /* 4 - DMA Position Buffer Upper Base */ +#define HDAC_SDCTL0 0x80 /* 3 - Stream Descriptor Control */ +#define HDAC_SDCTL1 0x81 /* 3 - Stream Descriptor Control */ +#define HDAC_SDCTL2 0x82 /* 3 - Stream Descriptor Control */ +#define HDAC_SDSTS 0x83 /* 1 - Stream Descriptor Status */ +#define HDAC_SDLPIB 0x84 /* 4 - Link Position in Buffer */ +#define HDAC_SDCBL 0x88 /* 4 - Cyclic Buffer Length */ +#define HDAC_SDLVI 0x8C /* 2 - Last Valid Index */ +#define HDAC_SDFIFOS 0x90 /* 2 - FIFOS */ +#define HDAC_SDFMT 0x92 /* 2 - fmt */ +#define HDAC_SDBDPL 0x98 /* 4 - Buffer Descriptor Pointer Lower Base */ +#define HDAC_SDBDPU 0x9C /* 4 - Buffer Descriptor Pointer Upper Base */ + +#define _HDAC_ISDOFFSET(n, iss, oss) (0x80 + ((n) * 0x20)) +#define _HDAC_ISDCTL(n, iss, oss) (0x00 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDSTS(n, iss, oss) (0x03 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDPICB(n, iss, oss) (0x04 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDCBL(n, iss, oss) (0x08 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDLVI(n, iss, oss) (0x0c + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDFIFOD(n, iss, oss) (0x10 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDFMT(n, iss, oss) (0x12 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDBDPL(n, iss, oss) (0x18 + _HDAC_ISDOFFSET(n, iss, oss)) +#define _HDAC_ISDBDPU(n, iss, oss) (0x1c + _HDAC_ISDOFFSET(n, iss, oss)) + +#define _HDAC_OSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((n) * 0x20)) +#define _HDAC_OSDCTL(n, iss, oss) (0x00 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDSTS(n, iss, oss) (0x03 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDPICB(n, iss, oss) (0x04 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDCBL(n, iss, oss) (0x08 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDLVI(n, iss, oss) (0x0c + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDFIFOD(n, iss, oss) (0x10 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDFMT(n, iss, oss) (0x12 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDBDPL(n, iss, oss) (0x18 + _HDAC_OSDOFFSET(n, iss, oss)) +#define _HDAC_OSDBDPU(n, iss, oss) (0x1c + _HDAC_OSDOFFSET(n, iss, oss)) + +#define _HDAC_BSDOFFSET(n, iss, oss) (0x80 + ((iss) * 0x20) + ((oss) * 0x20) + ((n) * 0x20)) +#define _HDAC_BSDCTL(n, iss, oss) (0x00 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDSTS(n, iss, oss) (0x03 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDPICB(n, iss, oss) (0x04 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDCBL(n, iss, oss) (0x08 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDLVI(n, iss, oss) (0x0c + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDFIFOD(n, iss, oss) (0x10 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDFMT(n, iss, oss) (0x12 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDBDPL(n, iss, oss) (0x18 + _HDAC_BSDOFFSET(n, iss, oss)) +#define _HDAC_BSDBDBU(n, iss, oss) (0x1c + _HDAC_BSDOFFSET(n, iss, oss)) + +/**************************************************************************** + * HDA Controller Register Fields + ****************************************************************************/ + +/* GCAP - Global Capabilities */ +#define HDAC_GCAP_64OK 0x0001 +#define HDAC_GCAP_NSDO_MASK 0x0006 +#define HDAC_GCAP_NSDO_SHIFT 1 +#define HDAC_GCAP_BSS_MASK 0x00f8 +#define HDAC_GCAP_BSS_SHIFT 3 +#define HDAC_GCAP_ISS_MASK 0x0f00 +#define HDAC_GCAP_ISS_SHIFT 8 +#define HDAC_GCAP_OSS_MASK 0xf000 +#define HDAC_GCAP_OSS_SHIFT 12 + +#define HDAC_GCAP_NSDO_1SDO 0x00 +#define HDAC_GCAP_NSDO_2SDO 0x02 +#define HDAC_GCAP_NSDO_4SDO 0x04 + +#define HDAC_GCAP_BSS(gcap) \ + (((gcap) & HDAC_GCAP_BSS_MASK) >> HDAC_GCAP_BSS_SHIFT) +#define HDAC_GCAP_ISS(gcap) \ + (((gcap) & HDAC_GCAP_ISS_MASK) >> HDAC_GCAP_ISS_SHIFT) +#define HDAC_GCAP_OSS(gcap) \ + (((gcap) & HDAC_GCAP_OSS_MASK) >> HDAC_GCAP_OSS_SHIFT) +#define HDAC_GCAP_NSDO(gcap) \ + (((gcap) & HDAC_GCAP_NSDO_MASK) >> HDAC_GCAP_NSDO_SHIFT) + +/* GCTL - Global Control */ +#define HDAC_GCTL_CRST 0x00000001 +#define HDAC_GCTL_FCNTRL 0x00000002 +#define HDAC_GCTL_UNSOL 0x00000100 + +/* WAKEEN - Wake Enable */ +#define HDAC_WAKEEN_SDIWEN_MASK 0x7fff +#define HDAC_WAKEEN_SDIWEN_SHIFT 0 + +/* STATESTS - State Change Status */ +#define HDAC_STATESTS_SDIWAKE_MASK 0x7fff +#define HDAC_STATESTS_SDIWAKE_SHIFT 0 + +#define HDAC_STATESTS_SDIWAKE(statests, n) \ + (((((statests) & HDAC_STATESTS_SDIWAKE_MASK) >> \ + HDAC_STATESTS_SDIWAKE_SHIFT) >> (n)) & 0x0001) + +/* GSTS - Global Status */ +#define HDAC_GSTS_FSTS 0x0002 + +/* INTCTL - Interrut Control */ +#define HDAC_INTCTL_SIE_MASK 0x3fffffff +#define HDAC_INTCTL_SIE_SHIFT 0 +#define HDAC_INTCTL_CIE 0x40000000 +#define HDAC_INTCTL_GIE 0x80000000 + +/* INTSTS - Interrupt Status */ +#define HDAC_INTSTS_SIS_MASK 0x3fffffff +#define HDAC_INTSTS_SIS_SHIFT 0 +#define HDAC_INTSTS_CIS 0x40000000 +#define HDAC_INTSTS_GIS 0x80000000 + +/* SSYNC - Stream Synchronization */ +#define HDAC_SSYNC_SSYNC_MASK 0x3fffffff +#define HDAC_SSYNC_SSYNC_SHIFT 0 + +/* CORBWP - CORB Write Pointer */ +#define HDAC_CORBWP_CORBWP_MASK 0x00ff +#define HDAC_CORBWP_CORBWP_SHIFT 0 + +/* CORBRP - CORB Read Pointer */ +#define HDAC_CORBRP_CORBRP_MASK 0x00ff +#define HDAC_CORBRP_CORBRP_SHIFT 0 +#define HDAC_CORBRP_CORBRPRST 0x8000 + +/* CORBCTL - CORB Control */ +#define HDAC_CORBCTL_CMEIE 0x01 +#define HDAC_CORBCTL_CORBRUN 0x02 + +/* CORBSTS - CORB Status */ +#define HDAC_CORBSTS_CMEI 0x01 + +/* CORBSIZE - CORB Size */ +#define HDAC_CORBSIZE_CORBSIZE_MASK 0x03 +#define HDAC_CORBSIZE_CORBSIZE_SHIFT 0 +#define HDAC_CORBSIZE_CORBSZCAP_MASK 0xf0 +#define HDAC_CORBSIZE_CORBSZCAP_SHIFT 4 + +#define HDAC_CORBSIZE_CORBSIZE_2 0x00 +#define HDAC_CORBSIZE_CORBSIZE_16 0x01 +#define HDAC_CORBSIZE_CORBSIZE_256 0x02 + +#define HDAC_CORBSIZE_CORBSZCAP_2 0x10 +#define HDAC_CORBSIZE_CORBSZCAP_16 0x20 +#define HDAC_CORBSIZE_CORBSZCAP_256 0x40 + +#define HDAC_CORBSIZE_CORBSIZE(corbsize) \ + (((corbsize) & HDAC_CORBSIZE_CORBSIZE_MASK) >> HDAC_CORBSIZE_CORBSIZE_SHIFT) + +/* RIRBWP - RIRB Write Pointer */ +#define HDAC_RIRBWP_RIRBWP_MASK 0x00ff +#define HDAC_RIRBWP_RIRBWP_SHIFT 0 +#define HDAC_RIRBWP_RIRBWPRST 0x8000 + +/* RINTCTN - Response Interrupt Count */ +#define HDAC_RINTCNT_MASK 0x00ff +#define HDAC_RINTCNT_SHIFT 0 + +/* RIRBCTL - RIRB Control */ +#define HDAC_RIRBCTL_RINTCTL 0x01 +#define HDAC_RIRBCTL_RIRBDMAEN 0x02 +#define HDAC_RIRBCTL_RIRBOIC 0x04 + +/* RIRBSTS - RIRB Status */ +#define HDAC_RIRBSTS_RINTFL 0x01 +#define HDAC_RIRBSTS_RIRBOIS 0x04 + +/* RIRBSIZE - RIRB Size */ +#define HDAC_RIRBSIZE_RIRBSIZE_MASK 0x03 +#define HDAC_RIRBSIZE_RIRBSIZE_SHIFT 0 +#define HDAC_RIRBSIZE_RIRBSZCAP_MASK 0xf0 +#define HDAC_RIRBSIZE_RIRBSZCAP_SHIFT 4 + +#define HDAC_RIRBSIZE_RIRBSIZE_2 0x00 +#define HDAC_RIRBSIZE_RIRBSIZE_16 0x01 +#define HDAC_RIRBSIZE_RIRBSIZE_256 0x02 + +#define HDAC_RIRBSIZE_RIRBSZCAP_2 0x10 +#define HDAC_RIRBSIZE_RIRBSZCAP_16 0x20 +#define HDAC_RIRBSIZE_RIRBSZCAP_256 0x40 + +#define HDAC_RIRBSIZE_RIRBSIZE(rirbsize) \ + (((rirbsize) & HDAC_RIRBSIZE_RIRBSIZE_MASK) >> HDAC_RIRBSIZE_RIRBSIZE_SHIFT) + +/* DPLBASE - DMA Position Lower Base Address */ +#define HDAC_DPLBASE_DPLBASE_MASK 0xffffff80 +#define HDAC_DPLBASE_DPLBASE_SHIFT 7 +#define HDAC_DPLBASE_DPLBASE_DMAPBE 0x00000001 + +/* SDCTL - Stream Descriptor Control */ +#define HDAC_SDCTL_SRST 0x000001 +#define HDAC_SDCTL_RUN 0x000002 +#define HDAC_SDCTL_IOCE 0x000004 +#define HDAC_SDCTL_FEIE 0x000008 +#define HDAC_SDCTL_DEIE 0x000010 +#define HDAC_SDCTL2_STRIPE_MASK 0x03 +#define HDAC_SDCTL2_STRIPE_SHIFT 0 +#define HDAC_SDCTL2_TP 0x04 +#define HDAC_SDCTL2_DIR 0x08 +#define HDAC_SDCTL2_STRM_MASK 0xf0 +#define HDAC_SDCTL2_STRM_SHIFT 4 + +#define HDAC_SDSTS_DESE (1 << 4) +#define HDAC_SDSTS_FIFOE (1 << 3) +#define HDAC_SDSTS_BCIS (1 << 2) + +#endif Index: pci_hda.h =================================================================== --- pci_hda.h +++ pci_hda.h @@ -0,0 +1,59 @@ + +#ifndef _HDA_EMUL_H_ +#define _HDA_EMUL_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "hda_reg.h" + +/* + * HDA Debug Log + */ +#define DEBUG_HDA 1 +#if DEBUG_HDA == 1 +extern FILE *dbg; +#define DPRINTF(fmt, arg...) \ +do {fprintf(dbg, "%s-%d: " fmt, __func__, __LINE__, ##arg); \ +fflush(dbg); } while (0) +#else +#define DPRINTF(fmt, arg...) +#endif + +#define HDA_FIFO_SIZE 0x100 + +struct hda_softc; +struct hda_codec_class; + +struct hda_codec_inst { + struct hda_codec_class *codec; + struct hda_softc *hda; + struct hda_ops *hops; + uint8_t cad; + void *priv; +}; + +struct hda_codec_class { + char *name; + int (*init)(struct hda_codec_inst *hci, const char *play, const char *rec, const char *opts); + int (*reset)(struct hda_codec_inst *hci); + int (*command)(struct hda_codec_inst *hci, uint32_t cmd_data); + int (*notify)(struct hda_codec_inst *hci, uint8_t run, uint8_t stream, uint8_t dir); +}; + +struct hda_ops { + int (*signal)(struct hda_codec_inst *hci); + int (*response)(struct hda_codec_inst *hci, uint32_t response, uint8_t unsol); + int (*transfer)(struct hda_codec_inst *hci, uint8_t stream, uint8_t dir, void *buf, size_t count); +}; + +#define HDA_EMUL_SET(x) DATA_SET(hda_codec_class_set, x); + +#endif /* _HDA_EMUL_H_ */ Index: pci_hda.c =================================================================== --- pci_hda.c +++ pci_hda.c @@ -0,0 +1,1337 @@ + +#include + +#include "pci_hda.h" +#include "bhyverun.h" +#include "pci_emul.h" +#include "hdac_reg.h" + +/* + * HDA defines + */ +#define PCIR_HDCTL 0x40 +#define INTEL_VENDORID 0x8086 +#define HDA_INTEL_82801G 0x27d8 + +#define HDA_IOSS_NO 0x08 +#define HDA_OSS_NO 0x04 +#define HDA_ISS_NO 0x04 +#define HDA_CODEC_MAX 0x0f +#define HDA_LAST_OFFSET (0x2084 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20)) +#define HDA_SET_REG_TABLE_SZ (0x80 + ((HDA_ISS_NO) * 0x20) + ((HDA_OSS_NO) * 0x20)) +#define HDA_CORB_ENTRY_LEN 0x04 +#define HDA_RIRB_ENTRY_LEN 0x08 +#define HDA_BDL_ENTRY_LEN 0x10 +#define HDA_DMA_PIB_ENTRY_LEN 0x08 +#define HDA_STREAM_TAGS_CNT 0x10 +#define HDA_STREAM_REGS_BASE 0x80 +#define HDA_STREAM_REGS_LEN 0x20 + +#define HDA_DMA_ACCESS_LEN (sizeof(uint32_t)) +#define HDA_BDL_MAX_LEN 0x0100 + +#define HDAC_SDSTS_FIFORDY (1 << 5) + +#define HDA_RIRBSTS_IRQ_MASK (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS) +#define HDA_STATESTS_IRQ_MASK ((1 << HDA_CODEC_MAX) - 1) +#define HDA_SDSTS_IRQ_MASK (HDAC_SDSTS_DESE | HDAC_SDSTS_FIFOE | HDAC_SDSTS_BCIS) + +/* + * HDA data structures + */ + +struct hda_softc; + +typedef void (*hda_set_reg_handler)(struct hda_softc *sc, uint32_t offset, uint32_t old); + +struct hda_bdle { + uint32_t addrl; + uint32_t addrh; + uint32_t len; + uint32_t ioc; +} __packed; + +struct hda_bdle_desc { + void *addr; + uint32_t len; + uint8_t ioc; +}; + +struct hda_codec_cmd_ctl { + char *name; + void *dma_vaddr; + uint16_t size; + uint16_t wp; + uint16_t rp; + uint8_t run; +}; + +struct hda_stream_desc { + uint8_t stream; + uint8_t run; + uint8_t dir; + + /* bp is the no. of bytes transferred in the current bdle */ + uint32_t bp; + /* be is the no. of bdles transferred in the bdl */ + uint32_t be; + + struct hda_bdle_desc bdl[HDA_BDL_MAX_LEN]; + uint32_t bdl_cnt; +}; + +struct hda_softc { + struct pci_devinst *pci_dev; + uint32_t regs[HDA_LAST_OFFSET]; + + uint8_t lintr; + uint8_t rirb_cnt; + uint64_t wall_clock_start; + + struct hda_codec_cmd_ctl corb; + struct hda_codec_cmd_ctl rirb; + + struct hda_codec_inst *codecs[HDA_CODEC_MAX]; + uint8_t codecs_no; + + /* Base Address of the DMA Position Buffer */ + void *dma_pib_vaddr; + + struct hda_stream_desc streams[HDA_IOSS_NO]; + /* 2 tables for output and input */ + uint8_t stream_map[2][HDA_STREAM_TAGS_CNT]; +}; + +/* + * HDA module function declarations + */ +static inline void +hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t value); +static inline uint32_t +hda_get_reg_by_offset(struct hda_softc *sc, uint32_t offset); +static inline void +hda_set_field_by_offset(struct hda_softc *sc, 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(const char *opts); +static void +hda_update_intr(struct hda_softc *sc); +static void +hda_response_interrupt(struct hda_softc *sc); +static int +hda_codec_constructor(struct hda_softc *sc, 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 int +hda_send_command(struct hda_softc *sc, uint32_t verb); +static int +hda_notify_codecs(struct hda_softc *sc, uint8_t run, uint8_t stream, uint8_t dir); +static void +hda_reset(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 int +hda_stream_start(struct hda_softc *sc, uint8_t stream_ind); +static int +hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind); +static uint32_t +hda_read(struct hda_softc *sc, uint32_t offset); +static int +hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size, uint32_t value); + +static inline void +hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p); +static int +hda_corb_start(struct hda_softc *sc); +static int +hda_corb_run(struct hda_softc *sc); +static int +hda_rirb_start(struct hda_softc *sc); + +static void * +hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr, size_t len); +static void +hda_dma_st_dword(void *dma_vaddr, uint32_t data); +static uint32_t +hda_dma_ld_dword(void *dma_vaddr); + +static inline uint8_t +hda_get_stream_by_offsets(uint32_t offset, uint8_t reg_offset); +static inline uint32_t +hda_get_offset_stream(uint8_t stream_ind); + +static void +hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_statests(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_corbctl(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_rirbctl(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_rirbsts(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old); +static void +hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old); + +static int +hda_signal_state_change(struct hda_codec_inst *hci); +static int +hda_response(struct hda_codec_inst *hci, uint32_t response, uint8_t unsol); +static int +hda_transfer(struct hda_codec_inst *hci, uint8_t stream, 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 uint64_t hda_get_clock_ns(void); + +/* + * PCI HDA function declarations + */ +static int +pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts); +static void +pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + 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, + int baridx, uint64_t offset, int size); +/* + * HDA global data + */ + +static const hda_set_reg_handler hda_set_reg_table[] = { + [HDAC_GCTL] = hda_set_gctl, + [HDAC_STATESTS] = hda_set_statests, + [HDAC_CORBWP] = hda_set_corbwp, + [HDAC_CORBCTL] = hda_set_corbctl, + [HDAC_RIRBCTL] = hda_set_rirbctl, + [HDAC_RIRBSTS] = hda_set_rirbsts, + [HDAC_DPIBLBASE] = hda_set_dpiblbase, + +#define HDAC_ISTREAM(n, iss, oss) \ + [_HDAC_ISDCTL(n, iss, oss)] = hda_set_sdctl, \ + [_HDAC_ISDCTL(n, iss, oss) + 2] = hda_set_sdctl2, \ + [_HDAC_ISDSTS(n, iss, oss)] = hda_set_sdsts, \ + +#define HDAC_OSTREAM(n, iss, oss) \ + [_HDAC_OSDCTL(n, iss, oss)] = hda_set_sdctl, \ + [_HDAC_OSDCTL(n, iss, oss) + 2] = hda_set_sdctl2, \ + [_HDAC_OSDSTS(n, iss, oss)] = hda_set_sdsts, \ + + HDAC_ISTREAM(0, HDA_ISS_NO, HDA_OSS_NO) + HDAC_ISTREAM(1, HDA_ISS_NO, HDA_OSS_NO) + HDAC_ISTREAM(2, HDA_ISS_NO, HDA_OSS_NO) + HDAC_ISTREAM(3, HDA_ISS_NO, HDA_OSS_NO) + + HDAC_OSTREAM(0, HDA_ISS_NO, HDA_OSS_NO) + HDAC_OSTREAM(1, HDA_ISS_NO, HDA_OSS_NO) + HDAC_OSTREAM(2, HDA_ISS_NO, HDA_OSS_NO) + HDAC_OSTREAM(3, HDA_ISS_NO, HDA_OSS_NO) + + [HDA_SET_REG_TABLE_SZ] = NULL, +}; + +static const uint16_t hda_corb_sizes[] = { + [HDAC_CORBSIZE_CORBSIZE_2] = 2, + [HDAC_CORBSIZE_CORBSIZE_16] = 16, + [HDAC_CORBSIZE_CORBSIZE_256] = 256, + [HDAC_CORBSIZE_CORBSIZE_MASK] = 0, +}; + +static const uint16_t hda_rirb_sizes[] = { + [HDAC_RIRBSIZE_RIRBSIZE_2] = 2, + [HDAC_RIRBSIZE_RIRBSIZE_16] = 16, + [HDAC_RIRBSIZE_RIRBSIZE_256] = 256, + [HDAC_RIRBSIZE_RIRBSIZE_MASK] = 0, +}; + +static struct hda_ops hops = { + .signal = hda_signal_state_change, + .response = hda_response, + .transfer = hda_transfer, +}; + +struct pci_devemu pci_de_hda = { + .pe_emu = "hda", + .pe_init = pci_hda_init, + .pe_barwrite = pci_hda_write, + .pe_barread = pci_hda_read +}; + +PCI_EMUL_SET(pci_de_hda); + +SET_DECLARE(hda_codec_class_set, struct hda_codec_class); + +#if DEBUG_HDA == 1 +FILE *dbg; +#endif + +/* + * HDA module function definitions + */ + +static inline void +hda_set_reg_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t value) +{ + assert(offset < HDA_LAST_OFFSET); + sc->regs[offset] = value; + + return; +} + +static inline uint32_t +hda_get_reg_by_offset(struct hda_softc *sc, uint32_t offset) +{ + assert(offset < HDA_LAST_OFFSET); + return sc->regs[offset]; +} + +static inline void +hda_set_field_by_offset(struct hda_softc *sc, uint32_t offset, uint32_t mask, uint32_t value) +{ + uint32_t reg_value = 0; + + reg_value = hda_get_reg_by_offset(sc, offset); + + reg_value &= ~mask; + reg_value |= (value & mask); + + hda_set_reg_by_offset(sc, offset, reg_value); + + return; +} + +static uint8_t +hda_parse_config(const char *opts, const char *key, char *val) +{ + char buf[64]; + char *s = buf; + char *tmp = NULL; + int len; + int i; + + if (!opts) + return 0; + + len = strlen(opts); + + if (len >= 64) { + DPRINTF("Opts too big\n"); + return 0; + } + + DPRINTF("opts: %s\n", 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 *hda_init(const char *opts) +{ + struct hda_softc *sc = NULL; + struct hda_codec_class *codec = NULL; + char play[64]; + char rec[64]; + int err, p, r; + +#if DEBUG_HDA == 1 + dbg = fopen("/tmp/bhyve_hda.log", "w+"); +#endif + + DPRINTF("opts: %s\n", opts); + + sc = calloc(1, sizeof(*sc)); + if (!sc) + return NULL; + + hda_reset_regs(sc); + + /* + * TODO search all the codecs declared in opts + * For now we play with one single codec + */ + codec = hda_find_codec_class("hda_codec"); + if (codec) { + p = hda_parse_config(opts, "play=", play); + r = hda_parse_config(opts, "rec=", rec); + DPRINTF("play: %s rec: %s\n", play, rec); + if (p | r) { + err = hda_codec_constructor(sc, codec, p ? play : NULL, r ? rec : NULL, NULL); + assert(!err); + } + } + + return sc; +} + +static void +hda_update_intr(struct hda_softc *sc) +{ + struct pci_devinst *pi = sc->pci_dev; + uint32_t intctl = hda_get_reg_by_offset(sc, HDAC_INTCTL); + uint32_t intsts = 0; + uint32_t sdsts = 0; + uint32_t rirbsts = 0; + uint32_t wakeen = 0; + uint32_t statests = 0; + uint32_t off = 0; + int i; + + /* update the CIS bits */ + rirbsts = hda_get_reg_by_offset(sc, HDAC_RIRBSTS); + if (rirbsts & (HDAC_RIRBSTS_RINTFL | HDAC_RIRBSTS_RIRBOIS)) + intsts |= HDAC_INTSTS_CIS; + + wakeen = hda_get_reg_by_offset(sc, HDAC_WAKEEN); + statests = hda_get_reg_by_offset(sc, HDAC_STATESTS); + if (statests & wakeen) + intsts |= HDAC_INTSTS_CIS; + + /* update the SIS bits */ + for (i = 0; i < HDA_IOSS_NO; i++) { + off = hda_get_offset_stream(i); + sdsts = hda_get_reg_by_offset(sc, off + HDAC_SDSTS); + if (sdsts & HDAC_SDSTS_BCIS) + intsts |= (1 << i); + } + + /* update the GIS bit */ + if (intsts) + intsts |= HDAC_INTSTS_GIS; + + hda_set_reg_by_offset(sc, HDAC_INTSTS, intsts); + + if ((intctl & HDAC_INTCTL_GIE) && ((intsts & ~HDAC_INTSTS_GIS) & intctl)) { + if (!sc->lintr) { + pci_lintr_assert(pi); + sc->lintr = 1; + } + } else { + if (sc->lintr) { + pci_lintr_deassert(pi); + sc->lintr = 0; + } + } + + return; +} + +static void +hda_response_interrupt(struct hda_softc *sc) +{ + uint8_t rirbctl = hda_get_reg_by_offset(sc, HDAC_RIRBCTL); + + if ((rirbctl & HDAC_RIRBCTL_RINTCTL) && sc->rirb_cnt) { + sc->rirb_cnt = 0; + hda_set_field_by_offset(sc, HDAC_RIRBSTS, HDAC_RIRBSTS_RINTFL, HDAC_RIRBSTS_RINTFL); + hda_update_intr(sc); + } + + return; +} + +static int +hda_codec_constructor(struct hda_softc *sc, struct hda_codec_class *codec, + const char *play, const char *rec, const char *opts) +{ + struct hda_codec_inst *hci = NULL; + + if (sc->codecs_no >= HDA_CODEC_MAX) + return -1; + + hci = calloc(1, sizeof(struct hda_codec_inst)); + if (!hci) + return -1; + + hci->hda = sc; + hci->hops = &hops; + hci->cad = sc->codecs_no; + hci->codec = codec; + + sc->codecs[sc->codecs_no++] = hci; + + if (!codec->init) { + DPRINTF("This codec does not implement the init function\n"); + return -1; + } + + return codec->init(hci, play, rec, opts); +} + +static struct hda_codec_class * +hda_find_codec_class(const char *name) +{ + struct hda_codec_class **pdpp = NULL, *pdp = NULL; + + SET_FOREACH(pdpp, hda_codec_class_set) { + pdp = *pdpp; + if (!strcmp(pdp->name, name)) { + return (pdp); + } + } + + return NULL; +} + +static int +hda_send_command(struct hda_softc *sc, uint32_t verb) +{ + struct hda_codec_inst *hci = NULL; + struct hda_codec_class *codec = NULL; + uint8_t cad = (verb >> HDA_CMD_CAD_SHIFT) & 0x0f; + + hci = sc->codecs[cad]; + if (!hci) + return -1; + + DPRINTF("cad: 0x%x verb: 0x%x\n", cad, verb); + + codec = hci->codec; + assert(codec); + + if (!codec->command) { + DPRINTF("This codec does not implement the command function\n"); + return -1; + } + + return codec->command(hci, verb); +} + +static int +hda_notify_codecs(struct hda_softc *sc, uint8_t run, uint8_t stream, uint8_t dir) +{ + struct hda_codec_inst *hci = NULL; + struct hda_codec_class *codec = NULL; + int err; + int i; + + /* Notify each codec */ + for (i = 0; i < sc->codecs_no; i++) { + hci = sc->codecs[i]; + assert(hci); + + codec = hci->codec; + assert(codec); + + if (codec->notify) { + err = codec->notify(hci, run, stream, dir); + if (!err) + break; + } + } + + return i == sc->codecs_no ? (-1) : 0; +} + +static void +hda_reset(struct hda_softc *sc) +{ + int i; + struct hda_codec_inst *hci = NULL; + struct hda_codec_class *codec = NULL; + + hda_reset_regs(sc); + + /* Reset each codec */ + for (i = 0; i < sc->codecs_no; i++) { + hci = sc->codecs[i]; + assert(hci); + + codec = hci->codec; + assert(codec); + + if (codec->reset) + codec->reset(hci); + } + + sc->wall_clock_start = hda_get_clock_ns(); + + return; +} + +static void +hda_reset_regs(struct hda_softc *sc) +{ + uint32_t off = 0; + uint8_t i; + + DPRINTF("Reset the HDA controller registers ...\n"); + + memset(sc->regs, 0, sizeof(sc->regs)); + + hda_set_reg_by_offset(sc, HDAC_GCAP, + HDAC_GCAP_64OK | + (HDA_ISS_NO << HDAC_GCAP_ISS_SHIFT) | + (HDA_OSS_NO << HDAC_GCAP_OSS_SHIFT)); + hda_set_reg_by_offset(sc, HDAC_VMAJ, 0x01); + hda_set_reg_by_offset(sc, HDAC_OUTPAY, 0x3c); + hda_set_reg_by_offset(sc, HDAC_INPAY, 0x1d); + hda_set_reg_by_offset(sc, HDAC_CORBSIZE, HDAC_CORBSIZE_CORBSZCAP_256 | HDAC_CORBSIZE_CORBSIZE_256); + hda_set_reg_by_offset(sc, HDAC_RIRBSIZE, HDAC_RIRBSIZE_RIRBSZCAP_256 | HDAC_RIRBSIZE_RIRBSIZE_256); + + for (i = 0; i < HDA_IOSS_NO; i++) { + off = hda_get_offset_stream(i); + hda_set_reg_by_offset(sc, off + HDAC_SDFIFOS, HDA_FIFO_SIZE); + } + + return; +} + +static void +hda_stream_reset(struct hda_softc *sc, uint8_t stream_ind) +{ + struct hda_stream_desc *st = &sc->streams[stream_ind]; + uint32_t off = hda_get_offset_stream(stream_ind); + + DPRINTF("Reset the HDA stream: 0x%x\n", stream_ind); + + /* Reset the Stream Descriptor registers */ + memset(sc->regs + HDA_STREAM_REGS_BASE + off, 0, HDA_STREAM_REGS_LEN); + + /* Reset the Stream Descriptor */ + memset(st, 0, sizeof(*st)); + + hda_set_field_by_offset(sc, off + HDAC_SDSTS, HDAC_SDSTS_FIFORDY, HDAC_SDSTS_FIFORDY); + hda_set_field_by_offset(sc, off + HDAC_SDCTL0, HDAC_SDCTL_SRST, HDAC_SDCTL_SRST); + + return; +} + +static int +hda_stream_start(struct hda_softc *sc, uint8_t stream_ind) +{ + struct hda_stream_desc *st = &sc->streams[stream_ind]; + struct hda_bdle_desc *bdle_desc = NULL; + struct hda_bdle *bdle = NULL; + uint32_t lvi = 0; + uint32_t bdl_cnt = 0; + uint64_t bdpl = 0; + uint64_t bdpu = 0; + uint64_t bdl_paddr = 0; + void *bdl_vaddr = NULL; + uint32_t bdle_sz = 0; + uint64_t bdle_addrl = 0; + uint64_t bdle_addrh = 0; + uint64_t bdle_paddr = 0; + void *bdle_vaddr = NULL; + uint32_t off = hda_get_offset_stream(stream_ind); + uint32_t sdctl = 0; + uint8_t strm = 0; + uint8_t dir = 0; + int i; + + assert(!st->run); + + lvi = hda_get_reg_by_offset(sc, off + HDAC_SDLVI); + bdpl = hda_get_reg_by_offset(sc, off + HDAC_SDBDPL); + bdpu = hda_get_reg_by_offset(sc, off + HDAC_SDBDPU); + + bdl_cnt = lvi + 1; + assert(bdl_cnt <= HDA_BDL_MAX_LEN); + + bdl_paddr = bdpl | (bdpu << 32); + bdl_vaddr = hda_dma_get_vaddr(sc, bdl_paddr, HDA_BDL_ENTRY_LEN * bdl_cnt); + if (!bdl_vaddr) { + DPRINTF("Fail to get the guest virtual address\n"); + return -1; + } + + DPRINTF("stream: 0x%x bdl_cnt: 0x%x bdl_paddr: 0x%lx\n", stream_ind, bdl_cnt, bdl_paddr); + + st->bdl_cnt = bdl_cnt; + + bdle = (struct hda_bdle *)bdl_vaddr; + for (i = 0; i < bdl_cnt; i++, bdle++) { + bdle_sz = bdle->len; + assert(!(bdle_sz % HDA_DMA_ACCESS_LEN)); + + bdle_addrl = bdle->addrl; + bdle_addrh = bdle->addrh; + + bdle_paddr = bdle_addrl | (bdle_addrh << 32); + bdle_vaddr = hda_dma_get_vaddr(sc, bdle_paddr, bdle_sz); + if (!bdle_vaddr) { + DPRINTF("Fail to get the guest virtual address\n"); + return -1; + } + + bdle_desc = &st->bdl[i]; + bdle_desc->addr = bdle_vaddr; + bdle_desc->len = bdle_sz; + bdle_desc->ioc = bdle->ioc; + + DPRINTF("bdle: 0x%x bdle_sz: 0x%x\n", i, bdle_sz); + } + + sdctl = hda_get_reg_by_offset(sc, off + HDAC_SDCTL0); + strm = (sdctl >> 20) & 0x0f; + dir = stream_ind >= HDA_ISS_NO; + + DPRINTF("strm: 0x%x, dir: 0x%x\n", strm, dir); + + sc->stream_map[dir][strm] = stream_ind; + st->stream = strm; + st->dir = dir; + st->bp = 0; + st->be = 0; + + hda_set_pib(sc, stream_ind, 0); + + st->run = 1; + + hda_notify_codecs(sc, 1, strm, dir); + + return 0; +} + +static int +hda_stream_stop(struct hda_softc *sc, uint8_t stream_ind) +{ + struct hda_stream_desc *st = &sc->streams[stream_ind]; + uint8_t strm = st->stream; + uint8_t dir = st->dir; + + DPRINTF("stream: 0x%x, strm: 0x%x, dir: 0x%x\n", stream_ind, strm, dir); + + st->run = 0; + + hda_notify_codecs(sc, 0, strm, dir); + + return 0; +} + +static uint32_t +hda_read(struct hda_softc *sc, uint32_t offset) +{ + if (offset == HDAC_WALCLK) + return (24 * (hda_get_clock_ns() - sc->wall_clock_start) / 1000); + + return hda_get_reg_by_offset(sc, offset); +} + +static int +hda_write(struct hda_softc *sc, uint32_t offset, uint8_t size, uint32_t value) +{ + uint32_t old = hda_get_reg_by_offset(sc, offset); + uint32_t masks[] = {0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff, 0xffffffff}; + hda_set_reg_handler set_reg_handler = hda_set_reg_table[offset]; + + hda_set_field_by_offset(sc, offset, masks[size], value); + + if (set_reg_handler) + set_reg_handler(sc, offset, old); + + return 0; +} + +static inline void +hda_print_cmd_ctl_data(struct hda_codec_cmd_ctl *p) +{ +#if DEBUG_HDA == 1 + char *name = p->name; +#endif + DPRINTF("%s size: %d\n", name, p->size); + DPRINTF("%s dma_vaddr: %p\n", name, p->dma_vaddr); + DPRINTF("%s wp: 0x%x\n", name, p->wp); + DPRINTF("%s rp: 0x%x\n", name, p->rp); + + return; +} + +static int +hda_corb_start(struct hda_softc *sc) +{ + struct hda_codec_cmd_ctl *corb = &sc->corb; + uint8_t corbsize = 0; + uint64_t corblbase = 0; + uint64_t corbubase = 0; + uint64_t corbpaddr = 0; + + corb->name = "CORB"; + + corbsize = hda_get_reg_by_offset(sc, HDAC_CORBSIZE) & HDAC_CORBSIZE_CORBSIZE_MASK; + corb->size = hda_corb_sizes[corbsize]; + + if (!corb->size) { + DPRINTF("Invalid corb size\n"); + return -1; + } + + corblbase = hda_get_reg_by_offset(sc, HDAC_CORBLBASE); + corbubase = hda_get_reg_by_offset(sc, HDAC_CORBUBASE); + + corbpaddr = corblbase | (corbubase << 32); + DPRINTF("CORB dma_paddr: %p\n", (void *)corbpaddr); + + corb->dma_vaddr = hda_dma_get_vaddr(sc, corbpaddr, HDA_CORB_ENTRY_LEN * corb->size); + if (!corb->dma_vaddr) { + DPRINTF("Fail to get the guest virtual address\n"); + return -1; + } + + corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP); + corb->rp = hda_get_reg_by_offset(sc, HDAC_CORBRP); + + corb->run = 1; + + hda_print_cmd_ctl_data(corb); + + return 0; +} + +static int +hda_corb_run(struct hda_softc *sc) +{ + struct hda_codec_cmd_ctl *corb = &sc->corb; + uint32_t verb = 0; + int err; + + corb->wp = hda_get_reg_by_offset(sc, HDAC_CORBWP); + + while (corb->rp != corb->wp && corb->run) { + corb->rp++; + corb->rp %= corb->size; + + verb = hda_dma_ld_dword(corb->dma_vaddr + HDA_CORB_ENTRY_LEN * corb->rp); + + err = hda_send_command(sc, verb); + assert(!err); + } + + hda_set_reg_by_offset(sc, HDAC_CORBRP, corb->rp); + + if (corb->run) + hda_response_interrupt(sc); + + return 0; +} + +static int +hda_rirb_start(struct hda_softc *sc) +{ + struct hda_codec_cmd_ctl *rirb = &sc->rirb; + uint8_t rirbsize = 0; + uint64_t rirblbase = 0; + uint64_t rirbubase = 0; + uint64_t rirbpaddr = 0; + + rirb->name = "RIRB"; + + rirbsize = hda_get_reg_by_offset(sc, HDAC_RIRBSIZE) & HDAC_RIRBSIZE_RIRBSIZE_MASK; + rirb->size = hda_rirb_sizes[rirbsize]; + + if (!rirb->size) { + DPRINTF("Invalid rirb size\n"); + return -1; + } + + rirblbase = hda_get_reg_by_offset(sc, HDAC_RIRBLBASE); + rirbubase = hda_get_reg_by_offset(sc, HDAC_RIRBUBASE); + + rirbpaddr = rirblbase | (rirbubase << 32); + DPRINTF("RIRB dma_paddr: %p\n", (void *)rirbpaddr); + + rirb->dma_vaddr = hda_dma_get_vaddr(sc, rirbpaddr, HDA_RIRB_ENTRY_LEN * rirb->size); + if (!rirb->dma_vaddr) { + DPRINTF("Fail to get the guest virtual address\n"); + return -1; + } + + rirb->wp = hda_get_reg_by_offset(sc, HDAC_RIRBWP); + rirb->rp = 0x0000; + + rirb->run = 1; + + hda_print_cmd_ctl_data(rirb); + + return 0; +} + +static void * +hda_dma_get_vaddr(struct hda_softc *sc, uint64_t dma_paddr, size_t len) +{ + struct pci_devinst *pi = sc->pci_dev; + + assert(pi); + + return paddr_guest2host(pi->pi_vmctx, (uintptr_t)dma_paddr, len); +} + +static void +hda_dma_st_dword(void *dma_vaddr, uint32_t data) +{ + *(uint32_t*)dma_vaddr = data; + + return; +} + +static uint32_t +hda_dma_ld_dword(void *dma_vaddr) +{ + return *(uint32_t*)dma_vaddr; +} + +static inline uint8_t +hda_get_stream_by_offsets(uint32_t offset, uint8_t reg_offset) +{ + uint8_t stream_ind = (offset - reg_offset) >> 5; + + assert(stream_ind < HDA_IOSS_NO); + + return stream_ind; +} + +static inline uint32_t +hda_get_offset_stream(uint8_t stream_ind) +{ + return stream_ind << 5; +} + +static void +hda_set_gctl(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + + if (!(value & HDAC_GCTL_CRST)) { + hda_reset(sc); + } + + return; +} + +static void +hda_set_statests(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + + hda_set_reg_by_offset(sc, offset, old); + + /* clear the corresponding bits written by the software (guest) */ + hda_set_field_by_offset(sc, offset, value & HDA_STATESTS_IRQ_MASK, 0); + + hda_update_intr(sc); + + return; +} + +static void +hda_set_corbwp(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + hda_corb_run(sc); + + return; +} + +static void +hda_set_corbctl(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + int err; + struct hda_codec_cmd_ctl *corb = NULL; + + if (value & HDAC_CORBCTL_CORBRUN) { + if (!(old & HDAC_CORBCTL_CORBRUN)) { + err = hda_corb_start(sc); + assert(!err); + } + } else { + corb = &sc->corb; + memset(corb, 0, sizeof(*corb)); + } + + hda_corb_run(sc); + + return; +} + +static void +hda_set_rirbctl(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + int err; + struct hda_codec_cmd_ctl *rirb = NULL; + + if (value & HDAC_RIRBCTL_RIRBDMAEN) { + err = hda_rirb_start(sc); + assert(!err); + } else { + rirb = &sc->rirb; + memset(rirb, 0, sizeof(*rirb)); + } + + return; +} + +static void +hda_set_rirbsts(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + + hda_set_reg_by_offset(sc, offset, old); + + /* clear the corresponding bits written by the software (guest) */ + hda_set_field_by_offset(sc, offset, value & HDA_RIRBSTS_IRQ_MASK, 0); + + hda_update_intr(sc); + + return; +} + +static void +hda_set_dpiblbase(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + uint64_t dpiblbase = 0; + uint64_t dpibubase = 0; + uint64_t dpibpaddr = 0; + + if ((value & HDAC_DPLBASE_DPLBASE_DMAPBE) != (old & HDAC_DPLBASE_DPLBASE_DMAPBE)) { + if (value & HDAC_DPLBASE_DPLBASE_DMAPBE) { + dpiblbase = value & HDAC_DPLBASE_DPLBASE_MASK; + dpibubase = hda_get_reg_by_offset(sc, HDAC_DPIBUBASE); + + dpibpaddr = dpiblbase | (dpibubase << 32); + DPRINTF("DMA Position In Buffer dma_paddr: %p\n", (void *)dpibpaddr); + + sc->dma_pib_vaddr = hda_dma_get_vaddr(sc, dpibpaddr, HDA_DMA_PIB_ENTRY_LEN * HDA_IOSS_NO); + if (!sc->dma_pib_vaddr) { + DPRINTF("Fail to get the guest virtual address\n"); + assert(0); + } + } else { + DPRINTF("DMA Position In Buffer Reset\n"); + sc->dma_pib_vaddr = NULL; + } + } + + return; +} + +static void +hda_set_sdctl(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint8_t stream_ind = hda_get_stream_by_offsets(offset, HDAC_SDCTL0); + uint32_t value = hda_get_reg_by_offset(sc, offset); + int err; + + DPRINTF("stream_ind: 0x%x old: 0x%x value: 0x%x\n", stream_ind, old, value); + + if (value & HDAC_SDCTL_SRST) { + hda_stream_reset(sc, stream_ind); + } + + if ((value & HDAC_SDCTL_RUN) != (old & HDAC_SDCTL_RUN)) { + if (value & HDAC_SDCTL_RUN) { + err = hda_stream_start(sc, stream_ind); + assert(!err); + } else { + err = hda_stream_stop(sc, stream_ind); + assert(!err); + } + } + + return; +} + +static void +hda_set_sdctl2(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + + hda_set_field_by_offset(sc, offset - 2, 0x00ff0000, value << 16); + + return; +} + +static void +hda_set_sdsts(struct hda_softc *sc, uint32_t offset, uint32_t old) +{ + uint32_t value = hda_get_reg_by_offset(sc, offset); + + hda_set_reg_by_offset(sc, offset, old); + + /* clear the corresponding bits written by the software (guest) */ + hda_set_field_by_offset(sc, offset, value & HDA_SDSTS_IRQ_MASK, 0); + + hda_update_intr(sc); + + return; +} + +static int +hda_signal_state_change(struct hda_codec_inst *hci) +{ + struct hda_softc *sc = NULL; + uint32_t sdiwake = 0; + + assert(hci); + assert(hci->hda); + + DPRINTF("cad: 0x%x\n", hci->cad); + + sc = hci->hda; + sdiwake = 1 << hci->cad; + + hda_set_field_by_offset(sc, HDAC_STATESTS, sdiwake, sdiwake); + hda_update_intr(sc); + + return 0; +} + +static int +hda_response(struct hda_codec_inst *hci, uint32_t response, uint8_t unsol) +{ + struct hda_softc *sc = NULL; + struct hda_codec_cmd_ctl *rirb = NULL; + uint32_t response_ex = 0; + uint8_t rintcnt = 0; + + assert(hci); + assert(hci->cad <= HDA_CODEC_MAX); + + response_ex = hci->cad | unsol; + + sc = hci->hda; + assert(sc); + + rirb = &sc->rirb; + + if (rirb->run) { + rirb->wp++; + rirb->wp %= rirb->size; + + hda_dma_st_dword(rirb->dma_vaddr + HDA_RIRB_ENTRY_LEN * rirb->wp, response); + hda_dma_st_dword(rirb->dma_vaddr + HDA_RIRB_ENTRY_LEN * rirb->wp + 0x04, response_ex); + + hda_set_reg_by_offset(sc, HDAC_RIRBWP, rirb->wp); + + sc->rirb_cnt++; + } + + rintcnt = hda_get_reg_by_offset(sc, HDAC_RINTCNT); + if (sc->rirb_cnt == rintcnt) + hda_response_interrupt(sc); + + return 0; +} + +static int +hda_transfer(struct hda_codec_inst *hci, uint8_t stream, uint8_t dir, void *buf, size_t count) +{ + struct hda_softc *sc = NULL; + struct hda_stream_desc *st = NULL; + struct hda_bdle_desc *bdl = NULL; + struct hda_bdle_desc *bdle_desc = NULL; + uint8_t stream_ind = 0; + uint32_t lpib = 0; + uint32_t off = 0; + size_t left = 0; + uint8_t irq = 0; + + assert(hci); + assert(hci->hda); + assert(buf); + assert(!(count % HDA_DMA_ACCESS_LEN)); + + if (!stream) { + DPRINTF("Invalid stream\n"); + return -1; + } + + sc = hci->hda; + + assert(stream < HDA_STREAM_TAGS_CNT); + stream_ind = sc->stream_map[dir][stream]; + + if (!dir) + assert(stream_ind < HDA_ISS_NO); + else + assert(stream_ind >= HDA_ISS_NO && stream_ind < HDA_IOSS_NO); + + st = &sc->streams[stream_ind]; + if (!st->run) { + DPRINTF("Stream 0x%x stopped\n", stream); + return -1; + } + + assert(st->stream == stream); + + off = hda_get_offset_stream(stream_ind); + + lpib = hda_get_reg_by_offset(sc, off + HDAC_SDLPIB); + + bdl = st->bdl; + + assert(st->be < st->bdl_cnt); + assert(st->bp < bdl[st->be].len); + + left = count; + while (left) { + bdle_desc = &bdl[st->be]; + + if (dir) + *(uint32_t *)buf = hda_dma_ld_dword(bdle_desc->addr + st->bp); + else + hda_dma_st_dword(bdle_desc->addr + st->bp, *(uint32_t *)buf); + + buf += HDA_DMA_ACCESS_LEN; + st->bp += HDA_DMA_ACCESS_LEN; + lpib += HDA_DMA_ACCESS_LEN; + left -= HDA_DMA_ACCESS_LEN; + + if (st->bp == bdle_desc->len) { + st->bp = 0; + if (bdle_desc->ioc) + irq = 1; + st->be++; + if (st->be == st->bdl_cnt) { + st->be = 0; + lpib = 0; + } + bdle_desc = &bdl[st->be]; + } + } + + hda_set_pib(sc, stream_ind, lpib); + + if (irq) { + hda_set_field_by_offset(sc, off + HDAC_SDSTS, HDAC_SDSTS_BCIS, HDAC_SDSTS_BCIS); + hda_update_intr(sc); + } + + return 0; +} + +static void +hda_set_pib(struct hda_softc *sc, uint8_t stream_ind, uint32_t pib) +{ + uint32_t off = hda_get_offset_stream(stream_ind); + + hda_set_reg_by_offset(sc, off + HDAC_SDLPIB, pib); + hda_set_reg_by_offset(sc, 0x2000 + off + HDAC_SDLPIB, pib); /* LPIB Alias */ + if (sc->dma_pib_vaddr) + *(uint32_t *)(sc->dma_pib_vaddr + stream_ind * HDA_DMA_PIB_ENTRY_LEN) = pib; + + return; +} + +static uint64_t hda_get_clock_ns(void) +{ + struct timespec ts; + int err; + + err = clock_gettime(CLOCK_MONOTONIC, &ts); + assert(!err); + + return ts.tv_sec * 1000000000LL + ts.tv_nsec; +} + +/* + * PCI HDA function definitions + */ +static int +pci_hda_init(struct vmctx *ctx, struct pci_devinst *pi, char *opts) +{ + struct hda_softc *sc = NULL; + + assert(ctx != NULL); + assert(pi != NULL); + + pci_set_cfgdata16(pi, PCIR_VENDOR, INTEL_VENDORID); + pci_set_cfgdata16(pi, PCIR_DEVICE, HDA_INTEL_82801G); + + pci_set_cfgdata8(pi, PCIR_SUBCLASS, PCIS_MULTIMEDIA_HDA); + pci_set_cfgdata8(pi, PCIR_CLASS, PCIC_MULTIMEDIA); + + /* select the Intel HDA mode */ + pci_set_cfgdata8(pi, PCIR_HDCTL, 0x01); + + /* allocate one BAR register for the Memory address offsets */ + pci_emul_alloc_bar(pi, 0, PCIBAR_MEM32, HDA_LAST_OFFSET); + + /* allocate an IRQ pin for our slot */ + pci_lintr_request(pi); + + sc = hda_init(opts); + if (!sc) + return -1; + + sc->pci_dev = pi; + pi->pi_arg = sc; + + return 0; +} + +static void +pci_hda_write(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size, uint64_t value) +{ + struct hda_softc *sc = pi->pi_arg; + int err; + + assert(sc); + assert(baridx == 0); + assert(size <= 4); + + DPRINTF("offset: 0x%lx value: 0x%lx\n", offset, value); + + err = hda_write(sc, offset, size, value); + assert(!err); + + return; +} + +static uint64_t +pci_hda_read(struct vmctx *ctx, int vcpu, struct pci_devinst *pi, + int baridx, uint64_t offset, int size) +{ + struct hda_softc *sc = pi->pi_arg; + uint64_t value = 0; + + assert(sc); + assert(baridx == 0); + assert(size <= 4); + + value = hda_read(sc, offset); + + DPRINTF("offset: 0x%lx value: 0x%lx\n", offset, value); + + return value; +} + +