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  *         Keyon Jie <yang.jie@linux.intel.com>
7  */
8 
9 /**
10  * \file include/sof/audio/component_ext.h
11  * \brief Component API for Infrastructure
12  * \author Liam Girdwood <liam.r.girdwood@linux.intel.com>
13  * \author Keyon Jie <yang.jie@linux.intel.com>
14  */
15 
16 #ifndef __SOF_AUDIO_COMPONENT_INT_H__
17 #define __SOF_AUDIO_COMPONENT_INT_H__
18 
19 #include <sof/audio/component.h>
20 #include <rtos/idc.h>
21 #include <sof/list.h>
22 #include <ipc/topology.h>
23 #include <kernel/abi.h>
24 #include <stdbool.h>
25 
26 /** \addtogroup component_api_helpers Component Mgmt API
27  *  @{
28  */
29 
30 /** \brief Holds list of registered components' drivers */
31 struct comp_driver_list {
32 	struct list_item list;	/**< list of component drivers */
33 	struct k_spinlock lock;	/**< list lock */
34 };
35 
36 /** \brief Retrieves the component device buffer list. */
37 #define comp_buffer_list(comp, dir) \
38 	((dir) == PPL_DIR_DOWNSTREAM ? &(comp)->bsink_list :	\
39 	 &(comp)->bsource_list)
40 
41 /** See comp_ops::new */
42 #if CONFIG_IPC_MAJOR_3
43 struct comp_dev *comp_new(struct sof_ipc_comp *comp);
44 #elif CONFIG_IPC_MAJOR_4
45 struct comp_dev *comp_new_ipc4(struct ipc4_module_init_instance *module_init);
46 #endif
47 
48 /** See comp_ops::free */
comp_free(struct comp_dev * dev)49 static inline void comp_free(struct comp_dev *dev)
50 {
51 	assert(dev->drv->ops.free);
52 
53 	/* free task if shared component or DP task*/
54 	if ((dev->is_shared || dev->ipc_config.proc_domain == COMP_PROCESSING_DOMAIN_DP) &&
55 	    dev->task) {
56 		schedule_task_free(dev->task);
57 		rfree(dev->task);
58 	}
59 
60 	dev->drv->ops.free(dev);
61 }
62 
63 /**
64  * Parameter init for component on other core.
65  * @param dev Component device.
66  * @param params Parameters to be set.
67  * @return 0 if succeeded, error code otherwise.
68  */
comp_params_remote(struct comp_dev * dev,struct sof_ipc_stream_params * params)69 static inline int comp_params_remote(struct comp_dev *dev,
70 				     struct sof_ipc_stream_params *params)
71 {
72 	struct idc_msg msg = { IDC_MSG_PARAMS, IDC_MSG_PARAMS_EXT(dev->ipc_config.id),
73 		dev->ipc_config.core, sizeof(*params), params, };
74 
75 	return idc_send_msg(&msg, IDC_BLOCKING);
76 }
77 
78 /** See comp_ops::params */
comp_params(struct comp_dev * dev,struct sof_ipc_stream_params * params)79 static inline int comp_params(struct comp_dev *dev,
80 			      struct sof_ipc_stream_params *params)
81 {
82 	int ret = 0;
83 
84 	if (dev->is_shared && !cpu_is_me(dev->ipc_config.core)) {
85 		ret = comp_params_remote(dev, params);
86 	} else {
87 		if (dev->drv->ops.params) {
88 			ret = dev->drv->ops.params(dev, params);
89 		} else {
90 			/* not defined, run the default handler */
91 			ret = comp_verify_params(dev, 0, params);
92 
93 /* BugLink: https://github.com/zephyrproject-rtos/zephyr/issues/43786
94  * TODO: Remove this once the bug gets fixed.
95  */
96 #ifndef __ZEPHYR__
97 			if (ret < 0)
98 				comp_err(dev, "pcm params verification failed");
99 #endif
100 		}
101 	}
102 
103 	return ret;
104 }
105 
106 /** See comp_ops::dai_get_hw_params */
comp_dai_get_hw_params(struct comp_dev * dev,struct sof_ipc_stream_params * params,int dir)107 static inline int comp_dai_get_hw_params(struct comp_dev *dev,
108 					 struct sof_ipc_stream_params *params,
109 					 int dir)
110 {
111 	if (dev->drv->ops.dai_get_hw_params)
112 		return dev->drv->ops.dai_get_hw_params(dev, params, dir);
113 
114 	return -EINVAL;
115 }
116 
117 /** See comp_ops::cmd */
comp_cmd(struct comp_dev * dev,int cmd,void * data,int max_data_size)118 static inline int comp_cmd(struct comp_dev *dev, int cmd, void *data,
119 			   int max_data_size)
120 {
121 	LOG_MODULE_DECLARE(component, CONFIG_SOF_LOG_LEVEL);
122 
123 	struct sof_ipc_ctrl_data *cdata = ASSUME_ALIGNED(data, 4);
124 
125 	if (cmd == COMP_CMD_SET_DATA &&
126 	    (cdata->data->magic != SOF_ABI_MAGIC ||
127 	     SOF_ABI_VERSION_INCOMPATIBLE(SOF_ABI_VERSION, cdata->data->abi))) {
128 		comp_err(dev, "comp_cmd(): invalid version, data->magic = %u, data->abi = %u",
129 			 cdata->data->magic, cdata->data->abi);
130 		return -EINVAL;
131 	}
132 
133 	if (dev->drv->ops.cmd)
134 		return dev->drv->ops.cmd(dev, cmd, data, max_data_size);
135 
136 	return -EINVAL;
137 }
138 
139 /**
140  * Runs comp_ops::trigger on the core the target component is assigned to.
141  */
comp_trigger_remote(struct comp_dev * dev,int cmd)142 static inline int comp_trigger_remote(struct comp_dev *dev, int cmd)
143 {
144 	struct idc_msg msg = { IDC_MSG_TRIGGER,
145 		IDC_MSG_TRIGGER_EXT(dev->ipc_config.id), dev->ipc_config.core, sizeof(cmd),
146 		&cmd, };
147 
148 	return idc_send_msg(&msg, IDC_BLOCKING);
149 }
150 
comp_trigger_local(struct comp_dev * dev,int cmd)151 static inline int comp_trigger_local(struct comp_dev *dev, int cmd)
152 {
153 	int ret;
154 
155 	ret = dev->drv->ops.trigger(dev, cmd);
156 
157 	/* start a thread in case of shared component or DP scheduling */
158 	if (dev->task) {
159 		/* schedule or cancel task */
160 		switch (cmd) {
161 		case COMP_TRIGGER_START:
162 		case COMP_TRIGGER_RELEASE:
163 			schedule_task(dev->task, 0, dev->period);
164 			break;
165 		case COMP_TRIGGER_XRUN:
166 		case COMP_TRIGGER_PAUSE:
167 		case COMP_TRIGGER_STOP:
168 			schedule_task_cancel(dev->task);
169 			break;
170 		}
171 	}
172 
173 	return ret;
174 }
175 
176 /** See comp_ops::trigger */
comp_trigger(struct comp_dev * dev,int cmd)177 static inline int comp_trigger(struct comp_dev *dev, int cmd)
178 {
179 	int ret;
180 	assert(dev->drv->ops.trigger);
181 
182 	if (dev->is_shared && !cpu_is_me(dev->ipc_config.core))
183 		ret = comp_trigger_remote(dev, cmd);
184 	else
185 		ret = comp_trigger_local(dev, cmd);
186 
187 	return ret;
188 }
189 
190 /** Runs comp_ops::prepare on the target component's core */
comp_prepare_remote(struct comp_dev * dev)191 static inline int comp_prepare_remote(struct comp_dev *dev)
192 {
193 	struct idc_msg msg = { IDC_MSG_PREPARE,
194 		IDC_MSG_PREPARE_EXT(dev->ipc_config.id), dev->ipc_config.core, };
195 
196 	return idc_send_msg(&msg, IDC_BLOCKING);
197 }
198 
199 /** See comp_ops::prepare */
comp_prepare(struct comp_dev * dev)200 static inline int comp_prepare(struct comp_dev *dev)
201 {
202 	if (dev->drv->ops.prepare)
203 		return (dev->is_shared && !cpu_is_me(dev->ipc_config.core)) ?
204 			comp_prepare_remote(dev) : dev->drv->ops.prepare(dev);
205 
206 	return 0;
207 }
208 
209 int comp_copy(struct comp_dev *dev);
210 
211 
212 /** See comp_ops::get_attribute */
comp_get_attribute(struct comp_dev * dev,uint32_t type,void * value)213 static inline int comp_get_attribute(struct comp_dev *dev, uint32_t type,
214 				     void *value)
215 {
216 	if (dev->drv->ops.get_attribute)
217 		return dev->drv->ops.get_attribute(dev, type, value);
218 
219 	return 0;
220 }
221 
222 /** See comp_ops::set_attribute */
comp_set_attribute(struct comp_dev * dev,uint32_t type,void * value)223 static inline int comp_set_attribute(struct comp_dev *dev, uint32_t type,
224 				     void *value)
225 {
226 	if (dev->drv->ops.set_attribute)
227 		return dev->drv->ops.set_attribute(dev, type, value);
228 
229 	return 0;
230 }
231 
232 /** Runs comp_ops::reset on the target component's core */
comp_reset_remote(struct comp_dev * dev)233 static inline int comp_reset_remote(struct comp_dev *dev)
234 {
235 	struct idc_msg msg = { IDC_MSG_RESET,
236 		IDC_MSG_RESET_EXT(dev->ipc_config.id), dev->ipc_config.core, };
237 
238 	return idc_send_msg(&msg, IDC_BLOCKING);
239 }
240 
241 /**
242  * Component reset and free runtime resources.
243  * @param dev Component device.
244  * @return 0 if succeeded, error code otherwise.
245  */
comp_reset(struct comp_dev * dev)246 static inline int comp_reset(struct comp_dev *dev)
247 {
248 	if (dev->drv->ops.reset)
249 		return (dev->is_shared && !cpu_is_me(dev->ipc_config.core)) ?
250 			comp_reset_remote(dev) : dev->drv->ops.reset(dev);
251 
252 	return 0;
253 }
254 
255 /** See comp_ops::dai_config */
comp_dai_config(struct comp_dev * dev,struct ipc_config_dai * config,const void * spec_config)256 static inline int comp_dai_config(struct comp_dev *dev, struct ipc_config_dai *config,
257 				  const void *spec_config)
258 {
259 	if (dev->drv->ops.dai_config)
260 		return dev->drv->ops.dai_config(dev, config, spec_config);
261 
262 	return 0;
263 }
264 
265 /** See comp_ops::position */
comp_position(struct comp_dev * dev,struct sof_ipc_stream_posn * posn)266 static inline int comp_position(struct comp_dev *dev,
267 				struct sof_ipc_stream_posn *posn)
268 {
269 	if (dev->drv->ops.position)
270 		return dev->drv->ops.position(dev, posn);
271 
272 	return 0;
273 }
274 
275 /**
276  * Allocates and initializes audio component list.
277  * To be called once at boot time.
278  */
279 void sys_comp_init(struct sof *sof);
280 
281 /**
282  * Checks if two component devices belong to the same parent pipeline.
283  * @param current Component device.
284  * @param previous Another component device.
285  * @return 1 if children of the same pipeline, 0 otherwise.
286  */
comp_is_single_pipeline(struct comp_dev * current,struct comp_dev * previous)287 static inline int comp_is_single_pipeline(struct comp_dev *current,
288 					  struct comp_dev *previous)
289 {
290 	return dev_comp_pipe_id(current) == dev_comp_pipe_id(previous);
291 }
292 
293 /**
294  * Checks if component device is active.
295  * @param current Component device.
296  * @return 1 if active, 0 otherwise.
297  */
comp_is_active(struct comp_dev * current)298 static inline int comp_is_active(struct comp_dev *current)
299 {
300 	return current->state == COMP_STATE_ACTIVE;
301 }
302 
303 /**
304  * Returns component state based on requested command.
305  * @param cmd Request command.
306  * @return Component state.
307  */
comp_get_requested_state(int cmd)308 static inline int comp_get_requested_state(int cmd)
309 {
310 	int state = COMP_STATE_INIT;
311 
312 	switch (cmd) {
313 	case COMP_TRIGGER_START:
314 	case COMP_TRIGGER_RELEASE:
315 		state = COMP_STATE_ACTIVE;
316 		break;
317 	case COMP_TRIGGER_PREPARE:
318 	case COMP_TRIGGER_STOP:
319 		state = COMP_STATE_PREPARE;
320 		break;
321 	case COMP_TRIGGER_PAUSE:
322 		state = COMP_STATE_PAUSED;
323 		break;
324 	case COMP_TRIGGER_XRUN:
325 	case COMP_TRIGGER_RESET:
326 		state = COMP_STATE_READY;
327 		break;
328 	case COMP_TRIGGER_PRE_START:
329 	case COMP_TRIGGER_PRE_RELEASE:
330 		state = COMP_STATE_PRE_ACTIVE;
331 		break;
332 	}
333 
334 	return state;
335 }
336 
337 /**
338  * \brief Returns endpoint type of given component.
339  * @param dev Component device
340  * @return Endpoint type, one of comp_endpoint_type.
341  */
comp_get_endpoint_type(struct comp_dev * dev)342 static inline int comp_get_endpoint_type(struct comp_dev *dev)
343 {
344 	switch (dev_comp_type(dev)) {
345 	case SOF_COMP_HOST:
346 		return COMP_ENDPOINT_HOST;
347 	case SOF_COMP_DAI:
348 		return COMP_ENDPOINT_DAI;
349 	default:
350 		return COMP_ENDPOINT_NODE;
351 	}
352 }
353 
354 #if CONFIG_IPC_MAJOR_4
355 #include <ipc4/copier.h>
comp_get_dai(struct comp_dev * parent,int index)356 static inline struct comp_dev *comp_get_dai(struct comp_dev *parent, int index)
357 {
358 	struct copier_data *cd = comp_get_drvdata(parent);
359 
360 	if (index >= ARRAY_SIZE(cd->endpoint))
361 		return NULL;
362 
363 	return cd->endpoint[index];
364 }
365 #elif CONFIG_IPC_MAJOR_3
comp_get_dai(struct comp_dev * parent,int index)366 static inline struct comp_dev *comp_get_dai(struct comp_dev *parent, int index)
367 {
368 	return parent;
369 }
370 #else
371 #error Unknown IPC major version
372 #endif
373 
374 /**
375  * Called to check whether component schedules its pipeline.
376  * @param dev Component device.
377  * @return True if this is scheduling component, false otherwise.
378  */
comp_is_scheduling_source(struct comp_dev * dev)379 static inline bool comp_is_scheduling_source(struct comp_dev *dev)
380 {
381 	return dev == dev->pipeline->sched_comp;
382 }
383 
384 /**
385  * Called to mark component as shared between cores
386  * @param dev Component device.
387  */
comp_make_shared(struct comp_dev * dev)388 static inline void comp_make_shared(struct comp_dev *dev)
389 {
390 	dev->is_shared = true;
391 }
392 
comp_drivers_get(void)393 static inline struct comp_driver_list *comp_drivers_get(void)
394 {
395 	return sof_get()->comp_drivers;
396 }
397 
comp_bind(struct comp_dev * dev,void * data)398 static inline int comp_bind(struct comp_dev *dev, void *data)
399 {
400 	int ret = 0;
401 
402 	if (dev->drv->ops.bind)
403 		ret = dev->drv->ops.bind(dev, data);
404 
405 	return ret;
406 }
407 
comp_unbind(struct comp_dev * dev,void * data)408 static inline int comp_unbind(struct comp_dev *dev, void *data)
409 {
410 	int ret = 0;
411 
412 	if (dev->drv->ops.unbind)
413 		ret = dev->drv->ops.unbind(dev, data);
414 
415 	return ret;
416 }
417 
comp_get_total_data_processed(struct comp_dev * dev,uint32_t stream_no,bool input)418 static inline uint64_t comp_get_total_data_processed(struct comp_dev *dev, uint32_t stream_no,
419 						     bool input)
420 {
421 	uint64_t ret = 0;
422 
423 	if (dev->drv->ops.get_total_data_processed)
424 		ret = dev->drv->ops.get_total_data_processed(dev, stream_no, input);
425 
426 	return ret;
427 }
428 
429 /** @}*/
430 
431 #endif /* __SOF_AUDIO_COMPONENT_INT_H__ */
432