// SPDX-License-Identifier: (GPL-2.0+ OR MIT) // // Xtensa Audio Framework API for communication with DSP // // Copyright (C) 2017 Cadence Design Systems, Inc. // Copyright 2018 NXP #include "fsl_dsp.h" #include "fsl_dsp_xaf_api.h" /* ...send a command message to component */ int xf_command(struct xf_client *client, struct xf_handle *handle, u32 port, u32 opcode, void *buffer, u32 length) { struct xf_proxy *proxy = handle->proxy; struct xf_message msg; /* ...fill-in message parameters */ msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(handle->id, port)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = opcode; msg.length = length; msg.buffer = buffer; msg.ret = 0; /* ...execute command synchronously */ return xf_cmd_send(proxy, msg.id, msg.opcode, msg.buffer, msg.length); } int xaf_comp_set_config(struct xf_client *client, struct xaf_comp *p_comp, u32 num_param, void *p_param) { struct xf_handle *p_handle; struct xf_message msg; struct xf_message *rmsg; struct xf_set_param_msg *smsg; struct xf_set_param_msg *param = (struct xf_set_param_msg *)p_param; struct xf_proxy *proxy; u32 i; p_handle = &p_comp->handle; proxy = p_handle->proxy; /* ...set persistent stream characteristics */ smsg = xf_buffer_data(p_handle->aux); for (i = 0; i < num_param; i++) { smsg[i].id = param[i].id; smsg[i].mixData.value = param[i].mixData.value; } /* ...set command parameters */ msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(p_handle->id, 0)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_SET_PARAM; msg.length = sizeof(*smsg) * num_param; msg.buffer = smsg; 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)) return PTR_ERR(rmsg); /* ...save received component global client-id */ /* TODO: review cleanup */ /* xf_msg_free(proxy, rmsg); * xf_unlock(&proxy->lock); */ /* ...make sure response is expected */ if ((rmsg->opcode != XF_SET_PARAM) || (rmsg->buffer != smsg)) { return -EPIPE; } return 0; } int xaf_comp_get_config(struct xf_client *client, struct xaf_comp *p_comp, u32 num_param, void *p_param) { struct xf_handle *p_handle; struct xf_message msg; struct xf_message *rmsg; struct xf_get_param_msg *smsg; struct xf_get_param_msg *param = (struct xf_get_param_msg *)p_param; struct xf_proxy *proxy; u32 i; p_handle = &p_comp->handle; proxy = p_handle->proxy; /* ...set persistent stream characteristics */ smsg = xf_buffer_data(p_handle->aux); for (i = 0; i < num_param; i++) smsg[i].id = param[i].id; msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(p_handle->id, 0)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_GET_PARAM; msg.length = sizeof(*smsg) * num_param; msg.buffer = smsg; 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); /* ...save received component global client-id */ if(IS_ERR(rmsg)) return PTR_ERR(rmsg); /* TODO: review cleanup */ /* xf_msg_free(proxy, rmsg); * xf_unlock(&proxy->lock); */ /* ...make sure response is expected */ if ((rmsg->opcode != (u32)XF_GET_PARAM) || (rmsg->buffer != smsg)) { return -EPIPE; } for (i = 0; i < num_param; i++) param[i].mixData.value = smsg[i].mixData.value; return 0; } int xaf_comp_flush(struct xf_client *client, struct xaf_comp *p_comp) { struct xf_handle *p_handle; struct xf_proxy *proxy; struct xf_message msg; struct xf_message *rmsg; p_handle = &p_comp->handle; proxy = p_handle->proxy; msg.id = __XF_MSG_ID(__XF_AP_CLIENT(0, 0), __XF_PORT_SPEC2(p_handle->id, 0)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_FLUSH; msg.length = 0; 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)) return PTR_ERR(rmsg); /* ...make sure response is expected */ if ((rmsg->opcode != (u32)XF_FLUSH) || rmsg->buffer) { return -EPIPE; } return 0; } int xaf_comp_create(struct xf_client *client, struct xf_proxy *proxy, struct xaf_comp *p_comp, int comp_type) { struct fsl_dsp *dsp_priv = container_of(proxy, struct fsl_dsp, proxy); char lib_path[200]; char lib_wrap_path[200]; struct xf_handle *p_handle; struct xf_buffer *buf; int ret = 0, size; bool loadlib = true; bool request_inbuf = true; memset((void *)p_comp, 0, sizeof(struct xaf_comp)); strcpy(lib_path, "/usr/lib/imx-mm/audio-codec/dsp/"); strcpy(lib_wrap_path, "/usr/lib/imx-mm/audio-codec/dsp/"); p_handle = &p_comp->handle; p_comp->comp_type = comp_type; /* No need to load library for PCM */ if (comp_type == RENDER_ESAI || comp_type == RENDER_SAI || comp_type == CODEC_PCM_DEC) loadlib = false; /* Need to allocate in buffer for PCM */ if (comp_type == RENDER_ESAI || comp_type == RENDER_SAI) request_inbuf = false; if (loadlib) { p_comp->codec_lib.filename = lib_path; p_comp->codec_wrap_lib.filename = lib_wrap_path; p_comp->codec_lib.lib_type = DSP_CODEC_LIB; } size = INBUF_SIZE; switch (comp_type) { case CODEC_PCM_DEC: p_comp->dec_id = "audio-decoder/pcm"; if (dsp_priv->dsp_is_lpa) size = INBUF_SIZE_LPA_PCM; break; case CODEC_MP3_DEC: p_comp->dec_id = "audio-decoder/mp3"; strcat(lib_path, "lib_dsp_mp3_dec.so"); if (dsp_priv->dsp_is_lpa) size = INBUF_SIZE_LPA; break; case CODEC_AAC_DEC: p_comp->dec_id = "audio-decoder/aac"; strcat(lib_path, "lib_dsp_aac_dec.so"); break; case RENDER_ESAI: p_comp->dec_id = "renderer/esai"; break; case RENDER_SAI: p_comp->dec_id = "renderer/sai"; break; default: return -EINVAL; break; } /* ...create decoder component instance (select core-0) */ ret = xf_open(client, proxy, p_handle, p_comp->dec_id, 0, NULL); if (ret) { dev_err(dsp_priv->dev, "create (%s) component error: %d\n", p_comp->dec_id, ret); return ret; } if (loadlib) { strcat(lib_wrap_path, "lib_dsp_codec_wrap.so"); p_comp->codec_wrap_lib.lib_type = DSP_CODEC_WRAP_LIB; /* ...load codec wrapper lib */ ret = xf_load_lib(client, p_handle, &p_comp->codec_wrap_lib); if (ret) { dev_err(dsp_priv->dev, "load codec wrap lib error\n"); goto err_wrap_load; } /* ...load codec lib */ ret = xf_load_lib(client, p_handle, &p_comp->codec_lib); if (ret) { dev_err(dsp_priv->dev, "load codec lib error\n"); goto err_codec_load; } } if (request_inbuf) { /* ...allocate input buffer */ ret = xf_pool_alloc(client, proxy, 1, size, XF_POOL_INPUT, &p_comp->inpool); if (ret) { dev_err(dsp_priv->dev, "alloc input buf error\n"); goto err_pool_alloc; } /* ...initialize input buffer pointer */ buf = xf_buffer_get(p_comp->inpool); p_comp->inptr = xf_buffer_data(buf); } p_comp->active = true; return ret; err_pool_alloc: xf_unload_lib(client, p_handle, &p_comp->codec_lib); err_codec_load: xf_unload_lib(client, p_handle, &p_comp->codec_wrap_lib); err_wrap_load: xf_close(client, p_handle); return ret; } int xaf_comp_delete(struct xf_client *client, struct xaf_comp *p_comp) { struct xf_handle *p_handle; bool loadlib = true; u32 ret = 0; if (!p_comp->active) return ret; /* mark component as unusable from this point */ p_comp->active = false; if (p_comp->comp_type == RENDER_ESAI || p_comp->comp_type == RENDER_SAI) loadlib = false; p_handle = &p_comp->handle; if (loadlib) { /* ...unload codec wrapper library */ xf_unload_lib(client, p_handle, &p_comp->codec_wrap_lib); /* ...unload codec library */ xf_unload_lib(client, p_handle, &p_comp->codec_lib); xf_pool_free(client, p_comp->inpool); } /* ...delete component */ xf_close(client, p_handle); return ret; } int xaf_comp_process(struct xf_client *client, struct xaf_comp *p_comp, void *p_buf, u32 length, u32 flag) { struct xf_handle *p_handle; u32 ret = 0; p_handle = &p_comp->handle; switch (flag) { case XF_FILL_THIS_BUFFER: /* ...send message to component output port (port-id=1) */ ret = xf_command(client, p_handle, 1, XF_FILL_THIS_BUFFER, p_buf, length); break; case XF_EMPTY_THIS_BUFFER: /* ...send message to component input port (port-id=0) */ ret = xf_command(client, p_handle, 0, XF_EMPTY_THIS_BUFFER, p_buf, length); break; default: break; } return ret; } /* ...port binding function */ int xf_route(struct xf_client *client, struct xf_handle *src, u32 src_port, struct xf_handle *dst, u32 dst_port, u32 num, u32 size, u32 align) { struct xf_proxy *proxy = src->proxy; struct xf_buffer *b; struct xf_route_port_msg *m; struct xf_message msg; struct xf_message *rmsg; /* ...sanity checks - proxy pointers are same */ if (proxy != dst->proxy) return -EINVAL; /* ...buffer data is sane */ if (!(num && size && xf_is_power_of_two(align))) return -EINVAL; /* ...get control buffer */ if ((b = xf_buffer_get(proxy->aux)) == NULL) return -EBUSY; /* ...get message buffer */ m = xf_buffer_data(b); /* ...fill-in message parameters */ m->src = __XF_PORT_SPEC2(src->id, src_port); m->dst = __XF_PORT_SPEC2(dst->id, dst_port); m->alloc_number = num; m->alloc_size = size; m->alloc_align = align; /* ...set command parameters */ msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_PORT_SPEC2(src->id, src_port)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_ROUTE; msg.length = sizeof(*m); msg.buffer = m; 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)) return PTR_ERR(rmsg); /* ...save received component global client-id */ /* TODO: review cleanup */ /* xf_msg_free(proxy, rmsg); * xf_unlock(&proxy->lock); */ /* ...synchronously execute command on remote DSP */ /* XF_CHK_API(xf_proxy_cmd_exec(proxy, &msg)); */ /* ...return buffer to proxy */ xf_buffer_put(b); /* ...check result is successful */ /* XF_CHK_ERR(msg.opcode == XF_ROUTE, -ENOMEM); */ return 0; } /* ...port unbinding function */ int xf_unroute(struct xf_client *client, struct xf_handle *src, u32 src_port) { struct xf_proxy *proxy = src->proxy; struct xf_buffer *b; struct xf_unroute_port_msg *m; struct xf_message msg; struct xf_message *rmsg; int r = 0; /* ...get control buffer */ if((b = xf_buffer_get(proxy->aux)) == NULL) return -EBUSY; /* ...get message buffer */ m = xf_buffer_data(b); /* ...fill-in message parameters */ m->src = __XF_PORT_SPEC2(src->id, src_port); /* ...set command parameters */ msg.id = __XF_MSG_ID(__XF_AP_PROXY(0), __XF_PORT_SPEC2(src->id, src_port)); msg.id = XF_MSG_AP_FROM_USER(msg.id, client->id); msg.opcode = XF_UNROUTE; msg.length = sizeof(*m); msg.buffer = m; 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)) return PTR_ERR(rmsg); /* ...save received component global client-id */ /*TODO: review cleanup */ /* xf_msg_free(proxy, rmsg); */ /* xf_unlock(&proxy->lock); */ /* ...return buffer to proxy */ xf_buffer_put(b); return r; } int xaf_connect(struct xf_client *client, struct xaf_comp *p_src, struct xaf_comp *p_dest, u32 num_buf, u32 buf_length) { /* ...connect p_src output port with p_dest input port */ return xf_route(client, &p_src->handle, 1, &p_dest->handle, 0, num_buf, buf_length, 8); } int xaf_disconnect(struct xf_client *client, struct xaf_comp *p_comp) { /* ...disconnect p_src output port with p_dest input port */ return xf_unroute(client, &p_comp->handle, 0); } int xaf_comp_add(struct xaf_pipeline *p_pipe, struct xaf_comp *p_comp) { int ret = 0; p_comp->next = p_pipe->comp_chain; p_comp->pipeline = p_pipe; p_pipe->comp_chain = p_comp; return ret; } int xaf_pipeline_create(struct xaf_pipeline *p_pipe) { int ret = 0; memset(p_pipe, 0, sizeof(struct xaf_pipeline)); return ret; } int xaf_pipeline_delete(struct xaf_pipeline *p_pipe) { int ret = 0; memset(p_pipe, 0, sizeof(struct xaf_pipeline)); return ret; }