blob: 5246e73a5936c9d5b7055c570736e2dfa8b30888 [file] [log] [blame]
// Copyright 2019 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 <limits>
#include <utility>
#include <ddk/debug.h>
#include <fbl/alloc_checker.h>
#include <soc/mt8167/mt8167-audio-out.h>
#include <soc/mt8167/mt8167-audio-regs.h>
std::unique_ptr<MtAudioOutDevice> MtAudioOutDevice::Create(ddk::MmioBuffer mmio, MtI2sCh ch) {
uint32_t fifo_depth = 36 * 1024; // in bytes.
// TODO(andresoportus): Support other configurations.
if (ch != I2S2) {
return nullptr;
}
fbl::AllocChecker ac;
auto dev =
std::unique_ptr<MtAudioOutDevice>(new (&ac) MtAudioOutDevice(std::move(mmio), fifo_depth));
if (!ac.check()) {
return nullptr;
}
dev->InitRegs();
return dev;
}
void MtAudioOutDevice::InitRegs() {
// Enable the AFE module.
AFE_DAC_CON0::Get().ReadFrom(&mmio_).set_AFE_ON(1).WriteTo(&mmio_);
// Power up the AFE module by clearing the power down bit.
AUDIO_TOP_CON0::Get().ReadFrom(&mmio_).set_PDN_AFE(0).WriteTo(&mmio_);
// I2S mode 48k, DL1 stereo, DL1 mode 48k.
auto dac_con1 = AFE_DAC_CON1::Get().ReadFrom(&mmio_);
dac_con1.set_DL1_DATA(0).set_DL1_MODE(10).WriteTo(&mmio_);
// Disable clock gating.
AUDIO_TOP_CON1::Get().ReadFrom(&mmio_).set_I2S2_BCLK_SW_CG(0).WriteTo(&mmio_);
// I2S2: Enable, I2S (not EIAJ), 16/32 bits, OUT_MODE set to 48k, and TDMOUT PAD set to I2S.
constexpr bool is_32_bits = 1;
constexpr uint32_t out_mode_48k = 10;
auto i2s_con1 = AFE_I2S_CON1::Get().ReadFrom(&mmio_);
i2s_con1.set_I2S2_EN(1).set_I2S2_FMT(1).set_I2S2_WLEN(is_32_bits);
i2s_con1.set_I2S2_OUT_MODE(out_mode_48k);
i2s_con1.set_I2S2_TDMOUT_MUX(1);
i2s_con1.WriteTo(&mmio_);
// Enable path from DL1 data to I2S/DL_SRC.
AFE_CONN1::Get().ReadFrom(&mmio_).set_I05_O03_S(1).WriteTo(&mmio_);
AFE_CONN2::Get().ReadFrom(&mmio_).set_I06_O04_S(1).WriteTo(&mmio_);
// Disable 24 bits.
AFE_CONN_24BIT::Get().ReadFrom(&mmio_).set_O03_24BIT(0).WriteTo(&mmio_);
AFE_CONN_24BIT::Get().ReadFrom(&mmio_).set_O04_24BIT(0).WriteTo(&mmio_);
}
uint32_t MtAudioOutDevice::GetRingPosition() {
return AFE_DL1_CUR::Get().ReadFrom(&mmio_).reg_value() -
AFE_DL1_BASE::Get().ReadFrom(&mmio_).reg_value();
}
zx_status_t MtAudioOutDevice::SetBuffer(zx_paddr_t buf, size_t len) {
if ((buf % 16) || ((buf + len - 1) > std::numeric_limits<uint32_t>::max()) || (len < 16) ||
(len % 16)) {
return ZX_ERR_INVALID_ARGS;
}
// End is inclusive.
AFE_DL1_BASE::Get().FromValue(static_cast<uint32_t>(buf)).WriteTo(&mmio_);
AFE_DL1_END::Get().FromValue(static_cast<uint32_t>(buf + len - 1)).WriteTo(&mmio_);
return ZX_OK;
}
uint64_t MtAudioOutDevice::Start() {
AFE_DAC_CON0::Get().ReadFrom(&mmio_).set_DL1_ON(1).WriteTo(&mmio_);
return 0;
}
void MtAudioOutDevice::Stop() {
AFE_DAC_CON0::Get().ReadFrom(&mmio_).set_DL1_ON(0).WriteTo(&mmio_);
}
void MtAudioOutDevice::Shutdown() {
Stop();
// Disable the AFE module.
AFE_DAC_CON0::Get().ReadFrom(&mmio_).set_AFE_ON(0).WriteTo(&mmio_);
}