fsl_dsp_pool.c 3.66 KB
// SPDX-License-Identifier: GPL-2.0+
//
// Xtensa buffer pool API
//
// Copyright 2018 NXP
// Copyright (c) 2012-2013 by Tensilica Inc.

#include <linux/slab.h>
#include <soc/imx8/sc/ipc.h>

#include "fsl_dsp_pool.h"
#include "fsl_dsp.h"

/* ...allocate buffer pool */
int xf_pool_alloc(struct xf_client *client, struct xf_proxy *proxy,
		  u32 number, u32 length, xf_pool_type_t type,
		  struct xf_pool **pool)
{
	struct xf_pool      *p;
	struct xf_buffer    *b;
	void  *data;
	struct xf_message    msg;
	struct xf_message   *rmsg;

	/* ...basic sanity checks; number of buffers is positive */
	if (number <=0)
		return -EINVAL;

	/* ...get properly aligned buffer length */
	length = ALIGN(length, XF_PROXY_ALIGNMENT);

	p = kzalloc(offsetof(struct xf_pool, buffer) +
		    number * sizeof(struct xf_buffer), GFP_KERNEL);
	if(!p)
		return -ENOMEM;

	/* ...prepare command parameters */
	msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0));
	msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
	msg.opcode = XF_ALLOC;
	msg.length = length * number;
	msg.buffer = NULL;
	msg.ret = 0;

	/* ...execute command synchronously */
	rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
					 msg.buffer, msg.length, &client->work,
					 &client->compr_complete);
	if (IS_ERR(rmsg)) {
		kfree(p);
		return PTR_ERR(rmsg);
	}

	p->p = rmsg->buffer;
	/* TODO: review cleanup */
	/* xf_msg_free(proxy, rmsg);
	 * xf_unlock(&proxy->lock); */

	/* ...if operation is failed, do cleanup */
	/* ...set pool parameters */
	p->number = number, p->length = length;
	p->proxy = proxy;

	/* ...create individual buffers and link them into free list */
	for (p->free = b = &p->buffer[0], data = p->p; number > 0;
			number--, b++) {
		/* ...set address of the buffer (no length there) */
		b->address = data;

		/* ...file buffer into the free list */
		b->link.next = b + 1;

		/* ...advance data pointer in contiguous buffer */
		data += length;
	}

	/* ...terminate list of buffers (not too good - tbd) */
	b[-1].link.next = NULL;

	/* ...return buffer pointer */
	*pool = p;

	return 0;
}
/* ...buffer pool destruction */
int xf_pool_free(struct xf_client *client, struct xf_pool *pool)
{
	struct xf_proxy     *proxy;
	struct xf_message    msg;
	struct xf_message   *rmsg;

	/* ...basic sanity checks; pool is positive */
	if (pool == NULL)
		return -EINVAL;

	/* ...get proxy pointer */
	if ((proxy = pool->proxy) == NULL)
		return -EINVAL;

	/* ...prepare command parameters */
	msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_DSP_PROXY(0));
	msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id);
	msg.opcode = XF_FREE;
	msg.length = pool->length * pool->number;
	msg.buffer = pool->p;
	msg.ret = 0;

	/* ...execute command synchronously */
	rmsg = xf_cmd_send_recv_complete(client, proxy, msg.id, msg.opcode,
					 msg.buffer, msg.length, &client->work,
					 &client->compr_complete);
	kfree(pool);
	if (IS_ERR(rmsg))
		return PTR_ERR(rmsg);

	/* TODO: review cleanup */
	/* xf_msg_free(proxy, rmsg);
	 * xf_unlock(&proxy->lock); */

	return 0;
}

/* ...get new buffer from a pool */
struct xf_buffer *xf_buffer_get(struct xf_pool *pool)
{
	struct xf_buffer    *b;

	xf_lock(&pool->proxy->lock);
	/* ...take buffer from a head of the free list */
	b = pool->free;
	if (b) {
		/* ...advance free list head */
		pool->free = b->link.next, b->link.pool = pool;
	}

	xf_unlock(&pool->proxy->lock);
	return b;
}

/* ...return buffer back to pool */
void xf_buffer_put(struct xf_buffer *buffer)
{
	struct xf_pool  *pool = buffer->link.pool;

	xf_lock(&pool->proxy->lock);
	/* ...use global proxy lock for pool operations protection */
	/* ...put buffer back to a pool */
	buffer->link.next = pool->free, pool->free = buffer;

	xf_unlock(&pool->proxy->lock);
}