282 lines
6.4 KiB
C
Executable file
282 lines
6.4 KiB
C
Executable file
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Samsung Exynos SoC series dsp driver
|
|
*
|
|
* Copyright (c) 2019 Samsung Electronics Co., Ltd.
|
|
* http://www.samsung.com/
|
|
*/
|
|
|
|
#include <linux/io.h>
|
|
#include <linux/slab.h>
|
|
|
|
#include "dsp-log.h"
|
|
#include "dsp-util.h"
|
|
|
|
void dsp_util_queue_dump(struct dsp_util_queue *queue)
|
|
{
|
|
unsigned long long kva_low, kva_high, kva;
|
|
|
|
dsp_enter();
|
|
dsp_notice("queue front(%u)/rear(%u)\n",
|
|
readl(&queue->front), readl(&queue->rear));
|
|
dsp_notice("queue data_size(%u)/data_count(%u)\n",
|
|
readl(&queue->data_size), readl(&queue->data_count));
|
|
|
|
kva_low = readl(&queue->kva_low);
|
|
kva_high = readl(&queue->kva_high);
|
|
kva = (kva_high << 32) | kva_low;
|
|
|
|
dsp_notice("queue iova(%#x)/kva(%#llx)/size(%u)\n",
|
|
readl(&queue->iova), kva, readl(&queue->size));
|
|
dsp_leave();
|
|
}
|
|
|
|
int dsp_util_queue_read_count(struct dsp_util_queue *queue)
|
|
{
|
|
return readl(&queue->data_count);
|
|
}
|
|
|
|
int dsp_util_queue_check_full(struct dsp_util_queue *queue)
|
|
{
|
|
unsigned int count;
|
|
unsigned int mirror_front, mirror_rear;
|
|
unsigned int front, rear;
|
|
|
|
dsp_check();
|
|
count = readl(&queue->data_count);
|
|
mirror_front = readl(&queue->front);
|
|
mirror_rear = readl(&queue->rear);
|
|
front = mirror_front % (count << 1);
|
|
rear = mirror_rear % (count << 1);
|
|
|
|
return (front == rear && mirror_front != mirror_rear);
|
|
}
|
|
|
|
int dsp_util_queue_enqueue(struct dsp_util_queue *queue, void *data,
|
|
size_t data_size)
|
|
{
|
|
int ret;
|
|
unsigned int rear, mirror_rear;
|
|
unsigned int q_data_size, q_data_count;
|
|
unsigned long long kva_low, kva_high;
|
|
void *kva;
|
|
|
|
dsp_enter();
|
|
q_data_size = readl(&queue->data_size);
|
|
if (data_size > q_data_size) {
|
|
ret = -EINVAL;
|
|
dsp_err("size(%zu) can't be greater than data_size(%u) of q\n",
|
|
data_size, q_data_size);
|
|
goto p_err;
|
|
}
|
|
|
|
q_data_count = readl(&queue->data_count);
|
|
mirror_rear = readl(&queue->rear);
|
|
rear = mirror_rear % q_data_count;
|
|
|
|
kva_low = readl(&queue->kva_low);
|
|
kva_high = readl(&queue->kva_high);
|
|
kva = (void *)((kva_high << 32) | kva_low);
|
|
memcpy(kva + (rear * q_data_size), data, data_size);
|
|
writel((mirror_rear + 1) % (q_data_count << 1), &queue->rear);
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_util_queue_check_empty(struct dsp_util_queue *queue)
|
|
{
|
|
return readl(&queue->front) == readl(&queue->rear);
|
|
}
|
|
|
|
int dsp_util_queue_dequeue(struct dsp_util_queue *queue, void *data,
|
|
size_t data_size)
|
|
{
|
|
int ret;
|
|
unsigned int front, mirror_front;
|
|
unsigned int q_data_size, q_data_count;
|
|
unsigned long long kva_low, kva_high;
|
|
void *kva;
|
|
|
|
dsp_enter();
|
|
q_data_size = readl(&queue->data_size);
|
|
if (data_size > q_data_size) {
|
|
ret = -EINVAL;
|
|
dsp_err("size(%zu) can't be greater than data_size(%u) of q\n",
|
|
data_size, q_data_size);
|
|
goto p_err;
|
|
}
|
|
|
|
q_data_count = readl(&queue->data_count);
|
|
mirror_front = readl(&queue->front);
|
|
front = mirror_front % q_data_count;
|
|
|
|
kva_low = readl(&queue->kva_low);
|
|
kva_high = readl(&queue->kva_high);
|
|
kva = (void *)((kva_high << 32) | kva_low);
|
|
memcpy(data, kva + (front * q_data_size), data_size);
|
|
writel((mirror_front + 1) % (q_data_count << 1), &queue->front);
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
int dsp_util_queue_init(struct dsp_util_queue *queue, unsigned int data_size,
|
|
unsigned int queue_size, unsigned int iova,
|
|
unsigned long long kva)
|
|
{
|
|
int ret;
|
|
|
|
dsp_enter();
|
|
if (!data_size || !queue_size || !iova || !kva) {
|
|
ret = -EINVAL;
|
|
dsp_err("queue parameter can't be zero(%u/%u/%u/%llu)\n",
|
|
data_size, queue_size, iova, kva);
|
|
goto p_err;
|
|
}
|
|
|
|
if (data_size > queue_size) {
|
|
ret = -EINVAL;
|
|
dsp_err("data can't be greater than the entire queue(%u/%u)\n",
|
|
data_size, queue_size);
|
|
goto p_err;
|
|
}
|
|
|
|
writel(0, &queue->front);
|
|
writel(0, &queue->rear);
|
|
writel(data_size, &queue->data_size);
|
|
writel(queue_size / data_size, &queue->data_count);
|
|
writel(iova, &queue->iova);
|
|
writel(queue_size, &queue->size);
|
|
writel(kva & 0xffffffff, &queue->kva_low);
|
|
writel(kva >> 32, &queue->kva_high);
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
void dsp_util_bitmap_dump(struct dsp_util_bitmap *map)
|
|
{
|
|
dsp_enter();
|
|
dsp_notice("bitmap[%s] dump : size(%u)/used_size(%u)/base_bit(%u)\n",
|
|
map->name, map->bitmap_size, map->used_size,
|
|
map->base_bit);
|
|
print_hex_dump(KERN_NOTICE, "[Exynos][DSP][NOTICE]: bitmap raw: ",
|
|
DUMP_PREFIX_NONE, 32, 8, map->bitmap,
|
|
BITS_TO_LONGS(map->bitmap_size) * sizeof(long), false);
|
|
dsp_leave();
|
|
}
|
|
|
|
int dsp_util_bitmap_set_region(struct dsp_util_bitmap *map, unsigned int size)
|
|
{
|
|
int ret;
|
|
unsigned long start, end, check;
|
|
bool turn = false;
|
|
|
|
dsp_enter();
|
|
if (!size) {
|
|
ret = -EINVAL;
|
|
dsp_err("Invalid bitmap size[%s](%u)\n", map->name, size);
|
|
goto p_err;
|
|
}
|
|
|
|
if (size > map->bitmap_size - map->used_size) {
|
|
ret = -ENOMEM;
|
|
dsp_err("Not enough bitmap[%s](%u)\n", map->name, size);
|
|
goto p_err;
|
|
}
|
|
|
|
start = map->base_bit;
|
|
again:
|
|
start = find_next_zero_bit(map->bitmap, map->bitmap_size, start);
|
|
|
|
end = start + size - 1;
|
|
if (end >= map->bitmap_size) {
|
|
if (turn) {
|
|
ret = -ENOMEM;
|
|
dsp_err("Not enough contiguous bitmap[%s](%u)\n",
|
|
map->name, size);
|
|
goto p_err;
|
|
} else {
|
|
turn = true;
|
|
start = 0;
|
|
goto again;
|
|
}
|
|
}
|
|
|
|
check = find_next_bit(map->bitmap, end, start);
|
|
if (check < end) {
|
|
start = check + 1;
|
|
goto again;
|
|
}
|
|
|
|
bitmap_set(map->bitmap, start, size);
|
|
map->base_bit = end + 1;
|
|
map->used_size += size;
|
|
|
|
dsp_leave();
|
|
return start;
|
|
p_err:
|
|
dsp_util_bitmap_dump(map);
|
|
return ret;
|
|
}
|
|
|
|
void dsp_util_bitmap_clear_region(struct dsp_util_bitmap *map,
|
|
unsigned int start, unsigned int size)
|
|
{
|
|
dsp_enter();
|
|
if ((map->bitmap_size < start + size - 1) ||
|
|
size > map->used_size) {
|
|
dsp_warn("Invalid clear parameter[%s](%u/%u)\n",
|
|
map->name, start, size);
|
|
dsp_util_bitmap_dump(map);
|
|
return;
|
|
}
|
|
|
|
map->used_size -= size;
|
|
bitmap_clear(map->bitmap, start, size);
|
|
dsp_leave();
|
|
}
|
|
|
|
int dsp_util_bitmap_init(struct dsp_util_bitmap *map, const char *name,
|
|
unsigned int size)
|
|
{
|
|
int ret;
|
|
|
|
dsp_enter();
|
|
if (!size) {
|
|
ret = -EINVAL;
|
|
dsp_err("bitmap size can not be zero\n");
|
|
goto p_err;
|
|
}
|
|
|
|
map->bitmap = kzalloc(BITS_TO_LONGS(size) * sizeof(long), GFP_KERNEL);
|
|
if (!map->bitmap) {
|
|
ret = -ENOMEM;
|
|
dsp_err("Failed to init bitmap(%u/%lu)\n",
|
|
size, BITS_TO_LONGS(size) * sizeof(long));
|
|
goto p_err;
|
|
}
|
|
|
|
snprintf(map->name, DSP_BITMAP_NAME_LEN, "%s", name);
|
|
map->bitmap_size = size;
|
|
map->used_size = 0;
|
|
map->base_bit = 0;
|
|
|
|
dsp_leave();
|
|
return 0;
|
|
p_err:
|
|
return ret;
|
|
}
|
|
|
|
void dsp_util_bitmap_deinit(struct dsp_util_bitmap *map)
|
|
{
|
|
dsp_enter();
|
|
kfree(map->bitmap);
|
|
dsp_leave();
|
|
}
|