kernel_samsung_a53x/sound/soc/samsung/vts/vts_dma.c
2024-06-15 16:02:09 -03:00

472 lines
13 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0-or-later
/* sound/soc/samsung/vts/vts-plat.c
*
* ALSA SoC - Samsung VTS platform 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.
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <linux/firmware.h>
#include <linux/dma-mapping.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/regmap.h>
#include <linux/pm_wakeup.h>
#include <asm-generic/delay.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
#include <sound/samsung/mailbox.h>
#include <sound/samsung/vts.h>
#include <soc/samsung/exynos-pmu-if.h>
#include "vts.h"
#include "vts_res.h"
#include "vts_dbg.h"
static const struct snd_pcm_hardware vts_dma_hardware = {
.info = SNDRV_PCM_INFO_INTERLEAVED
| SNDRV_PCM_INFO_BLOCK_TRANSFER
| SNDRV_PCM_INFO_MMAP
| SNDRV_PCM_INFO_MMAP_VALID,
.formats = SNDRV_PCM_FMTBIT_S16,
.rates = SNDRV_PCM_RATE_16000,
.channels_min = 1,
.channels_max = 2,
.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 struct vts_dma_data *vts_get_drvdata(
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct snd_soc_dai_driver *dai_drv = dai->driver;
struct vts_data *data =
dev_get_drvdata(dai->dev);
struct vts_dma_data *dma_data =
platform_get_drvdata(data->pdev_vtsdma[dai_drv->id]);
return dma_data;
}
static int vts_dma_hw_params(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
vts_dev_info(dev, "%s state %d %d\n", __func__,
data->vts_data->vts_state,
data->vts_data->clk_path);
if (data->type == PLATFORM_VTS_TRIGGER_RECORD)
snd_pcm_set_runtime_buffer(
substream, &data->vts_data->dmab);
else
snd_pcm_set_runtime_buffer(
substream, &data->vts_data->dmab_rec);
vts_dev_info(dev, "%s:%s:DmaAddr=%pad Total=%zu"
" PrdSz=%u(%u) #Prds=%u dma_area=%p\n",
__func__, snd_pcm_stream_str(substream),
&runtime->dma_addr, runtime->dma_bytes,
params_period_size(params),
params_period_bytes(params),
params_periods(params), runtime->dma_area);
data->pointer = 0;
return 0;
}
static int vts_dma_hw_free(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
vts_dev_info(dev, "%s\n", __func__);
if (IS_ENABLED(CONFIG_SOC_EXYNOS2100) || \
IS_ENABLED(CONFIG_SOC_S5E9925) || \
IS_ENABLED(CONFIG_SOC_S5E8825)) {
vts_request_dram_on(dev, data->type, false);
} else {
vts_dev_dbg(dev, "%s request MIF by fw\n", __func__);
}
return 0;
}
static int vts_dma_prepare(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
vts_dev_info(dev, "%s\n", __func__);
if (IS_ENABLED(CONFIG_SOC_EXYNOS2100) || \
IS_ENABLED(CONFIG_SOC_S5E9925) || \
IS_ENABLED(CONFIG_SOC_S5E8825)) {
vts_request_dram_on(dev, data->type, true);
} else {
vts_dev_dbg(dev, "%s request MIF by fw\n", __func__);
}
return 0;
}
static int vts_dma_trigger(struct snd_soc_component *component,
struct snd_pcm_substream *substream, int cmd)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
u32 values[3] = {0, 0, 0};
int result = 0;
vts_dev_info(dev, "%s ++ CMD: %d\n", __func__, cmd);
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (data->type == PLATFORM_VTS_TRIGGER_RECORD) {
vts_dev_dbg(dev, "%s VTS_IRQ_AP_START_COPY\n",
__func__);
result = vts_start_ipc_transaction(dev, data->vts_data,
VTS_IRQ_AP_START_COPY, &values, 1, 1);
data->vts_data->vts_tri_state = VTS_TRI_STATE_COPY_START;
} else {
vts_dev_dbg(dev, "%s VTS_IRQ_AP_START_REC\n", __func__);
result = vts_start_ipc_transaction(dev, data->vts_data,
VTS_IRQ_AP_START_REC, &values, 1, 1);
data->vts_data->vts_rec_state = VTS_REC_STATE_START;
}
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (data->type == PLATFORM_VTS_TRIGGER_RECORD) {
vts_dev_dbg(dev, "%s VTS_IRQ_AP_STOP_COPY\n", __func__);
result = vts_start_ipc_transaction(dev, data->vts_data,
VTS_IRQ_AP_STOP_COPY, &values, 1, 1);
data->vts_data->vts_tri_state = VTS_TRI_STATE_COPY_STOP;
} else {
vts_dev_dbg(dev, "%s VTS_IRQ_AP_STOP_REC\n", __func__);
result = vts_start_ipc_transaction(dev, data->vts_data,
VTS_IRQ_AP_STOP_REC, &values, 1, 1);
data->vts_data->vts_rec_state = VTS_REC_STATE_STOP;
}
break;
default:
result = -EINVAL;
break;
}
vts_dev_info(dev, "%s -- CMD: %d\n", __func__, cmd);
return result;
}
static snd_pcm_uframes_t vts_dma_pointer(
struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
vts_dev_dbg(dev, "%s: pointer=%08x\n", __func__, data->pointer);
return bytes_to_frames(runtime, data->pointer);
}
static int vts_dma_open(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
int result = 0;
vts_dev_info(dev, "%s\n", __func__);
/* vts_try_request_firmware_interface(data->vts_data); */
pm_runtime_get_sync(dev);
vts_start_runtime_resume(dev, 0);
snd_soc_set_runtime_hwparams(substream, &vts_dma_hardware);
/* Serial LIF Worked -> VTS (Trigger/Recoding) */
if (data->vts_data->clk_path == VTS_CLK_SRC_AUD0) {
data->vts_data->syssel_rate = 4;
vts_clk_set_rate(&data->vts_data->pdev->dev,
data->vts_data->syssel_rate);
vts_dev_info(dev, "[Serial LIF Worked] Set VTS 3072000Hz\n");
}
if (data->type == PLATFORM_VTS_NORMAL_RECORD) {
vts_dev_info(dev, "%s open --\n", __func__);
#ifdef VTS_SICD_CHECK
vts_dev_info(dev, "SOC down : %d, MIF down : %d",
readl(data->vts_data->sicd_base + SICD_SOC_DOWN_OFFSET),
readl(data->vts_data->sicd_base + SICD_MIF_DOWN_OFFSET)
);
#endif
result = vts_set_dmicctrl(data->vts_data->pdev,
VTS_MICCONF_FOR_RECORD, true);
if (result < 0) {
vts_dev_err(dev, "%s: MIC control configuration failed\n",
__func__);
pm_runtime_put_sync(dev);
}
}
return result;
}
static int vts_dma_close(struct snd_soc_component *component,
struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct vts_dma_data *data = vts_get_drvdata(substream);
int result = 0;
vts_dev_info(dev, "%s\n", __func__);
vts_dev_dbg(dev, "%s 0x%x 0x%x \n",
__func__,
data->vts_data->shared_info->log_pos_read,
data->vts_data->shared_info->log_pos_write);
if (data->type == PLATFORM_VTS_NORMAL_RECORD) {
vts_dev_info(dev, "%s close --\n", __func__);
#ifdef VTS_SICD_CHECK
vts_dev_info(dev, "SOC down : %d, MIF down : %d",
readl(data->vts_data->sicd_base + SICD_SOC_DOWN_OFFSET),
readl(data->vts_data->sicd_base + SICD_MIF_DOWN_OFFSET)
);
#endif
result = vts_set_dmicctrl(data->vts_data->pdev,
VTS_MICCONF_FOR_RECORD, false);
if (result < 0)
vts_dev_warn(dev, "%s: MIC control configuration failed\n",
__func__);
}
pm_runtime_put_sync(dev);
return result;
}
static int vts_dma_mmap(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
struct vm_area_struct *vma)
{
struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream);
struct snd_soc_dai *dai = asoc_rtd_to_cpu(rtd, 0);
struct device *dev = dai->dev;
struct snd_pcm_runtime *runtime = substream->runtime;
vts_dev_info(dev, "%s\n", __func__);
return dma_mmap_wc(dev, vma,
runtime->dma_area,
runtime->dma_addr,
runtime->dma_bytes);
}
static int vts_dma_copy_user(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
int channel, unsigned long pos, void __user *buf, unsigned long bytes)
{
struct snd_pcm_runtime *runtime = substream->runtime;
void *hwbuf;
hwbuf = runtime->dma_area + pos +
channel * (runtime->dma_bytes / runtime->channels);
if (copy_to_user((void __user *)buf, hwbuf, bytes))
return -EFAULT;
return 0;
}
static int vts_dma_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *runtime)
{
struct snd_soc_dai *dai = asoc_rtd_to_cpu(runtime, 0);
struct snd_soc_dai_driver *dai_drv = dai->driver;
struct device *dev = dai->dev;
struct vts_data *vtsdata = dev_get_drvdata(dai->dev);
struct vts_dma_data *data =
platform_get_drvdata(vtsdata->pdev_vtsdma[dai_drv->id]);
struct snd_pcm_substream *substream =
runtime->pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
vts_dev_info(dev, "%s\n", __func__);
data->substream = substream;
vts_dev_info(dev, "%s Update Soc Card from runtime!!\n", __func__);
data->vts_data->card = runtime->card;
return 0;
}
static void vts_dma_free(struct snd_soc_component *component,
struct snd_pcm *pcm)
{
}
static int vts_dma_ioctl(struct snd_soc_component *component,
struct snd_pcm_substream *substream,
unsigned int cmd, void *arg)
{
int ret = 0;
ret = snd_pcm_lib_ioctl(substream, cmd, arg);
return ret;
}
static const struct snd_soc_component_driver vts_dma = {
.pcm_construct = vts_dma_new,
.pcm_destruct = vts_dma_free,
.open = vts_dma_open,
.close = vts_dma_close,
.ioctl = vts_dma_ioctl,
.hw_params = vts_dma_hw_params,
.hw_free = vts_dma_hw_free,
.prepare = vts_dma_prepare,
.trigger = vts_dma_trigger,
.pointer = vts_dma_pointer,
.copy_user = vts_dma_copy_user,
.mmap = vts_dma_mmap,
};
static struct snd_soc_dai_driver vts_dma_dai_drv = {
.capture = {
.channels_min = 1,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16,
.sig_bits = 16,
},
};
static int samsung_vts_dma_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct device_node *np_vts;
struct vts_dma_data *data;
int result;
int ret = 0;
const char *type;
dev_info(dev, "%s\n", __func__);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(dev, "Failed to allocate memory\n");
return -ENOMEM;
}
platform_set_drvdata(pdev, data);
data->dev = dev;
np_vts = of_parse_phandle(np, "vts", 0);
if (!np_vts) {
dev_err(dev, "Failed to get vts device node\n");
return -EPROBE_DEFER;
}
data->pdev_vts = of_find_device_by_node(np_vts);
if (!data->pdev_vts) {
dev_err(dev, "Failed to get vts platform device\n");
return -EPROBE_DEFER;
}
data->vts_data = platform_get_drvdata(data->pdev_vts);
result = of_property_read_u32_index(np, "id", 0, &data->id);
if (result < 0) {
dev_err(dev, "id property reading fail\n");
return result;
}
result = of_property_read_string(np, "type", &type);
if (result < 0) {
dev_err(dev, "type property reading fail\n");
return result;
}
if (!strncmp(type, "vts-record", sizeof("vts-record"))) {
data->type = PLATFORM_VTS_NORMAL_RECORD;
dev_info(dev, "%s - vts-record Probed\n", __func__);
} else {
data->type = PLATFORM_VTS_TRIGGER_RECORD;
dev_info(dev, "%s - vts-trigger-record Probed\n", __func__);
}
vts_register_dma(data->vts_data->pdev, pdev, data->id);
ret = devm_snd_soc_register_component(dev, &vts_dma,
&vts_dma_dai_drv, 1);
if (ret < 0) {
dev_err(dev, "devm_snd_soc_register_component Fail");
return ret;
}
dev_info(dev, "Probed successfully\n");
return ret;
}
static int samsung_vts_dma_remove(struct platform_device *pdev)
{
return 0;
}
static const struct of_device_id samsung_vts_dma_match[] = {
{
.compatible = "samsung,vts-dma",
},
{},
};
MODULE_DEVICE_TABLE(of, samsung_vts_dma_match);
struct platform_driver samsung_vts_dma_driver = {
.probe = samsung_vts_dma_probe,
.remove = samsung_vts_dma_remove,
.driver = {
.name = "samsung-vts-dma",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(samsung_vts_dma_match),
},
};