Merge remote-tracking branches 'asoc/topic/rt5670', 'asoc/topic/rt5677', 'asoc/topic/rx51', 'asoc/topic/samsung' and 'asoc/topic/sh' into asoc-next

This commit is contained in:
Mark Brown 2015-02-09 15:10:26 +08:00
20 changed files with 306 additions and 500 deletions

View file

@ -33,6 +33,25 @@ Required SoC Specific Properties:
"iis" is the i2s bus clock and i2s_opclk0, i2s_opclk1 are sources of the root "iis" is the i2s bus clock and i2s_opclk0, i2s_opclk1 are sources of the root
clk. i2s0 has internal mux to select the source of root clk and i2s1 and i2s2 clk. i2s0 has internal mux to select the source of root clk and i2s1 and i2s2
doesn't have any such mux. doesn't have any such mux.
- #clock-cells: should be 1, this property must be present if the I2S device
is a clock provider in terms of the common clock bindings, described in
../clock/clock-bindings.txt.
- clock-output-names: from the common clock bindings, names of the CDCLK
I2S output clocks, suggested values are "i2s_cdclk0", "i2s_cdclk1",
"i2s_cdclk3" for the I2S0, I2S1, I2S2 devices recpectively.
There are following clocks available at the I2S device nodes:
CLK_I2S_CDCLK - the CDCLK (CODECLKO) gate clock,
CLK_I2S_RCLK_PSR - the RCLK prescaler divider clock (corresponding to the
IISPSR register),
CLK_I2S_RCLK_SRC - the RCLKSRC mux clock (corresponding to RCLKSRC bit in
IISMOD register).
Refer to the SoC datasheet for availability of the above clocks.
The CLK_I2S_RCLK_PSR and CLK_I2S_RCLK_SRC clocks are usually only available
in the IIS Multi Audio Interface (I2S0).
Note: Old DTs may not have the #clock-cells, clock-output-names properties
and then not use the I2S node as a clock supplier.
Optional SoC Specific Properties: Optional SoC Specific Properties:
@ -41,6 +60,7 @@ Optional SoC Specific Properties:
- pinctrl-0: Should specify pin control groups used for this controller. - pinctrl-0: Should specify pin control groups used for this controller.
- pinctrl-names: Should contain only one value - "default". - pinctrl-names: Should contain only one value - "default".
Example: Example:
i2s0: i2s@03830000 { i2s0: i2s@03830000 {
@ -54,6 +74,8 @@ i2s0: i2s@03830000 {
<&clock_audss EXYNOS_I2S_BUS>, <&clock_audss EXYNOS_I2S_BUS>,
<&clock_audss EXYNOS_SCLK_I2S>; <&clock_audss EXYNOS_SCLK_I2S>;
clock-names = "iis", "i2s_opclk0", "i2s_opclk1"; clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
#clock-cells;
clock-output-names = "i2s_cdclk0";
samsung,idma-addr = <0x03000000>; samsung,idma-addr = <0x03000000>;
pinctrl-names = "default"; pinctrl-names = "default";
pinctrl-0 = <&i2s0_bus>; pinctrl-0 = <&i2s0_bus>;

View file

@ -61,9 +61,12 @@
reg = <0x03830000 0x100>; reg = <0x03830000 0x100>;
clocks = <&clock_audss EXYNOS_I2S_BUS>; clocks = <&clock_audss EXYNOS_I2S_BUS>;
clock-names = "iis"; clock-names = "iis";
#clock-cells = <1>;
clock-output-names = "i2s_cdclk0";
dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>; dmas = <&pdma0 12>, <&pdma0 11>, <&pdma0 10>;
dma-names = "tx", "rx", "tx-sec"; dma-names = "tx", "rx", "tx-sec";
samsung,idma-addr = <0x03000000>; samsung,idma-addr = <0x03000000>;
#sound-dai-cells = <1>;
status = "disabled"; status = "disabled";
}; };
@ -372,8 +375,11 @@
reg = <0x13960000 0x100>; reg = <0x13960000 0x100>;
clocks = <&clock CLK_I2S1>; clocks = <&clock CLK_I2S1>;
clock-names = "iis"; clock-names = "iis";
#clock-cells = <1>;
clock-output-names = "i2s_cdclk1";
dmas = <&pdma1 12>, <&pdma1 11>; dmas = <&pdma1 12>, <&pdma1 11>;
dma-names = "tx", "rx"; dma-names = "tx", "rx";
#sound-dai-cells = <1>;
status = "disabled"; status = "disabled";
}; };
@ -382,8 +388,11 @@
reg = <0x13970000 0x100>; reg = <0x13970000 0x100>;
clocks = <&clock CLK_I2S2>; clocks = <&clock CLK_I2S2>;
clock-names = "iis"; clock-names = "iis";
#clock-cells = <1>;
clock-output-names = "i2s_cdclk2";
dmas = <&pdma0 14>, <&pdma0 13>; dmas = <&pdma0 14>, <&pdma0 13>;
dma-names = "tx", "rx"; dma-names = "tx", "rx";
#sound-dai-cells = <1>;
status = "disabled"; status = "disabled";
}; };

View file

