diff options
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/Kconfig | 1 | ||||
-rw-r--r-- | sound/soc/Makefile | 1 | ||||
-rw-r--r-- | sound/soc/codecs/rt286.c | 25 | ||||
-rw-r--r-- | sound/soc/omap/Kconfig | 2 | ||||
-rw-r--r-- | sound/soc/omap/n810.c | 23 | ||||
-rw-r--r-- | sound/soc/omap/omap-hdmi-audio.c | 2 | ||||
-rw-r--r-- | sound/soc/qcom/Kconfig | 25 | ||||
-rw-r--r-- | sound/soc/qcom/Makefile | 11 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-cpu.c | 491 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-lpaif-ipq806x.h | 172 | ||||
-rw-r--r-- | sound/soc/qcom/lpass-platform.c | 526 | ||||
-rw-r--r-- | sound/soc/qcom/lpass.h | 51 | ||||
-rw-r--r-- | sound/soc/qcom/storm.c | 162 | ||||
-rw-r--r-- | sound/soc/sh/Kconfig | 6 | ||||
-rw-r--r-- | sound/soc/sh/rcar/Makefile | 7 | ||||
-rw-r--r-- | sound/soc/sh/rcar/adg.c | 4 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 262 | ||||
-rw-r--r-- | sound/soc/sh/rcar/dma.c | 616 | ||||
-rw-r--r-- | sound/soc/sh/rcar/dvc.c | 28 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 152 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 81 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsrc-card.c | 512 | ||||
-rw-r--r-- | sound/soc/sh/rcar/src.c | 233 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 56 |
24 files changed, 2935 insertions, 514 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index dcc79aa0236b..3ba52da18bc6 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig | |||
@@ -47,6 +47,7 @@ source "sound/soc/kirkwood/Kconfig" | |||
47 | source "sound/soc/intel/Kconfig" | 47 | source "sound/soc/intel/Kconfig" |
48 | source "sound/soc/mxs/Kconfig" | 48 | source "sound/soc/mxs/Kconfig" |
49 | source "sound/soc/pxa/Kconfig" | 49 | source "sound/soc/pxa/Kconfig" |
50 | source "sound/soc/qcom/Kconfig" | ||
50 | source "sound/soc/rockchip/Kconfig" | 51 | source "sound/soc/rockchip/Kconfig" |
51 | source "sound/soc/samsung/Kconfig" | 52 | source "sound/soc/samsung/Kconfig" |
52 | source "sound/soc/sh/Kconfig" | 53 | source "sound/soc/sh/Kconfig" |
diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 5b3c8f67c8db..974ba708b482 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile | |||
@@ -28,6 +28,7 @@ obj-$(CONFIG_SND_SOC) += nuc900/ | |||
28 | obj-$(CONFIG_SND_SOC) += omap/ | 28 | obj-$(CONFIG_SND_SOC) += omap/ |
29 | obj-$(CONFIG_SND_SOC) += kirkwood/ | 29 | obj-$(CONFIG_SND_SOC) += kirkwood/ |
30 | obj-$(CONFIG_SND_SOC) += pxa/ | 30 | obj-$(CONFIG_SND_SOC) += pxa/ |
31 | obj-$(CONFIG_SND_SOC) += qcom/ | ||
31 | obj-$(CONFIG_SND_SOC) += rockchip/ | 32 | obj-$(CONFIG_SND_SOC) += rockchip/ |
32 | obj-$(CONFIG_SND_SOC) += samsung/ | 33 | obj-$(CONFIG_SND_SOC) += samsung/ |
33 | obj-$(CONFIG_SND_SOC) += sh/ | 34 | obj-$(CONFIG_SND_SOC) += sh/ |
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 826037090c83..0fcda35a3a93 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c | |||
@@ -397,7 +397,7 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack) | |||
397 | 397 | ||
398 | if (jack) { | 398 | if (jack) { |
399 | /* enable IRQ */ | 399 | /* enable IRQ */ |
400 | if (rt286->jack->status | SND_JACK_HEADPHONE) | 400 | if (rt286->jack->status & SND_JACK_HEADPHONE) |
401 | snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); | 401 | snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO1"); |
402 | regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); | 402 | regmap_update_bits(rt286->regmap, RT286_IRQ_CTRL, 0x2, 0x2); |
403 | /* Send an initial empty report */ | 403 | /* Send an initial empty report */ |
@@ -1048,7 +1048,6 @@ static int rt286_probe(struct snd_soc_codec *codec) | |||
1048 | struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); | 1048 | struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec); |
1049 | 1049 | ||
1050 | rt286->codec = codec; | 1050 | rt286->codec = codec; |
1051 | codec->dapm.bias_level = SND_SOC_BIAS_OFF; | ||
1052 | 1051 | ||
1053 | if (rt286->i2c->irq) { | 1052 | if (rt286->i2c->irq) { |
1054 | regmap_update_bits(rt286->regmap, | 1053 | regmap_update_bits(rt286->regmap, |
@@ -1220,7 +1219,7 @@ static int rt286_i2c_probe(struct i2c_client *i2c, | |||
1220 | { | 1219 | { |
1221 | struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); | 1220 | struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev); |
1222 | struct rt286_priv *rt286; | 1221 | struct rt286_priv *rt286; |
1223 | int i, ret; | 1222 | int i, ret, val; |
1224 | 1223 | ||
1225 | rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), | 1224 | rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286), |
1226 | GFP_KERNEL); | 1225 | GFP_KERNEL); |
@@ -1235,11 +1234,15 @@ static int rt286_i2c_probe(struct i2c_client *i2c, | |||
1235 | return ret; | 1234 | return ret; |
1236 | } | 1235 | } |
1237 | 1236 | ||
1238 | regmap_read(rt286->regmap, | 1237 | ret = regmap_read(rt286->regmap, |
1239 | RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret); | 1238 | RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &val); |
1240 | if (ret != RT286_VENDOR_ID && ret != RT288_VENDOR_ID) { | 1239 | if (ret != 0) { |
1240 | dev_err(&i2c->dev, "I2C error %d\n", ret); | ||
1241 | return ret; | ||
1242 | } | ||
1243 | if (val != RT286_VENDOR_ID && val != RT288_VENDOR_ID) { | ||
1241 | dev_err(&i2c->dev, | 1244 | dev_err(&i2c->dev, |
1242 | "Device with ID register %x is not rt286\n", ret); | 1245 | "Device with ID register %x is not rt286\n", val); |
1243 | return -ENODEV; | 1246 | return -ENODEV; |
1244 | } | 1247 | } |
1245 | 1248 | ||
@@ -1247,6 +1250,14 @@ static int rt286_i2c_probe(struct i2c_client *i2c, | |||
1247 | rt286->i2c = i2c; | 1250 | rt286->i2c = i2c; |
1248 | i2c_set_clientdata(i2c, rt286); | 1251 | i2c_set_clientdata(i2c, rt286); |
1249 | 1252 | ||
1253 | /* restore codec default */ | ||
1254 | for (i = 0; i < INDEX_CACHE_SIZE; i++) | ||
1255 | regmap_write(rt286->regmap, rt286->index_cache[i].reg, | ||
1256 | rt286->index_cache[i].def); | ||
1257 | for (i = 0; i < ARRAY_SIZE(rt286_reg); i++) | ||
1258 | regmap_write(rt286->regmap, rt286_reg[i].reg, | ||
1259 | rt286_reg[i].def); | ||
1260 | |||
1250 | if (pdata) | 1261 | if (pdata) |
1251 | rt286->pdata = *pdata; | 1262 | rt286->pdata = *pdata; |
1252 | 1263 | ||
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig index e7c78b0406b5..6768e4f7d7d0 100644 --- a/sound/soc/omap/Kconfig +++ b/sound/soc/omap/Kconfig | |||
@@ -105,7 +105,7 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040 | |||
105 | select SND_OMAP_SOC_MCPDM | 105 | select SND_OMAP_SOC_MCPDM |
106 | select SND_SOC_TWL6040 | 106 | select SND_SOC_TWL6040 |
107 | select SND_SOC_DMIC | 107 | select SND_SOC_DMIC |
108 | select COMMON_CLK_PALMAS if SOC_OMAP5 | 108 | select COMMON_CLK_PALMAS if MFD_PALMAS |
109 | help | 109 | help |
110 | Say Y if you want to add support for SoC audio on OMAP boards using | 110 | Say Y if you want to add support for SoC audio on OMAP boards using |
111 | ABE and twl6040 codec. This driver currently supports: | 111 | ABE and twl6040 codec. This driver currently supports: |
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 5d7f9cebe041..dcb5336b5698 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c | |||
@@ -98,12 +98,11 @@ static int n810_startup(struct snd_pcm_substream *substream) | |||
98 | { | 98 | { |
99 | struct snd_pcm_runtime *runtime = substream->runtime; | 99 | struct snd_pcm_runtime *runtime = substream->runtime; |
100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 100 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
101 | struct snd_soc_codec *codec = rtd->codec; | ||
102 | 101 | ||
103 | snd_pcm_hw_constraint_minmax(runtime, | 102 | snd_pcm_hw_constraint_minmax(runtime, |
104 | SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); | 103 | SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2); |
105 | 104 | ||
106 | n810_ext_control(&codec->dapm); | 105 | n810_ext_control(&rtd->card->dapm); |
107 | return clk_prepare_enable(sys_clkout2); | 106 | return clk_prepare_enable(sys_clkout2); |
108 | } | 107 | } |
109 | 108 | ||
@@ -255,24 +254,6 @@ static const struct snd_kcontrol_new aic33_n810_controls[] = { | |||
255 | n810_get_input, n810_set_input), | 254 | n810_get_input, n810_set_input), |
256 | }; | 255 | }; |
257 | 256 | ||
258 | static int n810_aic33_init(struct snd_soc_pcm_runtime *rtd) | ||
259 | { | ||
260 | struct snd_soc_codec *codec = rtd->codec; | ||
261 | struct snd_soc_dapm_context *dapm = &codec->dapm; | ||
262 | |||
263 | /* Not connected */ | ||
264 | snd_soc_dapm_nc_pin(dapm, "MONO_LOUT"); | ||
265 | snd_soc_dapm_nc_pin(dapm, "HPLCOM"); | ||
266 | snd_soc_dapm_nc_pin(dapm, "HPRCOM"); | ||
267 | snd_soc_dapm_nc_pin(dapm, "MIC3L"); | ||
268 | snd_soc_dapm_nc_pin(dapm, "MIC3R"); | ||
269 | snd_soc_dapm_nc_pin(dapm, "LINE1R"); | ||
270 | snd_soc_dapm_nc_pin(dapm, "LINE2L"); | ||
271 | snd_soc_dapm_nc_pin(dapm, "LINE2R"); | ||
272 | |||
273 | return 0; | ||
274 | } | ||
275 | |||
276 | /* Digital audio interface glue - connects codec <--> CPU */ | 257 | /* Digital audio interface glue - connects codec <--> CPU */ |
277 | static struct snd_soc_dai_link n810_dai = { | 258 | static struct snd_soc_dai_link n810_dai = { |
278 | .name = "TLV320AIC33", | 259 | .name = "TLV320AIC33", |
@@ -283,7 +264,6 @@ static struct snd_soc_dai_link n810_dai = { | |||
283 | .codec_dai_name = "tlv320aic3x-hifi", | 264 | .codec_dai_name = "tlv320aic3x-hifi", |
284 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | 265 | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | |
285 | SND_SOC_DAIFMT_CBM_CFM, | 266 | SND_SOC_DAIFMT_CBM_CFM, |
286 | .init = n810_aic33_init, | ||
287 | .ops = &n810_ops, | 267 | .ops = &n810_ops, |
288 | }; | 268 | }; |
289 | 269 | ||
@@ -300,6 +280,7 @@ static struct snd_soc_card snd_soc_n810 = { | |||
300 | .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), | 280 | .num_dapm_widgets = ARRAY_SIZE(aic33_dapm_widgets), |
301 | .dapm_routes = audio_map, | 281 | .dapm_routes = audio_map, |
302 | .num_dapm_routes = ARRAY_SIZE(audio_map), | 282 | .num_dapm_routes = ARRAY_SIZE(audio_map), |
283 | .fully_routed = true, | ||
303 | }; | 284 | }; |
304 | 285 | ||
305 | static struct platform_device *n810_snd_device; | 286 | static struct platform_device *n810_snd_device; |
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c index f7eb42aa3f38..4775da4c4db5 100644 --- a/sound/soc/omap/omap-hdmi-audio.c +++ b/sound/soc/omap/omap-hdmi-audio.c | |||
@@ -142,8 +142,6 @@ static int hdmi_dai_hw_params(struct snd_pcm_substream *substream, | |||
142 | 142 | ||
143 | iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; | 143 | iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE; |
144 | 144 | ||
145 | iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID; | ||
146 | |||
147 | iec->status[1] = IEC958_AES1_CON_GENERAL; | 145 | iec->status[1] = IEC958_AES1_CON_GENERAL; |
148 | 146 | ||
149 | iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; | 147 | iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC; |
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig new file mode 100644 index 000000000000..5f58e4f1bca9 --- /dev/null +++ b/sound/soc/qcom/Kconfig | |||
@@ -0,0 +1,25 @@ | |||
1 | config SND_SOC_QCOM | ||
2 | tristate "ASoC support for QCOM platforms" | ||
3 | help | ||
4 | Say Y or M if you want to add support to use audio devices | ||
5 | in Qualcomm Technologies SOC-based platforms. | ||
6 | |||
7 | config SND_SOC_LPASS_CPU | ||
8 | tristate | ||
9 | depends on SND_SOC_QCOM | ||
10 | select REGMAP_MMIO | ||
11 | |||
12 | config SND_SOC_LPASS_PLATFORM | ||
13 | tristate | ||
14 | depends on SND_SOC_QCOM | ||
15 | select REGMAP_MMIO | ||
16 | |||
17 | config SND_SOC_STORM | ||
18 | tristate "ASoC I2S support for Storm boards" | ||
19 | depends on (ARCH_QCOM && SND_SOC_QCOM) || COMPILE_TEST | ||
20 | select SND_SOC_LPASS_CPU | ||
21 | select SND_SOC_LPASS_PLATFORM | ||
22 | select SND_SOC_MAX98357A | ||
23 | help | ||
24 | Say Y or M if you want add support for SoC audio on the | ||
25 | Qualcomm Technologies IPQ806X-based Storm board. | ||
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile new file mode 100644 index 000000000000..c5ce96c761c4 --- /dev/null +++ b/sound/soc/qcom/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # Platform | ||
2 | snd-soc-lpass-cpu-objs := lpass-cpu.o | ||
3 | snd-soc-lpass-platform-objs := lpass-platform.o | ||
4 | |||
5 | obj-$(CONFIG_SND_SOC_LPASS_CPU) += snd-soc-lpass-cpu.o | ||
6 | obj-$(CONFIG_SND_SOC_LPASS_PLATFORM) += snd-soc-lpass-platform.o | ||
7 | |||
8 | # Machine | ||
9 | snd-soc-storm-objs := storm.o | ||
10 | |||
11 | obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o | ||
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c new file mode 100644 index 000000000000..6698d058de29 --- /dev/null +++ b/sound/soc/qcom/lpass-cpu.c | |||
@@ -0,0 +1,491 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * lpass-cpu.c -- ALSA SoC CPU DAI driver for QTi LPASS | ||
14 | */ | ||
15 | |||
16 | #include <linux/clk.h> | ||
17 | #include <linux/compiler.h> | ||
18 | #include <linux/device.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/ioport.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/mod_devicetable.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/of.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <sound/pcm.h> | ||
27 | #include <sound/pcm_params.h> | ||
28 | #include <linux/regmap.h> | ||
29 | #include <sound/soc.h> | ||
30 | #include <sound/soc-dai.h> | ||
31 | #include "lpass-lpaif-ipq806x.h" | ||
32 | #include "lpass.h" | ||
33 | |||
34 | static int lpass_cpu_daiops_set_sysclk(struct snd_soc_dai *dai, int clk_id, | ||
35 | unsigned int freq, int dir) | ||
36 | { | ||
37 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
38 | int ret; | ||
39 | |||
40 | ret = clk_set_rate(drvdata->mi2s_osr_clk, freq); | ||
41 | if (ret) | ||
42 | dev_err(dai->dev, "%s() error setting mi2s osrclk to %u: %d\n", | ||
43 | __func__, freq, ret); | ||
44 | |||
45 | return ret; | ||
46 | } | ||
47 | |||
48 | static int lpass_cpu_daiops_startup(struct snd_pcm_substream *substream, | ||
49 | struct snd_soc_dai *dai) | ||
50 | { | ||
51 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
52 | int ret; | ||
53 | |||
54 | ret = clk_prepare_enable(drvdata->mi2s_osr_clk); | ||
55 | if (ret) { | ||
56 | dev_err(dai->dev, "%s() error in enabling mi2s osr clk: %d\n", | ||
57 | __func__, ret); | ||
58 | return ret; | ||
59 | } | ||
60 | |||
61 | ret = clk_prepare_enable(drvdata->mi2s_bit_clk); | ||
62 | if (ret) { | ||
63 | dev_err(dai->dev, "%s() error in enabling mi2s bit clk: %d\n", | ||
64 | __func__, ret); | ||
65 | clk_disable_unprepare(drvdata->mi2s_osr_clk); | ||
66 | return ret; | ||
67 | } | ||
68 | |||
69 | return 0; | ||
70 | } | ||
71 | |||
72 | static void lpass_cpu_daiops_shutdown(struct snd_pcm_substream *substream, | ||
73 | struct snd_soc_dai *dai) | ||
74 | { | ||
75 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
76 | |||
77 | clk_disable_unprepare(drvdata->mi2s_bit_clk); | ||
78 | clk_disable_unprepare(drvdata->mi2s_osr_clk); | ||
79 | } | ||
80 | |||
81 | static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream, | ||
82 | struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) | ||
83 | { | ||
84 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
85 | snd_pcm_format_t format = params_format(params); | ||
86 | unsigned int channels = params_channels(params); | ||
87 | unsigned int rate = params_rate(params); | ||
88 | unsigned int regval; | ||
89 | int bitwidth, ret; | ||
90 | |||
91 | bitwidth = snd_pcm_format_width(format); | ||
92 | if (bitwidth < 0) { | ||
93 | dev_err(dai->dev, "%s() invalid bit width given: %d\n", | ||
94 | __func__, bitwidth); | ||
95 | return bitwidth; | ||
96 | } | ||
97 | |||
98 | regval = LPAIF_I2SCTL_LOOPBACK_DISABLE | | ||
99 | LPAIF_I2SCTL_WSSRC_INTERNAL; | ||
100 | |||
101 | switch (bitwidth) { | ||
102 | case 16: | ||
103 | regval |= LPAIF_I2SCTL_BITWIDTH_16; | ||
104 | break; | ||
105 | case 24: | ||
106 | regval |= LPAIF_I2SCTL_BITWIDTH_24; | ||
107 | break; | ||
108 | case 32: | ||
109 | regval |= LPAIF_I2SCTL_BITWIDTH_32; | ||
110 | break; | ||
111 | default: | ||
112 | dev_err(dai->dev, "%s() invalid bitwidth given: %d\n", | ||
113 | __func__, bitwidth); | ||
114 | return -EINVAL; | ||
115 | } | ||
116 | |||
117 | switch (channels) { | ||
118 | case 1: | ||
119 | regval |= LPAIF_I2SCTL_SPKMODE_SD0; | ||
120 | regval |= LPAIF_I2SCTL_SPKMONO_MONO; | ||
121 | break; | ||
122 | case 2: | ||
123 | regval |= LPAIF_I2SCTL_SPKMODE_SD0; | ||
124 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
125 | break; | ||
126 | case 4: | ||
127 | regval |= LPAIF_I2SCTL_SPKMODE_QUAD01; | ||
128 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
129 | break; | ||
130 | case 6: | ||
131 | regval |= LPAIF_I2SCTL_SPKMODE_6CH; | ||
132 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
133 | break; | ||
134 | case 8: | ||
135 | regval |= LPAIF_I2SCTL_SPKMODE_8CH; | ||
136 | regval |= LPAIF_I2SCTL_SPKMONO_STEREO; | ||
137 | break; | ||
138 | default: | ||
139 | dev_err(dai->dev, "%s() invalid channels given: %u\n", | ||
140 | __func__, channels); | ||
141 | return -EINVAL; | ||
142 | } | ||
143 | |||
144 | ret = regmap_write(drvdata->lpaif_map, | ||
145 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), regval); | ||
146 | if (ret) { | ||
147 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
148 | __func__, ret); | ||
149 | return ret; | ||
150 | } | ||
151 | |||
152 | ret = clk_set_rate(drvdata->mi2s_bit_clk, rate * bitwidth * 2); | ||
153 | if (ret) { | ||
154 | dev_err(dai->dev, "%s() error setting mi2s bitclk to %u: %d\n", | ||
155 | __func__, rate * bitwidth * 2, ret); | ||
156 | return ret; | ||
157 | } | ||
158 | |||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int lpass_cpu_daiops_hw_free(struct snd_pcm_substream *substream, | ||
163 | struct snd_soc_dai *dai) | ||
164 | { | ||
165 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
166 | int ret; | ||
167 | |||
168 | ret = regmap_write(drvdata->lpaif_map, | ||
169 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); | ||
170 | if (ret) | ||
171 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
172 | __func__, ret); | ||
173 | |||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream, | ||
178 | struct snd_soc_dai *dai) | ||
179 | { | ||
180 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
181 | int ret; | ||
182 | |||
183 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
184 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), | ||
185 | LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE); | ||
186 | if (ret) | ||
187 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
188 | __func__, ret); | ||
189 | |||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream, | ||
194 | int cmd, struct snd_soc_dai *dai) | ||
195 | { | ||
196 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
197 | int ret; | ||
198 | |||
199 | switch (cmd) { | ||
200 | case SNDRV_PCM_TRIGGER_START: | ||
201 | case SNDRV_PCM_TRIGGER_RESUME: | ||
202 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
203 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
204 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), | ||
205 | LPAIF_I2SCTL_SPKEN_MASK, | ||
206 | LPAIF_I2SCTL_SPKEN_ENABLE); | ||
207 | if (ret) | ||
208 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
209 | __func__, ret); | ||
210 | break; | ||
211 | case SNDRV_PCM_TRIGGER_STOP: | ||
212 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
213 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
214 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
215 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), | ||
216 | LPAIF_I2SCTL_SPKEN_MASK, | ||
217 | LPAIF_I2SCTL_SPKEN_DISABLE); | ||
218 | if (ret) | ||
219 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
220 | __func__, ret); | ||
221 | break; | ||
222 | } | ||
223 | |||
224 | return ret; | ||
225 | } | ||
226 | |||
227 | static struct snd_soc_dai_ops lpass_cpu_dai_ops = { | ||
228 | .set_sysclk = lpass_cpu_daiops_set_sysclk, | ||
229 | .startup = lpass_cpu_daiops_startup, | ||
230 | .shutdown = lpass_cpu_daiops_shutdown, | ||
231 | .hw_params = lpass_cpu_daiops_hw_params, | ||
232 | .hw_free = lpass_cpu_daiops_hw_free, | ||
233 | .prepare = lpass_cpu_daiops_prepare, | ||
234 | .trigger = lpass_cpu_daiops_trigger, | ||
235 | }; | ||
236 | |||
237 | static int lpass_cpu_dai_probe(struct snd_soc_dai *dai) | ||
238 | { | ||
239 | struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai); | ||
240 | int ret; | ||
241 | |||
242 | /* ensure audio hardware is disabled */ | ||
243 | ret = regmap_write(drvdata->lpaif_map, | ||
244 | LPAIF_I2SCTL_REG(LPAIF_I2S_PORT_MI2S), 0); | ||
245 | if (ret) | ||
246 | dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n", | ||
247 | __func__, ret); | ||
248 | |||
249 | return ret; | ||
250 | } | ||
251 | |||
252 | static struct snd_soc_dai_driver lpass_cpu_dai_driver = { | ||
253 | .playback = { | ||
254 | .stream_name = "lpass-cpu-playback", | ||
255 | .formats = SNDRV_PCM_FMTBIT_S16 | | ||
256 | SNDRV_PCM_FMTBIT_S24 | | ||
257 | SNDRV_PCM_FMTBIT_S32, | ||
258 | .rates = SNDRV_PCM_RATE_8000 | | ||
259 | SNDRV_PCM_RATE_16000 | | ||
260 | SNDRV_PCM_RATE_32000 | | ||
261 | SNDRV_PCM_RATE_48000 | | ||
262 | SNDRV_PCM_RATE_96000, | ||
263 | .rate_min = 8000, | ||
264 | .rate_max = 96000, | ||
265 | .channels_min = 1, | ||
266 | .channels_max = 8, | ||
267 | }, | ||
268 | .probe = &lpass_cpu_dai_probe, | ||
269 | .ops = &lpass_cpu_dai_ops, | ||
270 | }; | ||
271 | |||
272 | static const struct snd_soc_component_driver lpass_cpu_comp_driver = { | ||
273 | .name = "lpass-cpu", | ||
274 | }; | ||
275 | |||
276 | static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg) | ||
277 | { | ||
278 | int i; | ||
279 | |||
280 | for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) | ||
281 | if (reg == LPAIF_I2SCTL_REG(i)) | ||
282 | return true; | ||
283 | |||
284 | for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { | ||
285 | if (reg == LPAIF_IRQEN_REG(i)) | ||
286 | return true; | ||
287 | if (reg == LPAIF_IRQCLEAR_REG(i)) | ||
288 | return true; | ||
289 | } | ||
290 | |||
291 | for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { | ||
292 | if (reg == LPAIF_RDMACTL_REG(i)) | ||
293 | return true; | ||
294 | if (reg == LPAIF_RDMABASE_REG(i)) | ||
295 | return true; | ||
296 | if (reg == LPAIF_RDMABUFF_REG(i)) | ||
297 | return true; | ||
298 | if (reg == LPAIF_RDMAPER_REG(i)) | ||
299 | return true; | ||
300 | } | ||
301 | |||
302 | return false; | ||
303 | } | ||
304 | |||
305 | static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg) | ||
306 | { | ||
307 | int i; | ||
308 | |||
309 | for (i = 0; i < LPAIF_I2S_PORT_NUM; ++i) | ||
310 | if (reg == LPAIF_I2SCTL_REG(i)) | ||
311 | return true; | ||
312 | |||
313 | for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) { | ||
314 | if (reg == LPAIF_IRQEN_REG(i)) | ||
315 | return true; | ||
316 | if (reg == LPAIF_IRQSTAT_REG(i)) | ||
317 | return true; | ||
318 | } | ||
319 | |||
320 | for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) { | ||
321 | if (reg == LPAIF_RDMACTL_REG(i)) | ||
322 | return true; | ||
323 | if (reg == LPAIF_RDMABASE_REG(i)) | ||
324 | return true; | ||
325 | if (reg == LPAIF_RDMABUFF_REG(i)) | ||
326 | return true; | ||
327 | if (reg == LPAIF_RDMACURR_REG(i)) | ||
328 | return true; | ||
329 | if (reg == LPAIF_RDMAPER_REG(i)) | ||
330 | return true; | ||
331 | } | ||
332 | |||
333 | return false; | ||
334 | } | ||
335 | |||
336 | static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg) | ||
337 | { | ||
338 | int i; | ||
339 | |||
340 | for (i = 0; i < LPAIF_IRQ_PORT_NUM; ++i) | ||
341 | if (reg == LPAIF_IRQSTAT_REG(i)) | ||
342 | return true; | ||
343 | |||
344 | for (i = 0; i < LPAIF_RDMA_CHAN_NUM; ++i) | ||
345 | if (reg == LPAIF_RDMACURR_REG(i)) | ||
346 | return true; | ||
347 | |||
348 | return false; | ||
349 | } | ||
350 | |||
351 | static const struct regmap_config lpass_cpu_regmap_config = { | ||
352 | .reg_bits = 32, | ||
353 | .reg_stride = 4, | ||
354 | .val_bits = 32, | ||
355 | .max_register = LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MAX), | ||
356 | .writeable_reg = lpass_cpu_regmap_writeable, | ||
357 | .readable_reg = lpass_cpu_regmap_readable, | ||
358 | .volatile_reg = lpass_cpu_regmap_volatile, | ||
359 | .cache_type = REGCACHE_FLAT, | ||
360 | }; | ||
361 | |||
362 | static int lpass_cpu_platform_probe(struct platform_device *pdev) | ||
363 | { | ||
364 | struct lpass_data *drvdata; | ||
365 | struct device_node *dsp_of_node; | ||
366 | struct resource *res; | ||
367 | int ret; | ||
368 | |||
369 | dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0); | ||
370 | if (dsp_of_node) { | ||
371 | dev_err(&pdev->dev, "%s() DSP exists and holds audio resources\n", | ||
372 | __func__); | ||
373 | return -EBUSY; | ||
374 | } | ||
375 | |||
376 | drvdata = devm_kzalloc(&pdev->dev, sizeof(struct lpass_data), | ||
377 | GFP_KERNEL); | ||
378 | if (!drvdata) | ||
379 | return -ENOMEM; | ||
380 | platform_set_drvdata(pdev, drvdata); | ||
381 | |||
382 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lpass-lpaif"); | ||
383 | if (!res) { | ||
384 | dev_err(&pdev->dev, "%s() error getting resource\n", __func__); | ||
385 | return -ENODEV; | ||
386 | } | ||
387 | |||
388 | drvdata->lpaif = devm_ioremap_resource(&pdev->dev, res); | ||
389 | if (IS_ERR((void const __force *)drvdata->lpaif)) { | ||
390 | dev_err(&pdev->dev, "%s() error mapping reg resource: %ld\n", | ||
391 | __func__, | ||
392 | PTR_ERR((void const __force *)drvdata->lpaif)); | ||
393 | return PTR_ERR((void const __force *)drvdata->lpaif); | ||
394 | } | ||
395 | |||
396 | drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif, | ||
397 | &lpass_cpu_regmap_config); | ||
398 | if (IS_ERR(drvdata->lpaif_map)) { | ||
399 | dev_err(&pdev->dev, "%s() error initializing regmap: %ld\n", | ||
400 | __func__, PTR_ERR(drvdata->lpaif_map)); | ||
401 | return PTR_ERR(drvdata->lpaif_map); | ||
402 | } | ||
403 | |||
404 | drvdata->mi2s_osr_clk = devm_clk_get(&pdev->dev, "mi2s-osr-clk"); | ||
405 | if (IS_ERR(drvdata->mi2s_osr_clk)) { | ||
406 | dev_err(&pdev->dev, "%s() error getting mi2s-osr-clk: %ld\n", | ||
407 | __func__, PTR_ERR(drvdata->mi2s_osr_clk)); | ||
408 | return PTR_ERR(drvdata->mi2s_osr_clk); | ||
409 | } | ||
410 | |||
411 | drvdata->mi2s_bit_clk = devm_clk_get(&pdev->dev, "mi2s-bit-clk"); | ||
412 | if (IS_ERR(drvdata->mi2s_bit_clk)) { | ||
413 | dev_err(&pdev->dev, "%s() error getting mi2s-bit-clk: %ld\n", | ||
414 | __func__, PTR_ERR(drvdata->mi2s_bit_clk)); | ||
415 | return PTR_ERR(drvdata->mi2s_bit_clk); | ||
416 | } | ||
417 | |||
418 | drvdata->ahbix_clk = devm_clk_get(&pdev->dev, "ahbix-clk"); | ||
419 | if (IS_ERR(drvdata->ahbix_clk)) { | ||
420 | dev_err(&pdev->dev, "%s() error getting ahbix-clk: %ld\n", | ||
421 | __func__, PTR_ERR(drvdata->ahbix_clk)); | ||
422 | return PTR_ERR(drvdata->ahbix_clk); | ||
423 | } | ||
424 | |||
425 | ret = clk_set_rate(drvdata->ahbix_clk, LPASS_AHBIX_CLOCK_FREQUENCY); | ||
426 | if (ret) { | ||
427 | dev_err(&pdev->dev, "%s() error setting rate on ahbix_clk: %d\n", | ||
428 | __func__, ret); | ||
429 | return ret; | ||
430 | } | ||
431 | dev_dbg(&pdev->dev, "%s() set ahbix_clk rate to %lu\n", __func__, | ||
432 | clk_get_rate(drvdata->ahbix_clk)); | ||
433 | |||
434 | ret = clk_prepare_enable(drvdata->ahbix_clk); | ||
435 | if (ret) { | ||
436 | dev_err(&pdev->dev, "%s() error enabling ahbix_clk: %d\n", | ||
437 | __func__, ret); | ||
438 | return ret; | ||
439 | } | ||
440 | |||
441 | ret = devm_snd_soc_register_component(&pdev->dev, | ||
442 | &lpass_cpu_comp_driver, &lpass_cpu_dai_driver, 1); | ||
443 | if (ret) { | ||
444 | dev_err(&pdev->dev, "%s() error registering cpu driver: %d\n", | ||
445 | __func__, ret); | ||
446 | goto err_clk; | ||
447 | } | ||
448 | |||
449 | ret = asoc_qcom_lpass_platform_register(pdev); | ||
450 | if (ret) { | ||
451 | dev_err(&pdev->dev, "%s() error registering platform driver: %d\n", | ||
452 | __func__, ret); | ||
453 | goto err_clk; | ||
454 | } | ||
455 | |||
456 | return 0; | ||
457 | |||
458 | err_clk: | ||
459 | clk_disable_unprepare(drvdata->ahbix_clk); | ||
460 | return ret; | ||
461 | } | ||
462 | |||
463 | static int lpass_cpu_platform_remove(struct platform_device *pdev) | ||
464 | { | ||
465 | struct lpass_data *drvdata = platform_get_drvdata(pdev); | ||
466 | |||
467 | clk_disable_unprepare(drvdata->ahbix_clk); | ||
468 | |||
469 | return 0; | ||
470 | } | ||
471 | |||
472 | #ifdef CONFIG_OF | ||
473 | static const struct of_device_id lpass_cpu_device_id[] = { | ||
474 | { .compatible = "qcom,lpass-cpu" }, | ||
475 | {} | ||
476 | }; | ||
477 | MODULE_DEVICE_TABLE(of, lpass_cpu_device_id); | ||
478 | #endif | ||
479 | |||
480 | static struct platform_driver lpass_cpu_platform_driver = { | ||
481 | .driver = { | ||
482 | .name = "lpass-cpu", | ||
483 | .of_match_table = of_match_ptr(lpass_cpu_device_id), | ||
484 | }, | ||
485 | .probe = lpass_cpu_platform_probe, | ||
486 | .remove = lpass_cpu_platform_remove, | ||
487 | }; | ||
488 | module_platform_driver(lpass_cpu_platform_driver); | ||
489 | |||
490 | MODULE_DESCRIPTION("QTi LPASS CPU Driver"); | ||
491 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/qcom/lpass-lpaif-ipq806x.h b/sound/soc/qcom/lpass-lpaif-ipq806x.h new file mode 100644 index 000000000000..dc423b888842 --- /dev/null +++ b/sound/soc/qcom/lpass-lpaif-ipq806x.h | |||
@@ -0,0 +1,172 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * lpass-lpaif-ipq806x.h -- Definitions for the QTi LPAIF in the ipq806x LPASS | ||
14 | */ | ||
15 | |||
16 | #ifndef __LPASS_LPAIF_H__ | ||
17 | #define __LPASS_LPAIF_H__ | ||
18 | |||
19 | #define LPAIF_BANK_OFFSET 0x1000 | ||
20 | |||
21 | /* LPAIF I2S */ | ||
22 | |||
23 | #define LPAIF_I2SCTL_REG_BASE 0x0010 | ||
24 | #define LPAIF_I2SCTL_REG_STRIDE 0x4 | ||
25 | #define LPAIF_I2SCTL_REG_ADDR(addr, port) \ | ||
26 | (LPAIF_I2SCTL_REG_BASE + (addr) + (LPAIF_I2SCTL_REG_STRIDE * (port))) | ||
27 | |||
28 | enum lpaif_i2s_ports { | ||
29 | LPAIF_I2S_PORT_MIN = 0, | ||
30 | |||
31 | LPAIF_I2S_PORT_CODEC_SPK = 0, | ||
32 | LPAIF_I2S_PORT_CODEC_MIC = 1, | ||
33 | LPAIF_I2S_PORT_SEC_SPK = 2, | ||
34 | LPAIF_I2S_PORT_SEC_MIC = 3, | ||
35 | LPAIF_I2S_PORT_MI2S = 4, | ||
36 | |||
37 | LPAIF_I2S_PORT_MAX = 4, | ||
38 | LPAIF_I2S_PORT_NUM = 5, | ||
39 | }; | ||
40 | |||
41 | #define LPAIF_I2SCTL_REG(port) LPAIF_I2SCTL_REG_ADDR(0x0, (port)) | ||
42 | |||
43 | #define LPAIF_I2SCTL_LOOPBACK_MASK 0x8000 | ||
44 | #define LPAIF_I2SCTL_LOOPBACK_SHIFT 15 | ||
45 | #define LPAIF_I2SCTL_LOOPBACK_DISABLE (0 << LPAIF_I2SCTL_LOOPBACK_SHIFT) | ||
46 | #define LPAIF_I2SCTL_LOOPBACK_ENABLE (1 << LPAIF_I2SCTL_LOOPBACK_SHIFT) | ||
47 | |||
48 | #define LPAIF_I2SCTL_SPKEN_MASK 0x4000 | ||
49 | #define LPAIF_I2SCTL_SPKEN_SHIFT 14 | ||
50 | #define LPAIF_I2SCTL_SPKEN_DISABLE (0 << LPAIF_I2SCTL_SPKEN_SHIFT) | ||
51 | #define LPAIF_I2SCTL_SPKEN_ENABLE (1 << LPAIF_I2SCTL_SPKEN_SHIFT) | ||
52 | |||
53 | #define LPAIF_I2SCTL_SPKMODE_MASK 0x3C00 | ||
54 | #define LPAIF_I2SCTL_SPKMODE_SHIFT 10 | ||
55 | #define LPAIF_I2SCTL_SPKMODE_NONE (0 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
56 | #define LPAIF_I2SCTL_SPKMODE_SD0 (1 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
57 | #define LPAIF_I2SCTL_SPKMODE_SD1 (2 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
58 | #define LPAIF_I2SCTL_SPKMODE_SD2 (3 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
59 | #define LPAIF_I2SCTL_SPKMODE_SD3 (4 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
60 | #define LPAIF_I2SCTL_SPKMODE_QUAD01 (5 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
61 | #define LPAIF_I2SCTL_SPKMODE_QUAD23 (6 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
62 | #define LPAIF_I2SCTL_SPKMODE_6CH (7 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
63 | #define LPAIF_I2SCTL_SPKMODE_8CH (8 << LPAIF_I2SCTL_SPKMODE_SHIFT) | ||
64 | |||
65 | #define LPAIF_I2SCTL_SPKMONO_MASK 0x0200 | ||
66 | #define LPAIF_I2SCTL_SPKMONO_SHIFT 9 | ||
67 | #define LPAIF_I2SCTL_SPKMONO_STEREO (0 << LPAIF_I2SCTL_SPKMONO_SHIFT) | ||
68 | #define LPAIF_I2SCTL_SPKMONO_MONO (1 << LPAIF_I2SCTL_SPKMONO_SHIFT) | ||
69 | |||
70 | #define LPAIF_I2SCTL_WSSRC_MASK 0x0004 | ||
71 | #define LPAIF_I2SCTL_WSSRC_SHIFT 2 | ||
72 | #define LPAIF_I2SCTL_WSSRC_INTERNAL (0 << LPAIF_I2SCTL_WSSRC_SHIFT) | ||
73 | #define LPAIF_I2SCTL_WSSRC_EXTERNAL (1 << LPAIF_I2SCTL_WSSRC_SHIFT) | ||
74 | |||
75 | #define LPAIF_I2SCTL_BITWIDTH_MASK 0x0003 | ||
76 | #define LPAIF_I2SCTL_BITWIDTH_SHIFT 0 | ||
77 | #define LPAIF_I2SCTL_BITWIDTH_16 (0 << LPAIF_I2SCTL_BITWIDTH_SHIFT) | ||
78 | #define LPAIF_I2SCTL_BITWIDTH_24 (1 << LPAIF_I2SCTL_BITWIDTH_SHIFT) | ||
79 | #define LPAIF_I2SCTL_BITWIDTH_32 (2 << LPAIF_I2SCTL_BITWIDTH_SHIFT) | ||
80 | |||
81 | /* LPAIF IRQ */ | ||
82 | |||
83 | #define LPAIF_IRQ_REG_BASE 0x3000 | ||
84 | #define LPAIF_IRQ_REG_STRIDE 0x1000 | ||
85 | #define LPAIF_IRQ_REG_ADDR(addr, port) \ | ||
86 | (LPAIF_IRQ_REG_BASE + (addr) + (LPAIF_IRQ_REG_STRIDE * (port))) | ||
87 | |||
88 | enum lpaif_irq_ports { | ||
89 | LPAIF_IRQ_PORT_MIN = 0, | ||
90 | |||
91 | LPAIF_IRQ_PORT_HOST = 0, | ||
92 | LPAIF_IRQ_PORT_ADSP = 1, | ||
93 | |||
94 | LPAIF_IRQ_PORT_MAX = 2, | ||
95 | LPAIF_IRQ_PORT_NUM = 3, | ||
96 | }; | ||
97 | |||
98 | #define LPAIF_IRQEN_REG(port) LPAIF_IRQ_REG_ADDR(0x0, (port)) | ||
99 | #define LPAIF_IRQSTAT_REG(port) LPAIF_IRQ_REG_ADDR(0x4, (port)) | ||
100 | #define LPAIF_IRQCLEAR_REG(port) LPAIF_IRQ_REG_ADDR(0xC, (port)) | ||
101 | |||
102 | #define LPAIF_IRQ_BITSTRIDE 3 | ||
103 | #define LPAIF_IRQ_PER(chan) (1 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
104 | #define LPAIF_IRQ_XRUN(chan) (2 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
105 | #define LPAIF_IRQ_ERR(chan) (4 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
106 | #define LPAIF_IRQ_ALL(chan) (7 << (LPAIF_IRQ_BITSTRIDE * (chan))) | ||
107 | |||
108 | /* LPAIF DMA */ | ||
109 | |||
110 | #define LPAIF_RDMA_REG_BASE 0x6000 | ||
111 | #define LPAIF_RDMA_REG_STRIDE 0x1000 | ||
112 | #define LPAIF_RDMA_REG_ADDR(addr, chan) \ | ||
113 | (LPAIF_RDMA_REG_BASE + (addr) + (LPAIF_RDMA_REG_STRIDE * (chan))) | ||
114 | |||
115 | enum lpaif_dma_channels { | ||
116 | LPAIF_RDMA_CHAN_MIN = 0, | ||
117 | |||
118 | LPAIF_RDMA_CHAN_MI2S = 0, | ||
119 | LPAIF_RDMA_CHAN_PCM0 = 1, | ||
120 | LPAIF_RDMA_CHAN_PCM1 = 2, | ||
121 | |||
122 | LPAIF_RDMA_CHAN_MAX = 4, | ||
123 | LPAIF_RDMA_CHAN_NUM = 5, | ||
124 | }; | ||
125 | |||
126 | #define LPAIF_RDMACTL_REG(chan) LPAIF_RDMA_REG_ADDR(0x00, (chan)) | ||
127 | #define LPAIF_RDMABASE_REG(chan) LPAIF_RDMA_REG_ADDR(0x04, (chan)) | ||
128 | #define LPAIF_RDMABUFF_REG(chan) LPAIF_RDMA_REG_ADDR(0x08, (chan)) | ||
129 | #define LPAIF_RDMACURR_REG(chan) LPAIF_RDMA_REG_ADDR(0x0C, (chan)) | ||
130 | #define LPAIF_RDMAPER_REG(chan) LPAIF_RDMA_REG_ADDR(0x10, (chan)) | ||
131 | |||
132 | #define LPAIF_RDMACTL_BURSTEN_MASK 0x800 | ||
133 | #define LPAIF_RDMACTL_BURSTEN_SHIFT 11 | ||
134 | #define LPAIF_RDMACTL_BURSTEN_SINGLE (0 << LPAIF_RDMACTL_BURSTEN_SHIFT) | ||
135 | #define LPAIF_RDMACTL_BURSTEN_INCR4 (1 << LPAIF_RDMACTL_BURSTEN_SHIFT) | ||
136 | |||
137 | #define LPAIF_RDMACTL_WPSCNT_MASK 0x700 | ||
138 | #define LPAIF_RDMACTL_WPSCNT_SHIFT 8 | ||
139 | #define LPAIF_RDMACTL_WPSCNT_ONE (0 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
140 | #define LPAIF_RDMACTL_WPSCNT_TWO (1 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
141 | #define LPAIF_RDMACTL_WPSCNT_THREE (2 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
142 | #define LPAIF_RDMACTL_WPSCNT_FOUR (3 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
143 | #define LPAIF_RDMACTL_WPSCNT_SIX (5 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
144 | #define LPAIF_RDMACTL_WPSCNT_EIGHT (7 << LPAIF_RDMACTL_WPSCNT_SHIFT) | ||
145 | |||
146 | #define LPAIF_RDMACTL_AUDINTF_MASK 0x0F0 | ||
147 | #define LPAIF_RDMACTL_AUDINTF_SHIFT 4 | ||
148 | #define LPAIF_RDMACTL_AUDINTF_NONE (0 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
149 | #define LPAIF_RDMACTL_AUDINTF_CODEC (1 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
150 | #define LPAIF_RDMACTL_AUDINTF_PCM (2 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
151 | #define LPAIF_RDMACTL_AUDINTF_SEC_I2S (3 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
152 | #define LPAIF_RDMACTL_AUDINTF_MI2S (4 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
153 | #define LPAIF_RDMACTL_AUDINTF_HDMI (5 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
154 | #define LPAIF_RDMACTL_AUDINTF_SEC_PCM (7 << LPAIF_RDMACTL_AUDINTF_SHIFT) | ||
155 | |||
156 | #define LPAIF_RDMACTL_FIFOWM_MASK 0x00E | ||
157 | #define LPAIF_RDMACTL_FIFOWM_SHIFT 1 | ||
158 | #define LPAIF_RDMACTL_FIFOWM_1 (0 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
159 | #define LPAIF_RDMACTL_FIFOWM_2 (1 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
160 | #define LPAIF_RDMACTL_FIFOWM_3 (2 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
161 | #define LPAIF_RDMACTL_FIFOWM_4 (3 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
162 | #define LPAIF_RDMACTL_FIFOWM_5 (4 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
163 | #define LPAIF_RDMACTL_FIFOWM_6 (5 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
164 | #define LPAIF_RDMACTL_FIFOWM_7 (6 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
165 | #define LPAIF_RDMACTL_FIFOWM_8 (7 << LPAIF_RDMACTL_FIFOWM_SHIFT) | ||
166 | |||
167 | #define LPAIF_RDMACTL_ENABLE_MASK 0x1 | ||
168 | #define LPAIF_RDMACTL_ENABLE_SHIFT 0 | ||
169 | #define LPAIF_RDMACTL_ENABLE_OFF (0 << LPAIF_RDMACTL_ENABLE_SHIFT) | ||
170 | #define LPAIF_RDMACTL_ENABLE_ON (1 << LPAIF_RDMACTL_ENABLE_SHIFT) | ||
171 | |||
172 | #endif /* __LPASS_LPAIF_H__ */ | ||
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c new file mode 100644 index 000000000000..2fa6280dfb23 --- /dev/null +++ b/sound/soc/qcom/lpass-platform.c | |||
@@ -0,0 +1,526 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS | ||
14 | */ | ||
15 | |||
16 | #include <linux/compiler.h> | ||
17 | #include <linux/device.h> | ||
18 | #include <linux/dma-mapping.h> | ||
19 | #include <linux/err.h> | ||
20 | #include <linux/export.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/module.h> | ||
23 | #include <linux/io.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <sound/memalloc.h> | ||
26 | #include <sound/pcm.h> | ||
27 | #include <sound/pcm_params.h> | ||
28 | #include <linux/regmap.h> | ||
29 | #include <sound/soc.h> | ||
30 | #include "lpass-lpaif-ipq806x.h" | ||
31 | #include "lpass.h" | ||
32 | |||
33 | #define LPASS_PLATFORM_BUFFER_SIZE (16 * 1024) | ||
34 | #define LPASS_PLATFORM_PERIODS 2 | ||
35 | |||
36 | static struct snd_pcm_hardware lpass_platform_pcm_hardware = { | ||
37 | .info = SNDRV_PCM_INFO_MMAP | | ||
38 | SNDRV_PCM_INFO_MMAP_VALID | | ||
39 | SNDRV_PCM_INFO_INTERLEAVED | | ||
40 | SNDRV_PCM_INFO_PAUSE | | ||
41 | SNDRV_PCM_INFO_RESUME, | ||
42 | .formats = SNDRV_PCM_FMTBIT_S16 | | ||
43 | SNDRV_PCM_FMTBIT_S24 | | ||
44 | SNDRV_PCM_FMTBIT_S32, | ||
45 | .rates = SNDRV_PCM_RATE_8000_192000, | ||
46 | .rate_min = 8000, | ||
47 | .rate_max = 192000, | ||
48 | .channels_min = 1, | ||
49 | .channels_max = 8, | ||
50 | .buffer_bytes_max = LPASS_PLATFORM_BUFFER_SIZE, | ||
51 | .period_bytes_max = LPASS_PLATFORM_BUFFER_SIZE / | ||
52 | LPASS_PLATFORM_PERIODS, | ||
53 | .period_bytes_min = LPASS_PLATFORM_BUFFER_SIZE / | ||
54 | LPASS_PLATFORM_PERIODS, | ||
55 | .periods_min = LPASS_PLATFORM_PERIODS, | ||
56 | .periods_max = LPASS_PLATFORM_PERIODS, | ||
57 | .fifo_size = 0, | ||
58 | }; | ||
59 | |||
60 | static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream) | ||
61 | { | ||
62 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
63 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
64 | int ret; | ||
65 | |||
66 | snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware); | ||
67 | |||
68 | runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max; | ||
69 | |||
70 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
71 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
72 | if (ret < 0) { | ||
73 | dev_err(soc_runtime->dev, "%s() setting constraints failed: %d\n", | ||
74 | __func__, ret); | ||
75 | return -EINVAL; | ||
76 | } | ||
77 | |||
78 | snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); | ||
79 | |||
80 | return 0; | ||
81 | } | ||
82 | |||
83 | static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream, | ||
84 | struct snd_pcm_hw_params *params) | ||
85 | { | ||
86 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
87 | struct lpass_data *drvdata = | ||
88 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
89 | snd_pcm_format_t format = params_format(params); | ||
90 | unsigned int channels = params_channels(params); | ||
91 | unsigned int regval; | ||
92 | int bitwidth; | ||
93 | int ret; | ||
94 | |||
95 | bitwidth = snd_pcm_format_width(format); | ||
96 | if (bitwidth < 0) { | ||
97 | dev_err(soc_runtime->dev, "%s() invalid bit width given: %d\n", | ||
98 | __func__, bitwidth); | ||
99 | return bitwidth; | ||
100 | } | ||
101 | |||
102 | regval = LPAIF_RDMACTL_BURSTEN_INCR4 | | ||
103 | LPAIF_RDMACTL_AUDINTF_MI2S | | ||
104 | LPAIF_RDMACTL_FIFOWM_8; | ||
105 | |||
106 | switch (bitwidth) { | ||
107 | case 16: | ||
108 | switch (channels) { | ||
109 | case 1: | ||
110 | case 2: | ||
111 | regval |= LPAIF_RDMACTL_WPSCNT_ONE; | ||
112 | break; | ||
113 | case 4: | ||
114 | regval |= LPAIF_RDMACTL_WPSCNT_TWO; | ||
115 | break; | ||
116 | case 6: | ||
117 | regval |= LPAIF_RDMACTL_WPSCNT_THREE; | ||
118 | break; | ||
119 | case 8: | ||
120 | regval |= LPAIF_RDMACTL_WPSCNT_FOUR; | ||
121 | break; | ||
122 | default: | ||
123 | dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", | ||
124 | __func__, bitwidth, channels); | ||
125 | return -EINVAL; | ||
126 | } | ||
127 | break; | ||
128 | case 24: | ||
129 | case 32: | ||
130 | switch (channels) { | ||
131 | case 1: | ||
132 | regval |= LPAIF_RDMACTL_WPSCNT_ONE; | ||
133 | break; | ||
134 | case 2: | ||
135 | regval |= LPAIF_RDMACTL_WPSCNT_TWO; | ||
136 | break; | ||
137 | case 4: | ||
138 | regval |= LPAIF_RDMACTL_WPSCNT_FOUR; | ||
139 | break; | ||
140 | case 6: | ||
141 | regval |= LPAIF_RDMACTL_WPSCNT_SIX; | ||
142 | break; | ||
143 | case 8: | ||
144 | regval |= LPAIF_RDMACTL_WPSCNT_EIGHT; | ||
145 | break; | ||
146 | default: | ||
147 | dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", | ||
148 | __func__, bitwidth, channels); | ||
149 | return -EINVAL; | ||
150 | } | ||
151 | break; | ||
152 | default: | ||
153 | dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n", | ||
154 | __func__, bitwidth, channels); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | |||
158 | ret = regmap_write(drvdata->lpaif_map, | ||
159 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), regval); | ||
160 | if (ret) { | ||
161 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
162 | __func__, ret); | ||
163 | return ret; | ||
164 | } | ||
165 | |||
166 | return 0; | ||
167 | } | ||
168 | |||
169 | static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream) | ||
170 | { | ||
171 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
172 | struct lpass_data *drvdata = | ||
173 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
174 | int ret; | ||
175 | |||
176 | ret = regmap_write(drvdata->lpaif_map, | ||
177 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); | ||
178 | if (ret) | ||
179 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
180 | __func__, ret); | ||
181 | |||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream) | ||
186 | { | ||
187 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
188 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
189 | struct lpass_data *drvdata = | ||
190 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
191 | int ret; | ||
192 | |||
193 | ret = regmap_write(drvdata->lpaif_map, | ||
194 | LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), | ||
195 | runtime->dma_addr); | ||
196 | if (ret) { | ||
197 | dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n", | ||
198 | __func__, ret); | ||
199 | return ret; | ||
200 | } | ||
201 | |||
202 | ret = regmap_write(drvdata->lpaif_map, | ||
203 | LPAIF_RDMABUFF_REG(LPAIF_RDMA_CHAN_MI2S), | ||
204 | (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1); | ||
205 | if (ret) { | ||
206 | dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n", | ||
207 | __func__, ret); | ||
208 | return ret; | ||
209 | } | ||
210 | |||
211 | ret = regmap_write(drvdata->lpaif_map, | ||
212 | LPAIF_RDMAPER_REG(LPAIF_RDMA_CHAN_MI2S), | ||
213 | (snd_pcm_lib_period_bytes(substream) >> 2) - 1); | ||
214 | if (ret) { | ||
215 | dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n", | ||
216 | __func__, ret); | ||
217 | return ret; | ||
218 | } | ||
219 | |||
220 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
221 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), | ||
222 | LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON); | ||
223 | if (ret) { | ||
224 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
225 | __func__, ret); | ||
226 | return ret; | ||
227 | } | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream, | ||
233 | int cmd) | ||
234 | { | ||
235 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
236 | struct lpass_data *drvdata = | ||
237 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
238 | int ret; | ||
239 | |||
240 | switch (cmd) { | ||
241 | case SNDRV_PCM_TRIGGER_START: | ||
242 | case SNDRV_PCM_TRIGGER_RESUME: | ||
243 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
244 | /* clear status before enabling interrupts */ | ||
245 | ret = regmap_write(drvdata->lpaif_map, | ||
246 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
247 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); | ||
248 | if (ret) { | ||
249 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
250 | __func__, ret); | ||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
255 | LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), | ||
256 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), | ||
257 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S)); | ||
258 | if (ret) { | ||
259 | dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", | ||
260 | __func__, ret); | ||
261 | return ret; | ||
262 | } | ||
263 | |||
264 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
265 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), | ||
266 | LPAIF_RDMACTL_ENABLE_MASK, | ||
267 | LPAIF_RDMACTL_ENABLE_ON); | ||
268 | if (ret) { | ||
269 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
270 | __func__, ret); | ||
271 | return ret; | ||
272 | } | ||
273 | break; | ||
274 | case SNDRV_PCM_TRIGGER_STOP: | ||
275 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
276 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
277 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
278 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), | ||
279 | LPAIF_RDMACTL_ENABLE_MASK, | ||
280 | LPAIF_RDMACTL_ENABLE_OFF); | ||
281 | if (ret) { | ||
282 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
283 | __func__, ret); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | ret = regmap_update_bits(drvdata->lpaif_map, | ||
288 | LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), | ||
289 | LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S), 0); | ||
290 | if (ret) { | ||
291 | dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", | ||
292 | __func__, ret); | ||
293 | return ret; | ||
294 | } | ||
295 | break; | ||
296 | } | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | static snd_pcm_uframes_t lpass_platform_pcmops_pointer( | ||
302 | struct snd_pcm_substream *substream) | ||
303 | { | ||
304 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
305 | struct lpass_data *drvdata = | ||
306 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
307 | unsigned int base_addr, curr_addr; | ||
308 | int ret; | ||
309 | |||
310 | ret = regmap_read(drvdata->lpaif_map, | ||
311 | LPAIF_RDMABASE_REG(LPAIF_RDMA_CHAN_MI2S), &base_addr); | ||
312 | if (ret) { | ||
313 | dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n", | ||
314 | __func__, ret); | ||
315 | return ret; | ||
316 | } | ||
317 | |||
318 | ret = regmap_read(drvdata->lpaif_map, | ||
319 | LPAIF_RDMACURR_REG(LPAIF_RDMA_CHAN_MI2S), &curr_addr); | ||
320 | if (ret) { | ||
321 | dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n", | ||
322 | __func__, ret); | ||
323 | return ret; | ||
324 | } | ||
325 | |||
326 | return bytes_to_frames(substream->runtime, curr_addr - base_addr); | ||
327 | } | ||
328 | |||
329 | static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream, | ||
330 | struct vm_area_struct *vma) | ||
331 | { | ||
332 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
333 | |||
334 | return dma_mmap_coherent(substream->pcm->card->dev, vma, | ||
335 | runtime->dma_area, runtime->dma_addr, | ||
336 | runtime->dma_bytes); | ||
337 | } | ||
338 | |||
339 | static struct snd_pcm_ops lpass_platform_pcm_ops = { | ||
340 | .open = lpass_platform_pcmops_open, | ||
341 | .ioctl = snd_pcm_lib_ioctl, | ||
342 | .hw_params = lpass_platform_pcmops_hw_params, | ||
343 | .hw_free = lpass_platform_pcmops_hw_free, | ||
344 | .prepare = lpass_platform_pcmops_prepare, | ||
345 | .trigger = lpass_platform_pcmops_trigger, | ||
346 | .pointer = lpass_platform_pcmops_pointer, | ||
347 | .mmap = lpass_platform_pcmops_mmap, | ||
348 | }; | ||
349 | |||
350 | static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data) | ||
351 | { | ||
352 | struct snd_pcm_substream *substream = data; | ||
353 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
354 | struct lpass_data *drvdata = | ||
355 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
356 | unsigned int interrupts; | ||
357 | irqreturn_t ret = IRQ_NONE; | ||
358 | int rv; | ||
359 | |||
360 | rv = regmap_read(drvdata->lpaif_map, | ||
361 | LPAIF_IRQSTAT_REG(LPAIF_IRQ_PORT_HOST), &interrupts); | ||
362 | if (rv) { | ||
363 | dev_err(soc_runtime->dev, "%s() error reading from irqstat reg: %d\n", | ||
364 | __func__, rv); | ||
365 | return IRQ_NONE; | ||
366 | } | ||
367 | interrupts &= LPAIF_IRQ_ALL(LPAIF_RDMA_CHAN_MI2S); | ||
368 | |||
369 | if (interrupts & LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)) { | ||
370 | rv = regmap_write(drvdata->lpaif_map, | ||
371 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
372 | LPAIF_IRQ_PER(LPAIF_RDMA_CHAN_MI2S)); | ||
373 | if (rv) { | ||
374 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
375 | __func__, rv); | ||
376 | return IRQ_NONE; | ||
377 | } | ||
378 | snd_pcm_period_elapsed(substream); | ||
379 | ret = IRQ_HANDLED; | ||
380 | } | ||
381 | |||
382 | if (interrupts & LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)) { | ||
383 | rv = regmap_write(drvdata->lpaif_map, | ||
384 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
385 | LPAIF_IRQ_XRUN(LPAIF_RDMA_CHAN_MI2S)); | ||
386 | if (rv) { | ||
387 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
388 | __func__, rv); | ||
389 | return IRQ_NONE; | ||
390 | } | ||
391 | dev_warn(soc_runtime->dev, "%s() xrun warning\n", __func__); | ||
392 | snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN); | ||
393 | ret = IRQ_HANDLED; | ||
394 | } | ||
395 | |||
396 | if (interrupts & LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)) { | ||
397 | rv = regmap_write(drvdata->lpaif_map, | ||
398 | LPAIF_IRQCLEAR_REG(LPAIF_IRQ_PORT_HOST), | ||
399 | LPAIF_IRQ_ERR(LPAIF_RDMA_CHAN_MI2S)); | ||
400 | if (rv) { | ||
401 | dev_err(soc_runtime->dev, "%s() error writing to irqclear reg: %d\n", | ||
402 | __func__, rv); | ||
403 | return IRQ_NONE; | ||
404 | } | ||
405 | dev_err(soc_runtime->dev, "%s() bus access error\n", __func__); | ||
406 | snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED); | ||
407 | ret = IRQ_HANDLED; | ||
408 | } | ||
409 | |||
410 | return ret; | ||
411 | } | ||
412 | |||
413 | static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream, | ||
414 | struct snd_soc_pcm_runtime *soc_runtime) | ||
415 | { | ||
416 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
417 | size_t size = lpass_platform_pcm_hardware.buffer_bytes_max; | ||
418 | |||
419 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
420 | buf->dev.dev = soc_runtime->dev; | ||
421 | buf->private_data = NULL; | ||
422 | buf->area = dma_alloc_coherent(soc_runtime->dev, size, &buf->addr, | ||
423 | GFP_KERNEL); | ||
424 | if (!buf->area) { | ||
425 | dev_err(soc_runtime->dev, "%s: Could not allocate DMA buffer\n", | ||
426 | __func__); | ||
427 | return -ENOMEM; | ||
428 | } | ||
429 | buf->bytes = size; | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static void lpass_platform_free_buffer(struct snd_pcm_substream *substream, | ||
435 | struct snd_soc_pcm_runtime *soc_runtime) | ||
436 | { | ||
437 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
438 | |||
439 | if (buf->area) { | ||
440 | dma_free_coherent(soc_runtime->dev, buf->bytes, buf->area, | ||
441 | buf->addr); | ||
442 | } | ||
443 | buf->area = NULL; | ||
444 | } | ||
445 | |||
446 | static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime) | ||
447 | { | ||
448 | struct snd_pcm *pcm = soc_runtime->pcm; | ||
449 | struct snd_pcm_substream *substream = | ||
450 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | ||
451 | struct lpass_data *drvdata = | ||
452 | snd_soc_platform_get_drvdata(soc_runtime->platform); | ||
453 | int ret; | ||
454 | |||
455 | soc_runtime->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
456 | soc_runtime->dev->dma_mask = &soc_runtime->dev->coherent_dma_mask; | ||
457 | |||
458 | ret = lpass_platform_alloc_buffer(substream, soc_runtime); | ||
459 | if (ret) | ||
460 | return ret; | ||
461 | |||
462 | ret = devm_request_irq(soc_runtime->dev, drvdata->lpaif_irq, | ||
463 | lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING, | ||
464 | "lpass-irq-lpaif", substream); | ||
465 | if (ret) { | ||
466 | dev_err(soc_runtime->dev, "%s() irq request failed: %d\n", | ||
467 | __func__, ret); | ||
468 | goto err_buf; | ||
469 | } | ||
470 | |||
471 | /* ensure audio hardware is disabled */ | ||
472 | ret = regmap_write(drvdata->lpaif_map, | ||
473 | LPAIF_IRQEN_REG(LPAIF_IRQ_PORT_HOST), 0); | ||
474 | if (ret) { | ||
475 | dev_err(soc_runtime->dev, "%s() error writing to irqen reg: %d\n", | ||
476 | __func__, ret); | ||
477 | return ret; | ||
478 | } | ||
479 | ret = regmap_write(drvdata->lpaif_map, | ||
480 | LPAIF_RDMACTL_REG(LPAIF_RDMA_CHAN_MI2S), 0); | ||
481 | if (ret) { | ||
482 | dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n", | ||
483 | __func__, ret); | ||
484 | return ret; | ||
485 | } | ||
486 | |||
487 | return 0; | ||
488 | |||
489 | err_buf: | ||
490 | lpass_platform_free_buffer(substream, soc_runtime); | ||
491 | return ret; | ||
492 | } | ||
493 | |||
494 | static void lpass_platform_pcm_free(struct snd_pcm *pcm) | ||
495 | { | ||
496 | struct snd_pcm_substream *substream = | ||
497 | pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; | ||
498 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
499 | |||
500 | lpass_platform_free_buffer(substream, soc_runtime); | ||
501 | } | ||
502 | |||
503 | static struct snd_soc_platform_driver lpass_platform_driver = { | ||
504 | .pcm_new = lpass_platform_pcm_new, | ||
505 | .pcm_free = lpass_platform_pcm_free, | ||
506 | .ops = &lpass_platform_pcm_ops, | ||
507 | }; | ||
508 | |||
509 | int asoc_qcom_lpass_platform_register(struct platform_device *pdev) | ||
510 | { | ||
511 | struct lpass_data *drvdata = platform_get_drvdata(pdev); | ||
512 | |||
513 | drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif"); | ||
514 | if (drvdata->lpaif_irq < 0) { | ||
515 | dev_err(&pdev->dev, "%s() error getting irq handle: %d\n", | ||
516 | __func__, drvdata->lpaif_irq); | ||
517 | return -ENODEV; | ||
518 | } | ||
519 | |||
520 | return devm_snd_soc_register_platform(&pdev->dev, | ||
521 | &lpass_platform_driver); | ||
522 | } | ||
523 | EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register); | ||
524 | |||
525 | MODULE_DESCRIPTION("QTi LPASS Platform Driver"); | ||
526 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h new file mode 100644 index 000000000000..5c99b3dace86 --- /dev/null +++ b/sound/soc/qcom/lpass.h | |||
@@ -0,0 +1,51 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * lpass.h - Definitions for the QTi LPASS | ||
14 | */ | ||
15 | |||
16 | #ifndef __LPASS_H__ | ||
17 | #define __LPASS_H__ | ||
18 | |||
19 | #include <linux/clk.h> | ||
20 | #include <linux/compiler.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/regmap.h> | ||
23 | |||
24 | #define LPASS_AHBIX_CLOCK_FREQUENCY 131072000 | ||
25 | |||
26 | /* Both the CPU DAI and platform drivers will access this data */ | ||
27 | struct lpass_data { | ||
28 | |||
29 | /* AHB-I/X bus clocks inside the low-power audio subsystem (LPASS) */ | ||
30 | struct clk *ahbix_clk; | ||
31 | |||
32 | /* MI2S system clock */ | ||
33 | struct clk *mi2s_osr_clk; | ||
34 | |||
35 | /* MI2S bit clock (derived from system clock by a divider */ | ||
36 | struct clk *mi2s_bit_clk; | ||
37 | |||
38 | /* low-power audio interface (LPAIF) registers */ | ||
39 | void __iomem *lpaif; | ||
40 | |||
41 | /* regmap backed by the low-power audio interface (LPAIF) registers */ | ||
42 | struct regmap *lpaif_map; | ||
43 | |||
44 | /* interrupts from the low-power audio interface (LPAIF) */ | ||
45 | int lpaif_irq; | ||
46 | }; | ||
47 | |||
48 | /* register the platform driver from the CPU DAI driver */ | ||
49 | int asoc_qcom_lpass_platform_register(struct platform_device *); | ||
50 | |||
51 | #endif /* __LPASS_H__ */ | ||
diff --git a/sound/soc/qcom/storm.c b/sound/soc/qcom/storm.c new file mode 100644 index 000000000000..b8bd296190ad --- /dev/null +++ b/sound/soc/qcom/storm.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 and | ||
6 | * only version 2 as published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it will be useful, | ||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
11 | * GNU General Public License for more details. | ||
12 | * | ||
13 | * storm.c -- ALSA SoC machine driver for QTi ipq806x-based Storm board | ||
14 | */ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/of.h> | ||
19 | #include <linux/mod_devicetable.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <sound/pcm.h> | ||
22 | #include <sound/pcm_params.h> | ||
23 | #include <sound/soc.h> | ||
24 | |||
25 | #define STORM_SYSCLK_MULT 4 | ||
26 | |||
27 | static int storm_ops_hw_params(struct snd_pcm_substream *substream, | ||
28 | struct snd_pcm_hw_params *params) | ||
29 | { | ||
30 | struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; | ||
31 | struct snd_soc_card *card = soc_runtime->card; | ||
32 | snd_pcm_format_t format = params_format(params); | ||
33 | unsigned int rate = params_rate(params); | ||
34 | unsigned int sysclk_freq; | ||
35 | int bitwidth, ret; | ||
36 | |||
37 | bitwidth = snd_pcm_format_width(format); | ||
38 | if (bitwidth < 0) { | ||
39 | dev_err(card->dev, "%s() invalid bit width given: %d\n", | ||
40 | __func__, bitwidth); | ||
41 | return bitwidth; | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * as the CPU DAI is the I2S bus master and no system clock is needed by | ||
46 | * the MAX98357a DAC, simply set the system clock to be a constant | ||
47 | * multiple of the bit clock for the clock divider | ||
48 | */ | ||
49 | sysclk_freq = rate * bitwidth * 2 * STORM_SYSCLK_MULT; | ||
50 | |||
51 | ret = snd_soc_dai_set_sysclk(soc_runtime->cpu_dai, 0, sysclk_freq, 0); | ||
52 | if (ret) { | ||
53 | dev_err(card->dev, "%s() error setting sysclk to %u: %d\n", | ||
54 | __func__, sysclk_freq, ret); | ||
55 | return ret; | ||
56 | } | ||
57 | |||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static struct snd_soc_ops storm_soc_ops = { | ||
62 | .hw_params = storm_ops_hw_params, | ||
63 | }; | ||
64 | |||
65 | static struct snd_soc_dai_link storm_dai_link = { | ||
66 | .name = "Primary", | ||
67 | .stream_name = "Primary", | ||
68 | .codec_dai_name = "HiFi", | ||
69 | .ops = &storm_soc_ops, | ||
70 | }; | ||
71 | |||
72 | static struct snd_soc_card storm_soc_card = { | ||
73 | .name = "ipq806x-storm", | ||
74 | .dev = NULL, | ||
75 | }; | ||
76 | |||
77 | static int storm_parse_of(struct snd_soc_card *card) | ||
78 | { | ||
79 | struct snd_soc_dai_link *dai_link = card->dai_link; | ||
80 | struct device_node *np = card->dev->of_node; | ||
81 | |||
82 | dai_link->cpu_of_node = of_parse_phandle(np, "cpu", 0); | ||
83 | if (!dai_link->cpu_of_node) { | ||
84 | dev_err(card->dev, "%s() error getting cpu phandle\n", | ||
85 | __func__); | ||
86 | return -EINVAL; | ||
87 | } | ||
88 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
89 | |||
90 | dai_link->codec_of_node = of_parse_phandle(np, "codec", 0); | ||
91 | if (!dai_link->codec_of_node) { | ||
92 | dev_err(card->dev, "%s() error getting codec phandle\n", | ||
93 | __func__); | ||
94 | return -EINVAL; | ||
95 | } | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static int storm_platform_probe(struct platform_device *pdev) | ||
101 | { | ||
102 | struct snd_soc_card *card = &storm_soc_card; | ||
103 | int ret; | ||
104 | |||
105 | if (card->dev) { | ||
106 | dev_err(&pdev->dev, "%s() error, existing soundcard\n", | ||
107 | __func__); | ||
108 | return -ENODEV; | ||
109 | } | ||
110 | card->dev = &pdev->dev; | ||
111 | platform_set_drvdata(pdev, card); | ||
112 | |||
113 | ret = snd_soc_of_parse_card_name(card, "qcom,model"); | ||
114 | if (ret) { | ||
115 | dev_err(&pdev->dev, "%s() error parsing card name: %d\n", | ||
116 | __func__, ret); | ||
117 | return ret; | ||
118 | } | ||
119 | |||
120 | card->dai_link = &storm_dai_link; | ||
121 | card->num_links = 1; | ||
122 | |||
123 | ret = storm_parse_of(card); | ||
124 | if (ret) { | ||
125 | dev_err(&pdev->dev, "%s() error resolving dai links: %d\n", | ||
126 | __func__, ret); | ||
127 | return ret; | ||
128 | } | ||
129 | |||
130 | ret = devm_snd_soc_register_card(&pdev->dev, card); | ||
131 | if (ret == -EPROBE_DEFER) { | ||
132 | card->dev = NULL; | ||
133 | return ret; | ||
134 | } else if (ret) { | ||
135 | dev_err(&pdev->dev, "%s() error registering soundcard: %d\n", | ||
136 | __func__, ret); | ||
137 | return ret; | ||
138 | } | ||
139 | |||
140 | return 0; | ||
141 | } | ||
142 | |||
143 | #ifdef CONFIG_OF | ||
144 | static const struct of_device_id storm_device_id[] = { | ||
145 | { .compatible = "google,storm-audio" }, | ||
146 | {}, | ||
147 | }; | ||
148 | MODULE_DEVICE_TABLE(of, storm_device_id); | ||
149 | #endif | ||
150 | |||
151 | static struct platform_driver storm_platform_driver = { | ||
152 | .driver = { | ||
153 | .name = "storm-audio", | ||
154 | .of_match_table = | ||
155 | of_match_ptr(storm_device_id), | ||
156 | }, | ||
157 | .probe = storm_platform_probe, | ||
158 | }; | ||
159 | module_platform_driver(storm_platform_driver); | ||
160 | |||
161 | MODULE_DESCRIPTION("QTi IPQ806x-based Storm Machine Driver"); | ||
162 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 80245b6eebd6..07114b0b0dc1 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
@@ -36,11 +36,17 @@ config SND_SOC_SH4_SIU | |||
36 | 36 | ||
37 | config SND_SOC_RCAR | 37 | config SND_SOC_RCAR |
38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" | 38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" |
39 | depends on DMA_OF | ||
39 | select SND_SIMPLE_CARD | 40 | select SND_SIMPLE_CARD |
40 | select REGMAP_MMIO | 41 | select REGMAP_MMIO |
41 | help | 42 | help |
42 | This option enables R-Car SUR/SCU/SSIU/SSI sound support | 43 | This option enables R-Car SUR/SCU/SSIU/SSI sound support |
43 | 44 | ||
45 | config SND_SOC_RSRC_CARD | ||
46 | tristate "Renesas Sampling Rate Convert Sound Card" | ||
47 | help | ||
48 | This option enables simple sound if you need sampling rate convert | ||
49 | |||
44 | ## | 50 | ## |
45 | ## Boards | 51 | ## Boards |
46 | ## | 52 | ## |
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile index 9ac536429800..f1b445173fba 100644 --- a/sound/soc/sh/rcar/Makefile +++ b/sound/soc/sh/rcar/Makefile | |||
@@ -1,2 +1,5 @@ | |||
1 | snd-soc-rcar-objs := core.o gen.o src.o adg.o ssi.o dvc.o | 1 | snd-soc-rcar-objs := core.o gen.o dma.o src.o adg.o ssi.o dvc.o |
2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file | 2 | obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o |
3 | |||
4 | snd-soc-rsrc-card-objs := rsrc-card.o | ||
5 | obj-$(CONFIG_SND_SOC_RSRC_CARD) += snd-soc-rsrc-card.o | ||
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index 7ac35c9d1cb8..fefc881dbac2 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c | |||
@@ -183,6 +183,8 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, | |||
183 | 183 | ||
184 | rsnd_mod_bset(mod, DIV_EN, en, en); | 184 | rsnd_mod_bset(mod, DIV_EN, en, en); |
185 | 185 | ||
186 | dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); | ||
187 | |||
186 | return 0; | 188 | return 0; |
187 | } | 189 | } |
188 | 190 | ||
@@ -432,7 +434,5 @@ int rsnd_adg_probe(struct platform_device *pdev, | |||
432 | 434 | ||
433 | priv->adg = adg; | 435 | priv->adg = adg; |
434 | 436 | ||
435 | dev_dbg(dev, "adg probed\n"); | ||
436 | |||
437 | return 0; | 437 | return 0; |
438 | } | 438 | } |
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c index 31202e95be1e..9f48d75fa992 100644 --- a/sound/soc/sh/rcar/core.c +++ b/sound/soc/sh/rcar/core.c | |||
@@ -94,21 +94,20 @@ | |||
94 | * | 94 | * |
95 | */ | 95 | */ |
96 | #include <linux/pm_runtime.h> | 96 | #include <linux/pm_runtime.h> |
97 | #include <linux/shdma-base.h> | ||
98 | #include "rsnd.h" | 97 | #include "rsnd.h" |
99 | 98 | ||
100 | #define RSND_RATES SNDRV_PCM_RATE_8000_96000 | 99 | #define RSND_RATES SNDRV_PCM_RATE_8000_96000 |
101 | #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) | 100 | #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) |
102 | 101 | ||
103 | static struct rsnd_of_data rsnd_of_data_gen1 = { | 102 | static const struct rsnd_of_data rsnd_of_data_gen1 = { |
104 | .flags = RSND_GEN1, | 103 | .flags = RSND_GEN1, |
105 | }; | 104 | }; |
106 | 105 | ||
107 | static struct rsnd_of_data rsnd_of_data_gen2 = { | 106 | static const struct rsnd_of_data rsnd_of_data_gen2 = { |
108 | .flags = RSND_GEN2, | 107 | .flags = RSND_GEN2, |
109 | }; | 108 | }; |
110 | 109 | ||
111 | static struct of_device_id rsnd_of_match[] = { | 110 | static const struct of_device_id rsnd_of_match[] = { |
112 | { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, | 111 | { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 }, |
113 | { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, | 112 | { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 }, |
114 | {}, | 113 | {}, |
@@ -138,15 +137,12 @@ char *rsnd_mod_name(struct rsnd_mod *mod) | |||
138 | return mod->ops->name; | 137 | return mod->ops->name; |
139 | } | 138 | } |
140 | 139 | ||
141 | char *rsnd_mod_dma_name(struct rsnd_mod *mod) | 140 | struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod) |
142 | { | 141 | { |
143 | if (!mod || !mod->ops) | 142 | if (!mod || !mod->ops || !mod->ops->dma_req) |
144 | return "unknown"; | 143 | return NULL; |
145 | |||
146 | if (!mod->ops->dma_name) | ||
147 | return mod->ops->name; | ||
148 | 144 | ||
149 | return mod->ops->dma_name(mod); | 145 | return mod->ops->dma_req(mod); |
150 | } | 146 | } |
151 | 147 | ||
152 | int rsnd_mod_init(struct rsnd_mod *mod, | 148 | int rsnd_mod_init(struct rsnd_mod *mod, |
@@ -175,228 +171,6 @@ void rsnd_mod_quit(struct rsnd_mod *mod) | |||
175 | } | 171 | } |
176 | 172 | ||
177 | /* | 173 | /* |
178 | * rsnd_dma functions | ||
179 | */ | ||
180 | void rsnd_dma_stop(struct rsnd_dma *dma) | ||
181 | { | ||
182 | dmaengine_terminate_all(dma->chan); | ||
183 | } | ||
184 | |||
185 | static void rsnd_dma_complete(void *data) | ||
186 | { | ||
187 | struct rsnd_dma *dma = (struct rsnd_dma *)data; | ||
188 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
189 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
190 | |||
191 | /* | ||
192 | * Renesas sound Gen1 needs 1 DMAC, | ||
193 | * Gen2 needs 2 DMAC. | ||
194 | * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. | ||
195 | * But, Audio-DMAC-peri-peri doesn't have interrupt, | ||
196 | * and this driver is assuming that here. | ||
197 | * | ||
198 | * If Audio-DMAC-peri-peri has interrpt, | ||
199 | * rsnd_dai_pointer_update() will be called twice, | ||
200 | * ant it will breaks io->byte_pos | ||
201 | */ | ||
202 | |||
203 | rsnd_dai_pointer_update(io, io->byte_per_period); | ||
204 | } | ||
205 | |||
206 | void rsnd_dma_start(struct rsnd_dma *dma) | ||
207 | { | ||
208 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
209 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
210 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
211 | struct snd_pcm_substream *substream = io->substream; | ||
212 | struct device *dev = rsnd_priv_to_dev(priv); | ||
213 | struct dma_async_tx_descriptor *desc; | ||
214 | |||
215 | desc = dmaengine_prep_dma_cyclic(dma->chan, | ||
216 | (dma->addr) ? dma->addr : | ||
217 | substream->runtime->dma_addr, | ||
218 | snd_pcm_lib_buffer_bytes(substream), | ||
219 | snd_pcm_lib_period_bytes(substream), | ||
220 | dma->dir, | ||
221 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
222 | |||
223 | if (!desc) { | ||
224 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | ||
225 | return; | ||
226 | } | ||
227 | |||
228 | desc->callback = rsnd_dma_complete; | ||
229 | desc->callback_param = dma; | ||
230 | |||
231 | if (dmaengine_submit(desc) < 0) { | ||
232 | dev_err(dev, "dmaengine_submit() fail\n"); | ||
233 | return; | ||
234 | } | ||
235 | |||
236 | dma_async_issue_pending(dma->chan); | ||
237 | } | ||
238 | |||
239 | int rsnd_dma_available(struct rsnd_dma *dma) | ||
240 | { | ||
241 | return !!dma->chan; | ||
242 | } | ||
243 | |||
244 | #define DMA_NAME_SIZE 16 | ||
245 | #define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ | ||
246 | static int _rsnd_dma_of_name(char *dma_name, struct rsnd_mod *mod) | ||
247 | { | ||
248 | if (mod) | ||
249 | return snprintf(dma_name, DMA_NAME_SIZE / 2, "%s%d", | ||
250 | rsnd_mod_dma_name(mod), rsnd_mod_id(mod)); | ||
251 | else | ||
252 | return snprintf(dma_name, DMA_NAME_SIZE / 2, "mem"); | ||
253 | |||
254 | } | ||
255 | |||
256 | static void rsnd_dma_of_name(struct rsnd_mod *mod_from, | ||
257 | struct rsnd_mod *mod_to, | ||
258 | char *dma_name) | ||
259 | { | ||
260 | int index = 0; | ||
261 | |||
262 | index = _rsnd_dma_of_name(dma_name + index, mod_from); | ||
263 | *(dma_name + index++) = '_'; | ||
264 | index = _rsnd_dma_of_name(dma_name + index, mod_to); | ||
265 | } | ||
266 | |||
267 | static void rsnd_dma_of_path(struct rsnd_dma *dma, | ||
268 | int is_play, | ||
269 | struct rsnd_mod **mod_from, | ||
270 | struct rsnd_mod **mod_to) | ||
271 | { | ||
272 | struct rsnd_mod *this = rsnd_dma_to_mod(dma); | ||
273 | struct rsnd_dai_stream *io = rsnd_mod_to_io(this); | ||
274 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); | ||
275 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | ||
276 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | ||
277 | struct rsnd_mod *mod[MOD_MAX]; | ||
278 | int i, index; | ||
279 | |||
280 | |||
281 | for (i = 0; i < MOD_MAX; i++) | ||
282 | mod[i] = NULL; | ||
283 | |||
284 | /* | ||
285 | * in play case... | ||
286 | * | ||
287 | * src -> dst | ||
288 | * | ||
289 | * mem -> SSI | ||
290 | * mem -> SRC -> SSI | ||
291 | * mem -> SRC -> DVC -> SSI | ||
292 | */ | ||
293 | mod[0] = NULL; /* for "mem" */ | ||
294 | index = 1; | ||
295 | for (i = 1; i < MOD_MAX; i++) { | ||
296 | if (!src) { | ||
297 | mod[i] = ssi; | ||
298 | } else if (!dvc) { | ||
299 | mod[i] = src; | ||
300 | src = NULL; | ||
301 | } else { | ||
302 | if ((!is_play) && (this == src)) | ||
303 | this = dvc; | ||
304 | |||
305 | mod[i] = (is_play) ? src : dvc; | ||
306 | i++; | ||
307 | mod[i] = (is_play) ? dvc : src; | ||
308 | src = NULL; | ||
309 | dvc = NULL; | ||
310 | } | ||
311 | |||
312 | if (mod[i] == this) | ||
313 | index = i; | ||
314 | |||
315 | if (mod[i] == ssi) | ||
316 | break; | ||
317 | } | ||
318 | |||
319 | if (is_play) { | ||
320 | *mod_from = mod[index - 1]; | ||
321 | *mod_to = mod[index]; | ||
322 | } else { | ||
323 | *mod_from = mod[index]; | ||
324 | *mod_to = mod[index - 1]; | ||
325 | } | ||
326 | } | ||
327 | |||
328 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | ||
329 | int is_play, int id) | ||
330 | { | ||
331 | struct device *dev = rsnd_priv_to_dev(priv); | ||
332 | struct dma_slave_config cfg; | ||
333 | struct rsnd_mod *mod_from; | ||
334 | struct rsnd_mod *mod_to; | ||
335 | char dma_name[DMA_NAME_SIZE]; | ||
336 | dma_cap_mask_t mask; | ||
337 | int ret; | ||
338 | |||
339 | if (dma->chan) { | ||
340 | dev_err(dev, "it already has dma channel\n"); | ||
341 | return -EIO; | ||
342 | } | ||
343 | |||
344 | dma_cap_zero(mask); | ||
345 | dma_cap_set(DMA_SLAVE, mask); | ||
346 | |||
347 | rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); | ||
348 | rsnd_dma_of_name(mod_from, mod_to, dma_name); | ||
349 | |||
350 | cfg.slave_id = id; | ||
351 | cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | ||
352 | cfg.src_addr = rsnd_gen_dma_addr(priv, mod_from, is_play, 1); | ||
353 | cfg.dst_addr = rsnd_gen_dma_addr(priv, mod_to, is_play, 0); | ||
354 | cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
355 | cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
356 | |||
357 | dev_dbg(dev, "dma : %s %pad -> %pad\n", | ||
358 | dma_name, &cfg.src_addr, &cfg.dst_addr); | ||
359 | |||
360 | dma->chan = dma_request_slave_channel_compat(mask, shdma_chan_filter, | ||
361 | (void *)id, dev, | ||
362 | dma_name); | ||
363 | if (!dma->chan) { | ||
364 | dev_err(dev, "can't get dma channel\n"); | ||
365 | goto rsnd_dma_channel_err; | ||
366 | } | ||
367 | |||
368 | ret = dmaengine_slave_config(dma->chan, &cfg); | ||
369 | if (ret < 0) | ||
370 | goto rsnd_dma_init_err; | ||
371 | |||
372 | dma->addr = is_play ? cfg.src_addr : cfg.dst_addr; | ||
373 | dma->dir = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | ||
374 | |||
375 | return 0; | ||
376 | |||
377 | rsnd_dma_init_err: | ||
378 | rsnd_dma_quit(priv, dma); | ||
379 | rsnd_dma_channel_err: | ||
380 | |||
381 | /* | ||
382 | * DMA failed. try to PIO mode | ||
383 | * see | ||
384 | * rsnd_ssi_fallback() | ||
385 | * rsnd_rdai_continuance_probe() | ||
386 | */ | ||
387 | return -EAGAIN; | ||
388 | } | ||
389 | |||
390 | void rsnd_dma_quit(struct rsnd_priv *priv, | ||
391 | struct rsnd_dma *dma) | ||
392 | { | ||
393 | if (dma->chan) | ||
394 | dma_release_channel(dma->chan); | ||
395 | |||
396 | dma->chan = NULL; | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * settting function | 174 | * settting function |
401 | */ | 175 | */ |
402 | u32 rsnd_get_adinr(struct rsnd_mod *mod) | 176 | u32 rsnd_get_adinr(struct rsnd_mod *mod) |
@@ -429,7 +203,7 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod) | |||
429 | ({ \ | 203 | ({ \ |
430 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ | 204 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); \ |
431 | struct device *dev = rsnd_priv_to_dev(priv); \ | 205 | struct device *dev = rsnd_priv_to_dev(priv); \ |
432 | u32 mask = 1 << __rsnd_mod_shift_##func; \ | 206 | u32 mask = (1 << __rsnd_mod_shift_##func) & ~(1 << 31); \ |
433 | u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ | 207 | u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func; \ |
434 | int ret = 0; \ | 208 | int ret = 0; \ |
435 | if ((mod->status & mask) == call) { \ | 209 | if ((mod->status & mask) == call) { \ |
@@ -471,7 +245,7 @@ static int rsnd_dai_connect(struct rsnd_mod *mod, | |||
471 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | 245 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
472 | struct device *dev = rsnd_priv_to_dev(priv); | 246 | struct device *dev = rsnd_priv_to_dev(priv); |
473 | 247 | ||
474 | dev_err(dev, "%s%d is not empty\n", | 248 | dev_err(dev, "%s[%d] is not empty\n", |
475 | rsnd_mod_name(mod), | 249 | rsnd_mod_name(mod), |
476 | rsnd_mod_id(mod)); | 250 | rsnd_mod_id(mod)); |
477 | return -EIO; | 251 | return -EIO; |
@@ -887,20 +661,28 @@ static int rsnd_dai_probe(struct platform_device *pdev, | |||
887 | drv[i].name = rdai[i].name; | 661 | drv[i].name = rdai[i].name; |
888 | drv[i].ops = &rsnd_soc_dai_ops; | 662 | drv[i].ops = &rsnd_soc_dai_ops; |
889 | if (pmod) { | 663 | if (pmod) { |
664 | snprintf(rdai[i].playback.name, RSND_DAI_NAME_SIZE, | ||
665 | "DAI%d Playback", i); | ||
666 | |||
890 | drv[i].playback.rates = RSND_RATES; | 667 | drv[i].playback.rates = RSND_RATES; |
891 | drv[i].playback.formats = RSND_FMTS; | 668 | drv[i].playback.formats = RSND_FMTS; |
892 | drv[i].playback.channels_min = 2; | 669 | drv[i].playback.channels_min = 2; |
893 | drv[i].playback.channels_max = 2; | 670 | drv[i].playback.channels_max = 2; |
671 | drv[i].playback.stream_name = rdai[i].playback.name; | ||
894 | 672 | ||
895 | rdai[i].playback.info = &info->dai_info[i].playback; | 673 | rdai[i].playback.info = &info->dai_info[i].playback; |
896 | rdai[i].playback.rdai = rdai + i; | 674 | rdai[i].playback.rdai = rdai + i; |
897 | rsnd_path_init(priv, &rdai[i], &rdai[i].playback); | 675 | rsnd_path_init(priv, &rdai[i], &rdai[i].playback); |
898 | } | 676 | } |
899 | if (cmod) { | 677 | if (cmod) { |
678 | snprintf(rdai[i].capture.name, RSND_DAI_NAME_SIZE, | ||
679 | "DAI%d Capture", i); | ||
680 | |||
900 | drv[i].capture.rates = RSND_RATES; | 681 | drv[i].capture.rates = RSND_RATES; |
901 | drv[i].capture.formats = RSND_FMTS; | 682 | drv[i].capture.formats = RSND_FMTS; |
902 | drv[i].capture.channels_min = 2; | 683 | drv[i].capture.channels_min = 2; |
903 | drv[i].capture.channels_max = 2; | 684 | drv[i].capture.channels_max = 2; |
685 | drv[i].capture.stream_name = rdai[i].capture.name; | ||
904 | 686 | ||
905 | rdai[i].capture.info = &info->dai_info[i].capture; | 687 | rdai[i].capture.info = &info->dai_info[i].capture; |
906 | rdai[i].capture.rdai = rdai + i; | 688 | rdai[i].capture.rdai = rdai + i; |
@@ -946,6 +728,15 @@ static int rsnd_pcm_open(struct snd_pcm_substream *substream) | |||
946 | static int rsnd_hw_params(struct snd_pcm_substream *substream, | 728 | static int rsnd_hw_params(struct snd_pcm_substream *substream, |
947 | struct snd_pcm_hw_params *hw_params) | 729 | struct snd_pcm_hw_params *hw_params) |
948 | { | 730 | { |
731 | struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); | ||
732 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | ||
733 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); | ||
734 | int ret; | ||
735 | |||
736 | ret = rsnd_dai_call(hw_params, io, substream, hw_params); | ||
737 | if (ret) | ||
738 | return ret; | ||
739 | |||
949 | return snd_pcm_lib_malloc_pages(substream, | 740 | return snd_pcm_lib_malloc_pages(substream, |
950 | params_buffer_bytes(hw_params)); | 741 | params_buffer_bytes(hw_params)); |
951 | } | 742 | } |
@@ -1210,6 +1001,7 @@ static int rsnd_probe(struct platform_device *pdev) | |||
1210 | const struct rsnd_of_data *of_data, | 1001 | const struct rsnd_of_data *of_data, |
1211 | struct rsnd_priv *priv) = { | 1002 | struct rsnd_priv *priv) = { |
1212 | rsnd_gen_probe, | 1003 | rsnd_gen_probe, |
1004 | rsnd_dma_probe, | ||
1213 | rsnd_ssi_probe, | 1005 | rsnd_ssi_probe, |
1214 | rsnd_src_probe, | 1006 | rsnd_src_probe, |
1215 | rsnd_dvc_probe, | 1007 | rsnd_dvc_probe, |
diff --git a/sound/soc/sh/rcar/dma.c b/sound/soc/sh/rcar/dma.c new file mode 100644 index 000000000000..ac3756f6af60 --- /dev/null +++ b/sound/soc/sh/rcar/dma.c | |||
@@ -0,0 +1,616 @@ | |||
1 | /* | ||
2 | * Renesas R-Car Audio DMAC support | ||
3 | * | ||
4 | * Copyright (C) 2015 Renesas Electronics Corp. | ||
5 | * Copyright (c) 2015 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include <linux/delay.h> | ||
12 | #include <linux/of_dma.h> | ||
13 | #include "rsnd.h" | ||
14 | |||
15 | /* | ||
16 | * Audio DMAC peri peri register | ||
17 | */ | ||
18 | #define PDMASAR 0x00 | ||
19 | #define PDMADAR 0x04 | ||
20 | #define PDMACHCR 0x0c | ||
21 | |||
22 | /* PDMACHCR */ | ||
23 | #define PDMACHCR_DE (1 << 0) | ||
24 | |||
25 | struct rsnd_dma_ctrl { | ||
26 | void __iomem *base; | ||
27 | int dmapp_num; | ||
28 | }; | ||
29 | |||
30 | #define rsnd_priv_to_dmac(p) ((struct rsnd_dma_ctrl *)(p)->dma) | ||
31 | |||
32 | /* | ||
33 | * Audio DMAC | ||
34 | */ | ||
35 | static void rsnd_dmaen_complete(void *data) | ||
36 | { | ||
37 | struct rsnd_dma *dma = (struct rsnd_dma *)data; | ||
38 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
39 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
40 | |||
41 | /* | ||
42 | * Renesas sound Gen1 needs 1 DMAC, | ||
43 | * Gen2 needs 2 DMAC. | ||
44 | * In Gen2 case, it are Audio-DMAC, and Audio-DMAC-peri-peri. | ||
45 | * But, Audio-DMAC-peri-peri doesn't have interrupt, | ||
46 | * and this driver is assuming that here. | ||
47 | * | ||
48 | * If Audio-DMAC-peri-peri has interrpt, | ||
49 | * rsnd_dai_pointer_update() will be called twice, | ||
50 | * ant it will breaks io->byte_pos | ||
51 | */ | ||
52 | |||
53 | rsnd_dai_pointer_update(io, io->byte_per_period); | ||
54 | } | ||
55 | |||
56 | static void rsnd_dmaen_stop(struct rsnd_dma *dma) | ||
57 | { | ||
58 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
59 | |||
60 | dmaengine_terminate_all(dmaen->chan); | ||
61 | } | ||
62 | |||
63 | static void rsnd_dmaen_start(struct rsnd_dma *dma) | ||
64 | { | ||
65 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
66 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
67 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
68 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
69 | struct snd_pcm_substream *substream = io->substream; | ||
70 | struct device *dev = rsnd_priv_to_dev(priv); | ||
71 | struct dma_async_tx_descriptor *desc; | ||
72 | int is_play = rsnd_io_is_play(io); | ||
73 | |||
74 | desc = dmaengine_prep_dma_cyclic(dmaen->chan, | ||
75 | substream->runtime->dma_addr, | ||
76 | snd_pcm_lib_buffer_bytes(substream), | ||
77 | snd_pcm_lib_period_bytes(substream), | ||
78 | is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, | ||
79 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
80 | |||
81 | if (!desc) { | ||
82 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | ||
83 | return; | ||
84 | } | ||
85 | |||
86 | desc->callback = rsnd_dmaen_complete; | ||
87 | desc->callback_param = dma; | ||
88 | |||
89 | if (dmaengine_submit(desc) < 0) { | ||
90 | dev_err(dev, "dmaengine_submit() fail\n"); | ||
91 | return; | ||
92 | } | ||
93 | |||
94 | dma_async_issue_pending(dmaen->chan); | ||
95 | } | ||
96 | |||
97 | struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, | ||
98 | struct rsnd_mod *mod, char *name) | ||
99 | { | ||
100 | struct dma_chan *chan; | ||
101 | struct device_node *np; | ||
102 | int i = 0; | ||
103 | |||
104 | for_each_child_of_node(of_node, np) { | ||
105 | if (i == rsnd_mod_id(mod)) | ||
106 | break; | ||
107 | i++; | ||
108 | } | ||
109 | |||
110 | chan = of_dma_request_slave_channel(np, name); | ||
111 | |||
112 | of_node_put(np); | ||
113 | of_node_put(of_node); | ||
114 | |||
115 | return chan; | ||
116 | } | ||
117 | |||
118 | static struct dma_chan *rsnd_dmaen_request_channel(struct rsnd_mod *mod_from, | ||
119 | struct rsnd_mod *mod_to) | ||
120 | { | ||
121 | if ((!mod_from && !mod_to) || | ||
122 | (mod_from && mod_to)) | ||
123 | return NULL; | ||
124 | |||
125 | if (mod_from) | ||
126 | return rsnd_mod_dma_req(mod_from); | ||
127 | else | ||
128 | return rsnd_mod_dma_req(mod_to); | ||
129 | } | ||
130 | |||
131 | static int rsnd_dmaen_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, | ||
132 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) | ||
133 | { | ||
134 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
135 | struct device *dev = rsnd_priv_to_dev(priv); | ||
136 | struct dma_slave_config cfg = {}; | ||
137 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
138 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
139 | int is_play = rsnd_io_is_play(io); | ||
140 | int ret; | ||
141 | |||
142 | if (dmaen->chan) { | ||
143 | dev_err(dev, "it already has dma channel\n"); | ||
144 | return -EIO; | ||
145 | } | ||
146 | |||
147 | if (dev->of_node) { | ||
148 | dmaen->chan = rsnd_dmaen_request_channel(mod_from, mod_to); | ||
149 | } else { | ||
150 | dma_cap_mask_t mask; | ||
151 | |||
152 | dma_cap_zero(mask); | ||
153 | dma_cap_set(DMA_SLAVE, mask); | ||
154 | |||
155 | dmaen->chan = dma_request_channel(mask, shdma_chan_filter, | ||
156 | (void *)id); | ||
157 | } | ||
158 | if (IS_ERR_OR_NULL(dmaen->chan)) { | ||
159 | dev_err(dev, "can't get dma channel\n"); | ||
160 | goto rsnd_dma_channel_err; | ||
161 | } | ||
162 | |||
163 | cfg.direction = is_play ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM; | ||
164 | cfg.src_addr = dma->src_addr; | ||
165 | cfg.dst_addr = dma->dst_addr; | ||
166 | cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
167 | cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; | ||
168 | |||
169 | dev_dbg(dev, "dma : %pad -> %pad\n", | ||
170 | &cfg.src_addr, &cfg.dst_addr); | ||
171 | |||
172 | ret = dmaengine_slave_config(dmaen->chan, &cfg); | ||
173 | if (ret < 0) | ||
174 | goto rsnd_dma_init_err; | ||
175 | |||
176 | return 0; | ||
177 | |||
178 | rsnd_dma_init_err: | ||
179 | rsnd_dma_quit(dma); | ||
180 | rsnd_dma_channel_err: | ||
181 | |||
182 | /* | ||
183 | * DMA failed. try to PIO mode | ||
184 | * see | ||
185 | * rsnd_ssi_fallback() | ||
186 | * rsnd_rdai_continuance_probe() | ||
187 | */ | ||
188 | return -EAGAIN; | ||
189 | } | ||
190 | |||
191 | static void rsnd_dmaen_quit(struct rsnd_dma *dma) | ||
192 | { | ||
193 | struct rsnd_dmaen *dmaen = rsnd_dma_to_dmaen(dma); | ||
194 | |||
195 | if (dmaen->chan) | ||
196 | dma_release_channel(dmaen->chan); | ||
197 | |||
198 | dmaen->chan = NULL; | ||
199 | } | ||
200 | |||
201 | static struct rsnd_dma_ops rsnd_dmaen_ops = { | ||
202 | .start = rsnd_dmaen_start, | ||
203 | .stop = rsnd_dmaen_stop, | ||
204 | .init = rsnd_dmaen_init, | ||
205 | .quit = rsnd_dmaen_quit, | ||
206 | }; | ||
207 | |||
208 | /* | ||
209 | * Audio DMAC peri peri | ||
210 | */ | ||
211 | static const u8 gen2_id_table_ssiu[] = { | ||
212 | 0x00, /* SSI00 */ | ||
213 | 0x04, /* SSI10 */ | ||
214 | 0x08, /* SSI20 */ | ||
215 | 0x0c, /* SSI3 */ | ||
216 | 0x0d, /* SSI4 */ | ||
217 | 0x0e, /* SSI5 */ | ||
218 | 0x0f, /* SSI6 */ | ||
219 | 0x10, /* SSI7 */ | ||
220 | 0x11, /* SSI8 */ | ||
221 | 0x12, /* SSI90 */ | ||
222 | }; | ||
223 | static const u8 gen2_id_table_scu[] = { | ||
224 | 0x2d, /* SCU_SRCI0 */ | ||
225 | 0x2e, /* SCU_SRCI1 */ | ||
226 | 0x2f, /* SCU_SRCI2 */ | ||
227 | 0x30, /* SCU_SRCI3 */ | ||
228 | 0x31, /* SCU_SRCI4 */ | ||
229 | 0x32, /* SCU_SRCI5 */ | ||
230 | 0x33, /* SCU_SRCI6 */ | ||
231 | 0x34, /* SCU_SRCI7 */ | ||
232 | 0x35, /* SCU_SRCI8 */ | ||
233 | 0x36, /* SCU_SRCI9 */ | ||
234 | }; | ||
235 | static const u8 gen2_id_table_cmd[] = { | ||
236 | 0x37, /* SCU_CMD0 */ | ||
237 | 0x38, /* SCU_CMD1 */ | ||
238 | }; | ||
239 | |||
240 | static u32 rsnd_dmapp_get_id(struct rsnd_mod *mod) | ||
241 | { | ||
242 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
243 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); | ||
244 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | ||
245 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | ||
246 | const u8 *entry = NULL; | ||
247 | int id = rsnd_mod_id(mod); | ||
248 | int size = 0; | ||
249 | |||
250 | if (mod == ssi) { | ||
251 | entry = gen2_id_table_ssiu; | ||
252 | size = ARRAY_SIZE(gen2_id_table_ssiu); | ||
253 | } else if (mod == src) { | ||
254 | entry = gen2_id_table_scu; | ||
255 | size = ARRAY_SIZE(gen2_id_table_scu); | ||
256 | } else if (mod == dvc) { | ||
257 | entry = gen2_id_table_cmd; | ||
258 | size = ARRAY_SIZE(gen2_id_table_cmd); | ||
259 | } | ||
260 | |||
261 | if (!entry) | ||
262 | return 0xFF; | ||
263 | |||
264 | if (size <= id) | ||
265 | return 0xFF; | ||
266 | |||
267 | return entry[id]; | ||
268 | } | ||
269 | |||
270 | static u32 rsnd_dmapp_get_chcr(struct rsnd_mod *mod_from, | ||
271 | struct rsnd_mod *mod_to) | ||
272 | { | ||
273 | return (rsnd_dmapp_get_id(mod_from) << 24) + | ||
274 | (rsnd_dmapp_get_id(mod_to) << 16); | ||
275 | } | ||
276 | |||
277 | #define rsnd_dmapp_addr(dmac, dma, reg) \ | ||
278 | (dmac->base + 0x20 + reg + \ | ||
279 | (0x10 * rsnd_dma_to_dmapp(dma)->dmapp_id)) | ||
280 | static void rsnd_dmapp_write(struct rsnd_dma *dma, u32 data, u32 reg) | ||
281 | { | ||
282 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
283 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
284 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
285 | struct device *dev = rsnd_priv_to_dev(priv); | ||
286 | |||
287 | dev_dbg(dev, "w %p : %08x\n", rsnd_dmapp_addr(dmac, dma, reg), data); | ||
288 | |||
289 | iowrite32(data, rsnd_dmapp_addr(dmac, dma, reg)); | ||
290 | } | ||
291 | |||
292 | static u32 rsnd_dmapp_read(struct rsnd_dma *dma, u32 reg) | ||
293 | { | ||
294 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
295 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
296 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
297 | |||
298 | return ioread32(rsnd_dmapp_addr(dmac, dma, reg)); | ||
299 | } | ||
300 | |||
301 | static void rsnd_dmapp_stop(struct rsnd_dma *dma) | ||
302 | { | ||
303 | int i; | ||
304 | |||
305 | rsnd_dmapp_write(dma, 0, PDMACHCR); | ||
306 | |||
307 | for (i = 0; i < 1024; i++) { | ||
308 | if (0 == rsnd_dmapp_read(dma, PDMACHCR)) | ||
309 | return; | ||
310 | udelay(1); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | static void rsnd_dmapp_start(struct rsnd_dma *dma) | ||
315 | { | ||
316 | struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); | ||
317 | |||
318 | rsnd_dmapp_write(dma, dma->src_addr, PDMASAR); | ||
319 | rsnd_dmapp_write(dma, dma->dst_addr, PDMADAR); | ||
320 | rsnd_dmapp_write(dma, dmapp->chcr, PDMACHCR); | ||
321 | } | ||
322 | |||
323 | static int rsnd_dmapp_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, | ||
324 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to) | ||
325 | { | ||
326 | struct rsnd_dmapp *dmapp = rsnd_dma_to_dmapp(dma); | ||
327 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
328 | struct device *dev = rsnd_priv_to_dev(priv); | ||
329 | |||
330 | dmapp->dmapp_id = dmac->dmapp_num; | ||
331 | dmapp->chcr = rsnd_dmapp_get_chcr(mod_from, mod_to) | PDMACHCR_DE; | ||
332 | |||
333 | dmac->dmapp_num++; | ||
334 | |||
335 | rsnd_dmapp_stop(dma); | ||
336 | |||
337 | dev_dbg(dev, "id/src/dst/chcr = %d/%pad/%pad/%08x\n", | ||
338 | dmapp->dmapp_id, &dma->src_addr, &dma->dst_addr, dmapp->chcr); | ||
339 | |||
340 | return 0; | ||
341 | } | ||
342 | |||
343 | static struct rsnd_dma_ops rsnd_dmapp_ops = { | ||
344 | .start = rsnd_dmapp_start, | ||
345 | .stop = rsnd_dmapp_stop, | ||
346 | .init = rsnd_dmapp_init, | ||
347 | .quit = rsnd_dmapp_stop, | ||
348 | }; | ||
349 | |||
350 | /* | ||
351 | * Common DMAC Interface | ||
352 | */ | ||
353 | |||
354 | /* | ||
355 | * DMA read/write register offset | ||
356 | * | ||
357 | * RSND_xxx_I_N for Audio DMAC input | ||
358 | * RSND_xxx_O_N for Audio DMAC output | ||
359 | * RSND_xxx_I_P for Audio DMAC peri peri input | ||
360 | * RSND_xxx_O_P for Audio DMAC peri peri output | ||
361 | * | ||
362 | * ex) R-Car H2 case | ||
363 | * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out | ||
364 | * SSI : 0xec541000 / 0xec241008 / 0xec24100c | ||
365 | * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 | ||
366 | * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 | ||
367 | * CMD : 0xec500000 / / 0xec008000 0xec308000 | ||
368 | */ | ||
369 | #define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) | ||
370 | #define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) | ||
371 | |||
372 | #define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
373 | #define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
374 | |||
375 | #define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
376 | #define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
377 | |||
378 | #define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) | ||
379 | #define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) | ||
380 | |||
381 | #define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) | ||
382 | #define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) | ||
383 | |||
384 | #define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) | ||
385 | #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) | ||
386 | |||
387 | static dma_addr_t | ||
388 | rsnd_gen2_dma_addr(struct rsnd_priv *priv, | ||
389 | struct rsnd_mod *mod, | ||
390 | int is_play, int is_from) | ||
391 | { | ||
392 | struct device *dev = rsnd_priv_to_dev(priv); | ||
393 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
394 | phys_addr_t ssi_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SSI); | ||
395 | phys_addr_t src_reg = rsnd_gen_get_phy_addr(priv, RSND_GEN2_SCU); | ||
396 | int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); | ||
397 | int use_src = !!rsnd_io_to_mod_src(io); | ||
398 | int use_dvc = !!rsnd_io_to_mod_dvc(io); | ||
399 | int id = rsnd_mod_id(mod); | ||
400 | struct dma_addr { | ||
401 | dma_addr_t out_addr; | ||
402 | dma_addr_t in_addr; | ||
403 | } dma_addrs[3][2][3] = { | ||
404 | /* SRC */ | ||
405 | {{{ 0, 0 }, | ||
406 | /* Capture */ | ||
407 | { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, | ||
408 | { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, | ||
409 | /* Playback */ | ||
410 | {{ 0, 0, }, | ||
411 | { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, | ||
412 | { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } | ||
413 | }, | ||
414 | /* SSI */ | ||
415 | /* Capture */ | ||
416 | {{{ RDMA_SSI_O_N(ssi, id), 0 }, | ||
417 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
418 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
419 | /* Playback */ | ||
420 | {{ 0, RDMA_SSI_I_N(ssi, id) }, | ||
421 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
422 | { 0, RDMA_SSIU_I_P(ssi, id) } } | ||
423 | }, | ||
424 | /* SSIU */ | ||
425 | /* Capture */ | ||
426 | {{{ RDMA_SSIU_O_N(ssi, id), 0 }, | ||
427 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
428 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
429 | /* Playback */ | ||
430 | {{ 0, RDMA_SSIU_I_N(ssi, id) }, | ||
431 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
432 | { 0, RDMA_SSIU_I_P(ssi, id) } } }, | ||
433 | }; | ||
434 | |||
435 | /* it shouldn't happen */ | ||
436 | if (use_dvc && !use_src) | ||
437 | dev_err(dev, "DVC is selected without SRC\n"); | ||
438 | |||
439 | /* use SSIU or SSI ? */ | ||
440 | if (is_ssi && rsnd_ssi_use_busif(mod)) | ||
441 | is_ssi++; | ||
442 | |||
443 | return (is_from) ? | ||
444 | dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : | ||
445 | dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; | ||
446 | } | ||
447 | |||
448 | static dma_addr_t rsnd_dma_addr(struct rsnd_priv *priv, | ||
449 | struct rsnd_mod *mod, | ||
450 | int is_play, int is_from) | ||
451 | { | ||
452 | /* | ||
453 | * gen1 uses default DMA addr | ||
454 | */ | ||
455 | if (rsnd_is_gen1(priv)) | ||
456 | return 0; | ||
457 | |||
458 | if (!mod) | ||
459 | return 0; | ||
460 | |||
461 | return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); | ||
462 | } | ||
463 | |||
464 | #define MOD_MAX 4 /* MEM/SSI/SRC/DVC */ | ||
465 | static void rsnd_dma_of_path(struct rsnd_dma *dma, | ||
466 | int is_play, | ||
467 | struct rsnd_mod **mod_from, | ||
468 | struct rsnd_mod **mod_to) | ||
469 | { | ||
470 | struct rsnd_mod *this = rsnd_dma_to_mod(dma); | ||
471 | struct rsnd_dai_stream *io = rsnd_mod_to_io(this); | ||
472 | struct rsnd_mod *ssi = rsnd_io_to_mod_ssi(io); | ||
473 | struct rsnd_mod *src = rsnd_io_to_mod_src(io); | ||
474 | struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io); | ||
475 | struct rsnd_mod *mod[MOD_MAX]; | ||
476 | int i, index; | ||
477 | |||
478 | |||
479 | for (i = 0; i < MOD_MAX; i++) | ||
480 | mod[i] = NULL; | ||
481 | |||
482 | /* | ||
483 | * in play case... | ||
484 | * | ||
485 | * src -> dst | ||
486 | * | ||
487 | * mem -> SSI | ||
488 | * mem -> SRC -> SSI | ||
489 | * mem -> SRC -> DVC -> SSI | ||
490 | */ | ||
491 | mod[0] = NULL; /* for "mem" */ | ||
492 | index = 1; | ||
493 | for (i = 1; i < MOD_MAX; i++) { | ||
494 | if (!src) { | ||
495 | mod[i] = ssi; | ||
496 | } else if (!dvc) { | ||
497 | mod[i] = src; | ||
498 | src = NULL; | ||
499 | } else { | ||
500 | if ((!is_play) && (this == src)) | ||
501 | this = dvc; | ||
502 | |||
503 | mod[i] = (is_play) ? src : dvc; | ||
504 | i++; | ||
505 | mod[i] = (is_play) ? dvc : src; | ||
506 | src = NULL; | ||
507 | dvc = NULL; | ||
508 | } | ||
509 | |||
510 | if (mod[i] == this) | ||
511 | index = i; | ||
512 | |||
513 | if (mod[i] == ssi) | ||
514 | break; | ||
515 | } | ||
516 | |||
517 | if (is_play) { | ||
518 | *mod_from = mod[index - 1]; | ||
519 | *mod_to = mod[index]; | ||
520 | } else { | ||
521 | *mod_from = mod[index]; | ||
522 | *mod_to = mod[index - 1]; | ||
523 | } | ||
524 | } | ||
525 | |||
526 | void rsnd_dma_stop(struct rsnd_dma *dma) | ||
527 | { | ||
528 | dma->ops->stop(dma); | ||
529 | } | ||
530 | |||
531 | void rsnd_dma_start(struct rsnd_dma *dma) | ||
532 | { | ||
533 | dma->ops->start(dma); | ||
534 | } | ||
535 | |||
536 | void rsnd_dma_quit(struct rsnd_dma *dma) | ||
537 | { | ||
538 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
539 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
540 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
541 | |||
542 | if (!dmac) | ||
543 | return; | ||
544 | |||
545 | dma->ops->quit(dma); | ||
546 | } | ||
547 | |||
548 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id) | ||
549 | { | ||
550 | struct rsnd_mod *mod = rsnd_dma_to_mod(dma); | ||
551 | struct rsnd_mod *mod_from; | ||
552 | struct rsnd_mod *mod_to; | ||
553 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
554 | struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv); | ||
555 | int is_play = rsnd_io_is_play(io); | ||
556 | |||
557 | /* | ||
558 | * DMA failed. try to PIO mode | ||
559 | * see | ||
560 | * rsnd_ssi_fallback() | ||
561 | * rsnd_rdai_continuance_probe() | ||
562 | */ | ||
563 | if (!dmac) | ||
564 | return -EAGAIN; | ||
565 | |||
566 | rsnd_dma_of_path(dma, is_play, &mod_from, &mod_to); | ||
567 | |||
568 | dma->src_addr = rsnd_dma_addr(priv, mod_from, is_play, 1); | ||
569 | dma->dst_addr = rsnd_dma_addr(priv, mod_to, is_play, 0); | ||
570 | |||
571 | /* for Gen2 */ | ||
572 | if (mod_from && mod_to) | ||
573 | dma->ops = &rsnd_dmapp_ops; | ||
574 | else | ||
575 | dma->ops = &rsnd_dmaen_ops; | ||
576 | |||
577 | /* for Gen1, overwrite */ | ||
578 | if (rsnd_is_gen1(priv)) | ||
579 | dma->ops = &rsnd_dmaen_ops; | ||
580 | |||
581 | return dma->ops->init(priv, dma, id, mod_from, mod_to); | ||
582 | } | ||
583 | |||
584 | int rsnd_dma_probe(struct platform_device *pdev, | ||
585 | const struct rsnd_of_data *of_data, | ||
586 | struct rsnd_priv *priv) | ||
587 | { | ||
588 | struct device *dev = rsnd_priv_to_dev(priv); | ||
589 | struct rsnd_dma_ctrl *dmac; | ||
590 | struct resource *res; | ||
591 | |||
592 | /* | ||
593 | * for Gen1 | ||
594 | */ | ||
595 | if (rsnd_is_gen1(priv)) | ||
596 | return 0; | ||
597 | |||
598 | /* | ||
599 | * for Gen2 | ||
600 | */ | ||
601 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audmapp"); | ||
602 | dmac = devm_kzalloc(dev, sizeof(*dmac), GFP_KERNEL); | ||
603 | if (!dmac || !res) { | ||
604 | dev_err(dev, "dma allocate failed\n"); | ||
605 | return 0; /* it will be PIO mode */ | ||
606 | } | ||
607 | |||
608 | dmac->dmapp_num = 0; | ||
609 | dmac->base = devm_ioremap_resource(dev, res); | ||
610 | if (IS_ERR(dmac->base)) | ||
611 | return PTR_ERR(dmac->base); | ||
612 | |||
613 | priv->dma = dmac; | ||
614 | |||
615 | return 0; | ||
616 | } | ||
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c index 261997a3f589..e5fcb062ad77 100644 --- a/sound/soc/sh/rcar/dvc.c +++ b/sound/soc/sh/rcar/dvc.c | |||
@@ -24,6 +24,9 @@ struct rsnd_dvc { | |||
24 | struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ | 24 | struct rsnd_kctrl_cfg_s rdown; /* Ramp Rate Down */ |
25 | }; | 25 | }; |
26 | 26 | ||
27 | #define rsnd_dvc_of_node(priv) \ | ||
28 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,dvc") | ||
29 | |||
27 | #define rsnd_mod_to_dvc(_mod) \ | 30 | #define rsnd_mod_to_dvc(_mod) \ |
28 | container_of((_mod), struct rsnd_dvc, mod) | 31 | container_of((_mod), struct rsnd_dvc, mod) |
29 | 32 | ||
@@ -33,7 +36,7 @@ struct rsnd_dvc { | |||
33 | ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ | 36 | ((pos) = (struct rsnd_dvc *)(priv)->dvc + i); \ |
34 | i++) | 37 | i++) |
35 | 38 | ||
36 | static const char const *dvc_ramp_rate[] = { | 39 | static const char * const dvc_ramp_rate[] = { |
37 | "128 dB/1 step", /* 00000 */ | 40 | "128 dB/1 step", /* 00000 */ |
38 | "64 dB/1 step", /* 00001 */ | 41 | "64 dB/1 step", /* 00001 */ |
39 | "32 dB/1 step", /* 00010 */ | 42 | "32 dB/1 step", /* 00010 */ |
@@ -116,17 +119,6 @@ static void rsnd_dvc_volume_update(struct rsnd_mod *mod) | |||
116 | rsnd_mod_write(mod, DVC_DVUER, 1); | 119 | rsnd_mod_write(mod, DVC_DVUER, 1); |
117 | } | 120 | } |
118 | 121 | ||
119 | static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod, | ||
120 | struct rsnd_priv *priv) | ||
121 | { | ||
122 | struct device *dev = rsnd_priv_to_dev(priv); | ||
123 | |||
124 | dev_dbg(dev, "%s[%d] (Gen2) is probed\n", | ||
125 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
126 | |||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, | 122 | static int rsnd_dvc_remove_gen2(struct rsnd_mod *mod, |
131 | struct rsnd_priv *priv) | 123 | struct rsnd_priv *priv) |
132 | { | 124 | { |
@@ -269,9 +261,17 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod, | |||
269 | return 0; | 261 | return 0; |
270 | } | 262 | } |
271 | 263 | ||
264 | static struct dma_chan *rsnd_dvc_dma_req(struct rsnd_mod *mod) | ||
265 | { | ||
266 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
267 | |||
268 | return rsnd_dma_request_channel(rsnd_dvc_of_node(priv), | ||
269 | mod, "tx"); | ||
270 | } | ||
271 | |||
272 | static struct rsnd_mod_ops rsnd_dvc_ops = { | 272 | static struct rsnd_mod_ops rsnd_dvc_ops = { |
273 | .name = DVC_NAME, | 273 | .name = DVC_NAME, |
274 | .probe = rsnd_dvc_probe_gen2, | 274 | .dma_req = rsnd_dvc_dma_req, |
275 | .remove = rsnd_dvc_remove_gen2, | 275 | .remove = rsnd_dvc_remove_gen2, |
276 | .init = rsnd_dvc_init, | 276 | .init = rsnd_dvc_init, |
277 | .quit = rsnd_dvc_quit, | 277 | .quit = rsnd_dvc_quit, |
@@ -370,8 +370,6 @@ int rsnd_dvc_probe(struct platform_device *pdev, | |||
370 | clk, RSND_MOD_DVC, i); | 370 | clk, RSND_MOD_DVC, i); |
371 | if (ret) | 371 | if (ret) |
372 | return ret; | 372 | return ret; |
373 | |||
374 | dev_dbg(dev, "CMD%d probed\n", i); | ||
375 | } | 373 | } |
376 | 374 | ||
377 | return 0; | 375 | return 0; |
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c index de0685f2abae..8c7dc51b1c4f 100644 --- a/sound/soc/sh/rcar/gen.c +++ b/sound/soc/sh/rcar/gen.c | |||
@@ -28,6 +28,7 @@ struct rsnd_gen { | |||
28 | 28 | ||
29 | struct regmap *regmap[RSND_BASE_MAX]; | 29 | struct regmap *regmap[RSND_BASE_MAX]; |
30 | struct regmap_field *regs[RSND_REG_MAX]; | 30 | struct regmap_field *regs[RSND_REG_MAX]; |
31 | phys_addr_t res[RSND_REG_MAX]; | ||
31 | }; | 32 | }; |
32 | 33 | ||
33 | #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) | 34 | #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) |
@@ -118,11 +119,19 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, | |||
118 | mask, data); | 119 | mask, data); |
119 | } | 120 | } |
120 | 121 | ||
121 | #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf) \ | 122 | phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id) |
122 | _rsnd_gen_regmap_init(priv, id_size, reg_id, conf, ARRAY_SIZE(conf)) | 123 | { |
124 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
125 | |||
126 | return gen->res[reg_id]; | ||
127 | } | ||
128 | |||
129 | #define rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf) \ | ||
130 | _rsnd_gen_regmap_init(priv, id_size, reg_id, name, conf, ARRAY_SIZE(conf)) | ||
123 | static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | 131 | static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, |
124 | int id_size, | 132 | int id_size, |
125 | int reg_id, | 133 | int reg_id, |
134 | const char *name, | ||
126 | struct rsnd_regmap_field_conf *conf, | 135 | struct rsnd_regmap_field_conf *conf, |
127 | int conf_size) | 136 | int conf_size) |
128 | { | 137 | { |
@@ -141,8 +150,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | |||
141 | regc.reg_bits = 32; | 150 | regc.reg_bits = 32; |
142 | regc.val_bits = 32; | 151 | regc.val_bits = 32; |
143 | regc.reg_stride = 4; | 152 | regc.reg_stride = 4; |
153 | regc.name = name; | ||
144 | 154 | ||
145 | res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); | 155 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); |
156 | if (!res) | ||
157 | res = platform_get_resource(pdev, IORESOURCE_MEM, reg_id); | ||
146 | if (!res) | 158 | if (!res) |
147 | return -ENODEV; | 159 | return -ENODEV; |
148 | 160 | ||
@@ -156,6 +168,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | |||
156 | 168 | ||
157 | gen->base[reg_id] = base; | 169 | gen->base[reg_id] = base; |
158 | gen->regmap[reg_id] = regmap; | 170 | gen->regmap[reg_id] = regmap; |
171 | gen->res[reg_id] = res->start; | ||
159 | 172 | ||
160 | for (i = 0; i < conf_size; i++) { | 173 | for (i = 0; i < conf_size; i++) { |
161 | 174 | ||
@@ -176,125 +189,11 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv, | |||
176 | } | 189 | } |
177 | 190 | ||
178 | /* | 191 | /* |
179 | * DMA read/write register offset | ||
180 | * | ||
181 | * RSND_xxx_I_N for Audio DMAC input | ||
182 | * RSND_xxx_O_N for Audio DMAC output | ||
183 | * RSND_xxx_I_P for Audio DMAC peri peri input | ||
184 | * RSND_xxx_O_P for Audio DMAC peri peri output | ||
185 | * | ||
186 | * ex) R-Car H2 case | ||
187 | * mod / DMAC in / DMAC out / DMAC PP in / DMAC pp out | ||
188 | * SSI : 0xec541000 / 0xec241008 / 0xec24100c | ||
189 | * SSIU: 0xec541000 / 0xec100000 / 0xec100000 / 0xec400000 / 0xec400000 | ||
190 | * SCU : 0xec500000 / 0xec000000 / 0xec004000 / 0xec300000 / 0xec304000 | ||
191 | * CMD : 0xec500000 / / 0xec008000 0xec308000 | ||
192 | */ | ||
193 | #define RDMA_SSI_I_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0x8) | ||
194 | #define RDMA_SSI_O_N(addr, i) (addr ##_reg - 0x00300000 + (0x40 * i) + 0xc) | ||
195 | |||
196 | #define RDMA_SSIU_I_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
197 | #define RDMA_SSIU_O_N(addr, i) (addr ##_reg - 0x00441000 + (0x1000 * i)) | ||
198 | |||
199 | #define RDMA_SSIU_I_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
200 | #define RDMA_SSIU_O_P(addr, i) (addr ##_reg - 0x00141000 + (0x1000 * i)) | ||
201 | |||
202 | #define RDMA_SRC_I_N(addr, i) (addr ##_reg - 0x00500000 + (0x400 * i)) | ||
203 | #define RDMA_SRC_O_N(addr, i) (addr ##_reg - 0x004fc000 + (0x400 * i)) | ||
204 | |||
205 | #define RDMA_SRC_I_P(addr, i) (addr ##_reg - 0x00200000 + (0x400 * i)) | ||
206 | #define RDMA_SRC_O_P(addr, i) (addr ##_reg - 0x001fc000 + (0x400 * i)) | ||
207 | |||
208 | #define RDMA_CMD_O_N(addr, i) (addr ##_reg - 0x004f8000 + (0x400 * i)) | ||
209 | #define RDMA_CMD_O_P(addr, i) (addr ##_reg - 0x001f8000 + (0x400 * i)) | ||
210 | |||
211 | static dma_addr_t | ||
212 | rsnd_gen2_dma_addr(struct rsnd_priv *priv, | ||
213 | struct rsnd_mod *mod, | ||
214 | int is_play, int is_from) | ||
215 | { | ||
216 | struct platform_device *pdev = rsnd_priv_to_pdev(priv); | ||
217 | struct device *dev = rsnd_priv_to_dev(priv); | ||
218 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
219 | dma_addr_t ssi_reg = platform_get_resource(pdev, | ||
220 | IORESOURCE_MEM, RSND_GEN2_SSI)->start; | ||
221 | dma_addr_t src_reg = platform_get_resource(pdev, | ||
222 | IORESOURCE_MEM, RSND_GEN2_SCU)->start; | ||
223 | int is_ssi = !!(rsnd_io_to_mod_ssi(io) == mod); | ||
224 | int use_src = !!rsnd_io_to_mod_src(io); | ||
225 | int use_dvc = !!rsnd_io_to_mod_dvc(io); | ||
226 | int id = rsnd_mod_id(mod); | ||
227 | struct dma_addr { | ||
228 | dma_addr_t out_addr; | ||
229 | dma_addr_t in_addr; | ||
230 | } dma_addrs[3][2][3] = { | ||
231 | /* SRC */ | ||
232 | {{{ 0, 0 }, | ||
233 | /* Capture */ | ||
234 | { RDMA_SRC_O_N(src, id), RDMA_SRC_I_P(src, id) }, | ||
235 | { RDMA_CMD_O_N(src, id), RDMA_SRC_I_P(src, id) } }, | ||
236 | /* Playback */ | ||
237 | {{ 0, 0, }, | ||
238 | { RDMA_SRC_O_P(src, id), RDMA_SRC_I_N(src, id) }, | ||
239 | { RDMA_CMD_O_P(src, id), RDMA_SRC_I_N(src, id) } } | ||
240 | }, | ||
241 | /* SSI */ | ||
242 | /* Capture */ | ||
243 | {{{ RDMA_SSI_O_N(ssi, id), 0 }, | ||
244 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
245 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
246 | /* Playback */ | ||
247 | {{ 0, RDMA_SSI_I_N(ssi, id) }, | ||
248 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
249 | { 0, RDMA_SSIU_I_P(ssi, id) } } | ||
250 | }, | ||
251 | /* SSIU */ | ||
252 | /* Capture */ | ||
253 | {{{ RDMA_SSIU_O_N(ssi, id), 0 }, | ||
254 | { RDMA_SSIU_O_P(ssi, id), 0 }, | ||
255 | { RDMA_SSIU_O_P(ssi, id), 0 } }, | ||
256 | /* Playback */ | ||
257 | {{ 0, RDMA_SSIU_I_N(ssi, id) }, | ||
258 | { 0, RDMA_SSIU_I_P(ssi, id) }, | ||
259 | { 0, RDMA_SSIU_I_P(ssi, id) } } }, | ||
260 | }; | ||
261 | |||
262 | /* it shouldn't happen */ | ||
263 | if (use_dvc && !use_src) | ||
264 | dev_err(dev, "DVC is selected without SRC\n"); | ||
265 | |||
266 | /* use SSIU or SSI ? */ | ||
267 | if (is_ssi && (0 == strcmp(rsnd_mod_dma_name(mod), "ssiu"))) | ||
268 | is_ssi++; | ||
269 | |||
270 | return (is_from) ? | ||
271 | dma_addrs[is_ssi][is_play][use_src + use_dvc].out_addr : | ||
272 | dma_addrs[is_ssi][is_play][use_src + use_dvc].in_addr; | ||
273 | } | ||
274 | |||
275 | dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, | ||
276 | struct rsnd_mod *mod, | ||
277 | int is_play, int is_from) | ||
278 | { | ||
279 | /* | ||
280 | * gen1 uses default DMA addr | ||
281 | */ | ||
282 | if (rsnd_is_gen1(priv)) | ||
283 | return 0; | ||
284 | |||
285 | if (!mod) | ||
286 | return 0; | ||
287 | |||
288 | return rsnd_gen2_dma_addr(priv, mod, is_play, is_from); | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Gen2 | 192 | * Gen2 |
293 | */ | 193 | */ |
294 | static int rsnd_gen2_probe(struct platform_device *pdev, | 194 | static int rsnd_gen2_probe(struct platform_device *pdev, |
295 | struct rsnd_priv *priv) | 195 | struct rsnd_priv *priv) |
296 | { | 196 | { |
297 | struct device *dev = rsnd_priv_to_dev(priv); | ||
298 | struct rsnd_regmap_field_conf conf_ssiu[] = { | 197 | struct rsnd_regmap_field_conf conf_ssiu[] = { |
299 | RSND_GEN_S_REG(SSI_MODE0, 0x800), | 198 | RSND_GEN_S_REG(SSI_MODE0, 0x800), |
300 | RSND_GEN_S_REG(SSI_MODE1, 0x804), | 199 | RSND_GEN_S_REG(SSI_MODE1, 0x804), |
@@ -368,18 +267,16 @@ static int rsnd_gen2_probe(struct platform_device *pdev, | |||
368 | int ret_adg; | 267 | int ret_adg; |
369 | int ret_ssi; | 268 | int ret_ssi; |
370 | 269 | ||
371 | ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, conf_ssiu); | 270 | ret_ssiu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSIU, "ssiu", conf_ssiu); |
372 | ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, conf_scu); | 271 | ret_scu = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SCU, "scu", conf_scu); |
373 | ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, conf_adg); | 272 | ret_adg = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_ADG, "adg", conf_adg); |
374 | ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, conf_ssi); | 273 | ret_ssi = rsnd_gen_regmap_init(priv, 10, RSND_GEN2_SSI, "ssi", conf_ssi); |
375 | if (ret_ssiu < 0 || | 274 | if (ret_ssiu < 0 || |
376 | ret_scu < 0 || | 275 | ret_scu < 0 || |
377 | ret_adg < 0 || | 276 | ret_adg < 0 || |
378 | ret_ssi < 0) | 277 | ret_ssi < 0) |
379 | return ret_ssiu | ret_scu | ret_adg | ret_ssi; | 278 | return ret_ssiu | ret_scu | ret_adg | ret_ssi; |
380 | 279 | ||
381 | dev_dbg(dev, "Gen2 is probed\n"); | ||
382 | |||
383 | return 0; | 280 | return 0; |
384 | } | 281 | } |
385 | 282 | ||
@@ -390,7 +287,6 @@ static int rsnd_gen2_probe(struct platform_device *pdev, | |||
390 | static int rsnd_gen1_probe(struct platform_device *pdev, | 287 | static int rsnd_gen1_probe(struct platform_device *pdev, |
391 | struct rsnd_priv *priv) | 288 | struct rsnd_priv *priv) |
392 | { | 289 | { |
393 | struct device *dev = rsnd_priv_to_dev(priv); | ||
394 | struct rsnd_regmap_field_conf conf_sru[] = { | 290 | struct rsnd_regmap_field_conf conf_sru[] = { |
395 | RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), | 291 | RSND_GEN_S_REG(SRC_ROUTE_SEL, 0x00), |
396 | RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), | 292 | RSND_GEN_S_REG(SRC_TMG_SEL0, 0x08), |
@@ -440,16 +336,14 @@ static int rsnd_gen1_probe(struct platform_device *pdev, | |||
440 | int ret_adg; | 336 | int ret_adg; |
441 | int ret_ssi; | 337 | int ret_ssi; |
442 | 338 | ||
443 | ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, conf_sru); | 339 | ret_sru = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SRU, "sru", conf_sru); |
444 | ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, conf_adg); | 340 | ret_adg = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_ADG, "adg", conf_adg); |
445 | ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, conf_ssi); | 341 | ret_ssi = rsnd_gen_regmap_init(priv, 9, RSND_GEN1_SSI, "ssi", conf_ssi); |
446 | if (ret_sru < 0 || | 342 | if (ret_sru < 0 || |
447 | ret_adg < 0 || | 343 | ret_adg < 0 || |
448 | ret_ssi < 0) | 344 | ret_ssi < 0) |
449 | return ret_sru | ret_adg | ret_ssi; | 345 | return ret_sru | ret_adg | ret_ssi; |
450 | 346 | ||
451 | dev_dbg(dev, "Gen1 is probed\n"); | ||
452 | |||
453 | return 0; | 347 | return 0; |
454 | } | 348 | } |
455 | 349 | ||
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 1bccc5515b5a..4e6de6804cfb 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -170,21 +170,47 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod); | |||
170 | /* | 170 | /* |
171 | * R-Car DMA | 171 | * R-Car DMA |
172 | */ | 172 | */ |
173 | struct rsnd_dma { | 173 | struct rsnd_dma; |
174 | struct sh_dmae_slave slave; | 174 | struct rsnd_dma_ops { |
175 | void (*start)(struct rsnd_dma *dma); | ||
176 | void (*stop)(struct rsnd_dma *dma); | ||
177 | int (*init)(struct rsnd_priv *priv, struct rsnd_dma *dma, int id, | ||
178 | struct rsnd_mod *mod_from, struct rsnd_mod *mod_to); | ||
179 | void (*quit)(struct rsnd_dma *dma); | ||
180 | }; | ||
181 | |||
182 | struct rsnd_dmaen { | ||
175 | struct dma_chan *chan; | 183 | struct dma_chan *chan; |
176 | enum dma_transfer_direction dir; | ||
177 | dma_addr_t addr; | ||
178 | }; | 184 | }; |
179 | 185 | ||
186 | struct rsnd_dmapp { | ||
187 | int dmapp_id; | ||
188 | u32 chcr; | ||
189 | }; | ||
190 | |||
191 | struct rsnd_dma { | ||
192 | struct rsnd_dma_ops *ops; | ||
193 | dma_addr_t src_addr; | ||
194 | dma_addr_t dst_addr; | ||
195 | union { | ||
196 | struct rsnd_dmaen en; | ||
197 | struct rsnd_dmapp pp; | ||
198 | } dma; | ||
199 | }; | ||
200 | #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en) | ||
201 | #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp) | ||
202 | |||
180 | void rsnd_dma_start(struct rsnd_dma *dma); | 203 | void rsnd_dma_start(struct rsnd_dma *dma); |
181 | void rsnd_dma_stop(struct rsnd_dma *dma); | 204 | void rsnd_dma_stop(struct rsnd_dma *dma); |
182 | int rsnd_dma_available(struct rsnd_dma *dma); | 205 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, int id); |
183 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | 206 | void rsnd_dma_quit(struct rsnd_dma *dma); |
184 | int is_play, int id); | 207 | int rsnd_dma_probe(struct platform_device *pdev, |
185 | void rsnd_dma_quit(struct rsnd_priv *priv, | 208 | const struct rsnd_of_data *of_data, |
186 | struct rsnd_dma *dma); | 209 | struct rsnd_priv *priv); |
210 | struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, | ||
211 | struct rsnd_mod *mod, char *name); | ||
187 | 212 | ||
213 | #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) | ||
188 | 214 | ||
189 | /* | 215 | /* |
190 | * R-Car sound mod | 216 | * R-Car sound mod |
@@ -198,7 +224,7 @@ enum rsnd_mod_type { | |||
198 | 224 | ||
199 | struct rsnd_mod_ops { | 225 | struct rsnd_mod_ops { |
200 | char *name; | 226 | char *name; |
201 | char* (*dma_name)(struct rsnd_mod *mod); | 227 | struct dma_chan* (*dma_req)(struct rsnd_mod *mod); |
202 | int (*probe)(struct rsnd_mod *mod, | 228 | int (*probe)(struct rsnd_mod *mod, |
203 | struct rsnd_priv *priv); | 229 | struct rsnd_priv *priv); |
204 | int (*remove)(struct rsnd_mod *mod, | 230 | int (*remove)(struct rsnd_mod *mod, |
@@ -213,6 +239,9 @@ struct rsnd_mod_ops { | |||
213 | struct rsnd_priv *priv); | 239 | struct rsnd_priv *priv); |
214 | int (*pcm_new)(struct rsnd_mod *mod, | 240 | int (*pcm_new)(struct rsnd_mod *mod, |
215 | struct snd_soc_pcm_runtime *rtd); | 241 | struct snd_soc_pcm_runtime *rtd); |
242 | int (*hw_params)(struct rsnd_mod *mod, | ||
243 | struct snd_pcm_substream *substream, | ||
244 | struct snd_pcm_hw_params *hw_params); | ||
216 | int (*fallback)(struct rsnd_mod *mod, | 245 | int (*fallback)(struct rsnd_mod *mod, |
217 | struct rsnd_priv *priv); | 246 | struct rsnd_priv *priv); |
218 | }; | 247 | }; |
@@ -236,6 +265,9 @@ struct rsnd_mod { | |||
236 | * 2 0: start 1: stop | 265 | * 2 0: start 1: stop |
237 | * 3 0: pcm_new | 266 | * 3 0: pcm_new |
238 | * 4 0: fallback | 267 | * 4 0: fallback |
268 | * | ||
269 | * 31 bit is always called (see __rsnd_mod_call) | ||
270 | * 31 0: hw_params | ||
239 | */ | 271 | */ |
240 | #define __rsnd_mod_shift_probe 0 | 272 | #define __rsnd_mod_shift_probe 0 |
241 | #define __rsnd_mod_shift_remove 0 | 273 | #define __rsnd_mod_shift_remove 0 |
@@ -245,6 +277,7 @@ struct rsnd_mod { | |||
245 | #define __rsnd_mod_shift_stop 2 | 277 | #define __rsnd_mod_shift_stop 2 |
246 | #define __rsnd_mod_shift_pcm_new 3 | 278 | #define __rsnd_mod_shift_pcm_new 3 |
247 | #define __rsnd_mod_shift_fallback 4 | 279 | #define __rsnd_mod_shift_fallback 4 |
280 | #define __rsnd_mod_shift_hw_params 31 /* always called */ | ||
248 | 281 | ||
249 | #define __rsnd_mod_call_probe 0 | 282 | #define __rsnd_mod_call_probe 0 |
250 | #define __rsnd_mod_call_remove 1 | 283 | #define __rsnd_mod_call_remove 1 |
@@ -254,10 +287,10 @@ struct rsnd_mod { | |||
254 | #define __rsnd_mod_call_stop 1 | 287 | #define __rsnd_mod_call_stop 1 |
255 | #define __rsnd_mod_call_pcm_new 0 | 288 | #define __rsnd_mod_call_pcm_new 0 |
256 | #define __rsnd_mod_call_fallback 0 | 289 | #define __rsnd_mod_call_fallback 0 |
290 | #define __rsnd_mod_call_hw_params 0 | ||
257 | 291 | ||
258 | #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) | 292 | #define rsnd_mod_to_priv(mod) (rsnd_io_to_priv(rsnd_mod_to_io(mod))) |
259 | #define rsnd_mod_to_dma(mod) (&(mod)->dma) | 293 | #define rsnd_mod_to_dma(mod) (&(mod)->dma) |
260 | #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) | ||
261 | #define rsnd_mod_to_io(mod) ((mod)->io) | 294 | #define rsnd_mod_to_io(mod) ((mod)->io) |
262 | #define rsnd_mod_id(mod) ((mod)->id) | 295 | #define rsnd_mod_id(mod) ((mod)->id) |
263 | #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) | 296 | #define rsnd_mod_hw_start(mod) clk_enable((mod)->clk) |
@@ -270,13 +303,14 @@ int rsnd_mod_init(struct rsnd_mod *mod, | |||
270 | int id); | 303 | int id); |
271 | void rsnd_mod_quit(struct rsnd_mod *mod); | 304 | void rsnd_mod_quit(struct rsnd_mod *mod); |
272 | char *rsnd_mod_name(struct rsnd_mod *mod); | 305 | char *rsnd_mod_name(struct rsnd_mod *mod); |
273 | char *rsnd_mod_dma_name(struct rsnd_mod *mod); | 306 | struct dma_chan *rsnd_mod_dma_req(struct rsnd_mod *mod); |
274 | 307 | ||
275 | /* | 308 | /* |
276 | * R-Car sound DAI | 309 | * R-Car sound DAI |
277 | */ | 310 | */ |
278 | #define RSND_DAI_NAME_SIZE 16 | 311 | #define RSND_DAI_NAME_SIZE 16 |
279 | struct rsnd_dai_stream { | 312 | struct rsnd_dai_stream { |
313 | char name[RSND_DAI_NAME_SIZE]; | ||
280 | struct snd_pcm_substream *substream; | 314 | struct snd_pcm_substream *substream; |
281 | struct rsnd_mod *mod[RSND_MOD_MAX]; | 315 | struct rsnd_mod *mod[RSND_MOD_MAX]; |
282 | struct rsnd_dai_path_info *info; /* rcar_snd.h */ | 316 | struct rsnd_dai_path_info *info; /* rcar_snd.h */ |
@@ -332,9 +366,7 @@ int rsnd_gen_probe(struct platform_device *pdev, | |||
332 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | 366 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, |
333 | struct rsnd_mod *mod, | 367 | struct rsnd_mod *mod, |
334 | enum rsnd_reg reg); | 368 | enum rsnd_reg reg); |
335 | dma_addr_t rsnd_gen_dma_addr(struct rsnd_priv *priv, | 369 | phys_addr_t rsnd_gen_get_phy_addr(struct rsnd_priv *priv, int reg_id); |
336 | struct rsnd_mod *mod, | ||
337 | int is_play, int is_from); | ||
338 | 370 | ||
339 | #define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) | 371 | #define rsnd_is_gen1(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN1) |
340 | #define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) | 372 | #define rsnd_is_gen2(s) (((s)->info->flags & RSND_GEN_MASK) == RSND_GEN2) |
@@ -390,6 +422,11 @@ struct rsnd_priv { | |||
390 | void *adg; | 422 | void *adg; |
391 | 423 | ||
392 | /* | 424 | /* |
425 | * below value will be filled on rsnd_dma_probe() | ||
426 | */ | ||
427 | void *dma; | ||
428 | |||
429 | /* | ||
393 | * below value will be filled on rsnd_ssi_probe() | 430 | * below value will be filled on rsnd_ssi_probe() |
394 | */ | 431 | */ |
395 | void *ssi; | 432 | void *ssi; |
@@ -415,19 +452,6 @@ struct rsnd_priv { | |||
415 | #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) | 452 | #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) |
416 | #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) | 453 | #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) |
417 | 454 | ||
418 | #define rsnd_info_is_playback(priv, type) \ | ||
419 | ({ \ | ||
420 | struct rcar_snd_info *info = rsnd_priv_to_info(priv); \ | ||
421 | int i, is_play = 0; \ | ||
422 | for (i = 0; i < info->dai_info_nr; i++) { \ | ||
423 | if (info->dai_info[i].playback.type == (type)->info) { \ | ||
424 | is_play = 1; \ | ||
425 | break; \ | ||
426 | } \ | ||
427 | } \ | ||
428 | is_play; \ | ||
429 | }) | ||
430 | |||
431 | /* | 455 | /* |
432 | * rsnd_kctrl | 456 | * rsnd_kctrl |
433 | */ | 457 | */ |
@@ -506,6 +530,7 @@ void rsnd_ssi_remove(struct platform_device *pdev, | |||
506 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); | 530 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); |
507 | int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); | 531 | int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod); |
508 | int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); | 532 | int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod); |
533 | int rsnd_ssi_use_busif(struct rsnd_mod *mod); | ||
509 | 534 | ||
510 | /* | 535 | /* |
511 | * R-Car DVC | 536 | * R-Car DVC |
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c new file mode 100644 index 000000000000..a68517afe615 --- /dev/null +++ b/sound/soc/sh/rcar/rsrc-card.c | |||
@@ -0,0 +1,512 @@ | |||
1 | /* | ||
2 | * Renesas Sampling Rate Convert Sound Card for DPCM | ||
3 | * | ||
4 | * Copyright (C) 2015 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * based on ${LINUX}/sound/soc/generic/simple-card.c | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | #include <linux/clk.h> | ||
14 | #include <linux/device.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of.h> | ||
17 | #include <linux/of_device.h> | ||
18 | #include <linux/platform_device.h> | ||
19 | #include <linux/string.h> | ||
20 | #include <sound/jack.h> | ||
21 | #include <sound/soc.h> | ||
22 | #include <sound/soc-dai.h> | ||
23 | |||
24 | struct rsrc_card_of_data { | ||
25 | const char *prefix; | ||
26 | const struct snd_soc_dapm_route *routes; | ||
27 | int num_routes; | ||
28 | }; | ||
29 | |||
30 | static const struct snd_soc_dapm_route routes_ssi0_ak4642[] = { | ||
31 | {"ak4642 Playback", NULL, "DAI0 Playback"}, | ||
32 | {"DAI0 Capture", NULL, "ak4642 Capture"}, | ||
33 | }; | ||
34 | |||
35 | static const struct rsrc_card_of_data routes_of_ssi0_ak4642 = { | ||
36 | .prefix = "ak4642", | ||
37 | .routes = routes_ssi0_ak4642, | ||
38 | .num_routes = ARRAY_SIZE(routes_ssi0_ak4642), | ||
39 | }; | ||
40 | |||
41 | static const struct of_device_id rsrc_card_of_match[] = { | ||
42 | { .compatible = "renesas,rsrc-card,lager", .data = &routes_of_ssi0_ak4642 }, | ||
43 | { .compatible = "renesas,rsrc-card,koelsch", .data = &routes_of_ssi0_ak4642 }, | ||
44 | {}, | ||
45 | }; | ||
46 | MODULE_DEVICE_TABLE(of, rsrc_card_of_match); | ||
47 | |||
48 | struct rsrc_card_dai { | ||
49 | const char *name; | ||
50 | unsigned int fmt; | ||
51 | unsigned int sysclk; | ||
52 | struct clk *clk; | ||
53 | }; | ||
54 | |||
55 | #define RSRC_FB_NUM 2 /* FE/BE */ | ||
56 | #define IDX_CPU 0 | ||
57 | #define IDX_CODEC 1 | ||
58 | struct rsrc_card_priv { | ||
59 | struct snd_soc_card snd_card; | ||
60 | struct rsrc_card_dai_props { | ||
61 | struct rsrc_card_dai cpu_dai; | ||
62 | struct rsrc_card_dai codec_dai; | ||
63 | } dai_props[RSRC_FB_NUM]; | ||
64 | struct snd_soc_codec_conf codec_conf; | ||
65 | struct snd_soc_dai_link dai_link[RSRC_FB_NUM]; | ||
66 | u32 convert_rate; | ||
67 | }; | ||
68 | |||
69 | #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev) | ||
70 | #define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + i) | ||
71 | #define rsrc_priv_to_props(priv, i) ((priv)->dai_props + i) | ||
72 | #define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data) | ||
73 | |||
74 | static int rsrc_card_startup(struct snd_pcm_substream *substream) | ||
75 | { | ||
76 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
77 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
78 | struct rsrc_card_dai_props *dai_props = | ||
79 | &priv->dai_props[rtd - rtd->card->rtd]; | ||
80 | int ret; | ||
81 | |||
82 | ret = clk_prepare_enable(dai_props->cpu_dai.clk); | ||
83 | if (ret) | ||
84 | return ret; | ||
85 | |||
86 | ret = clk_prepare_enable(dai_props->codec_dai.clk); | ||
87 | if (ret) | ||
88 | clk_disable_unprepare(dai_props->cpu_dai.clk); | ||
89 | |||
90 | return ret; | ||
91 | } | ||
92 | |||
93 | static void rsrc_card_shutdown(struct snd_pcm_substream *substream) | ||
94 | { | ||
95 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
96 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
97 | struct rsrc_card_dai_props *dai_props = | ||
98 | &priv->dai_props[rtd - rtd->card->rtd]; | ||
99 | |||
100 | clk_disable_unprepare(dai_props->cpu_dai.clk); | ||
101 | |||
102 | clk_disable_unprepare(dai_props->codec_dai.clk); | ||
103 | } | ||
104 | |||
105 | static struct snd_soc_ops rsrc_card_ops = { | ||
106 | .startup = rsrc_card_startup, | ||
107 | .shutdown = rsrc_card_shutdown, | ||
108 | }; | ||
109 | |||
110 | static int __rsrc_card_dai_init(struct snd_soc_dai *dai, | ||
111 | struct rsrc_card_dai *set) | ||
112 | { | ||
113 | int ret; | ||
114 | |||
115 | if (set->fmt) { | ||
116 | ret = snd_soc_dai_set_fmt(dai, set->fmt); | ||
117 | if (ret && ret != -ENOTSUPP) { | ||
118 | dev_err(dai->dev, "set_fmt error\n"); | ||
119 | goto err; | ||
120 | } | ||
121 | } | ||
122 | |||
123 | if (set->sysclk) { | ||
124 | ret = snd_soc_dai_set_sysclk(dai, 0, set->sysclk, 0); | ||
125 | if (ret && ret != -ENOTSUPP) { | ||
126 | dev_err(dai->dev, "set_sysclk error\n"); | ||
127 | goto err; | ||
128 | } | ||
129 | } | ||
130 | |||
131 | ret = 0; | ||
132 | |||
133 | err: | ||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | static int rsrc_card_dai_init(struct snd_soc_pcm_runtime *rtd) | ||
138 | { | ||
139 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
140 | struct snd_soc_dai *codec = rtd->codec_dai; | ||
141 | struct snd_soc_dai *cpu = rtd->cpu_dai; | ||
142 | struct rsrc_card_dai_props *dai_props; | ||
143 | int num, ret; | ||
144 | |||
145 | num = rtd - rtd->card->rtd; | ||
146 | dai_props = &priv->dai_props[num]; | ||
147 | ret = __rsrc_card_dai_init(codec, &dai_props->codec_dai); | ||
148 | if (ret < 0) | ||
149 | return ret; | ||
150 | |||
151 | ret = __rsrc_card_dai_init(cpu, &dai_props->cpu_dai); | ||
152 | if (ret < 0) | ||
153 | return ret; | ||
154 | |||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | ||
159 | struct snd_pcm_hw_params *params) | ||
160 | { | ||
161 | struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card); | ||
162 | struct snd_interval *rate = hw_param_interval(params, | ||
163 | SNDRV_PCM_HW_PARAM_RATE); | ||
164 | |||
165 | if (!priv->convert_rate) | ||
166 | return 0; | ||
167 | |||
168 | rate->min = rate->max = priv->convert_rate; | ||
169 | |||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static int | ||
174 | rsrc_card_sub_parse_of(struct rsrc_card_priv *priv, | ||
175 | struct device_node *np, | ||
176 | struct rsrc_card_dai *dai, | ||
177 | struct snd_soc_dai_link *dai_link, | ||
178 | int *args_count) | ||
179 | { | ||
180 | struct device *dev = rsrc_priv_to_dev(priv); | ||
181 | const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); | ||
182 | struct of_phandle_args args; | ||
183 | struct device_node **p_node; | ||
184 | struct clk *clk; | ||
185 | const char **dai_name; | ||
186 | const char **name; | ||
187 | u32 val; | ||
188 | int ret; | ||
189 | |||
190 | if (args_count) { | ||
191 | p_node = &dai_link->cpu_of_node; | ||
192 | dai_name = &dai_link->cpu_dai_name; | ||
193 | name = &dai_link->cpu_name; | ||
194 | } else { | ||
195 | p_node = &dai_link->codec_of_node; | ||
196 | dai_name = &dai_link->codec_dai_name; | ||
197 | name = &dai_link->codec_name; | ||
198 | } | ||
199 | |||
200 | if (!np) { | ||
201 | /* use snd-soc-dummy */ | ||
202 | *p_node = NULL; | ||
203 | *dai_name = "snd-soc-dummy-dai"; | ||
204 | *name = "snd-soc-dummy"; | ||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | /* | ||
209 | * Get node via "sound-dai = <&phandle port>" | ||
210 | * it will be used as xxx_of_node on soc_bind_dai_link() | ||
211 | */ | ||
212 | ret = of_parse_phandle_with_args(np, "sound-dai", | ||
213 | "#sound-dai-cells", 0, &args); | ||
214 | if (ret) | ||
215 | return ret; | ||
216 | |||
217 | *p_node = args.np; | ||
218 | |||
219 | /* Get dai->name */ | ||
220 | ret = snd_soc_of_get_dai_name(np, dai_name); | ||
221 | if (ret < 0) | ||
222 | return ret; | ||
223 | |||
224 | /* | ||
225 | * FIXME | ||
226 | * | ||
227 | * rsrc assumes DPCM playback/capture | ||
228 | */ | ||
229 | dai_link->dpcm_playback = 1; | ||
230 | dai_link->dpcm_capture = 1; | ||
231 | |||
232 | if (args_count) { | ||
233 | *args_count = args.args_count; | ||
234 | dai_link->dynamic = 1; | ||
235 | } else { | ||
236 | dai_link->no_pcm = 1; | ||
237 | priv->codec_conf.of_node = (*p_node); | ||
238 | priv->codec_conf.name_prefix = of_data->prefix; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Parse dai->sysclk come from "clocks = <&xxx>" | ||
243 | * (if system has common clock) | ||
244 | * or "system-clock-frequency = <xxx>" | ||
245 | * or device's module clock. | ||
246 | */ | ||
247 | if (of_property_read_bool(np, "clocks")) { | ||
248 | clk = of_clk_get(np, 0); | ||
249 | if (IS_ERR(clk)) { | ||
250 | ret = PTR_ERR(clk); | ||
251 | return ret; | ||
252 | } | ||
253 | |||
254 | dai->sysclk = clk_get_rate(clk); | ||
255 | dai->clk = clk; | ||
256 | } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) { | ||
257 | dai->sysclk = val; | ||
258 | } else { | ||
259 | clk = of_clk_get(args.np, 0); | ||
260 | if (!IS_ERR(clk)) | ||
261 | dai->sysclk = clk_get_rate(clk); | ||
262 | } | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | static int rsrc_card_parse_daifmt(struct device_node *node, | ||
268 | struct rsrc_card_priv *priv, | ||
269 | struct device_node *codec, | ||
270 | int idx) | ||
271 | { | ||
272 | struct device_node *bitclkmaster = NULL; | ||
273 | struct device_node *framemaster = NULL; | ||
274 | struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); | ||
275 | struct rsrc_card_dai *cpu_dai = &dai_props->cpu_dai; | ||
276 | struct rsrc_card_dai *codec_dai = &dai_props->codec_dai; | ||
277 | unsigned int daifmt; | ||
278 | |||
279 | daifmt = snd_soc_of_parse_daifmt(node, NULL, | ||
280 | &bitclkmaster, &framemaster); | ||
281 | daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK; | ||
282 | |||
283 | if (!bitclkmaster && !framemaster) | ||
284 | return -EINVAL; | ||
285 | |||
286 | if (codec == bitclkmaster) | ||
287 | daifmt |= (codec == framemaster) ? | ||
288 | SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS; | ||
289 | else | ||
290 | daifmt |= (codec == framemaster) ? | ||
291 | SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS; | ||
292 | |||
293 | cpu_dai->fmt = daifmt; | ||
294 | codec_dai->fmt = daifmt; | ||
295 | |||
296 | of_node_put(bitclkmaster); | ||
297 | of_node_put(framemaster); | ||
298 | |||
299 | return 0; | ||
300 | } | ||
301 | |||
302 | static int rsrc_card_dai_link_of(struct device_node *node, | ||
303 | struct rsrc_card_priv *priv, | ||
304 | int idx) | ||
305 | { | ||
306 | struct device *dev = rsrc_priv_to_dev(priv); | ||
307 | struct snd_soc_dai_link *dai_link = rsrc_priv_to_link(priv, idx); | ||
308 | struct rsrc_card_dai_props *dai_props = rsrc_priv_to_props(priv, idx); | ||
309 | struct device_node *cpu = NULL; | ||
310 | struct device_node *codec = NULL; | ||
311 | char *name; | ||
312 | char prop[128]; | ||
313 | int ret, cpu_args; | ||
314 | |||
315 | cpu = of_get_child_by_name(node, "cpu"); | ||
316 | codec = of_get_child_by_name(node, "codec"); | ||
317 | |||
318 | if (!cpu || !codec) { | ||
319 | ret = -EINVAL; | ||
320 | dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop); | ||
321 | goto dai_link_of_err; | ||
322 | } | ||
323 | |||
324 | ret = rsrc_card_parse_daifmt(node, priv, codec, idx); | ||
325 | if (ret < 0) | ||
326 | goto dai_link_of_err; | ||
327 | |||
328 | ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CPU) ? cpu : NULL, | ||
329 | &dai_props->cpu_dai, | ||
330 | dai_link, | ||
331 | &cpu_args); | ||
332 | if (ret < 0) | ||
333 | goto dai_link_of_err; | ||
334 | |||
335 | ret = rsrc_card_sub_parse_of(priv, (idx == IDX_CODEC) ? codec : NULL, | ||
336 | &dai_props->codec_dai, | ||
337 | dai_link, | ||
338 | NULL); | ||
339 | if (ret < 0) | ||
340 | goto dai_link_of_err; | ||
341 | |||
342 | if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) { | ||
343 | ret = -EINVAL; | ||
344 | goto dai_link_of_err; | ||
345 | } | ||
346 | |||
347 | /* Simple Card assumes platform == cpu */ | ||
348 | dai_link->platform_of_node = dai_link->cpu_of_node; | ||
349 | |||
350 | /* DAI link name is created from CPU/CODEC dai name */ | ||
351 | name = devm_kzalloc(dev, | ||
352 | strlen(dai_link->cpu_dai_name) + | ||
353 | strlen(dai_link->codec_dai_name) + 2, | ||
354 | GFP_KERNEL); | ||
355 | if (!name) { | ||
356 | ret = -ENOMEM; | ||
357 | goto dai_link_of_err; | ||
358 | } | ||
359 | |||
360 | sprintf(name, "%s-%s", dai_link->cpu_dai_name, | ||
361 | dai_link->codec_dai_name); | ||
362 | dai_link->name = dai_link->stream_name = name; | ||
363 | dai_link->ops = &rsrc_card_ops; | ||
364 | dai_link->init = rsrc_card_dai_init; | ||
365 | |||
366 | if (idx == IDX_CODEC) | ||
367 | dai_link->be_hw_params_fixup = rsrc_card_be_hw_params_fixup; | ||
368 | |||
369 | dev_dbg(dev, "\tname : %s\n", dai_link->stream_name); | ||
370 | dev_dbg(dev, "\tcpu : %s / %04x / %d\n", | ||
371 | dai_link->cpu_dai_name, | ||
372 | dai_props->cpu_dai.fmt, | ||
373 | dai_props->cpu_dai.sysclk); | ||
374 | dev_dbg(dev, "\tcodec : %s / %04x / %d\n", | ||
375 | dai_link->codec_dai_name, | ||
376 | dai_props->codec_dai.fmt, | ||
377 | dai_props->codec_dai.sysclk); | ||
378 | |||
379 | /* | ||
380 | * In soc_bind_dai_link() will check cpu name after | ||
381 | * of_node matching if dai_link has cpu_dai_name. | ||
382 | * but, it will never match if name was created by | ||
383 | * fmt_single_name() remove cpu_dai_name if cpu_args | ||
384 | * was 0. See: | ||
385 | * fmt_single_name() | ||
386 | * fmt_multiple_name() | ||
387 | */ | ||
388 | if (!cpu_args) | ||
389 | dai_link->cpu_dai_name = NULL; | ||
390 | |||
391 | dai_link_of_err: | ||
392 | of_node_put(cpu); | ||
393 | of_node_put(codec); | ||
394 | |||
395 | return ret; | ||
396 | } | ||
397 | |||
398 | static int rsrc_card_parse_of(struct device_node *node, | ||
399 | struct rsrc_card_priv *priv) | ||
400 | { | ||
401 | struct device *dev = rsrc_priv_to_dev(priv); | ||
402 | const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev); | ||
403 | int ret; | ||
404 | int i; | ||
405 | |||
406 | if (!node) | ||
407 | return -EINVAL; | ||
408 | |||
409 | /* Parse the card name from DT */ | ||
410 | snd_soc_of_parse_card_name(&priv->snd_card, "card-name"); | ||
411 | |||
412 | /* DAPM routes */ | ||
413 | priv->snd_card.of_dapm_routes = of_data->routes; | ||
414 | priv->snd_card.num_of_dapm_routes = of_data->num_routes; | ||
415 | |||
416 | /* sampling rate convert */ | ||
417 | of_property_read_u32(node, "convert-rate", &priv->convert_rate); | ||
418 | |||
419 | dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n", | ||
420 | priv->snd_card.name ? priv->snd_card.name : "", | ||
421 | priv->convert_rate); | ||
422 | |||
423 | /* FE/BE */ | ||
424 | for (i = 0; i < RSRC_FB_NUM; i++) { | ||
425 | ret = rsrc_card_dai_link_of(node, priv, i); | ||
426 | if (ret < 0) | ||
427 | return ret; | ||
428 | } | ||
429 | |||
430 | if (!priv->snd_card.name) | ||
431 | priv->snd_card.name = priv->snd_card.dai_link->name; | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | /* Decrease the reference count of the device nodes */ | ||
437 | static int rsrc_card_unref(struct snd_soc_card *card) | ||
438 | { | ||
439 | struct snd_soc_dai_link *dai_link; | ||
440 | int num_links; | ||
441 | |||
442 | for (num_links = 0, dai_link = card->dai_link; | ||
443 | num_links < card->num_links; | ||
444 | num_links++, dai_link++) { | ||
445 | of_node_put(dai_link->cpu_of_node); | ||
446 | of_node_put(dai_link->codec_of_node); | ||
447 | } | ||
448 | return 0; | ||
449 | } | ||
450 | |||
451 | static int rsrc_card_probe(struct platform_device *pdev) | ||
452 | { | ||
453 | struct rsrc_card_priv *priv; | ||
454 | struct snd_soc_dai_link *dai_link; | ||
455 | struct device_node *np = pdev->dev.of_node; | ||
456 | struct device *dev = &pdev->dev; | ||
457 | int ret; | ||
458 | |||
459 | /* Allocate the private data */ | ||
460 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
461 | if (!priv) | ||
462 | return -ENOMEM; | ||
463 | |||
464 | /* Init snd_soc_card */ | ||
465 | priv->snd_card.owner = THIS_MODULE; | ||
466 | priv->snd_card.dev = dev; | ||
467 | dai_link = priv->dai_link; | ||
468 | priv->snd_card.dai_link = dai_link; | ||
469 | priv->snd_card.num_links = RSRC_FB_NUM; | ||
470 | priv->snd_card.codec_conf = &priv->codec_conf; | ||
471 | priv->snd_card.num_configs = 1; | ||
472 | |||
473 | ret = rsrc_card_parse_of(np, priv); | ||
474 | if (ret < 0) { | ||
475 | if (ret != -EPROBE_DEFER) | ||
476 | dev_err(dev, "parse error %d\n", ret); | ||
477 | goto err; | ||
478 | } | ||
479 | |||
480 | snd_soc_card_set_drvdata(&priv->snd_card, priv); | ||
481 | |||
482 | ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card); | ||
483 | if (ret >= 0) | ||
484 | return ret; | ||
485 | err: | ||
486 | rsrc_card_unref(&priv->snd_card); | ||
487 | |||
488 | return ret; | ||
489 | } | ||
490 | |||
491 | static int rsrc_card_remove(struct platform_device *pdev) | ||
492 | { | ||
493 | struct snd_soc_card *card = platform_get_drvdata(pdev); | ||
494 | |||
495 | return rsrc_card_unref(card); | ||
496 | } | ||
497 | |||
498 | static struct platform_driver rsrc_card = { | ||
499 | .driver = { | ||
500 | .name = "renesas-src-audio-card", | ||
501 | .of_match_table = rsrc_card_of_match, | ||
502 | }, | ||
503 | .probe = rsrc_card_probe, | ||
504 | .remove = rsrc_card_remove, | ||
505 | }; | ||
506 | |||
507 | module_platform_driver(rsrc_card); | ||
508 | |||
509 | MODULE_ALIAS("platform:renesas-src-audio-card"); | ||
510 | MODULE_LICENSE("GPL"); | ||
511 | MODULE_DESCRIPTION("Renesas Sampling Rate Convert Sound Card"); | ||
512 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); | ||
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index c77d059edc84..3beb32eb412a 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c | |||
@@ -22,16 +22,20 @@ | |||
22 | struct rsnd_src { | 22 | struct rsnd_src { |
23 | struct rsnd_src_platform_info *info; /* rcar_snd.h */ | 23 | struct rsnd_src_platform_info *info; /* rcar_snd.h */ |
24 | struct rsnd_mod mod; | 24 | struct rsnd_mod mod; |
25 | struct rsnd_kctrl_cfg_s sen; /* sync convert enable */ | ||
26 | struct rsnd_kctrl_cfg_s sync; /* sync convert */ | ||
27 | u32 convert_rate; /* sampling rate convert */ | ||
25 | int err; | 28 | int err; |
26 | }; | 29 | }; |
27 | 30 | ||
28 | #define RSND_SRC_NAME_SIZE 16 | 31 | #define RSND_SRC_NAME_SIZE 16 |
29 | 32 | ||
30 | #define rsnd_src_convert_rate(p) ((p)->info->convert_rate) | 33 | #define rsnd_enable_sync_convert(src) ((src)->sen.val) |
34 | #define rsnd_src_of_node(priv) \ | ||
35 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src") | ||
36 | |||
31 | #define rsnd_mod_to_src(_mod) \ | 37 | #define rsnd_mod_to_src(_mod) \ |
32 | container_of((_mod), struct rsnd_src, mod) | 38 | container_of((_mod), struct rsnd_src, mod) |
33 | #define rsnd_src_dma_available(src) \ | ||
34 | rsnd_dma_available(rsnd_mod_to_dma(&(src)->mod)) | ||
35 | 39 | ||
36 | #define for_each_rsnd_src(pos, priv, i) \ | 40 | #define for_each_rsnd_src(pos, priv, i) \ |
37 | for ((i) = 0; \ | 41 | for ((i) = 0; \ |
@@ -113,6 +117,17 @@ struct rsnd_src { | |||
113 | /* | 117 | /* |
114 | * Gen1/Gen2 common functions | 118 | * Gen1/Gen2 common functions |
115 | */ | 119 | */ |
120 | static struct dma_chan *rsnd_src_dma_req(struct rsnd_mod *mod) | ||
121 | { | ||
122 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
123 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
124 | int is_play = rsnd_io_is_play(io); | ||
125 | |||
126 | return rsnd_dma_request_channel(rsnd_src_of_node(priv), | ||
127 | mod, | ||
128 | is_play ? "rx" : "tx"); | ||
129 | } | ||
130 | |||
116 | int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, | 131 | int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod, |
117 | int use_busif) | 132 | int use_busif) |
118 | { | 133 | { |
@@ -220,6 +235,30 @@ int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod) | |||
220 | return 0; | 235 | return 0; |
221 | } | 236 | } |
222 | 237 | ||
238 | static u32 rsnd_src_convert_rate(struct rsnd_src *src) | ||
239 | { | ||
240 | struct rsnd_mod *mod = &src->mod; | ||
241 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
242 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
243 | u32 convert_rate; | ||
244 | |||
245 | if (!runtime) | ||
246 | return 0; | ||
247 | |||
248 | if (!rsnd_enable_sync_convert(src)) | ||
249 | return src->convert_rate; | ||
250 | |||
251 | convert_rate = src->sync.val; | ||
252 | |||
253 | if (!convert_rate) | ||
254 | convert_rate = src->convert_rate; | ||
255 | |||
256 | if (!convert_rate) | ||
257 | convert_rate = runtime->rate; | ||
258 | |||
259 | return convert_rate; | ||
260 | } | ||
261 | |||
223 | unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, | 262 | unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv, |
224 | struct rsnd_dai_stream *io, | 263 | struct rsnd_dai_stream *io, |
225 | struct snd_pcm_runtime *runtime) | 264 | struct snd_pcm_runtime *runtime) |
@@ -276,7 +315,43 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod) | |||
276 | return 0; | 315 | return 0; |
277 | } | 316 | } |
278 | 317 | ||
279 | static int rsnd_src_init(struct rsnd_mod *mod) | 318 | static int rsnd_src_hw_params(struct rsnd_mod *mod, |
319 | struct snd_pcm_substream *substream, | ||
320 | struct snd_pcm_hw_params *fe_params) | ||
321 | { | ||
322 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
323 | struct snd_soc_pcm_runtime *fe = substream->private_data; | ||
324 | |||
325 | /* default value (mainly for non-DT) */ | ||
326 | src->convert_rate = src->info->convert_rate; | ||
327 | |||
328 | /* | ||
329 | * SRC assumes that it is used under DPCM if user want to use | ||
330 | * sampling rate convert. Then, SRC should be FE. | ||
331 | * And then, this function will be called *after* BE settings. | ||
332 | * this means, each BE already has fixuped hw_params. | ||
333 | * see | ||
334 | * dpcm_fe_dai_hw_params() | ||
335 | * dpcm_be_dai_hw_params() | ||
336 | */ | ||
337 | if (fe->dai_link->dynamic) { | ||
338 | int stream = substream->stream; | ||
339 | struct snd_soc_dpcm *dpcm; | ||
340 | struct snd_pcm_hw_params *be_params; | ||
341 | |||
342 | list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { | ||
343 | be_params = &dpcm->hw_params; | ||
344 | |||
345 | if (params_rate(fe_params) != params_rate(be_params)) | ||
346 | src->convert_rate = params_rate(be_params); | ||
347 | } | ||
348 | } | ||
349 | |||
350 | return 0; | ||
351 | } | ||
352 | |||
353 | static int rsnd_src_init(struct rsnd_mod *mod, | ||
354 | struct rsnd_priv *priv) | ||
280 | { | 355 | { |
281 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 356 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
282 | 357 | ||
@@ -284,6 +359,9 @@ static int rsnd_src_init(struct rsnd_mod *mod) | |||
284 | 359 | ||
285 | src->err = 0; | 360 | src->err = 0; |
286 | 361 | ||
362 | /* reset sync convert_rate */ | ||
363 | src->sync.val = 0; | ||
364 | |||
287 | /* | 365 | /* |
288 | * Initialize the operation of the SRC internal circuits | 366 | * Initialize the operation of the SRC internal circuits |
289 | * see rsnd_src_start() | 367 | * see rsnd_src_start() |
@@ -305,6 +383,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod, | |||
305 | dev_warn(dev, "%s[%d] under/over flow err = %d\n", | 383 | dev_warn(dev, "%s[%d] under/over flow err = %d\n", |
306 | rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); | 384 | rsnd_mod_name(mod), rsnd_mod_id(mod), src->err); |
307 | 385 | ||
386 | src->convert_rate = 0; | ||
387 | |||
388 | /* reset sync convert_rate */ | ||
389 | src->sync.val = 0; | ||
390 | |||
308 | return 0; | 391 | return 0; |
309 | } | 392 | } |
310 | 393 | ||
@@ -448,23 +531,12 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod) | |||
448 | return 0; | 531 | return 0; |
449 | } | 532 | } |
450 | 533 | ||
451 | static int rsnd_src_probe_gen1(struct rsnd_mod *mod, | ||
452 | struct rsnd_priv *priv) | ||
453 | { | ||
454 | struct device *dev = rsnd_priv_to_dev(priv); | ||
455 | |||
456 | dev_dbg(dev, "%s[%d] (Gen1) is probed\n", | ||
457 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static int rsnd_src_init_gen1(struct rsnd_mod *mod, | 534 | static int rsnd_src_init_gen1(struct rsnd_mod *mod, |
463 | struct rsnd_priv *priv) | 535 | struct rsnd_priv *priv) |
464 | { | 536 | { |
465 | int ret; | 537 | int ret; |
466 | 538 | ||
467 | ret = rsnd_src_init(mod); | 539 | ret = rsnd_src_init(mod, priv); |
468 | if (ret < 0) | 540 | if (ret < 0) |
469 | return ret; | 541 | return ret; |
470 | 542 | ||
@@ -505,11 +577,12 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod, | |||
505 | 577 | ||
506 | static struct rsnd_mod_ops rsnd_src_gen1_ops = { | 578 | static struct rsnd_mod_ops rsnd_src_gen1_ops = { |
507 | .name = SRC_NAME, | 579 | .name = SRC_NAME, |
508 | .probe = rsnd_src_probe_gen1, | 580 | .dma_req = rsnd_src_dma_req, |
509 | .init = rsnd_src_init_gen1, | 581 | .init = rsnd_src_init_gen1, |
510 | .quit = rsnd_src_quit, | 582 | .quit = rsnd_src_quit, |
511 | .start = rsnd_src_start_gen1, | 583 | .start = rsnd_src_start_gen1, |
512 | .stop = rsnd_src_stop_gen1, | 584 | .stop = rsnd_src_stop_gen1, |
585 | .hw_params = rsnd_src_hw_params, | ||
513 | }; | 586 | }; |
514 | 587 | ||
515 | /* | 588 | /* |
@@ -607,13 +680,17 @@ static irqreturn_t rsnd_src_interrupt_gen2(int irq, void *data) | |||
607 | 680 | ||
608 | if (rsnd_src_error_record_gen2(mod)) { | 681 | if (rsnd_src_error_record_gen2(mod)) { |
609 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | 682 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
683 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
610 | struct device *dev = rsnd_priv_to_dev(priv); | 684 | struct device *dev = rsnd_priv_to_dev(priv); |
611 | 685 | ||
612 | _rsnd_src_stop_gen2(mod); | ||
613 | _rsnd_src_start_gen2(mod); | ||
614 | |||
615 | dev_dbg(dev, "%s[%d] restart\n", | 686 | dev_dbg(dev, "%s[%d] restart\n", |
616 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | 687 | rsnd_mod_name(mod), rsnd_mod_id(mod)); |
688 | |||
689 | _rsnd_src_stop_gen2(mod); | ||
690 | if (src->err < 1024) | ||
691 | _rsnd_src_start_gen2(mod); | ||
692 | else | ||
693 | dev_warn(dev, "no more SRC restart\n"); | ||
617 | } | 694 | } |
618 | 695 | ||
619 | return IRQ_HANDLED; | 696 | return IRQ_HANDLED; |
@@ -627,6 +704,7 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) | |||
627 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | 704 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
628 | struct rsnd_src *src = rsnd_mod_to_src(mod); | 705 | struct rsnd_src *src = rsnd_mod_to_src(mod); |
629 | u32 convert_rate = rsnd_src_convert_rate(src); | 706 | u32 convert_rate = rsnd_src_convert_rate(src); |
707 | u32 cr, route; | ||
630 | uint ratio; | 708 | uint ratio; |
631 | int ret; | 709 | int ret; |
632 | 710 | ||
@@ -647,13 +725,21 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod) | |||
647 | if (ret < 0) | 725 | if (ret < 0) |
648 | return ret; | 726 | return ret; |
649 | 727 | ||
650 | rsnd_mod_write(mod, SRC_SRCCR, 0x00011110); | 728 | cr = 0x00011110; |
651 | 729 | route = 0x0; | |
652 | if (convert_rate) { | 730 | if (convert_rate) { |
653 | /* Gen1/Gen2 are not compatible */ | 731 | route = 0x1; |
654 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1); | 732 | |
733 | if (rsnd_enable_sync_convert(src)) { | ||
734 | cr |= 0x1; | ||
735 | route |= rsnd_io_is_play(io) ? | ||
736 | (0x1 << 24) : (0x1 << 25); | ||
737 | } | ||
655 | } | 738 | } |
656 | 739 | ||
740 | rsnd_mod_write(mod, SRC_SRCCR, cr); | ||
741 | rsnd_mod_write(mod, SRC_ROUTE_MODE0, route); | ||
742 | |||
657 | switch (rsnd_mod_id(mod)) { | 743 | switch (rsnd_mod_id(mod)) { |
658 | case 5: | 744 | case 5: |
659 | case 6: | 745 | case 6: |
@@ -708,24 +794,12 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod, | |||
708 | IRQF_SHARED, | 794 | IRQF_SHARED, |
709 | dev_name(dev), mod); | 795 | dev_name(dev), mod); |
710 | if (ret) | 796 | if (ret) |
711 | goto rsnd_src_probe_gen2_fail; | 797 | return ret; |
712 | } | 798 | } |
713 | 799 | ||
714 | ret = rsnd_dma_init(priv, | 800 | ret = rsnd_dma_init(priv, |
715 | rsnd_mod_to_dma(mod), | 801 | rsnd_mod_to_dma(mod), |
716 | rsnd_info_is_playback(priv, src), | ||
717 | src->info->dma_id); | 802 | src->info->dma_id); |
718 | if (ret) | ||
719 | goto rsnd_src_probe_gen2_fail; | ||
720 | |||
721 | dev_dbg(dev, "%s[%d] (Gen2) is probed\n", | ||
722 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
723 | |||
724 | return ret; | ||
725 | |||
726 | rsnd_src_probe_gen2_fail: | ||
727 | dev_err(dev, "%s[%d] (Gen2) failed\n", | ||
728 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
729 | 803 | ||
730 | return ret; | 804 | return ret; |
731 | } | 805 | } |
@@ -733,7 +807,7 @@ rsnd_src_probe_gen2_fail: | |||
733 | static int rsnd_src_remove_gen2(struct rsnd_mod *mod, | 807 | static int rsnd_src_remove_gen2(struct rsnd_mod *mod, |
734 | struct rsnd_priv *priv) | 808 | struct rsnd_priv *priv) |
735 | { | 809 | { |
736 | rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); | 810 | rsnd_dma_quit(rsnd_mod_to_dma(mod)); |
737 | 811 | ||
738 | return 0; | 812 | return 0; |
739 | } | 813 | } |
@@ -743,7 +817,7 @@ static int rsnd_src_init_gen2(struct rsnd_mod *mod, | |||
743 | { | 817 | { |
744 | int ret; | 818 | int ret; |
745 | 819 | ||
746 | ret = rsnd_src_init(mod); | 820 | ret = rsnd_src_init(mod, priv); |
747 | if (ret < 0) | 821 | if (ret < 0) |
748 | return ret; | 822 | return ret; |
749 | 823 | ||
@@ -778,14 +852,91 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod, | |||
778 | return ret; | 852 | return ret; |
779 | } | 853 | } |
780 | 854 | ||
855 | static void rsnd_src_reconvert_update(struct rsnd_mod *mod) | ||
856 | { | ||
857 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
858 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
859 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
860 | u32 convert_rate = rsnd_src_convert_rate(src); | ||
861 | u32 fsrate; | ||
862 | |||
863 | if (!runtime) | ||
864 | return; | ||
865 | |||
866 | if (!convert_rate) | ||
867 | convert_rate = runtime->rate; | ||
868 | |||
869 | fsrate = 0x0400000 / convert_rate * runtime->rate; | ||
870 | |||
871 | /* update IFS */ | ||
872 | rsnd_mod_write(mod, SRC_IFSVR, fsrate); | ||
873 | } | ||
874 | |||
875 | static int rsnd_src_pcm_new(struct rsnd_mod *mod, | ||
876 | struct snd_soc_pcm_runtime *rtd) | ||
877 | { | ||
878 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
879 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
880 | struct rsnd_dai *rdai = rsnd_io_to_rdai(io); | ||
881 | struct rsnd_src *src = rsnd_mod_to_src(mod); | ||
882 | int ret; | ||
883 | |||
884 | /* | ||
885 | * enable SRC sync convert if possible | ||
886 | */ | ||
887 | |||
888 | /* | ||
889 | * Gen1 is not supported | ||
890 | */ | ||
891 | if (rsnd_is_gen1(priv)) | ||
892 | return 0; | ||
893 | |||
894 | /* | ||
895 | * SRC sync convert needs clock master | ||
896 | */ | ||
897 | if (!rsnd_rdai_is_clk_master(rdai)) | ||
898 | return 0; | ||
899 | |||
900 | /* | ||
901 | * We can't use SRC sync convert | ||
902 | * if it has DVC | ||
903 | */ | ||
904 | if (rsnd_io_to_mod_dvc(io)) | ||
905 | return 0; | ||
906 | |||
907 | /* | ||
908 | * enable sync convert | ||
909 | */ | ||
910 | ret = rsnd_kctrl_new_s(mod, rtd, | ||
911 | rsnd_io_is_play(io) ? | ||
912 | "SRC Out Rate Switch" : | ||
913 | "SRC In Rate Switch", | ||
914 | rsnd_src_reconvert_update, | ||
915 | &src->sen, 1); | ||
916 | if (ret < 0) | ||
917 | return ret; | ||
918 | |||
919 | ret = rsnd_kctrl_new_s(mod, rtd, | ||
920 | rsnd_io_is_play(io) ? | ||
921 | "SRC Out Rate" : | ||
922 | "SRC In Rate", | ||
923 | rsnd_src_reconvert_update, | ||
924 | &src->sync, 192000); | ||
925 | |||
926 | return ret; | ||
927 | } | ||
928 | |||
781 | static struct rsnd_mod_ops rsnd_src_gen2_ops = { | 929 | static struct rsnd_mod_ops rsnd_src_gen2_ops = { |
782 | .name = SRC_NAME, | 930 | .name = SRC_NAME, |
931 | .dma_req = rsnd_src_dma_req, | ||
783 | .probe = rsnd_src_probe_gen2, | 932 | .probe = rsnd_src_probe_gen2, |
784 | .remove = rsnd_src_remove_gen2, | 933 | .remove = rsnd_src_remove_gen2, |
785 | .init = rsnd_src_init_gen2, | 934 | .init = rsnd_src_init_gen2, |
786 | .quit = rsnd_src_quit, | 935 | .quit = rsnd_src_quit, |
787 | .start = rsnd_src_start_gen2, | 936 | .start = rsnd_src_start_gen2, |
788 | .stop = rsnd_src_stop_gen2, | 937 | .stop = rsnd_src_stop_gen2, |
938 | .hw_params = rsnd_src_hw_params, | ||
939 | .pcm_new = rsnd_src_pcm_new, | ||
789 | }; | 940 | }; |
790 | 941 | ||
791 | struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) | 942 | struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id) |
@@ -810,7 +961,7 @@ static void rsnd_of_parse_src(struct platform_device *pdev, | |||
810 | if (!of_data) | 961 | if (!of_data) |
811 | return; | 962 | return; |
812 | 963 | ||
813 | src_node = of_get_child_by_name(dev->of_node, "rcar_sound,src"); | 964 | src_node = rsnd_src_of_node(priv); |
814 | if (!src_node) | 965 | if (!src_node) |
815 | return; | 966 | return; |
816 | 967 | ||
@@ -893,8 +1044,6 @@ int rsnd_src_probe(struct platform_device *pdev, | |||
893 | ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); | 1044 | ret = rsnd_mod_init(&src->mod, ops, clk, RSND_MOD_SRC, i); |
894 | if (ret) | 1045 | if (ret) |
895 | return ret; | 1046 | return ret; |
896 | |||
897 | dev_dbg(dev, "SRC%d probed\n", i); | ||
898 | } | 1047 | } |
899 | 1048 | ||
900 | return 0; | 1049 | return 0; |
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c index f7cb1fd635a0..7bb9c087f3dc 100644 --- a/sound/soc/sh/rcar/ssi.c +++ b/sound/soc/sh/rcar/ssi.c | |||
@@ -80,13 +80,13 @@ struct rsnd_ssi { | |||
80 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) | 80 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) |
81 | #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) | 81 | #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) |
82 | #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) | 82 | #define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0) |
83 | #define rsnd_ssi_dma_available(ssi) \ | ||
84 | rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) | ||
85 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) | 83 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) |
86 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) | 84 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) |
87 | #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) | 85 | #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) |
86 | #define rsnd_ssi_of_node(priv) \ | ||
87 | of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi") | ||
88 | 88 | ||
89 | static int rsnd_ssi_use_busif(struct rsnd_mod *mod) | 89 | int rsnd_ssi_use_busif(struct rsnd_mod *mod) |
90 | { | 90 | { |
91 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | 91 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); |
92 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | 92 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); |
@@ -416,11 +416,14 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data) | |||
416 | /* | 416 | /* |
417 | * restart SSI | 417 | * restart SSI |
418 | */ | 418 | */ |
419 | rsnd_ssi_stop(mod, priv); | ||
420 | rsnd_ssi_start(mod, priv); | ||
421 | |||
422 | dev_dbg(dev, "%s[%d] restart\n", | 419 | dev_dbg(dev, "%s[%d] restart\n", |
423 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | 420 | rsnd_mod_name(mod), rsnd_mod_id(mod)); |
421 | |||
422 | rsnd_ssi_stop(mod, priv); | ||
423 | if (ssi->err < 1024) | ||
424 | rsnd_ssi_start(mod, priv); | ||
425 | else | ||
426 | dev_warn(dev, "no more SSI restart\n"); | ||
424 | } | 427 | } |
425 | 428 | ||
426 | rsnd_ssi_record_error(ssi, status); | 429 | rsnd_ssi_record_error(ssi, status); |
@@ -442,12 +445,6 @@ static int rsnd_ssi_pio_probe(struct rsnd_mod *mod, | |||
442 | rsnd_ssi_interrupt, | 445 | rsnd_ssi_interrupt, |
443 | IRQF_SHARED, | 446 | IRQF_SHARED, |
444 | dev_name(dev), ssi); | 447 | dev_name(dev), ssi); |
445 | if (ret) | ||
446 | dev_err(dev, "%s[%d] (PIO) request interrupt failed\n", | ||
447 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
448 | else | ||
449 | dev_dbg(dev, "%s[%d] (PIO) is probed\n", | ||
450 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
451 | 448 | ||
452 | return ret; | 449 | return ret; |
453 | } | 450 | } |
@@ -474,23 +471,11 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod, | |||
474 | IRQF_SHARED, | 471 | IRQF_SHARED, |
475 | dev_name(dev), ssi); | 472 | dev_name(dev), ssi); |
476 | if (ret) | 473 | if (ret) |
477 | goto rsnd_ssi_dma_probe_fail; | 474 | return ret; |
478 | 475 | ||
479 | ret = rsnd_dma_init( | 476 | ret = rsnd_dma_init( |
480 | priv, rsnd_mod_to_dma(mod), | 477 | priv, rsnd_mod_to_dma(mod), |
481 | rsnd_info_is_playback(priv, ssi), | ||
482 | dma_id); | 478 | dma_id); |
483 | if (ret) | ||
484 | goto rsnd_ssi_dma_probe_fail; | ||
485 | |||
486 | dev_dbg(dev, "%s[%d] (DMA) is probed\n", | ||
487 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
488 | |||
489 | return ret; | ||
490 | |||
491 | rsnd_ssi_dma_probe_fail: | ||
492 | dev_err(dev, "%s[%d] (DMA) is failed\n", | ||
493 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
494 | 479 | ||
495 | return ret; | 480 | return ret; |
496 | } | 481 | } |
@@ -502,7 +487,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod, | |||
502 | struct device *dev = rsnd_priv_to_dev(priv); | 487 | struct device *dev = rsnd_priv_to_dev(priv); |
503 | int irq = ssi->info->irq; | 488 | int irq = ssi->info->irq; |
504 | 489 | ||
505 | rsnd_dma_quit(priv, rsnd_mod_to_dma(mod)); | 490 | rsnd_dma_quit(rsnd_mod_to_dma(mod)); |
506 | 491 | ||
507 | /* PIO will request IRQ again */ | 492 | /* PIO will request IRQ again */ |
508 | devm_free_irq(dev, irq, ssi); | 493 | devm_free_irq(dev, irq, ssi); |
@@ -554,14 +539,25 @@ static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, | |||
554 | return 0; | 539 | return 0; |
555 | } | 540 | } |
556 | 541 | ||
557 | static char *rsnd_ssi_dma_name(struct rsnd_mod *mod) | 542 | static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_mod *mod) |
558 | { | 543 | { |
559 | return rsnd_ssi_use_busif(mod) ? "ssiu" : SSI_NAME; | 544 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
545 | struct rsnd_dai_stream *io = rsnd_mod_to_io(mod); | ||
546 | int is_play = rsnd_io_is_play(io); | ||
547 | char *name; | ||
548 | |||
549 | if (rsnd_ssi_use_busif(mod)) | ||
550 | name = is_play ? "rxu" : "txu"; | ||
551 | else | ||
552 | name = is_play ? "rx" : "tx"; | ||
553 | |||
554 | return rsnd_dma_request_channel(rsnd_ssi_of_node(priv), | ||
555 | mod, name); | ||
560 | } | 556 | } |
561 | 557 | ||
562 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { | 558 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { |
563 | .name = SSI_NAME, | 559 | .name = SSI_NAME, |
564 | .dma_name = rsnd_ssi_dma_name, | 560 | .dma_req = rsnd_ssi_dma_req, |
565 | .probe = rsnd_ssi_dma_probe, | 561 | .probe = rsnd_ssi_dma_probe, |
566 | .remove = rsnd_ssi_dma_remove, | 562 | .remove = rsnd_ssi_dma_remove, |
567 | .init = rsnd_ssi_init, | 563 | .init = rsnd_ssi_init, |
@@ -636,7 +632,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev, | |||
636 | if (!of_data) | 632 | if (!of_data) |
637 | return; | 633 | return; |
638 | 634 | ||
639 | node = of_get_child_by_name(dev->of_node, "rcar_sound,ssi"); | 635 | node = rsnd_ssi_of_node(priv); |
640 | if (!node) | 636 | if (!node) |
641 | return; | 637 | return; |
642 | 638 | ||