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#include <ddk/debug.h>
6#include <fbl/limits.h>
7#include <soc/aml-common/aml-pdm-audio.h>
8
9// Filter configurations
10//mode 1 lpf1
11static const uint32_t lpf1m1[] = {
12    0x000014, 0xffffb2, 0xfffed9, 0xfffdce, 0xfffd45, 0xfffe32, 0x000147,
13    0x000645, 0x000b86, 0x000e21, 0x000ae3, 0x000000, 0xffeece, 0xffdca8,
14    0xffd212, 0xffd7d1, 0xfff2a7, 0x001f4c, 0x0050c2, 0x0072aa, 0x006ff1,
15    0x003c32, 0xffdc4e, 0xff6a18, 0xff0fef, 0xfefbaf, 0xff4c40, 0x000000,
16    0x00ebc8, 0x01c077, 0x02209e, 0x01c1a4, 0x008e60, 0xfebe52, 0xfcd690,
17    0xfb8fa5, 0xfba498, 0xfd9812, 0x0181ce, 0x06f5f3, 0x0d112f, 0x12a958,
18    0x169686, 0x18000e, 0x169686, 0x12a958, 0x0d112f, 0x06f5f3, 0x0181ce,
19    0xfd9812, 0xfba498, 0xfb8fa5, 0xfcd690, 0xfebe52, 0x008e60, 0x01c1a4,
20    0x02209e, 0x01c077, 0x00ebc8, 0x000000, 0xff4c40, 0xfefbaf, 0xff0fef,
21    0xff6a18, 0xffdc4e, 0x003c32, 0x006ff1, 0x0072aa, 0x0050c2, 0x001f4c,
22    0xfff2a7, 0xffd7d1, 0xffd212, 0xffdca8, 0xffeece, 0x000000, 0x000ae3,
23    0x000e21, 0x000b86, 0x000645, 0x000147, 0xfffe32, 0xfffd45, 0xfffdce,
24    0xfffed9, 0xffffb2, 0x000014,
25};
26constexpr uint32_t kLpf1m1Len = static_cast<uint32_t>(countof(lpf1m1));
27
28//mode 1 lpf3
29static const uint32_t lpf3m1[] = {
30    0x000000, 0x000081, 0x000000, 0xfffedb, 0x000000, 0x00022d, 0x000000,
31    0xfffc46, 0x000000, 0x0005f7, 0x000000, 0xfff6eb, 0x000000, 0x000d4e,
32    0x000000, 0xffed1e, 0x000000, 0x001a1c, 0x000000, 0xffdcb0, 0x000000,
33    0x002ede, 0x000000, 0xffc2d1, 0x000000, 0x004ebe, 0x000000, 0xff9beb,
34    0x000000, 0x007dd7, 0x000000, 0xff633a, 0x000000, 0x00c1d2, 0x000000,
35    0xff11d5, 0x000000, 0x012368, 0x000000, 0xfe9c45, 0x000000, 0x01b252,
36    0x000000, 0xfdebf6, 0x000000, 0x0290b8, 0x000000, 0xfcca0d, 0x000000,
37    0x041d7c, 0x000000, 0xfa8152, 0x000000, 0x07e9c6, 0x000000, 0xf28fb5,
38    0x000000, 0x28b216, 0x3fffde, 0x28b216, 0x000000, 0xf28fb5, 0x000000,
39    0x07e9c6, 0x000000, 0xfa8152, 0x000000, 0x041d7c, 0x000000, 0xfcca0d,
40    0x000000, 0x0290b8, 0x000000, 0xfdebf6, 0x000000, 0x01b252, 0x000000,
41    0xfe9c45, 0x000000, 0x012368, 0x000000, 0xff11d5, 0x000000, 0x00c1d2,
42    0x000000, 0xff633a, 0x000000, 0x007dd7, 0x000000, 0xff9beb, 0x000000,
43    0x004ebe, 0x000000, 0xffc2d1, 0x000000, 0x002ede, 0x000000, 0xffdcb0,
44    0x000000, 0x001a1c, 0x000000, 0xffed1e, 0x000000, 0x000d4e, 0x000000,
45    0xfff6eb, 0x000000, 0x0005f7, 0x000000, 0xfffc46, 0x000000, 0x00022d,
46    0x000000, 0xfffedb, 0x000000, 0x000081, 0x000000,
47};
48constexpr uint32_t kLpf3m1Len = static_cast<uint32_t>(countof(lpf3m1));
49
50// osr64 lpf2
51static const uint32_t lpf2osr64[] = {
52    0x00050a, 0xfff004, 0x0002c1, 0x003c12, 0xffa818, 0xffc87d, 0x010aef,
53    0xff5223, 0xfebd93, 0x028f41, 0xff5c0e, 0xfc63f8, 0x055f81, 0x000000,
54    0xf478a0, 0x11c5e3, 0x2ea74d, 0x11c5e3, 0xf478a0, 0x000000, 0x055f81,
55    0xfc63f8, 0xff5c0e, 0x028f41, 0xfebd93, 0xff5223, 0x010aef, 0xffc87d,
56    0xffa818, 0x003c12, 0x0002c1, 0xfff004, 0x00050a,
57};
58constexpr uint32_t kLpf2osr64Len = static_cast<uint32_t>(countof(lpf2osr64));
59
60//static
61fbl::unique_ptr<AmlPdmDevice> AmlPdmDevice::Create(ddk::MmioBuffer pdm_mmio,
62                                                   ddk::MmioBuffer audio_mmio,
63                                                   ee_audio_mclk_src_t pdm_clk_src,
64                                                   uint32_t sysclk_div,
65                                                   uint32_t dclk_div,
66                                                   aml_toddr_t toddr_dev) {
67    //A and B FRDDR have 128 lines in fifo, C has 256
68    uint32_t fifo_depth = 128;
69    if (toddr_dev == TODDR_A) {
70        fifo_depth = 256;
71    }
72
73    fbl::AllocChecker ac;
74    auto pdm = fbl::unique_ptr<AmlPdmDevice>(
75        new (&ac) AmlPdmDevice(fbl::move(pdm_mmio), fbl::move(audio_mmio),
76                               pdm_clk_src, sysclk_div, dclk_div,
77                               toddr_dev, fifo_depth));
78    if (!ac.check()) {
79        zxlogf(ERROR, "%s: Could not create AmlPdmDevice\n", __func__);
80        return nullptr;
81    }
82
83    pdm->InitRegs();
84    pdm->ConfigFilters();
85
86    return pdm;
87}
88
89void AmlPdmDevice::InitRegs() {
90    // Setup toddr block
91    audio_mmio_.Write32((0x02 << 13) |  //Right justified 16-bit
92                        (31 << 8) |     //msb position of data out of pdm
93                        (16 << 3) |     //lsb position of data out of pdm
94                        (0x04 << 0),    //select pdm as data source
95                        GetToddrOffset(TODDR_CTRL0_OFFS));
96    audio_mmio_.Write32(((fifo_depth_ / 2) << 16) | //trigger ddr when fifo half full
97                        (0x02 << 8),                //STATUS2 source is ddr position
98                        GetToddrOffset(TODDR_CTRL1_OFFS));
99
100    //*To keep things simple, we are using the same clock sourse for both the
101    // pdm sysclk and dclk.  Sysclk needs to be ~100-200MHz per AmLogic recommendations.
102    // dclk is osr*fs
103    //*Sysclk must be configured, enabled, and PDM audio clock gated prior to
104    // accessing any of the registers mapped via pdm_mmio.  Writing without sysclk
105    // operating properly (and in range) will result in unknown results, reads
106    // will wedge the system.
107    audio_mmio_.Write32((1 << 31) | (clk_src_ << 24) | dclk_div_, EE_AUDIO_CLK_PDMIN_CTRL0);
108    audio_mmio_.Write32((1 << 31) | (clk_src_ << 24) | sysclk_div_, EE_AUDIO_CLK_PDMIN_CTRL1);
109
110    audio_mmio_.SetBits32((1 << 31) | (1 << toddr_ch_), EE_AUDIO_ARB_CTRL);
111
112    //Enable the audio domain clocks used by this instance.
113    AudioClkEna(EE_AUDIO_CLK_GATE_PDM |
114                (EE_AUDIO_CLK_GATE_TODDRA << toddr_ch_) |
115                EE_AUDIO_CLK_GATE_ARB);
116
117    //It is now safe to write to pdm registers
118
119    //Enable cts_pdm_clk gate (clock gate within pdm module)
120    pdm_mmio_.SetBits32(0x01, PDM_CLKG_CTRL);
121
122    //Enable Ch 0/1
123    pdm_mmio_.Write32((0x01 << 29) |    // 24bit output mode
124                          (0x03 << 8) | // Take Ch 0/1 out of reset
125                          (0x03 << 0)   // Enable Ch 0/1
126                      ,
127                      PDM_CTRL);
128
129    //This sets the number of sysclk cycles between edge of dclk and when
130    // data is sampled.  AmLogic material suggests this should be 3/4 of a
131    // dclk half-cycle.  Go ahead and set all eight channels.
132    uint32_t samp_delay = 3 * (dclk_div_ + 1) / (4 * 2 * (sysclk_div_ + 1));
133    pdm_mmio_.Write32((samp_delay << 0) | (samp_delay << 8) |
134                          (samp_delay << 16) | (samp_delay << 24),
135                      PDM_CHAN_CTRL);
136    pdm_mmio_.Write32((samp_delay << 0) | (samp_delay << 8) |
137                          (samp_delay << 16) | (samp_delay << 24),
138                      PDM_CHAN_CTRL1);
139}
140
141/*
142    Relies heavily on magic values from Amlogic.  @hollande following up to get
143    more information on operation as datasheet seems inconsistent with configuration.
144*/
145void AmlPdmDevice::ConfigFilters() {
146
147    pdm_mmio_.Write32((1 << 31) |        //Enable
148                      (0x11 << 24) |     //Final gain shift parameter)
149                      (0x80 << 16) |     //Final gain multiplier
150                      (0x08 << 4)  |     //hcic downsample rate=8
151                      (0x07 << 0),       //hcic stage number (must be between 3-9)
152                      PDM_HCIC_CTRL1);
153    pdm_mmio_.Write32((0x01 << 31) |     //Enable filter
154                      (0x01 << 16) |     //Round mode (this doesn't jibe w/ datasheet)
155                      (0x02 << 12) |     //Filter 1 downsample rate
156                      (kLpf1m1Len << 0), //Number of taps in filter
157                      PDM_F1_CTRL);
158    pdm_mmio_.Write32((0x01 << 31) |        //Enable filter
159                      (0x00 << 16) |        //Round mode (this doesn't jibe w/ datasheet)
160                      (0x02 << 12) |        //Filter 2 downsample rate
161                      (kLpf2osr64Len << 0), //Number of taps in filter
162                      PDM_F2_CTRL);
163    pdm_mmio_.Write32((0x01 << 31) |     //Enable filter
164                      (0x01 << 16) |     //Round mode (this doesn't jibe w/ datasheet)
165                      (0x02 << 12) |     //Filter 3 downsample rate
166                      (kLpf3m1Len << 0), //Number of taps in filter
167                      PDM_F3_CTRL);
168    pdm_mmio_.Write32((0x01 << 31) |     //Enable filter
169                      (0x07 << 16) |     //Shift steps
170                      (0x8000 << 0),     //Output factor
171                      PDM_HPF_CTRL);
172
173    //set coefficient index pointer to 0
174    pdm_mmio_.Write32(0x0000, PDM_COEFF_ADDR);
175
176    //Write coefficients to coefficient memory
177    //  --these appear to be packed with the filter length in each filter
178    //    control register being the mechanism that helps reference them
179    for (uint32_t i = 0; i < countof(lpf1m1); i++) {
180        pdm_mmio_.Write32(lpf1m1[i], PDM_COEFF_DATA);
181    }
182    for (uint32_t i = 0; i < countof(lpf2osr64); i++) {
183        pdm_mmio_.Write32(lpf2osr64[i], PDM_COEFF_DATA);
184    }
185    for (uint32_t i = 0; i < countof(lpf3m1); i++) {
186        pdm_mmio_.Write32(lpf3m1[i], PDM_COEFF_DATA);
187    }
188
189    //set coefficient index pointer back to 0
190    pdm_mmio_.Write32(0x0000, PDM_COEFF_ADDR);
191}
192
193uint32_t AmlPdmDevice::GetRingPosition() {
194    uint32_t pos = audio_mmio_.Read32(GetToddrOffset(TODDR_STATUS2_OFFS));
195    uint32_t base = audio_mmio_.Read32(GetToddrOffset(TODDR_START_ADDR_OFFS));
196    return (pos - base);
197}
198
199void AmlPdmDevice::AudioClkEna(uint32_t audio_blk_mask) {
200    audio_mmio_.SetBits32(audio_blk_mask, EE_AUDIO_CLK_GATE_EN);
201}
202
203void AmlPdmDevice::AudioClkDis(uint32_t audio_blk_mask) {
204    audio_mmio_.ClearBits32(audio_blk_mask, EE_AUDIO_CLK_GATE_EN);
205}
206
207zx_status_t AmlPdmDevice::SetBuffer(zx_paddr_t buf, size_t len) {
208    //Ensure ring buffer resides in lower memory (dma pointers are 32-bit)
209    //    and len is at least 8 (size of each dma operation)
210    if (((buf + len - 1) > fbl::numeric_limits<uint32_t>::max()) || (len < 8)) {
211        return ZX_ERR_INVALID_ARGS;
212    }
213
214    //Write32 the start and end pointers.  Each fetch is 64-bits, so end poitner
215    //    is pointer to the last 64-bit fetch (inclusive)
216    audio_mmio_.Write32(static_cast<uint32_t>(buf), GetToddrOffset(TODDR_START_ADDR_OFFS));
217    audio_mmio_.Write32(static_cast<uint32_t>(buf), GetToddrOffset(TODDR_INIT_ADDR_OFFS));
218    audio_mmio_.Write32(static_cast<uint32_t>(buf + len - 8),
219                        GetToddrOffset(TODDR_FINISH_ADDR_OFFS));
220    return ZX_OK;
221}
222
223// Stops the pdm from clocking
224void AmlPdmDevice::PdmInDisable() {
225    pdm_mmio_.ClearBits32((1 << 31) | (1 << 16), PDM_CTRL);
226}
227// Enables the pdm to clock data
228void AmlPdmDevice::PdmInEnable() {
229    pdm_mmio_.SetBits32((1 << 31) | (1 << 16), PDM_CTRL);
230}
231
232void AmlPdmDevice::TODDREnable() {
233    //Set the load bit, will make sure things start from beginning of buffer
234    audio_mmio_.SetBits32(1 << 31, GetToddrOffset(TODDR_CTRL0_OFFS));
235}
236
237void AmlPdmDevice::TODDRDisable() {
238    // Clear the load bit (this is the bit that forces the initial fetch of
239    //    start address into current ptr)
240    audio_mmio_.ClearBits32(1 << 31, GetToddrOffset(TODDR_CTRL0_OFFS));
241    audio_mmio_.ClearBits32(1 << 25, GetToddrOffset(TODDR_CTRL1_OFFS));
242}
243
244void AmlPdmDevice::Sync() {
245    pdm_mmio_.ClearBits32(1 << 16, PDM_CTRL);
246    pdm_mmio_.SetBits32(1 << 16, PDM_CTRL);
247}
248
249// Resets frddr mechanisms to start at beginning of buffer
250//   starts the frddr (this will fill the fifo)
251//   starts the tdm to clock out data on the bus
252// returns the start time
253uint64_t AmlPdmDevice::Start() {
254    uint64_t a, b;
255
256    Sync();
257    TODDREnable();
258    a = zx_clock_get(ZX_CLOCK_MONOTONIC);
259    PdmInEnable();
260    b = zx_clock_get(ZX_CLOCK_MONOTONIC);
261    return ((b - a) >> 1) + a;
262}
263
264void AmlPdmDevice::Stop() {
265    PdmInDisable();
266    TODDRDisable();
267}
268
269void AmlPdmDevice::Shutdown() {
270    Stop();
271}
272