Components Overview

A component adds processing of the audio signal to a pipeline running on the DSP. An instance of the component, called a component device (components are implemented in the driver-device model), chained with other component devices build an audio processing path organized as a pipeline.

Component Driver

Every component must implement a driver (see the comp_driver) which is responsible for creation of the instances by handling new component requests coming from the command handlers.

The driver must be registered on the system component driver list, by calling comp_register(comp_driver *) and providing unique component id in order to receive the requests.

Each component driver declares its unique type that is later used by the uAPI to create a component of that type. It also provides an entry point to the component ops implementation.

package component {

   class comp_driver {
   	type : uint32_t
   	module_id : uint32_t
   	ops : comp_ops
   }
   hide comp_driver methods

   class comp_ops
   hide comp_ops methods
   hide comp_ops attributes

   class comp_dev {
   	drv : comp_driver*

   	<i>more params goes here ...</i>

   	params : sof_ipc_stream_params

   	bsource_list
   	bsink_list
   	private: void*
   	comp : sof_ipc_comp
   }
   hide comp_dev methods

   comp_driver --> "ops" comp_ops : provides

   comp_driver -> comp_dev : creates in comp_new()
}

Figure 37 Component Driver

Creating a Component Device

When a new component device is requested, system comp_new() function finds the driver registered with the requested unique component type and calls new() function pointed by the registered driver’s data in order to instantiate the device.

Entry called to create a new component device:

struct comp_dev* comp_new(sof_ipc_comp *comp);

actor "ipc/ipc_comp_new()" as c
participant "audio/component" as comp
participant "comp_driver" as drv
participant "lib/heap" as heap

c -> comp : comp_new(comp : sof_ipc_comp)
	activate comp
	comp -> comp : get_drv(comp->type) : drv
	comp -> drv : drv->ops.new(comp)
  		activate drv
  		drv -> heap : rzalloc(Runtime)
  		drv <-- heap
	comp <-- drv : comp_dev*
	deactivate drv

	comp -> comp : init component
c <-- comp : comp_dev*
deactivate comp

Handling the Component Device State

Utility function comp_set_state() should be called a component code at the beginning of its state transition to verify whether the trigger is valid in the current state and set a new state accordingly to the state diagram.

hide empty description
[*] -right-> READY : <b>comp_ops.new()</b>

READY -right-> PREPARE : <b>prepare</b>

PREPARE --> ACTIVE : <b>start</b>
PAUSED --> ACTIVE : start

ACTIVE --> PREPARE : stop, xrun
PAUSED --> PREPARE : stop, xrun

ACTIVE -> PAUSED : pause

PAUSED --> ACTIVE : release

PREPARE --> READY : reset

READY
This is an initial state of a component device once it is created.
PREPARE
Transition to this state is usually invoked internally by the component’s implementation of the prepare() handler.
ACTIVE, PAUSE
Transitions to these states is caused by external trigger passed to the component’s implementation of the trigger() handler.

Implementing Component API (comp_ops)

Every component implements comp_ops API. All functions, except for new() and free() return 0 for success, negative values for errors and 1 to stop the pipeline walk operation.

Note

Some API functions are mandatory for specific component types only since the infrastructure code calls them selectively based on the target component type.

For instance dai_config() is called for SOF_COMP_DAI and SOF_COMP_SG_DAI only and there is no point in implementing this handler in case of a component of any other type.

class comp_ops {
	new(sof_ipc_comp*)
	free()
	params()
	<i>dai_config()</i>
	cmd()
	trigger()
	prepare()
	reset()
	copy()
	<i>host_buffer()</i>
	position()
}
hide comp_ops attributes

Figure 38 Component API