328 lines
7.6 KiB
C
328 lines
7.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2020 Rockchip Electronics Co., Ltd.
|
|
*
|
|
* Author: Zorro Liu <zorro.liu@rock-chips.com>
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/list.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/semaphore.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/wait.h>
|
|
|
|
#include "../ebc_dev.h"
|
|
#include "buf_manage.h"
|
|
#include "buf_list.h"
|
|
|
|
struct buf_info_s {
|
|
int buf_total_num;
|
|
unsigned long phy_mem_base;
|
|
char *virt_mem_base;
|
|
|
|
struct buf_list_s *buf_list; /* buffer list. */
|
|
int use_buf_is_empty;
|
|
|
|
struct buf_list_s *dsp_buf_list; /* dispplay buffer list. */
|
|
int dsp_buf_list_status;
|
|
struct ebc_buf_s *osd_buf;
|
|
|
|
struct mutex dsp_lock;
|
|
};
|
|
|
|
static struct buf_info_s ebc_buf_info;
|
|
static DECLARE_WAIT_QUEUE_HEAD(ebc_buf_wq);
|
|
|
|
int ebc_buf_release(struct ebc_buf_s *release_buf)
|
|
{
|
|
struct ebc_buf_s *temp_buf = release_buf;
|
|
|
|
if (temp_buf) {
|
|
if (temp_buf->status == buf_osd) {
|
|
kfree(temp_buf);
|
|
} else {
|
|
temp_buf->status = buf_idle;
|
|
if (1 == ebc_buf_info.use_buf_is_empty) {
|
|
ebc_buf_info.use_buf_is_empty = 0;
|
|
wake_up_interruptible_sync(&ebc_buf_wq);
|
|
}
|
|
}
|
|
}
|
|
|
|
return BUF_SUCCESS;
|
|
}
|
|
|
|
int ebc_remove_from_dsp_buf_list(struct ebc_buf_s *remove_buf)
|
|
{
|
|
mutex_lock(&ebc_buf_info.dsp_lock);
|
|
if (ebc_buf_info.dsp_buf_list) {
|
|
int pos;
|
|
|
|
pos = buf_list_get_pos(ebc_buf_info.dsp_buf_list, (int *)remove_buf);
|
|
buf_list_remove(ebc_buf_info.dsp_buf_list, pos);
|
|
}
|
|
mutex_unlock(&ebc_buf_info.dsp_lock);
|
|
|
|
return BUF_SUCCESS;
|
|
}
|
|
|
|
int ebc_add_to_dsp_buf_list(struct ebc_buf_s *dsp_buf)
|
|
{
|
|
struct ebc_buf_s *temp_buf;
|
|
int temp_pos;
|
|
int is_full_mode = 0;
|
|
|
|
mutex_lock(&ebc_buf_info.dsp_lock);
|
|
if (ebc_buf_info.dsp_buf_list) {
|
|
switch (dsp_buf->buf_mode) {
|
|
case EPD_DU:
|
|
case EPD_SUSPEND:
|
|
case EPD_RESUME:
|
|
case EPD_POWER_OFF:
|
|
case EPD_OVERLAY:
|
|
case EPD_RESET:
|
|
break;
|
|
|
|
default:
|
|
if (ebc_buf_info.dsp_buf_list->nb_elt > 1) {
|
|
temp_pos = ebc_buf_info.dsp_buf_list->nb_elt;
|
|
while (--temp_pos) {
|
|
temp_buf = (struct ebc_buf_s *)buf_list_get(ebc_buf_info.dsp_buf_list, temp_pos);
|
|
if ((temp_buf->buf_mode != EPD_FULL_GC16) &&
|
|
(temp_buf->buf_mode != EPD_FULL_GL16) &&
|
|
(temp_buf->buf_mode != EPD_FULL_GLR16) &&
|
|
(temp_buf->buf_mode != EPD_FULL_GLD16) &&
|
|
(temp_buf->buf_mode != EPD_FULL_GCC16) &&
|
|
(temp_buf->buf_mode != EPD_OVERLAY) &&
|
|
(temp_buf->buf_mode != EPD_DU) &&
|
|
(temp_buf->buf_mode != EPD_SUSPEND) &&
|
|
(temp_buf->buf_mode != EPD_RESUME) &&
|
|
(temp_buf->buf_mode != EPD_POWER_OFF)) {
|
|
buf_list_remove(ebc_buf_info.dsp_buf_list, temp_pos);
|
|
ebc_buf_release(temp_buf);
|
|
} else if ((1 == is_full_mode) &&
|
|
(temp_buf->buf_mode != EPD_DU) &&
|
|
(temp_buf->buf_mode != EPD_OVERLAY) &&
|
|
(temp_buf->buf_mode != EPD_SUSPEND) &&
|
|
(temp_buf->buf_mode != EPD_RESUME) &&
|
|
(temp_buf->buf_mode != EPD_POWER_OFF)) {
|
|
buf_list_remove(ebc_buf_info.dsp_buf_list, temp_pos);
|
|
ebc_buf_release(temp_buf);
|
|
} else {
|
|
is_full_mode = 1;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
dsp_buf->status = buf_dsp;
|
|
if (-1 == buf_list_add(ebc_buf_info.dsp_buf_list, (int *)dsp_buf, -1)) {
|
|
mutex_unlock(&ebc_buf_info.dsp_lock);
|
|
return BUF_ERROR;
|
|
}
|
|
}
|
|
mutex_unlock(&ebc_buf_info.dsp_lock);
|
|
|
|
return BUF_SUCCESS;
|
|
}
|
|
|
|
int ebc_get_dsp_list_enum_num(void)
|
|
{
|
|
return ebc_buf_info.dsp_buf_list->nb_elt;
|
|
}
|
|
|
|
struct ebc_buf_s *ebc_find_buf_by_phy_addr(unsigned long phy_addr)
|
|
{
|
|
struct ebc_buf_s *temp_buf;
|
|
int temp_pos;
|
|
|
|
if (ebc_buf_info.buf_list) {
|
|
temp_pos = 0;
|
|
while (temp_pos < ebc_buf_info.buf_list->nb_elt) {
|
|
temp_buf = (struct ebc_buf_s *)buf_list_get(ebc_buf_info.buf_list, temp_pos++);
|
|
if (temp_buf && (temp_buf->phy_addr == phy_addr))
|
|
return temp_buf;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct ebc_buf_s *ebc_dsp_buf_get(void)
|
|
{
|
|
struct ebc_buf_s *buf = NULL;
|
|
|
|
mutex_lock(&ebc_buf_info.dsp_lock);
|
|
if (ebc_buf_info.dsp_buf_list && (ebc_buf_info.dsp_buf_list->nb_elt > 0))
|
|
buf = (struct ebc_buf_s *)buf_list_get(ebc_buf_info.dsp_buf_list, 0);
|
|
mutex_unlock(&ebc_buf_info.dsp_lock);
|
|
|
|
return buf;
|
|
}
|
|
|
|
struct ebc_buf_s *ebc_osd_buf_get(void)
|
|
{
|
|
if (ebc_buf_info.osd_buf)
|
|
return ebc_buf_info.osd_buf;
|
|
return NULL;
|
|
}
|
|
|
|
struct ebc_buf_s *ebc_osd_buf_clone(void)
|
|
{
|
|
struct ebc_buf_s *temp_buf;
|
|
|
|
temp_buf = kzalloc(sizeof(*temp_buf), GFP_KERNEL);
|
|
if (NULL == temp_buf)
|
|
return NULL;
|
|
|
|
temp_buf->virt_addr = ebc_buf_info.osd_buf->virt_addr;
|
|
temp_buf->phy_addr = ebc_buf_info.osd_buf->phy_addr;
|
|
temp_buf->status = buf_osd;
|
|
|
|
return temp_buf;
|
|
}
|
|
|
|
struct ebc_buf_s *ebc_empty_buf_get(void)
|
|
{
|
|
struct ebc_buf_s *temp_buf;
|
|
int temp_pos;
|
|
|
|
if (ebc_buf_info.buf_list) {
|
|
temp_pos = 0;
|
|
|
|
while (temp_pos < ebc_buf_info.buf_list->nb_elt) {
|
|
temp_buf = (struct ebc_buf_s *)buf_list_get(ebc_buf_info.buf_list, temp_pos++);
|
|
if (temp_buf) {
|
|
if (temp_buf->status == buf_idle) {
|
|
temp_buf->status = buf_user;
|
|
memcpy(temp_buf->tid_name, current->comm, TASK_COMM_LEN); //store user thread name
|
|
return temp_buf;
|
|
}
|
|
// one tid only can get one buf at one time
|
|
else if ((temp_buf->status == buf_user) && (!strncmp(temp_buf->tid_name, current->comm, TASK_COMM_LEN - 7))) {
|
|
return temp_buf;
|
|
}
|
|
}
|
|
}
|
|
ebc_buf_info.use_buf_is_empty = 1;
|
|
|
|
wait_event_interruptible(ebc_buf_wq, ebc_buf_info.use_buf_is_empty != 1);
|
|
|
|
return ebc_empty_buf_get();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
unsigned long ebc_phy_buf_base_get(void)
|
|
{
|
|
return ebc_buf_info.phy_mem_base;
|
|
}
|
|
|
|
char *ebc_virt_buf_base_get(void)
|
|
{
|
|
return ebc_buf_info.virt_mem_base;
|
|
}
|
|
|
|
int ebc_buf_uninit(void)
|
|
{
|
|
struct ebc_buf_s *temp_buf;
|
|
int pos;
|
|
|
|
ebc_buf_info.buf_total_num = 0;
|
|
if (ebc_buf_info.buf_list) {
|
|
pos = ebc_buf_info.buf_list->nb_elt - 1;
|
|
while (pos >= 0) {
|
|
temp_buf = (struct ebc_buf_s *)buf_list_get(ebc_buf_info.buf_list, pos);
|
|
if (temp_buf)
|
|
kfree(temp_buf);
|
|
buf_list_remove(ebc_buf_info.buf_list, pos);
|
|
pos--;
|
|
}
|
|
}
|
|
|
|
return BUF_SUCCESS;
|
|
}
|
|
|
|
int ebc_buf_init(unsigned long phy_start, char *mem_start, int men_len, int dest_buf_len, int max_buf_num)
|
|
{
|
|
int res;
|
|
int use_len;
|
|
char *temp_addr;
|
|
struct ebc_buf_s *temp_buf;
|
|
|
|
if (max_buf_num < 0)
|
|
return BUF_ERROR;
|
|
|
|
if (NULL == mem_start)
|
|
return BUF_ERROR;
|
|
|
|
mutex_init(&ebc_buf_info.dsp_lock);
|
|
|
|
if (buf_list_init(&ebc_buf_info.buf_list, BUF_LIST_MAX_NUMBER))
|
|
return BUF_ERROR;
|
|
|
|
if (buf_list_init(&ebc_buf_info.dsp_buf_list, BUF_LIST_MAX_NUMBER)) {
|
|
res = BUF_ERROR;
|
|
goto buf_list_err;
|
|
}
|
|
|
|
ebc_buf_info.buf_total_num = 0;
|
|
use_len = 0;
|
|
|
|
temp_addr = mem_start;
|
|
ebc_buf_info.virt_mem_base = mem_start;
|
|
ebc_buf_info.phy_mem_base = phy_start;
|
|
use_len += dest_buf_len;
|
|
while (use_len <= men_len) {
|
|
temp_buf = kzalloc(sizeof(*temp_buf), GFP_KERNEL);
|
|
if (NULL == temp_buf) {
|
|
res = BUF_ERROR;
|
|
goto exit;
|
|
}
|
|
temp_buf->virt_addr = temp_addr;
|
|
temp_buf->phy_addr = phy_start;
|
|
temp_buf->len = dest_buf_len;
|
|
temp_buf->status = buf_idle;
|
|
|
|
if (-1 == buf_list_add(ebc_buf_info.buf_list, (int *)temp_buf, -1)) {
|
|
res = BUF_ERROR;
|
|
goto exit;
|
|
}
|
|
ebc_buf_info.use_buf_is_empty = 0;
|
|
|
|
temp_addr += dest_buf_len;
|
|
phy_start += dest_buf_len;
|
|
use_len += dest_buf_len;
|
|
|
|
if (ebc_buf_info.buf_list->nb_elt == max_buf_num)
|
|
break;
|
|
}
|
|
|
|
ebc_buf_info.buf_total_num = ebc_buf_info.buf_list->nb_elt;
|
|
if (use_len <= men_len) {
|
|
temp_buf = kzalloc(sizeof(*temp_buf), GFP_KERNEL);
|
|
if (NULL == temp_buf) {
|
|
res = BUF_ERROR;
|
|
goto exit;
|
|
}
|
|
temp_buf->virt_addr = temp_addr;
|
|
temp_buf->phy_addr = phy_start;
|
|
temp_buf->len = dest_buf_len;
|
|
temp_buf->status = buf_osd;
|
|
ebc_buf_info.osd_buf = temp_buf;
|
|
}
|
|
|
|
return BUF_SUCCESS;
|
|
exit:
|
|
ebc_buf_uninit();
|
|
buf_list_uninit(ebc_buf_info.dsp_buf_list);
|
|
buf_list_err:
|
|
buf_list_uninit(ebc_buf_info.buf_list);
|
|
|
|
return res;
|
|
}
|