Загрузка данных


#include <errno.h>

#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/audio/codec.h>
#include <zephyr/devicetree/clocks.h>

#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(wolfson_wm8960, CONFIG_AUDIO_CODEC_LOG_LEVEL);

#include "wm8960.h"

#define DT_DRV_COMPAT wolfson_wm8960

/*! @brief WM8960 PLLN range */
#define WM8960_PLL_N_MIN_VALUE 6U
#define WM8960_PLL_N_MAX_VALUE 12U

struct wm8960_driver_config {
	struct i2c_dt_spec i2c;
	int clock_source;
	bool enableSpeaker;
	const struct device *mclk_dev;
	clock_control_subsys_t mclk_name;
};

#define DEV_CFG(dev) ((const struct wm8960_driver_config *const)dev->config)

/*
 * 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_REG_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_REG_CACHEREGNUM];

static void wm8960_write_reg(const struct device *dev, uint8_t reg, uint16_t val);
static void wm8960_read_reg(const struct device *dev, uint8_t reg, uint16_t *val);
static void wm8960_update_reg(const struct device *dev, uint8_t reg, uint16_t mask, uint16_t val);
static void wm8960_soft_reset(const struct device *dev);
#if DEBUG_WM8960_REGISTER
static void WM8960_read_all_reg(const struct device *dev, uint16_t endAddress);
#endif
static void wm8960_configure_output(const struct device *dev);
static void wm8960_configure_input(const struct device *dev);
static int wm8960_apply_properties(const struct device *dev);
static void wm8960_set_module(const struct device *dev, wm8960_module_t module, bool isEnabled);
static void wm8960_set_data_route(const struct device *dev, audio_route_t route);
static void wm8960_set_play(const struct device *dev, uint32_t playSource);
static void wm8960_set_internal_PllConfig(const struct device *dev, uint32_t inputMclk, uint32_t outputClk, uint32_t sampleRate, uint32_t bitWidth);
static void wm8960_set_master_clock(const struct device *dev, uint32_t sysclk, uint32_t sampleRate, uint32_t bitWidth);

static int wm8960_protocol_config(const struct device *dev, audio_dai_type_t dai_type)
{
	wm8960_protocol_t proto;

	switch (dai_type) {
	case AUDIO_DAI_TYPE_I2S:
		proto = kWM8960_BusI2S;
		break;
	case AUDIO_DAI_TYPE_LEFT_JUSTIFIED:
		proto = kWM8960_BusLeftJustified;
		break;
	case AUDIO_DAI_TYPE_RIGHT_JUSTIFIED:
		proto = kWM8960_BusRightJustified;
		break;
	case AUDIO_DAI_TYPE_PCMA:
		proto = kWM8960_BusPCMA;
		break;
	case AUDIO_DAI_TYPE_PCMB:
		proto = kWM8960_BusPCMB;
		break;
	default:
		return -EINVAL;
	}

	wm8960_update_reg(dev, WM8960_REG_IFACE1, WM8960_IFACE1_FORMAT_MASK | WM8960_IFACE1_LRP_MASK, (uint16_t)proto);

	LOG_INF("Codec protocol: %#x", proto);
	return 0;
}

static void wm8960_set_master_slave(const struct device *dev, bool master)
{
    if (master)
    {
        wm8960_update_reg(dev, WM8960_REG_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_MASTER));
    }
    else
    {
        wm8960_update_reg(dev, WM8960_REG_IFACE1, WM8960_IFACE1_MS_MASK, WM8960_IFACE1_MS(WM8960_IFACE1_SLAVE));
    }
}

static int wm8960_audio_fmt_config(const struct device *dev, audio_dai_cfg_t *cfg, uint32_t mclk)
{
	uint32_t val;
	uint16_t word_size = cfg->i2s.word_size;
	uint32_t ratio = mclk / cfg->i2s.frame_clk_freq;

	switch (word_size) {
	case 16:
		val = WM8960_IFACE1_WL_16BITS;
		break;
	case 20:
		val = WM8960_IFACE1_WL_20BITS;
		break;
	case 24:
		val = WM8960_IFACE1_WL_24BITS;
		break;
	case 32:
		val = WM8960_IFACE1_WL_32BITS;
		break;
	default:
		LOG_WRN("Invalid codec bit width: %d", cfg->i2s.word_size);
		return -EINVAL;
	}

	wm8960_update_reg(dev, WM8960_REG_IFACE1, WM8960_IFACE1_WL_MASK, WM8960_IFACE1_WL(val));
	switch (cfg->i2s.frame_clk_freq) {
	case kWM8960_AudioSampleRate8kHz:
		val = 0x5U;
		break;
	case kWM8960_AudioSampleRate11025Hz:
	case kWM8960_AudioSampleRate12kHz:
		val = 0x4U;
		break;
	case kWM8960_AudioSampleRate16kHz:
		val = 0x3U;
		break;
	case kWM8960_AudioSampleRate22050Hz:
	case kWM8960_AudioSampleRate24kHz:
		val = 0x2U;
		break;
	case kWM8960_AudioSampleRate32kHz:
		val = 0x1U;
		break;
	case kWM8960_AudioSampleRate44100Hz:
	case kWM8960_AudioSampleRate48kHz:
		val = 0x0U;
		break;
	default:
		LOG_WRN("Invalid codec sample rate: %d", cfg->i2s.frame_clk_freq);
		return -EINVAL;
	}

	wm8960_write_reg(dev, WM8960_REG_ADDCTL3, val);
	/* Compute sample rate divider, dac and adc are the same sample rate */
	if (ratio == 256U)
	{
		val = 0;
	}
	else if (ratio > 256U)
	{
		val = (uint16_t)(((ratio / 256U) << 6U) | ((ratio / 256U) << 3U));
	}
	else
	{
		LOG_WRN("Invalid ratio: %d", ratio);
		return -EINVAL;
	}
