blob: cfcbaa471d0728f7aabbd44d876b28b24bdccdb5 [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 "intel-hda/utils/utils.h"
#include <lib/zx/time.h>
#include <zircon/syscalls/object.h>
#include <zircon/time.h>
#include <zircon/types.h>
#include <iterator>
#include <utility>
#include <abs_clock/clock.h>
#include <fbl/algorithm.h>
namespace audio {
namespace intel_hda {
using abs_clock::Clock;
zx_status_t WaitCondition(zx_duration_t timeout, zx_duration_t poll_interval, WaitConditionFn cond,
Clock* clock) {
ZX_DEBUG_ASSERT(poll_interval != ZX_TIME_INFINITE);
ZX_DEBUG_ASSERT(cond);
zx::time now = clock->Now();
zx::time deadline = now + zx::duration(timeout);
while (!cond()) {
// If we have passed our deadline, give up.
if (now >= deadline) {
return ZX_ERR_TIMED_OUT;
}
// Sleep until the next poll interval (or the deadline, if it is sooner).
zx::time next_poll_time = std::min(deadline, now + zx::duration(poll_interval));
clock->SleepUntil(next_poll_time);
now = clock->Now();
}
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, std::size(FRAME_RATE_LUT_48K), ASF_RANGE_FLAG_FPS_48000_FAMILY},
{FRAME_RATE_LUT_44_1K, std::size(FRAME_RATE_LUT_44_1K), ASF_RANGE_FLAG_FPS_44100_FAMILY},
};
static const struct {
uint32_t ihda_flag;
uint8_t sad_bitrate;
} FORMAT_SAD_LUT[] = {
{IHDA_PCM_SIZE_24BITS, edid::ShortAudioDescriptor::kLpcm_24},
{IHDA_PCM_SIZE_20BITS, edid::ShortAudioDescriptor::kLpcm_20},
{IHDA_PCM_SIZE_16BITS, edid::ShortAudioDescriptor::kLpcm_16},
};
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;
}
zx_status_t MakeNewSampleCaps(const SampleCaps& old_sample_caps,
const edid::ShortAudioDescriptor sad_list[], const size_t sad_count,
SampleCaps& new_sample_caps) {
static constexpr struct {
uint32_t flag;
uint32_t rate;
} kSadRates[7] = {
{edid::ShortAudioDescriptor::kHz32, 32000}, {edid::ShortAudioDescriptor::kHz44, 44100},
{edid::ShortAudioDescriptor::kHz48, 48000}, {edid::ShortAudioDescriptor::kHz88, 88200},
{edid::ShortAudioDescriptor::kHz96, 96000}, {edid::ShortAudioDescriptor::kHz176, 176400},
{edid::ShortAudioDescriptor::kHz192, 192000},
};
static constexpr struct {
uint32_t flag;
uint32_t rate;
} kSampleCapsRates[] = {
{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}, {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},
};
if (!(old_sample_caps.pcm_formats_ & IHDA_PCM_FORMAT_PCM)) {
return ZX_ERR_NOT_SUPPORTED;
}
new_sample_caps.pcm_formats_ |= IHDA_PCM_FORMAT_PCM;
for (uint8_t i = 0; i < sad_count; ++i) {
for (size_t j = 0; j < std::size(FORMAT_SAD_LUT); ++j) {
if (!(sad_list[i].bitrate & FORMAT_SAD_LUT[j].sad_bitrate)) {
continue;
}
if (old_sample_caps.pcm_size_rate_ & FORMAT_SAD_LUT[j].ihda_flag) {
new_sample_caps.pcm_size_rate_ |= FORMAT_SAD_LUT[j].ihda_flag;
}
}
for (size_t j = 0; j < std::size(kSadRates); ++j) {
if (!(sad_list[i].sampling_frequencies & kSadRates[j].flag)) {
continue;
}
for (size_t k = 0; k < std::size(kSampleCapsRates); ++k) {
if (kSadRates[j].rate == kSampleCapsRates[k].rate &&
(old_sample_caps.pcm_size_rate_ & kSampleCapsRates[k].flag)) {
new_sample_caps.pcm_size_rate_ |= kSampleCapsRates[k].flag;
break;
}
}
}
}
if (new_sample_caps.pcm_size_rate_ == 0) {
return ZX_ERR_NOT_FOUND;
}
return ZX_OK;
}
} // namespace intel_hda
} // namespace audio