// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. * Author: Kai Chieh Chuang */ #include #include #include #include #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 "); MODULE_LICENSE("GPL v2");