Part II - Modifying the Topology & Driver

Topology

Create tools/topology/m4/amp.m4 and add the following Amp widget definition. Note the highlighted line containing the definition of the type of your new processing component. The Driver section refers to it later.

Code Block 4 tools/topology/m4/amp.m4
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
divert(-1)

dnl Define macro for example Amp widget

dnl AMP(name)
define(`N_AMP', `AMP'PIPELINE_ID`.'$1)

dnl W_AMP(name, format, periods_sink, periods_source, kcontrols_list)
define(`W_AMP',
`SectionVendorTuples."'N_AMP($1)`_tuples_w" {'
`    tokens "sof_comp_tokens"'
`    tuples."word" {'
`            SOF_TKN_COMP_PERIOD_SINK_COUNT'         STR($3)
`            SOF_TKN_COMP_PERIOD_SOURCE_COUNT'       STR($4)
`            SOF_TKN_COMP_CORE_ID'                   STR($5)
`    }'
`}'
`SectionData."'N_AMP($1)`_data_w" {'
`    tuples "'N_AMP($1)`_tuples_w"'
`}'
`SectionVendorTuples."'N_AMP($1)`_tuples_str" {'
`    tokens "sof_comp_tokens"'
`    tuples."string" {'
`            SOF_TKN_COMP_FORMAT'    STR($2)
`    }'
`}'
`SectionData."'N_AMP($1)`_data_str" {'
`    tuples "'N_AMP($1)`_tuples_str"'
`}'
`SectionVendorTuples."'N_AMP($1)`_tuples_str_type" {'
`    tokens "sof_process_tokens"'
`    tuples."string" {'
`            SOF_TKN_PROCESS_TYPE'   "AMP"
`    }'
`}'
`SectionData."'N_AMP($1)`_data_str_type" {'
`    tuples "'N_AMP($1)`_tuples_str_type"'
`}'
`SectionWidget."'N_AMP($1)`" {'
`    index "'PIPELINE_ID`"'
`    type "effect"'
`    no_pm "true"'
`    data ['
`            "'N_AMP($1)`_data_w"'
`            "'N_AMP($1)`_data_str"'
`            "'N_AMP($1)`_data_str_type"'
`    ]'
`    bytes ['
             $6
`    ]'
`}')

divert(0)dnl

Add a definition of parameters and specify default values for them (handling parameters in the FW code is discussed in the next lesson but you prepare a complete topology upfront). Create tools/topology/amp_bytes.m4 and add the following code.

Note the size of the parameters data and the data highlighted (two 32-bit number set to 1 to unmute both channels by default, little-endian byte ordering). The data begins with struct sof_abi_hdr content, note the SOF magic number in line 3 and the ABI version in line 6. The latter must be set to a version compatible with the SOF stack.

Code Block 5 tools/topology/amp_bytes.m4
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# AMP Example - Parameters
CONTROLBYTES_PRIV(AMP_priv,
`       bytes "0x53,0x4f,0x46,0x00,'
`       0x00,0x00,0x00,0x00,'
`       0x08,0x00,0x00,0x00,'
`       0x00,0x00,0x00,0x03,'
`       0x00,0x00,0x00,0x00,''
`       0x00,0x00,0x00,0x00,'
`       0x00,0x00,0x00,0x00,'
`       0x00,0x00,0x00,0x00,'
`       0x01,0x00,0x00,0x00,'
`       0x01,0x00,0x00,0x00"'
)

Add the Amp widget to a playback pipeline. Create a copy of tools/topology/sof/pipe-volume-playback.m4 and save it as tools/topology/sof/pipe-amp-volume-playback.m4. Add the definitions in your copy as highlighted below.

Code Block 6 tools/topology/sof/pipe-amp-volume-playback.m4
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
# Low Latency Passthrough with volume Pipeline and PCM
#
# Pipeline Endpoints for connection are :-
#
#  host PCM_P --> B0 --> Amp -> B1 -> Volume 0 --> B2 --> sink DAI0

# Include topology builder
include(`utils.m4')
include(`buffer.m4')
include(`pcm.m4')
include(`pga.m4')
include(`dai.m4')
include(`mixercontrol.m4')
include(`bytecontrol.m4')
include(`pipeline.m4')
include(`amp.m4')

#
# Controls
#
# Volume Mixer control with max value of 32
C_CONTROLMIXER(Master Playback Volume, PIPELINE_ID,
     CONTROLMIXER_OPS(volsw, 256 binds the mixer control to volume get/put handlers, 256, 256),
     CONTROLMIXER_MAX(, 32),
     false,
     CONTROLMIXER_TLV(TLV 32 steps from -64dB to 0dB for 2dB, vtlv_m64s2),
     Channel register and shift for Front Left/Right,
     LIST(`  ', KCONTROL_CHANNEL(FL, 1, 0), KCONTROL_CHANNEL(FR, 1, 1)))

#
# Volume configuration
#

define(DEF_PGA_TOKENS, concat(`pga_tokens_', PIPELINE_ID))
define(DEF_PGA_CONF, concat(`pga_conf_', PIPELINE_ID))

W_VENDORTUPLES(DEF_PGA_TOKENS, sof_volume_tokens,
LIST(`               ', `SOF_TKN_VOLUME_RAMP_STEP_TYPE       "0"'
     `               ', `SOF_TKN_VOLUME_RAMP_STEP_MS         "250"'))

W_DATA(DEF_PGA_CONF, DEF_PGA_TOKENS)

# Amp Parameters
include(`amp_bytes.m4')

# Amp Bytes control with max value of 140
# The max size needs to also take into account the space required to hold the control data IPC message
# struct sof_ipc_ctrl_data requires 92 bytes
# AMP priv in amp_bytes.m4 (ABI header (32 bytes) + 2 dwords) requires 40 bytes
# Therefore at least 132 bytes are required for this kcontrol
# Any value lower than that would end up in a topology load error
C_CONTROLBYTES(AMP, PIPELINE_ID,
     CONTROLBYTES_OPS(bytes, 258 binds the control to bytes get/put handlers, 258, 258),
     CONTROLBYTES_EXTOPS(258 binds the control to bytes get/put handlers, 258, 258),
     , , ,
     CONTROLBYTES_MAX(, 140),
     ,
     AMP_priv)

#
# Components and Buffers
#

# Host "Passthrough Playback" PCM
# with 2 sink and 0 source periods
W_PCM_PLAYBACK(PCM_ID, Passthrough Playback, 2, 0, SCHEDULE_CORE)


# "Volume" has 2 source and 2 sink periods
W_PGA(0, PIPELINE_FORMAT, DAI_PERIODS, 2, DEF_PGA_CONF, SCHEDULE_CORE,
     LIST(`          ', "PIPELINE_ID Master Playback Volume"))

# "Amp" has 2 sink periods and 2 source periods
W_AMP(0, PIPELINE_FORMAT, 2, 2, SCHEDULE_CORE,
     LIST(`           ', "AMP"))

# Playback Buffers
W_BUFFER(0, COMP_BUFFER_SIZE(2,
     COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)),
     PLATFORM_HOST_MEM_CAP)
W_BUFFER(1, COMP_BUFFER_SIZE(2,
     COMP_SAMPLE_SIZE(PIPELINE_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)),
     PLATFORM_HOST_MEM_CAP)
W_BUFFER(2, COMP_BUFFER_SIZE(DAI_PERIODS,
     COMP_SAMPLE_SIZE(DAI_FORMAT), PIPELINE_CHANNELS, COMP_PERIOD_FRAMES(PCM_MAX_RATE, SCHEDULE_PERIOD)),
     PLATFORM_DAI_MEM_CAP)

#
# Pipeline Graph
#
#  host PCM_P --> B0 --> Amp -> B1 --> Volume 0 --> B2 --> sink DAI0

P_GRAPH(pipe-amp-volume-playback-PIPELINE_ID, PIPELINE_ID,
     LIST(`          ',
     `dapm(N_BUFFER(0), N_PCMP(PCM_ID))',
     `dapm(N_AMP(0), N_BUFFER(0))',
     `dapm(N_BUFFER(1), N_AMP(0))',
     `dapm(N_PGA(0), N_BUFFER(1))',
     `dapm(N_BUFFER(2), N_PGA(0))'))

#
# Pipeline Source and Sinks
#
indir(`define', concat(`PIPELINE_SOURCE_', PIPELINE_ID), N_BUFFER(2))
indir(`define', concat(`PIPELINE_PCM_', PIPELINE_ID), Passthrough Playback PCM_ID)


#
# PCM Configuration

#
PCM_CAPABILITIES(Passthrough Playback PCM_ID, `S32_LE,S24_LE,S16_LE', PCM_MIN_RATE, PCM_MAX_RATE, 2, PIPELINE_CHANNELS, 2, 16, 192, 16384, 65536, 65536)

Create a copy of your topology in tools/topology and replace the definition of low latency playback pipeline with the one crated in the previous step.

Code Block 7 Main topology .m4 file
1
2
3
4
5
6
# Low Latency playback pipeline 1 on PCM 0 using max 2 channels of s24le.
# Schedule 48 frames per 1000us deadline on core 0 with priority 0
PIPELINE_PCM_ADD(sof/pipe-amp-volume-playback.m4,
        1, 0, 2, s24le,
        1000, 0, 0,
        48000, 48000, 48000)

Driver

Add a mapping between SOF_TKN_PROCESS_TYPE set to “AMP” in your m4 topology definition and the SOF_COMP_AMP defined in the FW code in lesson 1. Refer to the driver documentation for further details about the topology mappings location and recompilation of the driver.