blob: 2a68beeca3ac1722a5b3fd882474f0c5786377c7 [file] [log] [blame]
/*
* drivers/amlogic/media/common/vfm/vfm.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/types.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/io.h>
#include <linux/uaccess.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>
#include <linux/amlogic/major.h>
/*for dumpinfos*/
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/codec_mm/configs.h>
/* Local headers */
#include "vftrace.h"
#include "vfm.h"
static DEFINE_SPINLOCK(lock);
#define DRV_NAME "vfm"
#define DEV_NAME "vfm"
#define BUS_NAME "vfm"
#define CLS_NAME "vfm"
#define VFM_NAME_LEN 100
#define VFM_MAP_SIZE 10
#define VFM_MAP_COUNT 20
static struct device *vfm_dev;
struct vfm_map_s {
char id[VFM_NAME_LEN];
char name[VFM_MAP_SIZE][VFM_NAME_LEN];
int vfm_map_size;
int valid;
int active;
};
struct vfm_map_s *vfm_map[VFM_MAP_COUNT];
static int vfm_map_num;
int vfm_debug_flag; /* 1; */
int vfm_trace_enable; /* 1; */
int vfm_trace_num = 40; /* */
void vf_update_active_map(void)
{
int i, j;
struct vframe_provider_s *vfp;
for (i = 0; i < vfm_map_num; i++) {
if (vfm_map[i] && vfm_map[i]->valid) {
for (j = 0; j < (vfm_map[i]->vfm_map_size - 1); j++) {
vfp = vf_get_provider_by_name(vfm_map[i]->name
[j]);
if (vfp == NULL)
vfm_map[i]->active &= (~(1 << j));
else {
if ((j > 0 && vfm_map[i]->active & 0x1)
|| j == 0)
vfm_map[i]->active |= (1 << j);
}
}
}
}
}
static int get_vfm_map_index(const char *id)
{
int index = -1;
int i;
for (i = 0; i < vfm_map_num; i++) {
if (vfm_map[i]) {
if (vfm_map[i]->valid &&
(!strcmp(vfm_map[i]->id, id))) {
index = i;
break;
}
}
}
return index;
}
static int vfm_map_remove_by_index(int index)
{
int i;
int ret = 0;
struct vframe_provider_s *vfp;
vfm_map[index]->active = 0;
for (i = 0; i < (vfm_map[index]->vfm_map_size - 1); i++) {
vfp = vf_get_provider_by_name(vfm_map[index]->name[i]);
if (vfp && vfp->ops && vfp->ops->event_cb) {
vfp->ops->event_cb(VFRAME_EVENT_RECEIVER_FORCE_UNREG,
NULL, vfp->op_arg);
pr_err("%s: VFRAME_EVENT_RECEIVER_FORCE_UNREG %s\n",
__func__, vfm_map[index]->name[i]);
}
}
for (i = 0; i < (vfm_map[index]->vfm_map_size - 1); i++) {
vfp = vf_get_provider_by_name(vfm_map[index]->name[i]);
if (vfp)
break;
}
if (i < (vfm_map[index]->vfm_map_size - 1)) {
pr_err("failed remove vfm map %s with active provider %s.\n",
vfm_map[index]->id, vfm_map[index]->name[i]);
ret = -1;
}
vfm_map[index]->valid = 0;
return ret;
}
int vfm_map_remove(char *id)
{
int i;
int index;
int ret = 0;
if (IS_ERR_OR_NULL(id))
return -1;
if (!strcmp(id, "all")) {
for (i = 0; i < vfm_map_num; i++) {
if (vfm_map[i])
ret = vfm_map_remove_by_index(i);
}
} else {
index = get_vfm_map_index(id);
if (index >= 0)
ret = vfm_map_remove_by_index(index);
}
return ret;
}
EXPORT_SYMBOL(vfm_map_remove);
int vfm_map_add(char *id, char *name_chain)
{
int i, j;
int ret = -1;
char *ptr = NULL, *token = NULL;
struct vfm_map_s *p;
int old_num = vfm_map_num;
unsigned long flags;
int add_ok = 0;
int cnt = 10;
ulong addr;
p = kmalloc(sizeof(struct vfm_map_s), GFP_KERNEL);
if (!p) {
pr_err("%s: Error, map no mem!!\n", __func__);
return -ENOMEM;
}
ptr = kstrdup(name_chain, GFP_KERNEL);
addr = (ulong)ptr;
if (!ptr) {
kfree(p);
return -ENOMEM;
}
memset(p, 0, sizeof(struct vfm_map_s));
if (strlen(id) >= VFM_NAME_LEN - 1) {
memcpy(p->id, id, VFM_NAME_LEN - 1);
p->id[VFM_NAME_LEN - 1] = '\0';
} else {
memcpy(p->id, id, strlen(id));
}
p->valid = 1;
do {
token = strsep(&ptr, "\n ");
if (!token)
break;
if (*token == '\0')
continue;
if (p->vfm_map_size >= VFM_MAP_SIZE)
break;
if (strlen(token) >= VFM_NAME_LEN - 1) {
memcpy(p->name[p->vfm_map_size], token,
VFM_NAME_LEN - 1);
p->name[p->vfm_map_size][VFM_NAME_LEN - 1] = '\0';
} else {
memcpy(p->name[p->vfm_map_size], token, strlen(token));
}
p->vfm_map_size++;
} while (token && cnt--);
cnt = 10; /*limit the cnt of retry to avoid the infinite loop*/
kfree((void *)addr);
retry:
for (i = 0; i < vfm_map_num; i++) {
struct vfm_map_s *pi = vfm_map[i];
if (!pi || (strcmp(pi->id, p->id))) {
/*not same id to next one*/
continue;
} else if (pi->valid) {
for (j = 0; j < p->vfm_map_size; j++) {
if (strcmp(pi->name[j],
p->name[j])){
break;
}
}
if (j == p->vfm_map_size) {
pi->valid = 1;
kfree(p);
add_ok = 1;
break;
}
} else if (!pi->valid) {
/*
*over write old setting.
* don't free old one,
* because it may on used.
*/
for (j = 0; j < p->vfm_map_size; j++) {
/*over write node.*/
strcpy(pi->name[j], p->name[j]);
}
pi->vfm_map_size = p->vfm_map_size;
pi->valid = 1;
kfree(p);
add_ok = 1;
break;
}
}
if (!add_ok) {
spin_lock_irqsave(&lock, flags);
if (i == old_num && old_num != vfm_map_num && cnt--) {
spin_unlock_irqrestore(&lock, flags);
pr_err("%s: vfm_map changed on add, need retry!\n",
__func__);
goto retry;
}
if (i == vfm_map_num) {
if (i < VFM_MAP_COUNT) {
vfm_map[i] = p;
vfm_map_num++;
add_ok = 1;
} else{
pr_err("%s: Error, map full\n", __func__);
ret = -1;
kfree(p);
}
} else
kfree(p);
spin_unlock_irqrestore(&lock, flags);
}
if (add_ok)
ret = 0;
return ret;
}
EXPORT_SYMBOL(vfm_map_add);
static char *vf_get_provider_name_inmap(int i, const char *receiver_name)
{
int j;
char *provider_name = NULL;
for (j = 0; j < vfm_map[i]->vfm_map_size; j++) {
if (!strcmp(vfm_map[i]->name[j], receiver_name)) {
if ((j > 0) &&
((vfm_map[i]->active >> (j - 1)) & 0x1)) {
provider_name = vfm_map[i]->name[j - 1];
}
break;
}
}
return provider_name;
}
char *vf_get_provider_name(const char *receiver_name)
{
int i;
char *provider_name = NULL;
for (i = 0; i < vfm_map_num; i++) {
if (vfm_map[i] && vfm_map[i]->active) {
provider_name = vf_get_provider_name_inmap(i,
receiver_name);
}
if (provider_name)
break;
}
return provider_name;
}
static char *vf_get_receiver_name_inmap(int i, const char *provider_name)
{
int j;
int provide_namelen = strlen(provider_name);
bool found = false;
char *receiver_name = NULL;
int namelen;
for (j = 0; j < vfm_map[i]->vfm_map_size; j++) {
namelen = strlen(vfm_map[i]->name[j]);
if (vfm_debug_flag & 2) {
pr_err("%s:vfm_map:%s\n", __func__,
vfm_map[i]->name[j]);
}
if ((!strncmp(vfm_map[i]->name[j], provider_name, namelen)) &&
((j + 1) < vfm_map[i]->vfm_map_size)) {
if (namelen == provide_namelen) {
/* exact match */
receiver_name = vfm_map[i]->name[j + 1];
found = true;
break;
} else if (provider_name[namelen] == '.') {
/*
*continue looking, an exact matching
* has higher priority
*/
receiver_name = vfm_map[i]->name[j + 1];
}
}
}
return receiver_name;
}
char *vf_get_receiver_name(const char *provider_name)
{
int i;
char *receiver_name = NULL;
for (i = 0; i < vfm_map_num; i++) {
if (vfm_map[i] && vfm_map[i]->valid && vfm_map[i]->active) {
receiver_name = vf_get_receiver_name_inmap(i,
provider_name);
}
if (receiver_name)
break;
}
return receiver_name;
}
static void vfm_init(void)
{
#if ((defined CONFIG_AMLOGIC_POST_PROCESS_MANAGER) && \
(defined CONFIG_AMLOGIC_MEDIA_DEINTERLACE))
char def_id[VFM_NAME_LEN] = "default";
#ifndef CONFIG_AMLOGIC_MEDIA_MULTI_DEC
char def_name_chain[] = "decoder ppmgr deinterlace amvideo";
#else
char def_name_chain[] = "decoder amvideo";
#endif
#elif (defined CONFIG_AMLOGIC_POST_PROCESS_MANAGER)
char def_id[VFM_NAME_LEN] = "default";
char def_name_chain[] = "decoder ppmgr amvideo";
#elif (defined CONFIG_AMLOGIC_MEDIA_DEINTERLACE)
char def_id[VFM_NAME_LEN] = "default";
char def_name_chain[] = "decoder deinterlace amvideo";
#else /**/
char def_id[VFM_NAME_LEN] = "default";
char def_name_chain[] = "decoder amvideo";
#endif /**/
#ifdef CONFIG_TVIN_VIUIN
char def_ext_id[VFM_NAME_LEN] = "default_ext";
char def_ext_name_chain[] = "vdin amvideo2";
#endif /**/
#ifdef CONFIG_VDIN_MIPI
char def_mipi_id[VFM_NAME_LEN] = "default_mipi";
char def_mipi_name_chain[] = "vdin mipi";
#endif /**/
#ifdef CONFIG_AMLOGIC_V4L_VIDEO2
char def_amlvideo2_id[VFM_NAME_LEN] = "default_amlvideo2";
char def_amlvideo2_chain[] = "vdin1 amlvideo2.1";
#endif /**/
#if (defined CONFIG_TVIN_AFE) || (defined CONFIG_TVIN_HDMI)
#ifdef CONFIG_AMLOGIC_POST_PROCESS_MANAGER
char tvpath_id[VFM_NAME_LEN] = "tvpath";
char tvpath_chain[] = "vdin0 ppmgr deinterlace amvideo";
#else
char tvpath_id[VFM_NAME_LEN] = "tvpath";
char tvpath_chain[] = "vdin0 deinterlace amvideo";
#endif
#endif /**/
#ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION
char def_dvbl_id[VFM_NAME_LEN] = "dvblpath";
/* char def_dvbl_chain[] = "dvbldec dvbl amvideo";*/
char def_dvbl_chain[] = "dvbldec amvideo";
char def_dvel_id[VFM_NAME_LEN] = "dvelpath";
char def_dvel_chain[] = "dveldec dvel";
#endif
#if 1/*def CONFIG_AM_HDMIIN_DV*/
char def_dvhdmiin_id[VFM_NAME_LEN] = "dvhdmiin";
char def_dvhdmiin_chain[] = "dv_vdin amvideo";
#endif
int i;
for (i = 0; i < VFM_MAP_COUNT; i++)
vfm_map[i] = NULL;
vfm_map_add(def_id, def_name_chain);
#ifdef CONFIG_VDIN_MIPI
vfm_map_add(def_mipi_id, def_mipi_name_chain);
#endif /**/
#if (defined CONFIG_TVIN_AFE) || (defined CONFIG_TVIN_HDMI)
vfm_map_add(tvpath_id, tvpath_chain);
#endif /**/
#ifdef CONFIG_AMLOGIC_V4L_VIDEO2
vfm_map_add(def_amlvideo2_id, def_amlvideo2_chain);
#endif /**/
#ifdef CONFIG_AMLOGIC_MEDIA_ENHANCEMENT_DOLBYVISION
vfm_map_add(def_dvbl_id, def_dvbl_chain);
vfm_map_add(def_dvel_id, def_dvel_chain);
#endif
#if 1/*def CONFIG_AM_HDMIIN_DV*/
vfm_map_add(def_dvhdmiin_id, def_dvhdmiin_chain);
#endif
}
/*
* cat /sys/class/vfm/map
*/
static ssize_t vfm_map_show(struct class *class,
struct class_attribute *attr, char *buf)
{
int i, j;
int len = 0;
for (i = 0; i < vfm_map_num; i++) {
if (vfm_map[i] && vfm_map[i]->valid) {
len += sprintf(buf + len, "[%02d] %s { ",
i,/*in slot num.*/
vfm_map[i]->id);
for (j = 0; j < vfm_map[i]->vfm_map_size; j++) {
if (j < (vfm_map[i]->vfm_map_size - 1)) {
len += sprintf(buf + len, "%s(%d) ",
vfm_map[i]->name[j],
(vfm_map[i]->active >> j) & 0x1);
} else{
len += sprintf(buf + len, "%s",
vfm_map[i]->name[j]);
}
}
len += sprintf(buf + len, "}\n");
}
}
len += provider_list(buf + len);
len += receiver_list(buf + len);
return len;
}
static int vfm_vf_get_states(struct vframe_provider_s *vfp,
struct vframe_states *states)
{
int ret = -1;
unsigned long flags;
spin_lock_irqsave(&lock, flags);
ret = vf_get_states(vfp, states);
spin_unlock_irqrestore(&lock, flags);
return ret;
}
static inline struct vframe_s *vfm_vf_peek(
struct vframe_provider_s *vfp)
{
if (!(vfp && vfp->ops && vfp->ops->peek))
return NULL;
return vfp->ops->peek(vfp->op_arg);
}
static void vfm_dump_provider(const char *name)
{
struct vframe_provider_s *prov;
struct vframe_states states;
unsigned long flags;
struct vframe_s *vf;
char *buf, *pbuf;
if (IS_ERR_OR_NULL(name))
return;
prov = vf_get_provider_by_name(name);
if (IS_ERR_OR_NULL(prov))
return;
buf = kzalloc(0x400, GFP_KERNEL);
if (!buf)
return;
pbuf = buf;
if (!vfm_vf_get_states(prov, &states)) {
pr_info("vframe_pool_size=%d\n",
states.vf_pool_size);
pr_info("vframe buf_free_num=%d\n",
states.buf_free_num);
pr_info("vframe buf_recycle_num=%d\n",
states.buf_recycle_num);
pr_info("vframe buf_avail_num=%d\n",
states.buf_avail_num);
spin_lock_irqsave(&lock, flags);
vf = vfm_vf_peek(prov);
if (vf) {
pbuf += sprintf(pbuf,
"vframe ready frame delayed =%dms\n",
(int)(jiffies_64 -
vf->ready_jiffies64) * 1000 /
HZ);
pbuf += sprintf(pbuf, "vf index=%d\n", vf->index);
pbuf += sprintf(pbuf, "vf->pts=%d\n", vf->pts);
pbuf += sprintf(pbuf, "vf->type=%d\n", vf->type);
if (vf->type & VIDTYPE_COMPRESS) {
pbuf += sprintf(pbuf, "vf compHeadAddr=%x\n",
vf->compHeadAddr);
pbuf += sprintf(pbuf, "vf compBodyAddr =%x\n",
vf->compBodyAddr);
} else {
pbuf += sprintf(pbuf, "vf canvas0Addr=%x\n",
vf->canvas0Addr);
pbuf += sprintf(pbuf, "vf canvas1Addr=%x\n",
vf->canvas1Addr);
pbuf += sprintf(pbuf,
"vf canvas0Addr.y.addr=%x(%d)\n",
canvas_get_addr(
canvasY(vf->canvas0Addr)),
canvas_get_addr(
canvasY(vf->canvas0Addr)));
pbuf += sprintf(pbuf,
"vf canvas0Adr.uv.adr=%x(%d)\n",
canvas_get_addr(
canvasUV(vf->canvas0Addr)),
canvas_get_addr(
canvasUV(vf->canvas0Addr)));
}
}
spin_unlock_irqrestore(&lock, flags);
pr_info("%s\n", buf);
}
vftrace_dump_trace_infos(prov->traceget);
vftrace_dump_trace_infos(prov->traceput);
kfree(buf);
}
#define VFM_CMD_ADD 1
#define VFM_CMD_RM 2
#define VFM_CMD_DUMP 3
#define VFM_CMD_ADDDUMMY 4
/*dummy receiver*/
static int dummy_receiver_event_fun(int type, void *data, void *arg)
{
struct vframe_receiver_s *dummy_vf_recv
= (struct vframe_receiver_s *)arg;
if (type == VFRAME_EVENT_PROVIDER_UNREG) {
char *provider_name = (char *)data;
pr_info("%s, provider %s unregistered\n",
__func__, provider_name);
} else if (type ==
VFRAME_EVENT_PROVIDER_VFRAME_READY) {
struct vframe_s *vframe_tmp = vf_get(dummy_vf_recv->name);
while (vframe_tmp) {
vf_put(vframe_tmp, dummy_vf_recv->name);
vf_notify_provider(dummy_vf_recv->name,
VFRAME_EVENT_RECEIVER_PUT, NULL);
vframe_tmp = vf_get(dummy_vf_recv->name);
}
} else if (type == VFRAME_EVENT_PROVIDER_QUREY_STATE) {
return RECEIVER_ACTIVE;
} else if (type == VFRAME_EVENT_PROVIDER_REG) {
char *provider_name = (char *)data;
pr_info("%s, provider %s registered\n",
__func__, provider_name);
}
return 0;
}
static const struct vframe_receiver_op_s dummy_vf_receiver = {
.event_cb = dummy_receiver_event_fun
};
static void add_dummy_receiver(char *vfm_name_)
{
struct vframe_receiver_s *dummy_vf_recv =
kmalloc(sizeof(struct vframe_receiver_s), GFP_KERNEL);
pr_info("%s(%s)\n", __func__, vfm_name_);
if (dummy_vf_recv) {
char *vfm_name = kmalloc(16, GFP_KERNEL);
snprintf(vfm_name, 16, "%s", vfm_name_);
vf_receiver_init(dummy_vf_recv, vfm_name,
&dummy_vf_receiver, dummy_vf_recv);
vf_reg_receiver(dummy_vf_recv);
pr_info("%s: %s\n", __func__, dummy_vf_recv->name);
}
}
/**/
/*
* echo add <name> <node1 node2 ...> > /sys/class/vfm/map
* echo rm <name> > /sys/class/vfm/map
* echo rm all > /sys/class/vfm/map
* echo dump providername > /sys/class/vfm/map
* echo dummy name > /sys/class/vfm/map
* <name> the name of the path.
* <node1 node2 ...> the name of the nodes in the path.
*/
static ssize_t vfm_map_store(struct class *class,
struct class_attribute *attr,
const char *buf, size_t count)
{
char *buf_orig, *ps, *token;
int i = 0;
int cmd = 0;
char *id = NULL;
if (vfm_debug_flag & 0x10000)
return count;
pr_err("%s:%s\n", __func__, buf);
buf_orig = kstrdup(buf, GFP_KERNEL);
ps = buf_orig;
while (1) {
token = strsep(&ps, "\n ");
if (token == NULL)
break;
if (*token == '\0')
continue;
if (i == 0) { /* command */
if (!strcmp(token, "add"))
cmd = VFM_CMD_ADD;
else if (!strcmp(token, "rm"))
cmd = VFM_CMD_RM;
else if (!strcmp(token, "dump"))
cmd = VFM_CMD_DUMP;
else if (!strcmp(token, "dummy"))
cmd = VFM_CMD_ADDDUMMY;
else
break;
} else if (i == 1) {
id = token;
if (cmd == VFM_CMD_ADD) {
/* pr_err("vfm_map_add(%s,%s)\n",id,ps); */
vfm_map_add(id, ps);
} else if (cmd == VFM_CMD_RM) {
/* pr_err("vfm_map_remove(%s)\n",id); */
if (vfm_map_remove(id) < 0)
count = 0;
} else if (cmd == VFM_CMD_DUMP) {
vfm_dump_provider(token);
} else if (cmd == VFM_CMD_ADDDUMMY) {
add_dummy_receiver(token);
}
break;
}
i++;
}
kfree(buf_orig);
return count;
}
static CLASS_ATTR(map, 0664, vfm_map_show, vfm_map_store);
static struct class vfm_class = {
.name = CLS_NAME,
};
int vfm_map_store_fun(const char *trigger, int id, const char *buf, int size)
{
int ret = size;
switch (id) {
case 0: return vfm_map_store(NULL, NULL, buf, size);
default:
ret = -1;
}
return size;
}
int vfm_map_show_fun(const char *trigger, int id, char *sbuf, int size)
{
int ret = -1;
void *buf, *getbuf = NULL;
if (size < PAGE_SIZE) {
getbuf = (void *)__get_free_page(GFP_KERNEL);
if (!getbuf)
return -ENOMEM;
buf = getbuf;
} else {
buf = sbuf;
}
switch (id) {
case 0:
ret = vfm_map_show(NULL, NULL, buf);
break;
default:
ret = -1;
}
if (ret > 0 && getbuf != NULL) {
ret = min_t(int, ret, size);
strncpy(sbuf, buf, ret);
}
if (getbuf != NULL)
free_page((unsigned long)getbuf);
return ret;
}
static struct mconfig vfm_configs[] = {
MC_FUN_ID("map", vfm_map_show_fun, vfm_map_store_fun, 0),
};
/*********************************************************
* /dev/vfm APIs
*********************************************************/
static int vfm_open(struct inode *inode, struct file *file)
{
return 0;
}
static int vfm_release(struct inode *inode, struct file *file)
{
return 0;
}
static long vfm_ioctl(struct file *file, unsigned int cmd, ulong arg)
{
long ret = 0;
struct vfmctl *user_argp = (void __user *)arg;
struct vfmctl argp;
if (IS_ERR_OR_NULL(user_argp))
return -EINVAL;
switch (cmd) {
case VFM_IOCTL_CMD_SET:{
ret = copy_from_user(argp.name, user_argp->name,
sizeof(argp.name) - 1);
argp.name[sizeof(argp.name) - 1] = '\0';
ret |= copy_from_user(argp.val, user_argp->val,
sizeof(argp.val) - 1);
argp.val[sizeof(argp.val) - 1] = '\0';
if (ret)
ret = -EINVAL;
else
ret = vfm_map_store(NULL, NULL, argp.val,
sizeof(argp.val) - 1);
}
break;
case VFM_IOCTL_CMD_GET:{
/*
*overflow bug, need fixed.
*vfm_map_show(NULL, NULL, argp.val);
*ret = copy_to_user(user_argp->val, argp.val, sizeof(argp.val));
*if (ret != 0)
* return -EIO;
*}
*/
return -EIO;
}
break;
case VFM_IOCTL_CMD_ADD:{
ret = copy_from_user(argp.name, user_argp->name,
sizeof(argp.name) - 1);
argp.name[sizeof(argp.name) - 1] = '\0';
ret |= copy_from_user(argp.val, user_argp->val,
sizeof(argp.val) - 1);
argp.val[sizeof(argp.val) - 1] = '\0';
if (ret)
ret = -EINVAL;
else
ret = vfm_map_add(argp.name, argp.val);
}
break;
case VFM_IOCTL_CMD_RM:{
ret = copy_from_user(argp.val, user_argp->val,
sizeof(argp.val) - 1);
argp.val[sizeof(argp.val) - 1] = '\0';
if (ret)
ret = -EINVAL;
else
ret = vfm_map_remove(argp.val);
}
break;
case VFM_IOCTL_CMD_DUMP:{
ret = copy_from_user(argp.val, user_argp->val,
sizeof(argp.val) - 1);
argp.val[sizeof(argp.val) - 1] = '\0';
if (ret)
ret = -EINVAL;
else
vfm_dump_provider(argp.val);
}
break;
case VFM_IOCTL_CMD_ADDDUMMY:{
ret = copy_from_user(argp.val, user_argp->val,
sizeof(argp.val) - 1);
argp.val[sizeof(argp.val) - 1] = '\0';
if (ret)
ret = -EINVAL;
else
add_dummy_receiver(argp.val);
}
break;
default:
return -EINVAL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long vfm_compat_ioctl(struct file *file, unsigned int cmd, ulong arg)
{
long ret = 0;
ret = vfm_ioctl(file, cmd, (ulong)compat_ptr(arg));
return ret;
}
#endif
static const struct file_operations vfm_fops = {
.owner = THIS_MODULE,
.open = vfm_open,
.release = vfm_release,
.unlocked_ioctl = vfm_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = vfm_compat_ioctl,
#endif
.poll = NULL,
};
static int __init vfm_class_init(void)
{
int error;
vfm_init();
error = class_register(&vfm_class);
if (error) {
pr_err("%s: class_register failed\n", __func__);
return error;
}
error = class_create_file(&vfm_class, &class_attr_map);
if (error) {
pr_err("%s: class_create_file failed\n", __func__);
class_unregister(&vfm_class);
}
REG_PATH_CONFIGS("media.vfm", vfm_configs);
/* create vfm device */
error = register_chrdev(VFM_MAJOR, "vfm", &vfm_fops);
if (error < 0) {
pr_err("Can't register major for vfm device\n");
return error;
}
vfm_dev = device_create(&vfm_class, NULL,
MKDEV(VFM_MAJOR, 0), NULL, DEV_NAME);
return error;
}
static void __exit vfm_class_exit(void)
{
class_unregister(&vfm_class);
unregister_chrdev(VFM_MAJOR, DEV_NAME);
}
fs_initcall(vfm_class_init);
module_exit(vfm_class_exit);
MODULE_PARM_DESC(vfm_debug_flag, "\n vfm_debug_flag\n");
module_param(vfm_debug_flag, int, 0664);
MODULE_PARM_DESC(vfm_map_num, "\n vfm_map_num\n");
module_param(vfm_map_num, int, 0664);
module_param(vfm_trace_enable, int, 0664);
MODULE_PARM_DESC(vfm_trace_enable, "\n vfm_trace_enable\n");
module_param(vfm_trace_num, int, 0664);
MODULE_PARM_DESC(vfm_trace_num, "\n vfm_trace_num\n");
MODULE_DESCRIPTION("Amlogic video frame manager driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Bobby Yang <bo.yang@amlogic.com>");