kernel_samsung_a34x-permissive/sound/soc/mediatek/common/mtk-sram-manager.c

299 lines
7.4 KiB
C
Raw Permalink Normal View History

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
* Author: Kai Chieh Chuang <kaichieh.chuang@mediatek.com>
*/
#include <linux/module.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "mtk-sram-manager.h"
static void mtk_audio_sram_update_block_valid(struct mtk_audio_sram *sram,
enum mtk_audio_sram_mode mode)
{
int i;
for (i = 0; i < sram->block_num; i++) {
if ((i + 1) * sram->block_size > sram->mode_size[mode])
sram->blocks[i].valid = false;
else
sram->blocks[i].valid = true;
}
}
static bool mtk_audio_sram_avail(struct mtk_audio_sram *sram,
unsigned int size,
unsigned int *blk_idx,
unsigned int *blk_num)
{
unsigned int max_avail_size = 0;
bool start_record = false;
struct mtk_audio_sram_block *sram_blk = NULL;
int i = 0;
*blk_idx = 0;
for (i = 0; i < sram->block_num; i++) {
sram_blk = &sram->blocks[i];
if ((sram_blk->user == NULL) && sram_blk->valid) {
max_avail_size += sram->block_size;
if (start_record == false) {
start_record = true;
*blk_idx = i;
}
(*blk_num)++;
/* can callocate sram */
if (max_avail_size >= size)
break;
}
/* when reach allocate buffer , reset condition*/
if ((sram_blk->user != NULL) && sram_blk->valid) {
max_avail_size = 0;
*blk_num = 0;
*blk_idx = 0;
start_record = false;
}
if (sram_blk->valid == 0) {
dev_warn(sram->dev, "%s(), sram_blk->valid == 0, i = %d\n",
__func__, i);
break;
}
}
if (max_avail_size < size)
dev_info(sram->dev,
"%s(), max_avail_size = %d, size = %d, blk_idx = %d, blk_num = %d\n",
__func__, max_avail_size, size, *blk_idx, *blk_num);
return max_avail_size >= size;
}
int mtk_audio_sram_init(struct device *dev,
struct mtk_audio_sram *sram,
const struct mtk_audio_sram_ops *ops)
{
struct device_node *sram_node = NULL;
const __be32 *regaddr_p;
u64 regaddr64, size64;
int i = 0;
int ret;
if (!ops->set_sram_mode) {
dev_err(sram->dev, "%s(), ops->set_sram_mode == NULL\n",
__func__);
return -EINVAL;
}
sram->ops.set_sram_mode = ops->set_sram_mode;
sram->dev = dev;
spin_lock_init(&sram->lock);
/* parse info from device tree */
sram_node = of_find_compatible_node(NULL, NULL,
"mediatek,audio_sram");
if (sram_node == NULL) {
dev_err(sram->dev, "%s(), sram_node == NULL\n", __func__);
goto of_error;
}
/* iomap sram address */
sram->virt_addr = of_iomap(sram_node, 0);
if (sram->virt_addr == NULL) {
dev_err(sram->dev, "%s(), sram->virt_addr == NULL\n", __func__);
goto of_error;
}
/* get physical address, size */
regaddr_p = of_get_address(sram_node, 0, &size64, NULL);
if (!regaddr_p) {
dev_err(sram->dev, "%s(), get sram address fail\n", __func__);
goto of_error;
}
regaddr64 = of_translate_address(sram_node, regaddr_p);
sram->phys_addr = (dma_addr_t)regaddr64;
sram->size = (unsigned int)size64;
/* get prefer sram mode, mode size */
ret = of_property_read_u32(sram_node,
"prefer_mode", &sram->prefer_mode);
if (ret) {
dev_err(sram->dev, "%s(), get prefer_mode fail\n", __func__);
goto of_error;
}
ret = of_property_read_u32_array(sram_node, "mode_size",
sram->mode_size,
MTK_AUDIO_SRAM_MODE_NUM);
if (ret) {
dev_err(sram->dev, "%s(), get mode_size fail, ret %d\n",
__func__, ret);
goto of_error;
}
/* get block size */
ret = of_property_read_u32(sram_node,
"block_size", &sram->block_size);
if (ret) {
dev_err(sram->dev, "%s(), get block_size fail\n", __func__);
goto of_error;
}
sram->block_num = (sram->size / sram->block_size);
of_node_put(sram_node);
dev_info(sram->dev, "%s(), size %d, block_size %d, block_num %d, virt_addr %p, phys_addr %pad\n",
__func__,
sram->size, sram->block_size, sram->block_num,
sram->virt_addr, &sram->phys_addr);
dev_info(sram->dev, "%s(), prefer_mode %d, mode_size[0] 0x%x, mode_size[1] 0x%x\n",
__func__,
sram->prefer_mode, sram->mode_size[0], sram->mode_size[1]);
/* Dynamic allocate sram blocks according to block_num */
sram->blocks = devm_kcalloc(sram->dev,
sram->block_num,
sizeof(struct mtk_audio_sram_block),
GFP_KERNEL);
for (i = 0; i < sram->block_num ; i++) {
sram->blocks[i].valid = true;
sram->blocks[i].size = sram->block_size;
sram->blocks[i].user = NULL;
sram->blocks[i].phys_addr = sram->phys_addr +
(sram->block_size *
(dma_addr_t)i);
sram->blocks[i].virt_addr = (void *)((char *)sram->virt_addr +
(sram->block_size * i));
}
/* init for normal mode or compact mode */
sram->sram_mode = sram->prefer_mode;
mtk_audio_sram_update_block_valid(sram, sram->sram_mode);
return 0;
of_error:
of_node_put(sram_node);
return -ENODEV;
}
EXPORT_SYMBOL_GPL(mtk_audio_sram_init);
int mtk_audio_sram_allocate(struct mtk_audio_sram *sram,
dma_addr_t *phys_addr, unsigned char **virt_addr,
unsigned int size, void *user,
snd_pcm_format_t format, bool force_normal)
{
unsigned int block_num = 0;
unsigned int block_idx = 0;
struct mtk_audio_sram_block *blocks = NULL;
enum mtk_audio_sram_mode request_sram_mode;
bool has_user = false;
int ret = 0;
int i;
spin_lock(&sram->lock);
dev_info(sram->dev, "%s(), size %d, user %p, format %d, force_normal %d, sram mode %d\n",
__func__, size, user, format, force_normal, sram->sram_mode);
/* check if sram has user */
for (i = 0; i < sram->block_num; i++) {
blocks = &sram->blocks[i];
if (blocks->valid == true && blocks->user != NULL) {
has_user = true;
break;
}
}
/* get sram mode for this request */
if (force_normal) {
request_sram_mode = MTK_AUDIO_SRAM_NORMAL_MODE;
} else {
if (format == SNDRV_PCM_FORMAT_S24 ||
format == SNDRV_PCM_FORMAT_U24) {
request_sram_mode = has_user ?
sram->sram_mode :
sram->prefer_mode;
} else {
request_sram_mode = MTK_AUDIO_SRAM_NORMAL_MODE;
}
}
/* change sram mode if needed */
if (sram->sram_mode != request_sram_mode) {
if (has_user) {
dev_info(sram->dev, "%s(), cannot change mode to %d\n",
__func__,
request_sram_mode);
spin_unlock(&sram->lock);
return -ENOMEM;
}
sram->sram_mode = request_sram_mode;
mtk_audio_sram_update_block_valid(sram, sram->sram_mode);
}
if (sram->ops.set_sram_mode)
sram->ops.set_sram_mode(sram->dev, sram->sram_mode);
else
dev_warn(sram->dev, "%s(), set_sram_mode == NULL\n",
__func__);
if (mtk_audio_sram_avail(sram, size, &block_idx, &block_num) == true) {
*phys_addr = sram->blocks[block_idx].phys_addr;
*virt_addr = (char *)sram->blocks[block_idx].virt_addr;
/* set aud sram with user*/
while (block_num) {
sram->blocks[block_idx].user = user;
block_num--;
block_idx++;
}
} else {
ret = -ENOMEM;
}
spin_unlock(&sram->lock);
return ret;
}
EXPORT_SYMBOL_GPL(mtk_audio_sram_allocate);
int mtk_audio_sram_free(struct mtk_audio_sram *sram, void *user)
{
unsigned int i = 0;
struct mtk_audio_sram_block *sram_blk = NULL;
dev_dbg(sram->dev, "%s(), user %p\n", __func__, user);
spin_lock(&sram->lock);
for (i = 0; i < sram->block_num ; i++) {
sram_blk = &sram->blocks[i];
if (sram_blk->user == user)
sram_blk->user = NULL;
}
spin_unlock(&sram->lock);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_audio_sram_free);
unsigned int mtk_audio_sram_get_size(struct mtk_audio_sram *sram, int mode)
{
return sram->mode_size[mode];
}
EXPORT_SYMBOL_GPL(mtk_audio_sram_get_size);
MODULE_DESCRIPTION("Mediatek sram manager");
MODULE_AUTHOR("Kai Chieh Chuang <kaichieh.chuang@mediatek.com>");
MODULE_LICENSE("GPL v2");