@ -7,6 +7,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <dt-bindings/sound/samsung-i2s.h>
#include <dt-bindings/input/input.h> #include <dt-bindings/input/input.h>
#include "exynos4412.dtsi" #include "exynos4412.dtsi"
@ -37,14 +38,13 @@
pinctrl-names = "default"; pinctrl-names = "default";
status = "okay"; status = "okay";
clocks = <&clock_audss EXYNOS_I2S_BUS>, clocks = <&clock_audss EXYNOS_I2S_BUS>,
<&clock_audss EXYNOS_DOUT_AUD_BUS>; <&clock_audss EXYNOS_DOUT_AUD_BUS>,
clock-names = "iis", "i2s_opclk0"; <&clock_audss EXYNOS_SCLK_I2S>;
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
}; };
sound: sound { sound: sound {
compatible = "samsung,odroidx2-audio"; compatible = "simple-audio-card";
samsung,i2s-controller = <&i2s0>;
samsung,audio-codec = <&max98090>;
assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>, assigned-clocks = <&clock_audss EXYNOS_MOUT_AUDSS>,
<&clock_audss EXYNOS_MOUT_I2S>, <&clock_audss EXYNOS_MOUT_I2S>,
<&clock_audss EXYNOS_DOUT_SRP>, <&clock_audss EXYNOS_DOUT_SRP>,
@ -55,6 +55,20 @@
<0>, <0>,
<192000000>, <192000000>,
<19200000>; <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 { mmc@12550000 {
@ -373,6 +387,9 @@
reg = <0x10>; reg = <0x10>;
interrupt-parent = <&gpx0>; interrupt-parent = <&gpx0>;
interrupts = <0 0>; interrupts = <0 0>;
clocks = <&i2s0 CLK_I2S_CDCLK>;
clock-names = "mclk";
#sound-dai-cells = <0>;
}; };
}; };

View file

@ -49,9 +49,11 @@
}; };
&sound { &sound {
compatible = "samsung,odroidu3-audio"; simple-audio-card,name = "Odroid-U3";
samsung,model = "Odroid-U3"; simple-audio-card,widgets =
samsung,audio-routing = "Headphone", "Headphone Jack",
"Speakers", "Speakers";
simple-audio-card,routing =
"Headphone Jack", "HPL", "Headphone Jack", "HPL",
"Headphone Jack", "HPR", "Headphone Jack", "HPR",
"Headphone Jack", "MICBIAS", "Headphone Jack", "MICBIAS",

View file

@ -23,8 +23,12 @@
}; };
&sound { &sound {
samsung,model = "Odroid-X2"; simple-audio-card,name = "Odroid-X2";
samsung,audio-routing = simple-audio-card,widgets =
"Headphone", "Headphone Jack",
"Microphone", "Mic Jack",
"Microphone", "DMIC";
simple-audio-card,routing =
"Headphone Jack", "HPL", "Headphone Jack", "HPL",
"Headphone Jack", "HPR", "Headphone Jack", "HPR",
"IN1", "Mic Jack", "IN1", "Mic Jack",

View file

@ -0,0 +1,8 @@
#ifndef _DT_BINDINGS_SAMSUNG_I2S_H
#define _DT_BINDINGS_SAMSUNG_I2S_H
#define CLK_I2S_CDCLK 0
#define CLK_I2S_RCLK_SRC 1
#define CLK_I2S_RCLK_PSR 2
#endif /* _DT_BINDINGS_SAMSUNG_I2S_H */

View file

@ -529,7 +529,7 @@ config SND_SOC_RT5677
config SND_SOC_RT5677_SPI config SND_SOC_RT5677_SPI
tristate tristate
default SND_SOC_RT5677 default SND_SOC_RT5677 && SPI
#Freescale sgtl5000 codec #Freescale sgtl5000 codec
config SND_SOC_SGTL5000 config SND_SOC_SGTL5000

View file

@ -2616,6 +2616,7 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
static const struct regmap_config rt5670_regmap = { static const struct regmap_config rt5670_regmap = {
.reg_bits = 8, .reg_bits = 8,
.val_bits = 16, .val_bits = 16,
.use_single_rw = true,
.max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) * .max_register = RT5670_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5670_ranges) *
RT5670_PR_SPACING), RT5670_PR_SPACING),
.volatile_reg = rt5670_volatile_register, .volatile_reg = rt5670_volatile_register,

View file

@ -702,6 +702,9 @@ static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
static bool activity; static bool activity;
int ret; int ret;
if (!IS_ENABLED(CONFIG_SND_SOC_RT5677_SPI))
return -ENXIO;
if (on && !activity) { if (on && !activity) {
activity = true; activity = true;

View file

@ -250,14 +250,14 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"FM Transmitter", NULL, "LLOUT"}, {"FM Transmitter", NULL, "LLOUT"},
{"FM Transmitter", NULL, "RLOUT"}, {"FM Transmitter", NULL, "RLOUT"},
{"DMic Rate 64", NULL, "Mic Bias"}, {"DMic Rate 64", NULL, "DMic"},
{"Mic Bias", NULL, "DMic"}, {"DMic", NULL, "Mic Bias"},
{"b LINE2R", NULL, "MONO_LOUT"}, {"b LINE2R", NULL, "MONO_LOUT"},
{"Earphone", NULL, "b HPLOUT"}, {"Earphone", NULL, "b HPLOUT"},
{"LINE1L", NULL, "b Mic Bias"}, {"LINE1L", NULL, "HS Mic"},
{"b Mic Bias", NULL, "HS Mic"} {"HS Mic", NULL, "b Mic Bias"},
}; };
static const char * const spk_function[] = {"Off", "On"}; static const char * const spk_function[] = {"Off", "On"};

View file

@ -54,7 +54,7 @@ config SND_SOC_SAMSUNG_JIVE_WM8750
config SND_SOC_SAMSUNG_SMDK_WM8580 config SND_SOC_SAMSUNG_SMDK_WM8580
tristate "SoC I2S Audio support for WM8580 on SMDK" tristate "SoC I2S Audio support for WM8580 on SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110) depends on SND_SOC_SAMSUNG && (MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110)
depends on REGMAP_I2C depends on I2C
select SND_SOC_WM8580 select SND_SOC_WM8580
select SND_SAMSUNG_I2S select SND_SAMSUNG_I2S
help help
@ -146,17 +146,6 @@ config SND_SOC_SMARTQ
select SND_SAMSUNG_I2S select SND_SAMSUNG_I2S
select SND_SOC_WM8750 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 config SND_SOC_SAMSUNG_SMDK_SPDIF
tristate "SoC S/PDIF Audio support for SMDK" tristate "SoC S/PDIF Audio support for SMDK"
depends on SND_SOC_SAMSUNG depends on SND_SOC_SAMSUNG
@ -167,7 +156,7 @@ config SND_SOC_SAMSUNG_SMDK_SPDIF
config SND_SOC_SMDK_WM8580_PCM config SND_SOC_SMDK_WM8580_PCM
tristate "SoC PCM Audio support for WM8580 on SMDK" tristate "SoC PCM Audio support for WM8580 on SMDK"
depends on SND_SOC_SAMSUNG && (MACH_SMDKV210 || MACH_SMDKC110) depends on SND_SOC_SAMSUNG && (MACH_SMDKV210 || MACH_SMDKC110)
depends on REGMAP_I2C depends on I2C
select SND_SOC_WM8580 select SND_SOC_WM8580
select SND_SAMSUNG_PCM select SND_SAMSUNG_PCM
help help

