blob: e779dede524a6e91009638ed69b8a069e5f58b3a [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <fbl/auto_call.h>
#include <fbl/limits.h>
#include <zircon/device/audio.h>
#include <audio-proto-utils/format-utils.h>
#include "intel-audio-dsp.h"
#include "intel-dsp-stream.h"
namespace audio {
namespace intel_hda {
IntelDspStream::IntelDspStream(uint32_t id, bool is_input, const DspPipeline& pipeline)
: IntelHDAStreamBase(id, is_input), pipeline_(pipeline) {
snprintf(log_prefix_, sizeof(log_prefix_), "IHDA DSP %cStream #%u", is_input ? 'I' : 'O', id);
}
zx_status_t IntelDspStream::ProcessSetStreamFmt(const ihda_proto::SetStreamFmtResp& codec_resp,
zx::channel&& ring_buffer_channel) {
ZX_DEBUG_ASSERT(ring_buffer_channel.is_valid());
fbl::AutoLock lock(obj_lock());
audio_proto::StreamSetFmtResp resp = { };
zx_status_t res = ZX_OK;
// Are we shutting down?
if (!is_active()) {
return ZX_ERR_BAD_STATE;
}
// The DSP needs to coordinate with ring buffer commands. Set up an additional
// channel to intercept messages on the ring buffer channel.
zx::channel client_endpoint;
res = CreateClientRingBufferChannelLocked(fbl::move(ring_buffer_channel), &client_endpoint);
if (res != ZX_OK) {
LOG(ERROR, "Failed to set up client ring buffer channel (res %d)\n", res);
goto finished;
}
// Let the implementation send the commands required to finish changing the
// stream format.
res = FinishChangeStreamFormatLocked(encoded_fmt());
if (res != ZX_OK) {
LOG(ERROR, "Failed to finish set format (enc fmt 0x%04hx res %d)\n", encoded_fmt(), res);
goto finished;
}
ZX_DEBUG_ASSERT(client_endpoint.is_valid());
// Respond to the caller, transferring the DMA handle back in the process.
resp.hdr.cmd = AUDIO_STREAM_CMD_SET_FORMAT;
resp.hdr.transaction_id = set_format_tid();
resp.result = ZX_OK;
resp.external_delay_nsec = 0; // report his properly based on the codec path delay.
res = stream_channel()->Write(&resp, sizeof(resp), fbl::move(client_endpoint));
// If we don't have a set format operation in flight, or the stream channel
// has been closed, this set format operation has been canceled. Do not
// return an error up the stack; we don't want to close the connection to
// our codec device.
if ((set_format_tid() == AUDIO_INVALID_TRANSACTION_ID) ||
(stream_channel() == nullptr)) {
goto finished;
}
finished:
// Something went fatally wrong when trying to send the result back to the
// caller. Close the stream channel.
if ((res != ZX_OK) && (stream_channel() != nullptr)) {
OnChannelDeactivateLocked(*stream_channel());
stream_channel()->Deactivate();
stream_channel() = nullptr;
}
// One way or the other, this set format operation is finished. Clear out
// the in-flight transaction ID
SetFormatTidLocked(AUDIO_INVALID_TRANSACTION_ID);
return ZX_OK;
}
zx_status_t IntelDspStream::CreateClientRingBufferChannelLocked(
zx::channel&& ring_buffer_channel,
zx::channel* out_client_channel) {
// Attempt to allocate a new ring buffer channel and bind it to us.
// This channel is connected to the upstream device.
auto channel = dispatcher::Channel::Create();
if (channel == nullptr) {
return ZX_ERR_NO_MEMORY;
}
dispatcher::Channel::ProcessHandler phandler(
[stream = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t {
OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
return stream->ProcessRbRequest(channel);
});
dispatcher::Channel::ChannelClosedHandler chandler(
[stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void {
OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
stream->ProcessRbDeactivate(channel);
});
zx_status_t res = channel->Activate(fbl::move(ring_buffer_channel),
domain(),
fbl::move(phandler),
fbl::move(chandler));
if (res != ZX_OK) {
return res;
}
ZX_DEBUG_ASSERT(rb_channel_ == nullptr);
rb_channel_ = channel;
// Attempt to allocate a new ring buffer channel and bind it to us.
// This channel is connected to the client.
auto client_channel = dispatcher::Channel::Create();
if (client_channel == nullptr) {
rb_channel_->Deactivate();
rb_channel_ = nullptr;
return ZX_ERR_NO_MEMORY;
}
dispatcher::Channel::ProcessHandler client_phandler(
[stream = fbl::WrapRefPtr(this)](dispatcher::Channel* channel) -> zx_status_t {
OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
return stream->ProcessClientRbRequest(channel);
});
dispatcher::Channel::ChannelClosedHandler client_chandler(
[stream = fbl::WrapRefPtr(this)](const dispatcher::Channel* channel) -> void {
OBTAIN_EXECUTION_DOMAIN_TOKEN(t, stream->domain());
stream->ProcessClientRbDeactivate(channel);
});
res = client_channel->Activate(out_client_channel,
domain(),
fbl::move(client_phandler),
fbl::move(client_chandler));
if (res == ZX_OK) {
ZX_DEBUG_ASSERT(client_rb_channel_ == nullptr);
client_rb_channel_ = client_channel;
} else {
rb_channel_->Deactivate();
rb_channel_ = nullptr;
}
return res;
}
zx_status_t IntelDspStream::ProcessRbRequest(dispatcher::Channel* channel) {
ZX_DEBUG_ASSERT(channel != nullptr);
fbl::AutoLock lock(obj_lock());
// If we have lost our connection to the codec device, or are in the process
// of shutting down, there is nothing further we can do. Fail the request
// and close the connection to the caller.
if (!is_active() || (rb_channel_ == nullptr) || (client_rb_channel_ == nullptr)) {
return ZX_ERR_BAD_STATE;
}
zx::handle rxed_handle;
uint32_t req_size;
union {
audio_proto::CmdHdr hdr;
audio_proto::RingBufGetFifoDepthResp get_fifo_depth;
audio_proto::RingBufGetBufferResp get_buffer;
audio_proto::RingBufStartResp start;
audio_proto::RingBufStopResp stop;
} req;
// TODO(johngro) : How large is too large?
static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
zx_status_t res = channel->Read(&req, sizeof(req), &req_size, &rxed_handle);
if (res != ZX_OK) {
return res;
}
switch (req.hdr.cmd) {
case AUDIO_RB_CMD_START:
{
auto dsp = fbl::RefPtr<IntelAudioDsp>::Downcast(parent_codec());
zx_status_t st = dsp->StartPipeline(pipeline_);
if (st != ZX_OK) {
audio_proto::RingBufStartResp resp = { };
resp.hdr = req.hdr;
resp.result = st;
return client_rb_channel_->Write(&resp, sizeof(resp));
}
break;
}
default:
break;
}
return client_rb_channel_->Write(&req, req_size, fbl::move(rxed_handle));
}
void IntelDspStream::ProcessRbDeactivate(const dispatcher::Channel* channel) {
ZX_DEBUG_ASSERT(channel != nullptr);
fbl::AutoLock lock(obj_lock());
LOG(TRACE, "ProcessClientRbDeactivate\n");
ZX_DEBUG_ASSERT(channel == rb_channel_.get());
rb_channel_->Deactivate();
rb_channel_ = nullptr;
// Deactivate the client channel.
if (client_rb_channel_ != nullptr) {
client_rb_channel_->Deactivate();
client_rb_channel_ = nullptr;
}
}
zx_status_t IntelDspStream::ProcessClientRbRequest(dispatcher::Channel* channel) {
ZX_DEBUG_ASSERT(channel != nullptr);
fbl::AutoLock lock(obj_lock());
// If we have lost our connection to the codec device, or are in the process
// of shutting down, there is nothing further we can do. Fail the request
// and close the connection to the caller.
if (!is_active() || (rb_channel_ == nullptr) || (client_rb_channel_ == nullptr)) {
return ZX_ERR_BAD_STATE;
}
uint32_t req_size;
union {
audio_proto::CmdHdr hdr;
audio_proto::RingBufGetFifoDepthReq get_fifo_depth;
audio_proto::RingBufGetBufferReq get_buffer;
audio_proto::RingBufStartReq start;
audio_proto::RingBufStopReq stop;
} req;
// TODO(johngro) : How large is too large?
static_assert(sizeof(req) <= 256, "Request buffer is too large to hold on the stack!");
zx_status_t res = channel->Read(&req, sizeof(req), &req_size);
if (res != ZX_OK) {
return res;
}
switch (req.hdr.cmd) {
case AUDIO_RB_CMD_STOP:
{
auto dsp = fbl::RefPtr<IntelAudioDsp>::Downcast(parent_codec());
zx_status_t st = dsp->PausePipeline(pipeline_);
if (st != ZX_OK) {
audio_proto::RingBufStopResp resp = { };
resp.hdr = req.hdr;
resp.result = st;
return channel->Write(&resp, sizeof(resp));
}
break;
}
default:
break;
}
return rb_channel_->Write(&req, req_size);
}
void IntelDspStream::ProcessClientRbDeactivate(const dispatcher::Channel* channel) {
ZX_DEBUG_ASSERT(channel != nullptr);
fbl::AutoLock lock(obj_lock());
LOG(TRACE, "ProcessClientRbDeactivate\n");
ZX_DEBUG_ASSERT(channel == client_rb_channel_.get());
client_rb_channel_->Deactivate();
client_rb_channel_ = nullptr;
// Deactivate the upstream channel.
if (rb_channel_ != nullptr) {
rb_channel_->Deactivate();
rb_channel_ = nullptr;
}
}
zx_status_t IntelDspStream::OnActivateLocked() {
// FIXME(yky) Hardcode supported formats.
fbl::Vector<audio_proto::FormatRange> supported_formats;
zx_status_t res = MakeFormatRangeList(SampleCaps(IHDA_PCM_SIZE_16BITS | IHDA_PCM_RATE_48000,
IHDA_PCM_FORMAT_PCM),
2,
&supported_formats);
if (res != ZX_OK) {
return res;
}
SetSupportedFormatsLocked(fbl::move(supported_formats));
return ZX_OK;
}
void IntelDspStream::OnDeactivateLocked() {
LOG(TRACE, "OnDeactivateLocked\n");
}
void IntelDspStream::OnChannelDeactivateLocked(const dispatcher::Channel& channel) {
LOG(TRACE, "OnChannelDeactivateLocked\n");
}
zx_status_t IntelDspStream::OnDMAAssignedLocked() {
LOG(TRACE, "OnDMAAssignedLocked\n");
return PublishDeviceLocked();
}
zx_status_t IntelDspStream::OnSolicitedResponseLocked(const CodecResponse& resp) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t IntelDspStream::OnUnsolicitedResponseLocked(const CodecResponse& resp) {
return ZX_ERR_NOT_SUPPORTED;
}
zx_status_t IntelDspStream::BeginChangeStreamFormatLocked(
const audio_proto::StreamSetFmtReq& req) {
LOG(TRACE, "BeginChangeStreamFormatLocked\n");
return ZX_OK;
}
zx_status_t IntelDspStream::FinishChangeStreamFormatLocked(uint16_t encoded_fmt) {
LOG(TRACE, "FinishChangeStreamFormatLocked\n");
return ZX_OK;
}
void IntelDspStream::OnGetGainLocked(audio_proto::GetGainResp* out_resp) {
LOG(TRACE, "OnGetGainLocked\n");
}
void IntelDspStream::OnSetGainLocked(const audio_proto::SetGainReq& req,
audio_proto::SetGainResp* out_resp) {
LOG(TRACE, "OnSetGainLocked\n");
}
void IntelDspStream::OnPlugDetectLocked(dispatcher::Channel* response_channel,
const audio_proto::PlugDetectReq& req,
audio_proto::PlugDetectResp* out_resp) {
LOG(TRACE, "OnPlugDetectLocked\n");
}
} // namespace intel_hda
} // namespace audio