mirror of
https://github.com/Mathewnd/Astral.git
synced 2026-10-27 12:48:22 +00:00
audio: allocate streams separately from devices
This commit is contained in:
@@ -8,6 +8,11 @@
|
|||||||
|
|
||||||
typedef struct audio_stream audio_stream_t;
|
typedef struct audio_stream audio_stream_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int (*acquire_stream)(void *private, bool nonblocking, audio_stream_t **stream);
|
||||||
|
void (*release_stream)(void *private, audio_stream_t *stream);
|
||||||
|
} audio_device_ops_t;
|
||||||
|
|
||||||
#define AUDIO_STREAM_INFO_FRAGMENT_SIZE 1
|
#define AUDIO_STREAM_INFO_FRAGMENT_SIZE 1
|
||||||
#define AUDIO_STREAM_INFO_SPEED 2
|
#define AUDIO_STREAM_INFO_SPEED 2
|
||||||
#define AUDIO_STREAM_INFO_CHANNELS 3
|
#define AUDIO_STREAM_INFO_CHANNELS 3
|
||||||
@@ -33,7 +38,6 @@ typedef struct audio_stream {
|
|||||||
ringbuffer_t ringbuffer;
|
ringbuffer_t ringbuffer;
|
||||||
pollheader_t pollheader;
|
pollheader_t pollheader;
|
||||||
audio_stream_ops_t *ops;
|
audio_stream_ops_t *ops;
|
||||||
bool opened;
|
|
||||||
bool playing;
|
bool playing;
|
||||||
int trigger;
|
int trigger;
|
||||||
size_t underruns;
|
size_t underruns;
|
||||||
@@ -44,6 +48,6 @@ typedef struct audio_stream {
|
|||||||
|
|
||||||
bool audio_take_stream_data(audio_stream_t *stream, void *buffer, size_t size);
|
bool audio_take_stream_data(audio_stream_t *stream, void *buffer, size_t size);
|
||||||
int audio_initialize_stream(audio_stream_t *stream, audio_stream_ops_t *stream_ops);
|
int audio_initialize_stream(audio_stream_t *stream, audio_stream_ops_t *stream_ops);
|
||||||
int audio_register_stream(audio_stream_t *stream);
|
int audio_register_device(audio_device_ops_t *device_ops, void *private);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+52
-16
@@ -18,34 +18,61 @@ static size_t bytes_to_frames(int fmt, int channels, size_t bytes) {
|
|||||||
#define MAX_STREAM 64
|
#define MAX_STREAM 64
|
||||||
#define AUDIO_RINGBUFFER_SIZE (8 * 1024)
|
#define AUDIO_RINGBUFFER_SIZE (8 * 1024)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
mutex_t mutex;
|
||||||
|
audio_stream_t *stream;
|
||||||
|
audio_device_ops_t *ops;
|
||||||
|
void *private;
|
||||||
|
} audio_device_t;
|
||||||
|
|
||||||
static int current_minor = 0;
|
static int current_minor = 0;
|
||||||
static audio_stream_t *streams[MAX_STREAM];
|
static audio_device_t devices[MAX_STREAM];
|
||||||
|
|
||||||
|
static audio_device_t *get_device(int minor) {
|
||||||
|
if (minor < 0 || minor >= MAX_STREAM || devices[minor].ops == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return &devices[minor];
|
||||||
|
}
|
||||||
|
|
||||||
static int audio_open(int minor, vnode_t **vnode, int flags) {
|
static int audio_open(int minor, vnode_t **vnode, int flags) {
|
||||||
audio_stream_t *stream = streams[minor];
|
audio_device_t *device = get_device(minor);
|
||||||
if (stream == NULL)
|
if (device == NULL)
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
MUTEX_ACQUIRE(&stream->mutex);
|
MUTEX_ACQUIRE(&device->mutex);
|
||||||
|
|
||||||
if (stream->opened) {
|
if (device->stream) {
|
||||||
error = EBUSY;
|
error = EBUSY;
|
||||||
goto leave;
|
goto leave;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->opened = true;
|
audio_stream_t *stream;
|
||||||
|
error = device->ops->acquire_stream(device->private, flags & V_FFLAGS_NONBLOCKING, &stream);
|
||||||
|
if (error)
|
||||||
|
goto leave;
|
||||||
|
|
||||||
|
__assert(stream);
|
||||||
|
device->stream = stream;
|
||||||
|
|
||||||
leave:
|
leave:
|
||||||
MUTEX_RELEASE(&stream->mutex);
|
MUTEX_RELEASE(&device->mutex);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int audio_close(int minor, int flags) {
|
static int audio_close(int minor, int flags) {
|
||||||
audio_stream_t *stream = streams[minor];
|
audio_device_t *device = get_device(minor);
|
||||||
if (stream == NULL)
|
if (device == NULL)
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
|
|
||||||
|
MUTEX_ACQUIRE(&device->mutex);
|
||||||
|
audio_stream_t *stream = device->stream;
|
||||||
|
if (stream == NULL) {
|
||||||
|
MUTEX_RELEASE(&device->mutex);
|
||||||
|
return ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
MUTEX_ACQUIRE(&stream->mutex);
|
MUTEX_ACQUIRE(&stream->mutex);
|
||||||
|
|
||||||
if ((flags & V_FFLAGS_CLOSE_URGENT) == 0 && stream->playing)
|
if ((flags & V_FFLAGS_CLOSE_URGENT) == 0 && stream->playing)
|
||||||
@@ -55,14 +82,17 @@ static int audio_close(int minor, int flags) {
|
|||||||
stream->ops->reset(stream);
|
stream->ops->reset(stream);
|
||||||
ringbuffer_truncate(&stream->ringbuffer, 0xffffffff);
|
ringbuffer_truncate(&stream->ringbuffer, 0xffffffff);
|
||||||
stream->playing = false;
|
stream->playing = false;
|
||||||
stream->opened = false;
|
|
||||||
stream->underruns = 0;
|
stream->underruns = 0;
|
||||||
stream->trigger = AUDIO_TRIGGER_OUTPUT | AUDIO_TRIGGER_INPUT;
|
stream->trigger = AUDIO_TRIGGER_OUTPUT | AUDIO_TRIGGER_INPUT;
|
||||||
stream->bytes_submitted = 0;
|
stream->bytes_submitted = 0;
|
||||||
stream->bytes_written = 0;
|
stream->bytes_written = 0;
|
||||||
stream->bytes_played = 0;
|
stream->bytes_played = 0;
|
||||||
|
|
||||||
|
device->ops->release_stream(device->private, stream);
|
||||||
|
device->stream = NULL;
|
||||||
|
|
||||||
MUTEX_RELEASE(&stream->mutex);
|
MUTEX_RELEASE(&stream->mutex);
|
||||||
|
MUTEX_RELEASE(&device->mutex);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +109,8 @@ static int internal_poll(audio_stream_t *stream, polldata_t *data, int events) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int audio_write(int minor, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, int flags, size_t *writec) {
|
static int audio_write(int minor, iovec_iterator_t *iovec_iterator, size_t size, uintmax_t offset, int flags, size_t *writec) {
|
||||||
audio_stream_t *stream = streams[minor];
|
audio_device_t *device = get_device(minor);
|
||||||
|
audio_stream_t *stream = device ? device->stream : NULL;
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
|
|
||||||
@@ -133,7 +164,8 @@ static int audio_write(int minor, iovec_iterator_t *iovec_iterator, size_t size,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int audio_poll(int minor, polldata_t *data, int events) {
|
static int audio_poll(int minor, polldata_t *data, int events) {
|
||||||
audio_stream_t *stream = streams[minor];
|
audio_device_t *device = get_device(minor);
|
||||||
|
audio_stream_t *stream = device ? device->stream : NULL;
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return POLLERR;
|
return POLLERR;
|
||||||
|
|
||||||
@@ -181,7 +213,8 @@ typedef struct {
|
|||||||
#define SNDCTL_DSP_CURRENT_OPTR 0x80905024
|
#define SNDCTL_DSP_CURRENT_OPTR 0x80905024
|
||||||
|
|
||||||
static int audio_ioctl(int minor, unsigned long request, void *arg, int *result, cred_t *cred) {
|
static int audio_ioctl(int minor, unsigned long request, void *arg, int *result, cred_t *cred) {
|
||||||
audio_stream_t *stream = streams[minor];
|
audio_device_t *device = get_device(minor);
|
||||||
|
audio_stream_t *stream = device ? device->stream : NULL;
|
||||||
if (stream == NULL)
|
if (stream == NULL)
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
|
|
||||||
@@ -307,12 +340,16 @@ static devops_t devops = {
|
|||||||
.ioctl = audio_ioctl
|
.ioctl = audio_ioctl
|
||||||
};
|
};
|
||||||
|
|
||||||
int audio_register_stream(audio_stream_t *stream) {
|
int audio_register_device(audio_device_ops_t *device_ops, void *private) {
|
||||||
int minor = __atomic_fetch_add(¤t_minor, 1, __ATOMIC_RELAXED);
|
int minor = __atomic_fetch_add(¤t_minor, 1, __ATOMIC_RELAXED);
|
||||||
if (minor >= MAX_STREAM)
|
if (minor >= MAX_STREAM)
|
||||||
return ENXIO;
|
return ENXIO;
|
||||||
|
|
||||||
streams[minor] = stream;
|
audio_device_t *device = &devices[minor];
|
||||||
|
MUTEX_INIT(&device->mutex);
|
||||||
|
device->stream = NULL;
|
||||||
|
device->ops = device_ops;
|
||||||
|
device->private = private;
|
||||||
|
|
||||||
char name[10];
|
char name[10];
|
||||||
snprintf(name, 10, "dsp%d", minor);
|
snprintf(name, 10, "dsp%d", minor);
|
||||||
@@ -337,7 +374,6 @@ int audio_initialize_stream(audio_stream_t *stream, audio_stream_ops_t *ops) {
|
|||||||
|
|
||||||
MUTEX_INIT(&stream->mutex);
|
MUTEX_INIT(&stream->mutex);
|
||||||
POLL_INITHEADER(&stream->pollheader);
|
POLL_INITHEADER(&stream->pollheader);
|
||||||
stream->opened = false;
|
|
||||||
stream->playing = false;
|
stream->playing = false;
|
||||||
stream->ops = ops;
|
stream->ops = ops;
|
||||||
stream->trigger = AUDIO_TRIGGER_INPUT | AUDIO_TRIGGER_OUTPUT;
|
stream->trigger = AUDIO_TRIGGER_INPUT | AUDIO_TRIGGER_OUTPUT;
|
||||||
|
|||||||
+146
-42
@@ -7,6 +7,7 @@
|
|||||||
#include <semaphore.h>
|
#include <semaphore.h>
|
||||||
#include <kernel/alloc.h>
|
#include <kernel/alloc.h>
|
||||||
#include <kernel/audio.h>
|
#include <kernel/audio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
// TODO: support mono format as well
|
// TODO: support mono format as well
|
||||||
#define HDA_FORMAT_48KHZ_16BIT_STEREO 0x11
|
#define HDA_FORMAT_48KHZ_16BIT_STEREO 0x11
|
||||||
@@ -160,6 +161,9 @@ typedef struct {
|
|||||||
size_t size;
|
size_t size;
|
||||||
} hda_path_t;
|
} hda_path_t;
|
||||||
|
|
||||||
|
typedef struct hda hda_t;
|
||||||
|
typedef struct hda_output_path hda_output_path_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
list_t waiter_list;
|
list_t waiter_list;
|
||||||
spinlock_t waiter_list_lock;
|
spinlock_t waiter_list_lock;
|
||||||
@@ -172,11 +176,15 @@ typedef struct {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
audio_stream_t generic;
|
audio_stream_t generic;
|
||||||
|
hda_t *hda;
|
||||||
volatile hda_sd_t *sd;
|
volatile hda_sd_t *sd;
|
||||||
void *bdl_phys;
|
void *bdl_phys;
|
||||||
size_t tag;
|
size_t tag;
|
||||||
|
size_t descriptor_index;
|
||||||
bool bi;
|
bool bi;
|
||||||
hda_path_t path;
|
bool allocated;
|
||||||
|
bool active;
|
||||||
|
hda_output_path_t *owner;
|
||||||
size_t last;
|
size_t last;
|
||||||
size_t fill_ptr;
|
size_t fill_ptr;
|
||||||
uint32_t last_lpib;
|
uint32_t last_lpib;
|
||||||
@@ -184,7 +192,7 @@ typedef struct {
|
|||||||
eventheader_t underrun_event;
|
eventheader_t underrun_event;
|
||||||
} hda_stream_t;
|
} hda_stream_t;
|
||||||
|
|
||||||
typedef struct {
|
struct hda {
|
||||||
volatile hda_regs_t *regs;
|
volatile hda_regs_t *regs;
|
||||||
volatile uint32_t *corb;
|
volatile uint32_t *corb;
|
||||||
size_t corb_entries;
|
size_t corb_entries;
|
||||||
@@ -205,8 +213,17 @@ typedef struct {
|
|||||||
size_t allocated_input_stream_count;
|
size_t allocated_input_stream_count;
|
||||||
size_t allocated_bi_stream_count;
|
size_t allocated_bi_stream_count;
|
||||||
size_t current_stream_tag;
|
size_t current_stream_tag;
|
||||||
|
spinlock_t stream_lock;
|
||||||
|
semaphore_t output_stream_semaphore;
|
||||||
|
size_t usable_output_stream_count;
|
||||||
hda_stream_t **streams;
|
hda_stream_t **streams;
|
||||||
} hda_t;
|
};
|
||||||
|
|
||||||
|
struct hda_output_path {
|
||||||
|
hda_t *hda;
|
||||||
|
int codec;
|
||||||
|
hda_path_t path;
|
||||||
|
};
|
||||||
|
|
||||||
static uint32_t hda_stream_mask(hda_t *hda) {
|
static uint32_t hda_stream_mask(hda_t *hda) {
|
||||||
size_t count = hda->input_stream_count + hda->output_stream_count + hda->bi_stream_count;
|
size_t count = hda->input_stream_count + hda->output_stream_count + hda->bi_stream_count;
|
||||||
@@ -260,6 +277,9 @@ static bool hda_reset_stream(hda_stream_t *stream) {
|
|||||||
if (!hda_stream_stop(stream))
|
if (!hda_stream_stop(stream))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
long ipl = spinlock_acquire_raise_ipl(&stream->hda->stream_lock, IPL_AUDIO);
|
||||||
|
spinlock_release_lower_ipl(&stream->hda->stream_lock, ipl);
|
||||||
|
|
||||||
if (!hda_sd_set_reset(stream->sd, true)) {
|
if (!hda_sd_set_reset(stream->sd, true)) {
|
||||||
printf("hda: setting stream reset bit timed out\n");
|
printf("hda: setting stream reset bit timed out\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -376,35 +396,16 @@ static audio_stream_ops_t hda_stream_ops = {
|
|||||||
.get_info = stream_get_info
|
.get_info = stream_get_info
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: fix memory leak
|
static hda_stream_t *hda_initialize_output_stream(hda_t *hda, size_t stream_n, size_t tag, bool bi) {
|
||||||
static hda_stream_t *hda_allocate_output_stream(hda_t *hda) {
|
|
||||||
if (hda->current_stream_tag > 15) {
|
|
||||||
printf("hda: out of stream tags\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hda_stream_t *stream = alloc(sizeof(hda_stream_t));
|
hda_stream_t *stream = alloc(sizeof(hda_stream_t));
|
||||||
__assert(stream);
|
__assert(stream);
|
||||||
__assert(audio_initialize_stream(&stream->generic, &hda_stream_ops) == 0);
|
__assert(audio_initialize_stream(&stream->generic, &hda_stream_ops) == 0);
|
||||||
EVENT_INITHEADER(&stream->underrun_event);
|
EVENT_INITHEADER(&stream->underrun_event);
|
||||||
|
stream->hda = hda;
|
||||||
size_t stream_n;
|
stream->sd = &hda->regs->sds[stream_n];
|
||||||
if (hda->allocated_output_stream_count < hda->output_stream_count) {
|
stream->descriptor_index = stream_n;
|
||||||
stream->sd = &hda->output_sd[hda->allocated_output_stream_count];
|
stream->tag = tag;
|
||||||
stream_n = hda->input_stream_count + hda->allocated_output_stream_count;
|
stream->bi = bi;
|
||||||
++hda->allocated_output_stream_count;
|
|
||||||
} else if (hda->allocated_bi_stream_count < hda->bi_stream_count) {
|
|
||||||
stream->sd = &hda->bi_sd[hda->allocated_bi_stream_count];
|
|
||||||
stream_n = hda->input_stream_count + hda->output_stream_count + hda->allocated_bi_stream_count;
|
|
||||||
++hda->allocated_bi_stream_count;
|
|
||||||
stream->bi = true;
|
|
||||||
} else {
|
|
||||||
printf("hda: no more free streams\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
hda->streams[stream_n] = stream;
|
|
||||||
hda->regs->intctl |= (1 << stream_n);
|
|
||||||
|
|
||||||
stream->bdl_phys = mm_alloc_page(MEMORY_SECTION_DEFAULT);
|
stream->bdl_phys = mm_alloc_page(MEMORY_SECTION_DEFAULT);
|
||||||
__assert(stream->bdl_phys);
|
__assert(stream->bdl_phys);
|
||||||
@@ -420,12 +421,33 @@ static hda_stream_t *hda_allocate_output_stream(hda_t *hda) {
|
|||||||
bdl[i].flags = HDA_BDL_FLAGS_IOC;
|
bdl[i].flags = HDA_BDL_FLAGS_IOC;
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->tag = hda->current_stream_tag++;
|
__assert(hda_reset_stream(stream));
|
||||||
hda_reset_stream(stream);
|
hda->streams[stream_n] = stream;
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hda_initialize_output_stream_pool(hda_t *hda) {
|
||||||
|
while (hda->allocated_output_stream_count < hda->output_stream_count && hda->current_stream_tag <= 15) {
|
||||||
|
size_t i = hda->allocated_output_stream_count++;
|
||||||
|
size_t stream_n = hda->input_stream_count + i;
|
||||||
|
hda_initialize_output_stream(hda, stream_n, hda->current_stream_tag++, false);
|
||||||
|
++hda->usable_output_stream_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (hda->allocated_bi_stream_count < hda->bi_stream_count && hda->current_stream_tag <= 15) {
|
||||||
|
size_t i = hda->allocated_bi_stream_count++;
|
||||||
|
size_t stream_n = hda->input_stream_count + hda->output_stream_count + i;
|
||||||
|
hda_initialize_output_stream(hda, stream_n, hda->current_stream_tag++, true);
|
||||||
|
++hda->usable_output_stream_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hda->output_stream_count + hda->bi_stream_count > hda->usable_output_stream_count)
|
||||||
|
printf("hda: only %lu output streams are usable because stream tags are exhausted\n", hda->usable_output_stream_count);
|
||||||
|
|
||||||
|
SEMAPHORE_INIT(&hda->output_stream_semaphore, hda->usable_output_stream_count);
|
||||||
|
}
|
||||||
|
|
||||||
static hda_path_t hda_find_path(hda_fg_t *fg, hda_widget_t *pin, int goal_type) {
|
static hda_path_t hda_find_path(hda_fg_t *fg, hda_widget_t *pin, int goal_type) {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
list_node_t list_node;
|
list_node_t list_node;
|
||||||
@@ -531,6 +553,7 @@ static void hda_stream_fill_data(hda_stream_t *stream) {
|
|||||||
|
|
||||||
static void stream_irq(hda_t *hda) {
|
static void stream_irq(hda_t *hda) {
|
||||||
uint32_t status = hda->regs->intsts & hda_stream_mask(hda);
|
uint32_t status = hda->regs->intsts & hda_stream_mask(hda);
|
||||||
|
spinlock_acquire(&hda->stream_lock);
|
||||||
|
|
||||||
for (int i = 0; i < 30; ++i) {
|
for (int i = 0; i < 30; ++i) {
|
||||||
if ((status & (1 << i)) == 0)
|
if ((status & (1 << i)) == 0)
|
||||||
@@ -542,9 +565,11 @@ static void stream_irq(hda_t *hda) {
|
|||||||
|
|
||||||
uint8_t strsts = stream->sd->sts;
|
uint8_t strsts = stream->sd->sts;
|
||||||
stream->sd->sts = strsts & (HDA_SD_STS_BCIS | HDA_SD_STS_DESE | HDA_SD_STS_FIFOE);
|
stream->sd->sts = strsts & (HDA_SD_STS_BCIS | HDA_SD_STS_DESE | HDA_SD_STS_FIFOE);
|
||||||
if (strsts & HDA_SD_STS_BCIS)
|
if (stream->active && (strsts & HDA_SD_STS_BCIS))
|
||||||
hda_stream_fill_data(stream);
|
hda_stream_fill_data(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spinlock_release(&hda->stream_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rirb_irq(hda_t *hda) {
|
static void rirb_irq(hda_t *hda) {
|
||||||
@@ -664,6 +689,87 @@ static uint32_t hda_set_converter_stream_channel(hda_t *hda, uint32_t codec, uin
|
|||||||
return hda_submit_verb_and_wait(hda, codec, nid, 0x70600 | (stream << 4) | base_channel);
|
return hda_submit_verb_and_wait(hda, codec, nid, 0x70600 | (stream << 4) | base_channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hda_acquire_output_stream(void *private, bool nonblocking, audio_stream_t **generic) {
|
||||||
|
hda_output_path_t *output_path = private;
|
||||||
|
hda_t *hda = output_path->hda;
|
||||||
|
|
||||||
|
if (nonblocking) {
|
||||||
|
if (!semaphore_test(&hda->output_stream_semaphore))
|
||||||
|
return EBUSY;
|
||||||
|
} else {
|
||||||
|
int error = semaphore_wait(&hda->output_stream_semaphore, true);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
hda_stream_t *stream = NULL;
|
||||||
|
long ipl = spinlock_acquire_raise_ipl(&hda->stream_lock, IPL_AUDIO);
|
||||||
|
for (size_t i = hda->input_stream_count; i < hda->input_stream_count + hda->output_stream_count + hda->bi_stream_count; ++i) {
|
||||||
|
hda_stream_t *candidate = hda->streams[i];
|
||||||
|
if (candidate && !candidate->allocated) {
|
||||||
|
candidate->allocated = true;
|
||||||
|
candidate->owner = output_path;
|
||||||
|
stream = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spinlock_release_lower_ipl(&hda->stream_lock, ipl);
|
||||||
|
__assert(stream);
|
||||||
|
|
||||||
|
if (!hda_reset_stream(stream)) {
|
||||||
|
printf("hda: failed to reset allocated stream %lu\n", stream->descriptor_index);
|
||||||
|
ipl = spinlock_acquire_raise_ipl(&hda->stream_lock, IPL_AUDIO);
|
||||||
|
stream->owner = NULL;
|
||||||
|
spinlock_release_lower_ipl(&hda->stream_lock, ipl);
|
||||||
|
return EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
hda_widget_t *converter = output_path->path.widgets[output_path->path.size - 1];
|
||||||
|
hda_set_converter_stream_channel(hda, output_path->codec, converter->nid, stream->tag, 0);
|
||||||
|
|
||||||
|
ipl = spinlock_acquire_raise_ipl(&hda->stream_lock, IPL_AUDIO);
|
||||||
|
stream->active = true;
|
||||||
|
hda->regs->intctl |= 1u << stream->descriptor_index;
|
||||||
|
spinlock_release_lower_ipl(&hda->stream_lock, ipl);
|
||||||
|
|
||||||
|
*generic = &stream->generic;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void hda_release_output_stream(void *private, audio_stream_t *generic) {
|
||||||
|
hda_output_path_t *output_path = private;
|
||||||
|
hda_stream_t *stream = (hda_stream_t *)generic;
|
||||||
|
hda_t *hda = output_path->hda;
|
||||||
|
__assert(stream->owner == output_path);
|
||||||
|
|
||||||
|
long ipl = spinlock_acquire_raise_ipl(&hda->stream_lock, IPL_AUDIO);
|
||||||
|
hda->regs->intctl &= ~(1u << stream->descriptor_index);
|
||||||
|
stream->active = false;
|
||||||
|
spinlock_release_lower_ipl(&hda->stream_lock, ipl);
|
||||||
|
|
||||||
|
hda_widget_t *converter = output_path->path.widgets[output_path->path.size - 1];
|
||||||
|
hda_set_converter_stream_channel(hda, output_path->codec, converter->nid, 0, 0);
|
||||||
|
|
||||||
|
if (!hda_reset_stream(stream)) {
|
||||||
|
printf("hda: failed to reset released stream %lu; keeping it unavailable\n", stream->descriptor_index);
|
||||||
|
ipl = spinlock_acquire_raise_ipl(&hda->stream_lock, IPL_AUDIO);
|
||||||
|
stream->owner = NULL;
|
||||||
|
spinlock_release_lower_ipl(&hda->stream_lock, ipl);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ipl = spinlock_acquire_raise_ipl(&hda->stream_lock, IPL_AUDIO);
|
||||||
|
stream->owner = NULL;
|
||||||
|
stream->allocated = false;
|
||||||
|
spinlock_release_lower_ipl(&hda->stream_lock, ipl);
|
||||||
|
semaphore_signal(&hda->output_stream_semaphore);
|
||||||
|
}
|
||||||
|
|
||||||
|
static audio_device_ops_t hda_audio_device_ops = {
|
||||||
|
.acquire_stream = hda_acquire_output_stream,
|
||||||
|
.release_stream = hda_release_output_stream
|
||||||
|
};
|
||||||
|
|
||||||
// TODO: we should really be careful to not choose paths that go through the same selector/dac
|
// TODO: we should really be careful to not choose paths that go through the same selector/dac
|
||||||
static void hda_initialize_path(hda_t *hda, int codec, hda_fg_t *fg, hda_path_t path, bool output) {
|
static void hda_initialize_path(hda_t *hda, int codec, hda_fg_t *fg, hda_path_t path, bool output) {
|
||||||
__assert(output); // TODO: this entire function hardcodes an output path for now
|
__assert(output); // TODO: this entire function hardcodes an output path for now
|
||||||
@@ -879,18 +985,15 @@ static void init_function_group(hda_t *hda, int codec, hda_fg_t *function_group)
|
|||||||
|
|
||||||
hda_initialize_path(hda, codec, function_group, path, true);
|
hda_initialize_path(hda, codec, function_group, path, true);
|
||||||
|
|
||||||
hda_stream_t *stream = hda_allocate_output_stream(hda);
|
|
||||||
if (stream == NULL) {
|
|
||||||
printf("hda: failed to allocate stream\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->path = path;
|
|
||||||
|
|
||||||
hda_widget_t *converter = path.widgets[path.size - 1];
|
hda_widget_t *converter = path.widgets[path.size - 1];
|
||||||
hda_set_converter_format(hda, codec, converter->nid, HDA_FORMAT_48KHZ_16BIT_STEREO);
|
hda_set_converter_format(hda, codec, converter->nid, HDA_FORMAT_48KHZ_16BIT_STEREO);
|
||||||
hda_set_converter_stream_channel(hda, codec, converter->nid, stream->tag, 0);
|
|
||||||
__assert(audio_register_stream(&stream->generic) == 0);
|
hda_output_path_t *output_path = alloc(sizeof(hda_output_path_t));
|
||||||
|
__assert(output_path);
|
||||||
|
output_path->hda = hda;
|
||||||
|
output_path->codec = codec;
|
||||||
|
output_path->path = path;
|
||||||
|
__assert(audio_register_device(&hda_audio_device_ops, output_path) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1003,7 +1106,6 @@ static void initcontroller(pcienum_t *e) {
|
|||||||
SPINLOCK_INIT(hda->corb_lock);
|
SPINLOCK_INIT(hda->corb_lock);
|
||||||
hda->rirb = rirb;
|
hda->rirb = rirb;
|
||||||
hda->rirb_entries = rirb_entries;
|
hda->rirb_entries = rirb_entries;
|
||||||
|
|
||||||
hda->current_stream_tag = 1;
|
hda->current_stream_tag = 1;
|
||||||
|
|
||||||
hda->bi_stream_count = (regs->gcap >> 3) & 0x1f;
|
hda->bi_stream_count = (regs->gcap >> 3) & 0x1f;
|
||||||
@@ -1021,6 +1123,8 @@ static void initcontroller(pcienum_t *e) {
|
|||||||
|
|
||||||
hda->streams = alloc(sizeof(hda_stream_t *) * (hda->input_stream_count + hda->output_stream_count + hda->bi_stream_count));
|
hda->streams = alloc(sizeof(hda_stream_t *) * (hda->input_stream_count + hda->output_stream_count + hda->bi_stream_count));
|
||||||
__assert(hda->streams);
|
__assert(hda->streams);
|
||||||
|
SPINLOCK_INIT(hda->stream_lock);
|
||||||
|
hda_initialize_output_stream_pool(hda);
|
||||||
|
|
||||||
isr_t *isr = interrupt_allocate(hda_isr, ARCH_EOI, IPL_AUDIO);
|
isr_t *isr = interrupt_allocate(hda_isr, ARCH_EOI, IPL_AUDIO);
|
||||||
__assert(isr);
|
__assert(isr);
|
||||||
|
|||||||
Reference in New Issue
Block a user