Commit d1612c80 authored by Takashi Iwai's avatar Takashi Iwai

Merge tag 'asoc-v3.20-3' of...

Merge tag 'asoc-v3.20-3' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound into for-next

ASoC: Final updates for v3.20

A few more updates for v3.20 that have accumilated over the second half
of last week.  One new (relatively simple) driver for the Maxim
max98357a and some other driver specific fixes and enhancements.  I did
apply a few patches that haven't been in -next just now before sending
this, all fixes except for one simple device ID addition patch.
parents d34890cf f4c2e9bc
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";
};
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>;
};
......@@ -61,9 +61,12 @@ i2s0: i2s@03830000 {
reg = <0x03830000 0x100>;
clocks = <&clock_audss EXYNOS_I2S_BUS>;
clock-names = "iis";
#clock-cells = <1>;
clock-output-names = "i2s_cdclk0";
dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>;
dma-names = "tx", "rx", "tx-sec";
samsung,idma-addr = <0x03000000>;
#sound-dai-cells = <1>;
status = "disabled";
};
......@@ -368,22 +371,28 @@ port@0 {
};
i2s1: i2s@13960000 {
compatible = "samsung,s5pv210-i2s";
compatible = "samsung,s3c6410-i2s";
reg = <0x13960000 0x100>;
clocks = <&clock CLK_I2S1>;
clock-names = "iis";
#clock-cells = <1>;
clock-output-names = "i2s_cdclk1";
dmas = <&pdma1 12>, <&pdma1 11>;
dma-names = "tx", "rx";
#sound-dai-cells = <1>;
status = "disabled";
};
i2s2: i2s@13970000 {
compatible = "samsung,s5pv210-i2s";
compatible = "samsung,s3c6410-i2s";
reg = <0x13970000 0x100>;
clocks = <&clock CLK_I2S2>;
clock-names = "iis";
#clock-cells = <1>;
clock-output-names = "i2s_cdclk2";
dmas = <&pdma0 14>, <&pdma0 13>;
dma-names = "tx", "rx";
#sound-dai-cells = <1>;
status = "disabled";
};
......
......@@ -7,6 +7,7 @@
* published by the Free Software Foundation.
*/
#include <dt-bindings/sound/samsung-i2s.h>
#include <dt-bindings/input/input.h>
#include "exynos4412.dtsi"
......@@ -37,14 +38,13 @@ i2s0: i2s@03830000 {
pinctrl-names = "default";
status = "okay";
clocks = <&clock_audss EXYNOS_I2S_BUS>,
<&clock_audss EXYNOS_DOUT_AUD_BUS>;
clock-names = "iis", "i2s_opclk0";
<&clock_audss EXYNOS_DOUT_AUD_BUS>,
<&clock_audss EXYNOS_SCLK_I2S>;
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
};
sound: sound {
compatible = "samsung,odroidx2-audio";
samsung,i2s-controller = <&i2s0>;
samsung,audio-codec = <&max98090>;
compatible = "simple-audio-card";
assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>,
<&clock_audss EXYNOS_MOUT_I2S>,
<&clock_audss EXYNOS_DOUT_SRP>,
......@@ -55,6 +55,20 @@ sound: sound {
<0>,
<192000000>,
<19200000>;
simple-audio-card,format = "i2s";
simple-audio-card,bitclock-master = <&link0_codec>;
simple-audio-card,frame-master = <&link0_codec>;
simple-audio-card,cpu {
sound-dai = <&i2s0 0>;
system-clock-frequency = <19200000>;
};
link0_codec: simple-audio-card,codec {
sound-dai = <&max98090>;
clocks = <&i2s0 CLK_I2S_CDCLK>;
};
};
mmc@12550000 {
......@@ -373,6 +387,9 @@ max98090: max98090@10 {
reg = <0x10>;
interrupt-parent = <&gpx0>;
interrupts = <0 0>;
clocks = <&i2s0 CLK_I2S_CDCLK>;
clock-names = "mclk";
#sound-dai-cells = <0>;
};
};
......
......@@ -49,9 +49,11 @@ port@2 {
};
&sound {
compatible = "samsung,odroidu3-audio";
samsung,model = "Odroid-U3";
samsung,audio-routing =
simple-audio-card,name = "Odroid-U3";
simple-audio-card,widgets =
"Headphone", "Headphone Jack",
"Speakers", "Speakers";
simple-audio-card,routing =
"Headphone Jack", "HPL",
"Headphone Jack", "HPR",
"Headphone Jack", "MICBIAS",
......
......@@ -23,8 +23,12 @@ memory {
};
&sound {
samsung,model = "Odroid-X2";
samsung,audio-routing =
simple-audio-card,name = "Odroid-X2";
simple-audio-card,widgets =
"Headphone", "Headphone Jack",
"Microphone", "Mic Jack",
"Microphone", "DMIC";
simple-audio-card,routing =
"Headphone Jack", "HPL",
"Headphone Jack", "HPR",
"IN1", "Mic Jack",
......
......@@ -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
......
/* 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);
......@@ -34,6 +34,7 @@
#include "rt286.h"
#define RT286_VENDOR_ID 0x10ec0286
#define RT288_VENDOR_ID 0x10ec0288
struct rt286_priv {
struct regmap *regmap;
......@@ -305,6 +306,8 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
*hp = false;
*mic = false;
if (!rt286->codec)
return -EINVAL;
if (rt286->pdata.cbj_en) {
regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
*hp = buf & 0x80000000;
......@@ -1169,6 +1172,7 @@ static const struct regmap_config rt286_regmap = {
static const struct i2c_device_id rt286_i2c_id[] = {
{"rt286", 0},
{"rt288", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
......@@ -1189,6 +1193,17 @@ static struct dmi_system_id force_combo_jack_table[] = {
{ }
};
static struct dmi_system_id dmi_dell_dino[] = {
{
.ident = "Dell Dino",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_BOARD_NAME, "0144P8")
}
},
{ }
};
static int rt286_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
......@@ -1211,7 +1226,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
regmap_read(rt286->regmap,
RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
if (ret != RT286_VENDOR_ID) {
if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) {
dev_err(&i2c->dev,
"Device with ID register %x is not rt286\n", ret);
return -ENODEV;
......@@ -1224,7 +1239,8 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt286->pdata = *pdata;
if (dmi_check_system(force_combo_jack_table))
if (dmi_check_system(force_combo_jack_table) ||
dmi_check_system(dmi_dell_dino))
rt286->pdata.cbj_en = true;
regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
......@@ -1263,6 +1279,17 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
if (dmi_check_system(dmi_dell_dino)) {
regmap_update_bits(rt286->regmap,
RT286_SET_GPIO_MASK, 0x40, 0x40);
regmap_update_bits(rt286->regmap,
RT286_SET_GPIO_DIRECTION, 0x40, 0x40);
regmap_update_bits(rt286->regmap,
RT286_SET_GPIO_DATA, 0x40, 0x40);
regmap_update_bits(rt286->regmap,
RT286_GPIO_CTRL, 0xc, 0x8);
}
if (rt286->i2c->irq) {
ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
......
......@@ -117,6 +117,12 @@
VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
#define RT286_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
#define RT286_SET_GPIO_MASK\
VERB_CMD(AC_VERB_SET_GPIO_MASK, RT286_AUDIO_FUNCTION_GROUP, 0)
#define RT286_SET_GPIO_DIRECTION\
VERB_CMD(AC_VERB_SET_GPIO_DIRECTION, RT286_AUDIO_FUNCTION_GROUP, 0)
#define RT286_SET_GPIO_DATA\
VERB_CMD(AC_VERB_SET_GPIO_DATA, RT286_AUDIO_FUNCTION_GROUP, 0)
/* Index registers */
#define RT286_A_BIAS_CTRL1 0x01
......@@ -131,6 +137,7 @@
#define RT286_POWER_CTRL3 0x0f
#define RT286_MIC1_DET_CTRL 0x19
#define RT286_MISC_CTRL1 0x20
#define RT286_GPIO_CTRL 0x29
#define RT286_IRQ_CTRL 0x33
#define RT286_PLL_CTRL1 0x49
#define RT286_CBJ_CTRL1 0x4f
......
......@@ -615,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,
......
......@@ -1120,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)
......@@ -2189,6 +2166,19 @@ enum {
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;
......
......@@ -2616,6 +2616,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
static const struct regmap_config rt5670_regmap = {
.reg_bits = 8,
.val_bits = 16,
.use_single_rw = true,
.max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) *
RT5670_PR_SPACING),
.volatile_reg = rt5670_volatile_register,
......
......@@ -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/
/*
* 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(&params->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");
......@@ -338,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;
......@@ -970,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)
{
......@@ -1022,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)
......@@ -1084,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)
{
......@@ -1133,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)
......@@ -1451,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;
......@@ -1630,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,
......
......@@ -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);
......
......@@ -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 */
......@@ -1035,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;
}
......@@ -1051,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);
......@@ -1091,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);
......@@ -1139,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 < ARRAY_SIZE(mod_map); i++) {
pcm_data = &pdata->pcm[mod_map[i].dai_id][mod_map[i].stream];
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);
/* We need to wait until the DSP FW stops the streams */
msleep(2);
}
if (!pcm_data->substream)
continue;
dev_dbg(dev, "suspending pcm %d\n", i);
snd_pcm_suspend_all(pcm_data->hsw_pcm);
/* 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;
/* We need to wait until the DSP FW stops the streams */
msleep(2);
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 < 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);
}
/* put the DSP to sleep */
sst_hsw_dsp_runtime_sleep(hsw);
pdata->pm_state = HSW_PM_STATE_D3;
return 0;
......
......@@ -58,6 +58,7 @@ enum sst_algo_ops {
#define SST_BLOCK_TIMEOUT 1000
#define FW_SIGNATURE_SIZE 4
#define FW_NAME_SIZE 32
/* stream states */
enum sst_stream_states {
......@@ -426,7 +427,7 @@ struct intel_sst_drv {
* Holder for firmware name. Due to async call it needs to be
* persistent till worker thread gets called
*/
char firmware_name[20];
char firmware_name[FW_NAME_SIZE];
};
/* misc definitions */
......
......@@ -47,7 +47,7 @@ struct sst_machines {
char board[32];
char machine[32];
void (*machine_quirk)(void);
char firmware[32];
char firmware[FW_NAME_SIZE];
struct sst_platform_info *pdata;
};
......@@ -350,9 +350,9 @@ static struct sst_machines sst_acpi_bytcr[] = {
/* Cherryview-based platforms: CherryTrail and Braswell */
static struct sst_machines sst_acpi_chv[] = {
{"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin",
{"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "intel/fw_sst_22a8.bin",
&chv_platform_data },
{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "fw_sst_22a8.bin",
{"10EC5645", "cht-bsw", "cht-bsw-rt5645", NULL, "intel/fw_sst_22a8.bin",
&chv_platform_data },
{},
};
......
......@@ -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)
},
};
......
......@@ -146,17 +146,6 @@ config SND_SOC_SMARTQ
select SND_SAMSUNG_I2S
select SND_SOC_WM8750
config SND_SOC_GONI_AQUILA_WM8994
tristate "SoC I2S Audio support for AQUILA/GONI - WM8994"
depends on SND_SOC_SAMSUNG && (MACH_GONI || MACH_AQUILA)
depends on I2C=y
select SND_SAMSUNG_I2S
select MFD_WM8994
select SND_SOC_WM8994
help
Say Y if you want to add support for SoC audio on goni or aquila
with the WM8994.
config SND_SOC_SAMSUNG_SMDK_SPDIF
tristate "SoC S/PDIF Audio support for SMDK"
depends on SND_SOC_SAMSUNG
......
......@@ -35,7 +35,6 @@ snd-soc-smdk-wm8994-objs := smdk_wm8994.o
snd-soc-snow-objs := snow.o
snd-soc-smdk-wm9713-objs := smdk_wm9713.o
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.o
snd-soc-goni-wm8994-objs := goni_wm8994.o
snd-soc-smdk-spdif-objs := smdk_spdif.o
snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o
......@@ -63,7 +62,6 @@ obj-$(CONFIG_SND_SOC_SNOW) += snd-soc-snow.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.o
obj-$(CONFIG_SND_SOC_SAMSUNG_SMDK_SPDIF) += snd-soc-smdk-spdif.o
obj-$(CONFIG_SND_SOC_GONI_AQUILA_WM8994) += snd-soc-goni-wm8994.o
obj-$(CONFIG_SND_SOC_SMDK_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o
......
/*
* goni_wm8994.c
*
* Copyright (C) 2010 Samsung Electronics Co.Ltd
* Author: Chanwoo Choi <cw00.choi@samsung.com>
*
* 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; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/jack.h>
#include <asm/mach-types.h>
#include <mach/gpio-samsung.h>
#include "../codecs/wm8994.h"
#define MACHINE_NAME 0
#define CPU_VOICE_DAI 1
static const char *aquila_str[] = {
[MACHINE_NAME] = "aquila",
[CPU_VOICE_DAI] = "aquila-voice-dai",
};
static struct snd_soc_card goni;
static struct platform_device *goni_snd_device;
/* 3.5 pie jack */
static struct snd_soc_jack jack;
/* 3.5 pie jack detection DAPM pins */
static struct snd_soc_jack_pin jack_pins[] = {
{
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
}, {
.pin = "Headset Stereophone",
.mask = SND_JACK_HEADPHONE | SND_JACK_MECHANICAL |
SND_JACK_AVOUT,
},
};
/* 3.5 pie jack detection gpios */
static struct snd_soc_jack_gpio jack_gpios[] = {
{
.gpio = S5PV210_GPH0(6),
.name = "DET_3.5",
.report = SND_JACK_HEADSET | SND_JACK_MECHANICAL |
SND_JACK_AVOUT,
.debounce_time = 200,
},
};
static const struct snd_soc_dapm_widget goni_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Left Spk", NULL),
SND_SOC_DAPM_SPK("Ext Right Spk", NULL),
SND_SOC_DAPM_SPK("Ext Rcv", NULL),
SND_SOC_DAPM_HP("Headset Stereophone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_MIC("Main Mic", NULL),
SND_SOC_DAPM_MIC("2nd Mic", NULL),
SND_SOC_DAPM_LINE("Radio In", NULL),
};
static const struct snd_soc_dapm_route goni_dapm_routes[] = {
{"Ext Left Spk", NULL, "SPKOUTLP"},
{"Ext Left Spk", NULL, "SPKOUTLN"},
{"Ext Right Spk", NULL, "SPKOUTRP"},
{"Ext Right Spk", NULL, "SPKOUTRN"},
{"Ext Rcv", NULL, "HPOUT2N"},
{"Ext Rcv", NULL, "HPOUT2P"},
{"Headset Stereophone", NULL, "HPOUT1L"},
{"Headset Stereophone", NULL, "HPOUT1R"},
{"IN1RN", NULL, "Headset Mic"},
{"IN1RP", NULL, "Headset Mic"},
{"IN1RN", NULL, "2nd Mic"},
{"IN1RP", NULL, "2nd Mic"},
{"IN1LN", NULL, "Main Mic"},
{"IN1LP", NULL, "Main Mic"},
{"IN2LN", NULL, "Radio In"},
{"IN2RN", NULL, "Radio In"},
};
static int goni_wm8994_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
/* set endpoints to not connected */
snd_soc_dapm_nc_pin(dapm, "IN2LP:VXRN");
snd_soc_dapm_nc_pin(dapm, "IN2RP:VXRP");
snd_soc_dapm_nc_pin(dapm, "LINEOUT1N");
snd_soc_dapm_nc_pin(dapm, "LINEOUT1P");
snd_soc_dapm_nc_pin(dapm, "LINEOUT2N");
snd_soc_dapm_nc_pin(dapm, "LINEOUT2P");
if (machine_is_aquila()) {
snd_soc_dapm_nc_pin(dapm, "SPKOUTRN");
snd_soc_dapm_nc_pin(dapm, "SPKOUTRP");
}
/* Headset jack detection */
ret = snd_soc_jack_new(codec, "Headset Jack",
SND_JACK_HEADSET | SND_JACK_MECHANICAL | SND_JACK_AVOUT,
&jack);
if (ret)
return ret;
ret = snd_soc_jack_add_pins(&jack, ARRAY_SIZE(jack_pins), jack_pins);
if (ret)
return ret;
ret = snd_soc_jack_add_gpios(&jack, ARRAY_SIZE(jack_gpios), jack_gpios);
if (ret)
return ret;
return 0;
}
static int goni_hifi_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;
unsigned int pll_out = 24000000;
int ret = 0;
/* set the codec FLL */
ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, 0, pll_out,
params_rate(params) * 256);
if (ret < 0)
return ret;
/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL1,
params_rate(params) * 256, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops goni_hifi_ops = {
.hw_params = goni_hifi_hw_params,
};
static int goni_voice_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;
unsigned int pll_out = 24000000;
int ret = 0;
if (params_rate(params) != 8000)
return -EINVAL;
/* set the codec FLL */
ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL2, 0, pll_out,
params_rate(params) * 256);
if (ret < 0)
return ret;
/* set the codec system clock */
ret = snd_soc_dai_set_sysclk(codec_dai, WM8994_SYSCLK_FLL2,
params_rate(params) * 256, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_dai_driver voice_dai = {
.name = "goni-voice-dai",
.id = 0,
.playback = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,},
};
static const struct snd_soc_component_driver voice_component = {
.name = "goni-voice",
};
static struct snd_soc_ops goni_voice_ops = {
.hw_params = goni_voice_hw_params,
};
static struct snd_soc_dai_link goni_dai[] = {
{
.name = "WM8994",
.stream_name = "WM8994 HiFi",
.cpu_dai_name = "samsung-i2s.0",
.codec_dai_name = "wm8994-aif1",
.platform_name = "samsung-i2s.0",
.codec_name = "wm8994-codec.0-001a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM,
.init = goni_wm8994_init,
.ops = &goni_hifi_ops,
}, {
.name = "WM8994 Voice",
.stream_name = "Voice",
.cpu_dai_name = "goni-voice-dai",
.codec_dai_name = "wm8994-aif2",
.codec_name = "wm8994-codec.0-001a",
.dai_fmt = SND_SOC_DAIFMT_LEFT_J | SND_SOC_DAIFMT_IB_IF |
SND_SOC_DAIFMT_CBM_CFM,
.ops = &goni_voice_ops,
},
};
static struct snd_soc_card goni = {
.name = "goni",
.owner = THIS_MODULE,
.dai_link = goni_dai,
.num_links = ARRAY_SIZE(goni_dai),
.dapm_widgets = goni_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(goni_dapm_widgets),
.dapm_routes = goni_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(goni_dapm_routes),
};
static int __init goni_init(void)
{
int ret;
if (machine_is_aquila()) {
voice_dai.name = aquila_str[CPU_VOICE_DAI];
goni_dai[1].cpu_dai_name = aquila_str[CPU_VOICE_DAI];
goni.name = aquila_str[MACHINE_NAME];
} else if (!machine_is_goni())
return -ENODEV;
goni_snd_device = platform_device_alloc("soc-audio", -1);
if (!goni_snd_device)
return -ENOMEM;
/* register voice DAI here */
ret = devm_snd_soc_register_component(&goni_snd_device->dev,
&voice_component, &voice_dai, 1);
if (ret) {
platform_device_put(goni_snd_device);
return ret;
}
platform_set_drvdata(goni_snd_device, &goni);
ret = platform_device_add(goni_snd_device);
if (ret)
platform_device_put(goni_snd_device);
return ret;
}
static void __exit goni_exit(void)
{
platform_device_unregister(goni_snd_device);
}
module_init(goni_init);
module_exit(goni_exit);
/* Module information */
MODULE_DESCRIPTION("ALSA SoC WM8994 GONI(S5PV210)");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_LICENSE("GPL");
......@@ -2419,8 +2419,8 @@ int snd_soc_unregister_card(struct snd_soc_card *card)
card->instantiated = false;
snd_soc_dapm_shutdown(card);
soc_cleanup_card_resources(card);
dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
}
dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
return 0;
}
......
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