xref: /openbmc/linux/sound/soc/intel/avs/probes.c (revision 750e384b)
1dab8d000SCezary Rojewski // SPDX-License-Identifier: GPL-2.0-only
2dab8d000SCezary Rojewski //
3dab8d000SCezary Rojewski // Copyright(c) 2021-2022 Intel Corporation. All rights reserved.
4dab8d000SCezary Rojewski //
5dab8d000SCezary Rojewski // Authors: Cezary Rojewski <cezary.rojewski@intel.com>
6dab8d000SCezary Rojewski //          Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
7dab8d000SCezary Rojewski //
8dab8d000SCezary Rojewski 
9700462f5SCezary Rojewski #include <sound/compress_driver.h>
10700462f5SCezary Rojewski #include <sound/hdaudio_ext.h>
11700462f5SCezary Rojewski #include <sound/hdaudio.h>
12700462f5SCezary Rojewski #include <sound/soc.h>
13dab8d000SCezary Rojewski #include "avs.h"
14dab8d000SCezary Rojewski #include "messages.h"
15dab8d000SCezary Rojewski 
avs_dsp_init_probe(struct avs_dev * adev,union avs_connector_node_id node_id,size_t buffer_size)16dab8d000SCezary Rojewski static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id,
17dab8d000SCezary Rojewski 			      size_t buffer_size)
18dab8d000SCezary Rojewski {
19dab8d000SCezary Rojewski 	struct avs_probe_cfg cfg = {{0}};
20dab8d000SCezary Rojewski 	struct avs_module_entry mentry;
21320f4d86SAmadeusz Sławiński 	u8 dummy;
22750e384bSCezary Rojewski 	int ret;
23dab8d000SCezary Rojewski 
24750e384bSCezary Rojewski 	ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
25750e384bSCezary Rojewski 	if (ret)
26750e384bSCezary Rojewski 		return ret;
27dab8d000SCezary Rojewski 
28dab8d000SCezary Rojewski 	/*
29dab8d000SCezary Rojewski 	 * Probe module uses no cycles, audio data format and input and output
30dab8d000SCezary Rojewski 	 * frame sizes are unused. It is also not owned by any pipeline.
31dab8d000SCezary Rojewski 	 */
32dab8d000SCezary Rojewski 	cfg.base.ibs = 1;
33dab8d000SCezary Rojewski 	/* BSS module descriptor is always segment of index=2. */
34dab8d000SCezary Rojewski 	cfg.base.is_pages = mentry.segments[2].flags.length;
35dab8d000SCezary Rojewski 	cfg.gtw_cfg.node_id = node_id;
36dab8d000SCezary Rojewski 	cfg.gtw_cfg.dma_buffer_size = buffer_size;
37dab8d000SCezary Rojewski 
38dab8d000SCezary Rojewski 	return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg,
39dab8d000SCezary Rojewski 				   sizeof(cfg), &dummy);
40dab8d000SCezary Rojewski }
41dab8d000SCezary Rojewski 
avs_dsp_delete_probe(struct avs_dev * adev)42dab8d000SCezary Rojewski static void avs_dsp_delete_probe(struct avs_dev *adev)
43dab8d000SCezary Rojewski {
44dab8d000SCezary Rojewski 	struct avs_module_entry mentry;
45750e384bSCezary Rojewski 	int ret;
46dab8d000SCezary Rojewski 
47750e384bSCezary Rojewski 	ret = avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry);
48750e384bSCezary Rojewski 	if (!ret)
49dab8d000SCezary Rojewski 		/* There is only ever one probe module instance. */
50dab8d000SCezary Rojewski 		avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0);
51dab8d000SCezary Rojewski }
52700462f5SCezary Rojewski 
avs_compr_get_host_stream(struct snd_compr_stream * cstream)53700462f5SCezary Rojewski static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream)
54700462f5SCezary Rojewski {
55700462f5SCezary Rojewski 	return cstream->runtime->private_data;
56700462f5SCezary Rojewski }
57700462f5SCezary Rojewski 
avs_probe_compr_open(struct snd_compr_stream * cstream,struct snd_soc_dai * dai)58700462f5SCezary Rojewski static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
59700462f5SCezary Rojewski {
60700462f5SCezary Rojewski 	struct avs_dev *adev = to_avs_dev(dai->dev);
61700462f5SCezary Rojewski 	struct hdac_bus *bus = &adev->base.core;
62700462f5SCezary Rojewski 	struct hdac_ext_stream *host_stream;
63700462f5SCezary Rojewski 
64700462f5SCezary Rojewski 	if (adev->extractor) {
65700462f5SCezary Rojewski 		dev_err(dai->dev, "Cannot open more than one extractor stream\n");
66700462f5SCezary Rojewski 		return -EEXIST;
67700462f5SCezary Rojewski 	}
68700462f5SCezary Rojewski 
69700462f5SCezary Rojewski 	host_stream = snd_hdac_ext_cstream_assign(bus, cstream);
70700462f5SCezary Rojewski 	if (!host_stream) {
71700462f5SCezary Rojewski 		dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n");
72700462f5SCezary Rojewski 		return -EBUSY;
73700462f5SCezary Rojewski 	}
74700462f5SCezary Rojewski 
75700462f5SCezary Rojewski 	adev->extractor = host_stream;
76700462f5SCezary Rojewski 	hdac_stream(host_stream)->curr_pos = 0;
77700462f5SCezary Rojewski 	cstream->runtime->private_data = host_stream;
78700462f5SCezary Rojewski 
79700462f5SCezary Rojewski 	return 0;
80700462f5SCezary Rojewski }
81700462f5SCezary Rojewski 
avs_probe_compr_free(struct snd_compr_stream * cstream,struct snd_soc_dai * dai)82700462f5SCezary Rojewski static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai)
83700462f5SCezary Rojewski {
84700462f5SCezary Rojewski 	struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
85700462f5SCezary Rojewski 	struct avs_dev *adev = to_avs_dev(dai->dev);
86700462f5SCezary Rojewski 	struct avs_probe_point_desc *desc;
87700462f5SCezary Rojewski 	/* Extractor node identifier. */
88700462f5SCezary Rojewski 	unsigned int vindex = INVALID_NODE_ID.vindex;
89700462f5SCezary Rojewski 	size_t num_desc;
90700462f5SCezary Rojewski 	int i, ret;
91700462f5SCezary Rojewski 
92700462f5SCezary Rojewski 	/* Disconnect all probe points. */
93700462f5SCezary Rojewski 	ret = avs_ipc_probe_get_points(adev, &desc, &num_desc);
94700462f5SCezary Rojewski 	if (ret) {
95700462f5SCezary Rojewski 		dev_err(dai->dev, "get probe points failed: %d\n", ret);
96700462f5SCezary Rojewski 		ret = AVS_IPC_RET(ret);
97700462f5SCezary Rojewski 		goto exit;
98700462f5SCezary Rojewski 	}
99700462f5SCezary Rojewski 
100700462f5SCezary Rojewski 	for (i = 0; i < num_desc; i++)
101700462f5SCezary Rojewski 		if (desc[i].node_id.vindex == vindex)
102700462f5SCezary Rojewski 			avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1);
103700462f5SCezary Rojewski 	kfree(desc);
104700462f5SCezary Rojewski 
105700462f5SCezary Rojewski exit:
106700462f5SCezary Rojewski 	if (adev->num_probe_streams) {
107700462f5SCezary Rojewski 		adev->num_probe_streams--;
108700462f5SCezary Rojewski 		if (!adev->num_probe_streams) {
109700462f5SCezary Rojewski 			avs_dsp_delete_probe(adev);
110700462f5SCezary Rojewski 			avs_dsp_enable_d0ix(adev);
111700462f5SCezary Rojewski 		}
112700462f5SCezary Rojewski 	}
113700462f5SCezary Rojewski 
114700462f5SCezary Rojewski 	snd_hdac_stream_cleanup(hdac_stream(host_stream));
115700462f5SCezary Rojewski 	hdac_stream(host_stream)->prepared = 0;
116700462f5SCezary Rojewski 	snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST);
117700462f5SCezary Rojewski 
118700462f5SCezary Rojewski 	snd_compr_free_pages(cstream);
119700462f5SCezary Rojewski 	adev->extractor = NULL;
120700462f5SCezary Rojewski 
121700462f5SCezary Rojewski 	return ret;
122700462f5SCezary Rojewski }
123700462f5SCezary Rojewski 
avs_probe_compr_set_params(struct snd_compr_stream * cstream,struct snd_compr_params * params,struct snd_soc_dai * dai)124700462f5SCezary Rojewski static int avs_probe_compr_set_params(struct snd_compr_stream *cstream,
125700462f5SCezary Rojewski 				      struct snd_compr_params *params, struct snd_soc_dai *dai)
126700462f5SCezary Rojewski {
127700462f5SCezary Rojewski 	struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
128700462f5SCezary Rojewski 	struct snd_compr_runtime *rtd = cstream->runtime;
129700462f5SCezary Rojewski 	struct avs_dev *adev = to_avs_dev(dai->dev);
130700462f5SCezary Rojewski 	/* compr params do not store bit depth, default to S32_LE. */
131700462f5SCezary Rojewski 	snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE;
132700462f5SCezary Rojewski 	unsigned int format_val;
133700462f5SCezary Rojewski 	int bps, ret;
134700462f5SCezary Rojewski 
135700462f5SCezary Rojewski 	hdac_stream(host_stream)->bufsize = 0;
136700462f5SCezary Rojewski 	hdac_stream(host_stream)->period_bytes = 0;
137700462f5SCezary Rojewski 	hdac_stream(host_stream)->format_val = 0;
138700462f5SCezary Rojewski 	cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG;
139700462f5SCezary Rojewski 	cstream->dma_buffer.dev.dev = adev->dev;
140700462f5SCezary Rojewski 
141700462f5SCezary Rojewski 	ret = snd_compr_malloc_pages(cstream, rtd->buffer_size);
142700462f5SCezary Rojewski 	if (ret < 0)
143700462f5SCezary Rojewski 		return ret;
144700462f5SCezary Rojewski 	bps = snd_pcm_format_physical_width(format);
145700462f5SCezary Rojewski 	if (bps < 0)
146700462f5SCezary Rojewski 		return bps;
147700462f5SCezary Rojewski 	format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out,
148700462f5SCezary Rojewski 						 format, bps, 0);
149700462f5SCezary Rojewski 	ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val);
150700462f5SCezary Rojewski 	if (ret < 0)
151700462f5SCezary Rojewski 		return ret;
152700462f5SCezary Rojewski 	ret = snd_hdac_stream_setup(hdac_stream(host_stream));
153700462f5SCezary Rojewski 	if (ret < 0)
154700462f5SCezary Rojewski 		return ret;
155700462f5SCezary Rojewski 
156700462f5SCezary Rojewski 	hdac_stream(host_stream)->prepared = 1;
157700462f5SCezary Rojewski 
158700462f5SCezary Rojewski 	if (!adev->num_probe_streams) {
159700462f5SCezary Rojewski 		union avs_connector_node_id node_id;
160700462f5SCezary Rojewski 
161700462f5SCezary Rojewski 		/* D0ix not allowed during probing. */
162700462f5SCezary Rojewski 		ret = avs_dsp_disable_d0ix(adev);
163700462f5SCezary Rojewski 		if (ret)
164700462f5SCezary Rojewski 			return ret;
165700462f5SCezary Rojewski 
166700462f5SCezary Rojewski 		node_id.vindex = hdac_stream(host_stream)->stream_tag - 1;
167700462f5SCezary Rojewski 		node_id.dma_type = AVS_DMA_HDA_HOST_INPUT;
168700462f5SCezary Rojewski 
169700462f5SCezary Rojewski 		ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes);
170700462f5SCezary Rojewski 		if (ret < 0) {
171700462f5SCezary Rojewski 			dev_err(dai->dev, "probe init failed: %d\n", ret);
172700462f5SCezary Rojewski 			avs_dsp_enable_d0ix(adev);
173700462f5SCezary Rojewski 			return ret;
174700462f5SCezary Rojewski 		}
175700462f5SCezary Rojewski 	}
176700462f5SCezary Rojewski 
177700462f5SCezary Rojewski 	adev->num_probe_streams++;
178700462f5SCezary Rojewski 	return 0;
179700462f5SCezary Rojewski }
180700462f5SCezary Rojewski 
avs_probe_compr_trigger(struct snd_compr_stream * cstream,int cmd,struct snd_soc_dai * dai)181700462f5SCezary Rojewski static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd,
182700462f5SCezary Rojewski 				   struct snd_soc_dai *dai)
183700462f5SCezary Rojewski {
184700462f5SCezary Rojewski 	struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
185700462f5SCezary Rojewski 	struct avs_dev *adev = to_avs_dev(dai->dev);
186700462f5SCezary Rojewski 	struct hdac_bus *bus = &adev->base.core;
187700462f5SCezary Rojewski 	unsigned long cookie;
188700462f5SCezary Rojewski 
189700462f5SCezary Rojewski 	if (!hdac_stream(host_stream)->prepared)
190700462f5SCezary Rojewski 		return -EPIPE;
191700462f5SCezary Rojewski 
192700462f5SCezary Rojewski 	switch (cmd) {
193700462f5SCezary Rojewski 	case SNDRV_PCM_TRIGGER_START:
194700462f5SCezary Rojewski 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
195700462f5SCezary Rojewski 	case SNDRV_PCM_TRIGGER_RESUME:
196700462f5SCezary Rojewski 		spin_lock_irqsave(&bus->reg_lock, cookie);
1974fe20d62SZhang Yiqun 		snd_hdac_stream_start(hdac_stream(host_stream));
198700462f5SCezary Rojewski 		spin_unlock_irqrestore(&bus->reg_lock, cookie);
199700462f5SCezary Rojewski 		break;
200700462f5SCezary Rojewski 
201700462f5SCezary Rojewski 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
202700462f5SCezary Rojewski 	case SNDRV_PCM_TRIGGER_SUSPEND:
203700462f5SCezary Rojewski 	case SNDRV_PCM_TRIGGER_STOP:
204700462f5SCezary Rojewski 		spin_lock_irqsave(&bus->reg_lock, cookie);
205700462f5SCezary Rojewski 		snd_hdac_stream_stop(hdac_stream(host_stream));
206700462f5SCezary Rojewski 		spin_unlock_irqrestore(&bus->reg_lock, cookie);
207700462f5SCezary Rojewski 		break;
208700462f5SCezary Rojewski 
209700462f5SCezary Rojewski 	default:
210700462f5SCezary Rojewski 		return -EINVAL;
211700462f5SCezary Rojewski 	}
212700462f5SCezary Rojewski 
213700462f5SCezary Rojewski 	return 0;
214700462f5SCezary Rojewski }
215700462f5SCezary Rojewski 
avs_probe_compr_pointer(struct snd_compr_stream * cstream,struct snd_compr_tstamp * tstamp,struct snd_soc_dai * dai)216700462f5SCezary Rojewski static int avs_probe_compr_pointer(struct snd_compr_stream *cstream,
217700462f5SCezary Rojewski 				   struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai)
218700462f5SCezary Rojewski {
219700462f5SCezary Rojewski 	struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream);
220700462f5SCezary Rojewski 	struct snd_soc_pcm_stream *pstream;
221700462f5SCezary Rojewski 
222700462f5SCezary Rojewski 	pstream = &dai->driver->capture;
223700462f5SCezary Rojewski 	tstamp->copied_total = hdac_stream(host_stream)->curr_pos;
224700462f5SCezary Rojewski 	tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates);
225700462f5SCezary Rojewski 
226700462f5SCezary Rojewski 	return 0;
227700462f5SCezary Rojewski }
228700462f5SCezary Rojewski 
avs_probe_compr_copy(struct snd_soc_component * comp,struct snd_compr_stream * cstream,char __user * buf,size_t count)229700462f5SCezary Rojewski static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream,
230700462f5SCezary Rojewski 				char __user *buf, size_t count)
231700462f5SCezary Rojewski {
232700462f5SCezary Rojewski 	struct snd_compr_runtime *rtd = cstream->runtime;
233700462f5SCezary Rojewski 	unsigned int offset, n;
234700462f5SCezary Rojewski 	void *ptr;
235700462f5SCezary Rojewski 	int ret;
236700462f5SCezary Rojewski 
237700462f5SCezary Rojewski 	if (count > rtd->buffer_size)
238700462f5SCezary Rojewski 		count = rtd->buffer_size;
239700462f5SCezary Rojewski 
240700462f5SCezary Rojewski 	div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset);
241700462f5SCezary Rojewski 	ptr = rtd->dma_area + offset;
242700462f5SCezary Rojewski 	n = rtd->buffer_size - offset;
243700462f5SCezary Rojewski 
244700462f5SCezary Rojewski 	if (count < n) {
245700462f5SCezary Rojewski 		ret = copy_to_user(buf, ptr, count);
246700462f5SCezary Rojewski 	} else {
247700462f5SCezary Rojewski 		ret = copy_to_user(buf, ptr, n);
248700462f5SCezary Rojewski 		ret += copy_to_user(buf + n, rtd->dma_area, count - n);
249700462f5SCezary Rojewski 	}
250700462f5SCezary Rojewski 
251700462f5SCezary Rojewski 	if (ret)
252700462f5SCezary Rojewski 		return count - ret;
253700462f5SCezary Rojewski 	return count;
254700462f5SCezary Rojewski }
255700462f5SCezary Rojewski 
256e9f51212SKuninori Morimoto static const struct snd_soc_cdai_ops avs_probe_cdai_ops = {
257700462f5SCezary Rojewski 	.startup = avs_probe_compr_open,
258700462f5SCezary Rojewski 	.shutdown = avs_probe_compr_free,
259700462f5SCezary Rojewski 	.set_params = avs_probe_compr_set_params,
260700462f5SCezary Rojewski 	.trigger = avs_probe_compr_trigger,
261700462f5SCezary Rojewski 	.pointer = avs_probe_compr_pointer,
262700462f5SCezary Rojewski };
263700462f5SCezary Rojewski 
264e9f51212SKuninori Morimoto static const struct snd_soc_dai_ops avs_probe_dai_ops = {
265e9f51212SKuninori Morimoto 	.compress_new = snd_soc_new_compress,
266e9f51212SKuninori Morimoto };
267e9f51212SKuninori Morimoto 
268700462f5SCezary Rojewski static const struct snd_compress_ops avs_probe_compress_ops = {
269700462f5SCezary Rojewski 	.copy = avs_probe_compr_copy,
270700462f5SCezary Rojewski };
271ed914a2aSCezary Rojewski 
272ed914a2aSCezary Rojewski static struct snd_soc_dai_driver probe_cpu_dais[] = {
273ed914a2aSCezary Rojewski {
274ed914a2aSCezary Rojewski 	.name = "Probe Extraction CPU DAI",
275e9f51212SKuninori Morimoto 	.cops = &avs_probe_cdai_ops,
276e9f51212SKuninori Morimoto 	.ops  = &avs_probe_dai_ops,
277ed914a2aSCezary Rojewski 	.capture = {
278ed914a2aSCezary Rojewski 		.stream_name = "Probe Extraction",
279ed914a2aSCezary Rojewski 		.channels_min = 1,
280ed914a2aSCezary Rojewski 		.channels_max = 8,
281ed914a2aSCezary Rojewski 		.rates = SNDRV_PCM_RATE_48000,
282ed914a2aSCezary Rojewski 		.rate_min = 48000,
283ed914a2aSCezary Rojewski 		.rate_max = 48000,
284ed914a2aSCezary Rojewski 	},
285ed914a2aSCezary Rojewski },
286ed914a2aSCezary Rojewski };
287ed914a2aSCezary Rojewski 
288ed914a2aSCezary Rojewski static const struct snd_soc_component_driver avs_probe_component_driver = {
289ed914a2aSCezary Rojewski 	.name			= "avs-probe-compr",
290ed914a2aSCezary Rojewski 	.compress_ops		= &avs_probe_compress_ops,
291ed914a2aSCezary Rojewski 	.module_get_upon_open	= 1, /* increment refcount when a stream is opened */
292ed914a2aSCezary Rojewski };
293ed914a2aSCezary Rojewski 
avs_probe_platform_register(struct avs_dev * adev,const char * name)294ed914a2aSCezary Rojewski int avs_probe_platform_register(struct avs_dev *adev, const char *name)
295ed914a2aSCezary Rojewski {
296ed914a2aSCezary Rojewski 	return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver,
297ed914a2aSCezary Rojewski 					  probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais));
298ed914a2aSCezary Rojewski }
299