6db4831e98
Android 14
299 lines
7.4 KiB
C
299 lines
7.4 KiB
C
// 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");
|
|
|