1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2022, Linaro Limited |
3 | |
4 | #include <dt-bindings/sound/qcom,q6afe.h> |
5 | #include <linux/module.h> |
6 | #include <linux/platform_device.h> |
7 | #include <sound/soc.h> |
8 | #include <sound/soc-dapm.h> |
9 | #include <sound/pcm.h> |
10 | #include <linux/soundwire/sdw.h> |
11 | #include <sound/jack.h> |
12 | #include <linux/input-event-codes.h> |
13 | #include "qdsp6/q6afe.h" |
14 | #include "common.h" |
15 | #include "sdw.h" |
16 | |
17 | struct sc8280xp_snd_data { |
18 | bool stream_prepared[AFE_PORT_MAX]; |
19 | struct snd_soc_card *card; |
20 | struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; |
21 | struct snd_soc_jack jack; |
22 | bool jack_setup; |
23 | }; |
24 | |
25 | static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) |
26 | { |
27 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
28 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
29 | struct snd_soc_card *card = rtd->card; |
30 | |
31 | switch (cpu_dai->id) { |
32 | case WSA_CODEC_DMA_RX_0: |
33 | case WSA_CODEC_DMA_RX_1: |
34 | /* |
35 | * Set limit of -3 dB on Digital Volume and 0 dB on PA Volume |
36 | * to reduce the risk of speaker damage until we have active |
37 | * speaker protection in place. |
38 | */ |
39 | snd_soc_limit_volume(card, name: "WSA_RX0 Digital Volume" , max: 81); |
40 | snd_soc_limit_volume(card, name: "WSA_RX1 Digital Volume" , max: 81); |
41 | snd_soc_limit_volume(card, name: "SpkrLeft PA Volume" , max: 17); |
42 | snd_soc_limit_volume(card, name: "SpkrRight PA Volume" , max: 17); |
43 | break; |
44 | default: |
45 | break; |
46 | } |
47 | |
48 | return qcom_snd_wcd_jack_setup(rtd, jack: &data->jack, jack_setup: &data->jack_setup); |
49 | } |
50 | |
51 | static void sc8280xp_snd_shutdown(struct snd_pcm_substream *substream) |
52 | { |
53 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
54 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
55 | struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(card: rtd->card); |
56 | struct sdw_stream_runtime *sruntime = pdata->sruntime[cpu_dai->id]; |
57 | |
58 | pdata->sruntime[cpu_dai->id] = NULL; |
59 | sdw_release_stream(stream: sruntime); |
60 | } |
61 | |
62 | static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, |
63 | struct snd_pcm_hw_params *params) |
64 | { |
65 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
66 | struct snd_interval *rate = hw_param_interval(params, |
67 | SNDRV_PCM_HW_PARAM_RATE); |
68 | struct snd_interval *channels = hw_param_interval(params, |
69 | SNDRV_PCM_HW_PARAM_CHANNELS); |
70 | |
71 | rate->min = rate->max = 48000; |
72 | channels->min = 2; |
73 | channels->max = 2; |
74 | switch (cpu_dai->id) { |
75 | case TX_CODEC_DMA_TX_0: |
76 | case TX_CODEC_DMA_TX_1: |
77 | case TX_CODEC_DMA_TX_2: |
78 | case TX_CODEC_DMA_TX_3: |
79 | channels->min = 1; |
80 | break; |
81 | default: |
82 | break; |
83 | } |
84 | |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, |
90 | struct snd_pcm_hw_params *params) |
91 | { |
92 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
93 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
94 | struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(card: rtd->card); |
95 | |
96 | return qcom_snd_sdw_hw_params(substream, params, psruntime: &pdata->sruntime[cpu_dai->id]); |
97 | } |
98 | |
99 | static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) |
100 | { |
101 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
102 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
103 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
104 | struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; |
105 | |
106 | return qcom_snd_sdw_prepare(substream, runtime: sruntime, |
107 | stream_prepared: &data->stream_prepared[cpu_dai->id]); |
108 | } |
109 | |
110 | static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) |
111 | { |
112 | struct snd_soc_pcm_runtime *rtd = substream->private_data; |
113 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(card: rtd->card); |
114 | struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0); |
115 | struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; |
116 | |
117 | return qcom_snd_sdw_hw_free(substream, sruntime, |
118 | stream_prepared: &data->stream_prepared[cpu_dai->id]); |
119 | } |
120 | |
121 | static const struct snd_soc_ops sc8280xp_be_ops = { |
122 | .startup = qcom_snd_sdw_startup, |
123 | .shutdown = sc8280xp_snd_shutdown, |
124 | .hw_params = sc8280xp_snd_hw_params, |
125 | .hw_free = sc8280xp_snd_hw_free, |
126 | .prepare = sc8280xp_snd_prepare, |
127 | }; |
128 | |
129 | static void sc8280xp_add_be_ops(struct snd_soc_card *card) |
130 | { |
131 | struct snd_soc_dai_link *link; |
132 | int i; |
133 | |
134 | for_each_card_prelinks(card, i, link) { |
135 | if (link->no_pcm == 1) { |
136 | link->init = sc8280xp_snd_init; |
137 | link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup; |
138 | link->ops = &sc8280xp_be_ops; |
139 | } |
140 | } |
141 | } |
142 | |
143 | static int sc8280xp_platform_probe(struct platform_device *pdev) |
144 | { |
145 | struct snd_soc_card *card; |
146 | struct sc8280xp_snd_data *data; |
147 | struct device *dev = &pdev->dev; |
148 | int ret; |
149 | |
150 | card = devm_kzalloc(dev, size: sizeof(*card), GFP_KERNEL); |
151 | if (!card) |
152 | return -ENOMEM; |
153 | card->owner = THIS_MODULE; |
154 | /* Allocate the private data */ |
155 | data = devm_kzalloc(dev, size: sizeof(*data), GFP_KERNEL); |
156 | if (!data) |
157 | return -ENOMEM; |
158 | |
159 | card->dev = dev; |
160 | dev_set_drvdata(dev, data: card); |
161 | snd_soc_card_set_drvdata(card, data); |
162 | ret = qcom_snd_parse_of(card); |
163 | if (ret) |
164 | return ret; |
165 | |
166 | card->driver_name = of_device_get_match_data(dev); |
167 | sc8280xp_add_be_ops(card); |
168 | return devm_snd_soc_register_card(dev, card); |
169 | } |
170 | |
171 | static const struct of_device_id snd_sc8280xp_dt_match[] = { |
172 | {.compatible = "qcom,sc8280xp-sndcard" , "sc8280xp" }, |
173 | {.compatible = "qcom,sm8450-sndcard" , "sm8450" }, |
174 | {.compatible = "qcom,sm8550-sndcard" , "sm8550" }, |
175 | {.compatible = "qcom,sm8650-sndcard" , "sm8650" }, |
176 | {} |
177 | }; |
178 | |
179 | MODULE_DEVICE_TABLE(of, snd_sc8280xp_dt_match); |
180 | |
181 | static struct platform_driver snd_sc8280xp_driver = { |
182 | .probe = sc8280xp_platform_probe, |
183 | .driver = { |
184 | .name = "snd-sc8280xp" , |
185 | .of_match_table = snd_sc8280xp_dt_match, |
186 | }, |
187 | }; |
188 | module_platform_driver(snd_sc8280xp_driver); |
189 | MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org" ); |
190 | MODULE_DESCRIPTION("SC8280XP ASoC Machine Driver" ); |
191 | MODULE_LICENSE("GPL" ); |
192 | |