1 // SPDX-License-Identifier: GPL-2.0-only 2 // 3 // Copyright(c) 2021-2022 Intel Corporation 4 // 5 // Authors: Cezary Rojewski <cezary.rojewski@intel.com> 6 // Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com> 7 // 8 9 #include <sound/compress_driver.h> 10 #include <sound/hdaudio_ext.h> 11 #include <sound/hdaudio.h> 12 #include <sound/soc.h> 13 #include "avs.h" 14 #include "messages.h" 15 16 static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id, 17 size_t buffer_size) 18 { 19 struct avs_probe_cfg cfg = {{0}}; 20 struct avs_module_entry mentry; 21 u8 dummy; 22 int ret; 23 24 ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); 25 if (ret) 26 return ret; 27 28 /* 29 * Probe module uses no cycles, audio data format and input and output 30 * frame sizes are unused. It is also not owned by any pipeline. 31 */ 32 cfg.base.ibs = 1; 33 /* BSS module descriptor is always segment of index=2. */ 34 cfg.base.is_pages = mentry.segments[2].flags.length; 35 cfg.gtw_cfg.node_id = node_id; 36 cfg.gtw_cfg.dma_buffer_size = buffer_size; 37 38 return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg, 39 sizeof(cfg), &dummy); 40 } 41 42 static void avs_dsp_delete_probe(struct avs_dev *adev) 43 { 44 struct avs_module_entry mentry; 45 int ret; 46 47 ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); 48 if (!ret) 49 /* There is only ever one probe module instance. */ 50 avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); 51 } 52 53 static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream) 54 { 55 return cstream->runtime->private_data; 56 } 57 58 static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai) 59 { 60 struct avs_dev *adev = to_avs_dev(dai->dev); 61 struct hdac_bus *bus = &adev->base.core; 62 struct hdac_ext_stream *host_stream; 63 64 if (adev->extractor) { 65 dev_err(dai->dev, "Cannot open more than one extractor stream\n"); 66 return -EEXIST; 67 } 68 69 host_stream = snd_hdac_ext_cstream_assign(bus, cstream); 70 if (!host_stream) { 71 dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n"); 72 return -EBUSY; 73 } 74 75 adev->extractor = host_stream; 76 hdac_stream(host_stream)->curr_pos = 0; 77 cstream->runtime->private_data = host_stream; 78 79 return 0; 80 } 81 82 static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai) 83 { 84 struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); 85 struct avs_dev *adev = to_avs_dev(dai->dev); 86 struct avs_probe_point_desc *desc; 87 /* Extractor node identifier. */ 88 unsigned int vindex = INVALID_NODE_ID.vindex; 89 size_t num_desc; 90 int i, ret; 91 92 /* Disconnect all probe points. */ 93 ret = avs_ipc_probe_get_points(adev, &desc, &num_desc); 94 if (ret) { 95 dev_err(dai->dev, "get probe points failed: %d\n", ret); 96 ret = AVS_IPC_RET(ret); 97 goto exit; 98 } 99 100 for (i = 0; i < num_desc; i++) 101 if (desc[i].node_id.vindex == vindex) 102 avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1); 103 kfree(desc); 104 105 exit: 106 if (adev->num_probe_streams) { 107 adev->num_probe_streams--; 108 if (!adev->num_probe_streams) { 109 avs_dsp_delete_probe(adev); 110 avs_dsp_enable_d0ix(adev); 111 } 112 } 113 114 snd_hdac_stream_cleanup(hdac_stream(host_stream)); 115 hdac_stream(host_stream)->prepared = 0; 116 snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST); 117 118 snd_compr_free_pages(cstream); 119 adev->extractor = NULL; 120 121 return ret; 122 } 123 124 static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, 125 struct snd_compr_params *params, struct snd_soc_dai *dai) 126 { 127 struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); 128 struct snd_compr_runtime *rtd = cstream->runtime; 129 struct avs_dev *adev = to_avs_dev(dai->dev); 130 /* compr params do not store bit depth, default to S32_LE. */ 131 snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE; 132 unsigned int format_val; 133 int bps, ret; 134 135 hdac_stream(host_stream)->bufsize = 0; 136 hdac_stream(host_stream)->period_bytes = 0; 137 hdac_stream(host_stream)->format_val = 0; 138 cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; 139 cstream->dma_buffer.dev.dev = adev->dev; 140 141 ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); 142 if (ret < 0) 143 return ret; 144 bps = snd_pcm_format_physical_width(format); 145 if (bps < 0) 146 return bps; 147 format_val = snd_hdac_stream_format(params->codec.ch_out, bps, params->codec.sample_rate); 148 ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); 149 if (ret < 0) 150 return ret; 151 ret = snd_hdac_stream_setup(hdac_stream(host_stream), false); 152 if (ret < 0) 153 return ret; 154 155 hdac_stream(host_stream)->prepared = 1; 156 157 if (!adev->num_probe_streams) { 158 union avs_connector_node_id node_id; 159 160 /* D0ix not allowed during probing. */ 161 ret = avs_dsp_disable_d0ix(adev); 162 if (ret) 163 return ret; 164 165 node_id.vindex = hdac_stream(host_stream)->stream_tag - 1; 166 node_id.dma_type = AVS_DMA_HDA_HOST_INPUT; 167 168 ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes); 169 if (ret < 0) { 170 dev_err(dai->dev, "probe init failed: %d\n", ret); 171 avs_dsp_enable_d0ix(adev); 172 return ret; 173 } 174 } 175 176 adev->num_probe_streams++; 177 return 0; 178 } 179 180 static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, 181 struct snd_soc_dai *dai) 182 { 183 struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); 184 struct avs_dev *adev = to_avs_dev(dai->dev); 185 struct hdac_bus *bus = &adev->base.core; 186 unsigned long cookie; 187 188 if (!hdac_stream(host_stream)->prepared) 189 return -EPIPE; 190 191 switch (cmd) { 192 case SNDRV_PCM_TRIGGER_START: 193 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 194 case SNDRV_PCM_TRIGGER_RESUME: 195 spin_lock_irqsave(&bus->reg_lock, cookie); 196 snd_hdac_stream_start(hdac_stream(host_stream)); 197 spin_unlock_irqrestore(&bus->reg_lock, cookie); 198 break; 199 200 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 201 case SNDRV_PCM_TRIGGER_SUSPEND: 202 case SNDRV_PCM_TRIGGER_STOP: 203 spin_lock_irqsave(&bus->reg_lock, cookie); 204 snd_hdac_stream_stop(hdac_stream(host_stream)); 205 spin_unlock_irqrestore(&bus->reg_lock, cookie); 206 break; 207 208 default: 209 return -EINVAL; 210 } 211 212 return 0; 213 } 214 215 static int avs_probe_compr_pointer(struct snd_compr_stream *cstream, 216 struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) 217 { 218 struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); 219 struct snd_soc_pcm_stream *pstream; 220 221 pstream = &dai->driver->capture; 222 tstamp->copied_total = hdac_stream(host_stream)->curr_pos; 223 tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates); 224 225 return 0; 226 } 227 228 static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream, 229 char __user *buf, size_t count) 230 { 231 struct snd_compr_runtime *rtd = cstream->runtime; 232 unsigned int offset, n; 233 void *ptr; 234 int ret; 235 236 if (count > rtd->buffer_size) 237 count = rtd->buffer_size; 238 239 div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); 240 ptr = rtd->dma_area + offset; 241 n = rtd->buffer_size - offset; 242 243 if (count < n) { 244 ret = copy_to_user(buf, ptr, count); 245 } else { 246 ret = copy_to_user(buf, ptr, n); 247 ret += copy_to_user(buf + n, rtd->dma_area, count - n); 248 } 249 250 if (ret) 251 return count - ret; 252 return count; 253 } 254 255 static const struct snd_soc_cdai_ops avs_probe_cdai_ops = { 256 .startup = avs_probe_compr_open, 257 .shutdown = avs_probe_compr_free, 258 .set_params = avs_probe_compr_set_params, 259 .trigger = avs_probe_compr_trigger, 260 .pointer = avs_probe_compr_pointer, 261 }; 262 263 static const struct snd_soc_dai_ops avs_probe_dai_ops = { 264 .compress_new = snd_soc_new_compress, 265 }; 266 267 static const struct snd_compress_ops avs_probe_compress_ops = { 268 .copy = avs_probe_compr_copy, 269 }; 270 271 static struct snd_soc_dai_driver probe_cpu_dais[] = { 272 { 273 .name = "Probe Extraction CPU DAI", 274 .cops = &avs_probe_cdai_ops, 275 .ops = &avs_probe_dai_ops, 276 .capture = { 277 .stream_name = "Probe Extraction", 278 .channels_min = 1, 279 .channels_max = 8, 280 .rates = SNDRV_PCM_RATE_48000, 281 .rate_min = 48000, 282 .rate_max = 48000, 283 }, 284 }, 285 }; 286 287 static const struct snd_soc_component_driver avs_probe_component_driver = { 288 .name = "avs-probe-compr", 289 .compress_ops = &avs_probe_compress_ops, 290 .module_get_upon_open = 1, /* increment refcount when a stream is opened */ 291 }; 292 293 int avs_probe_platform_register(struct avs_dev *adev, const char *name) 294 { 295 return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver, 296 probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); 297 } 298
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.