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    Presently assumes stereo input with both streams multiplexed on the same
13    PDM input line. (TODO: support up to 8 channels to refactor gauss to use this)
14*/
15
16class AmlPdmDevice {
17
18public:
19    DISALLOW_COPY_ASSIGN_AND_MOVE(AmlPdmDevice);
20
21    static fbl::unique_ptr<AmlPdmDevice> Create(ddk::MmioBuffer pdm_mmio,
22                                                ddk::MmioBuffer audio_mmio,
23                                                ee_audio_mclk_src_t pdm_clk_src,
24                                                uint32_t sclk_div,
25                                                uint32_t dclk_div,
26                                                aml_toddr_t toddr_dev);
27
28    // Sets the buffer/length pointers for dma engine
29    //  must resize in lower 32-bits of address space
30    zx_status_t SetBuffer(zx_paddr_t buf, size_t len);
31
32    /*
33        Returns offset of dma pointer in the ring buffer
34    */
35    uint32_t GetRingPosition();
36
37    /*
38        Resets state of dma mechanisms and starts clocking data
39        in from pdm bus with data written to start of ring buffer
40    */
41    uint64_t Start();
42
43    /*
44        Stops clocking stat in off PDM bus
45        (physical pdm bus signals remain active)
46    */
47    void Stop();
48
49    /*
50        Synchronize the state of PDM bus signals with fifo/dma engine
51    */
52    void Sync();
53
54    /*
55        shuts down toddr, stops writing data to ring buffer
56    */
57    void Shutdown();
58
59    uint32_t fifo_depth() const { return fifo_depth_; };
60
61private:
62    friend class fbl::unique_ptr<AmlPdmDevice>;
63
64    AmlPdmDevice(ddk::MmioBuffer pdm_mmio, ddk::MmioBuffer audio_mmio,
65                 ee_audio_mclk_src_t clk_src, uint32_t sysclk_div, uint32_t dclk_div,
66                 aml_toddr_t toddr, uint32_t fifo_depth)
67        : fifo_depth_(fifo_depth),
68          toddr_ch_(toddr),
69          clk_src_(clk_src),
70          sysclk_div_(sysclk_div),
71          dclk_div_(dclk_div),
72          toddr_base_(GetToddrBase(toddr)),
73          pdm_mmio_(fbl::move(pdm_mmio)),
74          audio_mmio_(fbl::move(audio_mmio)){};
75
76    ~AmlPdmDevice() = default;
77
78    void ConfigFilters();
79
80    /* Get the resgister block offset for our ddr block */
81    static zx_off_t GetToddrBase(aml_toddr_t ch) {
82        switch (ch) {
83        case TODDR_A:
84            return EE_AUDIO_TODDR_A_CTRL0;
85        case TODDR_B:
86            return EE_AUDIO_TODDR_B_CTRL0;
87        case TODDR_C:
88            return EE_AUDIO_TODDR_C_CTRL0;
89        }
90        //We should never get here, but if we do, make it hard to ignore
91        ZX_PANIC("Invalid toddr channel specified!\n");
92        return 0;
93    }
94
95    void AudioClkEna(uint32_t audio_blk_mask);
96    void AudioClkDis(uint32_t audio_blk_mask);
97    void InitRegs();
98    void TODDREnable();
99    void TODDRDisable();
100    void PdmInDisable();
101    void PdmInEnable();
102
103    /* Get the resgister block offset for our ddr block */
104    zx_off_t GetToddrOffset(zx_off_t off) {
105        return toddr_base_ + off;
106    }
107    const uint32_t fifo_depth_;
108    const aml_toddr_t toddr_ch_; // fromddr channel used by this instance
109    const ee_audio_mclk_src_t clk_src_;
110    const uint32_t sysclk_div_;
111    const uint32_t dclk_div_;
112    const zx_off_t toddr_base_; // base offset of frddr ch used by this instance
113    const ddk::MmioBuffer pdm_mmio_;
114    const ddk::MmioBuffer audio_mmio_;
115};
116