mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
Merge remote-tracking branches 'asoc/topic/fsl-spdif', 'asoc/topic/imx', 'asoc/topic/intel', 'asoc/topic/jz4740' and 'asoc/topic/max98357a' into asoc-next
This commit is contained in:
commit
6e685ea6aa
@ -0,0 +1,23 @@
|
||||
Ingenic JZ4740 I2S controller
|
||||
|
||||
Required properties:
|
||||
- compatible : "ingenic,jz4740-i2s"
|
||||
- reg : I2S registers location and length
|
||||
- clocks : AIC and I2S PLL clock specifiers.
|
||||
- clock-names: "aic" and "i2s"
|
||||
- dmas: DMA controller phandle and DMA request line for I2S Tx and Rx channels
|
||||
- dma-names: Must be "tx" and "rx"
|
||||
|
||||
Example:
|
||||
|
||||
i2s: i2s@10020000 {
|
||||
compatible = "ingenic,jz4740-i2s";
|
||||
reg = <0x10020000 0x94>;
|
||||
|
||||
clocks = <&cgu JZ4740_CLK_AIC>, <&cgu JZ4740_CLK_I2SPLL>;
|
||||
clock-names = "aic", "i2s";
|
||||
|
||||
dmas = <&dma 2>, <&dma 3>;
|
||||
dma-names = "tx", "rx";
|
||||
|
||||
};
|
14
Documentation/devicetree/bindings/sound/max98357a.txt
Normal file
14
Documentation/devicetree/bindings/sound/max98357a.txt
Normal file
@ -0,0 +1,14 @@
|
||||
Maxim MAX98357A audio DAC
|
||||
|
||||
This node models the Maxim MAX98357A DAC.
|
||||
|
||||
Required properties:
|
||||
- compatible : "maxim,max98357a"
|
||||
- sdmode-gpios : GPIO specifier for the GPIO -> DAC SDMODE pin
|
||||
|
||||
Example:
|
||||
|
||||
max98357a {
|
||||
compatible = "maxim,max98357a";
|
||||
sdmode-gpios = <&qcom_pinmux 25 0>;
|
||||
};
|
@ -69,6 +69,7 @@ config SND_SOC_ALL_CODECS
|
||||
select SND_SOC_MAX98088 if I2C
|
||||
select SND_SOC_MAX98090 if I2C
|
||||
select SND_SOC_MAX98095 if I2C
|
||||
select SND_SOC_MAX98357A
|
||||
select SND_SOC_MAX9850 if I2C
|
||||
select SND_SOC_MAX9768 if I2C
|
||||
select SND_SOC_MAX9877 if I2C
|
||||
@ -456,6 +457,9 @@ config SND_SOC_MAX98090
|
||||
config SND_SOC_MAX98095
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX98357A
|
||||
tristate
|
||||
|
||||
config SND_SOC_MAX9850
|
||||
tristate
|
||||
|
||||
|
@ -64,6 +64,7 @@ snd-soc-max9768-objs := max9768.o
|
||||
snd-soc-max98088-objs := max98088.o
|
||||
snd-soc-max98090-objs := max98090.o
|
||||
snd-soc-max98095-objs := max98095.o
|
||||
snd-soc-max98357a-objs := max98357a.o
|
||||
snd-soc-max9850-objs := max9850.o
|
||||
snd-soc-mc13783-objs := mc13783.o
|
||||
snd-soc-ml26124-objs := ml26124.o
|
||||
@ -245,6 +246,7 @@ obj-$(CONFIG_SND_SOC_MAX9768) += snd-soc-max9768.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98088) += snd-soc-max98088.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
|
||||
obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
|
||||
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
|
||||
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
|
||||
obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o
|
||||
|
138
sound/soc/codecs/max98357a.c
Normal file
138
sound/soc/codecs/max98357a.c
Normal file
@ -0,0 +1,138 @@
|
||||
/* Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* max98357a.c -- MAX98357A ALSA SoC Codec driver
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
#define DRV_NAME "max98357a"
|
||||
|
||||
static int max98357a_daiops_trigger(struct snd_pcm_substream *substream,
|
||||
int cmd, struct snd_soc_dai *dai)
|
||||
{
|
||||
struct gpio_desc *sdmode = snd_soc_dai_get_drvdata(dai);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
gpiod_set_value(sdmode, 1);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
gpiod_set_value(sdmode, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget max98357a_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_DAC("SDMode", NULL, SND_SOC_NOPM, 0, 0),
|
||||
SND_SOC_DAPM_OUTPUT("Speaker"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route max98357a_dapm_routes[] = {
|
||||
{"Speaker", NULL, "SDMode"},
|
||||
};
|
||||
|
||||
static int max98357a_codec_probe(struct snd_soc_codec *codec)
|
||||
{
|
||||
struct gpio_desc *sdmode;
|
||||
|
||||
sdmode = devm_gpiod_get(codec->dev, "sdmode");
|
||||
if (IS_ERR(sdmode)) {
|
||||
dev_err(codec->dev, "%s() unable to get sdmode GPIO: %ld\n",
|
||||
__func__, PTR_ERR(sdmode));
|
||||
return PTR_ERR(sdmode);
|
||||
}
|
||||
gpiod_direction_output(sdmode, 0);
|
||||
snd_soc_codec_set_drvdata(codec, sdmode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_soc_codec_driver max98357a_codec_driver = {
|
||||
.probe = max98357a_codec_probe,
|
||||
.dapm_widgets = max98357a_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets),
|
||||
.dapm_routes = max98357a_dapm_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes),
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_ops max98357a_dai_ops = {
|
||||
.trigger = max98357a_daiops_trigger,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_driver max98357a_dai_driver = {
|
||||
.name = DRV_NAME,
|
||||
.playback = {
|
||||
.stream_name = DRV_NAME "-playback",
|
||||
.formats = SNDRV_PCM_FMTBIT_S16 |
|
||||
SNDRV_PCM_FMTBIT_S24 |
|
||||
SNDRV_PCM_FMTBIT_S32,
|
||||
.rates = SNDRV_PCM_RATE_8000 |
|
||||
SNDRV_PCM_RATE_16000 |
|
||||
SNDRV_PCM_RATE_48000 |
|
||||
SNDRV_PCM_RATE_96000,
|
||||
.rate_min = 8000,
|
||||
.rate_max = 96000,
|
||||
.channels_min = 1,
|
||||
.channels_max = 2,
|
||||
},
|
||||
.ops = &max98357a_dai_ops,
|
||||
};
|
||||
|
||||
static int max98357a_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = snd_soc_register_codec(&pdev->dev, &max98357a_codec_driver,
|
||||
&max98357a_dai_driver, 1);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "%s() error registering codec driver: %d\n",
|
||||
__func__, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int max98357a_platform_remove(struct platform_device *pdev)
|
||||
{
|
||||
snd_soc_unregister_codec(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id max98357a_device_id[] = {
|
||||
{ .compatible = "maxim," DRV_NAME, },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, max98357a_device_id);
|
||||
#endif
|
||||
|
||||
static struct platform_driver max98357a_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = of_match_ptr(max98357a_device_id),
|
||||
},
|
||||
.probe = max98357a_platform_probe,
|
||||
.remove = max98357a_platform_remove,
|
||||
};
|
||||
module_platform_driver(max98357a_platform_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Maxim MAX98357A Codec Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:" DRV_NAME);
|
@ -31,6 +31,7 @@
|
||||
#include "rt5645.h"
|
||||
|
||||
#define RT5645_DEVICE_ID 0x6308
|
||||
#define RT5650_DEVICE_ID 0x6419
|
||||
|
||||
#define RT5645_PR_RANGE_BASE (0xff + 1)
|
||||
#define RT5645_PR_SPACING 0x100
|
||||
@ -59,6 +60,10 @@ static const struct reg_default init_list[] = {
|
||||
};
|
||||
#define RT5645_INIT_REG_LEN ARRAY_SIZE(init_list)
|
||||
|
||||
static const struct reg_default rt5650_init_list[] = {
|
||||
{0xf6, 0x0100},
|
||||
};
|
||||
|
||||
static const struct reg_default rt5645_reg[] = {
|
||||
{ 0x00, 0x0000 },
|
||||
{ 0x01, 0xc8c8 },
|
||||
@ -86,6 +91,7 @@ static const struct reg_default rt5645_reg[] = {
|
||||
{ 0x2a, 0x5656 },
|
||||
{ 0x2b, 0x5454 },
|
||||
{ 0x2c, 0xaaa0 },
|
||||
{ 0x2d, 0x0000 },
|
||||
{ 0x2f, 0x1002 },
|
||||
{ 0x31, 0x5000 },
|
||||
{ 0x32, 0x0000 },
|
||||
@ -193,6 +199,8 @@ static const struct reg_default rt5645_reg[] = {
|
||||
{ 0xdb, 0x0003 },
|
||||
{ 0xdc, 0x0049 },
|
||||
{ 0xdd, 0x001b },
|
||||
{ 0xdf, 0x0008 },
|
||||
{ 0xe0, 0x4000 },
|
||||
{ 0xe6, 0x8000 },
|
||||
{ 0xe7, 0x0200 },
|
||||
{ 0xec, 0xb300 },
|
||||
@ -242,6 +250,7 @@ static bool rt5645_volatile_register(struct device *dev, unsigned int reg)
|
||||
case RT5645_IRQ_CTRL3:
|
||||
case RT5645_INT_IRQ_ST:
|
||||
case RT5645_IL_CMD:
|
||||
case RT5650_4BTN_IL_CMD1:
|
||||
case RT5645_VENDOR_ID:
|
||||
case RT5645_VENDOR_ID1:
|
||||
case RT5645_VENDOR_ID2:
|
||||
@ -287,6 +296,7 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
|
||||
case RT5645_STO_DAC_MIXER:
|
||||
case RT5645_MONO_DAC_MIXER:
|
||||
case RT5645_DIG_MIXER:
|
||||
case RT5650_A_DAC_SOUR:
|
||||
case RT5645_DIG_INF1_DATA:
|
||||
case RT5645_PDM_OUT_CTRL:
|
||||
case RT5645_REC_L1_MIXER:
|
||||
@ -378,6 +388,8 @@ static bool rt5645_readable_register(struct device *dev, unsigned int reg)
|
||||
case RT5645_IL_CMD:
|
||||
case RT5645_IL_CMD2:
|
||||
case RT5645_IL_CMD3:
|
||||
case RT5650_4BTN_IL_CMD1:
|
||||
case RT5650_4BTN_IL_CMD2:
|
||||
case RT5645_DRC1_HL_CTRL1:
|
||||
case RT5645_DRC2_HL_CTRL1:
|
||||
case RT5645_ADC_MONO_HP_CTRL1:
|
||||
@ -603,6 +615,87 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
|
||||
* @codec: SoC audio codec device.
|
||||
* @filter_mask: mask of filters.
|
||||
* @clk_src: clock source
|
||||
*
|
||||
* The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5645 can
|
||||
* only support standard 32fs or 64fs i2s format, ASRC should be enabled to
|
||||
* support special i2s clock format such as Intel's 100fs(100 * sampling rate).
|
||||
* ASRC function will track i2s clock and generate a corresponding system clock
|
||||
* for codec. This function provides an API to select the clock source for a
|
||||
* set of filters specified by the mask. And the codec driver will turn on ASRC
|
||||
* for these filters if ASRC is selected as their clock source.
|
||||
*/
|
||||
int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src)
|
||||
{
|
||||
unsigned int asrc2_mask = 0;
|
||||
unsigned int asrc2_value = 0;
|
||||
unsigned int asrc3_mask = 0;
|
||||
unsigned int asrc3_value = 0;
|
||||
|
||||
switch (clk_src) {
|
||||
case RT5645_CLK_SEL_SYS:
|
||||
case RT5645_CLK_SEL_I2S1_ASRC:
|
||||
case RT5645_CLK_SEL_I2S2_ASRC:
|
||||
case RT5645_CLK_SEL_SYS2:
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (filter_mask & RT5645_DA_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5645_DA_STO_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5645_DA_STO_CLK_SEL_MASK)
|
||||
| (clk_src << RT5645_DA_STO_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5645_DA_MONO_L_FILTER) {
|
||||
asrc2_mask |= RT5645_DA_MONOL_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5645_DA_MONOL_CLK_SEL_MASK)
|
||||
| (clk_src << RT5645_DA_MONOL_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5645_DA_MONO_R_FILTER) {
|
||||
asrc2_mask |= RT5645_DA_MONOR_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5645_DA_MONOR_CLK_SEL_MASK)
|
||||
| (clk_src << RT5645_DA_MONOR_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5645_AD_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5645_AD_STO1_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5645_AD_STO1_CLK_SEL_MASK)
|
||||
| (clk_src << RT5645_AD_STO1_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5645_AD_MONO_L_FILTER) {
|
||||
asrc3_mask |= RT5645_AD_MONOL_CLK_SEL_MASK;
|
||||
asrc3_value = (asrc3_value & ~RT5645_AD_MONOL_CLK_SEL_MASK)
|
||||
| (clk_src << RT5645_AD_MONOL_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5645_AD_MONO_R_FILTER) {
|
||||
asrc3_mask |= RT5645_AD_MONOR_CLK_SEL_MASK;
|
||||
asrc3_value = (asrc3_value & ~RT5645_AD_MONOR_CLK_SEL_MASK)
|
||||
| (clk_src << RT5645_AD_MONOR_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (asrc2_mask)
|
||||
snd_soc_update_bits(codec, RT5645_ASRC_2,
|
||||
asrc2_mask, asrc2_value);
|
||||
|
||||
if (asrc3_mask)
|
||||
snd_soc_update_bits(codec, RT5645_ASRC_3,
|
||||
asrc3_mask, asrc3_value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5645_sel_asrc_clk_src);
|
||||
|
||||
/* Digital Mixer */
|
||||
static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
|
||||
SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
|
||||
@ -1009,6 +1102,44 @@ static SOC_ENUM_SINGLE_DECL(
|
||||
static const struct snd_kcontrol_new rt5645_if1_adc_in_mux =
|
||||
SOC_DAPM_ENUM("IF1 ADC IN source", rt5645_if1_adc_in_enum);
|
||||
|
||||
/* MX-2d [3] [2] */
|
||||
static const char * const rt5650_a_dac1_src[] = {
|
||||
"DAC1", "Stereo DAC Mixer"
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
rt5650_a_dac1_l_enum, RT5650_A_DAC_SOUR,
|
||||
RT5650_A_DAC1_L_IN_SFT, rt5650_a_dac1_src);
|
||||
|
||||
static const struct snd_kcontrol_new rt5650_a_dac1_l_mux =
|
||||
SOC_DAPM_ENUM("A DAC1 L source", rt5650_a_dac1_l_enum);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
rt5650_a_dac1_r_enum, RT5650_A_DAC_SOUR,
|
||||
RT5650_A_DAC1_R_IN_SFT, rt5650_a_dac1_src);
|
||||
|
||||
static const struct snd_kcontrol_new rt5650_a_dac1_r_mux =
|
||||
SOC_DAPM_ENUM("A DAC1 R source", rt5650_a_dac1_r_enum);
|
||||
|
||||
/* MX-2d [1] [0] */
|
||||
static const char * const rt5650_a_dac2_src[] = {
|
||||
"Stereo DAC Mixer", "Mono DAC Mixer"
|
||||
};
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
rt5650_a_dac2_l_enum, RT5650_A_DAC_SOUR,
|
||||
RT5650_A_DAC2_L_IN_SFT, rt5650_a_dac2_src);
|
||||
|
||||
static const struct snd_kcontrol_new rt5650_a_dac2_l_mux =
|
||||
SOC_DAPM_ENUM("A DAC2 L source", rt5650_a_dac2_l_enum);
|
||||
|
||||
static SOC_ENUM_SINGLE_DECL(
|
||||
rt5650_a_dac2_r_enum, RT5650_A_DAC_SOUR,
|
||||
RT5650_A_DAC2_R_IN_SFT, rt5650_a_dac2_src);
|
||||
|
||||
static const struct snd_kcontrol_new rt5650_a_dac2_r_mux =
|
||||
SOC_DAPM_ENUM("A DAC2 R source", rt5650_a_dac2_r_enum);
|
||||
|
||||
/* MX-2F [13:12] */
|
||||
static const char * const rt5645_if2_adc_in_src[] = {
|
||||
"IF_ADC1", "IF_ADC2", "VAD_ADC"
|
||||
@ -1153,11 +1284,16 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
|
||||
case SND_SOC_DAPM_POST_PMU:
|
||||
hp_amp_power(codec, 1);
|
||||
/* headphone unmute sequence */
|
||||
snd_soc_update_bits(codec, RT5645_DEPOP_M3, RT5645_CP_FQ1_MASK |
|
||||
RT5645_CP_FQ2_MASK | RT5645_CP_FQ3_MASK,
|
||||
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
|
||||
(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
|
||||
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
|
||||
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
|
||||
snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
|
||||
} else {
|
||||
snd_soc_update_bits(codec, RT5645_DEPOP_M3,
|
||||
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
|
||||
RT5645_CP_FQ3_MASK,
|
||||
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ1_SFT) |
|
||||
(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
|
||||
(RT5645_CP_FQ_192_KHZ << RT5645_CP_FQ3_SFT));
|
||||
}
|
||||
regmap_write(rt5645->regmap,
|
||||
RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
|
||||
snd_soc_update_bits(codec, RT5645_DEPOP_M1,
|
||||
@ -1177,12 +1313,16 @@ static int rt5645_hp_event(struct snd_soc_dapm_widget *w,
|
||||
|
||||
case SND_SOC_DAPM_PRE_PMD:
|
||||
/* headphone mute sequence */
|
||||
snd_soc_update_bits(codec, RT5645_DEPOP_M3,
|
||||
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
|
||||
RT5645_CP_FQ3_MASK,
|
||||
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
|
||||
(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
|
||||
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
|
||||
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
|
||||
snd_soc_write(codec, RT5645_DEPOP_M3, 0x0737);
|
||||
} else {
|
||||
snd_soc_update_bits(codec, RT5645_DEPOP_M3,
|
||||
RT5645_CP_FQ1_MASK | RT5645_CP_FQ2_MASK |
|
||||
RT5645_CP_FQ3_MASK,
|
||||
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ1_SFT) |
|
||||
(RT5645_CP_FQ_12_KHZ << RT5645_CP_FQ2_SFT) |
|
||||
(RT5645_CP_FQ_96_KHZ << RT5645_CP_FQ3_SFT));
|
||||
}
|
||||
regmap_write(rt5645->regmap,
|
||||
RT5645_PR_BASE + RT5645_MAMP_INT_REG2, 0xfc00);
|
||||
snd_soc_update_bits(codec, RT5645_DEPOP_M1,
|
||||
@ -1576,6 +1716,17 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_OUTPUT("SPOR"),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_widget rt5650_specific_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_MUX("A DAC1 L Mux", SND_SOC_NOPM,
|
||||
0, 0, &rt5650_a_dac1_l_mux),
|
||||
SND_SOC_DAPM_MUX("A DAC1 R Mux", SND_SOC_NOPM,
|
||||
0, 0, &rt5650_a_dac1_r_mux),
|
||||
SND_SOC_DAPM_MUX("A DAC2 L Mux", SND_SOC_NOPM,
|
||||
0, 0, &rt5650_a_dac2_l_mux),
|
||||
SND_SOC_DAPM_MUX("A DAC2 R Mux", SND_SOC_NOPM,
|
||||
0, 0, &rt5650_a_dac2_r_mux),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
|
||||
{ "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
|
||||
{ "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc },
|
||||
@ -1781,13 +1932,9 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
|
||||
{ "DAC MIXR", "DAC R2 Switch", "DAC R2 Volume" },
|
||||
{ "DAC MIXR", "DAC L2 Switch", "DAC L2 Volume" },
|
||||
|
||||
{ "DAC L1", NULL, "Stereo DAC MIXL" },
|
||||
{ "DAC L1", NULL, "PLL1", is_sys_clk_from_pll },
|
||||
{ "DAC R1", NULL, "Stereo DAC MIXR" },
|
||||
{ "DAC R1", NULL, "PLL1", is_sys_clk_from_pll },
|
||||
{ "DAC L2", NULL, "Mono DAC MIXL" },
|
||||
{ "DAC L2", NULL, "PLL1", is_sys_clk_from_pll },
|
||||
{ "DAC R2", NULL, "Mono DAC MIXR" },
|
||||
{ "DAC R2", NULL, "PLL1", is_sys_clk_from_pll },
|
||||
|
||||
{ "SPK MIXL", "BST1 Switch", "BST1" },
|
||||
@ -1876,6 +2023,30 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
|
||||
{ "SPOR", NULL, "SPK amp" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5650_specific_dapm_routes[] = {
|
||||
{ "A DAC1 L Mux", "DAC1", "DAC1 MIXL"},
|
||||
{ "A DAC1 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"},
|
||||
{ "A DAC1 R Mux", "DAC1", "DAC1 MIXR"},
|
||||
{ "A DAC1 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"},
|
||||
|
||||
{ "A DAC2 L Mux", "Stereo DAC Mixer", "Stereo DAC MIXL"},
|
||||
{ "A DAC2 L Mux", "Mono DAC Mixer", "Mono DAC MIXL"},
|
||||
{ "A DAC2 R Mux", "Stereo DAC Mixer", "Stereo DAC MIXR"},
|
||||
{ "A DAC2 R Mux", "Mono DAC Mixer", "Mono DAC MIXR"},
|
||||
|
||||
{ "DAC L1", NULL, "A DAC1 L Mux" },
|
||||
{ "DAC R1", NULL, "A DAC1 R Mux" },
|
||||
{ "DAC L2", NULL, "A DAC2 L Mux" },
|
||||
{ "DAC R2", NULL, "A DAC2 R Mux" },
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route rt5645_specific_dapm_routes[] = {
|
||||
{ "DAC L1", NULL, "Stereo DAC MIXL" },
|
||||
{ "DAC R1", NULL, "Stereo DAC MIXR" },
|
||||
{ "DAC L2", NULL, "Mono DAC MIXL" },
|
||||
{ "DAC R2", NULL, "Mono DAC MIXR" },
|
||||
};
|
||||
|
||||
static int rt5645_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
|
||||
{
|
||||
@ -2295,6 +2466,22 @@ static int rt5645_probe(struct snd_soc_codec *codec)
|
||||
|
||||
rt5645->codec = codec;
|
||||
|
||||
switch (rt5645->codec_type) {
|
||||
case CODEC_TYPE_RT5645:
|
||||
snd_soc_dapm_add_routes(&codec->dapm,
|
||||
rt5645_specific_dapm_routes,
|
||||
ARRAY_SIZE(rt5645_specific_dapm_routes));
|
||||
break;
|
||||
case CODEC_TYPE_RT5650:
|
||||
snd_soc_dapm_new_controls(&codec->dapm,
|
||||
rt5650_specific_dapm_widgets,
|
||||
ARRAY_SIZE(rt5650_specific_dapm_widgets));
|
||||
snd_soc_dapm_add_routes(&codec->dapm,
|
||||
rt5650_specific_dapm_routes,
|
||||
ARRAY_SIZE(rt5650_specific_dapm_routes));
|
||||
break;
|
||||
}
|
||||
|
||||
rt5645_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
|
||||
@ -2426,6 +2613,7 @@ static const struct regmap_config rt5645_regmap = {
|
||||
|
||||
static const struct i2c_device_id rt5645_i2c_id[] = {
|
||||
{ "rt5645", 0 },
|
||||
{ "rt5650", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
|
||||
@ -2458,9 +2646,18 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
||||
}
|
||||
|
||||
regmap_read(rt5645->regmap, RT5645_VENDOR_ID2, &val);
|
||||
if (val != RT5645_DEVICE_ID) {
|
||||
|
||||
switch (val) {
|
||||
case RT5645_DEVICE_ID:
|
||||
rt5645->codec_type = CODEC_TYPE_RT5645;
|
||||
break;
|
||||
case RT5650_DEVICE_ID:
|
||||
rt5645->codec_type = CODEC_TYPE_RT5650;
|
||||
break;
|
||||
default:
|
||||
dev_err(&i2c->dev,
|
||||
"Device with ID register %x is not rt5645\n", val);
|
||||
"Device with ID register %x is not rt5645 or rt5650\n",
|
||||
val);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@ -2471,6 +2668,14 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
|
||||
if (ret != 0)
|
||||
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
|
||||
|
||||
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
|
||||
ret = regmap_register_patch(rt5645->regmap, rt5650_init_list,
|
||||
ARRAY_SIZE(rt5650_init_list));
|
||||
if (ret != 0)
|
||||
dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n",
|
||||
ret);
|
||||
}
|
||||
|
||||
if (rt5645->pdata.in2_diff)
|
||||
regmap_update_bits(rt5645->regmap, RT5645_IN2_CTRL,
|
||||
RT5645_IN_DF2, RT5645_IN_DF2);
|
||||
|
@ -47,6 +47,7 @@
|
||||
#define RT5645_STO_DAC_MIXER 0x2a
|
||||
#define RT5645_MONO_DAC_MIXER 0x2b
|
||||
#define RT5645_DIG_MIXER 0x2c
|
||||
#define RT5650_A_DAC_SOUR 0x2d
|
||||
#define RT5645_DIG_INF1_DATA 0x2f
|
||||
/* Mixer - PDM */
|
||||
#define RT5645_PDM_OUT_CTRL 0x31
|
||||
@ -150,6 +151,8 @@
|
||||
#define RT5645_IL_CMD 0xdb
|
||||
#define RT5645_IL_CMD2 0xdc
|
||||
#define RT5645_IL_CMD3 0xdd
|
||||
#define RT5650_4BTN_IL_CMD1 0xdf
|
||||
#define RT5650_4BTN_IL_CMD2 0xe0
|
||||
#define RT5645_DRC1_HL_CTRL1 0xe7
|
||||
#define RT5645_DRC2_HL_CTRL1 0xe9
|
||||
#define RT5645_MUTI_DRC_CTRL1 0xea
|
||||
@ -472,6 +475,12 @@
|
||||
#define RT5645_DAC_L2_DAC_R_VOL_MASK (0x1 << 4)
|
||||
#define RT5645_DAC_L2_DAC_R_VOL_SFT 4
|
||||
|
||||
/* Analog DAC1/2 Input Source Control (0x2d) */
|
||||
#define RT5650_A_DAC1_L_IN_SFT 3
|
||||
#define RT5650_A_DAC1_R_IN_SFT 2
|
||||
#define RT5650_A_DAC2_L_IN_SFT 1
|
||||
#define RT5650_A_DAC2_R_IN_SFT 0
|
||||
|
||||
/* Digital Interface Data Control (0x2f) */
|
||||
#define RT5645_IF1_ADC2_IN_SEL (0x1 << 15)
|
||||
#define RT5645_IF1_ADC2_IN_SFT 15
|
||||
@ -1111,50 +1120,27 @@
|
||||
#define RT5645_DMIC_2_M_NOR (0x0 << 8)
|
||||
#define RT5645_DMIC_2_M_ASYN (0x1 << 8)
|
||||
|
||||
/* ASRC clock source selection (0x84, 0x85) */
|
||||
#define RT5645_CLK_SEL_SYS (0x0)
|
||||
#define RT5645_CLK_SEL_I2S1_ASRC (0x1)
|
||||
#define RT5645_CLK_SEL_I2S2_ASRC (0x2)
|
||||
#define RT5645_CLK_SEL_SYS2 (0x5)
|
||||
|
||||
/* ASRC Control 2 (0x84) */
|
||||
#define RT5645_MDA_L_M_MASK (0x1 << 15)
|
||||
#define RT5645_MDA_L_M_SFT 15
|
||||
#define RT5645_MDA_L_M_NOR (0x0 << 15)
|
||||
#define RT5645_MDA_L_M_ASYN (0x1 << 15)
|
||||
#define RT5645_MDA_R_M_MASK (0x1 << 14)
|
||||
#define RT5645_MDA_R_M_SFT 14
|
||||
#define RT5645_MDA_R_M_NOR (0x0 << 14)
|
||||
#define RT5645_MDA_R_M_ASYN (0x1 << 14)
|
||||
#define RT5645_MAD_L_M_MASK (0x1 << 13)
|
||||
#define RT5645_MAD_L_M_SFT 13
|
||||
#define RT5645_MAD_L_M_NOR (0x0 << 13)
|
||||
#define RT5645_MAD_L_M_ASYN (0x1 << 13)
|
||||
#define RT5645_MAD_R_M_MASK (0x1 << 12)
|
||||
#define RT5645_MAD_R_M_SFT 12
|
||||
#define RT5645_MAD_R_M_NOR (0x0 << 12)
|
||||
#define RT5645_MAD_R_M_ASYN (0x1 << 12)
|
||||
#define RT5645_ADC_M_MASK (0x1 << 11)
|
||||
#define RT5645_ADC_M_SFT 11
|
||||
#define RT5645_ADC_M_NOR (0x0 << 11)
|
||||
#define RT5645_ADC_M_ASYN (0x1 << 11)
|
||||
#define RT5645_STO_DAC_M_MASK (0x1 << 5)
|
||||
#define RT5645_STO_DAC_M_SFT 5
|
||||
#define RT5645_STO_DAC_M_NOR (0x0 << 5)
|
||||
#define RT5645_STO_DAC_M_ASYN (0x1 << 5)
|
||||
#define RT5645_I2S1_R_D_MASK (0x1 << 4)
|
||||
#define RT5645_I2S1_R_D_SFT 4
|
||||
#define RT5645_I2S1_R_D_DIS (0x0 << 4)
|
||||
#define RT5645_I2S1_R_D_EN (0x1 << 4)
|
||||
#define RT5645_I2S2_R_D_MASK (0x1 << 3)
|
||||
#define RT5645_I2S2_R_D_SFT 3
|
||||
#define RT5645_I2S2_R_D_DIS (0x0 << 3)
|
||||
#define RT5645_I2S2_R_D_EN (0x1 << 3)
|
||||
#define RT5645_PRE_SCLK_MASK (0x3)
|
||||
#define RT5645_PRE_SCLK_SFT 0
|
||||
#define RT5645_PRE_SCLK_512 (0x0)
|
||||
#define RT5645_PRE_SCLK_1024 (0x1)
|
||||
#define RT5645_PRE_SCLK_2048 (0x2)
|
||||
#define RT5645_DA_STO_CLK_SEL_MASK (0xf << 12)
|
||||
#define RT5645_DA_STO_CLK_SEL_SFT 12
|
||||
#define RT5645_DA_MONOL_CLK_SEL_MASK (0xf << 8)
|
||||
#define RT5645_DA_MONOL_CLK_SEL_SFT 8
|
||||
#define RT5645_DA_MONOR_CLK_SEL_MASK (0xf << 4)
|
||||
#define RT5645_DA_MONOR_CLK_SEL_SFT 4
|
||||
#define RT5645_AD_STO1_CLK_SEL_MASK (0xf << 0)
|
||||
#define RT5645_AD_STO1_CLK_SEL_SFT 0
|
||||
|
||||
/* ASRC Control 3 (0x85) */
|
||||
#define RT5645_I2S1_RATE_MASK (0xf << 12)
|
||||
#define RT5645_I2S1_RATE_SFT 12
|
||||
#define RT5645_I2S2_RATE_MASK (0xf << 8)
|
||||
#define RT5645_I2S2_RATE_SFT 8
|
||||
#define RT5645_AD_MONOL_CLK_SEL_MASK (0xf << 4)
|
||||
#define RT5645_AD_MONOL_CLK_SEL_SFT 4
|
||||
#define RT5645_AD_MONOR_CLK_SEL_MASK (0xf << 0)
|
||||
#define RT5645_AD_MONOR_CLK_SEL_SFT 0
|
||||
|
||||
/* ASRC Control 4 (0x89) */
|
||||
#define RT5645_I2S1_PD_MASK (0x7 << 12)
|
||||
@ -2175,6 +2161,24 @@ enum {
|
||||
RT5645_DMIC_DATA_GPIO11,
|
||||
};
|
||||
|
||||
enum {
|
||||
CODEC_TYPE_RT5645,
|
||||
CODEC_TYPE_RT5650,
|
||||
};
|
||||
|
||||
/* filter mask */
|
||||
enum {
|
||||
RT5645_DA_STEREO_FILTER = 0x1,
|
||||
RT5645_DA_MONO_L_FILTER = (0x1 << 1),
|
||||
RT5645_DA_MONO_R_FILTER = (0x1 << 2),
|
||||
RT5645_AD_STEREO_FILTER = (0x1 << 3),
|
||||
RT5645_AD_MONO_L_FILTER = (0x1 << 4),
|
||||
RT5645_AD_MONO_R_FILTER = (0x1 << 5),
|
||||
};
|
||||
|
||||
int rt5645_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src);
|
||||
|
||||
struct rt5645_priv {
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt5645_platform_data pdata;
|
||||
@ -2184,6 +2188,7 @@ struct rt5645_priv {
|
||||
struct snd_soc_jack *mic_jack;
|
||||
struct delayed_work jack_detect_work;
|
||||
|
||||
int codec_type;
|
||||
int sysclk;
|
||||
int sysclk_src;
|
||||
int lrck[RT5645_AIFS];
|
||||
|
@ -592,6 +592,89 @@ static int can_use_asrc(struct snd_soc_dapm_widget *source,
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* rt5670_sel_asrc_clk_src - select ASRC clock source for a set of filters
|
||||
* @codec: SoC audio codec device.
|
||||
* @filter_mask: mask of filters.
|
||||
* @clk_src: clock source
|
||||
*
|
||||
* The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5670 can
|
||||
* only support standard 32fs or 64fs i2s format, ASRC should be enabled to
|
||||
* support special i2s clock format such as Intel's 100fs(100 * sampling rate).
|
||||
* ASRC function will track i2s clock and generate a corresponding system clock
|
||||
* for codec. This function provides an API to select the clock source for a
|
||||
* set of filters specified by the mask. And the codec driver will turn on ASRC
|
||||
* for these filters if ASRC is selected as their clock source.
|
||||
*/
|
||||
int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src)
|
||||
{
|
||||
unsigned int asrc2_mask = 0, asrc2_value = 0;
|
||||
unsigned int asrc3_mask = 0, asrc3_value = 0;
|
||||
|
||||
if (clk_src > RT5670_CLK_SEL_SYS3)
|
||||
return -EINVAL;
|
||||
|
||||
if (filter_mask & RT5670_DA_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5670_DA_STO_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5670_DA_STO_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_DA_STO_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_DA_MONO_L_FILTER) {
|
||||
asrc2_mask |= RT5670_DA_MONOL_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5670_DA_MONOL_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_DA_MONOL_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_DA_MONO_R_FILTER) {
|
||||
asrc2_mask |= RT5670_DA_MONOR_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5670_DA_MONOR_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_DA_MONOR_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_AD_STEREO_FILTER) {
|
||||
asrc2_mask |= RT5670_AD_STO1_CLK_SEL_MASK;
|
||||
asrc2_value = (asrc2_value & ~RT5670_AD_STO1_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_AD_STO1_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_AD_MONO_L_FILTER) {
|
||||
asrc3_mask |= RT5670_AD_MONOL_CLK_SEL_MASK;
|
||||
asrc3_value = (asrc3_value & ~RT5670_AD_MONOL_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_AD_MONOL_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_AD_MONO_R_FILTER) {
|
||||
asrc3_mask |= RT5670_AD_MONOR_CLK_SEL_MASK;
|
||||
asrc3_value = (asrc3_value & ~RT5670_AD_MONOR_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_AD_MONOR_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_UP_RATE_FILTER) {
|
||||
asrc3_mask |= RT5670_UP_CLK_SEL_MASK;
|
||||
asrc3_value = (asrc3_value & ~RT5670_UP_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_UP_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (filter_mask & RT5670_DOWN_RATE_FILTER) {
|
||||
asrc3_mask |= RT5670_DOWN_CLK_SEL_MASK;
|
||||
asrc3_value = (asrc3_value & ~RT5670_DOWN_CLK_SEL_MASK)
|
||||
| (clk_src << RT5670_DOWN_CLK_SEL_SFT);
|
||||
}
|
||||
|
||||
if (asrc2_mask)
|
||||
snd_soc_update_bits(codec, RT5670_ASRC_2,
|
||||
asrc2_mask, asrc2_value);
|
||||
|
||||
if (asrc3_mask)
|
||||
snd_soc_update_bits(codec, RT5670_ASRC_3,
|
||||
asrc3_mask, asrc3_value);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rt5670_sel_asrc_clk_src);
|
||||
|
||||
/* Digital Mixer */
|
||||
static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
|
||||
SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
|
||||
|
@ -1023,50 +1023,33 @@
|
||||
#define RT5670_DMIC_2_M_NOR (0x0 << 8)
|
||||
#define RT5670_DMIC_2_M_ASYN (0x1 << 8)
|
||||
|
||||
/* ASRC clock source selection (0x84, 0x85) */
|
||||
#define RT5670_CLK_SEL_SYS (0x0)
|
||||
#define RT5670_CLK_SEL_I2S1_ASRC (0x1)
|
||||
#define RT5670_CLK_SEL_I2S2_ASRC (0x2)
|
||||
#define RT5670_CLK_SEL_I2S3_ASRC (0x3)
|
||||
#define RT5670_CLK_SEL_SYS2 (0x5)
|
||||
#define RT5670_CLK_SEL_SYS3 (0x6)
|
||||
|
||||
/* ASRC Control 2 (0x84) */
|
||||
#define RT5670_MDA_L_M_MASK (0x1 << 15)
|
||||
#define RT5670_MDA_L_M_SFT 15
|
||||
#define RT5670_MDA_L_M_NOR (0x0 << 15)
|
||||
#define RT5670_MDA_L_M_ASYN (0x1 << 15)
|
||||
#define RT5670_MDA_R_M_MASK (0x1 << 14)
|
||||
#define RT5670_MDA_R_M_SFT 14
|
||||
#define RT5670_MDA_R_M_NOR (0x0 << 14)
|
||||
#define RT5670_MDA_R_M_ASYN (0x1 << 14)
|
||||
#define RT5670_MAD_L_M_MASK (0x1 << 13)
|
||||
#define RT5670_MAD_L_M_SFT 13
|
||||
#define RT5670_MAD_L_M_NOR (0x0 << 13)
|
||||
#define RT5670_MAD_L_M_ASYN (0x1 << 13)
|
||||
#define RT5670_MAD_R_M_MASK (0x1 << 12)
|
||||
#define RT5670_MAD_R_M_SFT 12
|
||||
#define RT5670_MAD_R_M_NOR (0x0 << 12)
|
||||
#define RT5670_MAD_R_M_ASYN (0x1 << 12)
|
||||
#define RT5670_ADC_M_MASK (0x1 << 11)
|
||||
#define RT5670_ADC_M_SFT 11
|
||||
#define RT5670_ADC_M_NOR (0x0 << 11)
|
||||
#define RT5670_ADC_M_ASYN (0x1 << 11)
|
||||
#define RT5670_STO_DAC_M_MASK (0x1 << 5)
|
||||
#define RT5670_STO_DAC_M_SFT 5
|
||||
#define RT5670_STO_DAC_M_NOR (0x0 << 5)
|
||||
#define RT5670_STO_DAC_M_ASYN (0x1 << 5)
|
||||
#define RT5670_I2S1_R_D_MASK (0x1 << 4)
|
||||
#define RT5670_I2S1_R_D_SFT 4
|
||||
#define RT5670_I2S1_R_D_DIS (0x0 << 4)
|
||||
#define RT5670_I2S1_R_D_EN (0x1 << 4)
|
||||
#define RT5670_I2S2_R_D_MASK (0x1 << 3)
|
||||
#define RT5670_I2S2_R_D_SFT 3
|
||||
#define RT5670_I2S2_R_D_DIS (0x0 << 3)
|
||||
#define RT5670_I2S2_R_D_EN (0x1 << 3)
|
||||
#define RT5670_PRE_SCLK_MASK (0x3)
|
||||
#define RT5670_PRE_SCLK_SFT 0
|
||||
#define RT5670_PRE_SCLK_512 (0x0)
|
||||
#define RT5670_PRE_SCLK_1024 (0x1)
|
||||
#define RT5670_PRE_SCLK_2048 (0x2)
|
||||
#define RT5670_DA_STO_CLK_SEL_MASK (0xf << 12)
|
||||
#define RT5670_DA_STO_CLK_SEL_SFT 12
|
||||
#define RT5670_DA_MONOL_CLK_SEL_MASK (0xf << 8)
|
||||
#define RT5670_DA_MONOL_CLK_SEL_SFT 8
|
||||
#define RT5670_DA_MONOR_CLK_SEL_MASK (0xf << 4)
|
||||
#define RT5670_DA_MONOR_CLK_SEL_SFT 4
|
||||
#define RT5670_AD_STO1_CLK_SEL_MASK (0xf << 0)
|
||||
#define RT5670_AD_STO1_CLK_SEL_SFT 0
|
||||
|
||||
/* ASRC Control 3 (0x85) */
|
||||
#define RT5670_I2S1_RATE_MASK (0xf << 12)
|
||||
#define RT5670_I2S1_RATE_SFT 12
|
||||
#define RT5670_I2S2_RATE_MASK (0xf << 8)
|
||||
#define RT5670_I2S2_RATE_SFT 8
|
||||
#define RT5670_UP_CLK_SEL_MASK (0xf << 12)
|
||||
#define RT5670_UP_CLK_SEL_SFT 12
|
||||
#define RT5670_DOWN_CLK_SEL_MASK (0xf << 8)
|
||||
#define RT5670_DOWN_CLK_SEL_SFT 8
|
||||
#define RT5670_AD_MONOL_CLK_SEL_MASK (0xf << 4)
|
||||
#define RT5670_AD_MONOL_CLK_SEL_SFT 4
|
||||
#define RT5670_AD_MONOR_CLK_SEL_MASK (0xf << 0)
|
||||
#define RT5670_AD_MONOR_CLK_SEL_SFT 0
|
||||
|
||||
/* ASRC Control 4 (0x89) */
|
||||
#define RT5670_I2S1_PD_MASK (0x7 << 12)
|
||||
@ -1983,6 +1966,21 @@ enum {
|
||||
RT5670_DMIC_DATA_GPIO5,
|
||||
};
|
||||
|
||||
/* filter mask */
|
||||
enum {
|
||||
RT5670_DA_STEREO_FILTER = 0x1,
|
||||
RT5670_DA_MONO_L_FILTER = (0x1 << 1),
|
||||
RT5670_DA_MONO_R_FILTER = (0x1 << 2),
|
||||
RT5670_AD_STEREO_FILTER = (0x1 << 3),
|
||||
RT5670_AD_MONO_L_FILTER = (0x1 << 4),
|
||||
RT5670_AD_MONO_R_FILTER = (0x1 << 5),
|
||||
RT5670_UP_RATE_FILTER = (0x1 << 6),
|
||||
RT5670_DOWN_RATE_FILTER = (0x1 << 7),
|
||||
};
|
||||
|
||||
int rt5670_sel_asrc_clk_src(struct snd_soc_codec *codec,
|
||||
unsigned int filter_mask, unsigned int clk_src);
|
||||
|
||||
struct rt5670_priv {
|
||||
struct snd_soc_codec *codec;
|
||||
struct rt5670_platform_data pdata;
|
||||
|
@ -90,7 +90,6 @@ struct spdif_mixer_control {
|
||||
* @sysclk: system clock for rx clock rate measurement
|
||||
* @dma_params_tx: DMA parameters for transmit channel
|
||||
* @dma_params_rx: DMA parameters for receive channel
|
||||
* @name: driver name
|
||||
*/
|
||||
struct fsl_spdif_priv {
|
||||
struct spdif_mixer_control fsl_spdif_control;
|
||||
@ -109,12 +108,8 @@ struct fsl_spdif_priv {
|
||||
struct clk *sysclk;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_tx;
|
||||
struct snd_dmaengine_dai_dma_data dma_params_rx;
|
||||
|
||||
/* The name space will be allocated dynamically */
|
||||
char name[0];
|
||||
};
|
||||
|
||||
|
||||
/* DPLL locked and lock loss interrupt handler */
|
||||
static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
|
||||
{
|
||||
@ -1169,19 +1164,15 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
if (!np)
|
||||
return -ENODEV;
|
||||
|
||||
spdif_priv = devm_kzalloc(&pdev->dev,
|
||||
sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1,
|
||||
GFP_KERNEL);
|
||||
spdif_priv = devm_kzalloc(&pdev->dev, sizeof(*spdif_priv), GFP_KERNEL);
|
||||
if (!spdif_priv)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(spdif_priv->name, np->name);
|
||||
|
||||
spdif_priv->pdev = pdev;
|
||||
|
||||
/* Initialize this copy of the CPU DAI driver structure */
|
||||
memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
|
||||
spdif_priv->cpu_dai_drv.name = spdif_priv->name;
|
||||
spdif_priv->cpu_dai_drv.name = dev_name(&pdev->dev);
|
||||
|
||||
/* Get the addresses and IRQ */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
@ -1203,7 +1194,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
|
||||
spdif_priv->name, spdif_priv);
|
||||
dev_name(&pdev->dev), spdif_priv);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "could not claim irq %u\n", irq);
|
||||
return ret;
|
||||
|
@ -160,7 +160,7 @@ struct fsl_ssi_soc_data {
|
||||
*/
|
||||
struct fsl_ssi_private {
|
||||
struct regmap *regs;
|
||||
unsigned int irq;
|
||||
int irq;
|
||||
struct snd_soc_dai_driver cpu_dai_drv;
|
||||
|
||||
unsigned int dai_fmt;
|
||||
|
@ -60,6 +60,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
|
||||
data->card.dev = &pdev->dev;
|
||||
data->card.dai_link = &data->dai;
|
||||
data->card.num_links = 1;
|
||||
data->card.owner = THIS_MODULE;
|
||||
|
||||
ret = snd_soc_of_parse_card_name(&data->card, "model");
|
||||
if (ret)
|
||||
|
@ -110,3 +110,14 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
|
||||
platforms with RT5672 audio codec.
|
||||
Say Y if you have such a device
|
||||
If unsure select "N".
|
||||
|
||||
config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
|
||||
tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5645 codec"
|
||||
depends on X86_INTEL_LPSS
|
||||
select SND_SOC_RT5645
|
||||
select SND_SST_MFLD_PLATFORM
|
||||
select SND_SST_IPC_ACPI
|
||||
help
|
||||
This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
|
||||
platforms with RT5645 audio codec.
|
||||
If unsure select "N".
|
||||
|
@ -28,6 +28,7 @@ snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
|
||||
snd-soc-sst-broadwell-objs := broadwell.o
|
||||
snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
|
||||
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
|
||||
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
|
||||
|
||||
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
|
||||
@ -35,6 +36,7 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
|
||||
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
|
||||
|
||||
# DSP driver
|
||||
obj-$(CONFIG_SND_SST_IPC) += sst/
|
||||
|
@ -140,8 +140,6 @@ static struct snd_soc_ops broadwell_rt286_ops = {
|
||||
|
||||
static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_codec *codec = rtd->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
|
||||
struct sst_hsw *broadwell = pdata->dsp;
|
||||
int ret;
|
||||
@ -155,14 +153,6 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* always connected - check HP for jack detect */
|
||||
snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
|
||||
snd_soc_dapm_enable_pin(dapm, "Speaker");
|
||||
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
|
||||
snd_soc_dapm_enable_pin(dapm, "Line Jack");
|
||||
snd_soc_dapm_enable_pin(dapm, "DMIC1");
|
||||
snd_soc_dapm_enable_pin(dapm, "DMIC2");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -132,7 +132,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = runtime->codec;
|
||||
struct snd_soc_dapm_context *dapm = &codec->dapm;
|
||||
struct snd_soc_card *card = runtime->card;
|
||||
const struct snd_soc_dapm_route *custom_map;
|
||||
int num_routes;
|
||||
@ -161,7 +160,7 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic1_map);
|
||||
}
|
||||
|
||||
ret = snd_soc_dapm_add_routes(dapm, custom_map, num_routes);
|
||||
ret = snd_soc_dapm_add_routes(&card->dapm, custom_map, num_routes);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -171,13 +170,8 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
|
||||
return ret;
|
||||
}
|
||||
|
||||
snd_soc_dapm_ignore_suspend(dapm, "HPOL");
|
||||
snd_soc_dapm_ignore_suspend(dapm, "HPOR");
|
||||
|
||||
snd_soc_dapm_ignore_suspend(dapm, "SPOLP");
|
||||
snd_soc_dapm_ignore_suspend(dapm, "SPOLN");
|
||||
snd_soc_dapm_ignore_suspend(dapm, "SPORP");
|
||||
snd_soc_dapm_ignore_suspend(dapm, "SPORN");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
|
||||
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
326
sound/soc/intel/cht_bsw_rt5645.c
Normal file
326
sound/soc/intel/cht_bsw_rt5645.c
Normal file
@ -0,0 +1,326 @@
|
||||
/*
|
||||
* cht-bsw-rt5645.c - ASoc Machine driver for Intel Cherryview-based platforms
|
||||
* Cherrytrail and Braswell, with RT5645 codec.
|
||||
*
|
||||
* Copyright (C) 2015 Intel Corp
|
||||
* Author: Fang, Yang A <yang.a.fang@intel.com>
|
||||
* N,Harshapriya <harshapriya.n@intel.com>
|
||||
* This file is modified from cht_bsw_rt5672.c
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; version 2 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/jack.h>
|
||||
#include "../codecs/rt5645.h"
|
||||
#include "sst-atom-controls.h"
|
||||
|
||||
#define CHT_PLAT_CLK_3_HZ 19200000
|
||||
#define CHT_CODEC_DAI "rt5645-aif1"
|
||||
|
||||
struct cht_mc_private {
|
||||
struct snd_soc_jack hp_jack;
|
||||
struct snd_soc_jack mic_jack;
|
||||
};
|
||||
|
||||
static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < card->num_rtd; i++) {
|
||||
struct snd_soc_pcm_runtime *rtd;
|
||||
|
||||
rtd = card->rtd + i;
|
||||
if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
|
||||
strlen(CHT_CODEC_DAI)))
|
||||
return rtd->codec_dai;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int platform_clock_control(struct snd_soc_dapm_widget *w,
|
||||
struct snd_kcontrol *k, int event)
|
||||
{
|
||||
struct snd_soc_dapm_context *dapm = w->dapm;
|
||||
struct snd_soc_card *card = dapm->card;
|
||||
struct snd_soc_dai *codec_dai;
|
||||
int ret;
|
||||
|
||||
codec_dai = cht_get_codec_dai(card);
|
||||
if (!codec_dai) {
|
||||
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (!SND_SOC_DAPM_EVENT_OFF(event))
|
||||
return 0;
|
||||
|
||||
/* Set codec sysclk source to its internal clock because codec PLL will
|
||||
* be off when idle and MCLK will also be off by ACPI when codec is
|
||||
* runtime suspended. Codec needs clock for jack detection and button
|
||||
* press.
|
||||
*/
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_RCCLK,
|
||||
0, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "can't set codec sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
|
||||
SND_SOC_DAPM_HP("Headphone", NULL),
|
||||
SND_SOC_DAPM_MIC("Headset Mic", NULL),
|
||||
SND_SOC_DAPM_MIC("Int Mic", NULL),
|
||||
SND_SOC_DAPM_SPK("Ext Spk", NULL),
|
||||
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
|
||||
platform_clock_control, SND_SOC_DAPM_POST_PMD),
|
||||
};
|
||||
|
||||
static const struct snd_soc_dapm_route cht_audio_map[] = {
|
||||
{"IN1P", NULL, "Headset Mic"},
|
||||
{"IN1N", NULL, "Headset Mic"},
|
||||
{"DMIC L1", NULL, "Int Mic"},
|
||||
{"DMIC R1", NULL, "Int Mic"},
|
||||
{"Headphone", NULL, "HPOL"},
|
||||
{"Headphone", NULL, "HPOR"},
|
||||
{"Ext Spk", NULL, "SPOL"},
|
||||
{"Ext Spk", NULL, "SPOR"},
|
||||
{"AIF1 Playback", NULL, "ssp2 Tx"},
|
||||
{"ssp2 Tx", NULL, "codec_out0"},
|
||||
{"ssp2 Tx", NULL, "codec_out1"},
|
||||
{"codec_in0", NULL, "ssp2 Rx" },
|
||||
{"codec_in1", NULL, "ssp2 Rx" },
|
||||
{"ssp2 Rx", NULL, "AIF1 Capture"},
|
||||
{"Headphone", NULL, "Platform Clock"},
|
||||
{"Headset Mic", NULL, "Platform Clock"},
|
||||
{"Int Mic", NULL, "Platform Clock"},
|
||||
{"Ext Spk", NULL, "Platform Clock"},
|
||||
};
|
||||
|
||||
static const struct snd_kcontrol_new cht_mc_controls[] = {
|
||||
SOC_DAPM_PIN_SWITCH("Headphone"),
|
||||
SOC_DAPM_PIN_SWITCH("Headset Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Int Mic"),
|
||||
SOC_DAPM_PIN_SWITCH("Ext Spk"),
|
||||
};
|
||||
|
||||
static int cht_aif1_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 *codec_dai = rtd->codec_dai;
|
||||
int ret;
|
||||
|
||||
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */
|
||||
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5645_PLL1_S_MCLK,
|
||||
CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_dai_set_sysclk(codec_dai, RT5645_SCLK_S_PLL1,
|
||||
params_rate(params) * 512, SND_SOC_CLOCK_IN);
|
||||
if (ret < 0) {
|
||||
dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_codec *codec = runtime->codec;
|
||||
struct snd_soc_dai *codec_dai = runtime->codec_dai;
|
||||
struct cht_mc_private *ctx = snd_soc_card_get_drvdata(runtime->card);
|
||||
|
||||
/* Select clk_i2s1_asrc as ASRC clock source */
|
||||
rt5645_sel_asrc_clk_src(codec,
|
||||
RT5645_DA_STEREO_FILTER |
|
||||
RT5645_DA_MONO_L_FILTER |
|
||||
RT5645_DA_MONO_R_FILTER |
|
||||
RT5645_AD_STEREO_FILTER,
|
||||
RT5645_CLK_SEL_I2S1_ASRC);
|
||||
|
||||
/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
|
||||
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
|
||||
if (ret < 0) {
|
||||
dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_jack_new(codec, "Headphone Jack",
|
||||
SND_JACK_HEADPHONE,
|
||||
&ctx->hp_jack);
|
||||
if (ret) {
|
||||
dev_err(runtime->dev, "HP jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = snd_soc_jack_new(codec, "Mic Jack",
|
||||
SND_JACK_MICROPHONE,
|
||||
&ctx->mic_jack);
|
||||
if (ret) {
|
||||
dev_err(runtime->dev, "Mic jack creation failed %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rt5645_set_jack_detect(codec, &ctx->hp_jack, &ctx->mic_jack);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
struct snd_interval *rate = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_RATE);
|
||||
struct snd_interval *channels = hw_param_interval(params,
|
||||
SNDRV_PCM_HW_PARAM_CHANNELS);
|
||||
|
||||
/* The DSP will covert the FE rate to 48k, stereo, 24bits */
|
||||
rate->min = rate->max = 48000;
|
||||
channels->min = channels->max = 2;
|
||||
|
||||
/* set SSP2 to 24-bit */
|
||||
snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT -
|
||||
SNDRV_PCM_HW_PARAM_FIRST_MASK],
|
||||
SNDRV_PCM_FORMAT_S24_LE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int rates_48000[] = {
|
||||
48000,
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list constraints_48000 = {
|
||||
.count = ARRAY_SIZE(rates_48000),
|
||||
.list = rates_48000,
|
||||
};
|
||||
|
||||
static int cht_aif1_startup(struct snd_pcm_substream *substream)
|
||||
{
|
||||
return snd_pcm_hw_constraint_list(substream->runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
&constraints_48000);
|
||||
}
|
||||
|
||||
static struct snd_soc_ops cht_aif1_ops = {
|
||||
.startup = cht_aif1_startup,
|
||||
};
|
||||
|
||||
static struct snd_soc_ops cht_be_ssp2_ops = {
|
||||
.hw_params = cht_aif1_hw_params,
|
||||
};
|
||||
|
||||
static struct snd_soc_dai_link cht_dailink[] = {
|
||||
[MERR_DPCM_AUDIO] = {
|
||||
.name = "Audio Port",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "media-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.ignore_suspend = 1,
|
||||
.dynamic = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &cht_aif1_ops,
|
||||
},
|
||||
[MERR_DPCM_COMPR] = {
|
||||
.name = "Compressed Port",
|
||||
.stream_name = "Compress",
|
||||
.cpu_dai_name = "compress-cpu-dai",
|
||||
.codec_dai_name = "snd-soc-dummy-dai",
|
||||
.codec_name = "snd-soc-dummy",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
},
|
||||
/* CODEC<->CODEC link */
|
||||
/* back ends */
|
||||
{
|
||||
.name = "SSP2-Codec",
|
||||
.be_id = 1,
|
||||
.cpu_dai_name = "ssp2-port",
|
||||
.platform_name = "sst-mfld-platform",
|
||||
.no_pcm = 1,
|
||||
.codec_dai_name = "rt5645-aif1",
|
||||
.codec_name = "i2c-10EC5645:00",
|
||||
.dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
|
||||
| SND_SOC_DAIFMT_CBS_CFS,
|
||||
.init = cht_codec_init,
|
||||
.be_hw_params_fixup = cht_codec_fixup,
|
||||
.ignore_suspend = 1,
|
||||
.dpcm_playback = 1,
|
||||
.dpcm_capture = 1,
|
||||
.ops = &cht_be_ssp2_ops,
|
||||
},
|
||||
};
|
||||
|
||||
/* SoC card */
|
||||
static struct snd_soc_card snd_soc_card_cht = {
|
||||
.name = "chtrt5645",
|
||||
.dai_link = cht_dailink,
|
||||
.num_links = ARRAY_SIZE(cht_dailink),
|
||||
.dapm_widgets = cht_dapm_widgets,
|
||||
.num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
|
||||
.dapm_routes = cht_audio_map,
|
||||
.num_dapm_routes = ARRAY_SIZE(cht_audio_map),
|
||||
.controls = cht_mc_controls,
|
||||
.num_controls = ARRAY_SIZE(cht_mc_controls),
|
||||
};
|
||||
|
||||
static int snd_cht_mc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret_val = 0;
|
||||
struct cht_mc_private *drv;
|
||||
|
||||
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
|
||||
if (!drv)
|
||||
return -ENOMEM;
|
||||
|
||||
snd_soc_card_cht.dev = &pdev->dev;
|
||||
snd_soc_card_set_drvdata(&snd_soc_card_cht, drv);
|
||||
ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
|
||||
if (ret_val) {
|
||||
dev_err(&pdev->dev,
|
||||
"snd_soc_register_card failed %d\n", ret_val);
|
||||
return ret_val;
|
||||
}
|
||||
platform_set_drvdata(pdev, &snd_soc_card_cht);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static struct platform_driver snd_cht_mc_driver = {
|
||||
.driver = {
|
||||
.name = "cht-bsw-rt5645",
|
||||
.pm = &snd_soc_pm_ops,
|
||||
},
|
||||
.probe = snd_cht_mc_probe,
|
||||
};
|
||||
|
||||
module_platform_driver(snd_cht_mc_driver)
|
||||
|
||||
MODULE_DESCRIPTION("ASoC Intel(R) Braswell Machine driver");
|
||||
MODULE_AUTHOR("Fang, Yang A,N,Harshapriya");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:cht-bsw-rt5645");
|
@ -140,6 +140,7 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
|
||||
{
|
||||
int ret;
|
||||
struct snd_soc_dai *codec_dai = runtime->codec_dai;
|
||||
struct snd_soc_codec *codec = codec_dai->codec;
|
||||
|
||||
/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
|
||||
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
|
||||
@ -148,6 +149,19 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Select codec ASRC clock source to track I2S1 clock, because codec
|
||||
* is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot
|
||||
* be supported by RT5672. Otherwise, ASRC will be disabled and cause
|
||||
* noise.
|
||||
*/
|
||||
rt5670_sel_asrc_clk_src(codec,
|
||||
RT5670_DA_STEREO_FILTER
|
||||
| RT5670_DA_MONO_L_FILTER
|
||||
| RT5670_DA_MONO_R_FILTER
|
||||
| RT5670_AD_STEREO_FILTER
|
||||
| RT5670_AD_MONO_L_FILTER
|
||||
| RT5670_AD_MONO_R_FILTER,
|
||||
RT5670_CLK_SEL_I2S1_ASRC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -320,11 +320,6 @@ static struct snd_pcm_ops sst_byt_pcm_ops = {
|
||||
.mmap = sst_byt_pcm_mmap,
|
||||
};
|
||||
|
||||
static void sst_byt_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -403,7 +398,6 @@ static struct snd_soc_platform_driver byt_soc_platform = {
|
||||
.remove = sst_byt_pcm_remove,
|
||||
.ops = &sst_byt_pcm_ops,
|
||||
.pcm_new = sst_byt_pcm_new,
|
||||
.pcm_free = sst_byt_pcm_free,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver byt_dai_component = {
|
||||
|
@ -410,8 +410,7 @@ void sst_dsp_free(struct sst_dsp *sst)
|
||||
if (sst->ops->free)
|
||||
sst->ops->free(sst);
|
||||
|
||||
if (sst->dma)
|
||||
sst_dma_free(sst->dma);
|
||||
sst_dma_free(sst->dma);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sst_dsp_free);
|
||||
|
||||
|
@ -791,6 +791,7 @@ int sst_module_alloc_blocks(struct sst_module *module)
|
||||
struct sst_block_allocator ba;
|
||||
int ret;
|
||||
|
||||
memset(&ba, 0, sizeof(ba));
|
||||
ba.size = module->size;
|
||||
ba.type = module->type;
|
||||
ba.offset = module->offset;
|
||||
@ -864,6 +865,7 @@ int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
|
||||
if (module->persistent_size == 0)
|
||||
return 0;
|
||||
|
||||
memset(&ba, 0, sizeof(ba));
|
||||
ba.size = module->persistent_size;
|
||||
ba.type = SST_MEM_DRAM;
|
||||
|
||||
|
@ -306,7 +306,7 @@ static void hsw_reset(struct sst_dsp *sst)
|
||||
static int hsw_set_dsp_D0(struct sst_dsp *sst)
|
||||
{
|
||||
int tries = 10;
|
||||
u32 reg;
|
||||
u32 reg, fw_dump_bit;
|
||||
|
||||
/* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
|
||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
|
||||
@ -368,7 +368,9 @@ finish:
|
||||
can't be accessed, please enable each block before accessing. */
|
||||
reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
|
||||
writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
/* for D0, always enable the block(DSRAM[0]) used for FW dump */
|
||||
fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
|
||||
writel(reg & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
|
||||
|
||||
/* disable DMA finish function for SSP0 & SSP1 */
|
||||
@ -491,6 +493,7 @@ static const struct sst_sram_shift sram_shift[] = {
|
||||
{SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
|
||||
{SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
|
||||
};
|
||||
|
||||
static u32 hsw_block_get_bit(struct sst_mem_block *block)
|
||||
{
|
||||
u32 bit = 0, shift = 0, index;
|
||||
@ -587,7 +590,9 @@ static int hsw_block_disable(struct sst_mem_block *block)
|
||||
|
||||
val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
bit = hsw_block_get_bit(block);
|
||||
writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
/* don't disable DSRAM[0], keep it always enable for FW dump*/
|
||||
if (bit != (1 << SST_VDRTCL0_DSRAMPGE_SHIFT))
|
||||
writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
|
||||
/* wait 18 DSP clock ticks */
|
||||
udelay(10);
|
||||
@ -612,7 +617,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
|
||||
const struct sst_adsp_memregion *region;
|
||||
struct device *dev;
|
||||
int ret = -ENODEV, i, j, region_count;
|
||||
u32 offset, size;
|
||||
u32 offset, size, fw_dump_bit;
|
||||
|
||||
dev = sst->dma_dev;
|
||||
|
||||
@ -669,9 +674,11 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
|
||||
}
|
||||
}
|
||||
|
||||
/* always enable the block(DSRAM[0]) used for FW dump */
|
||||
fw_dump_bit = 1 << SST_VDRTCL0_DSRAMPGE_SHIFT;
|
||||
/* set default power gating control, enable power gating control for all blocks. that is,
|
||||
can't be accessed, please enable each block before accessing. */
|
||||
writel(0xffffffff, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
writel(0xffffffff & ~fw_dump_bit, sst->addr.pci_cfg + SST_VDRTCTL0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -94,6 +94,8 @@
|
||||
/* Mailbox */
|
||||
#define IPC_MAX_MAILBOX_BYTES 256
|
||||
|
||||
#define INVALID_STREAM_HW_ID 0xffffffff
|
||||
|
||||
/* Global Message - Types and Replies */
|
||||
enum ipc_glb_type {
|
||||
IPC_GLB_GET_FW_VERSION = 0, /* Retrieves firmware version */
|
||||
@ -275,7 +277,6 @@ struct sst_hsw {
|
||||
/* FW config */
|
||||
struct sst_hsw_ipc_fw_ready fw_ready;
|
||||
struct sst_hsw_ipc_fw_version version;
|
||||
struct sst_module *scratch;
|
||||
bool fw_done;
|
||||
struct sst_fw *sst_fw;
|
||||
|
||||
@ -337,12 +338,6 @@ static inline u32 msg_get_stage_type(u32 msg)
|
||||
return (msg & IPC_STG_TYPE_MASK) >> IPC_STG_TYPE_SHIFT;
|
||||
}
|
||||
|
||||
static inline u32 msg_set_stage_type(u32 msg, u32 type)
|
||||
{
|
||||
return (msg & ~IPC_STG_TYPE_MASK) +
|
||||
(type << IPC_STG_TYPE_SHIFT);
|
||||
}
|
||||
|
||||
static inline u32 msg_get_stream_id(u32 msg)
|
||||
{
|
||||
return (msg & IPC_STR_ID_MASK) >> IPC_STR_ID_SHIFT;
|
||||
@ -969,45 +964,6 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw,
|
||||
}
|
||||
|
||||
/* Mixer Controls */
|
||||
int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
|
||||
u32 stage_id, u32 channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sst_hsw_stream_get_volume(hsw, stream, stage_id, channel,
|
||||
&stream->mute_volume[channel]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n",
|
||||
stream->reply.stream_hw_id, channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
stream->mute[channel] = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
|
||||
u32 stage_id, u32 channel)
|
||||
|
||||
{
|
||||
int ret;
|
||||
|
||||
stream->mute[channel] = 0;
|
||||
ret = sst_hsw_stream_set_volume(hsw, stream, stage_id, channel,
|
||||
stream->mute_volume[channel]);
|
||||
if (ret < 0) {
|
||||
dev_err(hsw->dev, "error: can't unmute stream %d channel %d\n",
|
||||
stream->reply.stream_hw_id, channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
|
||||
u32 stage_id, u32 channel, u32 *volume)
|
||||
{
|
||||
@ -1021,17 +977,6 @@ int sst_hsw_stream_get_volume(struct sst_hsw *hsw, struct sst_hsw_stream *stream
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u64 curve_duration,
|
||||
enum sst_hsw_volume_curve curve)
|
||||
{
|
||||
/* curve duration in steps of 100ns */
|
||||
stream->vol_req.curve_duration = curve_duration;
|
||||
stream->vol_req.curve_type = curve;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* stream volume */
|
||||
int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume)
|
||||
@ -1083,42 +1028,6 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sst_hsw_mixer_get_volume(hsw, stage_id, channel,
|
||||
&hsw->mute_volume[channel]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n",
|
||||
channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hsw->mute[channel] = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sst_hsw_mixer_set_volume(hsw, stage_id, channel,
|
||||
hsw->mixer_info.volume_register_address[channel]);
|
||||
if (ret < 0) {
|
||||
dev_err(hsw->dev, "error: failed to unmute mixer channel %d\n",
|
||||
channel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
hsw->mute[channel] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
|
||||
u32 *volume)
|
||||
{
|
||||
@ -1132,16 +1041,6 @@ int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw,
|
||||
u64 curve_duration, enum sst_hsw_volume_curve curve)
|
||||
{
|
||||
/* curve duration in steps of 100ns */
|
||||
hsw->curve_duration = curve_duration;
|
||||
hsw->curve_type = curve;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* global mixer volume */
|
||||
int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
|
||||
u32 volume)
|
||||
@ -1208,6 +1107,7 @@ struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
|
||||
return NULL;
|
||||
|
||||
spin_lock_irqsave(&sst->spinlock, flags);
|
||||
stream->reply.stream_hw_id = INVALID_STREAM_HW_ID;
|
||||
list_add(&stream->node, &hsw->stream_list);
|
||||
stream->notify_position = notify_position;
|
||||
stream->pdata = data;
|
||||
@ -1449,48 +1349,6 @@ int sst_hsw_stream_commit(struct sst_hsw *hsw, struct sst_hsw_stream *stream)
|
||||
|
||||
/* Stream Information - these calls could be inline but we want the IPC
|
||||
ABI to be opaque to client PCM drivers to cope with any future ABI changes */
|
||||
int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream)
|
||||
{
|
||||
return stream->reply.stream_hw_id;
|
||||
}
|
||||
|
||||
int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream)
|
||||
{
|
||||
return stream->reply.mixer_hw_id;
|
||||
}
|
||||
|
||||
u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream)
|
||||
{
|
||||
return stream->reply.read_position_register_address;
|
||||
}
|
||||
|
||||
u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream)
|
||||
{
|
||||
return stream->reply.presentation_position_register_address;
|
||||
}
|
||||
|
||||
u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 channel)
|
||||
{
|
||||
if (channel >= 2)
|
||||
return 0;
|
||||
|
||||
return stream->reply.peak_meter_register_address[channel];
|
||||
}
|
||||
|
||||
u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 channel)
|
||||
{
|
||||
if (channel >= 2)
|
||||
return 0;
|
||||
|
||||
return stream->reply.volume_register_address[channel];
|
||||
}
|
||||
|
||||
int sst_hsw_mixer_get_info(struct sst_hsw *hsw)
|
||||
{
|
||||
struct sst_hsw_ipc_stream_info_reply *reply;
|
||||
@ -1628,30 +1486,6 @@ u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
|
||||
return ppos;
|
||||
}
|
||||
|
||||
int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 stage_id, u32 position)
|
||||
{
|
||||
u32 header;
|
||||
int ret;
|
||||
|
||||
trace_stream_write_position(stream->reply.stream_hw_id, position);
|
||||
|
||||
header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
|
||||
IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
|
||||
header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
|
||||
header |= (IPC_STG_SET_WRITE_POSITION << IPC_STG_TYPE_SHIFT);
|
||||
header |= (stage_id << IPC_STG_ID_SHIFT);
|
||||
stream->wpos.position = position;
|
||||
|
||||
ret = ipc_tx_message_nowait(hsw, header, &stream->wpos,
|
||||
sizeof(stream->wpos));
|
||||
if (ret < 0)
|
||||
dev_err(hsw->dev, "error: stream %d set position %d failed\n",
|
||||
stream->reply.stream_hw_id, position);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* physical BE config */
|
||||
int sst_hsw_device_set_config(struct sst_hsw *hsw,
|
||||
enum sst_hsw_device_id dev, enum sst_hsw_device_mclk mclk,
|
||||
@ -2132,7 +1966,6 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
|
||||
dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
|
||||
hsw->dx_context, hsw->dx_context_paddr);
|
||||
sst_dsp_free(hsw->dsp);
|
||||
kfree(hsw->scratch);
|
||||
kthread_stop(hsw->tx_thread);
|
||||
kfree(hsw->msg);
|
||||
}
|
||||
|
@ -376,32 +376,17 @@ int sst_hsw_fw_get_version(struct sst_hsw *hsw,
|
||||
u32 create_channel_map(enum sst_hsw_channel_config config);
|
||||
|
||||
/* Stream Mixer Controls - */
|
||||
int sst_hsw_stream_mute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
|
||||
u32 stage_id, u32 channel);
|
||||
int sst_hsw_stream_unmute(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
|
||||
u32 stage_id, u32 channel);
|
||||
|
||||
int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 volume);
|
||||
int sst_hsw_stream_get_volume(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 stage_id, u32 channel, u32 *volume);
|
||||
|
||||
int sst_hsw_stream_set_volume_curve(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u64 curve_duration,
|
||||
enum sst_hsw_volume_curve curve);
|
||||
|
||||
/* Global Mixer Controls - */
|
||||
int sst_hsw_mixer_mute(struct sst_hsw *hsw, u32 stage_id, u32 channel);
|
||||
int sst_hsw_mixer_unmute(struct sst_hsw *hsw, u32 stage_id, u32 channel);
|
||||
|
||||
int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
|
||||
u32 volume);
|
||||
int sst_hsw_mixer_get_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
|
||||
u32 *volume);
|
||||
|
||||
int sst_hsw_mixer_set_volume_curve(struct sst_hsw *hsw,
|
||||
u64 curve_duration, enum sst_hsw_volume_curve curve);
|
||||
|
||||
/* Stream API */
|
||||
struct sst_hsw_stream *sst_hsw_stream_new(struct sst_hsw *hsw, int id,
|
||||
u32 (*get_write_position)(struct sst_hsw_stream *stream, void *data),
|
||||
@ -440,18 +425,6 @@ int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 offset, u32 size);
|
||||
int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 offset, u32 size);
|
||||
int sst_hsw_stream_get_hw_id(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream);
|
||||
int sst_hsw_stream_get_mixer_id(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream);
|
||||
u32 sst_hsw_stream_get_read_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream);
|
||||
u32 sst_hsw_stream_get_pointer_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream);
|
||||
u32 sst_hsw_stream_get_peak_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 channel);
|
||||
u32 sst_hsw_stream_get_vol_reg(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 channel);
|
||||
int sst_hsw_mixer_get_info(struct sst_hsw *hsw);
|
||||
|
||||
/* Stream ALSA trigger operations */
|
||||
@ -466,8 +439,6 @@ int sst_hsw_stream_get_read_pos(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 *position);
|
||||
int sst_hsw_stream_get_write_pos(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 *position);
|
||||
int sst_hsw_stream_set_write_position(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream, u32 stage_id, u32 position);
|
||||
u32 sst_hsw_get_dsp_position(struct sst_hsw *hsw,
|
||||
struct sst_hsw_stream *stream);
|
||||
u64 sst_hsw_get_dsp_presentation_position(struct sst_hsw *hsw,
|
||||
@ -481,8 +452,6 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
|
||||
/* DX Config */
|
||||
int sst_hsw_dx_set_state(struct sst_hsw *hsw,
|
||||
enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx);
|
||||
int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
|
||||
u32 *offset, u32 *size, u32 *source);
|
||||
|
||||
/* init */
|
||||
int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
|
||||
|
@ -78,7 +78,6 @@ static const u32 volume_map[] = {
|
||||
#define HSW_PCM_DAI_ID_OFFLOAD0 1
|
||||
#define HSW_PCM_DAI_ID_OFFLOAD1 2
|
||||
#define HSW_PCM_DAI_ID_LOOPBACK 3
|
||||
#define HSW_PCM_DAI_ID_CAPTURE 4
|
||||
|
||||
|
||||
static const struct snd_pcm_hardware hsw_pcm_hardware = {
|
||||
@ -99,6 +98,7 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
|
||||
|
||||
struct hsw_pcm_module_map {
|
||||
int dai_id;
|
||||
int stream;
|
||||
enum sst_hsw_module_id mod_id;
|
||||
};
|
||||
|
||||
@ -119,8 +119,9 @@ struct hsw_pcm_data {
|
||||
};
|
||||
|
||||
enum hsw_pm_state {
|
||||
HSW_PM_STATE_D3 = 0,
|
||||
HSW_PM_STATE_D0 = 1,
|
||||
HSW_PM_STATE_D0 = 0,
|
||||
HSW_PM_STATE_RTD3 = 1,
|
||||
HSW_PM_STATE_D3 = 2,
|
||||
};
|
||||
|
||||
/* private data for the driver */
|
||||
@ -135,7 +136,17 @@ struct hsw_priv_data {
|
||||
struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
|
||||
|
||||
/* DAI data */
|
||||
struct hsw_pcm_data pcm[HSW_PCM_COUNT];
|
||||
struct hsw_pcm_data pcm[HSW_PCM_COUNT][2];
|
||||
};
|
||||
|
||||
|
||||
/* static mappings between PCMs and modules - may be dynamic in future */
|
||||
static struct hsw_pcm_module_map mod_map[] = {
|
||||
{HSW_PCM_DAI_ID_SYSTEM, 0, SST_HSW_MODULE_PCM_SYSTEM},
|
||||
{HSW_PCM_DAI_ID_OFFLOAD0, 0, SST_HSW_MODULE_PCM},
|
||||
{HSW_PCM_DAI_ID_OFFLOAD1, 0, SST_HSW_MODULE_PCM},
|
||||
{HSW_PCM_DAI_ID_LOOPBACK, 1, SST_HSW_MODULE_PCM_REFERENCE},
|
||||
{HSW_PCM_DAI_ID_SYSTEM, 1, SST_HSW_MODULE_PCM_CAPTURE},
|
||||
};
|
||||
|
||||
static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data);
|
||||
@ -168,9 +179,14 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(platform);
|
||||
struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
u32 volume;
|
||||
int dai, stream;
|
||||
|
||||
dai = mod_map[mc->reg].dai_id;
|
||||
stream = mod_map[mc->reg].stream;
|
||||
pcm_data = &pdata->pcm[dai][stream];
|
||||
|
||||
mutex_lock(&pcm_data->mutex);
|
||||
pm_runtime_get_sync(pdata->dev);
|
||||
@ -212,9 +228,14 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
|
||||
(struct soc_mixer_control *)kcontrol->private_value;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(platform);
|
||||
struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
u32 volume;
|
||||
int dai, stream;
|
||||
|
||||
dai = mod_map[mc->reg].dai_id;
|
||||
stream = mod_map[mc->reg].stream;
|
||||
pcm_data = &pdata->pcm[dai][stream];
|
||||
|
||||
mutex_lock(&pcm_data->mutex);
|
||||
pm_runtime_get_sync(pdata->dev);
|
||||
@ -309,7 +330,7 @@ static const struct snd_kcontrol_new hsw_volume_controls[] = {
|
||||
ARRAY_SIZE(volume_map) - 1, 0,
|
||||
hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
|
||||
/* Mic Capture volume */
|
||||
SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
|
||||
SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
|
||||
ARRAY_SIZE(volume_map) - 1, 0,
|
||||
hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
|
||||
};
|
||||
@ -353,7 +374,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
struct sst_module *module_data;
|
||||
struct sst_dsp *dsp;
|
||||
@ -362,7 +383,10 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
|
||||
enum sst_hsw_stream_path_id path_id;
|
||||
u32 rate, bits, map, pages, module_id;
|
||||
u8 channels;
|
||||
int ret;
|
||||
int ret, dai;
|
||||
|
||||
dai = mod_map[rtd->cpu_dai->id].dai_id;
|
||||
pcm_data = &pdata->pcm[dai][substream->stream];
|
||||
|
||||
/* check if we are being called a subsequent time */
|
||||
if (pcm_data->allocated) {
|
||||
@ -552,8 +576,12 @@ static int hsw_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
int dai;
|
||||
|
||||
dai = mod_map[rtd->cpu_dai->id].dai_id;
|
||||
pcm_data = &pdata->pcm[dai][substream->stream];
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
@ -597,11 +625,16 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
snd_pcm_uframes_t offset;
|
||||
uint64_t ppos;
|
||||
u32 position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
|
||||
u32 position;
|
||||
int dai;
|
||||
|
||||
dai = mod_map[rtd->cpu_dai->id].dai_id;
|
||||
pcm_data = &pdata->pcm[dai][substream->stream];
|
||||
position = sst_hsw_get_dsp_position(hsw, pcm_data->stream);
|
||||
|
||||
offset = bytes_to_frames(runtime, position);
|
||||
ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
|
||||
@ -618,8 +651,10 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
int dai;
|
||||
|
||||
pcm_data = &pdata->pcm[rtd->cpu_dai->id];
|
||||
dai = mod_map[rtd->cpu_dai->id].dai_id;
|
||||
pcm_data = &pdata->pcm[dai][substream->stream];
|
||||
|
||||
mutex_lock(&pcm_data->mutex);
|
||||
pm_runtime_get_sync(pdata->dev);
|
||||
@ -648,9 +683,12 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
struct hsw_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct hsw_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
int ret;
|
||||
int ret, dai;
|
||||
|
||||
dai = mod_map[rtd->cpu_dai->id].dai_id;
|
||||
pcm_data = &pdata->pcm[dai][substream->stream];
|
||||
|
||||
mutex_lock(&pcm_data->mutex);
|
||||
ret = sst_hsw_stream_reset(hsw, pcm_data->stream);
|
||||
@ -685,15 +723,6 @@ static struct snd_pcm_ops hsw_pcm_ops = {
|
||||
.page = snd_pcm_sgbuf_ops_page,
|
||||
};
|
||||
|
||||
/* static mappings between PCMs and modules - may be dynamic in future */
|
||||
static struct hsw_pcm_module_map mod_map[] = {
|
||||
{HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
|
||||
{HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
|
||||
{HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
|
||||
{HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
|
||||
{HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
|
||||
};
|
||||
|
||||
static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
|
||||
{
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
@ -701,7 +730,7 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
|
||||
pcm_data = &pdata->pcm[i];
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
|
||||
/* create new runtime module, use same offset if recreated */
|
||||
pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
|
||||
@ -716,7 +745,7 @@ static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
|
||||
|
||||
err:
|
||||
for (--i; i >= 0; i--) {
|
||||
pcm_data = &pdata->pcm[i];
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
sst_hsw_runtime_module_free(pcm_data->runtime);
|
||||
}
|
||||
|
||||
@ -729,17 +758,12 @@ static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
|
||||
pcm_data = &pdata->pcm[i];
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
|
||||
sst_hsw_runtime_module_free(pcm_data->runtime);
|
||||
}
|
||||
}
|
||||
|
||||
static void hsw_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_pcm *pcm = rtd->pcm;
|
||||
@ -762,7 +786,10 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream)
|
||||
priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_PLAYBACK].hsw_pcm = pcm;
|
||||
if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream)
|
||||
priv_data->pcm[rtd->cpu_dai->id][SNDRV_PCM_STREAM_CAPTURE].hsw_pcm = pcm;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -871,10 +898,9 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
|
||||
/* allocate DSP buffer page tables */
|
||||
for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
|
||||
|
||||
mutex_init(&priv_data->pcm[i].mutex);
|
||||
|
||||
/* playback */
|
||||
if (hsw_dais[i].playback.channels_min) {
|
||||
mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_PLAYBACK].mutex);
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
|
||||
PAGE_SIZE, &priv_data->dmab[i][0]);
|
||||
if (ret < 0)
|
||||
@ -883,6 +909,7 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
|
||||
|
||||
/* capture */
|
||||
if (hsw_dais[i].capture.channels_min) {
|
||||
mutex_init(&priv_data->pcm[i][SNDRV_PCM_STREAM_CAPTURE].mutex);
|
||||
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, dma_dev,
|
||||
PAGE_SIZE, &priv_data->dmab[i][1]);
|
||||
if (ret < 0)
|
||||
@ -936,7 +963,6 @@ static struct snd_soc_platform_driver hsw_soc_platform = {
|
||||
.remove = hsw_pcm_remove,
|
||||
.ops = &hsw_pcm_ops,
|
||||
.pcm_new = hsw_pcm_new,
|
||||
.pcm_free = hsw_pcm_free,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver hsw_dai_component = {
|
||||
@ -1010,12 +1036,12 @@ static int hsw_pcm_runtime_suspend(struct device *dev)
|
||||
struct hsw_priv_data *pdata = dev_get_drvdata(dev);
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
|
||||
if (pdata->pm_state == HSW_PM_STATE_D3)
|
||||
if (pdata->pm_state >= HSW_PM_STATE_RTD3)
|
||||
return 0;
|
||||
|
||||
sst_hsw_dsp_runtime_suspend(hsw);
|
||||
sst_hsw_dsp_runtime_sleep(hsw);
|
||||
pdata->pm_state = HSW_PM_STATE_D3;
|
||||
pdata->pm_state = HSW_PM_STATE_RTD3;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1026,7 +1052,7 @@ static int hsw_pcm_runtime_resume(struct device *dev)
|
||||
struct sst_hsw *hsw = pdata->hsw;
|
||||
int ret;
|
||||
|
||||
if (pdata->pm_state == HSW_PM_STATE_D0)
|
||||
if (pdata->pm_state != HSW_PM_STATE_RTD3)
|
||||
return 0;
|
||||
|
||||
ret = sst_hsw_dsp_load(hsw);
|
||||
@ -1066,7 +1092,7 @@ static void hsw_pcm_complete(struct device *dev)
|
||||
struct hsw_pcm_data *pcm_data;
|
||||
int i, err;
|
||||
|
||||
if (pdata->pm_state == HSW_PM_STATE_D0)
|
||||
if (pdata->pm_state != HSW_PM_STATE_D3)
|
||||
return;
|
||||
|
||||
err = sst_hsw_dsp_load(hsw);
|
||||
@ -1081,8 +1107,8 @@ static void hsw_pcm_complete(struct device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
|
||||
pcm_data = &pdata->pcm[i];
|
||||
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
|
||||
if (!pcm_data->substream)
|
||||
continue;
|
||||
@ -1114,41 +1140,42 @@ static int hsw_pcm_prepare(struct device *dev)
|
||||
|
||||
if (pdata->pm_state == HSW_PM_STATE_D3)
|
||||
return 0;
|
||||
/* suspend all active streams */
|
||||
for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
|
||||
pcm_data = &pdata->pcm[i];
|
||||
else if (pdata->pm_state == HSW_PM_STATE_D0) {
|
||||
/* suspend all active streams */
|
||||
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
|
||||
if (!pcm_data->substream)
|
||||
continue;
|
||||
dev_dbg(dev, "suspending pcm %d\n", i);
|
||||
snd_pcm_suspend_all(pcm_data->hsw_pcm);
|
||||
if (!pcm_data->substream)
|
||||
continue;
|
||||
dev_dbg(dev, "suspending pcm %d\n", i);
|
||||
snd_pcm_suspend_all(pcm_data->hsw_pcm);
|
||||
|
||||
/* We need to wait until the DSP FW stops the streams */
|
||||
msleep(2);
|
||||
/* We need to wait until the DSP FW stops the streams */
|
||||
msleep(2);
|
||||
}
|
||||
|
||||
/* preserve persistent memory */
|
||||
for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
|
||||
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
|
||||
|
||||
if (!pcm_data->substream)
|
||||
continue;
|
||||
|
||||
dev_dbg(dev, "saving context pcm %d\n", i);
|
||||
err = sst_module_runtime_save(pcm_data->runtime,
|
||||
&pcm_data->context);
|
||||
if (err < 0)
|
||||
dev_err(dev, "failed to save context for PCM %d\n", i);
|
||||
}
|
||||
/* enter D3 state and stall */
|
||||
sst_hsw_dsp_runtime_suspend(hsw);
|
||||
/* put the DSP to sleep */
|
||||
sst_hsw_dsp_runtime_sleep(hsw);
|
||||
}
|
||||
|
||||
snd_soc_suspend(pdata->soc_card->dev);
|
||||
snd_soc_poweroff(pdata->soc_card->dev);
|
||||
|
||||
/* enter D3 state and stall */
|
||||
sst_hsw_dsp_runtime_suspend(hsw);
|
||||
|
||||
/* preserve persistent memory */
|
||||
for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
|
||||
pcm_data = &pdata->pcm[i];
|
||||
|
||||
if (!pcm_data->substream)
|
||||
continue;
|
||||
|
||||
dev_dbg(dev, "saving context pcm %d\n", i);
|
||||
err = sst_module_runtime_save(pcm_data->runtime,
|
||||
&pcm_data->context);
|
||||
if (err < 0)
|
||||
dev_err(dev, "failed to save context for PCM %d\n", i);
|
||||
}
|
||||
|
||||
/* put the DSP to sleep */
|
||||
sst_hsw_dsp_runtime_sleep(hsw);
|
||||
pdata->pm_state = HSW_PM_STATE_D3;
|
||||
|
||||
return 0;
|
||||
|
@ -643,12 +643,6 @@ static struct snd_pcm_ops sst_platform_ops = {
|
||||
.pointer = sst_platform_pcm_pointer,
|
||||
};
|
||||
|
||||
static void sst_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
dev_dbg(pcm->dev, "sst_pcm_free called\n");
|
||||
snd_pcm_lib_preallocate_free_for_all(pcm);
|
||||
}
|
||||
|
||||
static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
||||
{
|
||||
struct snd_soc_dai *dai = rtd->cpu_dai;
|
||||
@ -679,7 +673,6 @@ static struct snd_soc_platform_driver sst_soc_platform_drv = {
|
||||
.ops = &sst_platform_ops,
|
||||
.compr_ops = &sst_platform_compr_ops,
|
||||
.pcm_new = sst_pcm_new,
|
||||
.pcm_free = sst_pcm_free,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver sst_component = {
|
||||
|
@ -352,6 +352,8 @@ static struct sst_machines sst_acpi_bytcr[] = {
|
||||
static struct sst_machines sst_acpi_chv[] = {
|
||||
{"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin",
|
||||
&chv_platform_data },
|
||||
{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
|
||||
&chv_platform_data },
|
||||
{},
|
||||
};
|
||||
|
||||
|
@ -324,8 +324,7 @@ void sst_firmware_load_cb(const struct firmware *fw, void *context)
|
||||
|
||||
if (ctx->sst_state != SST_RESET ||
|
||||
ctx->fw_in_mem != NULL) {
|
||||
if (fw != NULL)
|
||||
release_firmware(fw);
|
||||
release_firmware(fw);
|
||||
mutex_unlock(&ctx->sst_lock);
|
||||
return;
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
@ -83,6 +85,8 @@
|
||||
#define JZ_AIC_I2S_STATUS_BUSY BIT(2)
|
||||
|
||||
#define JZ_AIC_CLK_DIV_MASK 0xf
|
||||
#define I2SDIV_DV_SHIFT 8
|
||||
#define I2SDIV_DV_MASK (0xf << I2SDIV_DV_SHIFT)
|
||||
|
||||
struct jz4740_i2s {
|
||||
struct resource *mem;
|
||||
@ -237,10 +241,14 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
{
|
||||
struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai);
|
||||
unsigned int sample_size;
|
||||
uint32_t ctrl;
|
||||
uint32_t ctrl, div_reg;
|
||||
int div;
|
||||
|
||||
ctrl = jz4740_i2s_read(i2s, JZ_REG_AIC_CTRL);
|
||||
|
||||
div_reg = jz4740_i2s_read(i2s, JZ_REG_AIC_CLK_DIV);
|
||||
div = clk_get_rate(i2s->clk_i2s) / (64 * params_rate(params));
|
||||
|
||||
switch (params_format(params)) {
|
||||
case SNDRV_PCM_FORMAT_S8:
|
||||
sample_size = 0;
|
||||
@ -264,7 +272,10 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream,
|
||||
ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET;
|
||||
}
|
||||
|
||||
div_reg &= ~I2SDIV_DV_MASK;
|
||||
div_reg |= (div - 1) << I2SDIV_DV_SHIFT;
|
||||
jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl);
|
||||
jz4740_i2s_write(i2s, JZ_REG_AIC_CLK_DIV, div_reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -415,6 +426,13 @@ static const struct snd_soc_component_driver jz4740_i2s_component = {
|
||||
.name = "jz4740-i2s",
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id jz4740_of_matches[] = {
|
||||
{ .compatible = "ingenic,jz4740-i2s" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
#endif
|
||||
|
||||
static int jz4740_i2s_dev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct jz4740_i2s *i2s;
|
||||
@ -455,6 +473,7 @@ static struct platform_driver jz4740_i2s_driver = {
|
||||
.probe = jz4740_i2s_dev_probe,
|
||||
.driver = {
|
||||
.name = "jz4740-i2s",
|
||||
.of_match_table = of_match_ptr(jz4740_of_matches)
|
||||
},
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user