| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <limits> |
| #include <memory> |
| #include <utility> |
| |
| #include <ddk/debug.h> |
| #include <soc/aml-common/aml-tdm-audio.h> |
| |
| void AmlTdmDevice::InitMclk() { |
| zx_off_t mclk_a = {}; |
| switch (version_) { |
| case metadata::AmlVersion::kS905D2G: |
| mclk_a = EE_AUDIO_MCLK_A_CTRL; |
| break; |
| case metadata::AmlVersion::kS905D3G: |
| mclk_a = EE_AUDIO_MCLK_A_CTRL_D3G; |
| break; |
| } |
| // Set chosen mclk channels input to selected source |
| // Since this is init, set the divider to max value assuming it will |
| // be set to proper value later (slower is safer from circuit standpoint) |
| // Leave disabled for now. |
| zx_off_t ptr = mclk_a + (mclk_ch_ * sizeof(uint32_t)); |
| GetMmio().Write32((clk_src_ << 24) | 0xffff, ptr); |
| } |
| |
| /* Notes |
| -div is desired divider minus 1. (want /100? write 99) |
| */ |
| zx_status_t AmlTdmDevice::SetMclkDiv(uint32_t div) { |
| // check that divider is in range |
| ZX_DEBUG_ASSERT(div < (1 << kMclkDivBits)); |
| |
| zx_off_t mclk_a = {}; |
| switch (version_) { |
| case metadata::AmlVersion::kS905D2G: |
| mclk_a = EE_AUDIO_MCLK_A_CTRL; |
| break; |
| case metadata::AmlVersion::kS905D3G: |
| mclk_a = EE_AUDIO_MCLK_A_CTRL_D3G; |
| break; |
| } |
| zx_off_t ptr = mclk_a + (mclk_ch_ * sizeof(uint32_t)); |
| // disable and clear out old divider value |
| GetMmio().ClearBits32((1 << 31) | ((1 << kMclkDivBits) - 1), ptr); |
| |
| GetMmio().SetBits32((1 << 31) | (clk_src_ << 24) | (div & ((1 << kMclkDivBits) - 1)), ptr); |
| return ZX_OK; |
| } |
| |
| /* Notes: |
| -sdiv is desired divider -1 (Want a divider of 10? write a value of 9) |
| */ |
| zx_status_t AmlTdmDevice::SetSclkDiv(uint32_t sdiv, uint32_t lrduty, uint32_t lrdiv, |
| bool sclk_invert_ph0) { |
| if (sdiv == 0) { |
| // sclk needs to be at least 2x mclk. writing a value of 0 (/1) to sdiv |
| // will result in no sclk being generated on the sclk pin. However, it |
| // appears that it is running properly as a lrclk is still generated at |
| // an expected rate (lrclk is derived from sclk) |
| return ZX_ERR_INVALID_ARGS; |
| } |
| ZX_DEBUG_ASSERT(sdiv < (1 << kSclkDivBits)); |
| ZX_DEBUG_ASSERT(lrdiv < (1 << kLRclkDivBits)); |
| // lrduty is in sclk cycles, so must be less than lrdiv |
| ZX_DEBUG_ASSERT(lrduty < lrdiv); |
| |
| zx_off_t ptr = EE_AUDIO_MST_A_SCLK_CTRL0 + (2 * mclk_ch_ * sizeof(uint32_t)); |
| GetMmio().Write32((0x3 << 30) | // Enable the channel |
| (sdiv << 20) | // sclk divider sclk=mclk/sdiv |
| (lrduty << 10) | // lrclk duty cycle in sclk cycles |
| (lrdiv << 0), // lrclk = sclk/lrdiv |
| ptr); |
| GetMmio().Write32(0, ptr + sizeof(uint32_t)); // Clear delay lines for phases |
| // Invert sclk. |
| GetMmio().Write32(sclk_invert_ph0 << 0, |
| EE_AUDIO_MST_A_SCLK_CTRL1 + (2 * mclk_ch_ * sizeof(uint32_t))); |
| return ZX_OK; |
| } |
| |
| zx_status_t AmlTdmDevice::SetMClkPad(aml_tdm_mclk_pad_t mclk_pad) { |
| switch (mclk_pad) { |
| case MCLK_PAD_0: |
| switch (version_) { |
| case metadata::AmlVersion::kS905D2G: |
| GetMmio().Write32(mclk_ch_, EE_AUDIO_MST_PAD_CTRL0); |
| break; |
| case metadata::AmlVersion::kS905D3G: |
| GetMmio().Write32((mclk_ch_ << 8) | (1 << 15), |
| EE_AUDIO_MST_PAD_CTRL0); // Bit 15 to enable. |
| break; |
| } |
| break; |
| case MCLK_PAD_1: |
| switch (version_) { |
| case metadata::AmlVersion::kS905D2G: |
| GetMmio().Write32(mclk_ch_ << 4, EE_AUDIO_MST_PAD_CTRL0); |
| break; |
| case metadata::AmlVersion::kS905D3G: |
| GetMmio().Write32((mclk_ch_ << 24) | (1 << 31), |
| EE_AUDIO_MST_PAD_CTRL0); // Bit 31 to enable. |
| break; |
| } |
| break; |
| default: |
| return ZX_ERR_INVALID_ARGS; |
| } |
| return ZX_OK; |
| } |
| |
| void AmlTdmDevice::AudioClkEna(uint32_t audio_blk_mask) { |
| GetMmio().SetBits32(audio_blk_mask, EE_AUDIO_CLK_GATE_EN); |
| } |
| |
| void AmlTdmDevice::AudioClkDis(uint32_t audio_blk_mask) { |
| GetMmio().ClearBits32(audio_blk_mask, EE_AUDIO_CLK_GATE_EN); |
| } |