File Index
Files and directories
Sign in
FILE

probes.c

File preview with syntax highlighting.

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