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