xref: /linux/sound/soc/qcom/x1e80100.c (revision d30c1683aaecb93d2ab95685dc4300a33d3cea7a)
1 // SPDX-License-Identifier: GPL-2.0
2 // Copyright (c) 2023, Linaro Limited
3 
4 #include <dt-bindings/sound/qcom,q6afe.h>
5 #include <linux/module.h>
6 #include <linux/platform_device.h>
7 #include <linux/soundwire/sdw.h>
8 #include <sound/pcm.h>
9 #include <sound/jack.h>
10 #include <sound/soc.h>
11 #include <sound/soc-dapm.h>
12 
13 #include "common.h"
14 #include "qdsp6/q6afe.h"
15 #include "qdsp6/q6dsp-common.h"
16 #include "sdw.h"
17 
18 struct x1e80100_snd_data {
19 	bool stream_prepared[AFE_PORT_MAX];
20 	struct snd_soc_card *card;
21 	struct snd_soc_jack jack;
22 	struct snd_soc_jack dp_jack[8];
23 	bool jack_setup;
24 };
25 
26 static int x1e80100_snd_init(struct snd_soc_pcm_runtime *rtd)
27 {
28 	struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
29 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
30 	struct snd_soc_jack *dp_jack = NULL;
31 	int dp_pcm_id = 0;
32 
33 	switch (cpu_dai->id) {
34 	case DISPLAY_PORT_RX_0:
35 		dp_pcm_id = 0;
36 		dp_jack = &data->dp_jack[dp_pcm_id];
37 		break;
38 	case DISPLAY_PORT_RX_1 ... DISPLAY_PORT_RX_7:
39 		dp_pcm_id = cpu_dai->id - DISPLAY_PORT_RX_1 + 1;
40 		dp_jack = &data->dp_jack[dp_pcm_id];
41 		break;
42 	default:
43 		break;
44 	}
45 
46 	if (dp_jack)
47 		return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id);
48 
49 	return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
50 }
51 
52 static int x1e80100_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
53 				     struct snd_pcm_hw_params *params)
54 {
55 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
56 	struct snd_interval *rate = hw_param_interval(params,
57 						      SNDRV_PCM_HW_PARAM_RATE);
58 	struct snd_interval *channels = hw_param_interval(params,
59 							  SNDRV_PCM_HW_PARAM_CHANNELS);
60 
61 	rate->min = rate->max = 48000;
62 	switch (cpu_dai->id) {
63 	case TX_CODEC_DMA_TX_0:
64 	case TX_CODEC_DMA_TX_1:
65 	case TX_CODEC_DMA_TX_2:
66 	case TX_CODEC_DMA_TX_3:
67 		channels->min = 1;
68 		break;
69 	default:
70 		break;
71 	}
72 
73 	return 0;
74 }
75 
76 static int x1e80100_snd_hw_map_channels(unsigned int *ch_map, int num)
77 {
78 	switch (num) {
79 	case 1:
80 		ch_map[0] = PCM_CHANNEL_FC;
81 		break;
82 	case 2:
83 		ch_map[0] = PCM_CHANNEL_FL;
84 		ch_map[1] = PCM_CHANNEL_FR;
85 		break;
86 	case 3:
87 		ch_map[0] = PCM_CHANNEL_FL;
88 		ch_map[1] = PCM_CHANNEL_FR;
89 		ch_map[2] = PCM_CHANNEL_FC;
90 		break;
91 	case 4:
92 		ch_map[0] = PCM_CHANNEL_FL;
93 		ch_map[1] = PCM_CHANNEL_LB;
94 		ch_map[2] = PCM_CHANNEL_FR;
95 		ch_map[3] = PCM_CHANNEL_RB;
96 		break;
97 	default:
98 		return -EINVAL;
99 	}
100 
101 	return 0;
102 }
103 
104 static int x1e80100_snd_prepare(struct snd_pcm_substream *substream)
105 {
106 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
107 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
108 	struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
109 	unsigned int channels = substream->runtime->channels;
110 	unsigned int rx_slot[4];
111 	int ret;
112 
113 	switch (cpu_dai->id) {
114 	case WSA_CODEC_DMA_RX_0:
115 	case WSA_CODEC_DMA_RX_1:
116 		ret = x1e80100_snd_hw_map_channels(rx_slot, channels);
117 		if (ret)
118 			return ret;
119 
120 		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
121 						  channels, rx_slot);
122 		if (ret)
123 			return ret;
124 		break;
125 	default:
126 		break;
127 	}
128 
129 	return qcom_snd_sdw_prepare(substream, &data->stream_prepared[cpu_dai->id]);
130 }
131 
132 static int x1e80100_snd_hw_free(struct snd_pcm_substream *substream)
133 {
134 	struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
135 	struct x1e80100_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
136 	struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
137 
138 	return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
139 }
140 
141 static const struct snd_soc_ops x1e80100_be_ops = {
142 	.startup = qcom_snd_sdw_startup,
143 	.shutdown = qcom_snd_sdw_shutdown,
144 	.hw_free = x1e80100_snd_hw_free,
145 	.prepare = x1e80100_snd_prepare,
146 };
147 
148 static void x1e80100_add_be_ops(struct snd_soc_card *card)
149 {
150 	struct snd_soc_dai_link *link;
151 	int i;
152 
153 	for_each_card_prelinks(card, i, link) {
154 		if (link->no_pcm == 1) {
155 			link->init = x1e80100_snd_init;
156 			link->be_hw_params_fixup = x1e80100_be_hw_params_fixup;
157 			link->ops = &x1e80100_be_ops;
158 		}
159 	}
160 }
161 
162 static int x1e80100_platform_probe(struct platform_device *pdev)
163 {
164 	struct snd_soc_card *card;
165 	struct x1e80100_snd_data *data;
166 	struct device *dev = &pdev->dev;
167 	int ret;
168 
169 	card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
170 	if (!card)
171 		return -ENOMEM;
172 	/* Allocate the private data */
173 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
174 	if (!data)
175 		return -ENOMEM;
176 
177 	card->owner = THIS_MODULE;
178 	card->dev = dev;
179 	dev_set_drvdata(dev, card);
180 	snd_soc_card_set_drvdata(card, data);
181 
182 	ret = qcom_snd_parse_of(card);
183 	if (ret)
184 		return ret;
185 
186 	card->driver_name = of_device_get_match_data(dev);
187 	x1e80100_add_be_ops(card);
188 
189 	return devm_snd_soc_register_card(dev, card);
190 }
191 
192 static const struct of_device_id snd_x1e80100_dt_match[] = {
193 	{ .compatible = "qcom,x1e80100-sndcard", .data = "x1e80100" },
194 	{ .compatible = "qcom,glymur-sndcard", .data = "glymur" },
195 	{}
196 };
197 MODULE_DEVICE_TABLE(of, snd_x1e80100_dt_match);
198 
199 static struct platform_driver snd_x1e80100_driver = {
200 	.probe  = x1e80100_platform_probe,
201 	.driver = {
202 		.name = "snd-x1e80100",
203 		.of_match_table = snd_x1e80100_dt_match,
204 	},
205 };
206 module_platform_driver(snd_x1e80100_driver);
207 MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org");
208 MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>");
209 MODULE_DESCRIPTION("Qualcomm X1E80100 ASoC Machine Driver");
210 MODULE_LICENSE("GPL");
211