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