blob: 14f02312b9f4a9c018a00efd35ee3d43d04e9bda [file] [log] [blame]
// Copyright 2018 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.
#ifndef SRC_DEVICES_LIB_AMLOGIC_INCLUDE_SOC_AML_COMMON_AML_TDM_AUDIO_H_
#define SRC_DEVICES_LIB_AMLOGIC_INCLUDE_SOC_AML_COMMON_AML_TDM_AUDIO_H_
#include <assert.h>
#include <lib/mmio/mmio.h>
#include <memory>
#include <utility>
#include <soc/aml-common/aml-audio-regs.h>
#include <soc/aml-common/aml-audio.h>
class AmlTdmDevice {
public:
DISALLOW_COPY_ASSIGN_AND_MOVE(AmlTdmDevice);
static std::unique_ptr<AmlTdmDevice> Create(
ddk::MmioBuffer mmio, ee_audio_mclk_src_t src, aml_tdm_out_t tdm, aml_frddr_t frddr,
aml_tdm_mclk_t mclk, metadata::AmlVersion version = metadata::AmlVersion::kS905D2G);
// Configure an mclk channel divider
zx_status_t SetMclkDiv(uint32_t div);
// Configure an sclk/lclk generator block
zx_status_t SetSclkDiv(uint32_t sdiv, uint32_t lrduty, uint32_t lrdiv, bool sclk_invert_ph0);
// Configures the mclk pad.
zx_status_t SetMClkPad(aml_tdm_mclk_pad_t mclk_pad);
// Configures placement of data on the tdm bus
virtual void ConfigTdmSlot(uint8_t bit_offset, uint8_t num_slots, uint8_t bits_per_slot,
uint8_t bits_per_sample, uint8_t mix_mask, bool i2s_mode) = 0;
// Configures Lanes.
virtual zx_status_t ConfigTdmLane(size_t lane, uint32_t enable_mask, uint32_t mute_mask) = 0;
// Configures TDM swaps.
virtual void ConfigTdmSwaps(uint32_t swaps) = 0;
// Sets the buffer/length pointers for dma engine
// must resize in lower 32-bits of address space.
virtual zx_status_t SetBuffer(zx_paddr_t buf, size_t len) = 0;
// Get HW alignment required in the buffer in bytes.
static uint32_t GetBufferAlignment() { return 8; }
// Returns offset of dma pointer in the ring buffer.
virtual uint32_t GetRingPosition() = 0;
// Resets state of dma mechanisms and starts clocking data
// onto/from tdm bus with data fetched from beginning of buffer.
virtual uint64_t Start() = 0;
// Stops clocking data out/in on/from the TDM bus (physical tdm bus signals remain active).
virtual void Stop() = 0;
// Synchronize the state of TDM bus signals with fifo/dma engine.
virtual void Sync() = 0;
// Start clocking, configure the DDR and TDM interfaces.
virtual void Initialize() = 0;
// Stops the clocking data, shuts down the DDR interface, and quiets output signals.
virtual void Shutdown() = 0;
virtual uint32_t fifo_depth() const = 0;
virtual const ddk::MmioBuffer& GetMmio() const = 0;
protected:
friend class std::default_delete<AmlTdmDevice>;
AmlTdmDevice(aml_tdm_mclk_t mclk_ch, const ee_audio_mclk_src_t clk_src,
const metadata::AmlVersion version)
: mclk_ch_(mclk_ch), clk_src_(clk_src), version_(version) {}
virtual ~AmlTdmDevice() = default;
void InitMclk();
void InitMstPad();
void AudioClkEna(uint32_t audio_blk_mask);
void AudioClkDis(uint32_t audio_blk_mask);
private:
static constexpr int32_t kMclkDivBits = 16;
static constexpr int32_t kSclkDivBits = 10;
static constexpr int32_t kLRclkDivBits = 10;
const aml_tdm_mclk_t mclk_ch_; // mclk channel used by this instance
const ee_audio_mclk_src_t clk_src_;
const metadata::AmlVersion version_;
};
class AmlTdmOutDevice : public AmlTdmDevice { // Not final for unit testing.
public:
static std::unique_ptr<AmlTdmDevice> Create(
ddk::MmioBuffer mmio, ee_audio_mclk_src_t src, aml_tdm_out_t tdm, aml_frddr_t frddr,
aml_tdm_mclk_t mclk, metadata::AmlVersion version = metadata::AmlVersion::kS905D2G);
void ConfigTdmSlot(uint8_t bit_offset, uint8_t num_slots, uint8_t bits_per_slot,
uint8_t bits_per_sample, uint8_t mix_mask, bool i2s_mode) override;
zx_status_t ConfigTdmLane(size_t lane, uint32_t enable_mask, uint32_t mute_mask) override;
void ConfigTdmSwaps(uint32_t swaps) override;
zx_status_t SetBuffer(zx_paddr_t buf, size_t len) override;
uint32_t GetRingPosition() override;
uint64_t Start() override;
void Stop() override;
void Sync() override;
void Initialize() override;
void Shutdown() override;
uint32_t fifo_depth() const override { return fifo_depth_; }
protected:
AmlTdmOutDevice(ddk::MmioBuffer mmio, ee_audio_mclk_src_t clk_src, aml_tdm_out_t tdm,
aml_frddr_t frddr, aml_tdm_mclk_t mclk, uint32_t fifo_depth,
metadata::AmlVersion version)
: AmlTdmDevice(mclk, clk_src, version),
fifo_depth_(fifo_depth),
tdm_ch_(tdm),
frddr_ch_(frddr),
mclk_ch_(mclk),
frddr_base_(GetFrddrBase(frddr)),
tdm_base_(GetTdmBase(tdm)),
mmio_(std::move(mmio)),
version_(version) {}
private:
const ddk::MmioBuffer& GetMmio() const override { return mmio_; }
/* Get the register block offset for our ddr block */
zx_off_t GetFrddrBase(aml_frddr_t ch) {
switch (ch) {
case FRDDR_A:
return EE_AUDIO_FRDDR_A_CTRL0;
case FRDDR_B:
return EE_AUDIO_FRDDR_B_CTRL0;
case FRDDR_C:
return EE_AUDIO_FRDDR_C_CTRL0;
}
// We should never get here, but if we do, make it obvious
assert(0);
return 0;
}
/* Get the register block offset for our tdm block */
zx_off_t GetTdmBase(aml_tdm_out_t ch) {
switch (ch) {
case TDM_OUT_A:
return EE_AUDIO_TDMOUT_A_CTRL0;
case TDM_OUT_B:
return EE_AUDIO_TDMOUT_B_CTRL0;
case TDM_OUT_C:
return EE_AUDIO_TDMOUT_C_CTRL0;
}
// We should never get here, but if we do, make it obvious
assert(0);
return 0;
}
void FRDDREnable();
void FRDDRDisable();
void TdmOutDisable();
void TdmOutEnable();
/* Get the register block offset for our ddr block */
zx_off_t GetFrddrOffset(zx_off_t off) { return frddr_base_ + off; }
/* Get the register block offset for our tdm block */
zx_off_t GetTdmOffset(zx_off_t off) { return tdm_base_ + off; }
const uint32_t fifo_depth_; // in bytes.
const aml_tdm_out_t tdm_ch_; // tdm output block used by this instance
const aml_frddr_t frddr_ch_; // fromddr channel used by this instance
const aml_tdm_mclk_t mclk_ch_; // mclk channel used by this instance
const zx_off_t frddr_base_; // base offset of frddr ch used by this instance
const zx_off_t tdm_base_; // base offset of our tdmout block
const ddk::MmioBuffer mmio_;
const metadata::AmlVersion version_;
};
class AmlTdmInDevice : public AmlTdmDevice { // Not final for unit testing.
public:
static std::unique_ptr<AmlTdmDevice> Create(
ddk::MmioBuffer mmio, ee_audio_mclk_src_t src, aml_tdm_in_t tdm, aml_toddr_t toddr,
aml_tdm_mclk_t mclk, metadata::AmlVersion version = metadata::AmlVersion::kS905D2G);
void ConfigTdmSlot(uint8_t bit_offset, uint8_t num_slots, uint8_t bits_per_slot,
uint8_t bits_per_sample, uint8_t mix_mask, bool i2s_mode) override;
zx_status_t ConfigTdmLane(size_t lane, uint32_t enable_mask, uint32_t mute_mask) override;
void ConfigTdmSwaps(uint32_t swaps) override;
zx_status_t SetBuffer(zx_paddr_t buf, size_t len) override;
uint32_t GetRingPosition() override;
uint64_t Start() override;
void Stop() override;
void Sync() override;
void Initialize() override;
void Shutdown() override;
uint32_t fifo_depth() const override { return fifo_depth_; }
protected:
AmlTdmInDevice(ddk::MmioBuffer mmio, ee_audio_mclk_src_t clk_src, aml_tdm_in_t tdm,
aml_toddr_t toddr, aml_tdm_mclk_t mclk, uint32_t fifo_depth,
metadata::AmlVersion version)
: AmlTdmDevice(mclk, clk_src, version),
fifo_depth_(fifo_depth),
tdm_ch_(tdm),
toddr_ch_(toddr),
mclk_ch_(mclk),
toddr_base_(GetToddrBase(toddr)),
tdm_base_(GetTdmBase(tdm)),
mmio_(std::move(mmio)),
version_(version) {}
private:
const ddk::MmioBuffer& GetMmio() const override { return mmio_; }
/* Get the register block offset for our ddr block */
static zx_off_t GetToddrBase(aml_toddr_t ch) {
switch (ch) {
case TODDR_A:
return EE_AUDIO_TODDR_A_CTRL0;
case TODDR_B:
return EE_AUDIO_TODDR_B_CTRL0;
case TODDR_C:
return EE_AUDIO_TODDR_C_CTRL0;
}
// We should never get here, but if we do, make it obvious
assert(0);
return 0;
}
/* Get the register block offset for our tdm block */
static zx_off_t GetTdmBase(aml_tdm_in_t ch) {
switch (ch) {
case TDM_IN_A:
return EE_AUDIO_TDMIN_A_CTRL0;
case TDM_IN_B:
return EE_AUDIO_TDMIN_B_CTRL0;
case TDM_IN_C:
return EE_AUDIO_TDMIN_C_CTRL0;
}
// We should never get here, but if we do, make it obvious
assert(0);
return 0;
}
void TODDREnable();
void TODDRDisable();
void TdmInDisable();
void TdmInEnable();
/* Get the register block offset for our ddr block */
zx_off_t GetToddrOffset(zx_off_t off) { return toddr_base_ + off; }
/* Get the register block offset for our tdm block */
zx_off_t GetTdmOffset(zx_off_t off) { return tdm_base_ + off; }
const uint32_t fifo_depth_; // in bytes.
const aml_tdm_in_t tdm_ch_; // tdm input block used by this instance
const aml_toddr_t toddr_ch_; // fromddr channel used by this instance
const aml_tdm_mclk_t mclk_ch_; // mclk channel used by this instance
const zx_off_t toddr_base_; // base offset of toddr ch used by this instance
const zx_off_t tdm_base_; // base offset of our tdmin block
const ddk::MmioBuffer mmio_;
const metadata::AmlVersion version_;
};
#endif // SRC_DEVICES_LIB_AMLOGIC_INCLUDE_SOC_AML_COMMON_AML_TDM_AUDIO_H_