blob: af6d1bd24c70593f930fff919e168c938c743a40 [file] [log] [blame]
/*
* drivers/amlogic/media/common/vfm/vframe_provider.c
*
* Copyright (C) 2017 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.
*
*/
/* Standard Linux headers */
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>
/* Amlogic headers */
#include <linux/amlogic/media/vfm/vframe.h>
#include <linux/amlogic/media/vfm/vframe_provider.h>
#include <linux/amlogic/media/vfm/vframe_receiver.h>
/* Local headers */
#include "vfm.h"
#include "vftrace.h"
#define MAX_PROVIDER_NUM 32
static struct vframe_provider_s *provider_table[MAX_PROVIDER_NUM];
static atomic_t provider_used = ATOMIC_INIT(0);
#define providers_lock() atomic_inc(&provider_used)
#define providers_unlock() atomic_dec(&provider_used)
#define providers_used() atomic_read(&provider_used)
static DEFINE_MUTEX(provider_table_mutex);
#define TABLE_LOCK() mutex_lock(&provider_table_mutex)
#define TABLE_UNLOCK() mutex_unlock(&provider_table_mutex)
#define VFPROVIER_DEBUG
#ifdef VFPROVIER_DEBUG
static DEFINE_SPINLOCK(provider_lock);
static char last_receiver[32];
static char last_provider[32];
void provider_update_caller(
const char *receiver,
const char *provider)
{
unsigned long flags;
spin_lock_irqsave(&provider_lock, flags);
if (receiver)
strncpy(last_receiver, receiver, 31);
if (provider)
strncpy(last_provider, provider, 31);
spin_unlock_irqrestore(&provider_lock, flags);
}
void provider_print_last_info(void)
{
unsigned long flags;
struct vframe_provider_s *p;
int i;
spin_lock_irqsave(&provider_lock, flags);
pr_info("last receiver: %s\n",
last_receiver[0] ? last_receiver : "null");
pr_info("last provider: %s\n",
last_provider[0] ? last_provider : "null");
pr_info("register provider:\n");
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p)
pr_info("%s: user_cnt:%d\n", p->name,
atomic_read(&p->use_cnt));
}
spin_unlock_irqrestore(&provider_lock, flags);
}
#else
void provider_update_caller(
const char *receiver,
const char *provider) {return }
void provider_print_last_info(void) {return}
#endif
int provider_list(char *buf)
{
struct vframe_provider_s *p = NULL;
int len = 0;
int i;
len += sprintf(buf + len, "\nprovider list:\n");
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p)
len += sprintf(buf + len, " %s\n", p->name);
}
return len;
}
/*
* get vframe provider from the provider list by receiver name.
*
*/
struct vframe_provider_s *vf_get_provider_by_name(const char *provider_name)
{
struct vframe_provider_s *p = NULL;
int i;
if (provider_name) {
int namelen = strlen(provider_name);
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p && p->name && !strncmp(p->name,
provider_name, namelen)) {
if (strlen(p->name) == namelen
|| p->name[namelen] == '.')
break;
}
}
if (i == MAX_PROVIDER_NUM)
p = NULL;
}
return p;
}
struct vframe_provider_s *vf_get_provider(const char *receiver_name)
{
struct vframe_provider_s *p = NULL;
char *provider_name;
provider_name = vf_get_provider_name(receiver_name);
p = vf_get_provider_by_name(provider_name);
return p;
}
EXPORT_SYMBOL(vf_get_provider);
/*#define NO_CHEKC_PROVIDER_USE*/
#ifdef NO_CHEKC_PROVIDER_USE
static inline int use_provider(struct vframe_provider_s *prov)
{
return prov != NULL;
}
static inline void unuse_provider(struct vframe_provider_s *prov)
{
}
static int vf_provider_close(struct vframe_provider_s *prov)
{
return 1;
}
#else
static inline int use_provider(struct vframe_provider_s *prov)
{
int ret = 0;
if (prov) {
ret = atomic_inc_return(&prov->use_cnt);
if (ret <= 0) {
atomic_dec(&prov->use_cnt);
pr_err("%s: Error, provider error-%d\n",
prov->name, atomic_read(&prov->use_cnt));
}
}
return ret > 0;
}
static inline void unuse_provider(struct vframe_provider_s *prov)
{
if (prov)
atomic_dec(&prov->use_cnt);
}
#define CLOSED_CNT -100000
static int vf_provider_close(struct vframe_provider_s *prov)
{
int ret;
int wait_max = 10000;
if (!prov)
return -1;
ret = atomic_add_return(CLOSED_CNT, &prov->use_cnt);
while (ret > CLOSED_CNT && wait_max-- > 0) {
schedule();/*shecule and wait for complete.*/
ret = atomic_read(&prov->use_cnt);
};
if (ret > CLOSED_CNT) {
pr_err("**ERR***,release, provider %s not finised,%d, wait=%d\n",
prov->name, ret, wait_max);
provider_print_last_info();
}
return 0;
}
#endif
int vf_notify_provider(const char *receiver_name, int event_type, void *data)
{
int ret = -1;
struct vframe_provider_s *provider = vf_get_provider(receiver_name);
if (use_provider(provider)) {
if (provider->ops && provider->ops->event_cb) {
provider->ops->event_cb(event_type, data,
provider->op_arg);
ret = 0;
}
unuse_provider(provider);
} else {
/* pr_err("Error: %s, fail to get provider of receiver %s\n",*/
/*__func__, receiver_name); */
}
return ret;
}
EXPORT_SYMBOL(vf_notify_provider);
int vf_notify_provider_by_name(const char *provider_name, int event_type,
void *data)
{
int ret = -1;
struct vframe_provider_s *provider =
vf_get_provider_by_name(provider_name);
if (use_provider(provider)) {
if (provider->ops && provider->ops->event_cb) {
provider->ops->event_cb(event_type, data,
provider->op_arg);
ret = 0;
}
unuse_provider(provider);
} else{
/* pr_err("Error: %s, fail to get provider of receiver %s\n",*/
/*__func__, receiver_name); */
}
return ret;
}
EXPORT_SYMBOL(vf_notify_provider_by_name);
void vf_provider_init(struct vframe_provider_s *prov,
const char *name,
const struct vframe_operations_s *ops, void *op_arg)
{
if (!prov)
return;
/* memset(prov, 0, sizeof(struct vframe_provider_s)); */
prov->name = name;
prov->ops = ops;
prov->op_arg = op_arg;
prov->traceget = NULL;
prov->traceput = NULL;
atomic_set(&prov->use_cnt, 0);
INIT_LIST_HEAD(&prov->list);
}
EXPORT_SYMBOL(vf_provider_init);
int vf_reg_provider(struct vframe_provider_s *prov)
{
struct vframe_provider_s *p = NULL;
struct vframe_receiver_s *receiver = NULL;
int i;
if (!prov || !prov->name)
return -1;
prov->traceget = NULL;
prov->traceput = NULL;
atomic_set(&prov->use_cnt, 0);/*set it ready for use.*/
TABLE_LOCK();
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p) {
if (!strcmp(p->name, prov->name)) {
TABLE_UNLOCK();
return -1;
}
}
}
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
if (provider_table[i] == NULL) {
provider_table[i] = prov;
break;
}
}
TABLE_UNLOCK();
if (i < MAX_PROVIDER_NUM) {
vf_update_active_map();
receiver = vf_get_receiver(prov->name);
if (receiver && receiver->ops && receiver->ops->event_cb) {
receiver->ops->event_cb(VFRAME_EVENT_PROVIDER_REG,
(void *)prov->name,
receiver->op_arg);
} else{
pr_err("%s Error to notify receiver\n", __func__);
}
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
} else {
pr_err("%s: Error, provider_table full\n", __func__);
}
if (vfm_trace_enable & 1)
prov->traceget = vftrace_alloc_trace(prov->name, 1,
vfm_trace_num);
if (vfm_trace_enable & 2)
prov->traceput = vftrace_alloc_trace(prov->name, 0,
vfm_trace_num);
return 0;
}
EXPORT_SYMBOL(vf_reg_provider);
void vf_unreg_provider(struct vframe_provider_s *prov)
{
struct vframe_provider_s *p = NULL;
struct vframe_receiver_s *receiver = NULL;
int i;
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
TABLE_LOCK();
p = provider_table[i];
if (p && !strcmp(p->name, prov->name)) {
provider_table[i] = NULL;
TABLE_UNLOCK();
if (p->traceget) {
vftrace_free_trace(prov->traceget);
p->traceget = NULL;
}
if (p->traceput) {
vftrace_free_trace(prov->traceput);
p->traceput = NULL;
}
vf_provider_close(prov);
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
receiver = vf_get_receiver(prov->name);
if (receiver && receiver->ops
&& receiver->ops->event_cb) {
receiver->ops->event_cb(
VFRAME_EVENT_PROVIDER_UNREG,
NULL,
receiver->op_arg);
} else {
pr_err("%s Error to notify receiver\n",
__func__);
}
vf_update_active_map();
break;
}
TABLE_UNLOCK();
}
{
/*
* wait no one used provider,
* if some one have on used.
* the provider memory may free,
* become unreachable.
*/
int cnt = 0;
while (providers_used()) {
schedule();
cnt++;
if (cnt > 10000) {
pr_err("unreg provider locked %s,%d!\n",
prov->name, cnt);
provider_print_last_info();
break;
}
}
}
}
EXPORT_SYMBOL(vf_unreg_provider);
void vf_light_reg_provider(struct vframe_provider_s *prov)
{
struct vframe_provider_s *p = NULL;
struct vframe_receiver_s *receiver = NULL;
int i;
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p && !strcmp(p->name, prov->name)) {
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
receiver = vf_get_receiver(prov->name);
if (receiver && receiver->ops
&& receiver->ops->event_cb) {
receiver->ops->event_cb(
VFRAME_EVENT_PROVIDER_REG,
(void *)prov->name,
receiver->op_arg);
} else{
pr_err("%s Error to notify receiver\n",
__func__);
}
break;
}
}
}
EXPORT_SYMBOL(vf_light_reg_provider);
void vf_light_unreg_provider(struct vframe_provider_s *prov)
{
struct vframe_provider_s *p = NULL;
struct vframe_receiver_s *receiver = NULL;
int i;
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
p = provider_table[i];
if (p && !strcmp(p->name, prov->name)) {
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
receiver = vf_get_receiver(p->name);
if (receiver && receiver->ops
&& receiver->ops->event_cb) {
receiver->ops->event_cb(
VFRAME_EVENT_PROVIDER_LIGHT_UNREG,
NULL, receiver->op_arg);
}
break;
}
}
}
EXPORT_SYMBOL(vf_light_unreg_provider);
void vf_ext_light_unreg_provider(struct vframe_provider_s *prov)
{
struct vframe_provider_s *p = NULL;
struct vframe_receiver_s *receiver = NULL;
int i;
for (i = 0; i < MAX_PROVIDER_NUM; i++) {
TABLE_LOCK();
p = provider_table[i];
if (p && !strcmp(p->name, prov->name)) {
provider_table[i] = NULL;
TABLE_UNLOCK();
if (vfm_debug_flag & 1)
pr_err("%s:%s\n", __func__, prov->name);
receiver = vf_get_receiver(prov->name);
if (receiver && receiver->ops
&& receiver->ops->event_cb) {
receiver->ops->event_cb(
VFRAME_EVENT_PROVIDER_LIGHT_UNREG,
NULL, receiver->op_arg);
}
vf_update_active_map();
break;
}
TABLE_UNLOCK();
}
}
EXPORT_SYMBOL(vf_ext_light_unreg_provider);
struct vframe_s *vf_peek(const char *receiver)
{
struct vframe_provider_s *vfp;
struct vframe_s *vf = NULL;
providers_lock();
vfp = vf_get_provider(receiver);
if (use_provider(vfp)) {
if (vfp->ops && vfp->ops->peek)
vf = vfp->ops->peek(vfp->op_arg);
unuse_provider(vfp);
}
providers_unlock();
return vf;
}
EXPORT_SYMBOL(vf_peek);
struct vframe_s *vf_get(const char *receiver)
{
struct vframe_provider_s *vfp;
struct vframe_s *vf = NULL;
providers_lock();
vfp = vf_get_provider(receiver);
if (use_provider(vfp)) {
provider_update_caller(receiver, vfp->name);
if (vfp->ops && vfp->ops->get)
vf = vfp->ops->get(vfp->op_arg);
if (vf)
vftrace_info_in(vfp->traceget, vf);
unuse_provider(vfp);
} else
provider_update_caller(receiver, NULL);
providers_unlock();
return vf;
}
EXPORT_SYMBOL(vf_get);
void vf_put(struct vframe_s *vf, const char *receiver)
{
struct vframe_provider_s *vfp;
providers_lock();
vfp = vf_get_provider(receiver);
if (use_provider(vfp)) {
provider_update_caller(receiver, vfp->name);
if (vfp->ops && vfp->ops->put)
vfp->ops->put(vf, vfp->op_arg);
if (vf)
vftrace_info_in(vfp->traceput, vf);
unuse_provider(vfp);
} else {
provider_update_caller(receiver, NULL);
}
providers_unlock();
}
EXPORT_SYMBOL(vf_put);
int vf_get_states(struct vframe_provider_s *vfp,
struct vframe_states *states)
{
int ret = -1;
providers_lock();
if (use_provider(vfp)) {
provider_update_caller(NULL, vfp->name);
if (vfp->ops && vfp->ops->vf_states)
ret = vfp->ops->vf_states(states, vfp->op_arg);
unuse_provider(vfp);
}
providers_unlock();
return ret;
}
EXPORT_SYMBOL(vf_get_states);
int vf_get_states_by_name(const char *receiver_name,
struct vframe_states *states)
{
struct vframe_provider_s *vfp;
vfp = vf_get_provider(receiver_name);
return vf_get_states(vfp, states);
}
EXPORT_SYMBOL(vf_get_states_by_name);