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 #include <sof/audio/buffer.h>
9 #include <sof/audio/component.h>
10 #include <sof/drivers/interrupt.h>
11 #include <sof/lib/alloc.h>
12 #include <sof/lib/cache.h>
13 #include <sof/lib/memory.h>
14 #include <sof/lib/notifier.h>
15 #include <sof/list.h>
16 #include <sof/spinlock.h>
17 #include <ipc/topology.h>
18 #include <errno.h>
19 #include <stddef.h>
20 #include <stdint.h>
21
22 /* 42544c92-8e92-4e41-b679-34519f1c1d28 */
23 DECLARE_SOF_RT_UUID("buffer", buffer_uuid, 0x42544c92, 0x8e92, 0x4e41,
24 0xb6, 0x79, 0x34, 0x51, 0x9f, 0x1c, 0x1d, 0x28);
25 DECLARE_TR_CTX(buffer_tr, SOF_UUID(buffer_uuid), LOG_LEVEL_INFO);
26
buffer_alloc(uint32_t size,uint32_t caps,uint32_t align)27 struct comp_buffer *buffer_alloc(uint32_t size, uint32_t caps, uint32_t align)
28 {
29 struct comp_buffer *buffer;
30
31 tr_dbg(&buffer_tr, "buffer_alloc()");
32
33 /* validate request */
34 if (size == 0 || size > HEAP_BUFFER_SIZE) {
35 tr_err(&buffer_tr, "buffer_alloc(): new size = %u is invalid",
36 size);
37 return NULL;
38 }
39
40 /* allocate new buffer */
41 buffer = rzalloc(SOF_MEM_ZONE_RUNTIME, 0, SOF_MEM_CAPS_RAM,
42 sizeof(*buffer));
43 if (!buffer) {
44 tr_err(&buffer_tr, "buffer_alloc(): could not alloc structure");
45 return NULL;
46 }
47
48 buffer->lock = rzalloc(SOF_MEM_ZONE_RUNTIME_SHARED, 0, SOF_MEM_CAPS_RAM,
49 sizeof(*buffer->lock));
50 if (!buffer->lock) {
51 rfree(buffer);
52 tr_err(&buffer_tr, "buffer_alloc(): could not alloc lock");
53 return NULL;
54 }
55
56 buffer->stream.addr = rballoc_align(0, caps, size, align);
57 if (!buffer->stream.addr) {
58 rfree(buffer);
59 tr_err(&buffer_tr, "buffer_alloc(): could not alloc size = %u bytes of type = %u",
60 size, caps);
61 return NULL;
62 }
63
64 buffer_init(buffer, size, caps);
65
66 list_init(&buffer->source_list);
67 list_init(&buffer->sink_list);
68 spinlock_init(buffer->lock);
69
70 return buffer;
71 }
72
buffer_zero(struct comp_buffer * buffer)73 void buffer_zero(struct comp_buffer *buffer)
74 {
75 buf_dbg(buffer, "stream_zero()");
76
77 bzero(buffer->stream.addr, buffer->stream.size);
78 if (buffer->caps & SOF_MEM_CAPS_DMA)
79 dcache_writeback_region(buffer->stream.addr,
80 buffer->stream.size);
81 }
82
buffer_set_size(struct comp_buffer * buffer,uint32_t size)83 int buffer_set_size(struct comp_buffer *buffer, uint32_t size)
84 {
85 void *new_ptr = NULL;
86
87 /* validate request */
88 if (size == 0 || size > HEAP_BUFFER_SIZE) {
89 buf_err(buffer, "resize size = %u is invalid", size);
90 return -EINVAL;
91 }
92
93 if (size == buffer->stream.size)
94 return 0;
95
96 new_ptr = rbrealloc(buffer->stream.addr, SOF_MEM_FLAG_NO_COPY,
97 buffer->caps, size, buffer->stream.size);
98
99 /* we couldn't allocate bigger chunk */
100 if (!new_ptr && size > buffer->stream.size) {
101 buf_err(buffer, "resize can't alloc %u bytes type %u",
102 buffer->stream.size, buffer->caps);
103 return -ENOMEM;
104 }
105
106 /* use bigger chunk, else just use the old chunk but set smaller */
107 if (new_ptr)
108 buffer->stream.addr = new_ptr;
109
110 buffer_init(buffer, size, buffer->caps);
111
112 return 0;
113 }
114
buffer_set_params(struct comp_buffer * buffer,struct sof_ipc_stream_params * params,bool force_update)115 int buffer_set_params(struct comp_buffer *buffer, struct sof_ipc_stream_params *params,
116 bool force_update)
117 {
118 int ret;
119 int i;
120
121 if (!params) {
122 buf_err(buffer, "buffer_set_params(): !params");
123 return -EINVAL;
124 }
125
126 if (buffer->hw_params_configured && !force_update)
127 return 0;
128
129 ret = audio_stream_set_params(&buffer->stream, params);
130 if (ret < 0) {
131 buf_err(buffer, "buffer_set_params(): audio_stream_set_params failed");
132 return -EINVAL;
133 }
134
135 buffer->buffer_fmt = params->buffer_fmt;
136 for (i = 0; i < SOF_IPC_MAX_CHANNELS; i++)
137 buffer->chmap[i] = params->chmap[i];
138
139 buffer->hw_params_configured = true;
140
141 return 0;
142 }
143
buffer_params_match(struct comp_buffer * buffer,struct sof_ipc_stream_params * params,uint32_t flag)144 bool buffer_params_match(struct comp_buffer *buffer, struct sof_ipc_stream_params *params,
145 uint32_t flag)
146 {
147 assert(params && buffer);
148
149 if ((flag & BUFF_PARAMS_FRAME_FMT) &&
150 buffer->stream.frame_fmt != params->frame_fmt)
151 return false;
152
153 if ((flag & BUFF_PARAMS_RATE) &&
154 buffer->stream.rate != params->rate)
155 return false;
156
157 if ((flag & BUFF_PARAMS_CHANNELS) &&
158 buffer->stream.channels != params->channels)
159 return false;
160
161 return true;
162 }
163
164 /* free component in the pipeline */
buffer_free(struct comp_buffer * buffer)165 void buffer_free(struct comp_buffer *buffer)
166 {
167 struct buffer_cb_free cb_data = {
168 .buffer = buffer,
169 };
170
171 buf_dbg(buffer, "buffer_free()");
172
173 notifier_event(buffer, NOTIFIER_ID_BUFFER_FREE,
174 NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data));
175
176 /* In case some listeners didn't unregister from buffer's callbacks */
177 notifier_unregister_all(NULL, buffer);
178
179 rfree(buffer->stream.addr);
180 rfree(buffer->lock);
181 rfree(buffer);
182 }
183
comp_update_buffer_produce(struct comp_buffer * buffer,uint32_t bytes)184 void comp_update_buffer_produce(struct comp_buffer *buffer, uint32_t bytes)
185 {
186 uint32_t flags = 0;
187 struct buffer_cb_transact cb_data = {
188 .buffer = buffer,
189 .transaction_amount = bytes,
190 .transaction_begin_address = buffer->stream.w_ptr,
191 };
192 char *addr;
193
194 /* return if no bytes */
195 if (!bytes) {
196 buf_dbg(buffer, "comp_update_buffer_produce(), no bytes to produce, source->comp.id = %u, source->comp.type = %u, sink->comp.id = %u, sink->comp.type = %u",
197 dev_comp_id(buffer->source),
198 dev_comp_type(buffer->source),
199 dev_comp_id(buffer->sink),
200 dev_comp_type(buffer->sink));
201 return;
202 }
203
204 buffer_lock(buffer, &flags);
205
206 audio_stream_produce(&buffer->stream, bytes);
207
208 notifier_event(buffer, NOTIFIER_ID_BUFFER_PRODUCE,
209 NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data));
210
211 buffer_unlock(buffer, flags);
212
213 addr = buffer->stream.addr;
214
215 buf_dbg(buffer, "comp_update_buffer_produce(), ((buffer->avail << 16) | buffer->free) = %08x, ((buffer->id << 16) | buffer->size) = %08x",
216 (audio_stream_get_avail_bytes(&buffer->stream) << 16) |
217 audio_stream_get_free_bytes(&buffer->stream),
218 (buffer->id << 16) | buffer->stream.size);
219 buf_dbg(buffer, "comp_update_buffer_produce(), ((buffer->r_ptr - buffer->addr) << 16 | (buffer->w_ptr - buffer->addr)) = %08x",
220 ((char *)buffer->stream.r_ptr - addr) << 16 |
221 ((char *)buffer->stream.w_ptr - addr));
222 }
223
comp_update_buffer_consume(struct comp_buffer * buffer,uint32_t bytes)224 void comp_update_buffer_consume(struct comp_buffer *buffer, uint32_t bytes)
225 {
226 uint32_t flags = 0;
227 struct buffer_cb_transact cb_data = {
228 .buffer = buffer,
229 .transaction_amount = bytes,
230 .transaction_begin_address = buffer->stream.r_ptr,
231 };
232 char *addr;
233
234 /* return if no bytes */
235 if (!bytes) {
236 buf_dbg(buffer, "comp_update_buffer_consume(), no bytes to consume, source->comp.id = %u, source->comp.type = %u, sink->comp.id = %u, sink->comp.type = %u",
237 dev_comp_id(buffer->source),
238 dev_comp_type(buffer->source),
239 dev_comp_id(buffer->sink),
240 dev_comp_type(buffer->sink));
241 return;
242 }
243
244 buffer_lock(buffer, &flags);
245
246 audio_stream_consume(&buffer->stream, bytes);
247
248 notifier_event(buffer, NOTIFIER_ID_BUFFER_CONSUME,
249 NOTIFIER_TARGET_CORE_LOCAL, &cb_data, sizeof(cb_data));
250
251 buffer_unlock(buffer, flags);
252
253 addr = buffer->stream.addr;
254
255 buf_dbg(buffer, "comp_update_buffer_consume(), (buffer->avail << 16) | buffer->free = %08x, (buffer->id << 16) | buffer->size = %08x, (buffer->r_ptr - buffer->addr) << 16 | (buffer->w_ptr - buffer->addr)) = %08x",
256 (audio_stream_get_avail_bytes(&buffer->stream) << 16) |
257 audio_stream_get_free_bytes(&buffer->stream),
258 (buffer->id << 16) | buffer->stream.size,
259 ((char *)buffer->stream.r_ptr - addr) << 16 |
260 ((char *)buffer->stream.w_ptr - addr));
261 }
262