View file

@ -35,7 +35,6 @@ snd-soc-smdk-wm8994-objs := smdk_wm8994.o
snd-soc-snow-objs := snow.o snd-soc-snow-objs := snow.o
snd-soc-smdk-wm9713-objs := smdk_wm9713.o snd-soc-smdk-wm9713-objs := smdk_wm9713.o
snd-soc-s3c64xx-smartq-wm8987-objs := smartq_wm8987.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-spdif-objs := smdk_spdif.o
snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o snd-soc-smdk-wm8580pcm-objs := smdk_wm8580pcm.o
snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.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_SAMSUNG_SMDK_WM9713) += snd-soc-smdk-wm9713.o
obj-$(CONFIG_SND_SOC_SMARTQ) += snd-soc-s3c64xx-smartq-wm8987.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_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_WM8580_PCM) += snd-soc-smdk-wm8580pcm.o
obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o obj-$(CONFIG_SND_SOC_SMDK_WM8994_PCM) += snd-soc-smdk-wm8994pcm.o
obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o obj-$(CONFIG_SND_SOC_SPEYSIDE) += snd-soc-speyside.o

View file

@ -1,289 +0,0 @@
/*
* 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");

View file

@ -10,9 +10,11 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <dt-bindings/sound/samsung-i2s.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
@ -59,10 +61,8 @@ struct samsung_i2s_dai_data {
struct i2s_dai { struct i2s_dai {
/* Platform device for this DAI */ /* Platform device for this DAI */
struct platform_device *pdev; struct platform_device *pdev;
/* IOREMAP'd SFRs */ /* Memory mapped SFR region */
void __iomem *addr; void __iomem *addr;
/* Physical base address of SFRs */
u32 base;
/* Rate of RCLK source clock */ /* Rate of RCLK source clock */
unsigned long rclk_srcrate; unsigned long rclk_srcrate;
/* Frame Clock */ /* Frame Clock */
@ -83,8 +83,6 @@ struct i2s_dai {
#define DAI_OPENED (1 << 0) /* Dai is opened */ #define DAI_OPENED (1 << 0) /* Dai is opened */
#define DAI_MANAGER (1 << 1) /* Dai is the manager */ #define DAI_MANAGER (1 << 1) /* Dai is the manager */
unsigned mode; unsigned mode;
/* CDCLK pin direction: 0 - input, 1 - output */
unsigned int cdclk_out:1;
/* Driver for this DAI */ /* Driver for this DAI */
struct snd_soc_dai_driver i2s_dai_drv; struct snd_soc_dai_driver i2s_dai_drv;
/* DMA parameters */ /* DMA parameters */
@ -95,8 +93,15 @@ struct i2s_dai {
u32 suspend_i2smod; u32 suspend_i2smod;
u32 suspend_i2scon; u32 suspend_i2scon;
u32 suspend_i2spsr; u32 suspend_i2spsr;
unsigned long gpios[7]; /* i2s gpio line numbers */
const struct samsung_i2s_variant_regs *variant_regs; const struct samsung_i2s_variant_regs *variant_regs;
/* Spinlock protecting access to the device's registers */
spinlock_t spinlock;
spinlock_t *lock;
/* Below fields are only valid if this is the primary FIFO */
struct clk *clk_table[3];
struct clk_onecell_data clk_data;
}; };
/* Lock for cross i/f checks */ /* Lock for cross i/f checks */
@ -133,10 +138,16 @@ static inline bool tx_active(struct i2s_dai *i2s)
return active ? true : false; return active ? true : false;
} }
/* Return pointer to the other DAI */
static inline struct i2s_dai *get_other_dai(struct i2s_dai *i2s)
{
return i2s->pri_dai ? : i2s->sec_dai;
}
/* If the other interface of the controller is transmitting data */ /* If the other interface of the controller is transmitting data */
static inline bool other_tx_active(struct i2s_dai *i2s) static inline bool other_tx_active(struct i2s_dai *i2s)
{ {
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
return tx_active(other); return tx_active(other);
} }
@ -163,7 +174,7 @@ static inline bool rx_active(struct i2s_dai *i2s)
/* If the other interface of the controller is receiving data */ /* If the other interface of the controller is receiving data */
static inline bool other_rx_active(struct i2s_dai *i2s) static inline bool other_rx_active(struct i2s_dai *i2s)
{ {
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
return rx_active(other); return rx_active(other);
} }
@ -464,18 +475,23 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int rfs, int dir) int clk_id, unsigned int rfs, int dir)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
u32 mod = readl(i2s->addr + I2SMOD);
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs; const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off; unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off; unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
u32 mod, mask, val = 0;
spin_lock(i2s->lock);
mod = readl(i2s->addr + I2SMOD);
spin_unlock(i2s->lock);
switch (clk_id) { switch (clk_id) {
case SAMSUNG_I2S_OPCLK: case SAMSUNG_I2S_OPCLK:
mod &= ~MOD_OPCLK_MASK; mask = MOD_OPCLK_MASK;
mod |= dir; val = dir;
break; break;
case SAMSUNG_I2S_CDCLK: case SAMSUNG_I2S_CDCLK:
mask = 1 << i2s_regs->cdclkcon_off;
/* Shouldn't matter in GATING(CLOCK_IN) mode */ /* Shouldn't matter in GATING(CLOCK_IN) mode */
if (dir == SND_SOC_CLOCK_IN) if (dir == SND_SOC_CLOCK_IN)
rfs = 0; rfs = 0;
@ -492,15 +508,15 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
} }
if (dir == SND_SOC_CLOCK_IN) if (dir == SND_SOC_CLOCK_IN)
mod |= 1 << i2s_regs->cdclkcon_off; val = 1 << i2s_regs->cdclkcon_off;
else
mod &= ~(1 << i2s_regs->cdclkcon_off);
i2s->rfs = rfs; i2s->rfs = rfs;
break; break;
case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */ case SAMSUNG_I2S_RCLKSRC_0: /* clock corrsponding to IISMOD[10] := 0 */
case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */ case SAMSUNG_I2S_RCLKSRC_1: /* clock corrsponding to IISMOD[10] := 1 */
mask = 1 << i2s_regs->rclksrc_off;
if ((i2s->quirks & QUIRK_NO_MUXPSR) if ((i2s->quirks & QUIRK_NO_MUXPSR)
|| (clk_id == SAMSUNG_I2S_RCLKSRC_0)) || (clk_id == SAMSUNG_I2S_RCLKSRC_0))
clk_id = 0; clk_id = 0;
@ -550,18 +566,19 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
return 0; return 0;
} }
if (clk_id == 0) if (clk_id == 1)
mod &= ~(1 << i2s_regs->rclksrc_off); val = 1 << i2s_regs->rclksrc_off;
else
mod |= 1 << i2s_regs->rclksrc_off;
break; break;
default: default:
dev_err(&i2s->pdev->dev, "We don't serve that!\n"); dev_err(&i2s->pdev->dev, "We don't serve that!\n");
return -EINVAL; return -EINVAL;
} }
spin_lock(i2s->lock);
mod = readl(i2s->addr + I2SMOD);
mod = (mod & ~mask) | val;
writel(mod, i2s->addr + I2SMOD); writel(mod, i2s->addr + I2SMOD);
spin_unlock(i2s->lock);
return 0; return 0;
} }
@ -570,9 +587,8 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
unsigned int fmt) unsigned int fmt)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
u32 mod = readl(i2s->addr + I2SMOD);
int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave; int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
u32 tmp = 0; u32 mod, tmp = 0;
lrp_shift = i2s->variant_regs->lrp_off; lrp_shift = i2s->variant_regs->lrp_off;
sdf_shift = i2s->variant_regs->sdf_off; sdf_shift = i2s->variant_regs->sdf_off;
@ -632,12 +648,15 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL; return -EINVAL;
} }
spin_lock(i2s->lock);
mod = readl(i2s->addr + I2SMOD);
/* /*
* Don't change the I2S mode if any controller is active on this * Don't change the I2S mode if any controller is active on this
* channel. * channel.
*/ */
if (any_active(i2s) && if (any_active(i2s) &&
((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) { ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
spin_unlock(i2s->lock);
dev_err(&i2s->pdev->dev, dev_err(&i2s->pdev->dev,
"%s:%d Other DAI busy\n", __func__, __LINE__); "%s:%d Other DAI busy\n", __func__, __LINE__);
return -EAGAIN; return -EAGAIN;
@ -646,6 +665,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
mod &= ~(sdf_mask | lrp_rlow | mod_slave); mod &= ~(sdf_mask | lrp_rlow | mod_slave);
mod |= tmp; mod |= tmp;
writel(mod, i2s->addr + I2SMOD); writel(mod, i2s->addr + I2SMOD);
spin_unlock(i2s->lock);
return 0; return 0;
} }
@ -654,16 +674,16 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
u32 mod = readl(i2s->addr + I2SMOD); u32 mod, mask = 0, val = 0;
if (!is_secondary(i2s)) if (!is_secondary(i2s))
mod &= ~(MOD_DC2_EN | MOD_DC1_EN); mask |= (MOD_DC2_EN | MOD_DC1_EN);
switch (params_channels(params)) { switch (params_channels(params)) {
case 6: case 6:
mod |= MOD_DC2_EN; val |= MOD_DC2_EN;
case 4: case 4:
mod |= MOD_DC1_EN; val |= MOD_DC1_EN;
break; break;
case 2: case 2:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@ -685,44 +705,49 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
} }
if (is_secondary(i2s)) if (is_secondary(i2s))
mod &= ~MOD_BLCS_MASK; mask |= MOD_BLCS_MASK;
else else
mod &= ~MOD_BLCP_MASK; mask |= MOD_BLCP_MASK;
if (is_manager(i2s)) if (is_manager(i2s))
mod &= ~MOD_BLC_MASK; mask |= MOD_BLC_MASK;
switch (params_width(params)) { switch (params_width(params)) {
case 8: case 8:
if (is_secondary(i2s)) if (is_secondary(i2s))
mod |= MOD_BLCS_8BIT; val |= MOD_BLCS_8BIT;
else else
mod |= MOD_BLCP_8BIT; val |= MOD_BLCP_8BIT;
if (is_manager(i2s)) if (is_manager(i2s))
mod |= MOD_BLC_8BIT; val |= MOD_BLC_8BIT;
break; break;
case 16: case 16:
if (is_secondary(i2s)) if (is_secondary(i2s))
mod |= MOD_BLCS_16BIT; val |= MOD_BLCS_16BIT;
else else
mod |= MOD_BLCP_16BIT; val |= MOD_BLCP_16BIT;
if (is_manager(i2s)) if (is_manager(i2s))
mod |= MOD_BLC_16BIT; val |= MOD_BLC_16BIT;
break; break;
case 24: case 24:
if (is_secondary(i2s)) if (is_secondary(i2s))
mod |= MOD_BLCS_24BIT; val |= MOD_BLCS_24BIT;
else else
mod |= MOD_BLCP_24BIT; val |= MOD_BLCP_24BIT;
if (is_manager(i2s)) if (is_manager(i2s))
mod |= MOD_BLC_24BIT; val |= MOD_BLC_24BIT;
break; break;
default: default:
dev_err(&i2s->pdev->dev, "Format(%d) not supported\n", dev_err(&i2s->pdev->dev, "Format(%d) not supported\n",
params_format(params)); params_format(params));
return -EINVAL; return -EINVAL;
} }
spin_lock(i2s->lock);
mod = readl(i2s->addr + I2SMOD);
mod = (mod & ~mask) | val;
writel(mod, i2s->addr + I2SMOD); writel(mod, i2s->addr + I2SMOD);
spin_unlock(i2s->lock);
samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture); samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
@ -736,7 +761,7 @@ static int i2s_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&lock, flags); spin_lock_irqsave(&lock, flags);
@ -753,9 +778,6 @@ static int i2s_startup(struct snd_pcm_substream *substream,
spin_unlock_irqrestore(&lock, flags); spin_unlock_irqrestore(&lock, flags);
if (!is_opened(other) && i2s->cdclk_out)
i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
0, SND_SOC_CLOCK_OUT);
return 0; return 0;
} }
@ -763,38 +785,27 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
unsigned long flags; unsigned long flags;
const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
spin_lock_irqsave(&lock, flags); spin_lock_irqsave(&lock, flags);
i2s->mode &= ~DAI_OPENED; i2s->mode &= ~DAI_OPENED;
i2s->mode &= ~DAI_MANAGER; i2s->mode &= ~DAI_MANAGER;
if (is_opened(other)) { if (is_opened(other))
other->mode |= DAI_MANAGER; other->mode |= DAI_MANAGER;
} else {
u32 mod = readl(i2s->addr + I2SMOD);
i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off));
if (other)
other->cdclk_out = i2s->cdclk_out;
}
/* Reset any constraint on RFS and BFS */ /* Reset any constraint on RFS and BFS */
i2s->rfs = 0; i2s->rfs = 0;
i2s->bfs = 0; i2s->bfs = 0;
spin_unlock_irqrestore(&lock, flags); spin_unlock_irqrestore(&lock, flags);
/* Gate CDCLK by default */
if (!is_opened(other))
i2s_set_sysclk(dai, SAMSUNG_I2S_CDCLK,
0, SND_SOC_CLOCK_IN);
} }
static int config_setup(struct i2s_dai *i2s) static int config_setup(struct i2s_dai *i2s)
{ {
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
unsigned rfs, bfs, blc; unsigned rfs, bfs, blc;
u32 psr; u32 psr;
@ -864,10 +875,10 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
local_irq_save(flags); spin_lock_irqsave(i2s->lock, flags);
if (config_setup(i2s)) { if (config_setup(i2s)) {
local_irq_restore(flags); spin_unlock_irqrestore(i2s->lock, flags);
return -EINVAL; return -EINVAL;
} }
@ -876,12 +887,12 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
else else
i2s_txctrl(i2s, 1); i2s_txctrl(i2s, 1);
local_irq_restore(flags); spin_unlock_irqrestore(i2s->lock, flags);
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
local_irq_save(flags); spin_lock_irqsave(i2s->lock, flags);
if (capture) { if (capture) {
i2s_rxctrl(i2s, 0); i2s_rxctrl(i2s, 0);
@ -891,7 +902,7 @@ static int i2s_trigger(struct snd_pcm_substream *substream,
i2s_fifo(i2s, FIC_TXFLUSH); i2s_fifo(i2s, FIC_TXFLUSH);
} }
local_irq_restore(flags); spin_unlock_irqrestore(i2s->lock, flags);
break; break;
} }
@ -902,7 +913,7 @@ static int i2s_set_clkdiv(struct snd_soc_dai *dai,
int div_id, int div) int div_id, int div)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
switch (div_id) { switch (div_id) {
case SAMSUNG_I2S_DIV_BCLK: case SAMSUNG_I2S_DIV_BCLK:
@ -971,58 +982,36 @@ static int i2s_resume(struct snd_soc_dai *dai)
static int samsung_i2s_dai_probe(struct snd_soc_dai *dai) static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
{ {
struct i2s_dai *i2s = to_info(dai); struct i2s_dai *i2s = to_info(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai; struct i2s_dai *other = get_other_dai(i2s);
int ret; unsigned long flags;
if (other && other->clk) { /* If this is probe on secondary */ if (is_secondary(i2s)) { /* If this is probe on the secondary DAI */
samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback, samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
NULL); NULL);
goto probe_exit; } else {
} samsung_asoc_init_dma_data(dai, &i2s->dma_playback,
&i2s->dma_capture);
i2s->addr = ioremap(i2s->base, 0x100); if (i2s->quirks & QUIRK_NEED_RSTCLR)
if (i2s->addr == NULL) { writel(CON_RSTCLR, i2s->addr + I2SCON);
dev_err(&i2s->pdev->dev, "cannot ioremap registers\n");
return -ENXIO;
}
i2s->clk = clk_get(&i2s->pdev->dev, "iis"); if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
if (IS_ERR(i2s->clk)) { idma_reg_addr_init(i2s->addr,
dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
iounmap(i2s->addr);
return PTR_ERR(i2s->clk);
}
ret = clk_prepare_enable(i2s->clk);
if (ret != 0) {
dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
if (other) {
other->addr = i2s->addr;
other->clk = i2s->clk;
}
if (i2s->quirks & QUIRK_NEED_RSTCLR)
writel(CON_RSTCLR, i2s->addr + I2SCON);
if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
idma_reg_addr_init(i2s->addr,
i2s->sec_dai->idma_playback.dma_addr); i2s->sec_dai->idma_playback.dma_addr);
}
probe_exit:
/* Reset any constraint on RFS and BFS */ /* Reset any constraint on RFS and BFS */
i2s->rfs = 0; i2s->rfs = 0;
i2s->bfs = 0; i2s->bfs = 0;
i2s->rclk_srcrate = 0; i2s->rclk_srcrate = 0;
spin_lock_irqsave(i2s->lock, flags);
i2s_txctrl(i2s, 0); i2s_txctrl(i2s, 0);
i2s_rxctrl(i2s, 0); i2s_rxctrl(i2s, 0);
i2s_fifo(i2s, FIC_TXFLUSH); i2s_fifo(i2s, FIC_TXFLUSH);
i2s_fifo(other, FIC_TXFLUSH); i2s_fifo(other, FIC_TXFLUSH);
i2s_fifo(i2s, FIC_RXFLUSH); i2s_fifo(i2s, FIC_RXFLUSH);
spin_unlock_irqrestore(i2s->lock, flags);
/* Gate CDCLK by default */ /* Gate CDCLK by default */
if (!is_opened(other)) if (!is_opened(other))
@ -1035,21 +1024,15 @@ probe_exit:
static int samsung_i2s_dai_remove(struct snd_soc_dai *dai) static int samsung_i2s_dai_remove(struct snd_soc_dai *dai)
{ {
struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai); struct i2s_dai *i2s = snd_soc_dai_get_drvdata(dai);
struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
if (!other || !other->clk) { if (!is_secondary(i2s)) {
if (i2s->quirks & QUIRK_NEED_RSTCLR) {
if (i2s->quirks & QUIRK_NEED_RSTCLR) spin_lock(i2s->lock);
writel(0, i2s->addr + I2SCON); writel(0, i2s->addr + I2SCON);
spin_unlock(i2s->lock);
clk_disable_unprepare(i2s->clk); }
clk_put(i2s->clk);
iounmap(i2s->addr);
} }
i2s->clk = NULL;
return 0; return 0;
} }
@ -1124,15 +1107,14 @@ static const struct of_device_id exynos_i2s_match[];
static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data( static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
struct platform_device *pdev) struct platform_device *pdev)
{ {
#ifdef CONFIG_OF if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
if (pdev->dev.of_node) {
const struct of_device_id *match; const struct of_device_id *match;
match = of_match_node(exynos_i2s_match, pdev->dev.of_node); match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
return match->data; return match ? match->data : NULL;
} else } else {
#endif
return (struct samsung_i2s_dai_data *) return (struct samsung_i2s_dai_data *)
platform_get_device_id(pdev)->driver_data; platform_get_device_id(pdev)->driver_data;
}
} }
#ifdef CONFIG_PM #ifdef CONFIG_PM
@ -1155,6 +1137,87 @@ static int i2s_runtime_resume(struct device *dev)
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
static void i2s_unregister_clocks(struct i2s_dai *i2s)
{
int i;
for (i = 0; i < i2s->clk_data.clk_num; i++) {
if (!IS_ERR(i2s->clk_table[i]))
clk_unregister(i2s->clk_table[i]);
}
}
static void i2s_unregister_clock_provider(struct platform_device *pdev)
{
struct i2s_dai *i2s = dev_get_drvdata(&pdev->dev);
of_clk_del_provider(pdev->dev.of_node);
i2s_unregister_clocks(i2s);
}
static int i2s_register_clock_provider(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct i2s_dai *i2s = dev_get_drvdata(dev);
const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" };
const char *p_names[2] = { NULL };
const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs;
struct clk *rclksrc;
int ret, i;
/* Register the clock provider only if it's expected in the DTB */
if (!of_find_property(dev->of_node, "#clock-cells", NULL))
return 0;
/* Get the RCLKSRC mux clock parent clock names */
for (i = 0; i < ARRAY_SIZE(p_names); i++) {
rclksrc = clk_get(dev, clk_name[i]);
if (IS_ERR(rclksrc))
continue;
p_names[i] = __clk_get_name(rclksrc);
clk_put(rclksrc);
}
if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
/* Activate the prescaler */
u32 val = readl(i2s->addr + I2SPSR);
writel(val | PSR_PSREN, i2s->addr + I2SPSR);
i2s->clk_table[CLK_I2S_RCLK_SRC] = clk_register_mux(NULL,
"i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
i2s->addr + I2SMOD, reg_info->rclksrc_off,
1, 0, i2s->lock);
i2s->clk_table[CLK_I2S_RCLK_PSR] = clk_register_divider(NULL,
"i2s_presc", "i2s_rclksrc",
CLK_SET_RATE_PARENT,
i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
p_names[0] = "i2s_presc";
i2s->clk_data.clk_num = 2;
}
of_property_read_string_index(dev->of_node,
"clock-output-names", 0, &clk_name[0]);
i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(NULL, clk_name[0],
p_names[0], CLK_SET_RATE_PARENT,
i2s->addr + I2SMOD, reg_info->cdclkcon_off,
CLK_GATE_SET_TO_DISABLE, i2s->lock);
i2s->clk_data.clk_num += 1;
i2s->clk_data.clks = i2s->clk_table;
ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
&i2s->clk_data);
if (ret < 0) {
dev_err(dev, "failed to add clock provider: %d\n", ret);
i2s_unregister_clocks(i2s);
}
return ret;
}
static int samsung_i2s_probe(struct platform_device *pdev) static int samsung_i2s_probe(struct platform_device *pdev)
{ {
struct i2s_dai *pri_dai, *sec_dai = NULL; struct i2s_dai *pri_dai, *sec_dai = NULL;
@ -1164,7 +1227,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
u32 regs_base, quirks = 0, idma_addr = 0; u32 regs_base, quirks = 0, idma_addr = 0;
struct device_node *np = pdev->dev.of_node; struct device_node *np = pdev->dev.of_node;
const struct samsung_i2s_dai_data *i2s_dai_data; const struct samsung_i2s_dai_data *i2s_dai_data;
int ret = 0; int ret;
/* Call during Seconday interface registration */ /* Call during Seconday interface registration */
i2s_dai_data = samsung_i2s_get_driver_data(pdev); i2s_dai_data = samsung_i2s_get_driver_data(pdev);
@ -1175,11 +1238,13 @@ static int samsung_i2s_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Unable to get drvdata\n"); dev_err(&pdev->dev, "Unable to get drvdata\n");
return -EFAULT; return -EFAULT;
} }
devm_snd_soc_register_component(&sec_dai->pdev->dev, ret = devm_snd_soc_register_component(&sec_dai->pdev->dev,
&samsung_i2s_component, &samsung_i2s_component,
&sec_dai->i2s_dai_drv, 1); &sec_dai->i2s_dai_drv, 1);
samsung_asoc_dma_platform_register(&pdev->dev); if (ret != 0)
return 0; return ret;
return samsung_asoc_dma_platform_register(&pdev->dev);
} }
pri_dai = i2s_alloc_dai(pdev, false); pri_dai = i2s_alloc_dai(pdev, false);
@ -1188,6 +1253,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
} }
spin_lock_init(&pri_dai->spinlock);
pri_dai->lock = &pri_dai->spinlock;
if (!np) { if (!np) {
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!res) { if (!res) {
@ -1229,25 +1297,29 @@ static int samsung_i2s_probe(struct platform_device *pdev)
} }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) { pri_dai->addr = devm_ioremap_resource(&pdev->dev, res);
dev_err(&pdev->dev, "Unable to get I2S SFR address\n"); if (IS_ERR(pri_dai->addr))
return -ENXIO; return PTR_ERR(pri_dai->addr);
}
if (!request_mem_region(res->start, resource_size(res),
"samsung-i2s")) {
dev_err(&pdev->dev, "Unable to request SFR region\n");
return -EBUSY;
}
regs_base = res->start; regs_base = res->start;
pri_dai->clk = devm_clk_get(&pdev->dev, "iis");
if (IS_ERR(pri_dai->clk)) {
dev_err(&pdev->dev, "Failed to get iis clock\n");
return PTR_ERR(pri_dai->clk);
}
ret = clk_prepare_enable(pri_dai->clk);
if (ret != 0) {
dev_err(&pdev->dev, "failed to enable clock: %d\n", ret);
return ret;
}
pri_dai->dma_playback.dma_addr = regs_base + I2STXD; pri_dai->dma_playback.dma_addr = regs_base + I2STXD;
pri_dai->dma_capture.dma_addr = regs_base + I2SRXD; pri_dai->dma_capture.dma_addr = regs_base + I2SRXD;
pri_dai->dma_playback.ch_name = "tx"; pri_dai->dma_playback.ch_name = "tx";
pri_dai->dma_capture.ch_name = "rx"; pri_dai->dma_capture.ch_name = "rx";
pri_dai->dma_playback.dma_size = 4; pri_dai->dma_playback.dma_size = 4;
pri_dai->dma_capture.dma_size = 4; pri_dai->dma_capture.dma_size = 4;
pri_dai->base = regs_base;
pri_dai->quirks = quirks; pri_dai->quirks = quirks;
pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs; pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
@ -1258,10 +1330,10 @@ static int samsung_i2s_probe(struct platform_device *pdev)
sec_dai = i2s_alloc_dai(pdev, true); sec_dai = i2s_alloc_dai(pdev, true);
if (!sec_dai) { if (!sec_dai) {
dev_err(&pdev->dev, "Unable to alloc I2S_sec\n"); dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
ret = -ENOMEM; return -ENOMEM;
goto err;
} }
sec_dai->lock = &pri_dai->spinlock;
sec_dai->variant_regs = pri_dai->variant_regs; sec_dai->variant_regs = pri_dai->variant_regs;
sec_dai->dma_playback.dma_addr = regs_base + I2STXDS; sec_dai->dma_playback.dma_addr = regs_base + I2STXDS;
sec_dai->dma_playback.ch_name = "tx-sec"; sec_dai->dma_playback.ch_name = "tx-sec";
@ -1273,7 +1345,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
} }
sec_dai->dma_playback.dma_size = 4; sec_dai->dma_playback.dma_size = 4;
sec_dai->base = regs_base; sec_dai->addr = pri_dai->addr;
sec_dai->clk = pri_dai->clk;
sec_dai->quirks = quirks; sec_dai->quirks = quirks;
sec_dai->idma_playback.dma_addr = idma_addr; sec_dai->idma_playback.dma_addr = idma_addr;
sec_dai->pri_dai = pri_dai; sec_dai->pri_dai = pri_dai;
@ -1282,8 +1355,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) { if (i2s_pdata && i2s_pdata->cfg_gpio && i2s_pdata->cfg_gpio(pdev)) {
dev_err(&pdev->dev, "Unable to configure gpio\n"); dev_err(&pdev->dev, "Unable to configure gpio\n");
ret = -EINVAL; return -EINVAL;
goto err;
} }
devm_snd_soc_register_component(&pri_dai->pdev->dev, devm_snd_soc_register_component(&pri_dai->pdev->dev,
@ -1292,32 +1364,30 @@ static int samsung_i2s_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev); pm_runtime_enable(&pdev->dev);
samsung_asoc_dma_platform_register(&pdev->dev); ret = samsung_asoc_dma_platform_register(&pdev->dev);
if (ret != 0)
return ret;
return 0; return i2s_register_clock_provider(pdev);
err:
if (res)
release_mem_region(regs_base, resource_size(res));
return ret;
} }
static int samsung_i2s_remove(struct platform_device *pdev) static int samsung_i2s_remove(struct platform_device *pdev)
{ {
struct i2s_dai *i2s, *other; struct i2s_dai *i2s, *other;
struct resource *res;
i2s = dev_get_drvdata(&pdev->dev); i2s = dev_get_drvdata(&pdev->dev);
other = i2s->pri_dai ? : i2s->sec_dai; other = get_other_dai(i2s);
if (other) { if (other) {
other->pri_dai = NULL; other->pri_dai = NULL;
other->sec_dai = NULL; other->sec_dai = NULL;
} else { } else {
pm_runtime_disable(&pdev->dev); pm_runtime_disable(&pdev->dev);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); }
if (res)
release_mem_region(res->start, resource_size(res)); if (!is_secondary(i2s)) {
i2s_unregister_clock_provider(pdev);
clk_disable_unprepare(i2s->clk);
} }
i2s->pri_dai = NULL; i2s->pri_dai = NULL;

View file

@ -83,22 +83,6 @@ static struct snd_soc_ops jive_ops = {
.hw_params = jive_hw_params, .hw_params = jive_hw_params,
}; };
static int jive_wm8750_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
/* These endpoints are not being used. */
snd_soc_dapm_nc_pin(dapm, "LINPUT2");
snd_soc_dapm_nc_pin(dapm, "RINPUT2");
snd_soc_dapm_nc_pin(dapm, "LINPUT3");
snd_soc_dapm_nc_pin(dapm, "RINPUT3");
snd_soc_dapm_nc_pin(dapm, "OUT3");
snd_soc_dapm_nc_pin(dapm, "MONO");
return 0;
}
static struct snd_soc_dai_link jive_dai = { static struct snd_soc_dai_link jive_dai = {
.name = "wm8750", .name = "wm8750",
.stream_name = "WM8750", .stream_name = "WM8750",
@ -106,7 +90,6 @@ static struct snd_soc_dai_link jive_dai = {
.codec_dai_name = "wm8750-hifi", .codec_dai_name = "wm8750-hifi",
.platform_name = "s3c2412-i2s", .platform_name = "s3c2412-i2s",
.codec_name = "wm8750.0-001a", .codec_name = "wm8750.0-001a",
.init = jive_wm8750_init,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAIFMT_CBS_CFS,
.ops = &jive_ops, .ops = &jive_ops,
@ -123,6 +106,7 @@ static struct snd_soc_card snd_soc_machine_jive = {
.num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets), .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
.dapm_routes = audio_map, .dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map), .num_dapm_routes = ARRAY_SIZE(audio_map),
.fully_routed = true,
}; };
static struct platform_device *jive_snd_device; static struct platform_device *jive_snd_device;

