Commit | Line | Data |
---|---|---|
295aeea6 SK |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2022, Linaro Limited | |
3 | ||
4 | #include <linux/module.h> | |
5 | #include <linux/platform_device.h> | |
6 | #include <linux/of_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" | |
0cbf1ecd | 15 | #include "sdw.h" |
295aeea6 SK |
16 | |
17 | #define DRIVER_NAME "sc8280xp" | |
18 | ||
19 | struct sc8280xp_snd_data { | |
20 | bool stream_prepared[AFE_PORT_MAX]; | |
21 | struct snd_soc_card *card; | |
22 | struct sdw_stream_runtime *sruntime[AFE_PORT_MAX]; | |
23 | struct snd_soc_jack jack; | |
24 | bool jack_setup; | |
25 | }; | |
26 | ||
27 | static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd) | |
28 | { | |
29 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); | |
30 | ||
31 | return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup); | |
32 | } | |
33 | ||
34 | static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, | |
35 | struct snd_pcm_hw_params *params) | |
36 | { | |
37 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
38 | struct snd_interval *rate = hw_param_interval(params, | |
39 | SNDRV_PCM_HW_PARAM_RATE); | |
40 | struct snd_interval *channels = hw_param_interval(params, | |
41 | SNDRV_PCM_HW_PARAM_CHANNELS); | |
42 | ||
43 | rate->min = rate->max = 48000; | |
44 | channels->min = 2; | |
45 | channels->max = 2; | |
46 | switch (cpu_dai->id) { | |
47 | case TX_CODEC_DMA_TX_0: | |
48 | case TX_CODEC_DMA_TX_1: | |
49 | case TX_CODEC_DMA_TX_2: | |
50 | case TX_CODEC_DMA_TX_3: | |
51 | channels->min = 1; | |
52 | break; | |
53 | default: | |
54 | break; | |
55 | } | |
56 | ||
57 | ||
58 | return 0; | |
59 | } | |
60 | ||
61 | static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream, | |
62 | struct snd_pcm_hw_params *params) | |
63 | { | |
64 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
65 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
66 | struct sc8280xp_snd_data *pdata = snd_soc_card_get_drvdata(rtd->card); | |
67 | ||
68 | return qcom_snd_sdw_hw_params(substream, params, &pdata->sruntime[cpu_dai->id]); | |
69 | } | |
70 | ||
71 | static int sc8280xp_snd_prepare(struct snd_pcm_substream *substream) | |
72 | { | |
73 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
74 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
75 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); | |
76 | struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; | |
77 | ||
78 | return qcom_snd_sdw_prepare(substream, sruntime, | |
79 | &data->stream_prepared[cpu_dai->id]); | |
80 | } | |
81 | ||
82 | static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream) | |
83 | { | |
84 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | |
85 | struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card); | |
86 | struct snd_soc_dai *cpu_dai = asoc_rtd_to_cpu(rtd, 0); | |
87 | struct sdw_stream_runtime *sruntime = data->sruntime[cpu_dai->id]; | |
88 | ||
89 | return qcom_snd_sdw_hw_free(substream, sruntime, | |
90 | &data->stream_prepared[cpu_dai->id]); | |
91 | } | |
92 | ||
93 | static const struct snd_soc_ops sc8280xp_be_ops = { | |
94 | .hw_params = sc8280xp_snd_hw_params, | |
95 | .hw_free = sc8280xp_snd_hw_free, | |
96 | .prepare = sc8280xp_snd_prepare, | |
97 | }; | |
98 | ||
99 | static void sc8280xp_add_be_ops(struct snd_soc_card *card) | |
100 | { | |
101 | struct snd_soc_dai_link *link; | |
102 | int i; | |
103 | ||
104 | for_each_card_prelinks(card, i, link) { | |
105 | if (link->no_pcm == 1) { | |
106 | link->init = sc8280xp_snd_init; | |
107 | link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup; | |
108 | link->ops = &sc8280xp_be_ops; | |
109 | } | |
110 | } | |
111 | } | |
112 | ||
113 | static int sc8280xp_platform_probe(struct platform_device *pdev) | |
114 | { | |
115 | struct snd_soc_card *card; | |
116 | struct sc8280xp_snd_data *data; | |
117 | struct device *dev = &pdev->dev; | |
118 | int ret; | |
119 | ||
120 | card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); | |
121 | if (!card) | |
122 | return -ENOMEM; | |
123 | card->owner = THIS_MODULE; | |
124 | /* Allocate the private data */ | |
125 | data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); | |
126 | if (!data) | |
127 | return -ENOMEM; | |
128 | ||
129 | card->dev = dev; | |
130 | dev_set_drvdata(dev, card); | |
131 | snd_soc_card_set_drvdata(card, data); | |
132 | ret = qcom_snd_parse_of(card); | |
133 | if (ret) | |
134 | return ret; | |
135 | ||
136 | card->driver_name = DRIVER_NAME; | |
137 | sc8280xp_add_be_ops(card); | |
138 | return devm_snd_soc_register_card(dev, card); | |
139 | } | |
140 | ||
141 | static const struct of_device_id snd_sc8280xp_dt_match[] = { | |
142 | {.compatible = "qcom,sc8280xp-sndcard",}, | |
143 | {} | |
144 | }; | |
145 | ||
146 | MODULE_DEVICE_TABLE(of, snd_sc8280xp_dt_match); | |
147 | ||
148 | static struct platform_driver snd_sc8280xp_driver = { | |
149 | .probe = sc8280xp_platform_probe, | |
150 | .driver = { | |
151 | .name = "snd-sc8280xp", | |
152 | .of_match_table = snd_sc8280xp_dt_match, | |
153 | }, | |
154 | }; | |
155 | module_platform_driver(snd_sc8280xp_driver); | |
156 | MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); | |
157 | MODULE_DESCRIPTION("SC8280XP ASoC Machine Driver"); | |
158 | MODULE_LICENSE("GPL v2"); |