media: Support Closed Caption and subtitle.

PD#170181

1.Add Support Closed Caption driver code.
2.Add subtitle driver code.

Change-Id: If0c6023592c8a3f9fb50a301fe46c6dc3cadb2f1
Signed-off-by: Peng Yixin <yixin.peng@amlogic.com>
This commit is contained in:
Peng Yixin
2018-06-19 13:54:55 +08:00
committed by Dongjin Kim
parent 74a6910af7
commit 2204358fe1
9 changed files with 791 additions and 7 deletions

View File

@@ -433,6 +433,7 @@ static void vh264_notify_work(struct work_struct *work);
static void user_data_push_work(struct work_struct *work);
#ifdef MH264_USERDATA_ENABLE
static void user_data_ready_notify_work(struct work_struct *work);
static void vmh264_wakeup_userdata_poll(void);
#endif
static const char vh264_dec_id[] = "vh264-dev";
@@ -5700,8 +5701,8 @@ static void print_data(unsigned char *pdata,
rec_id, len, flag,
duration, vpts, vpts_valid, poc_number);
#endif
pr_info("%d len = %d, flag = %d, vpts = 0x%x, poc = %d\n",
rec_id, len, flag, vpts, poc);
pr_info("%d len = %d, flag = %d, vpts = 0x%x\n",
rec_id, len, flag, vpts);
if (len == 96) {
int i;
@@ -6248,6 +6249,12 @@ static void vmh264_reset_userdata_fifo(struct vdec_s *vdec, int bInit)
mutex_unlock(&hw->userdata_mutex);
}
}
static void vmh264_wakeup_userdata_poll(void)
{
amstream_wakeup_userdata_poll();
}
#endif
static void user_data_push_work(struct work_struct *work)
@@ -7095,9 +7102,11 @@ static int ammvdec_h264_probe(struct platform_device *pdev)
pdata->dump_state = vmh264_dump_state;
#ifdef MH264_USERDATA_ENABLE
pdata->wakeup_userdata_poll = vmh264_wakeup_userdata_poll;
pdata->user_data_read = vmh264_user_data_read;
pdata->reset_userdata_fifo = vmh264_reset_userdata_fifo;
#else
pdata->wakeup_userdata_poll = NULL;
pdata->user_data_read = NULL;
pdata->reset_userdata_fifo = NULL;
#endif

View File

