| // 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 |