blob: 9abf308bd6bb479a51dd4b73dad077ad2178e19e [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 <fbl/alloc_checker.h>
#include <soc/as370/as370-audio-regs.h>
#include <soc/as370/as370-clk-regs.h>
#include <soc/as370/as370-dma.h>
#include <soc/as370/syn-audio-out.h>
std::unique_ptr<SynAudioOutDevice> SynAudioOutDevice::Create(ddk::MmioBuffer mmio_global,
ddk::MmioBuffer mmio_avio_global,
ddk::MmioBuffer mmio_i2s,
ddk::SharedDmaProtocolClient dma) {
fbl::AllocChecker ac;
auto dev = std::unique_ptr<SynAudioOutDevice>(new (&ac) SynAudioOutDevice(
std::move(mmio_global), std::move(mmio_avio_global), std::move(mmio_i2s), dma));
if (!ac.check()) {
return nullptr;
}
return dev;
}
SynAudioOutDevice::SynAudioOutDevice(ddk::MmioBuffer mmio_global, ddk::MmioBuffer mmio_avio_global,
ddk::MmioBuffer mmio_i2s, ddk::SharedDmaProtocolClient dma)
: global_(std::move(mmio_global)),
avio_global_(std::move(mmio_avio_global)),
i2s_(std::move(mmio_i2s)),
dma_(dma) {
AIO_PRI_TSD0_PRI_CTRL::Get().ReadFrom(&i2s_).set_ENABLE(0).WriteTo(&i2s_); // Disable channel 0.
AIO_IRQENABLE::Get().ReadFrom(&i2s_).set_PRIIRQ(1).WriteTo(&i2s_);
AIO_PRI_PRIPORT::Get().ReadFrom(&i2s_).set_ENABLE(1).WriteTo(&i2s_);
}
zx_status_t SynAudioOutDevice::Init() { return ZX_OK; }
uint32_t SynAudioOutDevice::GetRingPosition() { return dma_.GetBufferPosition(DmaId::kDmaIdMa0); }
zx_status_t SynAudioOutDevice::GetBuffer(size_t size, zx::vmo* buffer) {
return dma_.InitializeAndGetBuffer(DmaId::kDmaIdMa0, DMA_TYPE_CYCLIC, static_cast<uint32_t>(size),
buffer);
}
uint64_t SynAudioOutDevice::Start() {
AIO_PRI_TSD0_PRI_CTRL::Get().FromValue(0).set_ENABLE(1).set_MUTE(1).WriteTo(&i2s_);
constexpr uint32_t divider = 4; // BCLK = MCLK (24.576 MHz) / 8 = 3.072 MHz.
AIO_PRI_PRIAUD_CLKDIV::Get().FromValue(0).set_SETTING(divider).WriteTo(&i2s_);
AIO_MCLKPRI_ACLK_CTRL::Get()
.FromValue(0)
.set_sw_sync_rst(1)
.set_clkSel(4) // MCLK = APLL0 (196.608MHz) / 8 = 24.576MHz.
.set_clkSwitch(1)
.set_clk_Enable(1)
.WriteTo(&i2s_);
// Set I2S, 48K, 32 bits. So BCLK must be 32 * 2 * 48K = 3.072MHz.
AIO_PRI_PRIAUD_CTRL::Get()
.FromValue(0)
.set_LEFTJFY(0) // left.
.set_INVCLK(0)
.set_INVFS(0)
.set_TLSB(0) // MSB first.
.set_TDM(0) // Channel resolution, 16 bits per channel.
.set_TCF(2) // 32 bit-cloks for FSYNC half-period.
.set_TFM(2) // I2S.
.set_TDMMODE(0) // I2S.
.set_TDMWSHIGH(0)
.WriteTo(&i2s_);
enabled_ = true;
dma_.Start(DmaId::kDmaIdMa0);
AIO_PRI_TSD0_PRI_CTRL::Get().FromValue(0).set_ENABLE(1).set_MUTE(0).WriteTo(&i2s_);
return 0;
}
void SynAudioOutDevice::Stop() {
AIO_PRI_TSD0_PRI_CTRL::Get().ReadFrom(&i2s_).set_MUTE(1).WriteTo(&i2s_);
enabled_ = false;
dma_.Stop(DmaId::kDmaIdMa0);
}
void SynAudioOutDevice::Shutdown() {
Stop();
AIO_PRI_PRIPORT::Get().ReadFrom(&i2s_).set_ENABLE(0).WriteTo(&i2s_);
}