|
@@ -0,0 +1,621 @@
|
|
|
|
+/*
|
|
|
|
+ * Copyright (c) 2016, Freescale Semiconductor, Inc.
|
|
|
|
+ * Copyright 2016-2017 NXP
|
|
|
|
+ *
|
|
|
|
+ * Redistribution and use in source and binary forms, with or without modification,
|
|
|
|
+ * are permitted provided that the following conditions are met:
|
|
|
|
+ *
|
|
|
|
+ * o Redistributions of source code must retain the above copyright notice, this list
|
|
|
|
+ * of conditions and the following disclaimer.
|
|
|
|
+ *
|
|
|
|
+ * o Redistributions in binary form must reproduce the above copyright notice, this
|
|
|
|
+ * list of conditions and the following disclaimer in the documentation and/or
|
|
|
|
+ * other materials provided with the distribution.
|
|
|
|
+ *
|
|
|
|
+ * o Neither the name of the copyright holder nor the names of its
|
|
|
|
+ * contributors may be used to endorse or promote products derived from this
|
|
|
|
+ * software without specific prior written permission.
|
|
|
|
+ *
|
|
|
|
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
|
|
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
|
|
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
|
|
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
|
|
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
+ */
|
|
|
|
+#include "fsl_wm8960.h"
|
|
|
|
+#include "fsl_common.h"
|
|
|
|
+
|
|
|
|
+/*******************************************************************************
|
|
|
|
+ * Definitations
|
|
|
|
+ ******************************************************************************/
|
|
|
|
+
|
|
|
|
+/*******************************************************************************
|
|
|
|
+ * Prototypes
|
|
|
|
+ ******************************************************************************/
|
|
|
|
+
|
|
|
|
+/*******************************************************************************
|
|
|
|
+ * Variables
|
|
|
|
+ ******************************************************************************/
|
|
|
|
+/*
|
|
|
|
+ * wm8960 register cache
|
|
|
|
+ * We can't read the WM8960 register space when we are
|
|
|
|
+ * using 2 wire for device control, so we cache them instead.
|
|
|
|
+ */
|
|
|
|
+static const uint16_t wm8960_reg[WM8960_CACHEREGNUM] = {
|
|
|
|
+ 0x0097, 0x0097, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x000a, 0x01c0, 0x0000, 0x00ff, 0x00ff, 0x0000, 0x0000,
|
|
|
|
+ 0x0000, 0x0000, 0x0000, 0x007b, 0x0100, 0x0032, 0x0000, 0x00c3, 0x00c3, 0x01c0, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
|
|
+ 0x0000, 0x0000, 0x0000, 0x0000, 0x0100, 0x0100, 0x0050, 0x0050, 0x0050, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000,
|
|
|
|
+ 0x0040, 0x0000, 0x0000, 0x0050, 0x0050, 0x0000, 0x0002, 0x0037, 0x004d, 0x0080, 0x0008, 0x0031, 0x0026, 0x00e9,
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static uint16_t reg_cache[WM8960_CACHEREGNUM];
|
|
|
|
+/*******************************************************************************
|
|
|
|
+ * Code
|
|
|
|
+ ******************************************************************************/
|
|
|
|
+
|
|
|
|
+void WM8960_Init(wm8960_handle_t *handle, wm8960_config_t *config)
|
|
|
|
+{
|
|
|
|
+ uint32_t i = 4000000;
|
|
|
|
+
|
|
|
|
+ memcpy(reg_cache, wm8960_reg, sizeof(wm8960_reg));
|
|
|
|
+
|
|
|
|
+ /* Set WM8960 I2C address */
|
|
|
|
+ handle->xfer.slaveAddress = WM8960_I2C_ADDR;
|
|
|
|
+
|
|
|
|
+ /* NULL pointer means default setting. */
|
|
|
|
+ if (config == NULL)
|
|
|
|
+ {
|
|
|
|
+ /*
|
|
|
|
+ * Reset all registers
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_RESET, 0x00);
|
|
|
|
+
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_IFACE2, 0x40);
|
|
|
|
+ /*
|
|
|
|
+ * VMID=50K, Enable VREF, AINL, AINR, ADCL and ADCR
|
|
|
|
+ * I2S_IN (bit 0), I2S_OUT (bit 1), DAP (bit 4), DAC (bit 5), ADC (bit 6) are powered on
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_POWER1, 0xCA);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Enable DACL, DACR, LOUT1, ROUT1, PLL down
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_POWER2, 0x1E0);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Enable left and right channel input PGA, left and right output mixer
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_POWER3, 0xC);
|
|
|
|
+
|
|
|
|
+ /* Configure SYS_FS clock to 44.1kHz, MCLK_FREQ to 256*Fs, SYSCLK derived from MCLK input */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_CLOCK1, 0x00);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Audio data length = 32bit, Left justified data format
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_IFACE1, 0x0D);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * LMICBOOST = 0dB, Connect left and right PGA to left and right Input Boost Mixer
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LINPATH, 0x18);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_RINPATH, 0x18);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Left and right input boost, LIN3BOOST and RIN3BOOST = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_INBMIX1, 0x70);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_INBMIX2, 0x70);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Left DAC and LINPUT3 to left output mixer, LINPUT3 left output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x100);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Right DAC and RINPUT3 to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x100);
|
|
|
|
+
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_BYPASS1, 0x0);
|
|
|
|
+
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_BYPASS2, 0x0);
|
|
|
|
+
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_MONOMIX1, 0x00);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_MONOMIX2, 0x00);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ WM8960_SetDataRoute(handle, config->route);
|
|
|
|
+ WM8960_SetProtocol(handle, config->bus);
|
|
|
|
+ WM8960_SetMasterSlave(handle, config->master_slave);
|
|
|
|
+ }
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ADDCTL1, 0x0C4);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ADDCTL4, 0x40);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * ADC volume, 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LADC, 0x1F3);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_RADC, 0x1F3);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Digital DAC volume, 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LDAC, 0x1E0);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_RDAC, 0x1E0);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Headphone volume, LOUT1 and ROUT1, 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LOUT1, 0x16F);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ROUT1, 0x16F);
|
|
|
|
+
|
|
|
|
+ /* Delay for some while */
|
|
|
|
+ while (i)
|
|
|
|
+ {
|
|
|
|
+ __ASM("nop");
|
|
|
|
+ i--;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Unmute DAC. */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_DACCTL1, 0x0000);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void WM8960_Deinit(wm8960_handle_t *handle)
|
|
|
|
+{
|
|
|
|
+ WM8960_SetModule(handle, kWM8960_ModuleADC, false);
|
|
|
|
+ WM8960_SetModule(handle, kWM8960_ModuleDAC, false);
|
|
|
|
+ WM8960_SetModule(handle, kWM8960_ModuleVREF, false);
|
|
|
|
+ WM8960_SetModule(handle, kWM8960_ModuleLineIn, false);
|
|
|
|
+ WM8960_SetModule(handle, kWM8960_ModuleLineOut, false);
|
|
|
|
+ WM8960_SetModule(handle, kWM8960_ModuleSpeaker, false);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void WM8960_SetMasterSlave(wm8960_handle_t *handle, bool master)
|
|
|
|
+{
|
|
|
|
+ if (master == 1)
|
|
|
|
+ {
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_MASTER));
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_SLAVE));
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_SetModule(wm8960_handle_t *handle, wm8960_module_t module, bool isEnabled)
|
|
|
|
+{
|
|
|
|
+ status_t ret = kStatus_Success;
|
|
|
|
+ switch (module)
|
|
|
|
+ {
|
|
|
|
+ case kWM8960_ModuleADC:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_ADCL_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER1_ADCL_SHIFT));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_ADCR_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER1_ADCR_SHIFT));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleDAC:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_DACL_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER2_DACL_SHIFT));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_DACR_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER2_DACR_SHIFT));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleVREF:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_VREF_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER1_VREF_SHIFT));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleLineIn:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_AINL_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER1_AINL_SHIFT));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER1, WM8960_POWER1_AINR_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER1_AINR_SHIFT));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleLineOut:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_LOUT1_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER2_LOUT1_SHIFT));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_ROUT1_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER2_ROUT1_SHIFT));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleSpeaker:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_SPKL_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER2_SPKL_SHIFT));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_POWER2, WM8960_POWER2_SPKR_MASK,
|
|
|
|
+ ((uint16_t)isEnabled << WM8960_POWER2_SPKR_SHIFT));
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_CLASSD1, 0xF7);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ ret = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_SetDataRoute(wm8960_handle_t *handle, wm8960_route_t route)
|
|
|
|
+{
|
|
|
|
+ status_t ret = kStatus_Success;
|
|
|
|
+ switch (route)
|
|
|
|
+ {
|
|
|
|
+ case kWM8960_RouteBypass:
|
|
|
|
+ /* Bypass means from line-in to HP*/
|
|
|
|
+ /*
|
|
|
|
+ * Left LINPUT3 to left output mixer, LINPUT3 left output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x80);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Right RINPUT3 to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x80);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_RoutePlayback:
|
|
|
|
+ /* Data route I2S_IN-> DAC-> HP */
|
|
|
|
+ /*
|
|
|
|
+ * Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x100);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x100);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_RoutePlaybackandRecord:
|
|
|
|
+ /* I2S IN->DAC->HP LINE_IN->ADC->I2S_OUT */
|
|
|
|
+ /*
|
|
|
|
+ * Left and right input boost, LIN3BOOST and RIN3BOOST = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_INBMIX1, 0x50);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_INBMIX2, 0x50);
|
|
|
|
+ /*
|
|
|
|
+ * Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_LOUTMIX, 0x100);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_ROUTMIX, 0x100);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_RoutePlaybackwithDAP:
|
|
|
|
+ /* I2S_IN->DAP->DAC->HP */
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_RoutePlaybackwithDAPandRecord:
|
|
|
|
+ /* I2S_IN->DAP->DAC->HP, LINE_IN->ADC->I2S_OUT */
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_RouteRecord:
|
|
|
|
+ /* LINE_IN->ADC->I2S_OUT */
|
|
|
|
+ /*
|
|
|
|
+ * Left and right input boost, LIN3BOOST and RIN3BOOST = 0dB
|
|
|
|
+ */
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_INBMIX1, 0x50);
|
|
|
|
+ WM8960_WriteReg(handle, WM8960_INBMIX2, 0x50);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ ret = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_SetProtocol(wm8960_handle_t *handle, wm8960_protocol_t protocol)
|
|
|
|
+{
|
|
|
|
+ status_t ret = kStatus_Success;
|
|
|
|
+ switch (protocol)
|
|
|
|
+ {
|
|
|
|
+ case kWM8960_BusI2S:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK,
|
|
|
|
+ WM8960_IFACE1_FORMAT(WM8960_IFACE1_FORMAT_I2S));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_BusLeftJustified:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK,
|
|
|
|
+ WM8960_IFACE1_FORMAT(WM8960_IFACE1_FORMAT_LJ));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_BusRightJustified:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK,
|
|
|
|
+ WM8960_IFACE1_FORMAT(WM8960_IFACE1_FORMAT_RJ));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_BusPCMA:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK,
|
|
|
|
+ WM8960_IFACE1_FORMAT(WM8960_IFACE1_FORMAT_DSP));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_LRP_MASK, WM8960_IFACE1_LRP(WM8960_IFACE1_DSP_MODEA));
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_BusPCMB:
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK,
|
|
|
|
+ WM8960_IFACE1_FORMAT(WM8960_IFACE1_FORMAT_DSP));
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_LRP_MASK, WM8960_IFACE1_LRP(WM8960_IFACE1_DSP_MODEB));
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ ret = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL(WM8960_IFACE1_WL_32BITS));
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_SetVolume(wm8960_handle_t *handle, wm8960_module_t module, uint32_t volume)
|
|
|
|
+{
|
|
|
|
+ uint16_t vol = 0;
|
|
|
|
+ status_t ret = kStatus_Success;
|
|
|
|
+ switch (module)
|
|
|
|
+ {
|
|
|
|
+ case kWM8960_ModuleADC:
|
|
|
|
+ vol = 0x100 | volume;
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LADC, vol);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RADC, vol);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleDAC:
|
|
|
|
+ vol = 0x100 | volume;
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LDAC, vol);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RDAC, vol);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleHP:
|
|
|
|
+ vol = 0x100 | volume;
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LOUT1, vol);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_ROUT1, vol);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleLineIn:
|
|
|
|
+ vol = 0x100 | volume;
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LINVOL, vol);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RINVOL, vol);
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleSpeaker:
|
|
|
|
+ vol = 0x100 | volume;
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LOUT2, vol);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_ROUT2, vol);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ ret = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+uint32_t WM8960_GetVolume(wm8960_handle_t *handle, wm8960_module_t module)
|
|
|
|
+{
|
|
|
|
+ uint16_t vol = 0;
|
|
|
|
+ switch (module)
|
|
|
|
+ {
|
|
|
|
+ case kWM8960_ModuleADC:
|
|
|
|
+ WM8960_ReadReg(WM8960_LADC, &vol);
|
|
|
|
+ vol &= 0xFF;
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleDAC:
|
|
|
|
+ WM8960_ReadReg(WM8960_LDAC, &vol);
|
|
|
|
+ vol &= 0xFF;
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleHP:
|
|
|
|
+ WM8960_ReadReg(WM8960_LOUT1, &vol);
|
|
|
|
+ vol &= 0x7F;
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleLineOut:
|
|
|
|
+ WM8960_ReadReg(WM8960_LINVOL, &vol);
|
|
|
|
+ vol &= 0x3F;
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ vol = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return vol;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_SetMute(wm8960_handle_t *handle, wm8960_module_t module, bool isEnabled)
|
|
|
|
+{
|
|
|
|
+ status_t ret = kStatus_Success;
|
|
|
|
+ switch (module)
|
|
|
|
+ {
|
|
|
|
+ case kWM8960_ModuleADC:
|
|
|
|
+ /*
|
|
|
|
+ * Digital Mute
|
|
|
|
+ */
|
|
|
|
+ if (isEnabled)
|
|
|
|
+ {
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LADC, 0x100);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RADC, 0x100);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LADC, 0x1C3);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RADC, 0x1C3);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleDAC:
|
|
|
|
+ /*
|
|
|
|
+ * Digital mute
|
|
|
|
+ */
|
|
|
|
+ if (isEnabled)
|
|
|
|
+ {
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LDAC, 0x100);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RDAC, 0x100);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LDAC, 0x1FF);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_RDAC, 0x1FF);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleHP:
|
|
|
|
+ /*
|
|
|
|
+ * Analog mute
|
|
|
|
+ */
|
|
|
|
+ if (isEnabled)
|
|
|
|
+ {
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LOUT1, 0x100);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_ROUT1, 0x100);
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_LOUT1, 0x179);
|
|
|
|
+ ret = WM8960_WriteReg(handle, WM8960_ROUT1, 0x179);
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ case kWM8960_ModuleLineOut:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ ret = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_ConfigDataFormat(wm8960_handle_t *handle, uint32_t mclk, uint32_t sample_rate, uint8_t bits)
|
|
|
|
+{
|
|
|
|
+ status_t retval = kStatus_Success;
|
|
|
|
+
|
|
|
|
+ switch (sample_rate)
|
|
|
|
+ {
|
|
|
|
+ case 8000:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0x1B0);
|
|
|
|
+ break;
|
|
|
|
+ case 11025:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0xD8);
|
|
|
|
+ break;
|
|
|
|
+ case 12000:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0x120);
|
|
|
|
+ break;
|
|
|
|
+ case 16000:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0xD8);
|
|
|
|
+ break;
|
|
|
|
+ case 22050:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0xD8);
|
|
|
|
+ break;
|
|
|
|
+ case 24000:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0x90);
|
|
|
|
+ break;
|
|
|
|
+ case 32000:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0x48);
|
|
|
|
+ break;
|
|
|
|
+ case 44100:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0xD8);
|
|
|
|
+ break;
|
|
|
|
+ case 48000:
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_CLOCK1, 0x00);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ retval = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Slave mode (MS = 0), LRP = 0, 32bit WL, left justified (FORMAT[1:0]=0b01)
|
|
|
|
+ */
|
|
|
|
+ switch (bits)
|
|
|
|
+ {
|
|
|
|
+ case 16:
|
|
|
|
+ retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
|
|
+ WM8960_IFACE1_WL(WM8960_IFACE1_WL_16BITS));
|
|
|
|
+ break;
|
|
|
|
+ case 20:
|
|
|
|
+ retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
|
|
+ WM8960_IFACE1_WL(WM8960_IFACE1_WL_20BITS));
|
|
|
|
+ break;
|
|
|
|
+ case 24:
|
|
|
|
+ retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
|
|
+ WM8960_IFACE1_WL(WM8960_IFACE1_WL_24BITS));
|
|
|
|
+ break;
|
|
|
|
+ case 32:
|
|
|
|
+ retval = WM8960_ModifyReg(handle, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
|
|
+ WM8960_IFACE1_WL(WM8960_IFACE1_WL_32BITS));
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ retval = kStatus_InvalidArgument;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_SetJackDetect(wm8960_handle_t *handle, bool isEnabled)
|
|
|
|
+{
|
|
|
|
+ uint8_t retval = 0;
|
|
|
|
+ uint16_t val = 0;
|
|
|
|
+
|
|
|
|
+ WM8960_ReadReg(WM8960_ADDCTL2, &val);
|
|
|
|
+
|
|
|
|
+ if (isEnabled)
|
|
|
|
+ {
|
|
|
|
+ val |= 0x40U;
|
|
|
|
+ }
|
|
|
|
+ else
|
|
|
|
+ {
|
|
|
|
+ val &= 0xCF;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ retval = WM8960_WriteReg(handle, WM8960_ADDCTL2, val);
|
|
|
|
+
|
|
|
|
+ return retval;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_WriteReg(wm8960_handle_t *handle, uint8_t reg, uint16_t val)
|
|
|
|
+{
|
|
|
|
+ uint8_t cmd, buff;
|
|
|
|
+ uint8_t retval = 0;
|
|
|
|
+
|
|
|
|
+ /* The register address */
|
|
|
|
+ cmd = (reg << 1) | ((val >> 8U) & 0x0001U);
|
|
|
|
+ /* Data */
|
|
|
|
+ buff = val & 0xFF;
|
|
|
|
+
|
|
|
|
+ /* Copy data to cache */
|
|
|
|
+ reg_cache[reg] = val;
|
|
|
|
+
|
|
|
|
+#if defined(FSL_FEATURE_SOC_LPI2C_COUNT) && (FSL_FEATURE_SOC_LPI2C_COUNT)
|
|
|
|
+ uint8_t data[2];
|
|
|
|
+ data[0] = cmd;
|
|
|
|
+ data[1] = buff;
|
|
|
|
+ retval = LPI2C_MasterStart(handle->base, WM8960_I2C_ADDR, kLPI2C_Write);
|
|
|
|
+ retval = LPI2C_MasterSend(handle->base, data, 2);
|
|
|
|
+ retval = LPI2C_MasterStop(handle->base);
|
|
|
|
+#else
|
|
|
|
+ /* Config the I2C xfer */
|
|
|
|
+ handle->xfer.direction = kI2C_Write;
|
|
|
|
+ handle->xfer.subaddress = cmd;
|
|
|
|
+ handle->xfer.subaddressSize = 1U;
|
|
|
|
+ handle->xfer.data = &buff;
|
|
|
|
+ handle->xfer.dataSize = 1U;
|
|
|
|
+
|
|
|
|
+ retval = I2C_MasterTransferBlocking(handle->base, &handle->xfer);
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+ if (retval != kStatus_Success)
|
|
|
|
+ {
|
|
|
|
+ return kStatus_Fail;
|
|
|
|
+ }
|
|
|
|
+ return kStatus_Success;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_ReadReg(uint8_t reg, uint16_t *val)
|
|
|
|
+{
|
|
|
|
+ if (reg >= WM8960_CACHEREGNUM)
|
|
|
|
+ {
|
|
|
|
+ return kStatus_InvalidArgument;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *val = reg_cache[reg];
|
|
|
|
+
|
|
|
|
+ return kStatus_Success;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+status_t WM8960_ModifyReg(wm8960_handle_t *handle, uint8_t reg, uint16_t mask, uint16_t val)
|
|
|
|
+{
|
|
|
|
+ uint8_t retval = 0;
|
|
|
|
+ uint16_t reg_val = 0;
|
|
|
|
+ retval = WM8960_ReadReg(reg, ®_val);
|
|
|
|
+ if (retval != kStatus_Success)
|
|
|
|
+ {
|
|
|
|
+ return kStatus_Fail;
|
|
|
|
+ }
|
|
|
|
+ reg_val &= (uint16_t)~mask;
|
|
|
|
+ reg_val |= val;
|
|
|
|
+ retval = WM8960_WriteReg(handle, reg, reg_val);
|
|
|
|
+ if (retval != kStatus_Success)
|
|
|
|
+ {
|
|
|
|
+ return kStatus_Fail;
|
|
|
|
+ }
|
|
|
|
+ return kStatus_Success;
|
|
|
|
+}
|