blob: 915ee38568093ea9661b128619efd6a59438713c [file] [log] [blame]
/*
* drivers/amlogic/media/subtitle/subtitle.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.
*
*/
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/vmalloc.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/amlogic/media/utils/amstream.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
//#include "amports_priv.h"
//#include "amlog.h"
//MODULE_AMLOG(AMLOG_DEFAULT_LEVEL, 0, LOG_DEFAULT_LEVEL_DESC,
//LOG_DEFAULT_MASK_DESC);
#define DEVICE_NAME "amsubtitle"
/* Dev name as it appears in /proc/devices */
#define DEVICE_CLASS_NAME "subtitle"
static int subdevice_open;
#define MAX_SUBTITLE_PACKET 10
static DEFINE_MUTEX(amsubtitle_mutex);
struct subtitle_data_s {
int subtitle_size;
int subtitle_pts;
char *data;
};
static struct subtitle_data_s subtitle_data[MAX_SUBTITLE_PACKET];
static int subtitle_enable = 1;
static int subtitle_total;
static int subtitle_width;
static int subtitle_height;
static int subtitle_type = -1;
static int subtitle_current; /* no subtitle */
/* sub_index node will be modified by libplayer; amlogicplayer will use */
/* it to detect wheather libplayer switch sub finished or not */
static int subtitle_index; /* no subtitle */
/* static int subtitle_size = 0; */
/* static int subtitle_read_pos = 0; */
static int subtitle_write_pos;
static int subtitle_start_pts;
static int subtitle_fps;
static int subtitle_subtype;
static int subtitle_reset;
/* static int *subltitle_address[MAX_SUBTITLE_PACKET]; */
enum subinfo_para_e {
SUB_NULL = -1,
SUB_ENABLE = 0,
SUB_TOTAL,
SUB_WIDTH,
SUB_HEIGHT,
SUB_TYPE,
SUB_CURRENT,
SUB_INDEX,
SUB_WRITE_POS,
SUB_START_PTS,
SUB_FPS,
SUB_SUBTYPE,
SUB_RESET,
SUB_DATA_T_SIZE,
SUB_DATA_T_DATA
};
struct subinfo_para_s {
enum subinfo_para_e subinfo_type;
int subtitle_info;
char *data;
};
/* total */
/* curr */
/* bimap */
/* text */
/* type */
/* info */
/* pts */
/* duration */
/* color pallete */
/* width/height */
static ssize_t show_curr(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: current\n", subtitle_current);
}
static ssize_t store_curr(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int curr;
ssize_t r;
r = kstrtoint(buf, 0, &curr);
if (r < 0)
return -EINVAL;
subtitle_current = curr;
return size;
}
static ssize_t show_index(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: current\n", subtitle_index);
}
static ssize_t store_index(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int curr;
ssize_t r;
r = kstrtoint(buf, 0, &curr);
if (r < 0)
return -EINVAL;
subtitle_index = curr;
return size;
}
static ssize_t show_reset(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: current\n", subtitle_reset);
}
static ssize_t store_reset(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int reset;
ssize_t r;
r = kstrtoint(buf, 0, &reset);
pr_info("reset is %d\n", reset);
if (r < 0)
return -EINVAL;
subtitle_reset = reset;
return size;
}
static ssize_t show_type(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: type\n", subtitle_type);
}
static ssize_t store_type(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int type;
ssize_t r;
r = kstrtoint(buf, 0, &type);
if (r < 0)
return -EINVAL;
subtitle_type = type;
return size;
}
static ssize_t show_width(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: width\n", subtitle_width);
}
static ssize_t store_width(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int width;
ssize_t r;
r = kstrtoint(buf, 0, &width);
if (r < 0)
return -EINVAL;
subtitle_width = width;
return size;
}
static ssize_t show_height(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: height\n", subtitle_height);
}
static ssize_t store_height(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int height;
ssize_t r;
r = kstrtoint(buf, 0, &height);
if (r < 0)
return -EINVAL;
subtitle_height = height;
return size;
}
static ssize_t show_total(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: num\n", subtitle_total);
}
static ssize_t store_total(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int total;
ssize_t r;
r = kstrtoint(buf, 0, &total);
if (r < 0)
return -EINVAL;
pr_info("subtitle num is %d\n", total);
subtitle_total = total;
return size;
}
static ssize_t show_enable(struct class *class, struct class_attribute *attr,
char *buf)
{
if (subtitle_enable)
return sprintf(buf, "1: enabled\n");
return sprintf(buf, "0: disabled\n");
}
static ssize_t store_enable(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int mode;
ssize_t r;
r = kstrtoint(buf, 0, &mode);
if (r < 0)
return -EINVAL;
pr_info("subtitle enable is %d\n", mode);
subtitle_enable = mode ? 1 : 0;
return size;
}
static ssize_t show_size(struct class *class, struct class_attribute *attr,
char *buf)
{
if (subtitle_enable)
return sprintf(buf, "1: size\n");
return sprintf(buf, "0: size\n");
}
static ssize_t store_size(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int ssize;
ssize_t r;
r = kstrtoint(buf, 0, &ssize);
if (r < 0)
return -EINVAL;
pr_info("subtitle size is %d\n", ssize);
subtitle_data[subtitle_write_pos].subtitle_size = ssize;
return size;
}
static ssize_t show_startpts(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: pts\n", subtitle_start_pts);
}
static ssize_t store_startpts(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int spts;
ssize_t r;
r = kstrtoint(buf, 0, &spts);
if (r < 0)
return -EINVAL;
pr_info("subtitle start pts is %x\n", spts);
subtitle_start_pts = spts;
return size;
}
static ssize_t show_data(struct class *class, struct class_attribute *attr,
char *buf)
{
#if 0
if (subtitle_data[subtitle_write_pos].data)
return sprintf(buf, "%lld\n",
(unsigned long)(subtitle_data[subtitle_write_pos].data));
#endif
return sprintf(buf, "0: disabled\n");
}
static ssize_t store_data(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int address;
ssize_t r;
r = kstrtoint(buf, 0, &address);
if (r == (ssize_t)0)
return -EINVAL;
#if 0
if (subtitle_data[subtitle_write_pos].subtitle_size > 0) {
subtitle_data[subtitle_write_pos].data = vmalloc((
subtitle_data[subtitle_write_pos].subtitle_size));
if (subtitle_data[subtitle_write_pos].data)
memcpy(subtitle_data[subtitle_write_pos].data,
(unsigned long *)address,
subtitle_data[subtitle_write_pos].subtitle_size);
}
pr_info("subtitle data address is %x",
(unsigned int)(subtitle_data[subtitle_write_pos].data));
#endif
subtitle_write_pos++;
if (subtitle_write_pos >= MAX_SUBTITLE_PACKET)
subtitle_write_pos = 0;
return 1;
}
static ssize_t show_fps(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: fps\n", subtitle_fps);
}
static ssize_t store_fps(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int ssize;
ssize_t r;
r = kstrtoint(buf, 0, &ssize);
if (r < 0)
return -EINVAL;
pr_info("subtitle fps is %d\n", ssize);
subtitle_fps = ssize;
return size;
}
static ssize_t show_subtype(struct class *class, struct class_attribute *attr,
char *buf)
{
return sprintf(buf, "%d: subtype\n", subtitle_subtype);
}
static ssize_t store_subtype(struct class *class, struct class_attribute *attr,
const char *buf, size_t size)
{
unsigned int ssize;
ssize_t r;
r = kstrtoint(buf, 0, &ssize);
if (r < 0)
return -EINVAL;
pr_info("subtitle subtype is %d\n", ssize);
subtitle_subtype = ssize;
return size;
}
static struct class_attribute subtitle_class_attrs[] = {
__ATTR(enable, 0664, show_enable, store_enable),
__ATTR(total, 0664, show_total, store_total),
__ATTR(width, 0664, show_width, store_width),
__ATTR(height, 0664, show_height, store_height),
__ATTR(type, 0664, show_type, store_type),
__ATTR(curr, 0664, show_curr, store_curr),
__ATTR(index, 0664, show_index, store_index),
__ATTR(size, 0664, show_size, store_size),
__ATTR(data, 0664, show_data, store_data),
__ATTR(startpts, 0664, show_startpts,
store_startpts),
__ATTR(fps, 0664, show_fps, store_fps),
__ATTR(subtype, 0664, show_subtype,
store_subtype),
__ATTR(reset, 0644, show_reset, store_reset),
__ATTR_NULL
};
/*********************************************************
* /dev/amvideo APIs
*********************************************************/
static int amsubtitle_open(struct inode *inode, struct file *file)
{
mutex_lock(&amsubtitle_mutex);
if (subdevice_open) {
mutex_unlock(&amsubtitle_mutex);
return -EBUSY;
}
subdevice_open = 1;
try_module_get(THIS_MODULE);
mutex_unlock(&amsubtitle_mutex);
return 0;
}
static int amsubtitle_release(struct inode *inode, struct file *file)
{
mutex_lock(&amsubtitle_mutex);
subdevice_open = 0;
module_put(THIS_MODULE);
mutex_unlock(&amsubtitle_mutex);
return 0;
}
static long amsubtitle_ioctl(struct file *file, unsigned int cmd, ulong arg)
{
switch (cmd) {
case AMSTREAM_IOC_GET_SUBTITLE_INFO: {
struct subinfo_para_s Vstates;
struct subinfo_para_s *states = &Vstates;
if (copy_from_user((void *)states,
(void *)arg, sizeof(Vstates)))
return -EFAULT;
switch (states->subinfo_type) {
case SUB_ENABLE:
states->subtitle_info = subtitle_enable;
break;
case SUB_TOTAL:
states->subtitle_info = subtitle_total;
break;
case SUB_WIDTH:
states->subtitle_info = subtitle_width;
break;
case SUB_HEIGHT:
states->subtitle_info = subtitle_height;
break;
case SUB_TYPE:
states->subtitle_info = subtitle_type;
break;
case SUB_CURRENT:
states->subtitle_info = subtitle_current;
break;
case SUB_INDEX:
states->subtitle_info = subtitle_index;
break;
case SUB_WRITE_POS:
states->subtitle_info = subtitle_write_pos;
break;
case SUB_START_PTS:
states->subtitle_info = subtitle_start_pts;
break;
case SUB_FPS:
states->subtitle_info = subtitle_fps;
break;
case SUB_SUBTYPE:
states->subtitle_info = subtitle_subtype;
break;
case SUB_RESET:
states->subtitle_info = subtitle_reset;
break;
case SUB_DATA_T_SIZE:
states->subtitle_info =
subtitle_data[subtitle_write_pos].subtitle_size;
break;
case SUB_DATA_T_DATA: {
if (states->subtitle_info > 0)
states->subtitle_info =
(long)subtitle_data[subtitle_write_pos].data;
}
break;
default:
break;
}
if (copy_to_user((void *)arg, (void *)states, sizeof(Vstates)))
return -EFAULT;
}
break;
case AMSTREAM_IOC_SET_SUBTITLE_INFO: {
struct subinfo_para_s Vstates;
struct subinfo_para_s *states = &Vstates;
if (copy_from_user((void *)states,
(void *)arg, sizeof(Vstates)))
return -EFAULT;
switch (states->subinfo_type) {
case SUB_ENABLE:
subtitle_enable = states->subtitle_info;
break;
case SUB_TOTAL:
subtitle_total = states->subtitle_info;
break;
case SUB_WIDTH:
subtitle_width = states->subtitle_info;
break;
case SUB_HEIGHT:
subtitle_height = states->subtitle_info;
break;
case SUB_TYPE:
subtitle_type = states->subtitle_info;
break;
case SUB_CURRENT:
subtitle_current = states->subtitle_info;
break;
case SUB_INDEX:
subtitle_index = states->subtitle_info;
break;
case SUB_WRITE_POS:
subtitle_write_pos = states->subtitle_info;
break;
case SUB_START_PTS:
subtitle_start_pts = states->subtitle_info;
break;
case SUB_FPS:
subtitle_fps = states->subtitle_info;
break;
case SUB_SUBTYPE:
subtitle_subtype = states->subtitle_info;
break;
case SUB_RESET:
subtitle_reset = states->subtitle_info;
break;
case SUB_DATA_T_SIZE:
subtitle_data[subtitle_write_pos].subtitle_size =
states->subtitle_info;
break;
case SUB_DATA_T_DATA: {
if (states->subtitle_info > 0) {
subtitle_data[subtitle_write_pos].data =
vmalloc((states->subtitle_info));
if (subtitle_data[subtitle_write_pos].data)
memcpy(
subtitle_data[subtitle_write_pos].data,
(char *)states->data,
states->subtitle_info);
}
subtitle_write_pos++;
if (subtitle_write_pos >= MAX_SUBTITLE_PACKET)
subtitle_write_pos = 0;
}
break;
default:
break;
}
}
break;
default:
break;
}
return 0;
}
#ifdef CONFIG_COMPAT
static long amsub_compat_ioctl(struct file *file, unsigned int cmd, ulong arg)
{
long ret = 0;
ret = amsubtitle_ioctl(file, cmd, (ulong)compat_ptr(arg));
return ret;
}
#endif
static const struct file_operations amsubtitle_fops = {
.owner = THIS_MODULE,
.open = amsubtitle_open,
.release = amsubtitle_release,
.unlocked_ioctl = amsubtitle_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = amsub_compat_ioctl,
#endif
};
static struct device *amsubtitle_dev;
static dev_t amsub_devno;
static struct class *amsub_clsp;
static struct cdev *amsub_cdevp;
#define AMSUBTITLE_DEVICE_COUNT 1
static void create_amsub_attrs(struct class *class)
{
int i = 0;
for (i = 0; subtitle_class_attrs[i].attr.name; i++) {
if (class_create_file(class, &subtitle_class_attrs[i]) < 0)
break;
}
}
static void remove_amsub_attrs(struct class *class)
{
int i = 0;
for (i = 0; subtitle_class_attrs[i].attr.name; i++)
class_remove_file(class, &subtitle_class_attrs[i]);
}
int subtitle_init(void)
{
int ret = 0;
ret = alloc_chrdev_region(&amsub_devno, 0, AMSUBTITLE_DEVICE_COUNT,
DEVICE_NAME);
if (ret < 0) {
pr_info("amsub: failed to alloc major number\n");
ret = -ENODEV;
return ret;
}
amsub_clsp = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
if (IS_ERR(amsub_clsp)) {
ret = PTR_ERR(amsub_clsp);
goto err1;
}
create_amsub_attrs(amsub_clsp);
amsub_cdevp = kmalloc(sizeof(struct cdev), GFP_KERNEL);
if (!amsub_cdevp) {
/*pr_info("amsub: failed to allocate memory\n");*/
ret = -ENOMEM;
goto err2;
}
cdev_init(amsub_cdevp, &amsubtitle_fops);
amsub_cdevp->owner = THIS_MODULE;
/* connect the major/minor number to cdev */
ret = cdev_add(amsub_cdevp, amsub_devno, AMSUBTITLE_DEVICE_COUNT);
if (ret) {
pr_info("amsub:failed to add cdev\n");
goto err3;
}
amsubtitle_dev = device_create(amsub_clsp,
NULL, MKDEV(MAJOR(amsub_devno), 0),
NULL, DEVICE_NAME);
if (IS_ERR(amsubtitle_dev)) {
pr_err("## Can't create amsubtitle device\n");
goto err4;
}
return 0;
err4:
cdev_del(amsub_cdevp);
err3:
kfree(amsub_cdevp);
err2:
remove_amsub_attrs(amsub_clsp);
class_destroy(amsub_clsp);
err1:
unregister_chrdev_region(amsub_devno, 1);
return ret;
}
EXPORT_SYMBOL(subtitle_init);
void subtitle_exit(void)
{
unregister_chrdev_region(amsub_devno, 1);
device_destroy(amsub_clsp, MKDEV(MAJOR(amsub_devno), 0));
cdev_del(amsub_cdevp);
kfree(amsub_cdevp);
remove_amsub_attrs(amsub_clsp);
class_destroy(amsub_clsp);
}
EXPORT_SYMBOL(subtitle_exit);