Add the PCI HDAudio device model from the 2016 GSoC. Detailed information

can be found at

https://wiki.freebsd.org/SummerOfCode2016/HDAudioEmulationForBhyve

This commit has evolved from the original work to include Capsicum
integration.  As part of that, it only opens the host audio devices once
and leaves them open, instead of opening and closing them on each guest
access.  Thanks to Peter Grehan and Marcelo Araujo for their help in
bringing the work forward and providing some of the final techncial push.

Submitted by:	Alex Teaca <iateaca@freebsd.org>
Differential Revision:	D7840, D12419
This commit is contained in:
Scott Long 2019-06-24 19:31:32 +00:00
parent 673c1c2944
commit 7e3c742061
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=349335
8 changed files with 4377 additions and 0 deletions

View File

@ -16,6 +16,7 @@ BHYVE_SYSDIR?=${SRCTOP}
SRCS= \
atkbdc.c \
acpi.c \
audio.c \
bhyvegc.c \
bhyverun.c \
block_if.c \
@ -27,6 +28,7 @@ SRCS= \
dbgport.c \
fwctl.c \
gdb.c \
hda_codec.c \
inout.c \
ioapic.c \
mem.c \
@ -36,6 +38,7 @@ SRCS= \
pci_ahci.c \
pci_e82545.c \
pci_emul.c \
pci_hda.c \
pci_fbuf.c \
pci_hostbridge.c \
pci_irq.c \

282
usr.sbin/bhyve/audio.c Normal file
View File

