blob: 461621b6d3d725fb9045dd42ac51f5b4d442c85f [file] [log] [blame]
/*
* drivers/amlogic/amports/decoder/decoder_bmmu_box.c
*
* Copyright (C) 2015 Amlogic, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#define DEBUG
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/delay.h>
#include <linux/timer.h>
#include <linux/kfifo.h>
#include <linux/kthread.h>
#include <linux/slab.h>
#include <linux/amlogic/media/codec_mm/codec_mm_scatter.h>
#include <linux/platform_device.h>
#include <linux/amlogic/media/video_sink/video_keeper.h>
#include "decoder_bmmu_box.h"
#include <linux/amlogic/media/codec_mm/codec_mm.h>
#include <linux/amlogic/media/codec_mm/codec_mm_keeper.h>
struct decoder_bmmu_box {
int max_mm_num;
const char *name;
int channel_id;
struct mutex mutex;
struct list_head list;
int total_size;
int box_ref_cnt;
int change_size_on_need_smaller;
int align2n; /*can overwite on idx alloc */
int mem_flags; /*can overwite on idx alloc */
struct codec_mm_s *mm_list[1];
};
struct decoder_bmmu_box_mgr {
int num;
struct mutex mutex;
struct list_head box_list;
};
static struct decoder_bmmu_box_mgr global_blk_mgr;
static struct decoder_bmmu_box_mgr *get_decoder_bmmu_box_mgr(void)
{
return &global_blk_mgr;
}
static int decoder_bmmu_box_mgr_add_box(struct decoder_bmmu_box *box)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
mutex_lock(&mgr->mutex);
list_add_tail(&box->list, &mgr->box_list);
mutex_unlock(&mgr->mutex);
return 0;
}
static int decoder_bmmu_box_mgr_del_box(struct decoder_bmmu_box *box)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
mutex_lock(&mgr->mutex);
list_del(&box->list);
mutex_unlock(&mgr->mutex);
return 0;
}
bool decoder_bmmu_box_valide_check(void *box)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
struct decoder_bmmu_box *bmmu_box = NULL;
bool is_valide = false;
mutex_lock(&mgr->mutex);
list_for_each_entry(bmmu_box, &mgr->box_list, list) {
if (bmmu_box && bmmu_box == box) {
is_valide = true;
break;
}
}
mutex_unlock(&mgr->mutex);
return is_valide;
}
EXPORT_SYMBOL(decoder_bmmu_box_valide_check);
void *decoder_bmmu_box_alloc_box(const char *name,
int channel_id, int max_num,
int aligned, int mem_flags)
/*min_size_M:wait alloc this size*/
{
struct decoder_bmmu_box *box;
int size;
int tvp_flags;
tvp_flags = (mem_flags & CODEC_MM_FLAGS_TVP) ?
CODEC_MM_FLAGS_TVP : 0;
pr_debug("decoder_bmmu_box_alloc_box, tvp_flags = %x\n", tvp_flags);
size = sizeof(struct decoder_bmmu_box) + sizeof(struct codec_mm_s *) *
max_num;
box = kmalloc(size, GFP_KERNEL);
if (!box) {
pr_err("can't alloc decoder buffers box!!!\n");
return NULL;
}
memset(box, 0, size);
box->max_mm_num = max_num;
box->name = name;
box->channel_id = channel_id;
box->align2n = aligned;
box->mem_flags = mem_flags | tvp_flags;
mutex_init(&box->mutex);
INIT_LIST_HEAD(&box->list);
decoder_bmmu_box_mgr_add_box(box);
return (void *)box;
}
EXPORT_SYMBOL(decoder_bmmu_box_alloc_box);
int decoder_bmmu_box_alloc_idx(void *handle, int idx, int size, int aligned_2n,
int mem_flags)
/*align& flags if -1 user box default.*/
{
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
int align = aligned_2n;
int memflags = mem_flags;
if (!box || idx < 0 || idx >= box->max_mm_num) {
pr_err("can't alloc mmu box(%p),idx:%d\n",
box, idx);
return -1;
}
if (align == -1)
align = box->align2n;
if (memflags == -1)
memflags = box->mem_flags;
mutex_lock(&box->mutex);
mm = box->mm_list[idx];
if (mm) {
int invalid = 0;
int keeped = 0;
keeped = is_codec_mm_keeped(mm);
if (!keeped) {
if (mm->page_count * PAGE_SIZE < size) {
/*size is small. */
invalid = 1;
} else if (box->change_size_on_need_smaller &&
(mm->buffer_size > (size << 1))) {
/*size is too large. */
invalid = 2;
} else if (mm->phy_addr & ((1 << align) - 1)) {
/*addr is not align */
invalid = 4;
}
if (invalid) {
box->total_size -= mm->buffer_size;
codec_mm_release(mm, box->name);
box->mm_list[idx] = NULL;
mm = NULL;
}
} else {
box->total_size -= mm->buffer_size;
codec_mm_release(mm, box->name);
box->mm_list[idx] = NULL;
mm = NULL;
}
}
if (!mm) {
mm = codec_mm_alloc(box->name, size, align, memflags);
if (mm) {
box->mm_list[idx] = mm;
box->total_size += mm->buffer_size;
mm->ins_id = box->channel_id;
mm->ins_buffer_id = idx;
box->box_ref_cnt++;
}
}
mutex_unlock(&box->mutex);
return mm ? 0 : -ENOMEM;
}
int decoder_bmmu_box_free_idx(void *handle, int idx)
{
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
if (!box || idx < 0 || idx >= box->max_mm_num) {
pr_err("can't free idx of box(%p),idx:%d in (%d-%d)\n",
box, idx, 0,
box ? (box->max_mm_num - 1) : 0);
return -1;
}
mutex_lock(&box->mutex);
mm = box->mm_list[idx];
if (mm) {
box->total_size -= mm->buffer_size;
codec_mm_release(mm, box->name);
box->mm_list[idx] = NULL;
mm = NULL;
box->box_ref_cnt--;
}
mutex_unlock(&box->mutex);
return 0;
}
EXPORT_SYMBOL(decoder_bmmu_box_free_idx);
int decoder_bmmu_box_free(void *handle)
{
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
int i;
if (!box) {
pr_err("can't free box of NULL box!\n");
return -1;
}
mutex_lock(&box->mutex);
for (i = 0; i < box->max_mm_num; i++) {
mm = box->mm_list[i];
if (mm) {
codec_mm_release(mm, box->name);
box->mm_list[i] = NULL;
}
}
mutex_unlock(&box->mutex);
decoder_bmmu_box_mgr_del_box(box);
kfree(box);
return 0;
}
EXPORT_SYMBOL(decoder_bmmu_box_free);
void decoder_bmmu_try_to_release_box(void *handle)
{
struct decoder_bmmu_box *box = handle;
bool is_keep = false;
int i;
if (!box || box->box_ref_cnt)
return;
mutex_lock(&box->mutex);
for (i = 0; i < box->max_mm_num; i++) {
if (box->mm_list[i]) {
is_keep = true;
break;
}
}
mutex_unlock(&box->mutex);
if (!is_keep) {
decoder_bmmu_box_mgr_del_box(box);
kfree(box);
}
}
EXPORT_SYMBOL(decoder_bmmu_try_to_release_box);
void *decoder_bmmu_box_get_mem_handle(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
if (!box || idx < 0 || idx >= box->max_mm_num)
return NULL;
return box->mm_list[idx];
}
EXPORT_SYMBOL(decoder_bmmu_box_get_mem_handle);
int decoder_bmmu_box_get_mem_size(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
int size = 0;
if (!box || idx < 0 || idx >= box->max_mm_num)
return 0;
mutex_lock(&box->mutex);
if (box->mm_list[idx] != NULL)
size = box->mm_list[idx]->buffer_size;
mutex_unlock(&box->mutex);
return size;
}
unsigned long decoder_bmmu_box_get_phy_addr(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
struct codec_mm_s *mm;
if (!box || idx < 0 || idx >= box->max_mm_num)
return 0;
mm = box->mm_list[idx];
if (!mm)
return 0;
return mm->phy_addr;
}
EXPORT_SYMBOL(decoder_bmmu_box_get_phy_addr);
void *decoder_bmmu_box_get_virt_addr(void *box_handle, int idx)
{
struct decoder_bmmu_box *box = box_handle;
struct codec_mm_s *mm;
if (!box || idx < 0 || idx >= box->max_mm_num)
return NULL;
mm = box->mm_list[idx];
if (!mm)
return 0;
return codec_mm_phys_to_virt(mm->phy_addr);
}
/*flags: &0x1 for wait,*/
int decoder_bmmu_box_check_and_wait_size(int size, int flags)
{
if ((flags & BMMU_ALLOC_FLAGS_CAN_CLEAR_KEEPER) &&
codec_mm_get_free_size() < size) {
pr_err("CMA force free keep,for size = %d\n", size);
/*need free others?
*/
try_free_keep_video(1);
}
return codec_mm_enough_for_size(size,
flags & BMMU_ALLOC_FLAGS_WAIT);
}
int decoder_bmmu_box_alloc_idx_wait(
void *handle, int idx,
int size, int aligned_2n,
int mem_flags,
int wait_flags)
{
int have_space;
int ret = -1;
int keeped = 0;
if (decoder_bmmu_box_get_mem_size(handle, idx) >= size) {
struct decoder_bmmu_box *box = handle;
struct codec_mm_s *mm;
mutex_lock(&box->mutex);
mm = box->mm_list[idx];
keeped = is_codec_mm_keeped(mm);
mutex_unlock(&box->mutex);
if (!keeped)
return 0;/*have alloced memery before.*/
}
have_space = decoder_bmmu_box_check_and_wait_size(
size,
wait_flags);
if (have_space) {
ret = decoder_bmmu_box_alloc_idx(handle,
idx, size, aligned_2n, mem_flags);
if (ret == -ENOMEM) {
pr_info("bmmu alloc idx fail, try free keep video.\n");
try_free_keep_video(1);
}
} else {
try_free_keep_video(1);
ret = -ENOMEM;
}
return ret;
}
EXPORT_SYMBOL(decoder_bmmu_box_alloc_idx_wait);
int decoder_bmmu_box_alloc_buf_phy(
void *handle, int idx,
int size, unsigned char *driver_name,
unsigned long *buf_phy_addr)
{
if (!decoder_bmmu_box_check_and_wait_size(
size,
1)) {
pr_info("%s not enough buf for buf_idx = %d\n",
driver_name, idx);
return -ENOMEM;
}
if (!decoder_bmmu_box_alloc_idx_wait(
handle,
idx,
size,
-1,
-1,
BMMU_ALLOC_FLAGS_WAITCLEAR
)) {
*buf_phy_addr =
decoder_bmmu_box_get_phy_addr(
handle,
idx);
/*
*pr_info("%s malloc buf_idx = %d addr = %ld size = %d\n",
* driver_name, idx, *buf_phy_addr, size);
*/
} else {
pr_info("%s malloc failed %d\n", driver_name, idx);
return -ENOMEM;
}
return 0;
}
EXPORT_SYMBOL(decoder_bmmu_box_alloc_buf_phy);
static int decoder_bmmu_box_dump(struct decoder_bmmu_box *box, void *buf,
int size)
{
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int i;
if (!buf) {
pbuf = sbuf;
size = 512;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
for (i = 0; i < box->max_mm_num; i++) {
struct codec_mm_s *mm = box->mm_list[i];
if (buf && (size - tsize) < 256) {
BUFPRINT("\n\t**NOT END**\n");
break;
}
if (mm) {
BUFPRINT("code mem[%d]:%p, addr=%p, size=%d,from=%d\n",
i,
(void *)mm,
(void *)mm->phy_addr,
mm->buffer_size,
mm->from_flags);
if (!buf) {
pr_info("%s", sbuf);
pbuf = sbuf;
}
}
}
#undef BUFPRINT
return tsize;
}
static int decoder_bmmu_box_dump_all(void *buf, int size)
{
struct decoder_bmmu_box_mgr *mgr = get_decoder_bmmu_box_mgr();
char *pbuf = buf;
char sbuf[512];
int tsize = 0;
int s;
int i;
struct list_head *head, *list;
if (!buf) {
pbuf = sbuf;
size = 512;
}
#define BUFPRINT(args...) \
do {\
s = snprintf(pbuf, size - tsize, args);\
tsize += s;\
pbuf += s; \
} while (0)
mutex_lock(&mgr->mutex);
head = &mgr->box_list;
list = head->next;
i = 0;
while (list != head) {
struct decoder_bmmu_box *box;
box = list_entry(list, struct decoder_bmmu_box, list);
BUFPRINT("box[%d]: %s, %splayer_id:%d, max_num:%d, size:%d\n",
i, box->name,
(box->mem_flags & CODEC_MM_FLAGS_TVP) ?
"TVP mode " : "",
box->channel_id,
box->max_mm_num,
box->total_size);
if (buf) {
s = decoder_bmmu_box_dump(box, pbuf, size - tsize);
if (s > 0) {
tsize += s;
pbuf += s;
}
} else {
pr_info("%s", sbuf);
pbuf = sbuf;
tsize += decoder_bmmu_box_dump(box, NULL, 0);
}
list = list->next;
i++;
}
mutex_unlock(&mgr->mutex);
#undef BUFPRINT
if (!buf)
pr_info("%s", sbuf);
return tsize;
}
static ssize_t box_dump_show(struct class *class, struct class_attribute *attr,
char *buf)
{
ssize_t ret = 0;
ret = decoder_bmmu_box_dump_all(buf, PAGE_SIZE);
return ret;
}
static ssize_t box_debug_show(struct class *class,
struct class_attribute *attr,
char *buf)
{
ssize_t size = 0;
size += sprintf(buf, "box debug help:\n");
size += sprintf(buf + size, "echo n > debug\n");
size += sprintf(buf + size, "n==0: clear all debugs)\n");
size += sprintf(buf + size,
"n=1: dump all box\n");
return size;
}
static ssize_t box_debug_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned val;
ssize_t ret;
val = -1;
ret = sscanf(buf, "%d", &val);
if (ret != 1)
return -EINVAL;
switch (val) {
case 1:
decoder_bmmu_box_dump_all(NULL , 0);
break;
default:
pr_err("unknow cmd! %d\n", val);
}
return size;
}
static struct class_attribute decoder_bmmu_box_class_attrs[] = {
__ATTR_RO(box_dump),
__ATTR(debug, S_IRUGO | S_IWUSR | S_IWGRP,
box_debug_show, box_debug_store),
__ATTR_NULL
};
static struct class decoder_bmmu_box_class = {
.name = "decoder_bmmu_box",
.class_attrs = decoder_bmmu_box_class_attrs,
};
int decoder_bmmu_box_init(void)
{
int r;
memset(&global_blk_mgr, 0, sizeof(global_blk_mgr));
INIT_LIST_HEAD(&global_blk_mgr.box_list);
mutex_init(&global_blk_mgr.mutex);
r = class_register(&decoder_bmmu_box_class);
return r;
}
EXPORT_SYMBOL(decoder_bmmu_box_init);
void decoder_bmmu_box_exit(void)
{
class_unregister(&decoder_bmmu_box_class);
pr_info("dec bmmu box exit.\n");
}
#if 0
static int __init decoder_bmmu_box_init(void)
{
int r;
memset(&global_blk_mgr, 0, sizeof(global_blk_mgr));
INIT_LIST_HEAD(&global_blk_mgr.box_list);
mutex_init(&global_blk_mgr.mutex);
r = class_register(&decoder_bmmu_box_class);
return r;
}
module_init(decoder_bmmu_box_init);
#endif