Commit 848272e9 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branch 'asoc/topic/samsung' into asoc-next

parents 36e82da9 4718840e
......@@ -2,8 +2,10 @@ Samsung Exynos Odroid XU3/XU4 audio complex with MAX98090 codec
Required properties:
- compatible - "samsung,odroidxu3-audio" - for Odroid XU3 board,
"samsung,odroidxu4-audio" - for Odroid XU4 board
- compatible - "hardkernel,odroid-xu3-audio" - for Odroid XU3 board,
"hardkernel,odroid-xu4-audio" - for Odroid XU4 board (deprecated),
"samsung,odroid-xu3-audio" - for Odroid XU3 board (deprecated),
"samsung,odroid-xu4-audio" - for Odroid XU4 board (deprecated)
- model - the user-visible name of this sound complex
- clocks - should contain entries matching clock names in the clock-names
property
......@@ -35,7 +37,7 @@ Required sub-nodes:
Example:
sound {
compatible = "samsung,odroidxu3-audio";
compatible = "hardkernel,odroid-xu3-audio";
model = "Odroid-XU3";
samsung,audio-routing =
"Headphone Jack", "HPL",
......
......@@ -4,9 +4,13 @@ Required properties:
- compatible : "samsung,tm2-audio"
- model : the user-visible name of this sound complex
- audio-codec : the phandle of the wm5110 audio codec node,
as described in ../mfd/arizona.txt
- i2s-controller : the phandle of the I2S controller
- audio-codec : the first entry should be phandle of the wm5110 audio
codec node, as described in ../mfd/arizona.txt;
the second entry should be phandle of the HDMI
transmitter node
- i2s-controller : the list of phandle and argument tuples pointing to
I2S controllers, the first entry should be I2S0 and
the second one I2S1
- audio-amplifier : the phandle of the MAX98504 amplifier
- samsung,audio-routing : a list of the connections between audio components;
each entry is a pair of strings, the first being the
......@@ -22,8 +26,8 @@ Example:
sound {
compatible = "samsung,tm2-audio";
audio-codec = <&wm5110>;
i2s-controller = <&i2s0>;
audio-codec = <&wm5110>, <&hdmi>;
i2s-controller = <&i2s0 0>, <&i2s1 0>;
audio-amplifier = <&max98504>;
mic-bias-gpios = <&gpr3 2 0>;
model = "wm5110";
......
......@@ -7,7 +7,7 @@ Required SoC Specific Properties:
- samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
secondary fifo, s/w reset control and internal mux for root clk src.
- samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
playback, sterio channel capture, secondary fifo using internal
playback, stereo channel capture, secondary fifo using internal
or external dma, s/w reset control, internal mux for root clk src
and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
is to allow transfer of multiple channel audio data on single data line.
......@@ -25,7 +25,7 @@ Required SoC Specific Properties:
These strings correspond 1:1 with the ordered pairs in dmas.
- clocks: Handle to iis clock and RCLK source clk.
- clock-names:
i2s0 uses some base clks from CMU and some are from audio subsystem internal
i2s0 uses some base clocks from CMU and some are from audio subsystem internal
clock controller. The clock names for i2s0 should be "iis", "i2s_opclk0" and
"i2s_opclk1" as shown in the example below.
i2s1 and i2s2 uses clocks from CMU. The clock names for i2s1 and i2s2 should
......@@ -36,9 +36,9 @@ Required SoC Specific Properties:
- #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.
- clock-output-names (deprecated): 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 respectively.
There are following clocks available at the I2S device nodes:
CLK_I2S_CDCLK - the CDCLK (CODECLKO) gate clock,
......@@ -49,9 +49,10 @@ There are following clocks available at the I2S device nodes:
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.
in the IIS Multi Audio Interface.
Note: Old DTs may not have the #clock-cells property and then not use the I2S
node as a clock supplier.
Optional SoC Specific Properties:
......@@ -59,6 +60,7 @@ Optional SoC Specific Properties:
sub system(used in secondary sound source).
- pinctrl-0: Should specify pin control groups used for this controller.
- pinctrl-names: Should contain only one value - "default".
- #sound-dai-cells: should be 1.
Example:
......@@ -74,9 +76,9 @@ i2s0: i2s@3830000 {
<&clock_audss EXYNOS_I2S_BUS>,
<&clock_audss EXYNOS_SCLK_I2S>;
clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
#clock-cells;
clock-output-names = "i2s_cdclk0";
#clock-cells = <1>;
samsung,idma-addr = <0x03000000>;
pinctrl-names = "default";
pinctrl-0 = <&i2s0_bus>;
#sound-dai-cells = <1>;
};
......@@ -5,8 +5,17 @@ Required properties:
"google,snow-audio-max98090" or
"google,snow-audio-max98091" or
"google,snow-audio-max98095"
- samsung,i2s-controller: The phandle of the Samsung I2S controller
- samsung,audio-codec: The phandle of the audio codec
- samsung,i2s-controller (deprecated): The phandle of the Samsung I2S controller
- samsung,audio-codec (deprecated): The phandle of the audio codec
Required sub-nodes:
- 'cpu' subnode with a 'sound-dai' property containing the phandle of the I2S
controller
- 'codec' subnode with a 'sound-dai' property containing list of phandles
to the CODEC nodes, first entry must be the phandle of the MAX98090,
MAX98091 or MAX98095 CODEC (exact device type is indicated by the compatible
string) and the second entry must be the phandle of the HDMI IP block node
Optional:
- samsung,model: The name of the sound-card
......
......@@ -1807,6 +1807,7 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
int snd_soc_of_get_dai_link_codecs(struct device *dev,
struct device_node *of_node,
struct snd_soc_dai_link *dai_link);
void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link);
int snd_soc_add_dai_link(struct snd_soc_card *card,
struct snd_soc_dai_link *dai_link);
......
......@@ -21,8 +21,6 @@ obj-$(CONFIG_SND_SAMSUNG_I2S) += snd-soc-idma.o
# S3C24XX Machine Support
snd-soc-jive-wm8750-objs := jive_wm8750.o
snd-soc-neo1973-wm8753-objs := neo1973_wm8753.o
snd-soc-smdk2443-wm9710-objs := smdk2443_wm9710.o
snd-soc-ln2440sbc-alc650-objs := ln2440sbc_alc650.o
snd-soc-s3c24xx-uda134x-objs := s3c24xx_uda134x.o
snd-soc-s3c24xx-simtec-objs := s3c24xx_simtec.o
snd-soc-s3c24xx-simtec-hermes-objs := s3c24xx_simtec_hermes.o
......@@ -32,7 +30,6 @@ snd-soc-rx1950-uda1380-objs := rx1950_uda1380.o
snd-soc-smdk-wm8580-objs := smdk_wm8580.o
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-smdk-spdif-objs := smdk_spdif.o
snd-soc-smdk-wm8994pcm-objs := smdk_wm8994pcm.o
......
......@@ -65,11 +65,12 @@
#define CON_RXDMA_ACTIVE (1 << 1)
#define CON_ACTIVE (1 << 0)
#define MOD_OPCLK_CDCLK_OUT (0 << 30)
#define MOD_OPCLK_CDCLK_IN (1 << 30)
#define MOD_OPCLK_BCLK_OUT (2 << 30)
#define MOD_OPCLK_PCLK (3 << 30)
#define MOD_OPCLK_MASK (3 << 30)
#define MOD_OPCLK_SHIFT 30
#define MOD_OPCLK_CDCLK_OUT (0 << MOD_OPCLK_SHIFT)
#define MOD_OPCLK_CDCLK_IN (1 << MOD_OPCLK_SHIFT)
#define MOD_OPCLK_BCLK_OUT (2 << MOD_OPCLK_SHIFT)
#define MOD_OPCLK_PCLK (3 << MOD_OPCLK_SHIFT)
#define MOD_OPCLK_MASK (3 << MOD_OPCLK_SHIFT)
#define MOD_TXS_IDMA (1 << 28) /* Sec_TXFIFO use I-DMA */
#define MOD_BLCS_SHIFT 26
......
......@@ -489,7 +489,7 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
switch (clk_id) {
case SAMSUNG_I2S_OPCLK:
mask = MOD_OPCLK_MASK;
val = dir;
val = (dir << MOD_OPCLK_SHIFT) & MOD_OPCLK_MASK;
break;
case SAMSUNG_I2S_CDCLK:
mask = 1 << i2s_regs->cdclkcon_off;
......@@ -656,8 +656,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
tmp |= mod_slave;
break;
case SND_SOC_DAIFMT_CBS_CFS:
/* Set default source clock in Master mode */
if (i2s->rclk_srcrate == 0)
/*
* Set default source clock in Master mode, only when the
* CLK_I2S_RCLK_SRC clock is not exposed so we ensure any
* clock configuration assigned in DT is not overwritten.
*/
if (i2s->rclk_srcrate == 0 && i2s->clk_data.clks == NULL)
i2s_set_sysclk(dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
break;
......@@ -881,6 +885,11 @@ static int config_setup(struct i2s_dai *i2s)
return 0;
if (!(i2s->quirks & QUIRK_NO_MUXPSR)) {
struct clk *rclksrc = i2s->clk_table[CLK_I2S_RCLK_SRC];
if (rclksrc && !IS_ERR(rclksrc))
i2s->rclk_srcrate = clk_get_rate(rclksrc);
psr = i2s->rclk_srcrate / i2s->frmclk / rfs;
writel(((psr - 1) << 8) | PSR_PSREN, i2s->addr + I2SPSR);
dev_dbg(&i2s->pdev->dev,
......@@ -1184,11 +1193,13 @@ static void i2s_unregister_clock_provider(struct platform_device *pdev)
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 * const i2s_clk_desc[] = { "cdclk", "rclk_src", "prescaler" };
const char *clk_name[2] = { "i2s_opclk0", "i2s_opclk1" };
const char *p_names[2] = { NULL };
struct device *dev = &pdev->dev;
struct i2s_dai *i2s = dev_get_drvdata(dev);
const struct samsung_i2s_variant_regs *reg_info = i2s->variant_regs;
const char *i2s_clk_name[ARRAY_SIZE(i2s_clk_desc)];
struct clk *rclksrc;
int ret, i;
......@@ -1205,30 +1216,38 @@ static int i2s_register_clock_provider(struct platform_device *pdev)
clk_put(rclksrc);
}
for (i = 0; i < ARRAY_SIZE(i2s_clk_desc); i++) {
i2s_clk_name[i] = devm_kasprintf(dev, GFP_KERNEL, "%s_%s",
dev_name(dev), i2s_clk_desc[i]);
if (!i2s_clk_name[i])
return -ENOMEM;
}
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(dev,
"i2s_rclksrc", p_names, ARRAY_SIZE(p_names),
i2s_clk_name[CLK_I2S_RCLK_SRC], 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(dev,
"i2s_presc", "i2s_rclksrc",
i2s_clk_name[CLK_I2S_RCLK_PSR],
i2s_clk_name[CLK_I2S_RCLK_SRC],
CLK_SET_RATE_PARENT,
i2s->addr + I2SPSR, 8, 6, 0, i2s->lock);
p_names[0] = "i2s_presc";
p_names[0] = i2s_clk_name[CLK_I2S_RCLK_PSR];
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(dev, clk_name[0],
p_names[0], CLK_SET_RATE_PARENT,
i2s->clk_table[CLK_I2S_CDCLK] = clk_register_gate(dev,
i2s_clk_name[CLK_I2S_CDCLK], p_names[0],
CLK_SET_RATE_PARENT,
i2s->addr + I2SMOD, reg_info->cdclkcon_off,
CLK_GATE_SET_TO_DISABLE, i2s->lock);
......@@ -1385,9 +1404,14 @@ static int samsung_i2s_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
ret = i2s_register_clock_provider(pdev);
if (!ret)
return 0;
if (ret < 0)
goto err_disable_pm;
pri_dai->op_clk = clk_get_parent(pri_dai->clk_table[CLK_I2S_RCLK_SRC]);
return 0;
err_disable_pm:
pm_runtime_disable(&pdev->dev);
err_disable_clk:
clk_disable_unprepare(pri_dai->clk);
......
......@@ -16,11 +16,16 @@
#define SAMSUNG_I2S_DAI "samsung-i2s"
#define SAMSUNG_I2S_DAI_SEC "samsung-i2s-sec"
#define SAMSUNG_I2S_DIV_BCLK 1
#define SAMSUNG_I2S_DIV_BCLK 1
#define SAMSUNG_I2S_RCLKSRC_0 0
#define SAMSUNG_I2S_RCLKSRC_1 1
#define SAMSUNG_I2S_RCLKSRC_0 0
#define SAMSUNG_I2S_RCLKSRC_1 1
#define SAMSUNG_I2S_CDCLK 2
/* Operation clock for IIS logic */
#define SAMSUNG_I2S_OPCLK 3
#define SAMSUNG_I2S_OPCLK_CDCLK_OUT 0 /* CODEC clock out */
#define SAMSUNG_I2S_OPCLK_CDCLK_IN 1 /* CODEC clock in */
#define SAMSUNG_I2S_OPCLK_BCLK_OUT 2 /* Bit clock out */
#define SAMSUNG_I2S_OPCLK_PCLK 3 /* Audio bus clock */
#endif /* __SND_SOC_SAMSUNG_I2S_H */
......@@ -36,23 +36,24 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
unsigned int pll_freq, rclk_freq;
unsigned int pll_freq, rclk_freq, rfs;
int ret;
switch (params_rate(params)) {
case 32000:
case 64000:
pll_freq = 131072006U;
pll_freq = 196608001U;
rfs = 384;
break;
case 44100:
case 88200:
case 176400:
pll_freq = 180633609U;
rfs = 512;
break;
case 32000:
case 48000:
case 96000:
case 192000:
pll_freq = 196608001U;
rfs = 512;
break;
default:
return -EINVAL;
......@@ -67,7 +68,7 @@ static int odroid_card_hw_params(struct snd_pcm_substream *substream,
* frequency values due to the EPLL output frequency not being exact
* multiple of the audio sampling rate.
*/
rclk_freq = params_rate(params) * 256 + 1;
rclk_freq = params_rate(params) * rfs + 1;
ret = clk_set_rate(priv->sclk_i2s, rclk_freq);
if (ret < 0)
......@@ -90,18 +91,6 @@ static const struct snd_soc_ops odroid_card_ops = {
.hw_params = odroid_card_hw_params,
};
static void odroid_put_codec_of_nodes(struct snd_soc_dai_link *link)
{
struct snd_soc_dai_link_component *component = link->codecs;
int i;
for (i = 0; i < link->num_codecs; i++, component++) {
if (!component->of_node)
break;
of_node_put(component->of_node);
}
}
static int odroid_audio_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -196,7 +185,7 @@ static int odroid_audio_probe(struct platform_device *pdev)
err_put_i2s_n:
of_node_put(link->cpu_of_node);
err_put_codec_n:
odroid_put_codec_of_nodes(link);
snd_soc_of_put_dai_link_codecs(link);
return ret;
}
......@@ -205,7 +194,7 @@ static int odroid_audio_remove(struct platform_device *pdev)
struct odroid_priv *priv = platform_get_drvdata(pdev);
of_node_put(priv->dai_link.cpu_of_node);
odroid_put_codec_of_nodes(&priv->dai_link);
snd_soc_of_put_dai_link_codecs(&priv->dai_link);
clk_put(priv->sclk_i2s);
clk_put(priv->clk_i2s_bus);
......@@ -213,8 +202,10 @@ static int odroid_audio_remove(struct platform_device *pdev)
}
static const struct of_device_id odroid_audio_of_match[] = {
{ .compatible = "hardkernel,odroid-xu3-audio" },
{ .compatible = "hardkernel,odroid-xu4-audio" },
{ .compatible = "samsung,odroid-xu3-audio" },
{ .compatible = "samsung,odroid-xu4-audio"},
{ .compatible = "samsung,odroid-xu4-audio" },
{ },
};
MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
......
......@@ -11,97 +11,207 @@
* General Public License for more details.
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "i2s.h"
#define FIN_PLL_RATE 24000000
static struct snd_soc_dai_link snow_dai[] = {
{
.name = "Primary",
.stream_name = "Primary",
.codec_dai_name = "HiFi",
.dai_fmt = SND_SOC_DAIFMT_I2S |
SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
},
struct snow_priv {
struct snd_soc_dai_link dai_link;
struct clk *clk_i2s_bus;
};
static int snow_card_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
static const unsigned int pll_rate[] = {
73728000U, 67737602U, 49152000U, 45158401U, 32768001U
};
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snow_priv *priv = snd_soc_card_get_drvdata(rtd->card);
int bfs, psr, rfs, bitwidth;
unsigned long int rclk;
long int freq = -EINVAL;
int ret, i;
bitwidth = snd_pcm_format_width(params_format(params));
if (bitwidth < 0) {
dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);
return bitwidth;
}
if (bitwidth != 16 && bitwidth != 24) {
dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);
return -EINVAL;
}
bfs = 2 * bitwidth;
switch (params_rate(params)) {
case 16000:
case 22050:
case 24000:
case 32000:
case 44100:
case 48000:
case 88200:
case 96000:
rfs = 8 * bfs;
break;
case 64000:
rfs = 384;
break;
case 8000:
case 11025:
case 12000:
rfs = 16 * bfs;
break;
default:
return -EINVAL;
}
rclk = params_rate(params) * rfs;
for (psr = 8; psr > 0; psr /= 2) {
for (i = 0; i < ARRAY_SIZE(pll_rate); i++) {
if ((pll_rate[i] - rclk * psr) <= 2) {
freq = pll_rate[i];
break;
}
}
}
if (freq < 0) {
dev_err(rtd->card->dev, "Unsupported RCLK rate: %lu\n", rclk);
return -EINVAL;
}
ret = clk_set_rate(priv->clk_i2s_bus, freq);
if (ret < 0) {
dev_err(rtd->card->dev, "I2S bus clock rate set failed\n");
return ret;
}
return 0;
}
static const struct snd_soc_ops snow_card_ops = {
.hw_params = snow_card_hw_params,
};
static int snow_late_probe(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
struct snd_soc_dai *cpu_dai;
int ret;
rtd = snd_soc_get_pcm_runtime(card, card->dai_link[0].name);
codec_dai = rtd->codec_dai;
cpu_dai = rtd->cpu_dai;
/* Set the MCLK rate for the codec */
ret = snd_soc_dai_set_sysclk(codec_dai, 0,
FIN_PLL_RATE, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
/* In the multi-codec case codec_dais 0 is MAX98095 and 1 is HDMI. */
if (rtd->num_codecs > 1)
codec_dai = rtd->codec_dais[0];
else
codec_dai = rtd->codec_dai;
/* Select I2S Bus clock to set RCLK and BCLK */
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
0, SND_SOC_CLOCK_IN);
if (ret < 0)
return ret;
return 0;
/* Set the MCLK rate for the codec */
return snd_soc_dai_set_sysclk(codec_dai, 0,
FIN_PLL_RATE, SND_SOC_CLOCK_IN);
}
static struct snd_soc_card snow_snd = {
.name = "Snow-I2S",
.owner = THIS_MODULE,
.dai_link = snow_dai,
.num_links = ARRAY_SIZE(snow_dai),
.late_probe = snow_late_probe,
};
static int snow_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct snd_soc_card *card = &snow_snd;
struct device_node *i2s_node, *codec_node;
int i, ret;
i2s_node = of_parse_phandle(pdev->dev.of_node,
"samsung,i2s-controller", 0);
if (!i2s_node) {
dev_err(&pdev->dev,
"Property 'i2s-controller' missing or invalid\n");
return -EINVAL;
}
struct device_node *cpu, *codec;
struct snd_soc_dai_link *link;
struct snow_priv *priv;
int ret;
codec_node = of_parse_phandle(pdev->dev.of_node,
"samsung,audio-codec", 0);
if (!codec_node) {
dev_err(&pdev->dev,
"Property 'audio-codec' missing or invalid\n");
return -EINVAL;
}
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
link = &priv->dai_link;
link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS;
link->name = "Primary";
link->stream_name = link->name;
card->dai_link = link;
card->num_links = 1;
card->dev = dev;
/* Try new DT bindings with HDMI support first. */
cpu = of_get_child_by_name(dev->of_node, "cpu");
if (cpu) {
link->ops = &snow_card_ops;
for (i = 0; i < ARRAY_SIZE(snow_dai); i++) {
snow_dai[i].codec_of_node = codec_node;
snow_dai[i].cpu_of_node = i2s_node;
snow_dai[i].platform_of_node = i2s_node;
link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
of_node_put(cpu);
if (!link->cpu_of_node) {
dev_err(dev, "Failed parsing cpu/sound-dai property\n");
return -EINVAL;
}
codec = of_get_child_by_name(dev->of_node, "codec");
ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
of_node_put(codec);
if (ret < 0) {
of_node_put(link->cpu_of_node);
dev_err(dev, "Failed parsing codec node\n");
return ret;
}
priv->clk_i2s_bus = of_clk_get_by_name(link->cpu_of_node,
"i2s_opclk0");
if (IS_ERR(priv->clk_i2s_bus)) {
snd_soc_of_put_dai_link_codecs(link);
of_node_put(link->cpu_of_node);
return PTR_ERR(priv->clk_i2s_bus);
}
} else {
link->codec_dai_name = "HiFi",
link->cpu_of_node = of_parse_phandle(dev->of_node,
"samsung,i2s-controller", 0);
if (!link->cpu_of_node) {
dev_err(dev, "i2s-controller property parse error\n");
return -EINVAL;
}
link->codec_of_node = of_parse_phandle(dev->of_node,
"samsung,audio-codec", 0);
if (!link->codec_of_node) {
of_node_put(link->cpu_of_node);
dev_err(dev, "audio-codec property parse error\n");
return -EINVAL;
}
}
card->dev = &pdev->dev;
link->platform_of_node = link->cpu_of_node;
/* Update card-name if provided through DT, else use default name */
snd_soc_of_parse_card_name(card, "samsung,model");
ret = devm_snd_soc_register_card(&pdev->dev, card);
snd_soc_card_set_drvdata(card, priv);
ret = devm_snd_soc_register_card(dev, card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
return ret;
......@@ -110,6 +220,20 @@ static int snow_probe(struct platform_device *pdev)
return ret;
}
static int snow_remove(struct platform_device *pdev)
{
struct snow_priv *priv = platform_get_drvdata(pdev);
struct snd_soc_dai_link *link = &priv->dai_link;
of_node_put(link->cpu_of_node);
of_node_put(link->codec_of_node);
snd_soc_of_put_dai_link_codecs(link);
clk_put(priv->clk_i2s_bus);
return 0;
}
static const struct of_device_id snow_of_match[] = {
{ .compatible = "google,snow-audio-max98090", },
{ .compatible = "google,snow-audio-max98091", },
......@@ -125,6 +249,7 @@ static struct platform_driver snow_driver = {
.of_match_table = snow_of_match,
},
.probe = snow_probe,
.remove = snow_remove,
};
module_platform_driver(snow_driver);
......
......@@ -210,6 +210,59 @@ static struct snd_soc_ops tm2_aif2_ops = {
.hw_free = tm2_aif2_hw_free,
};
static int tm2_hdmi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
unsigned int bfs;
int bitwidth, ret;
bitwidth = snd_pcm_format_width(params_format(params));
if (bitwidth < 0) {
dev_err(rtd->card->dev, "Invalid bit-width: %d\n", bitwidth);
return bitwidth;
}
switch (bitwidth) {
case 48:
bfs = 64;
break;
case 16:
bfs = 32;
break;
default:
dev_err(rtd->card->dev, "Unsupported bit-width: %d\n", bitwidth);
return -EINVAL;
}
switch (params_rate(params)) {
case 48000:
case 96000:
case 192000:
break;
default:
dev_err(rtd->card->dev, "Unsupported sample rate: %d\n",
params_rate(params));
return -EINVAL;
}
ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_OPCLK,
0, SAMSUNG_I2S_OPCLK_PCLK);
if (ret < 0)
return ret;
ret = snd_soc_dai_set_clkdiv(cpu_dai, SAMSUNG_I2S_DIV_BCLK, bfs);
if (ret < 0)
return ret;
return 0;
}
static struct snd_soc_ops tm2_hdmi_ops = {
.hw_params = tm2_hdmi_hw_params,
};
static int tm2_mic_bias(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
......@@ -405,6 +458,12 @@ static struct snd_soc_dai_link tm2_dai_links[] = {
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM,
.ignore_suspend = 1,
}, {
.name = "HDMI",
.stream_name = "i2s1",
.ops = &tm2_hdmi_ops,
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
}
};
......@@ -412,7 +471,6 @@ static struct snd_soc_card tm2_card = {
.owner = THIS_MODULE,
.dai_link = tm2_dai_links,
.num_links = ARRAY_SIZE(tm2_dai_links),
.controls = tm2_controls,
.num_controls = ARRAY_SIZE(tm2_controls),
.dapm_widgets = tm2_dapm_widgets,
......@@ -426,11 +484,14 @@ static struct snd_soc_card tm2_card = {
static int tm2_probe(struct platform_device *pdev)
{
struct device_node *cpu_dai_node[2] = {};
struct device_node *codec_dai_node[2] = {};
const char *cells_name = NULL;
struct device *dev = &pdev->dev;
struct snd_soc_card *card = &tm2_card;
struct tm2_machine_priv *priv;
struct device_node *cpu_dai_node, *codec_dai_node;
int ret, i;
struct of_phandle_args args;
int num_codecs, ret, i;
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
......@@ -464,47 +525,92 @@ static int tm2_probe(struct platform_device *pdev)
return -EINVAL;
}
cpu_dai_node = of_parse_phandle(dev->of_node, "i2s-controller", 0);
if (!cpu_dai_node) {
dev_err(dev, "i2s-controllers property invalid or missing\n");
ret = -EINVAL;
goto amp_node_put;
num_codecs = of_count_phandle_with_args(dev->of_node, "audio-codec",
NULL);
/* Skip the HDMI link if not specified in DT */
if (num_codecs > 1) {
card->num_links = ARRAY_SIZE(tm2_dai_links);
cells_name = "#sound-dai-cells";
} else {
card->num_links = ARRAY_SIZE(tm2_dai_links) - 1;
}
codec_dai_node = of_parse_phandle(dev->of_node, "audio-codec", 0);
if (!codec_dai_node) {
dev_err(dev, "audio-codec property invalid or missing\n");
ret = -EINVAL;
goto cpu_dai_node_put;
for (i = 0; i < num_codecs; i++) {
struct of_phandle_args args;
ret = of_parse_phandle_with_args(dev->of_node, "i2s-controller",
cells_name, i, &args);
if (!args.np) {
dev_err(dev, "i2s-controller property parse error: %d\n", i);
ret = -EINVAL;
goto dai_node_put;
}
cpu_dai_node[i] = args.np;
codec_dai_node[i] = of_parse_phandle(dev->of_node,
"audio-codec", i);
if (!codec_dai_node[i]) {
dev_err(dev, "audio-codec property parse error\n");
ret = -EINVAL;
goto dai_node_put;
}
}
/* Initialize WM5110 - I2S and HDMI - I2S1 DAI links */
for (i = 0; i < card->num_links; i++) {
unsigned int dai_index = 0; /* WM5110 */
card->dai_link[i].cpu_name = NULL;
card->dai_link[i].platform_name = NULL;
card->dai_link[i].codec_of_node = codec_dai_node;
card->dai_link[i].cpu_of_node = cpu_dai_node;
card->dai_link[i].platform_of_node = cpu_dai_node;
if (num_codecs > 1 && i == card->num_links - 1)
dai_index = 1; /* HDMI */
card->dai_link[i].codec_of_node = codec_dai_node[dai_index];
card->dai_link[i].cpu_of_node = cpu_dai_node[dai_index];
card->dai_link[i].platform_of_node = cpu_dai_node[dai_index];
}
if (num_codecs > 1) {
/* HDMI DAI link (I2S1) */
i = card->num_links - 1;
ret = of_parse_phandle_with_fixed_args(dev->of_node,
"audio-codec", 0, 1, &args);
if (ret) {
dev_err(dev, "audio-codec property parse error\n");
goto dai_node_put;
}
ret = snd_soc_get_dai_name(&args, &card->dai_link[i].codec_dai_name);
if (ret) {
dev_err(dev, "Unable to get codec_dai_name\n");
goto dai_node_put;
}
}
ret = devm_snd_soc_register_component(dev, &tm2_component,
tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));
if (ret < 0) {
dev_err(dev, "Failed to register component: %d\n", ret);
goto codec_dai_node_put;
goto dai_node_put;
}
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0) {
dev_err(dev, "Failed to register card: %d\n", ret);
goto codec_dai_node_put;
goto dai_node_put;
}
dai_node_put:
for (i = 0; i < num_codecs; i++) {
of_node_put(codec_dai_node[i]);
of_node_put(cpu_dai_node[i]);
}
codec_dai_node_put:
of_node_put(codec_dai_node);
cpu_dai_node_put:
of_node_put(cpu_dai_node);
amp_node_put:
of_node_put(card->aux_dev[0].codec_of_node);
return ret;
}
......
......@@ -4356,6 +4356,26 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
}
EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
/*
* snd_soc_of_put_dai_link_codecs - Dereference device nodes in the codecs array
* @dai_link: DAI link
*
* Dereference device nodes acquired by snd_soc_of_get_dai_link_codecs().
*/
void snd_soc_of_put_dai_link_codecs(struct snd_soc_dai_link *dai_link)
{
struct snd_soc_dai_link_component *component = dai_link->codecs;
int index;
for (index = 0; index < dai_link->num_codecs; index++, component++) {
if (!component->of_node)
break;
of_node_put(component->of_node);
component->of_node = NULL;
}
}
EXPORT_SYMBOL_GPL(snd_soc_of_put_dai_link_codecs);
/*
* snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree
* @dev: Card device
......@@ -4365,7 +4385,8 @@ EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
* Builds an array of CODEC DAI components from the DAI link property
* 'sound-dai'.
* The array is set in the DAI link and the number of DAIs is set accordingly.
* The device nodes in the array (of_node) must be dereferenced by the caller.
* The device nodes in the array (of_node) must be dereferenced by calling
* snd_soc_of_put_dai_link_codecs() on @dai_link.
*
* Returns 0 for success
*/
......@@ -4413,14 +4434,7 @@ int snd_soc_of_get_dai_link_codecs(struct device *dev,
}
return 0;
err:
for (index = 0, component = dai_link->codecs;
index < dai_link->num_codecs;
index++, component++) {
if (!component->of_node)
break;
of_node_put(component->of_node);
component->of_node = NULL;
}
snd_soc_of_put_dai_link_codecs(dai_link);
dai_link->codecs = NULL;
dai_link->num_codecs = 0;
return ret;
......
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