blob: 7431574a5e9f029c1c514c53810a1943b4e0c89b [file] [log] [blame]
// Copyright 2017 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 <zircon/syscalls/object.h>
#include <zircon/time.h>
#include <zircon/types.h>
#include <fbl/algorithm.h>
#include <intel-hda/utils/utils.h>
#include <utility>
namespace audio {
namespace intel_hda {
zx_status_t WaitCondition(zx_duration_t timeout,
zx_duration_t poll_interval,
WaitConditionFn cond) {
ZX_DEBUG_ASSERT(poll_interval != ZX_TIME_INFINITE);
ZX_DEBUG_ASSERT(cond);
zx_time_t now = zx_clock_get_monotonic();
zx_time_t deadline = zx_time_add_duration(now, timeout);
while (!cond()) {
now = zx_clock_get_monotonic();
if (now >= deadline)
return ZX_ERR_TIMED_OUT;
zx_duration_t sleep_time = zx_time_sub_time(deadline, now);
if (poll_interval < sleep_time)
sleep_time = poll_interval;
zx_nanosleep(zx_deadline_after(sleep_time));
}
return ZX_OK;
}
fbl::RefPtr<RefCountedBti> RefCountedBti::Create(zx::bti initiator) {
fbl::AllocChecker ac;
auto ret = fbl::AdoptRef(new (&ac) RefCountedBti(std::move(initiator)));
if (!ac.check()) {
return nullptr;
}
return ret;
}
static constexpr audio_sample_format_t AUDIO_SAMPLE_FORMAT_UNSIGNED_8BIT =
static_cast<audio_sample_format_t>(AUDIO_SAMPLE_FORMAT_8BIT |
AUDIO_SAMPLE_FORMAT_FLAG_UNSIGNED);
static constexpr audio_sample_format_t AUDIO_SAMPLE_FORMAT_NONE =
static_cast<audio_sample_format_t>(0u);
struct FrameRateLut {
uint32_t flag;
uint32_t rate;
};
// Note: these LUTs must be kept in monotonically ascending order.
static const FrameRateLut FRAME_RATE_LUT_48K[] = {
{ IHDA_PCM_RATE_8000, 8000 },
{ IHDA_PCM_RATE_16000, 16000 },
{ IHDA_PCM_RATE_32000, 32000 },
{ IHDA_PCM_RATE_48000, 48000 },
{ IHDA_PCM_RATE_96000, 96000 },
{ IHDA_PCM_RATE_192000, 192000 },
{ IHDA_PCM_RATE_384000, 384000 },
};
static const FrameRateLut FRAME_RATE_LUT_44_1K[] = {
{ IHDA_PCM_RATE_11025, 11025 },
{ IHDA_PCM_RATE_22050, 22050 },
{ IHDA_PCM_RATE_44100, 44100 },
{ IHDA_PCM_RATE_88200, 88200 },
{ IHDA_PCM_RATE_176400, 176400 },
};
static const struct {
const FrameRateLut* lut;
size_t lut_size;
uint16_t family_flag;
} FRAME_RATE_LUTS[] = {
{ FRAME_RATE_LUT_48K, fbl::count_of(FRAME_RATE_LUT_48K), ASF_RANGE_FLAG_FPS_48000_FAMILY },
{ FRAME_RATE_LUT_44_1K, fbl::count_of(FRAME_RATE_LUT_44_1K), ASF_RANGE_FLAG_FPS_44100_FAMILY },
};
zx_obj_type_t GetHandleType(const zx::handle& handle) {
zx_info_handle_basic_t basic_info;
if (!handle.is_valid())
return ZX_OBJ_TYPE_NONE;
zx_status_t res = handle.get_info(ZX_INFO_HANDLE_BASIC,
&basic_info, sizeof(basic_info),
nullptr, nullptr);
return (res == ZX_OK) ? static_cast<zx_obj_type_t>(basic_info.type) : ZX_OBJ_TYPE_NONE;
}
zx_status_t MakeFormatRangeList(const SampleCaps& sample_caps,
uint32_t max_channels,
fbl::Vector<audio_stream_format_range_t>* ranges) {
if (ranges == nullptr || ranges->size()) return ZX_ERR_INVALID_ARGS;
if (!max_channels) return ZX_ERR_INVALID_ARGS;
// Signed and unsigned formats require separate audio_sample_format_t
// encodings. 8-bit is the only unsigned format supported by IHDA, however.
// Compute the set signed formats that this stream supports, check for
// unsigned 8 bit support in the process.
auto signed_formats = AUDIO_SAMPLE_FORMAT_NONE;
bool unsigned_8bit_supported = false;
if (sample_caps.pcm_formats_ & IHDA_PCM_FORMAT_PCM) {
static const struct {
uint32_t ihda_flag;
audio_sample_format_t audio_flag;
} FORMAT_LUT[] = {
{ IHDA_PCM_SIZE_32BITS, AUDIO_SAMPLE_FORMAT_32BIT },
{ IHDA_PCM_SIZE_24BITS, AUDIO_SAMPLE_FORMAT_24BIT_IN32 },
{ IHDA_PCM_SIZE_20BITS, AUDIO_SAMPLE_FORMAT_20BIT_IN32 },
{ IHDA_PCM_SIZE_16BITS, AUDIO_SAMPLE_FORMAT_16BIT },
};
for (const auto& f : FORMAT_LUT) {
if (sample_caps.pcm_size_rate_ & f.ihda_flag) {
signed_formats =
static_cast<audio_sample_format_t>(signed_formats | f.audio_flag);
}
}
if (sample_caps.pcm_size_rate_ & IHDA_PCM_SIZE_8BITS) {
unsigned_8bit_supported = true;
}
}
// If float is supported, add that into the set of signed formats that we
// support.
if (sample_caps.pcm_formats_ & IHDA_PCM_FORMAT_FLOAT32) {
signed_formats =
static_cast<audio_sample_format_t>(signed_formats | AUDIO_SAMPLE_FORMAT_32BIT_FLOAT);
}
// If we do not support any sample formats, simply get out early. There is
// no point in trying to compute the frame rate ranges.
if (!signed_formats && !unsigned_8bit_supported)
return ZX_OK;
// Next, produce the sets of frame rates in the 48 and 44.1KHz frame rate
// families which can be expressed using the [min, max] notation. In
// theory, it might be possible to combine each of these into a single
// audio_stream_format_range_t entry, but to keep things simple, we don't
// try to do so.
for (const auto& family : FRAME_RATE_LUTS) {
bool active_range = false;
uint32_t min_rate = 0, max_rate = 0;
for (size_t i = 0; i < family.lut_size; ++i) {
const auto& entry = family.lut[i];
bool supported_rate = (sample_caps.pcm_size_rate_ & entry.flag) != 0;
// If this rate is supported, then either start a new range of
// contiguous rates (if this is the first in this range) or bump up
// the max rate if we are already building a range.
if (supported_rate) {
if (!active_range) {
min_rate = entry.rate;
max_rate = entry.rate;
active_range = true;
} else {
max_rate = entry.rate;
}
}
// If we have an active range of contiguous rates, and we have
// either encountered a gap in the supported rates or this is the
// last rate in the family, then produce the format ranges needed
// for this range of rates.
if (active_range && (!supported_rate || (i == family.lut_size))) {
audio_stream_format_range_t range;
range.min_frames_per_second = min_rate;
range.max_frames_per_second = max_rate;
range.min_channels = 1;
range.max_channels = static_cast<uint8_t>(max_channels);
range.flags = family.family_flag;
if (signed_formats) {
range.sample_formats = signed_formats;
fbl::AllocChecker ac;
ranges->push_back(range, &ac);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
}
if (unsigned_8bit_supported) {
range.sample_formats = AUDIO_SAMPLE_FORMAT_UNSIGNED_8BIT;
fbl::AllocChecker ac;
ranges->push_back(range, &ac);
if (!ac.check())
return ZX_ERR_NO_MEMORY;
}
active_range = false;
}
}
}
return ZX_OK;
}
} // namespace intel_hda
} // namespace audio