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