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