@ -0,0 +1,282 @@
/*-
* Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
* 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 ``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.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#ifndef WITHOUT_CAPSICUM
#include <sys/capsicum.h>
#include <capsicum_helpers.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <err.h>
#include <sysexits.h>
#include "audio.h"
#include "pci_hda.h"
/*
* Audio Player internal data structures
*/
struct audio {
int fd;
uint8_t dir;
uint8_t inited;
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;
#ifndef WITHOUT_CAPSICUM
cap_rights_t rights;
cap_ioctl_t cmds[] = {
SNDCTL_DSP_RESET, SNDCTL_DSP_SETFMT, SNDCTL_DSP_CHANNELS,
SNDCTL_DSP_SPEED,
#ifdef DEBUG_HDA
SNDCTL_DSP_GETOSPACE, SNDCTL_DSP_GETISPACE,
#endif
};
#endif
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->dir = dir;
aud->fd = open(aud->dev_name, aud->dir ? O_WRONLY : O_RDONLY, 0);
if (aud->fd == -1) {
DPRINTF("Failed to open dev: %s, errno: %d\n",
aud->dev_name, errno);
return (NULL);
}
#ifndef WITHOUT_CAPSICUM
cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_WRITE);
if (caph_rights_limit(aud->fd, &rights) == -1)
errx(EX_OSERR, "Unable to apply rights for sandbox");
if (caph_ioctls_limit(aud->fd, cmds, nitems(cmds)) == -1)
errx(EX_OSERR, "Unable to limit ioctl rights for sandbox");
#endif
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;
int format, channels, rate;
int err;
#if DEBUG_HDA == 1
audio_buf_info info;
#endif
assert(aud);
assert(params);
if ((audio_fd = aud->fd) < 0) {
DPRINTF("Incorrect audio device descriptor for %s\n",
aud->dev_name);
return (-1);
}
/* Reset the device if it was previously opened */
if (aud->inited) {
err = ioctl(audio_fd, SNDCTL_DSP_RESET, NULL);
if (err == -1) {
DPRINTF("Failed to reset fd: %d, errno: %d\n",
aud->fd, errno);
return (-1);
}
} else
aud->inited = 1;
/* 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;
}

86
usr.sbin/bhyve/audio.h Normal file
View File

@ -0,0 +1,86 @@
/*-
* Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
* 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 ``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 _AUDIO_EMUL_H_
#define _AUDIO_EMUL_H_
#include <sys/types.h>
#include <sys/soundcard.h>
/*
* Audio Player data structures
*/
struct audio;
struct audio_params {
int channels;
int format;
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_ */

950
usr.sbin/bhyve/hda_codec.c Normal file
View File

@ -0,0 +1,950 @@
/*-
* Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
* 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 ``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.
*
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <pthread.h>
#include <pthread_np.h>
#include <unistd.h>
#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 {
char name[64];
uint8_t run;
uint8_t started;
void *priv;
pthread_t tid;
pthread_mutex_t mtx;
pthread_cond_t cond;
setup_func_t do_setup;
transfer_func_t do_transfer;
};
/*
* 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 channel;
uint16_t fmt;
uint8_t stream;
uint8_t left_gain;
uint8_t right_gain;
uint8_t left_mute;
uint8_t right_mute;
struct audio *aud;
struct hda_audio_ctxt actx;
};
struct hda_codec_softc {
uint32_t no_nodes;
uint32_t subsystem_id;
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_inst *hci;
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, \
/* 1 Subnode, StartNid = 1 */ \
[HDA_PARAM_SUB_NODE_COUNT] = 0x00010001, \
}, \
#define HDA_CODEC_FG_COMMON_DESC \
[HDA_PARAM_FCT_GRP_TYPE] = HDA_PARAM_FCT_GRP_TYPE_NODE_TYPE_AUDIO,\
/* B8 - B32, 8.0 - 192.0kHz */ \
[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x1f << 16) | 0x7ff, \
[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] = { \
/* 2 Subnodes, StartNid = 2 */ \
[HDA_PARAM_SUB_NODE_COUNT] = 0x00020002, \
HDA_CODEC_FG_COMMON_DESC \
}, \
#define HDA_CODEC_FG_INPUT_DESC \
[HDA_CODEC_FG_NID] = { \
/* 2 Subnodes, StartNid = 4 */ \
[HDA_PARAM_SUB_NODE_COUNT] = 0x00040002, \
HDA_CODEC_FG_COMMON_DESC \
}, \
#define HDA_CODEC_FG_DUPLEX_DESC \
[HDA_CODEC_FG_NID] = { \
/* 4 Subnodes, StartNid = 2 */ \
[HDA_PARAM_SUB_NODE_COUNT] = 0x00020004, \
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, \
/* B16, 16.0 - 192.0kHz */ \
[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc, \
[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, \
/* B16, 16.0 - 192.0kHz */ \
[HDA_PARAM_SUPP_PCM_SIZE_RATE] = (0x02 << 16) | 0x7fc, \
[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;
/* 4 bits */
cad = (cmd_data >> HDA_CMD_CAD_SHIFT) & 0x0f;
/* 8 bits */
nid = (cmd_data >> HDA_CMD_NID_SHIFT) & 0xff;
if ((cmd_data & 0x70000) == 0x70000) {
/* 12 bits */
verb = (cmd_data >> HDA_CMD_VERB_12BIT_SHIFT) & 0x0fff;
/* 8 bits */
payload = cmd_data & 0xff;
} else {
/* 4 bits */
verb = (cmd_data >> HDA_CMD_VERB_4BIT_SHIFT) & 0x0f;
/* 16 bits */
payload = cmd_data & 0xffff;
}
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);
}
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, &params);
if (err)
return (-1);
DPRINTF("rate: %d, channels: %d, format: 0x%x\n",
params.rate, params.channels, params.format);
return (audio_set_params(aud, &params));
}
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));
}
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, &params);
if (err)
return (-1);
DPRINTF("rate: %d, channels: %d, format: 0x%x\n",
params.rate, params.channels, params.format);
return (audio_set_params(aud, &params));
}
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 = 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);
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);
}

1367
usr.sbin/bhyve/hda_reg.h Normal file

File diff suppressed because it is too large Load Diff

269
usr.sbin/bhyve/hdac_reg.h Normal file
View File

@ -0,0 +1,269 @@
/*-
* Copyright (c) 2006 Stephane E. Potvin <sepotvin@videotron.ca>
* 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 /* _HDAC_REG_H_ */

1330
usr.sbin/bhyve/pci_hda.c Normal file

File diff suppressed because it is too large Load Diff

90
usr.sbin/bhyve/pci_hda.h Normal file
View File

@ -0,0 +1,90 @@
/*-
* Copyright (c) 2016 Alex Teaca <iateaca@FreeBSD.org>
* 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 ``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_EMUL_H_
#define _HDA_EMUL_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/queue.h>
#include <sys/kernel.h>
#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 {
uint8_t cad;
struct hda_codec_class *codec;
struct hda_softc *hda;
struct hda_ops *hops;
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_ */