blob: dd556eed65ed2a4464d8114a692d0b64e763da49 [file] [log] [blame]
/* sound/soc/samsung/abox/abox_rdma.c
*
* ALSA SoC Audio Layer - Samsung Abox RDMA driver
*
* Copyright (c) 2016 Samsung Electronics Co. Ltd.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
/* #define DEBUG */
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/of_platform.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/regmap.h>
#include <linux/iommu.h>
#include <linux/delay.h>
#include <linux/memblock.h>
#include <linux/sched/clock.h>
#include <sound/hwdep.h>
#include <linux/miscdevice.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/sounddev_abox.h>
#include <linux/dma-buf.h>
#include <linux/dma-buf-container.h>
#include <linux/ion_exynos.h>
#include "../../../../drivers/iommu/exynos-iommu.h"
#include "../../../../drivers/staging/android/uapi/ion.h"
#include <sound/samsung/abox.h>
#include "abox_util.h"
#include "abox_gic.h"
#include "abox_dbg.h"
#include "abox_vss.h"
#include "abox_cmpnt.h"
#include "abox_mmapfd.h"
#include "abox.h"
#define COMPR_USE_COPY
#define COMPR_USE_FIXED_MEMORY
/* Mailbox between driver and firmware for offload */
#define COMPR_CMD_CODE (0x0004)
#define COMPR_HANDLE_ID (0x0008)
#define COMPR_IP_TYPE (0x000C)
#define COMPR_SIZE_OF_FRAGMENT (0x0010)
#define COMPR_PHY_ADDR_INBUF (0x0014)
#define COMPR_SIZE_OF_INBUF (0x0018)
#define COMPR_LEFT_VOL (0x001C)
#define COMPR_RIGHT_VOL (0x0020)
#define EFFECT_EXT_ON (0x0024)
#define COMPR_ALPA_NOTI (0x0028)
#define COMPR_PARAM_RATE (0x0034)
#define COMPR_PARAM_SAMPLE (0x0038)
#define COMPR_PARAM_CH (0x003C)
#define COMPR_RENDERED_PCM_SIZE (0x004C)
#define COMPR_RETURN_CMD (0x0040)
#define COMPR_IP_ID (0x0044)
#define COMPR_SIZE_OUT_DATA (0x0048)
#define COMPR_UPSCALE (0x0050)
#define COMPR_CPU_LOCK_LV (0x0054)
#define COMPR_CHECK_CMD (0x0058)
#define COMPR_CHECK_RUNNING (0x005C)
#define COMPR_ACK (0x0060)
#define COMPR_INTR_ACK (0x0064)
#define COMPR_INTR_DMA_ACK (0x0068)
#define COMPR_MAX COMPR_INTR_DMA_ACK
/* COMPR_UPSCALE */
#define COMPR_BIT_SHIFT (0)
#define COMPR_BIT_MASK (0xFF)
#define COMPR_CH_SHIFT (8)
#define COMPR_CH_MASK (0xF)
#define COMPR_RATE_SHIFT (12)
#define COMPR_RATE_MASK (0xFFFFF)
/* Interrupt type */
#define INTR_WAKEUP (0x0)
#define INTR_READY (0x1000)
#define INTR_DMA (0x2000)
#define INTR_CREATED (0x3000)
#define INTR_DECODED (0x4000)
#define INTR_RENDERED (0x5000)
#define INTR_FLUSH (0x6000)
#define INTR_PAUSED (0x6001)
#define INTR_EOS (0x7000)
#define INTR_DESTROY (0x8000)
#define INTR_FX_EXT (0x9000)
#define INTR_EFF_REQUEST (0xA000)
#define INTR_SET_CPU_LOCK (0xC000)
#define INTR_FW_LOG (0xFFFF)
#define COMPRESSED_LR_VOL_MAX_STEPS 0x2000
#define DMA_VOL_FACTOR_MAX_STEPS (0xFFFFFF)
enum SEIREN_CMDTYPE {
/* OFFLOAD */
CMD_COMPR_CREATE = 0x50,
CMD_COMPR_DESTROY,
CMD_COMPR_SET_PARAM,
CMD_COMPR_WRITE,
CMD_COMPR_READ,
CMD_COMPR_START,
CMD_COMPR_STOP,
CMD_COMPR_PAUSE,
CMD_COMPR_EOS,
CMD_COMPR_GET_VOLUME,
CMD_COMPR_SET_VOLUME,
CMD_COMPR_CA5_WAKEUP,
CMD_COMPR_HPDET_NOTIFY,
};
enum OFFLOAD_IPTYPE {
COMPR_MP3 = 0x0,
COMPR_AAC = 0x1,
COMPR_FLAC = 0x2,
};
static const struct snd_compr_caps abox_rdma_compr_caps = {
.direction = SND_COMPRESS_PLAYBACK,
.min_fragment_size = SZ_4K,
.max_fragment_size = SZ_64K,
.min_fragments = 1,
.max_fragments = 5,
.num_codecs = 3,
.codecs = {
SND_AUDIOCODEC_MP3,
SND_AUDIOCODEC_AAC,
SND_AUDIOCODEC_FLAC
},
};
static void abox_rdma_mailbox_write(struct device *dev, u32 index, u32 value)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
int ret;
dev_dbg(dev, "%s(0x%x, 0x%x)\n", __func__, index, value);
if (!regmap) {
dev_err(dev, "%s: regmap is null\n", __func__);
return;
}
pm_runtime_get(dev);
ret = regmap_write(regmap, index, value);
if (ret < 0)
dev_warn(dev, "%s(%u) failed: %d\n", __func__, index, ret);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
}
static u32 abox_rdma_mailbox_read(struct device *dev, u32 index)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
int ret;
u32 val = 0;
dev_dbg(dev, "%s(0x%x)\n", __func__, index);
if (!regmap) {
dev_err(dev, "%s: regmap is null\n", __func__);
return 0;
}
pm_runtime_get(dev);
ret = regmap_read(regmap, index, &val);
if (ret < 0)
dev_warn(dev, "%s(%u) failed: %d\n", __func__, index, ret);
pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return val;
}
static void abox_mailbox_save(struct device *dev)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
if (regmap) {
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
}
}
static void abox_mailbox_restore(struct device *dev)
{
struct regmap *regmap = dev_get_regmap(dev, NULL);
if (regmap) {
regcache_cache_only(regmap, false);
regcache_sync(regmap);
}
}
static bool abox_mailbox_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
case COMPR_ACK:
case COMPR_INTR_ACK:
case COMPR_INTR_DMA_ACK:
case COMPR_RETURN_CMD:
case COMPR_SIZE_OUT_DATA:
case COMPR_IP_ID:
case COMPR_RENDERED_PCM_SIZE:
return true;
default:
return false;
}
}
static bool abox_mailbox_rw_reg(struct device *dev, unsigned int reg)
{
return true;
}
static const struct regmap_config abox_mailbox_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = COMPR_MAX,
.volatile_reg = abox_mailbox_volatile_reg,
.readable_reg = abox_mailbox_rw_reg,
.writeable_reg = abox_mailbox_rw_reg,
.cache_type = REGCACHE_FLAT,
.fast_io = true,
};
static int abox_rdma_request_ipc(struct abox_platform_data *data,
ABOX_IPC_MSG *msg, int atomic, int sync)
{
return abox_request_ipc(data->dev_abox, msg->ipcid, msg, sizeof(*msg),
atomic, sync);
}
static int abox_rdma_mailbox_send_cmd(struct device *dev, unsigned int cmd)
{
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct device *dev_abox = platform_data->dev_abox;
struct abox_compr_data *data = &platform_data->compr_data;
ABOX_IPC_MSG msg;
u64 timeout;
int ret;
dev_dbg(dev, "%s(%x)\n", __func__, cmd);
switch (cmd) {
case CMD_COMPR_CREATE:
dev_dbg(dev, "%s: CMD_COMPR_CREATE %d\n", __func__, cmd);
break;
case CMD_COMPR_DESTROY:
dev_dbg(dev, "%s: CMD_COMPR_DESTROY %d\n", __func__, cmd);
break;
case CMD_COMPR_SET_PARAM:
dev_dbg(dev, "%s: CMD_COMPR_SET_PARAM %d\n", __func__, cmd);
#ifdef CONFIG_SND_ESA_SA_EFFECT
abox_rdma_mailbox_write(dev, abox_data, COMPR_PARAM_RATE,
data->out_sample_rate);
#endif
break;
case CMD_COMPR_WRITE:
dev_dbg(dev, "%s: CMD_COMPR_WRITE %d\n", __func__, cmd);
break;
case CMD_COMPR_READ:
dev_dbg(dev, "%s: CMD_COMPR_READ %d\n", __func__, cmd);
break;
case CMD_COMPR_START:
dev_dbg(dev, "%s: CMD_COMPR_START %d\n", __func__, cmd);
break;
case CMD_COMPR_STOP:
dev_dbg(dev, "%s: CMD_COMPR_STOP %d\n", __func__, cmd);
break;
case CMD_COMPR_PAUSE:
dev_dbg(dev, "%s: CMD_COMPR_PAUSE %d\n", __func__, cmd);
break;
case CMD_COMPR_EOS:
dev_dbg(dev, "%s: CMD_COMPR_EOS %d\n", __func__, cmd);
break;
case CMD_COMPR_GET_VOLUME:
dev_dbg(dev, "%s: CMD_COMPR_GET_VOLUME %d\n", __func__, cmd);
break;
case CMD_COMPR_SET_VOLUME:
dev_dbg(dev, "%s: CMD_COMPR_SET_VOLUME %d\n", __func__, cmd);
break;
case CMD_COMPR_CA5_WAKEUP:
dev_dbg(dev, "%s: CMD_COMPR_CA5_WAKEUP %d\n", __func__, cmd);
break;
case CMD_COMPR_HPDET_NOTIFY:
dev_dbg(dev, "%s: CMD_COMPR_HPDET_NOTIFY %d\n", __func__, cmd);
break;
default:
dev_err(dev, "%s: unknown cmd %d\n", __func__, cmd);
return -EINVAL;
}
mutex_lock(&data->cmd_lock);
abox_rdma_mailbox_write(dev, COMPR_HANDLE_ID, data->handle_id);
abox_rdma_mailbox_write(dev, COMPR_CMD_CODE, cmd);
msg.ipcid = IPC_OFFLOAD;
ret = abox_request_ipc(dev_abox, msg.ipcid, &msg, sizeof(msg), 0, 0);
timeout = local_clock() + (1 * NSEC_PER_SEC);
while (!abox_rdma_mailbox_read(dev, COMPR_ACK)) {
if (local_clock() <= timeout) {
cond_resched();
continue;
}
dev_err(dev, "%s: No ack error!(%x)", __func__, cmd);
ret = -EFAULT;
break;
}
/* clear ACK */
abox_rdma_mailbox_write(dev, COMPR_ACK, 0);
mutex_unlock(&data->cmd_lock);
return ret;
}
static void abox_rdma_compr_clear_intr_ack(struct device *dev)
{
abox_rdma_mailbox_write(dev, COMPR_INTR_ACK, 0);
}
static int abox_rdma_compr_isr_handler(void *priv)
{
struct platform_device *pdev = priv;
struct device *dev = &pdev->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
int id = platform_data->id;
unsigned long flags;
u32 val, fw_stat;
dev_dbg(dev, "%s[%d]\n", __func__, id);
val = abox_rdma_mailbox_read(dev, COMPR_RETURN_CMD);
if (val == 1)
dev_err(dev, "%s: There is possibility of firmware CMD fail %u\n",
__func__, val);
fw_stat = val >> 16;
dev_dbg(dev, "fw_stat(%08x), val(%08x)\n", fw_stat, val);
switch (fw_stat) {
case INTR_CREATED:
dev_info(dev, "INTR_CREATED\n");
abox_rdma_compr_clear_intr_ack(dev);
data->created = true;
break;
case INTR_DECODED:
/* check the error */
val &= 0xFF;
if (val) {
dev_err(dev, "INTR_DECODED err(%d)\n", val);
} else if (data->cstream && data->cstream->runtime) {
/* update copied total bytes */
u32 size = abox_rdma_mailbox_read(dev,
COMPR_SIZE_OUT_DATA);
struct snd_compr_stream *cstream = data->cstream;
struct snd_compr_runtime *runtime = cstream->runtime;
dev_dbg(dev, "INTR_DECODED(%d)\n", size);
spin_lock_irqsave(&data->lock, flags);
/* update copied total bytes */
data->copied_total += size;
data->byte_offset += size;
if (data->byte_offset >= runtime->buffer_size)
data->byte_offset -= runtime->buffer_size;
spin_unlock_irqrestore(&data->lock, flags);
snd_compr_fragment_elapsed(data->cstream);
if (!data->start &&
runtime->state != SNDRV_PCM_STATE_PAUSED) {
/* Writes must be restarted from _copy() */
dev_err(dev, "%s: write_done received while not started(%d)",
__func__, runtime->state);
} else {
u64 bytes_available = data->received_total -
data->copied_total;
dev_dbg(dev, "%s: current free bufsize(%llu)\n",
__func__, runtime->buffer_size -
bytes_available);
if (bytes_available < runtime->fragment_size) {
dev_dbg(dev, "%s: WRITE_DONE Insufficient data to send.(avail:%llu)\n",
__func__, bytes_available);
}
}
} else {
dev_dbg(dev, "%s: INTR_DECODED after compress offload end\n",
__func__);
}
abox_rdma_compr_clear_intr_ack(dev);
break;
case INTR_FLUSH:
/* check the error */
val &= 0xFF;
if (val) {
dev_err(dev, "INTR_FLUSH err(%d)\n", val);
} else {
/* flush done */
data->stop_ack = 1;
wake_up_interruptible(&data->flush_wait);
}
abox_rdma_compr_clear_intr_ack(dev);
break;
case INTR_PAUSED:
/* check the error */
val &= 0xFF;
if (val)
dev_err(dev, "INTR_PAUSED err(%d)\n", val);
abox_rdma_compr_clear_intr_ack(dev);
break;
case INTR_EOS:
if (data->eos) {
if (data->copied_total != data->received_total)
dev_err(dev, "%s: EOS is not sync!(%llu/%llu)\n",
__func__, data->copied_total,
data->received_total);
/* ALSA Framework callback to notify drain complete */
snd_compr_drain_notify(data->cstream);
data->eos = 0;
dev_info(dev, "%s: DATA_CMD_EOS wake up\n", __func__);
}
abox_rdma_compr_clear_intr_ack(dev);
break;
case INTR_DESTROY:
/* check the error */
val &= 0xFF;
if (val) {
dev_err(dev, "INTR_DESTROY err(%d)\n", val);
} else {
/* destroied */
data->exit_ack = 1;
wake_up_interruptible(&data->exit_wait);
}
abox_rdma_compr_clear_intr_ack(dev);
break;
case INTR_FX_EXT:
/* To Do */
abox_rdma_compr_clear_intr_ack(dev);
break;
#ifdef CONFIG_SND_SAMSUNG_ELPE
case INTR_EFF_REQUEST:
/*To Do */
abox_rdma_compr_clear_intr_ack(dev);
break;
#endif
}
wake_up_interruptible(&data->ipc_wait);
return IRQ_HANDLED;
}
static int abox_rdma_compr_set_param(struct platform_device *pdev,
struct snd_compr_runtime *runtime)
{
struct device *dev = &pdev->dev;
struct abox_platform_data *platform_data = platform_get_drvdata(pdev);
struct abox_compr_data *data = &platform_data->compr_data;
int id = platform_data->id;
int ret;
dev_info(dev, "%s[%d] buffer: %llu\n", __func__, id,
runtime->buffer_size);
#ifdef COMPR_USE_FIXED_MEMORY
/* free memory allocated by ALSA */
if (runtime->buffer != data->dma_area)
kfree(runtime->buffer);
runtime->buffer = data->dma_area;
if (runtime->buffer_size > data->dma_size) {
dev_err(dev, "allocated buffer size is smaller than requested(%llu > %zu)\n",
runtime->buffer_size, data->dma_size);
ret = -ENOMEM;
goto error;
}
#else
#ifdef COMPR_USE_COPY
runtime->buffer = dma_alloc_coherent(dev, runtime->buffer_size,
&data->dma_addr, GFP_KERNEL);
if (!runtime->buffer) {
dev_err(dev, "dma memory allocation failed (size=%llu)\n",
runtime->buffer_size);
ret = -ENOMEM;
goto error;
}
#else
data->dma_addr = dma_map_single(dev, runtime->buffer,
runtime->buffer_size, DMA_TO_DEVICE);
ret = dma_mapping_error(dev, data->dma_addr);
if (ret) {
dev_err(dev, "dma memory mapping failed(%d)\n", ret);
goto error;
}
#endif
ret = abox_iommu_map(&platform_data->abox_data->pdev->dev,
IOVA_COMPR_BUFFER(id), virt_to_phys(runtime->buffer),
round_up(runtime->buffer_size, PAGE_SIZE), 0);
if (ret < 0) {
dev_err(dev, "iommu mapping failed(%d)\n", ret);
goto error;
}
#endif
/* set buffer information at mailbox */
abox_rdma_mailbox_write(dev, COMPR_SIZE_OF_INBUF, runtime->buffer_size);
abox_rdma_mailbox_write(dev, COMPR_PHY_ADDR_INBUF,
IOVA_COMPR_BUFFER(id));
abox_rdma_mailbox_write(dev, COMPR_PARAM_SAMPLE, data->sample_rate);
abox_rdma_mailbox_write(dev, COMPR_PARAM_CH, data->channels);
abox_rdma_mailbox_write(dev, COMPR_IP_TYPE, data->codec_id << 16);
data->created = false;
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_SET_PARAM);
if (ret < 0) {
dev_err(dev, "CMD_COMPR_SET_PARAM failed(%d)\n", ret);
goto error;
}
/* wait until the parameter is set up */
ret = wait_event_interruptible_timeout(data->ipc_wait,
data->created, msecs_to_jiffies(1000));
if (!ret) {
dev_err(dev, "%s: compress set param timed out!!! (%d)\n",
__func__, ret);
abox_rdma_mailbox_write(dev, COMPR_INTR_ACK, 0);
ret = -EBUSY;
goto error;
}
/* created instance */
data->handle_id = abox_rdma_mailbox_read(dev, COMPR_IP_ID);
dev_info(dev, "%s: codec id:0x%x, ret_val:0x%x, handle_id:0x%x\n",
__func__, data->codec_id,
abox_rdma_mailbox_read(dev, COMPR_RETURN_CMD),
data->handle_id);
dev_info(dev, "%s: allocated buffer address (0x%pad), size(0x%llx)\n",
__func__, &data->dma_addr, runtime->buffer_size);
#ifdef CONFIG_SND_ESA_SA_EFFECT
data->effect_on = false;
#endif
return 0;
error:
return ret;
}
static int abox_rdma_compr_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
struct abox_data *abox_data = platform_data->abox_data;
int id = platform_data->id;
dev_info(dev, "%s[%d]\n", __func__, id);
abox_wait_restored(abox_data);
/* init runtime data */
data->cstream = stream;
data->byte_offset = 0;
data->copied_total = 0;
data->received_total = 0;
data->sample_rate = 44100;
data->channels = 0x3; /* stereo channel mask */
data->eos = false;
data->start = false;
data->created = false;
data->bespoke_start = false;
pm_runtime_get_sync(dev);
abox_request_cpu_gear_dai(dev, abox_data, rtd->cpu_dai,
abox_data->cpu_gear_min);
return 0;
}
static int abox_rdma_compr_free(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
struct abox_data *abox_data = platform_data->abox_data;
int id = platform_data->id;
int ret = 0;
dev_info(dev, "%s[%d]\n", __func__, id);
if (data->eos) {
/* ALSA Framework callback to notify drain complete */
snd_compr_drain_notify(stream);
data->eos = 0;
dev_dbg(dev, "%s Call Drain notify to wakeup\n", __func__);
}
if (data->created) {
data->created = false;
data->exit_ack = 0;
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_DESTROY);
if (ret) {
dev_err(dev, "%s: can't send CMD_COMPR_DESTROY (%d)\n",
__func__, ret);
} else {
ret = wait_event_interruptible_timeout(data->exit_wait,
data->exit_ack, msecs_to_jiffies(1000));
if (!ret)
dev_err(dev, "%s: CMD_DESTROY timed out!!!\n",
__func__);
}
}
#ifdef COMPR_USE_FIXED_MEMORY
/* prevent kfree in ALSA */
stream->runtime->buffer = NULL;
#else
{
struct snd_compr_runtime *runtime = stream->runtime;
abox_iommu_unmap(&abox_data->pdev->dev, IOVA_COMPR_BUFFER(id));
#ifdef COMPR_USE_COPY
dma_free_coherent(dev, runtime->buffer_size, runtime->buffer,
data->dma_addr);
runtime->buffer = NULL;
#else
dma_unmap_single(dev, data->dma_addr, runtime->buffer_size,
DMA_TO_DEVICE);
#endif
}
#endif
abox_request_cpu_gear_dai(dev, abox_data, rtd->cpu_dai, 0);
pm_runtime_mark_last_busy(dev);
pm_runtime_put(dev);
return ret;
}
static int abox_rdma_compr_set_params(struct snd_compr_stream *stream,
struct snd_compr_params *params)
{
struct snd_compr_runtime *runtime = stream->runtime;
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct platform_device *pdev = to_platform_device(dev);
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
int id = platform_data->id;
int ret = 0;
dev_dbg(dev, "%s[%d]\n", __func__, id);
/* COMPR set_params */
memcpy(&data->codec_param, params, sizeof(data->codec_param));
data->byte_offset = 0;
data->copied_total = 0;
data->channels = data->codec_param.codec.ch_in;
data->sample_rate = data->codec_param.codec.sample_rate;
if (data->sample_rate == 0 ||
data->channels == 0) {
dev_err(dev, "%s: invalid parameters: sample(%u), ch(%u)\n",
__func__, data->sample_rate, data->channels);
return -EINVAL;
}
switch (params->codec.id) {
case SND_AUDIOCODEC_MP3:
data->codec_id = COMPR_MP3;
break;
case SND_AUDIOCODEC_AAC:
data->codec_id = COMPR_AAC;
break;
case SND_AUDIOCODEC_FLAC:
data->codec_id = COMPR_FLAC;
break;
default:
dev_err(dev, "%s: unknown codec id %d\n", __func__,
params->codec.id);
break;
}
ret = abox_rdma_compr_set_param(pdev, runtime);
if (ret) {
dev_err(dev, "%s: esa_compr_set_param fail(%d)\n", __func__,
ret);
return ret;
}
dev_info(dev, "%s: sample rate:%u, channels:%u\n", __func__,
data->sample_rate, data->channels);
return 0;
}
static int abox_rdma_compr_set_metadata(struct snd_compr_stream *stream,
struct snd_compr_metadata *metadata)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
dev_info(dev, "%s[%d]\n", __func__, id);
if (!metadata)
return -EINVAL;
if (metadata->key == SNDRV_COMPRESS_ENCODER_PADDING)
dev_dbg(dev, "%s: got encoder padding %u", __func__,
metadata->value[0]);
else if (metadata->key == SNDRV_COMPRESS_ENCODER_DELAY)
dev_dbg(dev, "%s: got encoder delay %u", __func__,
metadata->value[0]);
return 0;
}
static int abox_rdma_compr_trigger(struct snd_compr_stream *stream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
int id = platform_data->id;
int ret = 0;
dev_info(dev, "%s[%d](%d)\n", __func__, id, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
dev_info(dev, "SNDRV_PCM_TRIGGER_PAUSE_PUSH\n");
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_PAUSE);
if (ret < 0)
dev_err(dev, "%s: pause cmd failed(%d)\n", __func__,
ret);
break;
case SNDRV_PCM_TRIGGER_STOP:
dev_info(dev, "SNDRV_PCM_TRIGGER_STOP\n");
if (data->eos) {
/* ALSA Framework callback to notify drain complete */
snd_compr_drain_notify(stream);
data->eos = 0;
dev_dbg(dev, "interrupt drain and eos wait queues\n");
}
data->stop_ack = 0;
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_STOP);
if (ret < 0)
dev_err(dev, "%s: stop cmd failed (%d)\n",
__func__, ret);
ret = wait_event_interruptible_timeout(data->flush_wait,
data->stop_ack, msecs_to_jiffies(1000));
if (!ret) {
dev_err(dev, "CMD_STOP cmd timeout(%d)\n", ret);
ret = -ETIMEDOUT;
} else {
ret = 0;
}
data->start = false;
data->bespoke_start = false;
/* reset */
data->stop_ack = 0;
data->byte_offset = 0;
data->copied_total = 0;
data->received_total = 0;
break;
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
dev_info(dev, "%s: %s", __func__,
(cmd == SNDRV_PCM_TRIGGER_START) ?
"SNDRV_PCM_TRIGGER_START" :
"SNDRV_PCM_TRIGGER_PAUSE_RELEASE");
data->start = 1;
data->bespoke_start = true;
data->dirty = false;
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_START);
if (ret < 0)
dev_err(dev, "%s: start cmd failed\n", __func__);
break;
case SND_COMPR_TRIGGER_NEXT_TRACK:
pr_info("%s: SND_COMPR_TRIGGER_NEXT_TRACK\n", __func__);
break;
case SND_COMPR_TRIGGER_PARTIAL_DRAIN:
case SND_COMPR_TRIGGER_DRAIN:
dev_info(dev, "%s: %s", __func__,
(cmd == SND_COMPR_TRIGGER_DRAIN) ?
"SND_COMPR_TRIGGER_DRAIN" :
"SND_COMPR_TRIGGER_PARTIAL_DRAIN");
/* Make sure all the data is sent to F/W before sending EOS */
if (!data->start) {
dev_err(dev, "%s: stream is not in started state\n",
__func__);
ret = -EPERM;
break;
}
data->eos = 1;
dev_dbg(dev, "%s: CMD_EOS\n", __func__);
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_EOS);
if (ret < 0)
dev_err(dev, "%s: can't send eos (%d)\n", __func__,
ret);
else
pr_info("%s: Out of %s Drain", __func__,
(cmd == SND_COMPR_TRIGGER_DRAIN ?
"Full" : "Partial"));
break;
default:
break;
}
return 0;
}
static int abox_rdma_compr_pointer(struct snd_compr_stream *stream,
struct snd_compr_tstamp *tstamp)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
int id = platform_data->id;
unsigned int num_channel;
u32 pcm_size;
unsigned long flags;
dev_dbg(dev, "%s[%d]\n", __func__, id);
spin_lock_irqsave(&data->lock, flags);
tstamp->sampling_rate = data->sample_rate;
tstamp->byte_offset = data->byte_offset;
tstamp->copied_total = data->copied_total;
spin_unlock_irqrestore(&data->lock, flags);
pcm_size = abox_rdma_mailbox_read(dev, COMPR_RENDERED_PCM_SIZE);
/* set the number of channels */
num_channel = hweight32(data->channels);
if (pcm_size) {
tstamp->pcm_io_frames = pcm_size / (2 * num_channel);
dev_dbg(dev, "%s: pcm_size(%u), frame_count(%u), copied_total(%u)\n",
__func__, pcm_size, tstamp->pcm_io_frames,
tstamp->copied_total);
}
return 0;
}
static int abox_rdma_compr_mmap(struct snd_compr_stream *stream,
struct vm_area_struct *vma)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
struct snd_compr_runtime *runtime = stream->runtime;
dev_info(dev, "%s[%d]\n", __func__, id);
return dma_mmap_writecombine(dev, vma,
runtime->buffer,
virt_to_phys(runtime->buffer),
runtime->buffer_size);
}
static int abox_rdma_compr_ack(struct snd_compr_stream *stream, size_t bytes)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct abox_compr_data *data = &platform_data->compr_data;
int id = platform_data->id;
int ret;
dev_dbg(dev, "%s[%d]\n", __func__, id);
/* write mp3 data to firmware */
data->received_total += bytes;
abox_rdma_mailbox_write(dev, COMPR_SIZE_OF_FRAGMENT, bytes);
ret = abox_rdma_mailbox_send_cmd(dev, CMD_COMPR_WRITE);
return ret;
}
#ifdef COMPR_USE_COPY
static int abox_compr_write_data(struct snd_compr_stream *stream,
const char __user *buf, size_t count)
{
void *dstn;
size_t copy;
struct snd_compr_runtime *runtime = stream->runtime;
/* 64-bit Modulus */
u64 app_pointer = div64_u64(runtime->total_bytes_available,
runtime->buffer_size);
app_pointer = runtime->total_bytes_available -
(app_pointer * runtime->buffer_size);
dstn = runtime->buffer + app_pointer;
pr_debug("copying %ld at %lld\n",
(unsigned long)count, app_pointer);
if (count < runtime->buffer_size - app_pointer) {
if (copy_from_user(dstn, buf, count))
return -EFAULT;
} else {
copy = runtime->buffer_size - app_pointer;
if (copy_from_user(dstn, buf, copy))
return -EFAULT;
if (copy_from_user(runtime->buffer, buf + copy, count - copy))
return -EFAULT;
}
abox_rdma_compr_ack(stream, count);
return count;
}
static int abox_rdma_compr_copy(struct snd_compr_stream *stream,
char __user *buf, size_t count)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
dev_dbg(dev, "%s[%d]\n", __func__, id);
return abox_compr_write_data(stream, buf, count);
}
#endif
static int abox_rdma_compr_get_caps(struct snd_compr_stream *stream,
struct snd_compr_caps *caps)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
dev_info(dev, "%s[%d]\n", __func__, id);
memcpy(caps, &abox_rdma_compr_caps, sizeof(*caps));
return 0;
}
static int abox_rdma_compr_get_codec_caps(struct snd_compr_stream *stream,
struct snd_compr_codec_caps *codec)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
dev_info(dev, "%s[%d]\n", __func__, id);
return 0;
}
static void __maybe_unused __abox_rdma_compr_get_hw_params_legacy(
struct device *dev, struct snd_pcm_hw_params *params,
unsigned int upscale)
{
struct snd_mask *format_mask;
struct snd_interval *rate_interval;
rate_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
format_mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
switch (upscale) {
default:
/* fallback */
case 0:
/* 48kHz 16bit */
rate_interval->min = 48000;
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S16);
dev_info(dev, "%s: 48kHz 16bit\n", __func__);
break;
case 1:
/* 192kHz 24bit */
rate_interval->min = 192000;
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S24);
dev_info(dev, "%s: 192kHz 24bit\n", __func__);
break;
case 2:
/* 48kHz 24bit */
rate_interval->min = 48000;
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S24);
dev_info(dev, "%s: 48kHz 24bit\n", __func__);
break;
case 3:
/* 384kHz 32bit */
rate_interval->min = 384000;
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S32);
dev_info(dev, "%s: 384kHz 32bit\n", __func__);
break;
}
}
static void __maybe_unused __abox_rdma_compr_get_hw_params(struct device *dev,
struct snd_pcm_hw_params *params, unsigned int upscale)
{
unsigned int bit, ch, rate;
struct snd_mask *format_mask;
struct snd_interval *rate_interval, *ch_interval;
format_mask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
ch_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
rate_interval = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
bit = (upscale >> COMPR_BIT_SHIFT) & COMPR_BIT_MASK;
ch = (upscale >> COMPR_CH_SHIFT) & COMPR_CH_MASK;
rate = (upscale >> COMPR_RATE_SHIFT) & COMPR_RATE_MASK;
switch (bit) {
default:
/* fallback */
case 16:
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S16);
break;
case 24:
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S24);
break;
case 32:
snd_mask_set(format_mask, SNDRV_PCM_FORMAT_S32);
break;
}
ch_interval->min = ch ? ch : 2;
rate_interval->min = rate ? rate : 48000;
dev_info(dev, "%s: %ubit %uch %uHz\n", __func__, bit, ch, rate);
}
static void __abox_rdma_compr_set_hw_params_legacy(struct device *dev,
struct snd_pcm_hw_params *params)
{
struct abox_platform_data *pdata = dev_get_drvdata(dev);
struct abox_compr_data *cdata = &pdata->compr_data;
unsigned int width, rate;
unsigned int upscale;
rate = params_rate(params);
width = params_width(params);
if (rate <= 48000) {
if (rate != 48000)
dev_warn(dev, "unsupported offload rate: %u\n", rate);
if (width >= 24) {
upscale = 2;
dev_info(dev, "%s: 48kHz 24bit\n", __func__);
} else {
upscale = 0;
dev_info(dev, "%s: 48kHz 16bit\n", __func__);
}
} else {
if (rate != 192000)
dev_warn(dev, "unsupported offload rate: %u\n", rate);
upscale = 1;
dev_info(dev, "%s: 192kHz 24bit\n", __func__);
}
if (abox_rdma_mailbox_read(dev, COMPR_UPSCALE) != upscale) {
abox_rdma_mailbox_write(dev, COMPR_UPSCALE, upscale);
cdata->dirty = true;
}
}
static void __abox_rdma_compr_set_hw_params(struct device *dev,
struct snd_pcm_hw_params *params)
{
struct abox_platform_data *pdata = dev_get_drvdata(dev);
struct abox_compr_data *cdata = &pdata->compr_data;
unsigned int bit, ch, rate, upscale;
bit = params_width(params);
ch = params_channels(params);
rate = params_rate(params);
upscale = (bit >> COMPR_BIT_SHIFT) & COMPR_BIT_MASK;
upscale |= (ch >> COMPR_CH_SHIFT) & COMPR_CH_MASK;
upscale |= (rate >> COMPR_RATE_SHIFT) & COMPR_RATE_MASK;
if (abox_rdma_mailbox_read(dev, COMPR_UPSCALE) != upscale) {
abox_rdma_mailbox_write(dev, COMPR_UPSCALE, upscale);
cdata->dirty = true;
}
dev_info(dev, "%s: %ubit %uch %uHz\n", __func__, bit, ch, rate);
}
static int abox_rdma_compr_get_hw_params(struct snd_compr_stream *stream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
unsigned int upscale;
dev_dbg(dev, "%s[%d]\n", __func__, id);
abox_hw_params_fixup_helper(rtd, params, stream->direction);
upscale = abox_rdma_mailbox_read(dev, COMPR_UPSCALE);
if (upscale <= 0xF) {
__abox_rdma_compr_set_hw_params_legacy(dev, params);
upscale = abox_rdma_mailbox_read(dev, COMPR_UPSCALE);
__abox_rdma_compr_get_hw_params_legacy(dev, params, upscale);
} else {
__abox_rdma_compr_set_hw_params(dev, params);
upscale = abox_rdma_mailbox_read(dev, COMPR_UPSCALE);
__abox_rdma_compr_get_hw_params(dev, params, upscale);
}
return 0;
}
static struct snd_compr_ops abox_rdma_compr_ops = {
.open = abox_rdma_compr_open,
.free = abox_rdma_compr_free,
.set_params = abox_rdma_compr_set_params,
.set_metadata = abox_rdma_compr_set_metadata,
.trigger = abox_rdma_compr_trigger,
.pointer = abox_rdma_compr_pointer,
#ifdef COMPR_USE_COPY
.copy = abox_rdma_compr_copy,
#endif
.mmap = abox_rdma_compr_mmap,
.ack = abox_rdma_compr_ack,
.get_caps = abox_rdma_compr_get_caps,
.get_codec_caps = abox_rdma_compr_get_codec_caps,
.get_hw_params = abox_rdma_compr_get_hw_params,
};
static int abox_rdma_compr_vol_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int id = platform_data->id;
unsigned int volumes[2];
volumes[0] = (unsigned int)ucontrol->value.integer.value[0];
volumes[1] = (unsigned int)ucontrol->value.integer.value[1];
dev_dbg(dev, "%s[%d]: left_vol=%d right_vol=%d\n",
__func__, id, volumes[0], volumes[1]);
abox_rdma_mailbox_write(dev, mc->reg, volumes[0]);
abox_rdma_mailbox_write(dev, mc->rreg, volumes[1]);
return 0;
}
static int abox_rdma_compr_vol_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
int id = platform_data->id;
unsigned int volumes[2];
volumes[0] = abox_rdma_mailbox_read(dev, mc->reg);
volumes[1] = abox_rdma_mailbox_read(dev, mc->rreg);
dev_dbg(dev, "%s[%d]: left_vol=%d right_vol=%d\n",
__func__, id, volumes[0], volumes[1]);
ucontrol->value.integer.value[0] = volumes[0];
ucontrol->value.integer.value[1] = volumes[1];
return 0;
}
static const DECLARE_TLV_DB_LINEAR(abox_rdma_compr_vol_gain, 0,
COMPRESSED_LR_VOL_MAX_STEPS);
static int abox_rdma_compr_format_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
int id = platform_data->id;
unsigned int upscale;
dev_warn(dev, "%s is deprecated\n", kcontrol->id.name);
upscale = ucontrol->value.enumerated.item[0];
dev_dbg(dev, "%s[%d]: scale=%u\n", __func__, id, upscale);
abox_rdma_mailbox_write(dev, e->reg, upscale);
return 0;
}
static int abox_rdma_compr_format_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
int id = platform_data->id;
unsigned int upscale;
dev_warn(dev, "%s is deprecated\n", kcontrol->id.name);
upscale = abox_rdma_mailbox_read(dev, e->reg);
dev_dbg(dev, "%s[%d]: upscale=%u\n", __func__, id, upscale);
if (upscale >= e->items) {
unsigned int bit, rate;
bit = (upscale >> COMPR_BIT_SHIFT) & COMPR_BIT_MASK ;
rate = (upscale >> COMPR_RATE_SHIFT) & COMPR_RATE_MASK;
if (rate == 384000) {
upscale = 3;
} else if (rate == 192000) {
upscale = 1;
} else if (rate == 48000) {
if (bit == 24)
upscale = 2;
else
upscale = 0;
} else {
upscale = 0;
}
}
ucontrol->value.enumerated.item[0] = upscale;
return 0;
}
static const char * const abox_rdma_compr_format_text[] = {
"48kHz 16bit",
"192kHz 24bit",
"48kHz 24bit",
"384kHz 32bit",
};
static SOC_ENUM_SINGLE_DECL(abox_rdma_compr_format,
COMPR_UPSCALE, 0,
abox_rdma_compr_format_text);
static const struct snd_kcontrol_new abox_rdma_compr_controls[] = {
SOC_DOUBLE_R_EXT_TLV("ComprTx0 Volume", COMPR_LEFT_VOL, COMPR_RIGHT_VOL,
0, COMPRESSED_LR_VOL_MAX_STEPS, 0,
abox_rdma_compr_vol_get, abox_rdma_compr_vol_put,
abox_rdma_compr_vol_gain),
SOC_ENUM_EXT("ComprTx0 Format", abox_rdma_compr_format,
abox_rdma_compr_format_get, abox_rdma_compr_format_put),
SOC_SINGLE("ComprTx0 Bit", COMPR_UPSCALE, COMPR_BIT_SHIFT,
COMPR_BIT_MASK, 0),
SOC_SINGLE("ComprTx0 Ch", COMPR_UPSCALE, COMPR_CH_SHIFT,
COMPR_CH_MASK, 0),
SOC_SINGLE("ComprTx0 Rate", COMPR_UPSCALE, COMPR_RATE_SHIFT,
COMPR_RATE_MASK, 0),
};
static const struct snd_pcm_hardware abox_rdma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID,
.formats = ABOX_SAMPLE_FORMATS,
.channels_min = 1,
.channels_max = 8,
.buffer_bytes_max = BUFFER_BYTES_MAX,
.period_bytes_min = PERIOD_BYTES_MIN,
.period_bytes_max = PERIOD_BYTES_MAX,
.periods_min = BUFFER_BYTES_MAX / PERIOD_BYTES_MAX,
.periods_max = BUFFER_BYTES_MAX / PERIOD_BYTES_MIN,
};
static irqreturn_t abox_rdma_ipc_handler(int ipc, void *dev_id,
ABOX_IPC_MSG *msg)
{
struct abox_data *abox_data = dev_id;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg->msg.pcmtask;
int id = pcmtask_msg->channel_id;
struct abox_platform_data *data;
struct device *dev;
if (id >= ARRAY_SIZE(abox_data->pdev_rdma) || !abox_data->pdev_rdma[id])
return IRQ_NONE;
dev = &abox_data->pdev_rdma[id]->dev;
data = dev_get_drvdata(dev);
dev_dbg(dev, "%s(%d)\n", __func__, pcmtask_msg->msgtype);
switch (pcmtask_msg->msgtype) {
case PCM_PLTDAI_POINTER:
data->pointer = pcmtask_msg->param.pointer;
snd_pcm_period_elapsed(data->substream);
break;
case PCM_PLTDAI_ACK:
data->ack_enabled = !!pcmtask_msg->param.trigger;
break;
case PCM_PLTDAI_CLOSED:
complete(&data->closed);
break;
default:
dev_warn(dev, "unknown message: %d\n", pcmtask_msg->msgtype);
return IRQ_NONE;
}
return IRQ_HANDLED;
}
static int abox_rdma_progress(struct abox_platform_data *data)
{
unsigned int val = 0;
regmap_read(data->abox_data->regmap, ABOX_RDMA_STATUS(data->id), &val);
return !!(val & ABOX_RDMA_PROGRESS_MASK);
}
static void abox_rdma_disable_barrier(struct device *dev,
struct abox_platform_data *data)
{
int id = data->id;
struct abox_data *abox_data = data->abox_data;
u64 timeout = local_clock() + ABOX_DMA_TIMEOUT_NS;
while (abox_rdma_progress(data)) {
if (local_clock() <= timeout) {
cond_resched();
continue;
}
dev_warn_ratelimited(dev, "RDMA disable timeout[%d]\n", id);
abox_dbg_dump_simple(dev, abox_data, "RDMA disable timeout");
break;
}
}
static int abox_rdma_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
struct abox_data *abox_data = data->abox_data;
struct device *dev_abox = &data->abox_data->pdev->dev;
struct snd_pcm_runtime *runtime = substream->runtime;
int id = data->id;
size_t buffer_bytes = PAGE_ALIGN(params_buffer_bytes(params));
unsigned int freq;
int sbank_size, burst_len;
int ret;
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
dev_dbg(dev, "%s[%d]\n", __func__, id);
if (data->buf_type == BUFFER_TYPE_DMA) {
if (data->dmab.bytes < buffer_bytes) {
abox_iommu_unmap(dev_abox, IOVA_RDMA_BUFFER(id));
snd_dma_free_pages(&data->dmab);
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
dev,
buffer_bytes,
&data->dmab);
if (ret < 0)
return ret;
ret = abox_iommu_map(dev_abox, IOVA_RDMA_BUFFER(id),
data->dmab.addr, data->dmab.bytes,
data->dmab.area);
if (ret < 0)
return ret;
dev_info(dev, "dma buffer changed\n");
}
} else if (data->buf_type == BUFFER_TYPE_ION) {
dev_info(dev, "ion_buffer %s bytes(%d) size(%d)\n",
__func__,
buffer_bytes, data->ion_buf.size);
} else {
dev_err(dev, "buf_type is not defined\n");
}
snd_pcm_set_runtime_buffer(substream, &data->dmab);
runtime->dma_bytes = params_buffer_bytes(params);
sbank_size = abox_cmpnt_adjust_sbank(data->cmpnt,
rtd->cpu_dai->id, params);
if (sbank_size > 0 && sbank_size < SZ_128)
burst_len = 0x1;
else
burst_len = 0x8;
ret = snd_soc_component_update_bits(data->cmpnt,
ABOX_RDMA_CTRL0(id),
ABOX_RDMA_BURST_LEN_MASK,
burst_len << ABOX_RDMA_BURST_LEN_L);
if (ret < 0)
dev_err(dev, "burst length write error: %d\n", ret);
pcmtask_msg->channel_id = id;
msg.ipcid = IPC_PCMPLAYBACK;
msg.task_id = pcmtask_msg->channel_id = id;
pcmtask_msg->msgtype = PCM_SET_BUFFER;
pcmtask_msg->param.setbuff.phyaddr = IOVA_RDMA_BUFFER(id);
pcmtask_msg->param.setbuff.size = params_period_bytes(params);
pcmtask_msg->param.setbuff.count = params_periods(params);
ret = abox_rdma_request_ipc(data, &msg, 0, 0);
if (ret < 0)
return ret;
pcmtask_msg->msgtype = PCM_PLTDAI_HW_PARAMS;
pcmtask_msg->param.hw_params.sample_rate = params_rate(params);
pcmtask_msg->param.hw_params.bit_depth = params_width(params);
pcmtask_msg->param.hw_params.channels = params_channels(params);
ret = abox_rdma_request_ipc(data, &msg, 0, 0);
if (ret < 0)
return ret;
if (params_rate(params) > 48000)
abox_request_cpu_gear_dai(dev, abox_data, rtd->cpu_dai,
abox_data->cpu_gear_min - 1);
freq = data->pm_qos_cl0[abox_get_rate_type(params_rate(params))];
abox_request_cl0_freq_dai(dev, rtd->cpu_dai, freq);
freq = data->pm_qos_cl1[abox_get_rate_type(params_rate(params))];
abox_request_cl1_freq_dai(dev, rtd->cpu_dai, freq);
freq = data->pm_qos_cl2[abox_get_rate_type(params_rate(params))];
abox_request_cl2_freq_dai(dev, rtd->cpu_dai, freq);
dev_info(dev, "%s:Total=%zu PrdSz=%u(%u) #Prds=%u rate=%u, width=%d, channels=%u\n",
snd_pcm_stream_str(substream), runtime->dma_bytes,
params_period_size(params), params_period_bytes(params),
params_periods(params), params_rate(params),
params_width(params), params_channels(params));
return 0;
}
static int abox_rdma_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
dev_dbg(dev, "%s[%d]\n", __func__, id);
msg.ipcid = IPC_PCMPLAYBACK;
pcmtask_msg->msgtype = PCM_PLTDAI_HW_FREE;
msg.task_id = pcmtask_msg->channel_id = id;
abox_rdma_request_ipc(data, &msg, 0, 0);
abox_request_cl0_freq_dai(dev, rtd->cpu_dai, 0);
abox_request_cl1_freq_dai(dev, rtd->cpu_dai, 0);
abox_request_cl2_freq_dai(dev, rtd->cpu_dai, 0);
switch (data->type) {
default:
abox_rdma_disable_barrier(dev, data);
break;
}
snd_pcm_set_runtime_buffer(substream, NULL);
return 0;
}
static int abox_rdma_prepare(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
int ret;
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
dev_dbg(dev, "%s[%d]\n", __func__, id);
data->pointer = IOVA_RDMA_BUFFER(id);
/* Clear preallocated DMA buffer */
switch (data->type) {
case PLATFORM_NORMAL:
case PLATFORM_SYNC:
if (substream->runtime->dma_area)
memset(substream->runtime->dma_area, 0,
substream->runtime->dma_bytes);
break;
default:
/* nothing to do */
break;
}
msg.ipcid = IPC_PCMPLAYBACK;
pcmtask_msg->msgtype = PCM_PLTDAI_PREPARE;
msg.task_id = pcmtask_msg->channel_id = id;
ret = abox_rdma_request_ipc(data, &msg, 0, 0);
return ret;
}
static int abox_rdma_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
int ret;
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
dev_info(dev, "%s[%d](%d)\n", __func__, id, cmd);
msg.ipcid = IPC_PCMPLAYBACK;
pcmtask_msg->msgtype = PCM_PLTDAI_TRIGGER;
msg.task_id = pcmtask_msg->channel_id = id;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
pcmtask_msg->param.trigger = 1;
ret = abox_rdma_request_ipc(data, &msg, 1, 0);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
pcmtask_msg->param.trigger = 0;
ret = abox_rdma_request_ipc(data, &msg, 1, 0);
switch (data->type) {
case PLATFORM_REALTIME:
msg.ipcid = IPC_ERAP;
msg.msg.erap.msgtype = REALTIME_STOP;
ret = abox_rdma_request_ipc(data, &msg, 1, 0);
break;
default:
break;
}
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static snd_pcm_uframes_t abox_rdma_pointer(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
struct snd_pcm_runtime *runtime = substream->runtime;
int id = data->id;
ssize_t pointer;
unsigned int status = 0;
bool progress;
regmap_read(data->abox_data->regmap, ABOX_RDMA_STATUS(id), &status);
progress = !!(status & ABOX_RDMA_PROGRESS_MASK);
if (data->pointer >= IOVA_RDMA_BUFFER(id)) {
pointer = data->pointer - IOVA_RDMA_BUFFER(id);
} else if (((data->type == PLATFORM_NORMAL) ||
(data->type == PLATFORM_SYNC)) && progress) {
ssize_t offset, count;
ssize_t buffer_bytes, period_bytes;
buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
period_bytes = snd_pcm_lib_period_bytes(substream);
offset = (((status & ABOX_RDMA_RBUF_OFFSET_MASK) >>
ABOX_RDMA_RBUF_OFFSET_L) << 4);
if (period_bytes > ABOX_RDMA_RBUF_CNT_MASK + 1)
count = 0;
else
count = (status & ABOX_RDMA_RBUF_CNT_MASK);
while ((offset % period_bytes) && (buffer_bytes >= 0)) {
buffer_bytes -= period_bytes;
if ((buffer_bytes & offset) == offset)
offset = buffer_bytes;
}
pointer = offset + count;
} else {
pointer = 0;
}
dev_dbg(dev, "%s[%d]: pointer=%08zx\n", __func__, id, pointer);
return bytes_to_frames(runtime, pointer);
}
static int abox_rdma_open(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
struct abox_data *abox_data = data->abox_data;
int id = data->id;
int ret;
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
dev_dbg(dev, "%s[%d]\n", __func__, id);
abox_wait_restored(abox_data);
if (data->type == PLATFORM_CALL) {
if (abox_cpu_gear_idle(dev, ABOX_CPU_GEAR_CALL_VSS))
abox_request_cpu_gear_sync(dev, abox_data,
ABOX_CPU_GEAR_CALL_KERNEL,
ABOX_CPU_GEAR_MAX, rtd->cpu_dai->name);
ret = abox_vss_notify_call(dev, abox_data, 1);
if (ret < 0)
dev_warn(dev, "call notify failed: %d\n", ret);
}
abox_request_cpu_gear_dai(dev, abox_data, rtd->cpu_dai,
abox_data->cpu_gear_min);
snd_soc_set_runtime_hwparams(substream, &abox_rdma_hardware);
data->substream = substream;
msg.ipcid = IPC_PCMPLAYBACK;
pcmtask_msg->msgtype = PCM_PLTDAI_OPEN;
msg.task_id = pcmtask_msg->channel_id = id;
ret = abox_rdma_request_ipc(data, &msg, 0, 0);
return ret;
}
static int abox_rdma_close(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
struct abox_data *abox_data = data->abox_data;
int id = data->id;
int ret;
long time;
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
dev_dbg(dev, "%s[%d]\n", __func__, id);
data->substream = NULL;
msg.ipcid = IPC_PCMPLAYBACK;
pcmtask_msg->msgtype = PCM_PLTDAI_CLOSE;
msg.task_id = pcmtask_msg->channel_id = id;
ret = abox_rdma_request_ipc(data, &msg, 0, 1);
abox_request_cpu_gear_dai(dev, abox_data, rtd->cpu_dai, 0);
if (data->type == PLATFORM_CALL) {
abox_request_cpu_gear(dev, abox_data, ABOX_CPU_GEAR_CALL_KERNEL,
ABOX_CPU_GEAR_MIN, rtd->cpu_dai->name);
ret = abox_vss_notify_call(dev, abox_data, 0);
if (ret < 0)
dev_warn(dev, "call notify failed: %d\n", ret);
}
time = wait_for_completion_timeout(&data->closed,
nsecs_to_jiffies(ABOX_DMA_TIMEOUT_NS));
if (time == 0)
dev_err(dev, "%s: timeout\n", __func__);
/* Release ASRC to reuse it in other DMA */
abox_cmpnt_asrc_release(abox_data->cmpnt, SNDRV_PCM_STREAM_PLAYBACK, id);
return ret;
}
static int abox_rdma_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
struct snd_pcm_runtime *runtime = substream->runtime;
dev_info(dev, "%s[%d]\n", __func__, id);
/* Increased cpu gear for sound camp.
* Only sound camp uses mmap now.
*/
abox_request_cpu_gear_dai(dev, data->abox_data, rtd->cpu_dai,
data->abox_data->cpu_gear_min - 1);
if (data->buf_type == BUFFER_TYPE_ION)
return dma_buf_mmap(data->ion_buf.dma_buf, vma, 0);
else
return dma_mmap_writecombine(dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
}
static int abox_rdma_ack(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_uframes_t appl_ptr = runtime->control->appl_ptr;
snd_pcm_uframes_t appl_ofs = appl_ptr % runtime->buffer_size;
ssize_t appl_bytes = frames_to_bytes(runtime, appl_ofs);
ABOX_IPC_MSG msg;
struct IPC_PCMTASK_MSG *pcmtask_msg = &msg.msg.pcmtask;
if (!data->ack_enabled)
return 0;
dev_dbg(dev, "%s[%d]: %zd\n", __func__, id, appl_bytes);
msg.ipcid = IPC_PCMPLAYBACK;
pcmtask_msg->msgtype = PCM_PLTDAI_ACK;
pcmtask_msg->param.pointer = (unsigned int)appl_bytes;
msg.task_id = pcmtask_msg->channel_id = id;
return abox_rdma_request_ipc(data, &msg, 1, 0);
}
static struct snd_pcm_ops abox_rdma_ops = {
.open = abox_rdma_open,
.close = abox_rdma_close,
.ioctl = snd_pcm_lib_ioctl,
.hw_params = abox_rdma_hw_params,
.hw_free = abox_rdma_hw_free,
.prepare = abox_rdma_prepare,
.trigger = abox_rdma_trigger,
.pointer = abox_rdma_pointer,
.mmap = abox_rdma_mmap,
.ack = abox_rdma_ack,
};
static int abox_rdma_fio_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long _arg);
#ifdef CONFIG_COMPAT
static int abox_rdma_fio_compat_ioctl(struct snd_hwdep *hw,
struct file *file,
unsigned int cmd, unsigned long _arg);
#endif
static int abox_pcm_add_hwdep_dev(struct snd_soc_pcm_runtime *runtime,
struct abox_platform_data *data)
{
struct snd_hwdep *hwdep;
int rc;
char id[] = "ABOX_MMAP_FD_NN";
snprintf(id, sizeof(id), "ABOX_MMAP_FD_%d", SNDRV_PCM_STREAM_PLAYBACK);
pr_debug("%s: pcm dev %d\n", __func__, runtime->pcm->device);
rc = snd_hwdep_new(runtime->card->snd_card,
&id[0],
0 + runtime->pcm->device,
&hwdep);
if (!hwdep || rc < 0) {
pr_err("%s: hwdep intf failed to create %s - hwdep\n", __func__,
id);
return rc;
}
hwdep->iface = 0;
hwdep->private_data = data;
hwdep->ops.ioctl = abox_rdma_fio_ioctl;
hwdep->ops.ioctl_compat = abox_rdma_fio_compat_ioctl;
data->hwdep = hwdep;
return 0;
}
static int abox_rdma_vol_factor_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
unsigned int volumes;
unsigned int value = 0;
int ret = 0;
ret = snd_soc_component_read(platform_data->cmpnt,
ABOX_RDMA_VOL_FACTOR(id), &value);
if (ret < 0) {
dev_err(dev, "sfr access failed: %d\n", ret);
return ret;
}
volumes = (value & ABOX_RDMA_VOL_FACTOR_MASK);
dev_dbg(dev, "%s[%d]: %u\n", __func__, id, volumes);
ucontrol->value.integer.value[0] = volumes;
return 0;
}
static int abox_rdma_vol_factor_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
struct device *dev = platform->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
int id = platform_data->id;
unsigned int volumes;
unsigned int value = 0;
int ret = 0;
volumes = (unsigned int)ucontrol->value.integer.value[0];
dev_dbg(dev, "%s[%d]: %u\n", __func__, id, volumes);
ret = snd_soc_component_read(platform_data->cmpnt,
ABOX_RDMA_VOL_FACTOR(id), &value);
if (ret < 0) {
dev_err(dev, "sfr access failed: %d\n", ret);
return ret;
}
set_value_by_name(value, ABOX_RDMA_VOL_FACTOR, volumes);
ret = snd_soc_component_write(platform_data->cmpnt,
ABOX_RDMA_VOL_FACTOR(id), value);
if (ret < 0) {
dev_err(dev, "sfr access failed: %d\n", ret);
return ret;
}
return 0;
}
static const DECLARE_TLV_DB_LINEAR(abox_rdma_vol_factor_gain, 0,
DMA_VOL_FACTOR_MAX_STEPS);
struct snd_kcontrol_new abox_rdma_vol_factor_controls[] = {
SOC_SINGLE_EXT_TLV(NULL, 0, 0, DMA_VOL_FACTOR_MAX_STEPS, 0,
abox_rdma_vol_factor_get, abox_rdma_vol_factor_put,
abox_rdma_vol_factor_gain),
};
static int abox_rdma_new(struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_platform *platform = runtime->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
struct device *dev_abox = &data->abox_data->pdev->dev;
int id = data->id;
size_t buffer_bytes = data->dmab.bytes;
int ret;
if (data->buf_type == BUFFER_TYPE_ION) {
buffer_bytes = BUFFER_ION_BYTES_MAX;
data->ion_buf.fd = -2;
ret = abox_ion_alloc(data,
&data->ion_buf,
IOVA_RDMA_BUFFER(id),
buffer_bytes,
0);
if (ret < 0)
return ret;
/* update buffer infomation using ion allocated buffer */
data->dmab.area = data->ion_buf.kva;
data->dmab.addr = data->ion_buf.iova;
ret = abox_pcm_add_hwdep_dev(runtime, data);
if (ret < 0) {
dev_err(dev, "snd_hwdep_new() failed: %d\n", ret);
return ret;
}
} else {
ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
dev,
buffer_bytes,
&data->dmab);
if (ret < 0)
return ret;
ret = abox_iommu_map(dev_abox, IOVA_RDMA_BUFFER(id),
data->dmab.addr, data->dmab.bytes,
data->dmab.area);
}
return ret;
}
static void abox_rdma_free(struct snd_pcm *pcm)
{
struct snd_soc_pcm_runtime *runtime = pcm->private_data;
struct snd_soc_platform *platform = runtime->platform;
struct device *dev = platform->dev;
struct abox_platform_data *data = dev_get_drvdata(dev);
struct device *dev_abox = &data->abox_data->pdev->dev;
int id = data->id;
int ret = 0;
if (data->buf_type == BUFFER_TYPE_ION) {
ret = abox_ion_free(data);
if (ret < 0)
dev_err(dev, "abox_ion_free() failed %d\n", ret);
if (data->hwdep) {
snd_device_free(runtime->card->snd_card, data->hwdep);
data->hwdep = NULL;
}
} else {
abox_iommu_unmap(dev_abox, IOVA_RDMA_BUFFER(id));
snd_dma_free_pages(&data->dmab);
}
}
static int abox_rdma_probe(struct snd_soc_platform *platform)
{
struct device *dev = platform->dev;
struct abox_platform_data *data =
snd_soc_platform_get_drvdata(platform);
int ret;
if (data->type == PLATFORM_COMPRESS) {
struct abox_compr_data *compr_data = &data->compr_data;
struct device *dev_abox = &data->abox_data->pdev->dev;
ret = snd_soc_add_platform_controls(platform,
abox_rdma_compr_controls,
ARRAY_SIZE(abox_rdma_compr_controls));
if (ret < 0) {
dev_err(dev, "add platform control failed: %d\n", ret);
return ret;
}
#ifdef COMPR_USE_FIXED_MEMORY
compr_data->dma_size = abox_rdma_compr_caps.max_fragments *
abox_rdma_compr_caps.max_fragment_size;
compr_data->dma_area = dmam_alloc_coherent(dev,
compr_data->dma_size, &compr_data->dma_addr,
GFP_KERNEL);
if (compr_data->dma_area == NULL) {
dev_err(dev, "dma memory allocation failed: %lu\n",
PTR_ERR(compr_data->dma_area));
return -ENOMEM;
}
ret = abox_iommu_map(dev_abox, IOVA_COMPR_BUFFER(data->id),
compr_data->dma_addr,
PAGE_ALIGN(compr_data->dma_size),
compr_data->dma_area);
if (ret < 0) {
dev_err(dev, "dma memory iommu map failed: %d\n", ret);
return ret;
}
#endif
}
if (data->buf_type == BUFFER_TYPE_ION) {
abox_rdma_vol_factor_controls[0].name = kasprintf(GFP_KERNEL,
"RDMA VOL FACTOR%d", data->id);
ret = snd_soc_add_platform_controls(platform,
abox_rdma_vol_factor_controls,
ARRAY_SIZE(abox_rdma_vol_factor_controls));
kfree_const(abox_rdma_vol_factor_controls[0].name);
if (ret < 0) {
dev_err(dev, "add platform control failed: %d\n", ret);
return ret;
}
}
return 0;
}
static int abox_rdma_remove(struct snd_soc_platform *platform)
{
struct abox_platform_data *data =
snd_soc_platform_get_drvdata(platform);
if (data->type == PLATFORM_COMPRESS) {
struct device *dev_abox = &data->abox_data->pdev->dev;
abox_iommu_unmap(dev_abox, IOVA_COMPR_BUFFER(data->id));
}
return 0;
}
struct snd_soc_platform_driver abox_rdma = {
.probe = abox_rdma_probe,
.remove = abox_rdma_remove,
.compr_ops = &abox_rdma_compr_ops,
.ops = &abox_rdma_ops,
.pcm_new = abox_rdma_new,
.pcm_free = abox_rdma_free,
};
static int abox_rdma_runtime_suspend(struct device *dev)
{
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
dev_info(dev, "%s[%d]\n", __func__, id);
abox_mailbox_save(dev);
return 0;
}
static int abox_rdma_runtime_resume(struct device *dev)
{
struct abox_platform_data *data = dev_get_drvdata(dev);
int id = data->id;
dev_info(dev, "%s[%d]\n", __func__, id);
abox_mailbox_restore(dev);
return 0;
}
static int abox_rdma_cmpnt_probe(struct snd_soc_component *cmpnt)
{
struct device *dev = cmpnt->dev;
struct abox_platform_data *data = snd_soc_component_get_drvdata(cmpnt);
u32 id;
int ret;
dev_dbg(dev, "%s\n", __func__);
data->cmpnt = cmpnt;
snd_soc_component_init_regmap(cmpnt, data->abox_data->regmap);
abox_cmpnt_register_rdma(data->abox_data->pdev, to_platform_device(dev),
data->id, snd_soc_component_get_dapm(cmpnt),
data->of_data->get_dai_name(dev, data->id));
ret = of_samsung_property_read_u32(dev, dev->of_node, "asrc-id", &id);
if (ret >= 0) {
ret = abox_cmpnt_asrc_lock(data->abox_data->cmpnt,
SNDRV_PCM_STREAM_PLAYBACK, data->id, id);
if (ret < 0)
dev_err(dev, "asrc id lock failed\n");
else
dev_info(dev, "asrc id locked: %u\n", id);
}
return 0;
}
static void abox_rdma_cmpnt_remove(struct snd_soc_component *cmpnt)
{
struct device *dev = cmpnt->dev;
dev_info(dev, "%s\n", __func__);
}
static const struct snd_soc_component_driver abox_rdma_cmpnt = {
.probe = abox_rdma_cmpnt_probe,
.remove = abox_rdma_cmpnt_remove,
};
static int abox_rdma_dai_bespoke_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct device *dev = dai->dev;
struct abox_platform_data *platform_data = dev_get_drvdata(dev);
struct snd_soc_pcm_runtime *runtime = substream->private_data;
struct snd_soc_platform *platform = runtime->platform;
struct abox_compr_data *compr_data = &platform_data->compr_data;
struct snd_compr_stream *cstream = compr_data->cstream;
struct snd_compr_params *params = &compr_data->codec_param;
int stream = substream->stream;
struct list_head *clients = &runtime->dpcm[stream].be_clients;
struct snd_soc_dpcm *dpcm;
dev_info(dev, "%s(%d)\n", __func__, cmd);
if (!platform || !platform->driver->compr_ops ||
!platform->driver->compr_ops->trigger) {
dev_err(dev, "%s: invalid substream\n", __func__);
return -EINVAL;
}
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (compr_data->bespoke_start == false) {
if (compr_data->dirty) {
platform->driver->compr_ops->set_params(cstream,
params);
platform->driver->compr_ops->trigger(cstream,
SNDRV_PCM_TRIGGER_START);
}
list_for_each_entry(dpcm, clients, list_be) {
struct snd_soc_dai *be_dai = dpcm->be->cpu_dai;
if (be_dai->driver->ops &&
be_dai->driver->ops->trigger)
be_dai->driver->ops->trigger(substream,
cmd,
be_dai);
}
compr_data->bespoke_start = true;
} else {
dev_info(dev, "%s(%d) already started\n",
__func__, cmd);
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (compr_data->bespoke_start == true) {
list_for_each_entry(dpcm, clients, list_be) {
struct snd_soc_dai *be_dai = dpcm->be->cpu_dai;
if (be_dai->driver->ops && be_dai->driver->ops->trigger)
be_dai->driver->ops->trigger(substream, cmd,
be_dai);
}
if (compr_data->dirty)
platform->driver->compr_ops->trigger(cstream,
SNDRV_PCM_TRIGGER_STOP);
compr_data->bespoke_start = false;
} else {
dev_info(dev, "%s(%d) already stopped\n",
__func__, cmd);
}
break;
default:
return -EINVAL;
}
return 0;
}
const struct snd_soc_dai_ops abox_rdma_dai_ops = {
.bespoke_trigger = abox_rdma_dai_bespoke_trigger,
};
static struct snd_soc_dai_driver abox_rdma_dai_drv = {
.ops = &abox_rdma_dai_ops,
.playback = {
.channels_min = 1,
.channels_max = 8,
.rates = ABOX_SAMPLING_RATES,
.rate_min = 8000,
.rate_max = 384000,
.formats = ABOX_SAMPLE_FORMATS,
},
};
enum abox_dai abox_rdma_get_dai_id(int id)
{
int ret = ABOX_RDMA0 + id;
return (ret < ABOX_WDMA0) ? ret : -EINVAL;
}
const char *abox_rdma_get_dai_name(struct device *dev, int id)
{
return devm_kasprintf(dev, GFP_KERNEL, "RDMA%d", id);
}
const char *abox_rdma_get_str_name(struct device *dev, int id, int stream)
{
const char *ret;
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
ret = devm_kasprintf(dev, GFP_KERNEL, "RDMA%d Playback", id);
else
ret = ERR_PTR(-EINVAL);
return ret;
}
static const struct of_device_id samsung_abox_rdma_match[] = {
{
.compatible = "samsung,abox-rdma",
.data = (void *)&(struct abox_dma_of_data){
.get_dai_id = abox_rdma_get_dai_id,
.get_dai_name = abox_rdma_get_dai_name,
.get_str_name = abox_rdma_get_str_name,
.base_dai_drv = &abox_rdma_dai_drv,
},
},
{},
};
MODULE_DEVICE_TABLE(of, samsung_abox_rdma_match);
static int abox_rdma_fio_common_ioctl(struct snd_hwdep *hw, struct file *filp,
unsigned int cmd, unsigned long __user *_arg)
{
struct abox_platform_data *data = hw->private_data;
struct device *dev;
struct snd_pcm_mmap_fd mmap_fd;
int ret = 0;
unsigned long arg;
if (!data || (((cmd >> 8) & 0xff) != 'U'))
return -ENOTTY;
dev = &data->pdev->dev;
if (get_user(arg, _arg))
return -EFAULT;
dev_dbg(dev, "%s: ioctl(0x%x)\n", __func__, cmd);
switch (cmd) {
case SNDRV_PCM_IOCTL_MMAP_DATA_FD:
ret = abox_mmap_fd(data, &mmap_fd);
if (ret < 0)
dev_err(dev, "%s MMAP_FD failed: %d\n", __func__, ret);
if (copy_to_user(_arg, &mmap_fd, sizeof(mmap_fd)))
return -EFAULT;
break;
default:
dev_err(dev, "unknown ioctl = 0x%x\n", cmd);
return -EINVAL;
}
return 0;
}
static int abox_rdma_fio_ioctl(struct snd_hwdep *hw, struct file *file,
unsigned int cmd, unsigned long _arg)
{
return abox_rdma_fio_common_ioctl(hw, file,
cmd, (unsigned long __user *)_arg);
}
#ifdef CONFIG_COMPAT
static int abox_rdma_fio_compat_ioctl(struct snd_hwdep *hw,
struct file *file,
unsigned int cmd, unsigned long _arg)
{
return abox_rdma_fio_common_ioctl(hw, file, cmd, compat_ptr(_arg));
}
#endif /* CONFIG_COMPAT */
static int samsung_abox_rdma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct abox_platform_data *data;
int ret;
u32 value;
const char *type;
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
platform_set_drvdata(pdev, data);
data->pdev = pdev;
data->sfr_base = devm_get_ioremap(pdev, "sfr", NULL, NULL);
if (IS_ERR(data->sfr_base))
return PTR_ERR(data->sfr_base);
data->dev_abox = pdev->dev.parent;
if (!data->dev_abox) {
dev_err(dev, "Failed to get abox platform device\n");
return -EPROBE_DEFER;
}
data->abox_data = dev_get_drvdata(data->dev_abox);
spin_lock_init(&data->compr_data.lock);
mutex_init(&data->compr_data.cmd_lock);
init_waitqueue_head(&data->compr_data.flush_wait);
init_waitqueue_head(&data->compr_data.exit_wait);
init_waitqueue_head(&data->compr_data.ipc_wait);
init_completion(&data->closed);
data->compr_data.isr_handler = abox_rdma_compr_isr_handler;
abox_register_ipc_handler(data->dev_abox, IPC_PCMPLAYBACK,
abox_rdma_ipc_handler, data->abox_data);
ret = of_samsung_property_read_u32(dev, np, "id", &data->id);
if (ret < 0)
return ret;
ret = of_samsung_property_read_string(dev, np, "type", &type);
if (ret < 0)
type = "";
if (!strncmp(type, "call", sizeof("call")))
data->type = PLATFORM_CALL;
else if (!strncmp(type, "compress", sizeof("compress")))
data->type = PLATFORM_COMPRESS;
else if (!strncmp(type, "realtime", sizeof("realtime")))
data->type = PLATFORM_REALTIME;
else if (!strncmp(type, "vi-sensing", sizeof("vi-sensing")))
data->type = PLATFORM_VI_SENSING;
else if (!strncmp(type, "sync", sizeof("sync")))
data->type = PLATFORM_SYNC;
else
data->type = PLATFORM_NORMAL;
ret = of_samsung_property_read_u32(dev, np, "buffer_bytes", &value);
if (ret < 0)
value = BUFFER_BYTES_MIN;
data->dmab.bytes = value;
ret = of_samsung_property_read_string(dev, np, "buffer_type", &type);
if (ret < 0)
type = "";
if (!strncmp(type, "ion", sizeof("ion")))
data->buf_type = BUFFER_TYPE_ION;
else if (!strncmp(type, "dma", sizeof("dma")))
data->buf_type = BUFFER_TYPE_DMA;
else
data->buf_type = BUFFER_TYPE_DMA;
of_samsung_property_read_u32_array(dev, np, "pm-qos-lit",
data->pm_qos_cl0, ARRAY_SIZE(data->pm_qos_cl0));
ret = of_samsung_property_read_u32_array(dev, np, "pm-qos-mid",
data->pm_qos_cl1, ARRAY_SIZE(data->pm_qos_cl1));
if (ret < 0)
of_samsung_property_read_u32_array(dev, np, "pm-qos-big",
data->pm_qos_cl1, ARRAY_SIZE(data->pm_qos_cl1));
else
of_samsung_property_read_u32_array(dev, np, "pm-qos-big",
data->pm_qos_cl2, ARRAY_SIZE(data->pm_qos_cl2));
data->of_data = of_match_node(samsung_abox_rdma_match,
pdev->dev.of_node)->data;
data->dai_drv = devm_kzalloc(dev, sizeof(*data->dai_drv), GFP_KERNEL);
if (!data->dai_drv)
return -ENOMEM;
memcpy(data->dai_drv, data->of_data->base_dai_drv,
sizeof(struct snd_soc_dai_driver));
data->dai_drv->id = data->of_data->get_dai_id(data->id);
data->dai_drv->name = data->of_data->get_dai_name(dev, data->id);
if (data->dai_drv->playback.formats)
data->dai_drv->playback.stream_name =
data->of_data->get_str_name(dev, data->id,
SNDRV_PCM_STREAM_PLAYBACK);
if (data->type == PLATFORM_COMPRESS)
data->dai_drv->compress_new = snd_soc_new_compress;
ret = devm_snd_soc_register_component(dev, &abox_rdma_cmpnt,
data->dai_drv, 1);
if (ret < 0)
return ret;
if (data->type == PLATFORM_COMPRESS) {
data->mailbox_base = devm_get_ioremap(pdev, "mailbox",
NULL, NULL);
if (IS_ERR(data->mailbox_base))
return PTR_ERR(data->mailbox_base);
data->mailbox = devm_regmap_init_mmio(dev,
data->mailbox_base,
&abox_mailbox_config);
if (IS_ERR(data->mailbox))
return PTR_ERR(data->mailbox);
pm_runtime_set_autosuspend_delay(dev, 1);
pm_runtime_use_autosuspend(dev);
} else {
pm_runtime_no_callbacks(dev);
}
pm_runtime_enable(dev);
ret = snd_soc_register_platform(&pdev->dev, &abox_rdma);
if (ret < 0)
return ret;
data->hwdep = NULL;
return 0;
}
static int samsung_abox_rdma_remove(struct platform_device *pdev)
{
snd_soc_unregister_platform(&pdev->dev);
return 0;
}
static const struct dev_pm_ops samsung_abox_rdma_pm = {
SET_RUNTIME_PM_OPS(abox_rdma_runtime_suspend,
abox_rdma_runtime_resume, NULL)
};
static struct platform_driver samsung_abox_rdma_driver = {
.probe = samsung_abox_rdma_probe,
.remove = samsung_abox_rdma_remove,
.driver = {
.name = "samsung-abox-rdma",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_abox_rdma_match),
.pm = &samsung_abox_rdma_pm,
},
};
module_platform_driver(samsung_abox_rdma_driver);
/* Module information */
MODULE_AUTHOR("Gyeongtaek Lee, <gt82.lee@samsung.com>");
MODULE_DESCRIPTION("Samsung ASoC A-Box RDMA Driver");
MODULE_ALIAS("platform:samsung-abox-rdma");
MODULE_LICENSE("GPL");