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