Skip to content

Commit 13a5635

Browse files
committed
Merge tag 'asoc-sdw-mockup-codec' into next
ASoC: Add mockup SoundWire CODEC Useful for bringup testing, not for production usage.
2 parents ff56094 + 0ccac3b commit 13a5635

File tree

10 files changed

+595
-1
lines changed

10 files changed

+595
-1
lines changed

sound/soc/codecs/Kconfig

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ config SND_SOC_ALL_CODECS
187187
imply SND_SOC_RT715_SDCA_SDW
188188
imply SND_SOC_RT1308_SDW
189189
imply SND_SOC_RT1316_SDW
190+
imply SND_SOC_SDW_MOCKUP
190191
imply SND_SOC_SGTL5000
191192
imply SND_SOC_SI476X
192193
imply SND_SOC_SIMPLE_AMPLIFIER
@@ -1287,6 +1288,23 @@ config SND_SOC_RT715_SDCA_SDW
12871288
select REGMAP_SOUNDWIRE
12881289
select REGMAP_SOUNDWIRE_MBQ
12891290

1291+
config SND_SOC_SDW_MOCKUP
1292+
tristate "SoundWire mockup codec"
1293+
depends on EXPERT
1294+
depends on SOUNDWIRE
1295+
help
1296+
This option enables a SoundWire mockup codec that does not drive the
1297+
bus, take part in the command/command protocol or generate data on a
1298+
Source port.
1299+
This option is only intended to be used for tests on a device
1300+
with a connector, in combination with a bus analyzer, or to test new
1301+
topologies that differ from the actual hardware layout.
1302+
This mockup device could be totally virtual but could also be a
1303+
real physical one with one key restriction: it is not allowed by the
1304+
SoundWire specification to be configured via a sideband mechanism and
1305+
generate audio data for capture. However, nothing prevents such a
1306+
peripheral device from snooping the bus.
1307+
12901308
#Freescale sgtl5000 codec
12911309
config SND_SOC_SGTL5000
12921310
tristate "Freescale SGTL5000 CODEC"

sound/soc/codecs/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ snd-soc-rt711-objs := rt711.o rt711-sdw.o
203203
snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o
204204
snd-soc-rt715-objs := rt715.o rt715-sdw.o
205205
snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o
206+
snd-soc-sdw-mockup-objs := sdw-mockup.o
206207
snd-soc-sgtl5000-objs := sgtl5000.o
207208
snd-soc-alc5623-objs := alc5623.o
208209
snd-soc-alc5632-objs := alc5632.o
@@ -530,6 +531,7 @@ obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
530531
obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o
531532
obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o
532533
obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o
534+
obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o
533535
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
534536
obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
535537
obj-$(CONFIG_SND_SOC_SIGMADSP_I2C) += snd-soc-sigmadsp-i2c.o

