1 // SPDX-License-Identifier: BSD-3-Clause
2 //
3 // Copyright(c) 2016 Intel Corporation. All rights reserved.
4 //
5 // Author: Liam Girdwood <liam.r.girdwood@linux.intel.com>
6 
7 #include <sof/audio/component_ext.h>
8 #include <sof/common.h>
9 #include <rtos/panic.h>
10 #include <rtos/interrupt.h>
11 #include <sof/ipc/msg.h>
12 #include <rtos/alloc.h>
13 #include <rtos/cache.h>
14 #include <sof/lib/memory.h>
15 #include <sof/list.h>
16 #include <rtos/sof.h>
17 #include <rtos/string.h>
18 #include <ipc/topology.h>
19 #include <errno.h>
20 #include <stdbool.h>
21 #include <stddef.h>
22 #include <stdint.h>
23 
24 #if defined(__XCC__)
25 #include <xtensa/config/core-isa.h>
26 #if XCHAL_HAVE_HIFI3 || XCHAL_HAVE_HIFI4
27 #define STREAMCOPY_HIFI3
28 #endif
29 #endif
30 
31 LOG_MODULE_REGISTER(component, CONFIG_SOF_LOG_LEVEL);
32 
33 static SHARED_DATA struct comp_driver_list cd;
34 
35 /* 7c42ce8b-0108-43d0-9137-56d660478c5f */
36 DECLARE_SOF_UUID("component", comp_uuid, 0x7c42ce8b, 0x0108, 0x43d0,
37 		 0x91, 0x37, 0x56, 0xd6, 0x60, 0x47, 0x8c, 0x5f);
38 
39 DECLARE_TR_CTX(comp_tr, SOF_UUID(comp_uuid), LOG_LEVEL_INFO);
40 
comp_register(struct comp_driver_info * drv)41 int comp_register(struct comp_driver_info *drv)
42 {
43 	struct comp_driver_list *drivers = comp_drivers_get();
44 	k_spinlock_key_t key;
45 
46 	key = k_spin_lock(&drivers->lock);
47 	list_item_prepend(&drv->list, &drivers->list);
48 	k_spin_unlock(&drivers->lock, key);
49 
50 	return 0;
51 }
52 
comp_unregister(struct comp_driver_info * drv)53 void comp_unregister(struct comp_driver_info *drv)
54 {
55 	struct comp_driver_list *drivers = comp_drivers_get();
56 	k_spinlock_key_t key;
57 
58 	key = k_spin_lock(&drivers->lock);
59 	list_item_del(&drv->list);
60 	k_spin_unlock(&drivers->lock, key);
61 }
62 
63 /* NOTE: Keep the component state diagram up to date:
64  * sof-docs/developer_guides/firmware/components/images/comp-dev-states.pu
65  */
66 
comp_set_state(struct comp_dev * dev,int cmd)67 int comp_set_state(struct comp_dev *dev, int cmd)
68 {
69 	int requested_state = comp_get_requested_state(cmd);
70 
71 	if (dev->state == requested_state) {
72 		comp_info(dev, "comp_set_state(), state already set to %u",
73 			  dev->state);
74 #ifdef CONFIG_IPC_MAJOR_4
75 		return 0;
76 #else
77 		return COMP_STATUS_STATE_ALREADY_SET;
78 #endif
79 	}
80 
81 	switch (cmd) {
82 	case COMP_TRIGGER_START:
83 		if (dev->state != COMP_STATE_PRE_ACTIVE) {
84 			comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_START",
85 				 dev->state);
86 			return -EINVAL;
87 		}
88 		break;
89 	case COMP_TRIGGER_RELEASE:
90 		if (dev->state != COMP_STATE_PRE_ACTIVE) {
91 			comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_RELEASE",
92 				 dev->state);
93 			return -EINVAL;
94 		}
95 		break;
96 	case COMP_TRIGGER_STOP:
97 		if (dev->state != COMP_STATE_ACTIVE &&
98 		    dev->state != COMP_STATE_PAUSED) {
99 			comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_STOP",
100 				 dev->state);
101 			return -EINVAL;
102 		}
103 		break;
104 	case COMP_TRIGGER_PAUSE:
105 		/* only support pausing for running */
106 		if (dev->state != COMP_STATE_ACTIVE) {
107 			comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_PAUSE",
108 				 dev->state);
109 			return -EINVAL;
110 		}
111 		break;
112 	case COMP_TRIGGER_RESET:
113 		/* reset always succeeds */
114 		if (dev->state == COMP_STATE_ACTIVE)
115 			comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_RESET",
116 				 dev->state);
117 		else if (dev->state == COMP_STATE_PAUSED)
118 			comp_info(dev, "comp_set_state(): state = %u, COMP_TRIGGER_RESET",
119 				  dev->state);
120 		break;
121 	case COMP_TRIGGER_PREPARE:
122 		if (dev->state != COMP_STATE_READY) {
123 			comp_err(dev, "comp_set_state(): wrong state = %u, COMP_TRIGGER_PREPARE",
124 				 dev->state);
125 			return -EINVAL;
126 		}
127 		break;
128 	case COMP_TRIGGER_PRE_START:
129 		if (dev->state != COMP_STATE_PREPARE) {
130 			comp_err(dev,
131 				 "comp_set_state(): wrong state = %u, COMP_TRIGGER_PRE_START",
132 				 dev->state);
133 			return -EINVAL;
134 		}
135 		break;
136 	case COMP_TRIGGER_PRE_RELEASE:
137 		if (dev->state != COMP_STATE_PAUSED) {
138 			comp_err(dev,
139 				 "comp_set_state(): wrong state = %u, COMP_TRIGGER_PRE_RELEASE",
140 				 dev->state);
141 			return -EINVAL;
142 		}
143 		break;
144 	default:
145 		return 0;
146 	}
147 
148 	dev->state = requested_state;
149 
150 	return 0;
151 }
152 
sys_comp_init(struct sof * sof)153 void sys_comp_init(struct sof *sof)
154 {
155 	sof->comp_drivers = platform_shared_get(&cd, sizeof(cd));
156 
157 	list_init(&sof->comp_drivers->list);
158 	k_spinlock_init(&sof->comp_drivers->lock);
159 }
160 
comp_get_copy_limits(struct comp_buffer __sparse_cache * source,struct comp_buffer __sparse_cache * sink,struct comp_copy_limits * cl)161 void comp_get_copy_limits(struct comp_buffer __sparse_cache *source,
162 			  struct comp_buffer __sparse_cache *sink,
163 			  struct comp_copy_limits *cl)
164 {
165 	cl->frames = audio_stream_avail_frames(&source->stream, &sink->stream);
166 	cl->source_frame_bytes = audio_stream_frame_bytes(&source->stream);
167 	cl->sink_frame_bytes = audio_stream_frame_bytes(&sink->stream);
168 	cl->source_bytes = cl->frames * cl->source_frame_bytes;
169 	cl->sink_bytes = cl->frames * cl->sink_frame_bytes;
170 }
171 
comp_get_copy_limits_frame_aligned(const struct comp_buffer __sparse_cache * source,const struct comp_buffer __sparse_cache * sink,struct comp_copy_limits * cl)172 void comp_get_copy_limits_frame_aligned(const struct comp_buffer __sparse_cache *source,
173 					const struct comp_buffer __sparse_cache *sink,
174 					struct comp_copy_limits *cl)
175 {
176 	cl->frames = audio_stream_avail_frames_aligned(&source->stream, &sink->stream);
177 	cl->source_frame_bytes = audio_stream_frame_bytes(&source->stream);
178 	cl->sink_frame_bytes = audio_stream_frame_bytes(&sink->stream);
179 	cl->source_bytes = cl->frames * cl->source_frame_bytes;
180 	cl->sink_bytes = cl->frames * cl->sink_frame_bytes;
181 }
182 
183 #ifdef STREAMCOPY_HIFI3
184 
audio_stream_copy(const struct audio_stream __sparse_cache * source,uint32_t ioffset,struct audio_stream __sparse_cache * sink,uint32_t ooffset,uint32_t samples)185 int audio_stream_copy(const struct audio_stream __sparse_cache *source, uint32_t ioffset,
186 		      struct audio_stream __sparse_cache *sink, uint32_t ooffset, uint32_t samples)
187 {
188 	int ssize = audio_stream_sample_bytes(source); /* src fmt == sink fmt */
189 	ae_int16x4 *src = (ae_int16x4 *)((int8_t *)source->r_ptr + ioffset * ssize);
190 	ae_int16x4 *dst = (ae_int16x4 *)((int8_t *)sink->w_ptr + ooffset * ssize);
191 	int shorts = samples * ssize >> 1;
192 	int shorts_src;
193 	int shorts_dst;
194 	int shorts_copied;
195 	int left, m, i;
196 	ae_int16x4 in_sample = AE_ZERO16();
197 	ae_valign inu = AE_ZALIGN64();
198 	ae_valign outu = AE_ZALIGN64();
199 
200 	/* copy with 16bit as the minimum unit since the minimum sample size is 16 bit*/
201 	while (shorts) {
202 		src = audio_stream_wrap(source, src);
203 		dst = audio_stream_wrap(sink, dst);
204 		shorts_src = audio_stream_samples_without_wrap_s16(source, src);
205 		shorts_dst = audio_stream_samples_without_wrap_s16(sink, dst);
206 		shorts_copied = AE_MIN_32_signed(shorts_src, shorts_dst);
207 		shorts_copied = AE_MIN_32_signed(shorts, shorts_copied);
208 		m = shorts_copied >> 2;
209 		left = shorts_copied & 0x03;
210 		inu = AE_LA64_PP(src);
211 		/* copy 4 * 16bit(8 bytes)per loop */
212 		for (i = 0; i < m; i++) {
213 			AE_LA16X4_IP(in_sample, inu, src);
214 			AE_SA16X4_IP(in_sample, outu, dst);
215 		}
216 		AE_SA64POS_FP(outu, dst);
217 
218 		/* process the left bits that less than 4 * 16 */
219 		for (i = 0; i < left ; i++) {
220 			AE_L16_IP(in_sample, (ae_int16 *)src, sizeof(ae_int16));
221 			AE_S16_0_IP(in_sample, (ae_int16 *)dst, sizeof(ae_int16));
222 		}
223 		shorts -= shorts_copied;
224 	}
225 	return samples;
226 }
227 
228 #else
229 
audio_stream_copy(const struct audio_stream __sparse_cache * source,uint32_t ioffset,struct audio_stream __sparse_cache * sink,uint32_t ooffset,uint32_t samples)230 int audio_stream_copy(const struct audio_stream __sparse_cache *source, uint32_t ioffset,
231 		      struct audio_stream __sparse_cache *sink, uint32_t ooffset, uint32_t samples)
232 {
233 	int ssize = audio_stream_sample_bytes(source); /* src fmt == sink fmt */
234 	uint8_t *src = audio_stream_wrap(source, (uint8_t *)source->r_ptr + ioffset * ssize);
235 	uint8_t *snk = audio_stream_wrap(sink, (uint8_t *)sink->w_ptr + ooffset * ssize);
236 	size_t bytes = samples * ssize;
237 	size_t bytes_src;
238 	size_t bytes_snk;
239 	size_t bytes_copied;
240 
241 	while (bytes) {
242 		bytes_src = audio_stream_bytes_without_wrap(source, src);
243 		bytes_snk = audio_stream_bytes_without_wrap(sink, snk);
244 		bytes_copied = MIN(bytes_src, bytes_snk);
245 		bytes_copied = MIN(bytes, bytes_copied);
246 		memcpy(snk, src, bytes_copied);
247 		bytes -= bytes_copied;
248 		src = audio_stream_wrap(source, src + bytes_copied);
249 		snk = audio_stream_wrap(sink, snk + bytes_copied);
250 	}
251 
252 	return samples;
253 }
254 
255 #endif
256 
audio_stream_copy_from_linear(const void * linear_source,int ioffset,struct audio_stream __sparse_cache * sink,int ooffset,unsigned int samples)257 void audio_stream_copy_from_linear(const void *linear_source, int ioffset,
258 				   struct audio_stream __sparse_cache *sink, int ooffset,
259 				   unsigned int samples)
260 {
261 	int ssize = audio_stream_sample_bytes(sink); /* src fmt == sink fmt */
262 	uint8_t *src = (uint8_t *)linear_source + ioffset * ssize;
263 	uint8_t *snk = audio_stream_wrap(sink, (uint8_t *)sink->w_ptr + ooffset * ssize);
264 	size_t bytes = samples * ssize;
265 	size_t bytes_snk;
266 	size_t bytes_copied;
267 
268 	while (bytes) {
269 		bytes_snk = audio_stream_bytes_without_wrap(sink, snk);
270 		bytes_copied = MIN(bytes, bytes_snk);
271 		memcpy(snk, src, bytes_copied);
272 		bytes -= bytes_copied;
273 		src += bytes_copied;
274 		snk = audio_stream_wrap(sink, snk + bytes_copied);
275 	}
276 }
277 
audio_stream_copy_to_linear(const struct audio_stream __sparse_cache * source,int ioffset,void * linear_sink,int ooffset,unsigned int samples)278 void audio_stream_copy_to_linear(const struct audio_stream __sparse_cache *source, int ioffset,
279 				 void *linear_sink, int ooffset, unsigned int samples)
280 {
281 	int ssize = audio_stream_sample_bytes(source); /* src fmt == sink fmt */
282 	uint8_t *src = audio_stream_wrap(source, (uint8_t *)source->r_ptr + ioffset * ssize);
283 	uint8_t *snk = (uint8_t *)linear_sink + ooffset * ssize;
284 	size_t bytes = samples * ssize;
285 	size_t bytes_src;
286 	size_t bytes_copied;
287 
288 	while (bytes) {
289 		bytes_src = audio_stream_bytes_without_wrap(source, src);
290 		bytes_copied = MIN(bytes, bytes_src);
291 		memcpy(snk, src, bytes_copied);
292 		bytes -= bytes_copied;
293 		src = audio_stream_wrap(source, src + bytes_copied);
294 		snk += bytes_copied;
295 	}
296 }
297 
298 /** See comp_ops::copy */
comp_copy(struct comp_dev * dev)299 int comp_copy(struct comp_dev *dev)
300 {
301 	int ret = 0;
302 
303 	assert(dev->drv->ops.copy);
304 
305 	/* copy only if we are the owner of the LL component */
306 	if (dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_LL &&
307 	    cpu_is_me(dev->ipc_config.core)) {
308 #if CONFIG_PERFORMANCE_COUNTERS
309 		perf_cnt_init(&dev->pcd);
310 #endif
311 
312 		ret = dev->drv->ops.copy(dev);
313 
314 #if CONFIG_PERFORMANCE_COUNTERS
315 		perf_cnt_stamp(&dev->pcd, perf_trace_null, dev);
316 		perf_cnt_average(&dev->pcd, comp_perf_avg_info, dev);
317 #endif
318 	}
319 
320 	return ret;
321 }
322