diff options
author | Rohit kumar <rohitkr@codeaurora.org> | 2018-08-01 05:01:09 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2018-08-01 07:00:25 -0400 |
commit | 6b1687bf76ef84cb1e31386c4871a01fe66937bf (patch) | |
tree | 3661622d7803b05b38826e9c79e4ec90e1a0db33 /sound | |
parent | c25e295cd77b37903ddc9ee27384e17aad08f27c (diff) |
ASoC: qcom: add sdm845 sound card support
This patch adds sdm845 audio machine driver support.
Acked-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Signed-off-by: Rohit kumar <rohitkr@codeaurora.org>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/soc/qcom/Kconfig | 8 | ||||
-rw-r--r-- | sound/soc/qcom/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/qcom/sdm845.c | 286 |
3 files changed, 296 insertions, 0 deletions
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig index 87838fa27997..350730839c6f 100644 --- a/sound/soc/qcom/Kconfig +++ b/sound/soc/qcom/Kconfig | |||
@@ -90,3 +90,11 @@ config SND_SOC_MSM8996 | |||
90 | Support for Qualcomm Technologies LPASS audio block in | 90 | Support for Qualcomm Technologies LPASS audio block in |
91 | APQ8096 SoC-based systems. | 91 | APQ8096 SoC-based systems. |
92 | Say Y if you want to use audio device on this SoCs | 92 | Say Y if you want to use audio device on this SoCs |
93 | |||
94 | config SND_SOC_SDM845 | ||
95 | tristate "SoC Machine driver for SDM845 boards" | ||
96 | select SND_SOC_QDSP6 | ||
97 | help | ||
98 | To add support for audio on Qualcomm Technologies Inc. | ||
99 | SDM845 SoC-based systems. | ||
100 | Say Y if you want to use audio device on this SoCs. | ||
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile index fefecc072265..f0e94d48ba98 100644 --- a/sound/soc/qcom/Makefile +++ b/sound/soc/qcom/Makefile | |||
@@ -14,10 +14,12 @@ obj-$(CONFIG_SND_SOC_LPASS_APQ8016) += snd-soc-lpass-apq8016.o | |||
14 | snd-soc-storm-objs := storm.o | 14 | snd-soc-storm-objs := storm.o |
15 | snd-soc-apq8016-sbc-objs := apq8016_sbc.o | 15 | snd-soc-apq8016-sbc-objs := apq8016_sbc.o |
16 | snd-soc-apq8096-objs := apq8096.o common.o | 16 | snd-soc-apq8096-objs := apq8096.o common.o |
17 | snd-soc-sdm845-objs := sdm845.o common.o | ||
17 | 18 | ||
18 | obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o | 19 | obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o |
19 | obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o | 20 | obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o |
20 | obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o | 21 | obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-apq8096.o |
22 | obj-$(CONFIG_SND_SOC_SDM845) += snd-soc-sdm845.o | ||
21 | 23 | ||
22 | #DSP lib | 24 | #DSP lib |
23 | obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ | 25 | obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/ |
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c new file mode 100644 index 000000000000..bf4ec4646906 --- /dev/null +++ b/sound/soc/qcom/sdm845.c | |||
@@ -0,0 +1,286 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Copyright (c) 2018, The Linux Foundation. All rights reserved. | ||
4 | */ | ||
5 | |||
6 | #include <linux/module.h> | ||
7 | #include <linux/platform_device.h> | ||
8 | #include <linux/atomic.h> | ||
9 | #include <linux/of_device.h> | ||
10 | #include <sound/pcm.h> | ||
11 | #include <sound/pcm_params.h> | ||
12 | #include <linux/soc/qcom/apr.h> | ||
13 | #include "common.h" | ||
14 | #include "qdsp6/q6afe.h" | ||
15 | |||
16 | #define DEFAULT_SAMPLE_RATE_48K 48000 | ||
17 | #define DEFAULT_MCLK_RATE 24576000 | ||
18 | #define DEFAULT_BCLK_RATE 12288000 | ||
19 | |||
20 | struct sdm845_snd_data { | ||
21 | struct snd_soc_card *card; | ||
22 | uint32_t pri_mi2s_clk_count; | ||
23 | uint32_t quat_tdm_clk_count; | ||
24 | }; | ||
25 | |||
26 | static unsigned int tdm_slot_offset[8] = {0, 4, 8, 12, 16, 20, 24, 28}; | ||
27 | |||
28 | static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream, | ||
29 | struct snd_pcm_hw_params *params) | ||
30 | { | ||
31 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
32 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
33 | int ret = 0; | ||
34 | int channels, slot_width; | ||
35 | |||
36 | switch (params_format(params)) { | ||
37 | case SNDRV_PCM_FORMAT_S16_LE: | ||
38 | slot_width = 32; | ||
39 | break; | ||
40 | default: | ||
41 | dev_err(rtd->dev, "%s: invalid param format 0x%x\n", | ||
42 | __func__, params_format(params)); | ||
43 | return -EINVAL; | ||
44 | } | ||
45 | |||
46 | channels = params_channels(params); | ||
47 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
48 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x3, | ||
49 | 8, slot_width); | ||
50 | if (ret < 0) { | ||
51 | dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", | ||
52 | __func__, ret); | ||
53 | goto end; | ||
54 | } | ||
55 | |||
56 | ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL, | ||
57 | channels, tdm_slot_offset); | ||
58 | if (ret < 0) { | ||
59 | dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", | ||
60 | __func__, ret); | ||
61 | goto end; | ||
62 | } | ||
63 | } else { | ||
64 | ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0, | ||
65 | 8, slot_width); | ||
66 | if (ret < 0) { | ||
67 | dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n", | ||
68 | __func__, ret); | ||
69 | goto end; | ||
70 | } | ||
71 | |||
72 | ret = snd_soc_dai_set_channel_map(cpu_dai, channels, | ||
73 | tdm_slot_offset, 0, NULL); | ||
74 | if (ret < 0) { | ||
75 | dev_err(rtd->dev, "%s: failed to set channel map, err:%d\n", | ||
76 | __func__, ret); | ||
77 | goto end; | ||
78 | } | ||
79 | } | ||
80 | end: | ||
81 | return ret; | ||
82 | } | ||
83 | |||
84 | static int sdm845_snd_hw_params(struct snd_pcm_substream *substream, | ||
85 | struct snd_pcm_hw_params *params) | ||
86 | { | ||
87 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
88 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
89 | int ret = 0; | ||
90 | |||
91 | switch (cpu_dai->id) { | ||
92 | case QUATERNARY_TDM_RX_0: | ||
93 | case QUATERNARY_TDM_TX_0: | ||
94 | ret = sdm845_tdm_snd_hw_params(substream, params); | ||
95 | break; | ||
96 | default: | ||
97 | pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); | ||
98 | break; | ||
99 | } | ||
100 | return ret; | ||
101 | } | ||
102 | |||
103 | static int sdm845_snd_startup(struct snd_pcm_substream *substream) | ||
104 | { | ||
105 | unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS; | ||
106 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
107 | struct snd_soc_card *card = rtd->card; | ||
108 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); | ||
109 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
110 | |||
111 | switch (cpu_dai->id) { | ||
112 | case PRIMARY_MI2S_RX: | ||
113 | case PRIMARY_MI2S_TX: | ||
114 | if (++(data->pri_mi2s_clk_count) == 1) { | ||
115 | snd_soc_dai_set_sysclk(cpu_dai, | ||
116 | Q6AFE_LPASS_CLK_ID_MCLK_1, | ||
117 | DEFAULT_MCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); | ||
118 | snd_soc_dai_set_sysclk(cpu_dai, | ||
119 | Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, | ||
120 | DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); | ||
121 | } | ||
122 | snd_soc_dai_set_fmt(cpu_dai, fmt); | ||
123 | break; | ||
124 | |||
125 | case QUATERNARY_TDM_RX_0: | ||
126 | case QUATERNARY_TDM_TX_0: | ||
127 | if (++(data->quat_tdm_clk_count) == 1) { | ||
128 | snd_soc_dai_set_sysclk(cpu_dai, | ||
129 | Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, | ||
130 | DEFAULT_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK); | ||
131 | } | ||
132 | break; | ||
133 | |||
134 | default: | ||
135 | pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); | ||
136 | break; | ||
137 | } | ||
138 | return 0; | ||
139 | } | ||
140 | |||
141 | static void sdm845_snd_shutdown(struct snd_pcm_substream *substream) | ||
142 | { | ||
143 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
144 | struct snd_soc_card *card = rtd->card; | ||
145 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); | ||
146 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
147 | |||
148 | switch (cpu_dai->id) { | ||
149 | case PRIMARY_MI2S_RX: | ||
150 | case PRIMARY_MI2S_TX: | ||
151 | if (--(data->pri_mi2s_clk_count) == 0) { | ||
152 | snd_soc_dai_set_sysclk(cpu_dai, | ||
153 | Q6AFE_LPASS_CLK_ID_MCLK_1, | ||
154 | 0, SNDRV_PCM_STREAM_PLAYBACK); | ||
155 | snd_soc_dai_set_sysclk(cpu_dai, | ||
156 | Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT, | ||
157 | 0, SNDRV_PCM_STREAM_PLAYBACK); | ||
158 | }; | ||
159 | break; | ||
160 | |||
161 | case QUATERNARY_TDM_RX_0: | ||
162 | case QUATERNARY_TDM_TX_0: | ||
163 | if (--(data->quat_tdm_clk_count) == 0) { | ||
164 | snd_soc_dai_set_sysclk(cpu_dai, | ||
165 | Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT, | ||
166 | 0, SNDRV_PCM_STREAM_PLAYBACK); | ||
167 | } | ||
168 | break; | ||
169 | |||
170 | default: | ||
171 | pr_err("%s: invalid dai id 0x%x\n", __func__, cpu_dai->id); | ||
172 | break; | ||
173 | } | ||
174 | } | ||
175 | |||
176 | static struct snd_soc_ops sdm845_be_ops = { | ||
177 | .hw_params = sdm845_snd_hw_params, | ||
178 | .startup = sdm845_snd_startup, | ||
179 | .shutdown = sdm845_snd_shutdown, | ||
180 | }; | ||
181 | |||
182 | static int sdm845_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | ||
183 | struct snd_pcm_hw_params *params) | ||
184 | { | ||
185 | struct snd_interval *rate = hw_param_interval(params, | ||
186 | SNDRV_PCM_HW_PARAM_RATE); | ||
187 | struct snd_interval *channels = hw_param_interval(params, | ||
188 | SNDRV_PCM_HW_PARAM_CHANNELS); | ||
189 | struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); | ||
190 | |||
191 | rate->min = rate->max = DEFAULT_SAMPLE_RATE_48K; | ||
192 | channels->min = channels->max = 2; | ||
193 | snd_mask_set_format(fmt, SNDRV_PCM_FORMAT_S16_LE); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | static void sdm845_add_be_ops(struct snd_soc_card *card) | ||
199 | { | ||
200 | struct snd_soc_dai_link *link = card->dai_link; | ||
201 | int i, num_links = card->num_links; | ||
202 | |||
203 | for (i = 0; i < num_links; i++) { | ||
204 | if (link->no_pcm == 1) { | ||
205 | link->ops = &sdm845_be_ops; | ||
206 | link->be_hw_params_fixup = sdm845_be_hw_params_fixup; | ||
207 | } | ||
208 | link++; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | static int sdm845_snd_platform_probe(struct platform_device *pdev) | ||
213 | { | ||
214 | struct snd_soc_card *card; | ||
215 | struct sdm845_snd_data *data; | ||
216 | struct device *dev = &pdev->dev; | ||
217 | int ret; | ||
218 | |||
219 | card = kzalloc(sizeof(*card), GFP_KERNEL); | ||
220 | if (!card) | ||
221 | return -ENOMEM; | ||
222 | |||
223 | /* Allocate the private data */ | ||
224 | data = kzalloc(sizeof(*data), GFP_KERNEL); | ||
225 | if (!data) | ||
226 | return -ENOMEM; | ||
227 | |||
228 | card->dev = dev; | ||
229 | card->auto_bind = true; | ||
230 | dev_set_drvdata(dev, card); | ||
231 | ret = qcom_snd_parse_of(card); | ||
232 | if (ret) { | ||
233 | dev_err(dev, "Error parsing OF data\n"); | ||
234 | goto parse_dt_fail; | ||
235 | } | ||
236 | |||
237 | data->card = card; | ||
238 | snd_soc_card_set_drvdata(card, data); | ||
239 | |||
240 | sdm845_add_be_ops(card); | ||
241 | ret = snd_soc_register_card(card); | ||
242 | if (ret) { | ||
243 | dev_err(dev, "Sound card registration failed\n"); | ||
244 | goto register_card_fail; | ||
245 | } | ||
246 | return ret; | ||
247 | |||
248 | register_card_fail: | ||
249 | kfree(card->dai_link); | ||
250 | parse_dt_fail: | ||
251 | kfree(data); | ||
252 | kfree(card); | ||
253 | return ret; | ||
254 | } | ||
255 | |||
256 | static int sdm845_snd_platform_remove(struct platform_device *pdev) | ||
257 | { | ||
258 | struct snd_soc_card *card = dev_get_drvdata(&pdev->dev); | ||
259 | struct sdm845_snd_data *data = snd_soc_card_get_drvdata(card); | ||
260 | |||
261 | card->auto_bind = false; | ||
262 | snd_soc_unregister_card(card); | ||
263 | kfree(card->dai_link); | ||
264 | kfree(data); | ||
265 | kfree(card); | ||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static const struct of_device_id sdm845_snd_device_id[] = { | ||
270 | { .compatible = "qcom,sdm845-sndcard" }, | ||
271 | {}, | ||
272 | }; | ||
273 | MODULE_DEVICE_TABLE(of, sdm845_snd_device_id); | ||
274 | |||
275 | static struct platform_driver sdm845_snd_driver = { | ||
276 | .probe = sdm845_snd_platform_probe, | ||
277 | .remove = sdm845_snd_platform_remove, | ||
278 | .driver = { | ||
279 | .name = "msm-snd-sdm845", | ||
280 | .of_match_table = sdm845_snd_device_id, | ||
281 | }, | ||
282 | }; | ||
283 | module_platform_driver(sdm845_snd_driver); | ||
284 | |||
285 | MODULE_DESCRIPTION("sdm845 ASoC Machine Driver"); | ||
286 | MODULE_LICENSE("GPL v2"); | ||