sound/soc/codecs/sdw-mockup.c

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
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/soc.h>
20+
21+
struct sdw_mockup_priv {
22+
struct sdw_slave *slave;
23+
};
24+
25+
struct sdw_stream_data {
26+
struct sdw_stream_runtime *sdw_stream;
27+
};
28+
29+
static int sdw_mockup_component_probe(struct snd_soc_component *component)
30+
{
31+
return 0;
32+
}
33+
34+
static void sdw_mockup_component_remove(struct snd_soc_component *component)
35+
{
36+
}
37+
38+
static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
39+
.probe = sdw_mockup_component_probe,
40+
.remove = sdw_mockup_component_remove,
41+
};
42+
43+
static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
44+
int direction)
45+
{
46+
struct sdw_stream_data *stream;
47+
48+
if (!sdw_stream)
49+
return 0;
50+
51+
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
52+
if (!stream)
53+
return -ENOMEM;
54+
55+
stream->sdw_stream = sdw_stream;
56+
57+
/* Use tx_mask or rx_mask to configure stream tag and set dma_data */
58+
if (direction == SNDRV_PCM_STREAM_PLAYBACK)
59+
dai->playback_dma_data = stream;
60+
else
61+
dai->capture_dma_data = stream;
62+
63+
return 0;
64+
}
65+
66+
static void sdw_mockup_shutdown(struct snd_pcm_substream *substream,
67+
struct snd_soc_dai *dai)
68+
{
69+
struct sdw_stream_data *stream;
70+
71+
stream = snd_soc_dai_get_dma_data(dai, substream);
72+
snd_soc_dai_set_dma_data(dai, substream, NULL);
73+
kfree(stream);
74+
}
75+
76+
static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
77+
struct snd_pcm_hw_params *params,
78+
struct snd_soc_dai *dai)
79+
{
80+
struct snd_soc_component *component = dai->component;
81+
struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
82+
struct sdw_stream_config stream_config;
83+
struct sdw_port_config port_config;
84+
enum sdw_data_direction direction;
85+
struct sdw_stream_data *stream;
86+
int num_channels;
87+
int port;
88+
int ret;
89+
90+
stream = snd_soc_dai_get_dma_data(dai, substream);
91+
if (!stream)
92+
return -EINVAL;
93+
94+
if (!sdw_mockup->slave)
95+
return -EINVAL;
96+
97+
/* SoundWire specific configuration */
98+
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
99+
direction = SDW_DATA_DIR_RX;
100+
port = 1;
101+
} else {
102+
direction = SDW_DATA_DIR_TX;
103+
port = 8;
104+
}
105+
106+
stream_config.frame_rate = params_rate(params);
107+
stream_config.ch_count = params_channels(params);
108+
stream_config.bps = snd_pcm_format_width(params_format(params));
109+
stream_config.direction = direction;
110+
111+
num_channels = params_channels(params);
112+
port_config.ch_mask = (1 << num_channels) - 1;
113+
port_config.num = port;
114+
115+
ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config,
116+
&port_config, 1, stream->sdw_stream);
117+
if (ret)
118+
dev_err(dai->dev, "Unable to configure port\n");
119+
120+
return ret;
121+
}
122+
123+
static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream,
124+
struct snd_soc_dai *dai)
125+
{
126+
struct snd_soc_component *component = dai->component;
127+
struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
128+
struct sdw_stream_data *stream =
129+
snd_soc_dai_get_dma_data(dai, substream);
130+
131+
if (!sdw_mockup->slave)
132+
return -EINVAL;
133+
134+
sdw_stream_remove_slave(sdw_mockup->slave, stream->sdw_stream);
135+
return 0;
136+
}
137+
138+
static const struct snd_soc_dai_ops sdw_mockup_ops = {
139+
.hw_params = sdw_mockup_pcm_hw_params,
140+
.hw_free = sdw_mockup_pcm_hw_free,
141+
.set_sdw_stream = sdw_mockup_set_sdw_stream,
142+
.shutdown = sdw_mockup_shutdown,
143+
};
144+
145+
static struct snd_soc_dai_driver sdw_mockup_dai[] = {
146+
{
147+
.name = "sdw-mockup-aif1",
148+
.id = 1,
149+
.playback = {
150+
.stream_name = "DP1 Playback",
151+
.channels_min = 1,
152+
.channels_max = 2,
153+
},
154+
.capture = {
155+
.stream_name = "DP8 Capture",
156+
.channels_min = 1,
157+
.channels_max = 2,
158+
},
159+
.ops = &sdw_mockup_ops,
160+
},
161+
};
162+
163+
static int sdw_mockup_update_status(struct sdw_slave *slave,
164+
enum sdw_slave_status status)
165+
{
166+
return 0;
167+
}
168+
169+
static int sdw_mockup_read_prop(struct sdw_slave *slave)
170+
{
171+
struct sdw_slave_prop *prop = &slave->prop;
172+
int nval;
173+
int i, j;
174+
u32 bit;
175+
unsigned long addr;
176+
struct sdw_dpn_prop *dpn;
177+
178+
prop->paging_support = false;
179+
180+
/*
181+
* first we need to allocate memory for set bits in port lists
182+
* the port allocation is completely arbitrary:
183+
* DP0 is not supported
184+
* DP1 is sink
185+
* DP8 is source
186+
*/
187+
prop->source_ports = BIT(8);
188+
prop->sink_ports = BIT(1);
189+
190+
nval = hweight32(prop->source_ports);
191+
prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
192+
sizeof(*prop->src_dpn_prop),
193+
GFP_KERNEL);
194+
if (!prop->src_dpn_prop)
195+
return -ENOMEM;
196+
197+
i = 0;
198+
dpn = prop->src_dpn_prop;
199+
addr = prop->source_ports;
200+
for_each_set_bit(bit, &addr, 32) {
201+
dpn[i].num = bit;
202+
dpn[i].type = SDW_DPN_FULL;
203+
dpn[i].simple_ch_prep_sm = true;
204+
i++;
205+
}
206+
207+
/* do this again for sink now */
208+
nval = hweight32(prop->sink_ports);
209+
prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
210+
sizeof(*prop->sink_dpn_prop),
211+
GFP_KERNEL);
212+
if (!prop->sink_dpn_prop)
213+
return -ENOMEM;
214+
215+
j = 0;
216+
dpn = prop->sink_dpn_prop;
217+
addr = prop->sink_ports;
218+
for_each_set_bit(bit, &addr, 32) {
219+
dpn[j].num = bit;
220+
dpn[j].type = SDW_DPN_FULL;
221+
dpn[j].simple_ch_prep_sm = true;
222+
j++;
223+
}
224+
225+
prop->simple_clk_stop_capable = true;
226+
227+
/* wake-up event */
228+
prop->wake_capable = 0;
229+
230+
return 0;
231+
}
232+
233+
static int sdw_mockup_bus_config(struct sdw_slave *slave,
234+
struct sdw_bus_params *params)
235+
{
236+
return 0;
237+
}
238+
239+
static int sdw_mockup_interrupt_callback(struct sdw_slave *slave,
240+
struct sdw_slave_intr_status *status)
241+
{
242+
return 0;
243+
}
244+
245+
static const struct sdw_slave_ops sdw_mockup_slave_ops = {
246+
.read_prop = sdw_mockup_read_prop,
247+
.interrupt_callback = sdw_mockup_interrupt_callback,
248+
.update_status = sdw_mockup_update_status,
249+
.bus_config = sdw_mockup_bus_config,
250+
};
251+
252+
static int sdw_mockup_sdw_probe(struct sdw_slave *slave,
253+
const struct sdw_device_id *id)
254+
{
255+
struct device *dev = &slave->dev;
256+
struct sdw_mockup_priv *sdw_mockup;
257+
int ret;
258+
259+
sdw_mockup = devm_kzalloc(dev, sizeof(*sdw_mockup), GFP_KERNEL);
260+
if (!sdw_mockup)
261+
return -ENOMEM;
262+
263+
dev_set_drvdata(dev, sdw_mockup);
264+
sdw_mockup->slave = slave;
265+
266+
ret = devm_snd_soc_register_component(dev,
267+
&snd_soc_sdw_mockup_component,
268+
sdw_mockup_dai,
269+
ARRAY_SIZE(sdw_mockup_dai));
270+
271+
return ret;
272+
}
273+
274+
static int sdw_mockup_sdw_remove(struct sdw_slave *slave)
275+
{
276+
return 0;
277+
}
278+
279+
/*
280+
* Intel reserved parts ID with the following mapping expected:
281+
* 0xAAAA: generic full-duplex codec
282+
* 0xAA55: headset codec (mock-up of RT711/RT5682) - full-duplex
283+
* 0x55AA: amplifier (mock-up of RT1308/Maxim 98373) - playback only with
284+
* IV feedback
285+
* 0x5555: mic codec (mock-up of RT715) - capture-only
286+
*/
287+
static const struct sdw_device_id sdw_mockup_id[] = {
288+
SDW_SLAVE_ENTRY_EXT(0x0105, 0xAAAA, 0x0, 0, 0),
289+
SDW_SLAVE_ENTRY_EXT(0x0105, 0xAA55, 0x0, 0, 0),
290+
SDW_SLAVE_ENTRY_EXT(0x0105, 0x55AA, 0x0, 0, 0),
291+
SDW_SLAVE_ENTRY_EXT(0x0105, 0x5555, 0x0, 0, 0),
292+
{},
293+
};
294+
MODULE_DEVICE_TABLE(sdw, sdw_mockup_id);
295+
296+
static struct sdw_driver sdw_mockup_sdw_driver = {
297+
.driver = {
298+
.name = "sdw-mockup",
299+
.owner = THIS_MODULE,
300+
},
301+
.probe = sdw_mockup_sdw_probe,
302+
.remove = sdw_mockup_sdw_remove,
303+
.ops = &sdw_mockup_slave_ops,
304+
.id_table = sdw_mockup_id,
305+
};
306+
module_sdw_driver(sdw_mockup_sdw_driver);
307+
308+
MODULE_DESCRIPTION("ASoC SDW mockup codec driver");
309+
MODULE_AUTHOR("Pierre-Louis Bossart <[email protected]>");
310+
MODULE_LICENSE("GPL");

sound/soc/intel/boards/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
602602
select SND_SOC_DMIC
603603
select SND_SOC_INTEL_HDA_DSP_COMMON
604604
select SND_SOC_INTEL_SOF_MAXIM_COMMON
605+
select SND_SOC_SDW_MOCKUP
605606
help
606607
Add support for Intel SoundWire-based platforms connected to
607608
MAX98373, RT700, RT711, RT1308 and RT715

0 commit comments

Comments
 (0)