View file

@ -21,6 +21,8 @@ struct odroidx2_drv_data {
/* The I2S CDCLK output clock frequency for the MAX98090 codec */ /* The I2S CDCLK output clock frequency for the MAX98090 codec */
#define MAX98090_MCLK 19200000 #define MAX98090_MCLK 19200000
static struct snd_soc_dai_link odroidx2_dai[];
static int odroidx2_late_probe(struct snd_soc_card *card) static int odroidx2_late_probe(struct snd_soc_card *card)
{ {
struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai; struct snd_soc_dai *codec_dai = card->rtd[0].codec_dai;
@ -29,7 +31,9 @@ static int odroidx2_late_probe(struct snd_soc_card *card)
ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK, ret = snd_soc_dai_set_sysclk(codec_dai, 0, MAX98090_MCLK,
SND_SOC_CLOCK_IN); SND_SOC_CLOCK_IN);
if (ret < 0)
if (ret < 0 || of_find_property(odroidx2_dai[0].codec_of_node,
"clocks", NULL))
return ret; return ret;
/* Set the cpu DAI configuration in order to use CDCLK */ /* Set the cpu DAI configuration in order to use CDCLK */

View file

@ -136,13 +136,10 @@ static const struct snd_soc_dapm_route smdk_wm8580_audio_map[] = {
static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd) static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd)
{ {
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
/* Enabling the microphone requires the fitting of a 0R /* Enabling the microphone requires the fitting of a 0R
* resistor to connect the line from the microphone jack. * resistor to connect the line from the microphone jack.
*/ */
snd_soc_dapm_disable_pin(dapm, "MicIn"); snd_soc_dapm_disable_pin(&rtd->card->dapm, "MicIn");
return 0; return 0;
} }

View file

@ -305,11 +305,6 @@ static struct snd_pcm_ops camelot_pcm_ops = {
.pointer = camelot_pos, .pointer = camelot_pos,
}; };
static void camelot_pcm_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd) static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
{ {
struct snd_pcm *pcm = rtd->pcm; struct snd_pcm *pcm = rtd->pcm;
@ -328,7 +323,6 @@ static int camelot_pcm_new(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_platform_driver sh7760_soc_platform = { static struct snd_soc_platform_driver sh7760_soc_platform = {
.ops = &camelot_pcm_ops, .ops = &camelot_pcm_ops,
.pcm_new = camelot_pcm_new, .pcm_new = camelot_pcm_new,
.pcm_free = camelot_pcm_free,
}; };
static int sh7760_soc_platform_probe(struct platform_device *pdev) static int sh7760_soc_platform_probe(struct platform_device *pdev)

View file

@ -1762,11 +1762,6 @@ static struct snd_pcm_ops fsi_pcm_ops = {
#define PREALLOC_BUFFER (32 * 1024) #define PREALLOC_BUFFER (32 * 1024)
#define PREALLOC_BUFFER_MAX (32 * 1024) #define PREALLOC_BUFFER_MAX (32 * 1024)
static void fsi_pcm_free(struct snd_pcm *pcm)
{
snd_pcm_lib_preallocate_free_for_all(pcm);
}
static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd) static int fsi_pcm_new(struct snd_soc_pcm_runtime *rtd)
{ {
return snd_pcm_lib_preallocate_pages_for_all( return snd_pcm_lib_preallocate_pages_for_all(
@ -1818,7 +1813,6 @@ static struct snd_soc_dai_driver fsi_soc_dai[] = {
static struct snd_soc_platform_driver fsi_soc_platform = { static struct snd_soc_platform_driver fsi_soc_platform = {
.ops = &fsi_pcm_ops, .ops = &fsi_pcm_ops,
.pcm_new = fsi_pcm_new, .pcm_new = fsi_pcm_new,
.pcm_free = fsi_pcm_free,
}; };
static const struct snd_soc_component_driver fsi_soc_component = { static const struct snd_soc_component_driver fsi_soc_component = {

View file

@ -589,7 +589,6 @@ static void siu_pcm_free(struct snd_pcm *pcm)
tasklet_kill(&port_info->playback.tasklet); tasklet_kill(&port_info->playback.tasklet);
siu_free_port(port_info); siu_free_port(port_info);
snd_pcm_lib_preallocate_free_for_all(pcm);
dev_dbg(pcm->card->dev, "%s\n", __func__); dev_dbg(pcm->card->dev, "%s\n", __func__);
} }