printk("%s(%d) mclk=%u sample_rate=%u divider=%u\r\n",__FUNCTION__,__LINE__,mclk,cfg->i2s.frame_clk_freq,ratio);
	wm8960_update_reg(dev, WM8960_REG_CLOCK1, 0x1F8U, val);
	return 0;
}

static int wm8960_out_update(const struct device *dev, audio_channel_t channel, uint16_t val,
			     uint16_t mask)
{
	switch (channel) {
	case AUDIO_CHANNEL_FRONT_LEFT:
		wm8960_update_reg(dev, WM8960_REG_LOUT2, mask, val);
		return 0;

	case AUDIO_CHANNEL_FRONT_RIGHT:
		wm8960_update_reg(dev, WM8960_REG_ROUT2, mask, val);
		return 0;

	case AUDIO_CHANNEL_HEADPHONE_LEFT:
		wm8960_update_reg(dev, WM8960_REG_LOUT1, mask, val);
		return 0;

	case AUDIO_CHANNEL_HEADPHONE_RIGHT:
		wm8960_update_reg(dev, WM8960_REG_ROUT1, mask, val);
		return 0;

	case AUDIO_CHANNEL_ALL:
		wm8960_update_reg(dev, WM8960_REG_LOUT1, mask, val);
		wm8960_update_reg(dev, WM8960_REG_ROUT1, mask, val);
		wm8960_update_reg(dev, WM8960_REG_LOUT2, mask, val);
		wm8960_update_reg(dev, WM8960_REG_ROUT2, mask, val);
		return 0;

	default:
		return -EINVAL;
	}
}

static int wm8960_out_volume_config(const struct device *dev, audio_channel_t channel, int volume)
{
	/* Set volume values with VU = 0 */
	const uint16_t val = WM8960_REGVAL_OUT_VOL(1, 0, volume);
	const uint16_t mask =
		WM8960_REGMASK_OUT_VU | WM8960_REGMASK_OUT_ZC | WM8960_REGMASK_OUT_VOL;

	return wm8960_out_update(dev, channel, val, mask);
}

static int wm8960_out_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
{
	uint8_t val = 0U;

	switch (channel) {
	case AUDIO_CHANNEL_FRONT_LEFT:
		val = mute ? 2U : 0U;
		wm8960_update_reg(dev, WM8960_REG_CLASSD1, WM8960_L_CH_MUTE_MASK, val);
		return 0;

	case AUDIO_CHANNEL_FRONT_RIGHT:
		val = mute ? 1U : 0U;
		wm8960_update_reg(dev, WM8960_REG_CLASSD1, WM8960_R_CH_MUTE_MASK, val);
		return 0;

	case AUDIO_CHANNEL_HEADPHONE_LEFT:
		val = mute ? 2U : 0U;
		wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_L_CH_MUTE_MASK, val);
		return 0;

	case AUDIO_CHANNEL_HEADPHONE_RIGHT:
		val = mute ? 1U : 0U;
		wm8960_update_reg(dev, WM8960_REG_POWER2, WM8960_R_CH_MUTE_MASK, val);
		return 0;

	case AUDIO_CHANNEL_ALL:
		val = mute ? 3U : 0U;
		wm8960_update_reg(dev, WM8960_REG_CLASSD1,
				  (WM8960_L_CH_MUTE_MASK | WM8960_R_CH_MUTE_MASK), val);
		wm8960_update_reg(dev, WM8960_REG_POWER2,
				  (WM8960_L_CH_MUTE_MASK | WM8960_R_CH_MUTE_MASK), val);
		return 0;

	default:
		return -EINVAL;
	}
}

static int wm8960_in_update(const struct device *dev, audio_channel_t channel, uint16_t mask,
			    uint16_t val)
{
	switch (channel) {
	case AUDIO_CHANNEL_FRONT_LEFT:
		wm8960_update_reg(dev, WM8960_REG_LINVOL, mask, val);
		return 0;

	case AUDIO_CHANNEL_FRONT_RIGHT:
		wm8960_update_reg(dev, WM8960_REG_RINVOL, mask, val);
		return 0;

	case AUDIO_CHANNEL_ALL:
		wm8960_update_reg(dev, WM8960_REG_LINVOL, mask, val);
		wm8960_update_reg(dev, WM8960_REG_RINVOL, mask, val);
		return 0;

	default:
		return -EINVAL;
	}
}

static int wm8960_in_volume_config(const struct device *dev, audio_channel_t channel, int volume)
{
	const uint16_t val = WM8960_REGVAL_IN_VOL(1, 0, 0, volume);
	const uint16_t mask = WM8960_REGMASK_IN_MUTE;

	return wm8960_in_update(dev, channel, mask, val);
}

static int wm8960_in_mute_config(const struct device *dev, audio_channel_t channel, bool mute)
{
	const uint16_t val = WM8960_REGVAL_IN_VOL(1, mute, 0, 0);
	const uint16_t mask = WM8960_REGMASK_IN_MUTE;

	return wm8960_in_update(dev, channel, mask, val);
}