626 lines
21 KiB
C
626 lines
21 KiB
C
/*
|
|
* Copyright (c) 2006-2023, RT-Thread Development Team
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Change Logs:
|
|
* Date Author Notes
|
|
* 2018-12-05 zylx The first version for STM32F4xx
|
|
* 2019-4-25 misonyo port to IMXRT
|
|
*/
|
|
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
|
|
#include "bsp_wm8960.h"
|
|
#include <stdlib.h>
|
|
#include <drv_log.h>
|
|
#include <rthw.h>
|
|
static rt_uint16_t wm8960_regval_tbl[56] = {
|
|
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 rt_uint16_t reg_cache[WM8960_CACHEREGNUM];
|
|
static void wm8960_write_reg(struct rt_i2c_bus_device *dev, rt_uint8_t reg, rt_uint16_t val)
|
|
{
|
|
struct rt_i2c_msg msg;
|
|
rt_uint8_t send_buffer[2];
|
|
RT_ASSERT(dev != RT_NULL);
|
|
/* store temp */
|
|
rt_uint16_t buff = val;
|
|
reg_cache[reg] = buff;
|
|
send_buffer[0] = (reg << 1) | ((val >> 8U) & 0x0001U);
|
|
send_buffer[1] = (rt_uint8_t)(val & 0xFF);
|
|
msg.addr = 0x1A;////WM8960_I2C_ADDR 0x1A
|
|
msg.flags = RT_I2C_WR;
|
|
msg.len = 2;
|
|
msg.buf = send_buffer;
|
|
rt_i2c_transfer(dev, &msg, 1);
|
|
}
|
|
static void wm8960_read_reg(struct rt_i2c_bus_device *dev, rt_uint8_t reg, rt_uint16_t *val)
|
|
{
|
|
*val = reg_cache[reg];
|
|
}
|
|
|
|
static void wm8960_modify_reg(struct rt_i2c_bus_device *dev, rt_uint8_t reg, rt_uint16_t mask, rt_uint16_t val)
|
|
{
|
|
uint16_t reg_val = 0;
|
|
wm8960_read_reg(dev, reg, ®_val);
|
|
reg_val &= (rt_uint16_t)~mask;
|
|
reg_val |= val;
|
|
|
|
wm8960_write_reg(dev, reg, reg_val);
|
|
}
|
|
void WM8960_SetMasterSlave(struct rt_i2c_bus_device *dev, rt_bool_t master)
|
|
{
|
|
if (master == 1)
|
|
{
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_MASTER));
|
|
}
|
|
else
|
|
{
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_SLAVE));
|
|
}
|
|
}
|
|
|
|
void WM8960_SetModule(struct rt_i2c_bus_device *dev, wm8960_module_t module, rt_bool_t isEnabled)
|
|
{
|
|
switch (module)
|
|
{
|
|
case kWM8960_ModuleADC:
|
|
wm8960_modify_reg(dev, WM8960_POWER1, WM8960_POWER1_ADCL_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER1_ADCL_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER1, WM8960_POWER1_ADCR_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER1_ADCR_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleDAC:
|
|
wm8960_modify_reg(dev, WM8960_POWER2, WM8960_POWER2_DACL_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER2_DACL_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER2, WM8960_POWER2_DACR_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER2_DACR_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleVREF:
|
|
wm8960_modify_reg(dev, WM8960_POWER1, WM8960_POWER1_VREF_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER1_VREF_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleLineIn:
|
|
wm8960_modify_reg(dev, WM8960_POWER1, WM8960_POWER1_AINL_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER1_AINL_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER1, WM8960_POWER1_AINR_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER1_AINR_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleLineOut:
|
|
wm8960_modify_reg(dev, WM8960_POWER2, WM8960_POWER2_LOUT1_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER2_LOUT1_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER2, WM8960_POWER2_ROUT1_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER2_ROUT1_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleMICB:
|
|
wm8960_modify_reg(dev, WM8960_POWER1, WM8960_POWER1_MICB_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER1_MICB_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleSpeaker:
|
|
wm8960_modify_reg(dev, WM8960_POWER2, WM8960_POWER2_SPKL_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER2_SPKL_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER2, WM8960_POWER2_SPKR_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER2_SPKR_SHIFT));
|
|
wm8960_write_reg(dev, WM8960_CLASSD1, 0xF7);
|
|
break;
|
|
case kWM8960_ModuleMIC:
|
|
wm8960_modify_reg(dev, WM8960_POWER3, WM8960_POWER3_LMIC_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER3_LMIC_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER3, WM8960_POWER3_RMIC_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER3_RMIC_SHIFT));
|
|
break;
|
|
case kWM8960_ModuleOMIX:
|
|
wm8960_modify_reg(dev, WM8960_POWER3, WM8960_POWER3_LOMIX_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER3_LOMIX_SHIFT));
|
|
wm8960_modify_reg(dev, WM8960_POWER3, WM8960_POWER3_ROMIX_MASK,
|
|
((uint16_t)isEnabled << WM8960_POWER3_ROMIX_SHIFT));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WM8960_SetDataRoute(struct rt_i2c_bus_device *dev, wm8960_route_t route)
|
|
{
|
|
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_write_reg(dev, WM8960_LOUTMIX, 0x80);
|
|
|
|
/*
|
|
* Right RINPUT3 to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
*/
|
|
wm8960_write_reg(dev, 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_write_reg(dev, WM8960_LOUTMIX, 0x100);
|
|
|
|
/*
|
|
* Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_ROUTMIX, 0x100);
|
|
wm8960_write_reg(dev, WM8960_POWER3, 0x0C);
|
|
/* Set power for DAC */
|
|
WM8960_SetModule(dev, kWM8960_ModuleDAC, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleOMIX, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleLineOut, RT_TRUE);
|
|
break;
|
|
case kWM8960_RoutePlaybackandRecord:
|
|
/*
|
|
* Left DAC to left output mixer, LINPUT3 left output mixer volume = 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_LOUTMIX, 0x100);
|
|
|
|
/*
|
|
* Right DAC to right output mixer, RINPUT3 right output mixer volume = 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_ROUTMIX, 0x100);
|
|
wm8960_write_reg(dev, WM8960_POWER3, 0x3C);
|
|
WM8960_SetModule(dev, kWM8960_ModuleDAC, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleADC, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleLineIn, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleOMIX, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleLineOut, RT_TRUE);
|
|
break;
|
|
case kWM8960_RouteRecord:
|
|
/* LINE_IN->ADC->I2S_OUT */
|
|
/*
|
|
* Left and right input boost, LIN3BOOST and RIN3BOOST = 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_POWER3, 0x30);
|
|
/* Power up ADC and AIN */
|
|
WM8960_SetModule(dev, kWM8960_ModuleLineIn, RT_TRUE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleADC, RT_TRUE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WM8960_SetLeftInput(struct rt_i2c_bus_device *dev, wm8960_input_t input)
|
|
{
|
|
uint16_t val = 0;
|
|
|
|
switch (input)
|
|
{
|
|
case kWM8960_InputSingleEndedMic:
|
|
/* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_write_reg(dev, WM8960_LINPATH, 0x138);
|
|
wm8960_write_reg(dev, WM8960_LINVOL, 0x117);
|
|
break;
|
|
case kWM8960_InputDifferentialMicInput2:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_write_reg(dev, WM8960_LINPATH, 0x178);
|
|
wm8960_write_reg(dev, WM8960_LINVOL, 0x117);
|
|
break;
|
|
case kWM8960_InputDifferentialMicInput3:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK | WM8960_POWER1_MICB_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_write_reg(dev, WM8960_LINPATH, 0x1B8);
|
|
wm8960_write_reg(dev, WM8960_LINVOL, 0x117);
|
|
break;
|
|
case kWM8960_InputLineINPUT2:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_read_reg(dev,WM8960_INBMIX1, &val);
|
|
val |= 0xE;
|
|
wm8960_write_reg(dev, WM8960_INBMIX1, val);
|
|
break;
|
|
case kWM8960_InputLineINPUT3:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINL_MASK | WM8960_POWER1_ADCL_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_read_reg(dev,WM8960_INBMIX1, &val);
|
|
val |= 0x70;
|
|
wm8960_write_reg(dev, WM8960_INBMIX1, val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WM8960_SetRightInput(struct rt_i2c_bus_device *dev, wm8960_input_t input)
|
|
{
|
|
uint16_t val = 0;
|
|
|
|
switch (input)
|
|
{
|
|
case kWM8960_InputSingleEndedMic:
|
|
/* Only LMN1 enabled, LMICBOOST to 13db, LMIC2B enabled */
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_write_reg(dev, WM8960_RINPATH, 0x138);
|
|
wm8960_write_reg(dev, WM8960_RINVOL, 0x117);
|
|
break;
|
|
case kWM8960_InputDifferentialMicInput2:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_write_reg(dev, WM8960_RINPATH, 0x178);
|
|
wm8960_write_reg(dev, WM8960_RINVOL, 0x117);
|
|
break;
|
|
case kWM8960_InputDifferentialMicInput3:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK | WM8960_POWER1_MICB_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_write_reg(dev, WM8960_RINPATH, 0x1B8);
|
|
wm8960_write_reg(dev, WM8960_RINVOL, 0x117);
|
|
break;
|
|
case kWM8960_InputLineINPUT2:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_read_reg(dev,WM8960_INBMIX2, &val);
|
|
val |= 0xE;
|
|
wm8960_write_reg(dev, WM8960_INBMIX2, val);
|
|
break;
|
|
case kWM8960_InputLineINPUT3:
|
|
wm8960_read_reg(dev,WM8960_POWER1, &val);
|
|
val |= (WM8960_POWER1_AINR_MASK | WM8960_POWER1_ADCR_MASK);
|
|
wm8960_write_reg(dev, WM8960_POWER1, val);
|
|
wm8960_read_reg(dev,WM8960_INBMIX2, &val);
|
|
val |= 0x70;
|
|
wm8960_write_reg(dev, WM8960_INBMIX2, val);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WM8960_SetProtocol(struct rt_i2c_bus_device *dev, wm8960_protocol_t protocol)
|
|
{
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_FORMAT_MASK | WM8960_IFACE1_LRP_MASK, protocol);
|
|
}
|
|
|
|
void WM8960_SetVolume(struct rt_i2c_bus_device *dev, wm8960_module_t module, rt_uint32_t volume)
|
|
{
|
|
uint16_t vol = 0;
|
|
switch (module)
|
|
{
|
|
case kWM8960_ModuleADC:
|
|
vol = volume;
|
|
wm8960_write_reg(dev, WM8960_LADC, vol);
|
|
wm8960_write_reg(dev, WM8960_RADC, vol);
|
|
/* Update volume */
|
|
vol = 0x100 | volume;
|
|
wm8960_write_reg(dev, WM8960_LADC, vol);
|
|
wm8960_write_reg(dev, WM8960_RADC, vol);
|
|
break;
|
|
case kWM8960_ModuleDAC:
|
|
vol = volume;
|
|
wm8960_write_reg(dev, WM8960_LDAC, vol);
|
|
wm8960_write_reg(dev, WM8960_RDAC, vol);
|
|
vol = 0x100 | volume;
|
|
wm8960_write_reg(dev, WM8960_LDAC, vol);
|
|
wm8960_write_reg(dev, WM8960_RDAC, vol);
|
|
break;
|
|
case kWM8960_ModuleHP:
|
|
vol = volume;
|
|
wm8960_write_reg(dev, WM8960_LOUT1, vol);
|
|
wm8960_write_reg(dev, WM8960_ROUT1, vol);
|
|
vol = 0x100 | volume;
|
|
wm8960_write_reg(dev, WM8960_LOUT1, vol);
|
|
wm8960_write_reg(dev, WM8960_ROUT1, vol);
|
|
break;
|
|
case kWM8960_ModuleLineIn:
|
|
vol = volume;
|
|
wm8960_write_reg(dev, WM8960_LINVOL, vol);
|
|
wm8960_write_reg(dev, WM8960_RINVOL, vol);
|
|
vol = 0x100 | volume;
|
|
wm8960_write_reg(dev, WM8960_LINVOL, vol);
|
|
wm8960_write_reg(dev, WM8960_RINVOL, vol);
|
|
break;
|
|
case kWM8960_ModuleSpeaker:
|
|
vol = volume;
|
|
wm8960_write_reg(dev, WM8960_LOUT2, vol);
|
|
wm8960_write_reg(dev, WM8960_ROUT2, vol);
|
|
vol = 0x100 | volume;
|
|
wm8960_write_reg(dev, WM8960_LOUT2, vol);
|
|
wm8960_write_reg(dev, WM8960_ROUT2, vol);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
rt_uint32_t WM8960_GetVolume(struct rt_i2c_bus_device *dev, wm8960_module_t module)
|
|
{
|
|
uint16_t vol = 0;
|
|
switch (module)
|
|
{
|
|
case kWM8960_ModuleADC:
|
|
wm8960_read_reg(dev,WM8960_LADC, &vol);
|
|
vol &= 0xFF;
|
|
break;
|
|
case kWM8960_ModuleDAC:
|
|
wm8960_read_reg(dev,WM8960_LDAC, &vol);
|
|
vol &= 0xFF;
|
|
break;
|
|
case kWM8960_ModuleHP:
|
|
wm8960_read_reg(dev,WM8960_LOUT1, &vol);
|
|
vol &= 0x7F;
|
|
break;
|
|
case kWM8960_ModuleLineOut:
|
|
wm8960_read_reg(dev,WM8960_LINVOL, &vol);
|
|
vol &= 0x3F;
|
|
break;
|
|
default:
|
|
vol = 0;
|
|
break;
|
|
}
|
|
return vol;
|
|
}
|
|
|
|
void WM8960_SetMute(struct rt_i2c_bus_device *dev, wm8960_module_t module, rt_bool_t isEnabled)
|
|
{
|
|
switch (module)
|
|
{
|
|
case kWM8960_ModuleADC:
|
|
/*
|
|
* Digital Mute
|
|
*/
|
|
if (isEnabled)
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LADC, 0x100);
|
|
wm8960_write_reg(dev, WM8960_RADC, 0x100);
|
|
}
|
|
else
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LADC, 0x1C3);
|
|
wm8960_write_reg(dev, WM8960_RADC, 0x1C3);
|
|
}
|
|
break;
|
|
case kWM8960_ModuleDAC:
|
|
/*
|
|
* Digital mute
|
|
*/
|
|
if (isEnabled)
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LDAC, 0x100);
|
|
wm8960_write_reg(dev, WM8960_RDAC, 0x100);
|
|
}
|
|
else
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LDAC, 0x1FF);
|
|
wm8960_write_reg(dev, WM8960_RDAC, 0x1FF);
|
|
}
|
|
break;
|
|
case kWM8960_ModuleHP:
|
|
/*
|
|
* Analog mute
|
|
*/
|
|
if (isEnabled)
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LOUT1, 0x100);
|
|
wm8960_write_reg(dev, WM8960_ROUT1, 0x100);
|
|
}
|
|
else
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LOUT1, 0x16F);
|
|
wm8960_write_reg(dev, WM8960_ROUT1, 0x16F);
|
|
}
|
|
break;
|
|
|
|
case kWM8960_ModuleSpeaker:
|
|
if (isEnabled)
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LOUT2, 0x100);
|
|
wm8960_write_reg(dev, WM8960_ROUT2, 0x100);
|
|
}
|
|
else
|
|
{
|
|
wm8960_write_reg(dev, WM8960_LOUT2, 0x16F);
|
|
wm8960_write_reg(dev, WM8960_ROUT2, 0x16f);
|
|
}
|
|
break;
|
|
|
|
case kWM8960_ModuleLineOut:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WM8960_ConfigDataFormat(struct rt_i2c_bus_device *dev, rt_uint32_t sysclk, rt_uint32_t sample_rate, rt_uint32_t bits)
|
|
{
|
|
uint32_t divider = 0;
|
|
uint16_t val = 0;
|
|
|
|
/* Compute sample rate divider, dac and adc are the same sample rate */
|
|
divider = sysclk / sample_rate;
|
|
if (divider == 256)
|
|
{
|
|
val = 0;
|
|
}
|
|
if (divider > 256)
|
|
{
|
|
val = (((divider / 256U) << 6U) | ((divider / 256U) << 3U));
|
|
}
|
|
|
|
wm8960_write_reg(dev, WM8960_CLOCK1, val);
|
|
|
|
/* Compute bclk divider */
|
|
divider /= bits * 2;
|
|
switch (divider)
|
|
{
|
|
case 4:
|
|
case 5:
|
|
case 6:
|
|
val = (0x1C0 | divider);
|
|
break;
|
|
case 8:
|
|
val = 0x1C7;
|
|
break;
|
|
case 11:
|
|
val = 0x1C8;
|
|
break;
|
|
case 12:
|
|
val = 0x1C9;
|
|
break;
|
|
case 16:
|
|
val = 0x1CA;
|
|
break;
|
|
case 22:
|
|
val = 0x1CB;
|
|
break;
|
|
case 24:
|
|
val = 0x1CC;
|
|
break;
|
|
case 32:
|
|
val = 0x1CF;
|
|
break;
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
wm8960_write_reg(dev, WM8960_CLOCK2, val);
|
|
/*
|
|
* Slave mode (MS = 0), LRP = 0, 32bit WL, left justified (FORMAT[1:0]=0b01)
|
|
*/
|
|
switch (bits)
|
|
{
|
|
case 16:
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
WM8960_IFACE1_WL(WM8960_IFACE1_WL_16BITS));
|
|
break;
|
|
case 20:
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
WM8960_IFACE1_WL(WM8960_IFACE1_WL_20BITS));
|
|
break;
|
|
case 24:
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
WM8960_IFACE1_WL(WM8960_IFACE1_WL_24BITS));
|
|
break;
|
|
case 32:
|
|
wm8960_modify_reg(dev, WM8960_IFACE1, WM8960_IFACE1_WL_MASK,
|
|
WM8960_IFACE1_WL(WM8960_IFACE1_WL_32BITS));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void WM8960_Deinit(struct rt_i2c_bus_device *dev)
|
|
{
|
|
WM8960_SetModule(dev, kWM8960_ModuleADC, RT_FALSE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleDAC, RT_FALSE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleVREF, RT_FALSE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleLineIn, RT_FALSE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleLineOut, RT_FALSE);
|
|
WM8960_SetModule(dev, kWM8960_ModuleSpeaker, RT_FALSE);
|
|
}
|
|
|
|
void WM8960_init(struct rt_i2c_bus_device *dev, wm8960_config_t *wm8960Config)
|
|
{
|
|
wm8960_config_t *config = wm8960Config;
|
|
rt_memcpy(reg_cache, wm8960_regval_tbl, sizeof(wm8960_regval_tbl));
|
|
/* Reset the codec */
|
|
wm8960_write_reg(dev, WM8960_RESET, 0x00);
|
|
/*
|
|
* 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_write_reg(dev, WM8960_POWER1, 0xFE);
|
|
/*
|
|
* Enable DACL, DACR, LOUT1, ROUT1, PLL down
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_POWER2, 0x1E0);
|
|
/*
|
|
* Enable left and right channel input PGA, left and right output mixer
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_POWER3, 0x3C);
|
|
/* ADC and DAC uses same clock */
|
|
wm8960_write_reg(dev, WM8960_IFACE2, 0x40);
|
|
/* set data route */
|
|
WM8960_SetDataRoute(dev, config->route);
|
|
/* set data protocol */
|
|
WM8960_SetProtocol(dev, config->bus);
|
|
/* set master or slave */
|
|
WM8960_SetMasterSlave(dev, config->master_slave);
|
|
/* select left input */
|
|
WM8960_SetLeftInput(dev, config->leftInputSource);
|
|
/* select right input */
|
|
WM8960_SetRightInput(dev, config->rightInputSource);
|
|
/* speaker power */
|
|
if (config->enableSpeaker)
|
|
{
|
|
WM8960_SetModule(dev, kWM8960_ModuleSpeaker, RT_TRUE);
|
|
}
|
|
wm8960_write_reg(dev, WM8960_ADDCTL1, 0x0C0);
|
|
wm8960_write_reg(dev, WM8960_ADDCTL4, 0x40);
|
|
|
|
wm8960_write_reg(dev, WM8960_BYPASS1, 0x0);
|
|
wm8960_write_reg(dev, WM8960_BYPASS2, 0x0);
|
|
/*
|
|
* ADC volume, 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_LADC, 0x1C3);
|
|
wm8960_write_reg(dev, WM8960_RADC, 0x1C3);
|
|
|
|
/*
|
|
* Digital DAC volume, 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_LDAC, 0x1E0);
|
|
wm8960_write_reg(dev, WM8960_RDAC, 0x1E0);
|
|
|
|
/*
|
|
* Headphone volume, LOUT1 and ROUT1, 0dB
|
|
*/
|
|
wm8960_write_reg(dev, WM8960_LOUT1, 0x16F);
|
|
wm8960_write_reg(dev, WM8960_ROUT1, 0x16F);
|
|
|
|
/* Unmute DAC. */
|
|
wm8960_write_reg(dev, WM8960_DACCTL1, 0x0000);
|
|
wm8960_write_reg(dev, WM8960_LINVOL, 0x117);
|
|
wm8960_write_reg(dev, WM8960_RINVOL, 0x117);
|
|
|
|
WM8960_ConfigDataFormat(dev, config->format.mclk_HZ, config->format.sampleRate, config->format.bitWidth);
|
|
}
|
|
void WM8960_SetPlay(struct rt_i2c_bus_device *dev, uint32_t playSource)
|
|
{
|
|
if (kWM8960_PlaySourcePGA & playSource)
|
|
{
|
|
wm8960_modify_reg(dev, WM8960_BYPASS1, 0x80U, 0x80U);
|
|
wm8960_modify_reg(dev, WM8960_BYPASS2, 0x80U, 0x80U);
|
|
wm8960_modify_reg(dev, WM8960_LOUTMIX, 0x180U, 0U);
|
|
wm8960_modify_reg(dev, WM8960_ROUTMIX, 0x180U, 0U);
|
|
}
|
|
|
|
if (playSource & kWM8960_PlaySourceDAC)
|
|
{
|
|
wm8960_modify_reg(dev, WM8960_BYPASS1, 0x80U, 0x00U);
|
|
wm8960_modify_reg(dev, WM8960_BYPASS2, 0x80U, 0x00U);
|
|
wm8960_modify_reg(dev, WM8960_LOUTMIX, 0x180U, 0x100U);
|
|
wm8960_modify_reg(dev, WM8960_ROUTMIX, 0x180U, 0x100U);
|
|
}
|
|
|
|
if (playSource & kWM8960_PlaySourceInput)
|
|
{
|
|
wm8960_modify_reg(dev, WM8960_BYPASS1, 0x80U, 0x0U);
|
|
wm8960_modify_reg(dev, WM8960_BYPASS2, 0x80U, 0x0U);
|
|
wm8960_modify_reg(dev, WM8960_LOUTMIX, 0x180U, 0x80U);
|
|
wm8960_modify_reg(dev, WM8960_ROUTMIX, 0x180U, 0x80U);
|
|
}
|
|
}
|