blob: 7b529163b3494986ce5f210ff0befc119e0a1499 [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* Copyright (c) 2019 Amlogic, Inc. All rights reserved.
*/
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/amlogic/scpi_protocol.h>
#include <linux/dev_printk.h>
#include "dsp_client_api.h"
int Mbox_invoke(struct device *dev, u32 dspid, int cmd, void *data, u32 len)
{
int ret = 0;
ret = mbox_message_send_data_sync(dev, cmd, data, len, dspid);
if (ret < 0)
dev_err(dev, "Mbox invoke write error: %d\n", ret);
return ret;
}
static u32 pcm_client_format_to_bytes(enum DSP_PCM_FORMAT format)
{
switch (format) {
case DSP_PCM_FORMAT_S32_LE:
case DSP_PCM_FORMAT_S32_BE:
case DSP_PCM_FORMAT_S24_LE:
case DSP_PCM_FORMAT_S24_BE:
return 32 >> 3; /*4 Bytes*/
case DSP_PCM_FORMAT_S24_3LE:
case DSP_PCM_FORMAT_S24_3BE:
return 24 >> 3; /*3 Bytes*/
default:
case DSP_PCM_FORMAT_S16_LE:
case DSP_PCM_FORMAT_S16_BE:
return 16 >> 3; /*2 Bytes*/
case DSP_PCM_FORMAT_S8:
return 8 >> 3; /*1 Bytes*/
};
}
u32 pcm_client_frame_to_bytes(void *hdl, u32 frame)
{
struct aml_pcm_ctx *p_aml_pcm_ctx = (struct aml_pcm_ctx *)hdl;
int bytes_per_sample = pcm_client_format_to_bytes(p_aml_pcm_ctx->config.format);
return (frame * bytes_per_sample * (p_aml_pcm_ctx->config.channels));
}
void *pcm_client_open(u32 card, u32 device, u32 flags, struct rpc_pcm_config *config,
struct device *dev, u32 dspid)
{
struct pcm_open_st arg;
struct aml_pcm_ctx *p_aml_pcm_ctx = kmalloc(sizeof(*p_aml_pcm_ctx), GFP_KERNEL);
memset(p_aml_pcm_ctx, 0, sizeof(struct aml_pcm_ctx));
memset(&arg, 0, sizeof(arg));
arg.card = card;
arg.device = device;
arg.flags = flags;
memcpy(&arg.pcm_config, config, sizeof(struct rpc_pcm_config));
arg.out_pcm = 0;
Mbox_invoke(dev, dspid, MBX_TINYALSA_OPEN, &arg, sizeof(arg));
if (!arg.out_pcm) {
kfree(p_aml_pcm_ctx);
return NULL;
}
p_aml_pcm_ctx->pcm_srv_hdl = arg.out_pcm;
memcpy(&p_aml_pcm_ctx->config, config, sizeof(struct rpc_pcm_config));
return p_aml_pcm_ctx;
}
int pcm_client_close(void *hdl, struct device *dev, u32 dspid)
{
struct pcm_close_st arg;
struct aml_pcm_ctx *p_aml_pcm_ctx = (struct aml_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, MBX_TINYALSA_CLOSE, &arg, sizeof(arg));
kfree(p_aml_pcm_ctx);
return arg.out_ret;
}
int pcm_client_writei(void *hdl, const phys_addr_t data, u32 count,
struct device *dev, u32 dspid)
{
struct pcm_io_st arg;
struct aml_pcm_ctx *p_aml_pcm_ctx = (struct aml_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.data = (u64)data;
arg.count = count;
arg.out_ret = 0;
Mbox_invoke(dev, dspid, MBX_TINYALSA_WRITEI, &arg, sizeof(arg));
return arg.out_ret;
}
int pcm_process_client_writei_to_speaker(void *hdl, const phys_addr_t data, u32 count,
u32 bypass, struct device *dev, u32 dspid)
{
struct pcm_process_io_st arg;
struct aml_pcm_ctx *p_aml_pcm_ctx = (struct aml_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.data = (u64)data;
arg.count = count;
arg.out_ret = 0;
arg.id = bypass;
Mbox_invoke(dev, dspid, MBX_CMD_APROCESS_WRITE_SPEAKER, &arg, sizeof(arg));
return arg.out_ret;
}
int pcm_client_readi(void *hdl, const phys_addr_t data, u32 count,
struct device *dev, u32 dspid)
{
struct pcm_io_st arg;
struct aml_pcm_ctx *p_aml_pcm_ctx = (struct aml_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.data = (u64)data;
arg.count = count;
arg.out_ret = 0;
Mbox_invoke(dev, dspid, MBX_TINYALSA_READI, &arg, sizeof(arg));
return arg.out_ret;
}
void *pcm_process_client_open(u32 card, u32 device, u32 flags,
struct rpc_pcm_config *config, struct device *dev, u32 dspid)
{
struct pcm_process_open_st arg;
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = kmalloc(sizeof(*p_aml_pcm_ctx), GFP_KERNEL);
memset(p_aml_pcm_ctx, 0, sizeof(struct aml_pro_pcm_ctx));
memset(&arg, 0, sizeof(arg));
arg.card = card;
arg.device = device;
arg.flags = flags;
memcpy(&arg.pcm_config, config, sizeof(struct rpc_pcm_config));
arg.out_pcm = 0;
Mbox_invoke(dev, dspid, MBX_CMD_APROCESS_OPEN, &arg, sizeof(arg));
if (!arg.out_pcm) {
kfree(p_aml_pcm_ctx);
return NULL;
}
p_aml_pcm_ctx->pcm_srv_hdl = arg.out_pcm;
memcpy(&p_aml_pcm_ctx->config, config, sizeof(struct rpc_pcm_config));
return p_aml_pcm_ctx;
}
int pcm_process_client_close(void *hdl, struct device *dev, u32 dspid)
{
struct pcm_process_close_st arg;
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, MBX_CMD_APROCESS_CLOSE, &arg, sizeof(arg));
kfree(p_aml_pcm_ctx);
return arg.out_ret;
}
int pcm_process_client_start(void *hdl, struct device *dev, u32 dspid)
{
struct pcm_process_io_st arg;
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, MBX_CMD_APROCESS_START, &arg, sizeof(arg));
return arg.out_ret;
}
int pcm_process_client_stop(void *hdl, struct device *dev, u32 dspid)
{
struct pcm_process_io_st arg;
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
memset(&arg, 0, sizeof(arg));
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, MBX_CMD_APROCESS_STOP, &arg, sizeof(arg));
return arg.out_ret;
}
int pcm_process_client_dqbuf(void *hdl, struct buf_info *buf, struct buf_info *release_buf,
u32 type, struct device *dev, u32 dspid)
{
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
struct pcm_process_buf_st arg;
if (type == PROCESSBUF)
arg.id = 0;
else
arg.id = 1;
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.buf_handle = 0;
arg.data = 0;
arg.count = 0;
arg.out_ret = -EINVAL;
if (release_buf && release_buf->handle &&
release_buf->phyaddr && release_buf->viraddr &&
release_buf->size &&
release_buf->viraddr == __va((phys_addr_t)release_buf->phyaddr))
arg.release_buf_handle = release_buf->handle;
else
arg.release_buf_handle = 0;
Mbox_invoke(dev, dspid, CMD_APROCESS_DQBUF, &arg, sizeof(arg));
buf->handle = arg.buf_handle;
buf->phyaddr = arg.data;
buf->size = arg.count;
if (arg.buf_handle)
buf->viraddr = __va((phys_addr_t)buf->phyaddr);
return arg.out_ret;
}
int pcm_process_client_qbuf(void *hdl, struct buf_info *buf, u32 type,
struct device *dev, u32 dspid)
{
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
struct pcm_process_buf_st arg;
if (!buf->handle)
return 0;
if (type == PROCESSBUF)
arg.id = 0;
else
arg.id = 1;
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.buf_handle = buf->handle;
arg.data = buf->phyaddr;
arg.count = 0;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, CMD_APROCESS_QBUF, &arg, sizeof(arg));
return arg.out_ret;
}
int pcm_process_client_get_volume_gain(void *hdl, int *gain,
int is_out, struct device *dev, u32 dspid)
{
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
struct pcm_process_gain_st arg;
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.gain = 0;
arg.is_out = is_out;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, CMD_APROCESS_GET_GAIN, &arg, sizeof(arg));
if (!arg.out_ret)
*gain = arg.gain;
return arg.out_ret;
}
int pcm_process_client_set_volume_gain(void *hdl, int gain,
int is_out, struct device *dev, u32 dspid)
{
struct aml_pro_pcm_ctx *p_aml_pcm_ctx = (struct aml_pro_pcm_ctx *)hdl;
struct pcm_process_gain_st arg;
arg.pcm = p_aml_pcm_ctx->pcm_srv_hdl;
arg.gain = gain;
arg.is_out = is_out;
arg.out_ret = -EINVAL;
Mbox_invoke(dev, dspid, CMD_APROCESS_SET_GAIN, &arg, sizeof(arg));
return arg.out_ret;
}
void *aml_dsp_mem_allocate(phys_addr_t *phy, size_t size,
struct device *dev, u32 dspid)
{
struct acodec_shm_alloc_st arg;
arg.size = size;
arg.pid = 0;
arg.phy = 0;
Mbox_invoke(dev, dspid, MBX_CMD_SHM_ALLOC, &arg, sizeof(arg));
if (!arg.phy)
return NULL;
*phy = (phys_addr_t)arg.phy;
return __va((phys_addr_t)arg.phy);
}
void aml_dsp_mem_free(phys_addr_t phy, struct device *dev, u32 dspid)
{
struct acodec_shm_free_st arg;
arg.phy = (u64)phy;
Mbox_invoke(dev, dspid, MBX_CMD_SHM_FREE, &arg, sizeof(arg));
}
void *audio_device_open(u32 card, u32 device, u32 flags,
struct rpc_pcm_config *config, struct device *dev, u32 dspid)
{
void *phandle = NULL;
if (flags & PCM_IN) {
config->period_count = 4;
config->start_threshold = 0;
config->silence_threshold = 0;
config->stop_threshold = 0;
phandle = pcm_process_client_open(card, device, PCM_IN, config, dev, dspid);
if (!phandle) {
pr_err("pcm client open fail\n");
return NULL;
}
} else {
config->period_count = 4;
config->start_threshold = 0;
config->silence_threshold = 0;
config->stop_threshold = 0;
phandle = pcm_client_open(card, device, PCM_OUT, config, dev, dspid);
if (!phandle) {
pr_err("pcm client open fail\n");
return NULL;
}
}
return phandle;
}