Commit e5fadb72 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/rk3036', 'asoc/topic/rockchip',...

Merge remote-tracking branches 'asoc/topic/rk3036', 'asoc/topic/rockchip', 'asoc/topic/rt274' and 'asoc/topic/rt5514' into asoc-next
......@@ -29,11 +29,14 @@ pdm: pdm@ff040000 {
dma-names = "rx";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&pdmm0_clk
&pdmm0_fsync
&pdmm0_sdi0
&pdmm0_sdi1
&pdmm0_sdi2
&pdmm0_sdi3>;
pinctrl-1 = <&pdmm0_sleep>;
pinctrl-1 = <&pdmm0_clk_sleep
&pdmm0_sdi0_sleep
&pdmm0_sdi1_sleep
&pdmm0_sdi2_sleep
&pdmm0_sdi3_sleep>;
status = "disabled";
};
......@@ -4,7 +4,7 @@ Required properties:
- compatible: "rockchip,rk3399-gru-sound"
- rockchip,cpu: The phandle of the Rockchip I2S controller that's
connected to the codecs
- rockchip,codec: The phandle of the MAX98357A/RT5514/DA7219 codecs
- rockchip,codec: The phandle of the audio codecs
Optional properties:
- dmic-wakeup-delay-ms : specify delay time (ms) for DMIC ready.
......
......@@ -7,8 +7,12 @@ Required properties:
- compatible: should be one of the following:
- "rockchip,rk3066-i2s": for rk3066
- "rockchip,rk3036-i2s", "rockchip,rk3066-i2s": for rk3036
- "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188
- "rockchip,rk3228-i2s", "rockchip,rk3066-i2s": for rk3228
- "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288
- "rockchip,rk3328-i2s", "rockchip,rk3066-i2s": for rk3328
- "rockchip,rk3366-i2s", "rockchip,rk3066-i2s": for rk3366
- "rockchip,rk3368-i2s", "rockchip,rk3066-i2s": for rk3368
- "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399
- reg: physical base address of the controller and length of memory mapped
......
......@@ -1102,6 +1102,7 @@ MODULE_DEVICE_TABLE(i2c, rt274_i2c_id);
static const struct acpi_device_id rt274_acpi_match[] = {
{ "10EC0274", 0 },
{ "INT34C2", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, rt274_acpi_match);
......
......@@ -43,9 +43,7 @@ struct rt5514_dsp {
struct mutex dma_lock;
struct snd_pcm_substream *substream;
unsigned int buf_base, buf_limit, buf_rp;
size_t buf_size;
size_t dma_offset;
size_t dsp_offset;
size_t buf_size, get_size, dma_offset;
};
static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
......@@ -80,6 +78,8 @@ static void rt5514_spi_copy_work(struct work_struct *work)
container_of(work, struct rt5514_dsp, copy_work.work);
struct snd_pcm_runtime *runtime;
size_t period_bytes, truncated_bytes = 0;
unsigned int cur_wp, remain_data;
u8 buf[8];
mutex_lock(&rt5514_dsp->dma_lock);
if (!rt5514_dsp->substream) {
......@@ -90,8 +90,24 @@ static void rt5514_spi_copy_work(struct work_struct *work)
runtime = rt5514_dsp->substream->runtime;
period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream);
if (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes)
period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
if (rt5514_dsp->get_size >= rt5514_dsp->buf_size) {
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
sizeof(buf));
cur_wp = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
if (cur_wp >= rt5514_dsp->buf_rp)
remain_data = (cur_wp - rt5514_dsp->buf_rp);
else
remain_data =
(rt5514_dsp->buf_limit - rt5514_dsp->buf_rp) +
(cur_wp - rt5514_dsp->buf_base);
if (remain_data < period_bytes) {
schedule_delayed_work(&rt5514_dsp->copy_work, 5);
goto done;
}
}
if (rt5514_dsp->buf_rp + period_bytes <= rt5514_dsp->buf_limit) {
rt5514_spi_burst_read(rt5514_dsp->buf_rp,
......@@ -112,24 +128,62 @@ static void rt5514_spi_copy_work(struct work_struct *work)
runtime->dma_area + rt5514_dsp->dma_offset +
truncated_bytes, period_bytes - truncated_bytes);
rt5514_dsp->buf_rp = rt5514_dsp->buf_base +
period_bytes - truncated_bytes;
rt5514_dsp->buf_rp = rt5514_dsp->buf_base + period_bytes -
truncated_bytes;
}
rt5514_dsp->get_size += period_bytes;
rt5514_dsp->dma_offset += period_bytes;
if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
rt5514_dsp->dma_offset = 0;
rt5514_dsp->dsp_offset += period_bytes;
snd_pcm_period_elapsed(rt5514_dsp->substream);
if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
schedule_delayed_work(&rt5514_dsp->copy_work, 5);
schedule_delayed_work(&rt5514_dsp->copy_work, 5);
done:
mutex_unlock(&rt5514_dsp->dma_lock);
}
static irqreturn_t rt5514_spi_irq(int irq, void *data)
{
struct rt5514_dsp *rt5514_dsp = data;
u8 buf[8];
rt5514_dsp->get_size = 0;
/**
* The address area x1800XXXX is the register address, and it cannot
* support spi burst read perfectly. So we use the spi burst read
* individually to make sure the data correctly.
*/
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
if (rt5514_dsp->buf_rp % 8)
rt5514_dsp->buf_rp = (rt5514_dsp->buf_rp / 8) * 8;
rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base;
if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
schedule_delayed_work(&rt5514_dsp->copy_work, 0);
return IRQ_HANDLED;
}
/* PCM for streaming audio from the DSP buffer */
static int rt5514_spi_pcm_open(struct snd_pcm_substream *substream)
{
......@@ -150,6 +204,7 @@ static int rt5514_spi_hw_params(struct snd_pcm_substream *substream,
ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
params_buffer_bytes(hw_params));
rt5514_dsp->substream = substream;
rt5514_dsp->dma_offset = 0;
mutex_unlock(&rt5514_dsp->dma_lock);
return ret;
......@@ -170,59 +225,6 @@ static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
return snd_pcm_lib_free_vmalloc_buffer(substream);
}
static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rt5514_dsp *rt5514_dsp =
snd_soc_platform_get_drvdata(rtd->platform);
u8 buf[8];
rt5514_dsp->dma_offset = 0;
rt5514_dsp->dsp_offset = 0;
/**
* The address area x1800XXXX is the register address, and it cannot
* support spi burst read perfectly. So we use the spi burst read
* individually to make sure the data correctly.
*/
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_BASE, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_base = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_LIMIT, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_limit = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_RP, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf,
sizeof(buf));
rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 |
buf[3] << 24;
return 0;
}
static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct rt5514_dsp *rt5514_dsp =
snd_soc_platform_get_drvdata(rtd->platform);
if (cmd == SNDRV_PCM_TRIGGER_START) {
if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit &&
rt5514_dsp->buf_rp && rt5514_dsp->buf_size)
schedule_delayed_work(&rt5514_dsp->copy_work, 0);
}
return 0;
}
static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
struct snd_pcm_substream *substream)
{
......@@ -238,8 +240,6 @@ static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
.open = rt5514_spi_pcm_open,
.hw_params = rt5514_spi_hw_params,
.hw_free = rt5514_spi_hw_free,
.trigger = rt5514_spi_trigger,
.prepare = rt5514_spi_prepare,
.pointer = rt5514_spi_pcm_pointer,
.mmap = snd_pcm_lib_mmap_vmalloc,
.page = snd_pcm_lib_get_vmalloc_page,
......@@ -248,6 +248,7 @@ static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
{
struct rt5514_dsp *rt5514_dsp;
int ret;
rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
GFP_KERNEL);
......@@ -257,6 +258,17 @@ static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
INIT_DELAYED_WORK(&rt5514_dsp->copy_work, rt5514_spi_copy_work);
snd_soc_platform_set_drvdata(platform, rt5514_dsp);
if (rt5514_spi->irq) {
ret = devm_request_threaded_irq(&rt5514_spi->dev,
rt5514_spi->irq, NULL, rt5514_spi_irq,
IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5514-spi",
rt5514_dsp);
if (ret)
dev_err(&rt5514_spi->dev,
"%s Failed to reguest IRQ: %d\n", __func__,
ret);
}
return 0;
}
......
......@@ -17,10 +17,9 @@
*/
#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
#define RT5514_BUFFER_VOICE_BASE 0x18000200
#define RT5514_BUFFER_VOICE_LIMIT 0x18000204
#define RT5514_BUFFER_VOICE_WP 0x1800020c
/* SPI Command */
enum {
......
......@@ -31,7 +31,7 @@
#include "rl6231.h"
#include "rt5514.h"
#if defined(CONFIG_SND_SOC_RT5514_SPI)
#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
#include "rt5514-spi.h"
#endif
......@@ -63,6 +63,9 @@ static const struct reg_sequence rt5514_patch[] = {
{RT5514_SRC_CTRL, 0x44000eee},
{RT5514_ANA_CTRL_LDO10, 0x00028604},
{RT5514_ANA_CTRL_ADCFED, 0x00000800},
{RT5514_ASRC_IN_CTRL1, 0x00000003},
{RT5514_DOWNFILTER0_CTRL3, 0x10000362},
{RT5514_DOWNFILTER1_CTRL3, 0x10000362},
};
static const struct reg_default rt5514_reg[] = {
......@@ -88,10 +91,10 @@ static const struct reg_default rt5514_reg[] = {
{RT5514_DELAY_BUF_CTRL3, 0x00000000},
{RT5514_DOWNFILTER0_CTRL1, 0x00020c2f},
{RT5514_DOWNFILTER0_CTRL2, 0x00020c2f},
{RT5514_DOWNFILTER0_CTRL3, 0x00000362},
{RT5514_DOWNFILTER0_CTRL3, 0x10000362},
{RT5514_DOWNFILTER1_CTRL1, 0x00020c2f},
{RT5514_DOWNFILTER1_CTRL2, 0x00020c2f},
{RT5514_DOWNFILTER1_CTRL3, 0x00000362},
{RT5514_DOWNFILTER1_CTRL3, 0x10000362},
{RT5514_ANA_CTRL_LDO10, 0x00028604},
{RT5514_ANA_CTRL_LDO18_16, 0x02000345},
{RT5514_ANA_CTRL_ADC12, 0x0000a2a8},
......@@ -311,7 +314,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
if (fw) {
#if defined(CONFIG_SND_SOC_RT5514_SPI)
#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
rt5514_spi_burst_write(0x4ff60000, fw->data,
((fw->size/8)+1)*8);
#else
......@@ -324,7 +327,7 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
request_firmware(&fw, RT5514_FIRMWARE2, codec->dev);
if (fw) {
#if defined(CONFIG_SND_SOC_RT5514_SPI)
#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
rt5514_spi_burst_write(0x4ffc0000, fw->data,
((fw->size/8)+1)*8);
#else
......@@ -335,6 +338,39 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
fw = NULL;
}
if (rt5514->model_buf && rt5514->model_len) {
#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
int ret;
ret = rt5514_spi_burst_write(0x4ff80000,
rt5514->model_buf,
((rt5514->model_len / 8) + 1) * 8);
if (ret) {
dev_err(codec->dev,
"Model load failed %d\n", ret);
return ret;
}
#else
dev_err(codec->dev,
"No SPI driver for loading firmware\n");
#endif
} else {
request_firmware(&fw, RT5514_FIRMWARE3,
codec->dev);
if (fw) {
#if IS_ENABLED(CONFIG_SND_SOC_RT5514_SPI)
rt5514_spi_burst_write(0x4ff80000,
fw->data,
((fw->size/8)+1)*8);
#else
dev_err(codec->dev,
"No SPI driver to load fw\n");
#endif
release_firmware(fw);
fw = NULL;
}
}
/* DSP run */
regmap_write(rt5514->i2c_regmap, 0x18002f00,
0x00055148);
......@@ -349,6 +385,34 @@ static int rt5514_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
return 0;
}
static int rt5514_hotword_model_put(struct snd_kcontrol *kcontrol,
const unsigned int __user *bytes, unsigned int size)
{
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;
int ret = 0;
if (rt5514->model_buf || rt5514->model_len < size) {
if (rt5514->model_buf)
devm_kfree(codec->dev, rt5514->model_buf);
rt5514->model_buf = devm_kmalloc(codec->dev, size, GFP_KERNEL);
if (!rt5514->model_buf) {
ret = -ENOMEM;
goto done;
}
}
/* Skips the TLV header. */
bytes += 2;
if (copy_from_user(rt5514->model_buf, bytes, size))
ret = -EFAULT;
done:
rt5514->model_len = (ret ? 0 : size);
return ret;
}
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),
......@@ -360,6 +424,8 @@ static const struct snd_kcontrol_new rt5514_snd_controls[] = {
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),
SND_SOC_BYTES_TLV("Hotword Model", 0x8504,
NULL, rt5514_hotword_model_put),
};
/* ADC Mixer*/
......@@ -471,33 +537,13 @@ 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)
static int rt5514_i2s_use_asrc(struct snd_soc_dapm_widget *source,
struct snd_soc_dapm_widget *sink)
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->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;
return (rt5514->sysclk > rt5514->lrck * 384);
}
static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
......@@ -570,6 +616,10 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0,
NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ASRC AD1", 1, RT5514_CLK_CTRL2,
RT5514_CLK_AD0_ASRC_EN_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY_S("ASRC AD2", 1, RT5514_CLK_CTRL2,
RT5514_CLK_AD1_ASRC_EN_BIT, 0, NULL, 0),
/* ADC Mux */
SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
......@@ -607,8 +657,6 @@ 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[] = {
......@@ -669,6 +717,7 @@ static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
{ "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
{ "Stereo1 ADC MIX", NULL, "adc stereo1 filter" },
{ "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
{ "adc stereo1 filter", NULL, "ASRC AD1", rt5514_i2s_use_asrc },
{ "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
{ "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
......@@ -685,6 +734,7 @@ static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
{ "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
{ "Stereo2 ADC MIX", NULL, "adc stereo2 filter" },
{ "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
{ "adc stereo2 filter", NULL, "ASRC AD2", rt5514_i2s_use_asrc },
{ "AIF1TX", NULL, "Stereo1 ADC MIX"},
{ "AIF1TX", NULL, "Stereo2 ADC MIX"},
......@@ -737,6 +787,9 @@ static int rt5514_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK,
val_len);
regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
RT5514_CLK_AD_ANA1_SEL_MASK,
(pre_div + 1) << RT5514_CLK_AD_ANA1_SEL_SFT);
regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK,
pre_div << RT5514_CLK_SYS_DIV_OUT_SFT |
......@@ -902,11 +955,38 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
{
struct snd_soc_codec *codec = dai->codec;
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
unsigned int val = 0;
unsigned int val = 0, val2 = 0;
if (rx_mask || tx_mask)
val |= RT5514_TDM_MODE;
switch (tx_mask) {
case 0x3:
val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 |
RT5514_TDM_DOCKING_START_SLOT0;
break;
case 0x30:
val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH2 |
RT5514_TDM_DOCKING_START_SLOT4;
break;
case 0xf:
val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 |
RT5514_TDM_DOCKING_START_SLOT0;
break;
case 0xf0:
val2 |= RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH4 |
RT5514_TDM_DOCKING_START_SLOT4;
break;
default:
break;
}
switch (slots) {
case 4:
val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
......@@ -952,6 +1032,10 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK |
RT5514_TDM_MODE2, val);
regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL2,
RT5514_TDM_DOCKING_MODE | RT5514_TDM_DOCKING_VALID_CH_MASK |
RT5514_TDM_DOCKING_START_MASK, val2);
return 0;
}
......@@ -975,6 +1059,24 @@ static int rt5514_set_bias_level(struct snd_soc_codec *codec,
}
break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
/*
* 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:
break;
}
......@@ -1019,7 +1121,7 @@ static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
struct snd_soc_dai_ops rt5514_aif_dai_ops = {
static const struct snd_soc_dai_ops rt5514_aif_dai_ops = {
.hw_params = rt5514_hw_params,
.set_fmt = rt5514_set_dai_fmt,
.set_sysclk = rt5514_set_dai_sysclk,
......@@ -1027,7 +1129,7 @@ struct snd_soc_dai_ops rt5514_aif_dai_ops = {
.set_tdm_slot = rt5514_set_tdm_slot,
};
struct snd_soc_dai_driver rt5514_dai[] = {
static struct snd_soc_dai_driver rt5514_dai[] = {
{
.name = "rt5514-aif1",
.id = 0,
......@@ -1097,7 +1199,7 @@ MODULE_DEVICE_TABLE(of, rt5514_of_match);
#endif
#ifdef CONFIG_ACPI
static struct acpi_device_id rt5514_acpi_match[] = {
static const struct acpi_device_id rt5514_acpi_match[] = {
{ "10EC5514", 0},
{},
};
......
......@@ -37,6 +37,7 @@
#define RT5514_PLL3_CALIB_CTRL5 0x2124
#define RT5514_DELAY_BUF_CTRL1 0x2140
#define RT5514_DELAY_BUF_CTRL3 0x2148
#define RT5514_ASRC_IN_CTRL1 0x2180
#define RT5514_DOWNFILTER0_CTRL1 0x2190
#define RT5514_DOWNFILTER0_CTRL2 0x2194
#define RT5514_DOWNFILTER0_CTRL3 0x2198
......@@ -164,6 +165,18 @@
#define RT5514_I2S_DL_24 (0x2 << 0)
#define RT5514_I2S_DL_8 (0x3 << 0)
/* RT5514_I2S_CTRL2 (0x2014) */
#define RT5514_TDM_DOCKING_MODE (0x1 << 31)
#define RT5514_TDM_DOCKING_MODE_SFT 31
#define RT5514_TDM_DOCKING_VALID_CH_MASK (0x1 << 29)
#define RT5514_TDM_DOCKING_VALID_CH_SFT 29
#define RT5514_TDM_DOCKING_VALID_CH2 (0x0 << 29)
#define RT5514_TDM_DOCKING_VALID_CH4 (0x1 << 29)
#define RT5514_TDM_DOCKING_START_MASK (0x1 << 28)
#define RT5514_TDM_DOCKING_START_SFT 28
#define RT5514_TDM_DOCKING_START_SLOT0 (0x0 << 28)
#define RT5514_TDM_DOCKING_START_SLOT4 (0x1 << 28)
/* RT5514_DIG_SOURCE_CTRL (0x20a4) */
#define RT5514_AD1_DMIC_INPUT_SEL (0x1 << 1)
#define RT5514_AD1_DMIC_INPUT_SEL_SFT 1
......@@ -185,8 +198,14 @@
#define RT5514_CLK_AD0_EN_BIT 23
#define RT5514_CLK_DMIC_OUT_SEL_MASK (0x7 << 8)
#define RT5514_CLK_DMIC_OUT_SEL_SFT 8
#define RT5514_CLK_AD_ANA1_SEL_MASK (0xf << 0)
#define RT5514_CLK_AD_ANA1_SEL_SFT 0
/* RT5514_CLK_CTRL2 (0x2108) */
#define RT5514_CLK_AD1_ASRC_EN (0x1 << 17)
#define RT5514_CLK_AD1_ASRC_EN_BIT 17
#define RT5514_CLK_AD0_ASRC_EN (0x1 << 16)
#define RT5514_CLK_AD0_ASRC_EN_BIT 16
#define RT5514_CLK_SYS_DIV_OUT_MASK (0x7 << 8)
#define RT5514_CLK_SYS_DIV_OUT_SFT 8
#define RT5514_SEL_ADC_OSR_MASK (0x7 << 4)
......@@ -236,6 +255,7 @@
#define RT5514_FIRMWARE1 "rt5514_dsp_fw1.bin"
#define RT5514_FIRMWARE2 "rt5514_dsp_fw2.bin"
#define RT5514_FIRMWARE3 "rt5514_dsp_fw3.bin"
/* System Clock Source */
enum {
......@@ -262,6 +282,8 @@ struct rt5514_priv {
int pll_in;
int pll_out;
int dsp_enabled;
u8 *model_buf;
unsigned int model_len;
};
#endif /* __RT5514_H__ */
......@@ -68,6 +68,8 @@ config SND_SOC_RK3399_GRU_SOUND
select SND_SOC_RT5514
select SND_SOC_DA7219
select SND_SOC_RT5514_SPI
select SND_SOC_HDMI_CODEC
select SND_SOC_DMIC
help
Say Y or M here if you want to add support multiple codecs for SoC
audio on Rockchip RK3399 GRU boards.
......@@ -147,7 +147,7 @@ static int rk_init(struct snd_soc_pcm_runtime *runtime)
return 0;
}
static struct snd_soc_ops rk_ops = {
static const struct snd_soc_ops rk_ops = {
.hw_params = rk_hw_params,
};
......@@ -272,8 +272,6 @@ static int snd_rk_mc_probe(struct platform_device *pdev)
return ret;
}
platform_set_drvdata(pdev, card);
return ret;
}
......
......@@ -38,7 +38,7 @@
#define SOUND_FS 256
static unsigned int rt5514_dmic_delay;
static unsigned int dmic_wakeup_delay;
static struct snd_soc_jack rockchip_sound_jack;
......@@ -126,7 +126,7 @@ static int rockchip_sound_rt5514_hw_params(struct snd_pcm_substream *substream,
}
/* Wait for DMIC stable */
msleep(rt5514_dmic_delay);
msleep(dmic_wakeup_delay);
return 0;
}
......@@ -228,6 +228,67 @@ static int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
static int rockchip_sound_cdndp_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int mclk, ret;
/* in bypass mode, the mclk has to be one of the frequencies below */
switch (params_rate(params)) {
case 8000:
case 16000:
case 24000:
case 32000:
case 48000:
case 64000:
case 96000:
mclk = 12288000;
break;
case 11025:
case 22050:
case 44100:
case 88200:
mclk = 11289600;
break;
default:
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, 0, mclk,
SND_SOC_CLOCK_OUT);
if (ret < 0) {
dev_err(codec_dai->dev, "Can't set cpu clock out %d\n", ret);
return ret;
}
return 0;
}
static int rockchip_sound_dmic_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
unsigned int mclk;
int ret;
mclk = params_rate(params) * SOUND_FS;
ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 0, mclk, 0);
if (ret) {
dev_err(rtd->card->dev, "%s() error setting sysclk to %u: %d\n",
__func__, mclk, ret);
return ret;
}
/* Wait for DMIC stable */
msleep(dmic_wakeup_delay);
return 0;
}
static const struct snd_soc_ops rockchip_sound_max98357a_ops = {
.hw_params = rockchip_sound_max98357a_hw_params,
};
......@@ -240,16 +301,70 @@ static const struct snd_soc_ops rockchip_sound_da7219_ops = {
.hw_params = rockchip_sound_da7219_hw_params,
};
static const struct snd_soc_ops rockchip_sound_cdndp_ops = {
.hw_params = rockchip_sound_cdndp_hw_params,
};
static const struct snd_soc_ops rockchip_sound_dmic_ops = {
.hw_params = rockchip_sound_dmic_hw_params,
};
static struct snd_soc_card rockchip_sound_card = {
.name = "rk3399-gru-sound",
.owner = THIS_MODULE,
.dapm_widgets = rockchip_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),
.dapm_routes = rockchip_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes),
.controls = rockchip_controls,
.num_controls = ARRAY_SIZE(rockchip_controls),
};
enum {
DAILINK_CDNDP,
DAILINK_DA7219,
DAILINK_DMIC,
DAILINK_MAX98357A,
DAILINK_RT5514,
DAILINK_DA7219,
DAILINK_RT5514_DSP,
};
#define DAILINK_ENTITIES (DAILINK_DA7219 + 1)
static const char * const dailink_compat[] = {
[DAILINK_CDNDP] = "rockchip,rk3399-cdn-dp",
[DAILINK_DA7219] = "dlg,da7219",
[DAILINK_DMIC] = "dmic-codec",
[DAILINK_MAX98357A] = "maxim,max98357a",
[DAILINK_RT5514] = "realtek,rt5514-i2c",
[DAILINK_RT5514_DSP] = "realtek,rt5514-spi",
};
static struct snd_soc_dai_link rockchip_dailinks[] = {
static const struct snd_soc_dai_link rockchip_dais[] = {
[DAILINK_CDNDP] = {
.name = "DP",
.stream_name = "DP PCM",
.codec_dai_name = "i2s-hifi",
.ops = &rockchip_sound_cdndp_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
[DAILINK_DA7219] = {
.name = "DA7219",
.stream_name = "DA7219 PCM",
.codec_dai_name = "da7219-hifi",
.init = rockchip_sound_da7219_init,
.ops = &rockchip_sound_da7219_ops,
/* set da7219 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
[DAILINK_DMIC] = {
.name = "DMIC",
.stream_name = "DMIC PCM",
.codec_dai_name = "dmic-hifi",
.ops = &rockchip_sound_dmic_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
[DAILINK_MAX98357A] = {
.name = "MAX98357A",
.stream_name = "MAX98357A PCM",
......@@ -268,108 +383,95 @@ static struct snd_soc_dai_link rockchip_dailinks[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
[DAILINK_DA7219] = {
.name = "DA7219",
.stream_name = "DA7219 PCM",
.codec_dai_name = "da7219-hifi",
.init = rockchip_sound_da7219_init,
.ops = &rockchip_sound_da7219_ops,
/* set da7219 as slave */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
/* RT5514 DSP for voice wakeup via spi bus */
[DAILINK_RT5514_DSP] = {
.name = "RT5514 DSP",
.stream_name = "Wake on Voice",
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.codec_dai_name = "rt5514-dsp-cpu-dai",
},
};
static struct snd_soc_card rockchip_sound_card = {
.name = "rk3399-gru-sound",
.owner = THIS_MODULE,
.dai_link = rockchip_dailinks,
.num_links = ARRAY_SIZE(rockchip_dailinks),
.dapm_widgets = rockchip_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),
.dapm_routes = rockchip_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes),
.controls = rockchip_controls,
.num_controls = ARRAY_SIZE(rockchip_controls),
};
static int rockchip_sound_match_stub(struct device *dev, void *data)
static int rockchip_sound_codec_node_match(struct device_node *np_codec)
{
return 1;
}
int i;
static int rockchip_sound_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &rockchip_sound_card;
struct device_node *cpu_node;
struct device *dev;
struct device_driver *drv;
int i, ret;
cpu_node = of_parse_phandle(pdev->dev.of_node, "rockchip,cpu", 0);
if (!cpu_node) {
dev_err(&pdev->dev, "Property 'rockchip,cpu' missing or invalid\n");
return -EINVAL;
for (i = 0; i < ARRAY_SIZE(dailink_compat); i++) {
if (of_device_is_compatible(np_codec, dailink_compat[i]))
return i;
}
return -1;
}
for (i = 0; i < DAILINK_ENTITIES; i++) {
rockchip_dailinks[i].platform_of_node = cpu_node;
rockchip_dailinks[i].cpu_of_node = cpu_node;
rockchip_dailinks[i].codec_of_node =
of_parse_phandle(pdev->dev.of_node, "rockchip,codec", i);
if (!rockchip_dailinks[i].codec_of_node) {
dev_err(&pdev->dev,
"Property[%d] 'rockchip,codec' missing or invalid\n", i);
static int rockchip_sound_of_parse_dais(struct device *dev,
struct snd_soc_card *card)
{
struct device_node *np_cpu, *np_cpu0, *np_cpu1;
struct device_node *np_codec;
struct snd_soc_dai_link *dai;
int i, index;
card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais),
GFP_KERNEL);
if (!card->dai_link)
return -ENOMEM;
np_cpu0 = of_parse_phandle(dev->of_node, "rockchip,cpu", 0);
np_cpu1 = of_parse_phandle(dev->of_node, "rockchip,cpu", 1);
card->num_links = 0;
for (i = 0; i < ARRAY_SIZE(rockchip_dais); i++) {
np_codec = of_parse_phandle(dev->of_node,
"rockchip,codec", i);
if (!np_codec)
break;
if (!of_device_is_available(np_codec))
continue;
index = rockchip_sound_codec_node_match(np_codec);
if (index < 0)
continue;
np_cpu = (index == DAILINK_CDNDP) ? np_cpu1 : np_cpu0;
if (!np_cpu) {
dev_err(dev, "Missing 'rockchip,cpu' for %s\n",
rockchip_dais[index].name);
return -EINVAL;
}
}
/**
* To acquire the spi driver of the rt5514 and set the dai-links names
* for soc_bind_dai_link
*/
drv = driver_find("rt5514", &spi_bus_type);
if (!drv) {
dev_err(&pdev->dev, "Can not find the rt5514 driver at the spi bus\n");
return -EINVAL;
dai = &card->dai_link[card->num_links++];
*dai = rockchip_dais[index];
dai->codec_of_node = np_codec;
dai->platform_of_node = np_cpu;
dai->cpu_of_node = np_cpu;
}
dev = driver_find_device(drv, NULL, NULL, rockchip_sound_match_stub);
if (!dev) {
dev_err(&pdev->dev, "Can not find the rt5514 device\n");
return -ENODEV;
return 0;
}
static int rockchip_sound_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &rockchip_sound_card;
int ret;
ret = rockchip_sound_of_parse_dais(&pdev->dev, card);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to parse dais: %d\n", ret);
return ret;
}
/* Set DMIC delay */
ret = device_property_read_u32(&pdev->dev, "dmic-delay",
&rt5514_dmic_delay);
/* Set DMIC wakeup delay */
ret = device_property_read_u32(&pdev->dev, "dmic-wakeup-delay-ms",
&dmic_wakeup_delay);
if (ret) {
rt5514_dmic_delay = 0;
dmic_wakeup_delay = 0;
dev_dbg(&pdev->dev,
"no optional property 'dmic-delay' found, default: no delay\n");
"no optional property 'dmic-wakeup-delay-ms' found, default: no delay\n");
}
rockchip_dailinks[DAILINK_RT5514_DSP].cpu_name = kstrdup_const(dev_name(dev), GFP_KERNEL);
rockchip_dailinks[DAILINK_RT5514_DSP].cpu_dai_name = kstrdup_const(dev_name(dev), GFP_KERNEL);
rockchip_dailinks[DAILINK_RT5514_DSP].platform_name = kstrdup_const(dev_name(dev), GFP_KERNEL);
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
__func__, ret);
return ret;
return devm_snd_soc_register_card(&pdev->dev, card);
}
static const struct of_device_id rockchip_sound_of_match[] = {
......
......@@ -579,10 +579,8 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
int val;
i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s) {
dev_err(&pdev->dev, "Can't allocate rk_i2s_dev\n");
if (!i2s)
return -ENOMEM;
}
i2s->dev = &pdev->dev;
......
......@@ -249,7 +249,7 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
return 0;
}
static struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
.set_fmt = rockchip_pdm_set_fmt,
.trigger = rockchip_pdm_trigger,
.hw_params = rockchip_pdm_hw_params,
......
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