Commit 6eebf35b authored by Oder Chiou's avatar Oder Chiou Committed by Mark Brown

ASoC: rt5514: add rt5514 SPI driver

The device has multiple control interfaces, I2C and SPI. The I2C interface
mainly controls the register settings of codec. The SPI interface is in
order to provide the high speed transmission of data. For example, high
bandwidth memory read/write of DSP. The patch adds the rt5514 SPI driver
for loading the firmware of DSP and retrieving the voice data from DSP
after the system is waked up by specific voice.
Signed-off-by: default avatarOder Chiou <oder_chiou@realtek.com>
Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 1a695a90
......@@ -643,6 +643,9 @@ config SND_SOC_RT298
config SND_SOC_RT5514
tristate
config SND_SOC_RT5514_SPI
tristate
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
depends on I2C
......
......@@ -100,6 +100,7 @@ snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
snd-soc-rt5514-objs := rt5514.o
snd-soc-rt5514-spi-objs := rt5514-spi.o
snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
......@@ -314,6 +315,7 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT298) += snd-soc-rt298.o
obj-$(CONFIG_SND_SOC_RT5514) += snd-soc-rt5514.o
obj-$(CONFIG_SND_SOC_RT5514_SPI) += snd-soc-rt5514-spi.o
obj-$(CONFIG_SND_SOC_RT5616) += snd-soc-rt5616.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
......
This diff is collapsed.
/*
* rt5514-spi.h -- RT5514 driver
*
* Copyright 2015 Realtek Semiconductor Corp.
* Author: Oder Chiou <oder_chiou@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __RT5514_SPI_H__
#define __RT5514_SPI_H__
/**
* RT5514_SPI_BUF_LEN is the buffer size of SPI master controller.
*/
#define RT5514_SPI_BUF_LEN 240
#define RT5514_BUFFER_VOICE_BASE 0x18001034
#define RT5514_BUFFER_VOICE_LIMIT 0x18001038
#define RT5514_BUFFER_VOICE_RP 0x1800103c
#define RT5514_BUFFER_VOICE_SIZE 0x18001040
/* SPI Command */
enum {
RT5514_SPI_CMD_16_READ = 0,
RT5514_SPI_CMD_16_WRITE,
RT5514_SPI_CMD_32_READ,
RT5514_SPI_CMD_32_WRITE,
RT5514_SPI_CMD_BURST_READ,
RT5514_SPI_CMD_BURST_WRITE,
};
int rt5514_spi_burst_read(unsigned int addr, u8 *rxbuf, size_t len);
int rt5514_spi_burst_write(u32 addr, const u8 *txbuf, size_t len);
#endif /* __RT5514_SPI_H__ */
......@@ -30,6 +30,9 @@
#include "rl6231.h"
#include "rt5514.h"
#if defined(CONFIG_SND_SOC_RT5514_SPI)
#include "rt5514-spi.h"
#endif
static const struct reg_sequence rt5514_i2c_patch[] = {
{0x1800101c, 0x00000000},
......@@ -110,6 +113,35 @@ static const struct reg_default rt5514_reg[] = {
{RT5514_VENDOR_ID2, 0x10ec5514},
};
static void rt5514_enable_dsp_prepare(struct rt5514_priv *rt5514)
{
/* Reset */
regmap_write(rt5514->i2c_regmap, 0x18002000, 0x000010ec);
/* LDO_I_limit */
regmap_write(rt5514->i2c_regmap, 0x18002200, 0x00028604);
/* I2C bypass enable */
regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000001);
/* mini-core reset */
regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x0005514b);
regmap_write(rt5514->i2c_regmap, 0x18002f00, 0x00055149);
/* I2C bypass disable */
regmap_write(rt5514->i2c_regmap, 0xfafafafa, 0x00000000);
/* PIN config */
regmap_write(rt5514->i2c_regmap, 0x18002070, 0x00000040);
/* PLL3(QN)=RCOSC*(10+2) */
regmap_write(rt5514->i2c_regmap, 0x18002240, 0x0000000a);
/* PLL3 source=RCOSC, fsi=rt_clk */
regmap_write(rt5514->i2c_regmap, 0x18002100, 0x0000000b);
/* Power on RCOSC, pll3 */
regmap_write(rt5514->i2c_regmap, 0x18002004, 0x00808b81);
/* DSP clk source = pll3, ENABLE DSP clk */
regmap_write(rt5514->i2c_regmap, 0x18002f08, 0x00000005);
/* Enable DSP clk auto switch */
regmap_write(rt5514->i2c_regmap, 0x18001114, 0x00000001);
/* Reduce DSP power */
regmap_write(rt5514->i2c_regmap, 0x18001118, 0x00000001);
}
static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
......@@ -248,6 +280,74 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
static int rt5514_dsp_voice_wake_up_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
ucontrol->value.integer.value[0] = rt5514->dsp_enabled;
return 0;
}
static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
struct snd_soc_codec *codec = rt5514->codec;
const struct firmware *fw = NULL;
if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
return 0;
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
rt5514->dsp_enabled = ucontrol->value.integer.value[0];
if (rt5514->dsp_enabled) {
rt5514_enable_dsp_prepare(rt5514);
request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
if (fw) {
#if defined(CONFIG_SND_SOC_RT5514_SPI)
rt5514_spi_burst_write(0x4ff60000, fw->data,
((fw->size/8)+1)*8);
#else
dev_err(codec->dev, "There is no SPI driver for"
" loading the firmware\n");
#endif
release_firmware(fw);
fw = NULL;
}
request_firmware(&fw, RT5514_FIRMWARE2, codec->dev);
if (fw) {
#if defined(CONFIG_SND_SOC_RT5514_SPI)
rt5514_spi_burst_write(0x4ffc0000, fw->data,
((fw->size/8)+1)*8);
#else
dev_err(codec->dev, "There is no SPI driver for"
" loading the firmware\n");
#endif
release_firmware(fw);
fw = NULL;
}
/* DSP run */
regmap_write(rt5514->i2c_regmap, 0x18002f00,
0x00055148);
} else {
regmap_multi_reg_write(rt5514->i2c_regmap,
rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
regcache_mark_dirty(rt5514->regmap);
regcache_sync(rt5514->regmap);
}
}
return 0;
}
static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
......@@ -257,6 +357,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = {
SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
adc_vol_tlv),
SOC_SINGLE_EXT("DSP Voice Wake Up", SND_SOC_NOPM, 0, 1, 0,
rt5514_dsp_voice_wake_up_get, rt5514_dsp_voice_wake_up_put),
};
/* ADC Mixer*/
......@@ -365,6 +467,35 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
return 0;
}
static int rt5514_pre_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
/**
* If the DSP is enabled in start of recording, the DSP
* should be disabled, and sync back to normal recording
* settings to make sure recording properly.
*/
if (rt5514->dsp_enabled) {
rt5514->dsp_enabled = 0;
regmap_multi_reg_write(rt5514->i2c_regmap,
rt5514_i2c_patch, ARRAY_SIZE(rt5514_i2c_patch));
regcache_mark_dirty(rt5514->regmap);
regcache_sync(rt5514->regmap);
}
break;
default:
return 0;
}
return 0;
}
static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC1L"),
......@@ -472,6 +603,8 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Audio Interface */
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_PRE("DAPM Pre", rt5514_pre_event),
};
static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
......@@ -871,7 +1004,6 @@ static const struct regmap_config rt5514_i2c_regmap = {
.reg_bits = 32,
.val_bits = 32,
.max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
.readable_reg = rt5514_i2c_readable_register,
.cache_type = REGCACHE_NONE,
......@@ -944,7 +1076,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch,
ret = regmap_multi_reg_write(rt5514->i2c_regmap, rt5514_i2c_patch,
ARRAY_SIZE(rt5514_i2c_patch));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
......
......@@ -225,6 +225,9 @@
#define RT5514_PLL_INP_MAX 40000000
#define RT5514_PLL_INP_MIN 256000
#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin"
#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin"
/* System Clock Source */
enum {
RT5514_SCLK_S_MCLK,
......@@ -247,6 +250,7 @@ struct rt5514_priv {
int pll_src;
int pll_in;
int pll_out;
int dsp_enabled;
};
#endif /* __RT5514_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment