1// Copyright 2018 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#pragma once 6 7#include <assert.h> 8#include <ddktl/mmio.h> 9#include <fbl/unique_ptr.h> 10#include <soc/aml-common/aml-audio-regs.h> 11 12class AmlTdmDevice { 13 14public: 15 16 DISALLOW_COPY_ASSIGN_AND_MOVE(AmlTdmDevice); 17 18 static constexpr int32_t kMclkDivBits = 16; 19 static constexpr int32_t kSclkDivBits = 10; 20 static constexpr int32_t kLRclkDivBits = 10; 21 22 static fbl::unique_ptr<AmlTdmDevice> Create(ddk::MmioBuffer mmio, 23 ee_audio_mclk_src_t src, 24 aml_tdm_out_t tdm_dev, 25 aml_frddr_t frddr_dev, 26 aml_tdm_mclk_t mclk); 27 28 //Configure an mclk channel divider 29 zx_status_t SetMclkDiv(uint32_t div); 30 //Configure an sclk/lclk generator block 31 zx_status_t SetSclkDiv(uint32_t sdiv, uint32_t lrduty, uint32_t lrdiv); 32 33 // Configures placement of data on the tdm bus 34 void ConfigTdmOutSlot(uint8_t bit_offset, uint8_t num_slots, 35 uint8_t bits_per_slot, uint8_t bits_per_sample); 36 37 // Sets the buffer/length pointers for dma engine 38 // must resize in lower 32-bits of address space 39 zx_status_t SetBuffer(zx_paddr_t buf, size_t len); 40 41 /* 42 Returns offset of dma pointer in the ring buffer 43 */ 44 uint32_t GetRingPosition(); 45 46 /* 47 Resets state of dma mechanisms and starts clocking data 48 onto tdm bus with data fetched from beginning of buffer 49 */ 50 uint64_t Start(); 51 52 /* 53 Stops clocking data out on the TDM bus 54 (physical tdm bus signals remain active) 55 */ 56 void Stop(); 57 58 /* 59 Synchronize the state of TDM bus signals with fifo/dma engine 60 */ 61 void Sync(); 62 63 /* 64 Stops the clocking data, shuts down frddr, and quiets output signals 65 */ 66 void Shutdown(); 67 68 uint32_t fifo_depth() const { return fifo_depth_;}; 69 70private: 71 const uint32_t fifo_depth_; 72 const aml_tdm_out_t tdm_ch_; // tdm output block used by this instance 73 const aml_frddr_t frddr_ch_; // fromddr channel used by this instance 74 const aml_tdm_mclk_t mclk_ch_; // mclk channel used by this instance 75 const ee_audio_mclk_src_t clk_src_; 76 const zx_off_t frddr_base_; // base offset of frddr ch used by this instance 77 const zx_off_t tdm_base_; // base offset of our tdmout block 78 const ddk::MmioBuffer mmio_; 79 friend class fbl::unique_ptr<AmlTdmDevice>; 80 81 AmlTdmDevice(ddk::MmioBuffer mmio, ee_audio_mclk_src_t clk_src, 82 aml_tdm_out_t tdm, aml_frddr_t frddr, aml_tdm_mclk_t mclk, 83 uint32_t fifo_depth) : 84 fifo_depth_(fifo_depth), 85 tdm_ch_(tdm), 86 frddr_ch_(frddr), 87 mclk_ch_(mclk), 88 clk_src_(clk_src), 89 frddr_base_(GetFrddrBase(frddr)), 90 tdm_base_(GetTdmBase(tdm)), 91 mmio_(fbl::move(mmio)) {}; 92 93 ~AmlTdmDevice() = default; 94 95 /* Get the resgister block offset for our ddr block */ 96 static zx_off_t GetFrddrBase(aml_frddr_t ch) { 97 switch (ch) { 98 case FRDDR_A: 99 return EE_AUDIO_FRDDR_A_CTRL0; 100 case FRDDR_B: 101 return EE_AUDIO_FRDDR_B_CTRL0; 102 case FRDDR_C: 103 return EE_AUDIO_FRDDR_C_CTRL0; 104 } 105 //We should never get here, but if we do, make it obvious 106 assert(0); 107 return 0; 108 } 109 /* Get the register block offset for our tdm block */ 110 static zx_off_t GetTdmBase(aml_tdm_out_t ch) { 111 switch (ch) { 112 case TDM_OUT_A: 113 return EE_AUDIO_TDMOUT_A_CTRL0; 114 case TDM_OUT_B: 115 return EE_AUDIO_TDMOUT_B_CTRL0; 116 case TDM_OUT_C: 117 return EE_AUDIO_TDMOUT_C_CTRL0; 118 } 119 //We should never get here, but if we do, make it obvious 120 assert(0); 121 return 0; 122 } 123 124 void AudioClkEna(uint32_t audio_blk_mask); 125 void AudioClkDis(uint32_t audio_blk_mask); 126 void InitRegs(); 127 void FRDDREnable(); 128 void FRDDRDisable(); 129 void TdmOutDisable(); 130 void TdmOutEnable(); 131 132 /* Get the resgister block offset for our ddr block */ 133 zx_off_t GetFrddrOffset(zx_off_t off) { 134 return frddr_base_ + off; 135 } 136 /* Get the register block offset for our tdm block */ 137 zx_off_t GetTdmOffset(zx_off_t off) { 138 return tdm_base_ + off; 139 } 140}; 141