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