@@ -1255,6 +1255,7 @@ static void vmpeg12_ppmgr_reset(void)
#endif
static void vmpeg12_reset_userdata_fifo(struct vdec_s *vdec, int bInit);
static void vmpeg12_wakeup_userdata_poll(void);
static void reset_do_work(struct work_struct *work)
{
@@ -1669,6 +1670,11 @@ static void vmpeg12_reset_userdata_fifo(struct vdec_s *vdec, int bInit)
mutex_unlock(&userdata_mutex);
}
static void vmpeg12_wakeup_userdata_poll(void)
{
amstream_wakeup_userdata_poll();
}
static int vmpeg12_vdec_info_init(void)
{
gvs = kzalloc(sizeof(struct vdec_info), GFP_KERNEL);
@@ -2016,7 +2022,7 @@ static int amvdec_mpeg12_probe(struct platform_device *pdev)
pdata->user_data_read = vmpeg12_user_data_read;
pdata->reset_userdata_fifo = vmpeg12_reset_userdata_fifo;
pdata->wakeup_userdata_poll = vmpeg12_wakeup_userdata_poll;
is_reset = 0;
vmpeg12_vdec_info_init();

View File

@@ -3366,8 +3366,10 @@ EXPORT_SYMBOL(vdec_read_user_data);
int vdec_wakeup_userdata_poll(struct vdec_s *vdec)
{
/*if (vdec && vdec == vdec_get_default_vdec_for_userdata())
amstream_wakeup_userdata_poll();*/ //DEBUG_TMP
if (vdec && vdec == vdec_get_default_vdec_for_userdata()) {
if (vdec->wakeup_userdata_poll)
vdec->wakeup_userdata_poll();
}
return 0;
}

View File

@@ -234,7 +234,7 @@ struct vdec_s {
int (*user_data_read)(struct vdec_s *vdec,
struct userdata_param_t *puserdata_para);
void (*reset_userdata_fifo)(struct vdec_s *vdec, int bInit);
void (*wakeup_userdata_poll)(void);
/* private */
void *private; /* decoder per instance specific data */
#ifdef VDEC_DEBUG_SUPPORT

View File

@@ -9,6 +9,7 @@ stream_input-objs += parser/esparser.o
stream_input-objs += parser/tsdemux.o
stream_input-objs += parser/psparser.o
stream_input-objs += parser/rmparser.o
stream_input-objs += subtitle/subtitle.o
obj-y += parser/hw_demux/
obj-y += tv_frontend/

View File

@@ -44,6 +44,7 @@ int amstream_request_firmware_from_sys(const char *file_name,
void set_vsync_pts_inc_mode(int inc);
void set_real_audio_info(void *arg);
void amstream_wakeup_userdata_poll(void);
#define dbg() pr_info("on %s,line %d\n", __func__, __LINE__);
struct device *amports_get_dma_device(void);

View File

@@ -80,6 +80,7 @@
#include "../../frame_provider/decoder/utils/firmware.h"
#include "../../common/chips/chips.h"
#include "../../common/chips/decoder_cpu_ver_info.h"
#include "../subtitle/subtitle.h"
//#define G12A_BRINGUP_DEBUG
@@ -1414,6 +1415,12 @@ int wakeup_userdata_poll(struct userdata_poc_info_t poc,
return userdata_buf->buf_rp;
}
EXPORT_SYMBOL(wakeup_userdata_poll);
void amstream_wakeup_userdata_poll(void)
{
atomic_set(&userdata_ready, 1);
wake_up_interruptible(&amstream_userdata_wait);
}
EXPORT_SYMBOL(amstream_wakeup_userdata_poll);
static unsigned int amstream_userdata_poll(struct file *file,
poll_table *wait_table)
@@ -2994,9 +3001,22 @@ static long amstream_do_ioctl_old(struct port_priv_s *priv,
}
break;
case AMSTREAM_IOC_UD_BUF_READ:
{
struct userdata_param_t __user *p_userdata_param;
p_userdata_param = (void *)arg;
if (this->type & PORT_TYPE_USERDATA) {
if (vdec_read_user_data(NULL,
p_userdata_param) == 0) {
r = -EFAULT;
break;
}
}
}
break;
case AMSTREAM_IOC_UD_FLUSH_USERDATA:
if (this->type & PORT_TYPE_USERDATA) {
reset_userdata_fifo(0);
vdec_reset_userdata_fifo(NULL, 0);
pr_info("reset_userdata_fifo\n");
} else
r = -EINVAL;
@@ -3900,12 +3920,19 @@ static int __init amstream_module_init(void)
pr_err("failed to register amstream module\n");
return -ENODEV;
}
if (subtitle_init()) {
pr_err("failed to init subtitle\n");
return -ENODEV;
}
return 0;
}
static void __exit amstream_module_exit(void)
{
platform_driver_unregister(&amstream_driver);
subtitle_exit();
}
module_init(amstream_module_init);

View File

@@ -0,0 +1,714 @@
/*
* 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 "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);
//r = sscanf(buf, "%d", &curr);
/* if ((r != 1)) */
/* 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 != 1)) */
/* 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 != 1)) */
/* 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 != 1)) */
/* 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 != 1)) */
/* 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 != 1)) */
/* 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 != 1))
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 == 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);

View File

@@ -0,0 +1,24 @@
/*
* drivers/amlogic/media/stream_input/amports/amports_priv.h
*
* Copyright (C) 2016 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.
*
*/
#ifndef SUBTITLE_H
#define SUBTITLE_H
int subtitle_init(void);
void subtitle_exit(void);
#endif