1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | // |
3 | // sdw-mockup.c -- a mockup SoundWire codec for tests where only the host |
4 | // drives the bus. |
5 | // |
6 | // Copyright(c) 2021 Intel Corporation |
7 | // |
8 | // |
9 | |
10 | #include <linux/device.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/module.h> |
13 | #include <linux/soundwire/sdw.h> |
14 | #include <linux/soundwire/sdw_type.h> |
15 | #include <linux/soundwire/sdw_registers.h> |
16 | #include <sound/core.h> |
17 | #include <sound/pcm.h> |
18 | #include <sound/pcm_params.h> |
19 | #include <sound/sdw.h> |
20 | #include <sound/soc.h> |
21 | |
22 | struct sdw_mockup_priv { |
23 | struct sdw_slave *slave; |
24 | }; |
25 | |
26 | static int sdw_mockup_component_probe(struct snd_soc_component *component) |
27 | { |
28 | return 0; |
29 | } |
30 | |
31 | static void sdw_mockup_component_remove(struct snd_soc_component *component) |
32 | { |
33 | } |
34 | |
35 | static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = { |
36 | .probe = sdw_mockup_component_probe, |
37 | .remove = sdw_mockup_component_remove, |
38 | .endianness = 1, |
39 | }; |
40 | |
41 | static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, |
42 | int direction) |
43 | { |
44 | snd_soc_dai_dma_data_set(dai, stream: direction, data: sdw_stream); |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static void sdw_mockup_shutdown(struct snd_pcm_substream *substream, |
50 | struct snd_soc_dai *dai) |
51 | { |
52 | snd_soc_dai_set_dma_data(dai, substream, NULL); |
53 | } |
54 | |
55 | static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream, |
56 | struct snd_pcm_hw_params *params, |
57 | struct snd_soc_dai *dai) |
58 | { |
59 | struct snd_soc_component *component = dai->component; |
60 | struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(c: component); |
61 | struct sdw_stream_config stream_config = {0}; |
62 | struct sdw_port_config port_config = {0}; |
63 | struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); |
64 | int ret; |
65 | |
66 | if (!sdw_stream) |
67 | return -EINVAL; |
68 | |
69 | if (!sdw_mockup->slave) |
70 | return -EINVAL; |
71 | |
72 | /* SoundWire specific configuration */ |
73 | snd_sdw_params_to_config(substream, params, stream_config: &stream_config, port_config: &port_config); |
74 | |
75 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
76 | port_config.num = 1; |
77 | else |
78 | port_config.num = 8; |
79 | |
80 | ret = sdw_stream_add_slave(slave: sdw_mockup->slave, stream_config: &stream_config, |
81 | port_config: &port_config, num_ports: 1, stream: sdw_stream); |
82 | if (ret) |
83 | dev_err(dai->dev, "Unable to configure port\n" ); |
84 | |
85 | return ret; |
86 | } |
87 | |
88 | static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream, |
89 | struct snd_soc_dai *dai) |
90 | { |
91 | struct snd_soc_component *component = dai->component; |
92 | struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(c: component); |
93 | struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); |
94 | |
95 | if (!sdw_mockup->slave) |
96 | return -EINVAL; |
97 | |
98 | sdw_stream_remove_slave(slave: sdw_mockup->slave, stream: sdw_stream); |
99 | return 0; |
100 | } |
101 | |
102 | static const struct snd_soc_dai_ops sdw_mockup_ops = { |
103 | .hw_params = sdw_mockup_pcm_hw_params, |
104 | .hw_free = sdw_mockup_pcm_hw_free, |
105 | .set_stream = sdw_mockup_set_sdw_stream, |
106 | .shutdown = sdw_mockup_shutdown, |
107 | }; |
108 | |
109 | static struct snd_soc_dai_driver sdw_mockup_dai[] = { |
110 | { |
111 | .name = "sdw-mockup-aif1" , |
112 | .id = 1, |
113 | .playback = { |
114 | .stream_name = "DP1 Playback" , |
115 | .channels_min = 1, |
116 | .channels_max = 2, |
117 | }, |
118 | .capture = { |
119 | .stream_name = "DP8 Capture" , |
120 | .channels_min = 1, |
121 | .channels_max = 2, |
122 | }, |
123 | .ops = &sdw_mockup_ops, |
124 | }, |
125 | }; |
126 | |
127 | static int sdw_mockup_update_status(struct sdw_slave *slave, |
128 | enum sdw_slave_status status) |
129 | { |
130 | return 0; |
131 | } |
132 | |
133 | static int sdw_mockup_read_prop(struct sdw_slave *slave) |
134 | { |
135 | struct sdw_slave_prop *prop = &slave->prop; |
136 | int nval; |
137 | int i, j; |
138 | u32 bit; |
139 | unsigned long addr; |
140 | struct sdw_dpn_prop *dpn; |
141 | |
142 | prop->paging_support = false; |
143 | |
144 | /* |
145 | * first we need to allocate memory for set bits in port lists |
146 | * the port allocation is completely arbitrary: |
147 | * DP0 is not supported |
148 | * DP1 is sink |
149 | * DP8 is source |
150 | */ |
151 | prop->source_ports = BIT(8); |
152 | prop->sink_ports = BIT(1); |
153 | |
154 | nval = hweight32(prop->source_ports); |
155 | prop->src_dpn_prop = devm_kcalloc(dev: &slave->dev, n: nval, |
156 | size: sizeof(*prop->src_dpn_prop), |
157 | GFP_KERNEL); |
158 | if (!prop->src_dpn_prop) |
159 | return -ENOMEM; |
160 | |
161 | i = 0; |
162 | dpn = prop->src_dpn_prop; |
163 | addr = prop->source_ports; |
164 | for_each_set_bit(bit, &addr, 32) { |
165 | dpn[i].num = bit; |
166 | dpn[i].type = SDW_DPN_FULL; |
167 | dpn[i].simple_ch_prep_sm = true; |
168 | i++; |
169 | } |
170 | |
171 | /* do this again for sink now */ |
172 | nval = hweight32(prop->sink_ports); |
173 | prop->sink_dpn_prop = devm_kcalloc(dev: &slave->dev, n: nval, |
174 | size: sizeof(*prop->sink_dpn_prop), |
175 | GFP_KERNEL); |
176 | if (!prop->sink_dpn_prop) |
177 | return -ENOMEM; |
178 | |
179 | j = 0; |
180 | dpn = prop->sink_dpn_prop; |
181 | addr = prop->sink_ports; |
182 | for_each_set_bit(bit, &addr, 32) { |
183 | dpn[j].num = bit; |
184 | dpn[j].type = SDW_DPN_FULL; |
185 | dpn[j].simple_ch_prep_sm = true; |
186 | j++; |
187 | } |
188 | |
189 | prop->simple_clk_stop_capable = true; |
190 | |
191 | /* wake-up event */ |
192 | prop->wake_capable = 0; |
193 | |
194 | return 0; |
195 | } |
196 | |
197 | static int sdw_mockup_bus_config(struct sdw_slave *slave, |
198 | struct sdw_bus_params *params) |
199 | { |
200 | return 0; |
201 | } |
202 | |
203 | static int sdw_mockup_interrupt_callback(struct sdw_slave *slave, |
204 | struct sdw_slave_intr_status *status) |
205 | { |
206 | return 0; |
207 | } |
208 | |
209 | static const struct sdw_slave_ops sdw_mockup_slave_ops = { |
210 | .read_prop = sdw_mockup_read_prop, |
211 | .interrupt_callback = sdw_mockup_interrupt_callback, |
212 | .update_status = sdw_mockup_update_status, |
213 | .bus_config = sdw_mockup_bus_config, |
214 | }; |
215 | |
216 | static int sdw_mockup_sdw_probe(struct sdw_slave *slave, |
217 | const struct sdw_device_id *id) |
218 | { |
219 | struct device *dev = &slave->dev; |
220 | struct sdw_mockup_priv *sdw_mockup; |
221 | int ret; |
222 | |
223 | sdw_mockup = devm_kzalloc(dev, size: sizeof(*sdw_mockup), GFP_KERNEL); |
224 | if (!sdw_mockup) |
225 | return -ENOMEM; |
226 | |
227 | dev_set_drvdata(dev, data: sdw_mockup); |
228 | sdw_mockup->slave = slave; |
229 | |
230 | slave->is_mockup_device = true; |
231 | |
232 | ret = devm_snd_soc_register_component(dev, |
233 | component_driver: &snd_soc_sdw_mockup_component, |
234 | dai_drv: sdw_mockup_dai, |
235 | ARRAY_SIZE(sdw_mockup_dai)); |
236 | |
237 | return ret; |
238 | } |
239 | |
240 | static int sdw_mockup_sdw_remove(struct sdw_slave *slave) |
241 | { |
242 | return 0; |
243 | } |
244 | |
245 | /* |
246 | * Intel reserved parts ID with the following mapping expected: |
247 | * 0xAAAA: generic full-duplex codec |
248 | * 0xAA55: headset codec (mock-up of RT711/RT5682) - full-duplex |
249 | * 0x55AA: amplifier (mock-up of RT1308/Maxim 98373) - playback only with |
250 | * IV feedback |
251 | * 0x5555: mic codec (mock-up of RT715) - capture-only |
252 | */ |
253 | static const struct sdw_device_id sdw_mockup_id[] = { |
254 | SDW_SLAVE_ENTRY_EXT(0x0105, 0xAAAA, 0x0, 0, 0), |
255 | SDW_SLAVE_ENTRY_EXT(0x0105, 0xAA55, 0x0, 0, 0), |
256 | SDW_SLAVE_ENTRY_EXT(0x0105, 0x55AA, 0x0, 0, 0), |
257 | SDW_SLAVE_ENTRY_EXT(0x0105, 0x5555, 0x0, 0, 0), |
258 | {}, |
259 | }; |
260 | MODULE_DEVICE_TABLE(sdw, sdw_mockup_id); |
261 | |
262 | static struct sdw_driver sdw_mockup_sdw_driver = { |
263 | .driver = { |
264 | .name = "sdw-mockup" , |
265 | .owner = THIS_MODULE, |
266 | }, |
267 | .probe = sdw_mockup_sdw_probe, |
268 | .remove = sdw_mockup_sdw_remove, |
269 | .ops = &sdw_mockup_slave_ops, |
270 | .id_table = sdw_mockup_id, |
271 | }; |
272 | module_sdw_driver(sdw_mockup_sdw_driver); |
273 | |
274 | MODULE_DESCRIPTION("ASoC SDW mockup codec driver" ); |
275 | MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>" ); |
276 | MODULE_LICENSE("GPL" ); |
277 | |