blob: cd9d01e6c8ac2e63898e1c721014eb05986289cc [file] [log] [blame]
/*
* drivers/amlogic/media/vin/tvin/tvafe/tvafe_vbi.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/errno.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/platform_device.h>
#include <linux/uaccess.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-mapping.h>
#include <linux/of_irq.h>
/* #include <linux/mutex.h> */
#include <linux/mm.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/vmalloc.h>
#include <linux/io.h> /* for virt_to_phys */
/* Local include */
#include "tvafe_regs.h"
#include "tvafe_cvd.h"
#include "tvafe_vbi.h"
#include "tvafe_debug.h"
#include "../tvin_global.h"
#define VBI_NAME "vbi"
#define VBI_DRIVER_NAME "vbi"
#define VBI_MODULE_NAME "vbi"
#define VBI_DEVICE_NAME "vbi"
#define VBI_CLASS_NAME "vbi"
#define VBI_VS_DELAY 4
#define VBI_START_DELAY 1000
static dev_t vbi_id;
static struct class *vbi_clsp;
static unsigned char field_data_flag;
/******debug********/
static unsigned int vbi_dbg_en;
MODULE_PARM_DESC(vbi_dbg_en, "\n vbi_dbg_en\n");
module_param(vbi_dbg_en, uint, 0664);
static bool capture_print_en;
MODULE_PARM_DESC(capture_print_en, "\n capture_print_en\n");
module_param(capture_print_en, bool, 0664);
static bool data_print_en;
MODULE_PARM_DESC(data_print_en, "\n data_print_en\n");
module_param(data_print_en, bool, 0664);
static bool vbi_nonblock_en;
MODULE_PARM_DESC(vbi_nonblock_en, "\n vbi_nonblock_en\n");
module_param(vbi_nonblock_en, bool, 0664);
/*cc will be delayed when interval larger than 2*/
static unsigned int vbi_wakeup_interval = 2;
MODULE_PARM_DESC(vbi_wakeup_interval, "\n vbi_wakeup_interval\n");
module_param(vbi_wakeup_interval, uint, 0664);
static unsigned int vcnt = 1;
static unsigned int wakeup_cnt;
static unsigned int init_cc_data_flag;
static bool vbi_pr_en;
static void vbi_hw_reset(struct vbi_dev_s *devp)
{
/*W_VBI_APB_REG(ACD_REG_22, 0x82080000);*/
/*W_VBI_APB_REG(ACD_REG_22, 0x04080000);*/
/* after hw reset,bellow parameters must be reset!!! */
vcnt = 1;
devp->pac_addr = devp->pac_addr_start;
devp->current_pac_wptr = 0;
}
static void vbi_tt_start_code_set(struct vbi_dev_s *devp)
{
unsigned int vbi_start_code = devp->vbi_start_code;
/* start code */
W_VBI_APB_REG(CVD2_VBI_TT_FRAME_CODE_CTL, vbi_start_code);
}
static void vbi_data_type_set(struct vbi_dev_s *devp)
{
unsigned int vbi_data_type = devp->vbi_data_type;
/* data type */
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE6, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE7, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE8, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE9, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE10, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE11, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE12, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE13, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE14, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE15, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE16, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE17, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE18, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE19, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE20, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE21, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE22, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE23, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE24, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE25, vbi_data_type);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE26, vbi_data_type);
}
static void vbi_dto_set(struct vbi_dev_s *devp)
{
unsigned int dto_cc, dto_tt, dto_wss, dto_vps;
dto_cc = devp->vbi_dto_cc;
dto_tt = devp->vbi_dto_tt;
dto_wss = devp->vbi_dto_wss;
dto_vps = devp->vbi_dto_vps;
W_VBI_APB_REG(CVD2_VBI_CC_DTO_MSB, ((dto_cc>>8) & 0xff));
W_VBI_APB_REG(CVD2_VBI_CC_DTO_LSB, (dto_cc & 0xff));
W_VBI_APB_REG(CVD2_VBI_TT_DTO_MSB, ((dto_tt>>8) & 0xff));
W_VBI_APB_REG(CVD2_VBI_TT_DTO_LSB, (dto_tt & 0xff));
W_VBI_APB_REG(CVD2_VBI_WSS_DTO_MSB, ((dto_wss>>8) & 0xff));
W_VBI_APB_REG(CVD2_VBI_WSS_DTO_LSB, (dto_wss & 0xff));
W_VBI_APB_REG(CVD2_VBI_VPS_DTO_MSB, ((dto_vps>>8) & 0xff));
W_VBI_APB_REG(CVD2_VBI_VPS_DTO_LSB, (dto_vps & 0xff));
}
/* hw reset,config vbi line data type,start code,dto */
static void vbi_slicer_type_set(struct vbi_dev_s *devp)
{
enum vbi_slicer_e slicer_type = devp->slicer->type;
vbi_hw_reset(devp);
switch (slicer_type) {
case VBI_TYPE_USCC:
devp->vbi_data_type = VBI_DATA_TYPE_USCC;
devp->vbi_start_code = VBI_START_CODE_USCC;
devp->vbi_dto_cc = VBI_DTO_USCC;
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE21,
VBI_DATA_TYPE_USCC);
break;
case VBI_TYPE_EUROCC:
devp->vbi_data_type = VBI_DATA_TYPE_EUROCC;
devp->vbi_start_code = VBI_START_CODE_EUROCC;
devp->vbi_dto_cc = VBI_DTO_EURCC;
/*line18 for PAL M*/
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE18,
VBI_DATA_TYPE_EUROCC);
/*line22 for PAL B,D,G,H,I,N,CN*/
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE22,
VBI_DATA_TYPE_EUROCC);
break;
case VBI_TYPE_VPS:
devp->vbi_data_type = VBI_DATA_TYPE_VPS;
devp->vbi_start_code = VBI_START_CODE_VPS;
devp->vbi_dto_vps = VBI_DTO_VPS;
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE16, VBI_DATA_TYPE_VPS);
break;
case VBI_TYPE_TT_625A:
devp->vbi_data_type = VBI_DATA_TYPE_TT_625A;
devp->vbi_start_code = VBI_START_CODE_TT_625A_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT625A;
break;
case VBI_TYPE_TT_625B:
devp->vbi_data_type = VBI_DATA_TYPE_TT_625B;
devp->vbi_start_code = VBI_START_CODE_TT_625B_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT625B;
break;
case VBI_TYPE_TT_625C:
devp->vbi_data_type = VBI_DATA_TYPE_TT_625B;
devp->vbi_start_code = VBI_START_CODE_TT_625C_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT625C;
break;
case VBI_TYPE_TT_625D:
devp->vbi_data_type = VBI_DATA_TYPE_TT_625D;
devp->vbi_start_code = VBI_START_CODE_TT_625D_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT625D;
break;
case VBI_TYPE_TT_525B:
devp->vbi_data_type = VBI_DATA_TYPE_TT_525B;
devp->vbi_start_code = VBI_START_CODE_TT_525B_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT525B;
break;
case VBI_TYPE_TT_525C:
devp->vbi_data_type = VBI_DATA_TYPE_TT_525C;
devp->vbi_start_code = VBI_START_CODE_TT_525C_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT525C;
break;
case VBI_TYPE_TT_525D:
devp->vbi_data_type = VBI_DATA_TYPE_TT_525D;
devp->vbi_start_code = VBI_START_CODE_TT_525D_REVERSE;
devp->vbi_dto_tt = VBI_DTO_TT525D;
break;
case VBI_TYPE_WSS625:
devp->vbi_data_type = VBI_DATA_TYPE_WSS625;
devp->vbi_start_code = VBI_START_CODE_WSS625;
devp->vbi_dto_wss = VBI_DTO_WSS625;
/*line17 for PAL M*/
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE17,
VBI_DATA_TYPE_WSS625);
/*line23 for PAL B,D,G,H,I,N,CN*/
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE23,
VBI_DATA_TYPE_WSS625);
break;
case VBI_TYPE_WSSJ:
devp->vbi_data_type = VBI_DATA_TYPE_WSSJ;
devp->vbi_start_code = VBI_START_CODE_WSSJ;
devp->vbi_dto_wss = VBI_DTO_WSSJ;
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE20,
VBI_DATA_TYPE_WSSJ);
break;
case VBI_TYPE_NULL:
devp->vbi_dto_cc = VBI_DTO_USCC;
devp->vbi_dto_wss = VBI_DTO_WSS625;
devp->vbi_dto_tt = VBI_DTO_TT625B;
devp->vbi_dto_vps = VBI_DTO_VPS;
devp->vbi_data_type = VBI_DATA_TYPE_NULL;
vbi_data_type_set(devp);
break;
default:/*for tt625b+wss625+vps*/
devp->vbi_dto_cc = VBI_DTO_USCC;
devp->vbi_dto_wss = VBI_DTO_WSS625;
devp->vbi_dto_tt = VBI_DTO_TT625B;
devp->vbi_dto_vps = VBI_DTO_VPS;
W_VBI_APB_REG(CVD2_VBI_TT_FRAME_CODE_CTL,
VBI_START_CODE_TT_625B_REVERSE);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE16, VBI_DATA_TYPE_VPS);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE20,
VBI_DATA_TYPE_TT_625B);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE21,
VBI_DATA_TYPE_TT_625B);
W_VBI_APB_REG(CVD2_VBI_DATA_TYPE_LINE23,
VBI_DATA_TYPE_WSS625);
break;
}
if ((slicer_type >= VBI_TYPE_TT_625A) &&
(slicer_type <= VBI_TYPE_TT_525D)) {
vbi_data_type_set(devp);
vbi_tt_start_code_set(devp);
}
vbi_dto_set(devp);
}
static void vbi_hw_init(struct vbi_dev_s *devp)
{
/* vbi memory setting */
memset(devp->pac_addr_start, 0, devp->mem_size);
cvd_vbi_mem_set(devp->mem_start >> 4, devp->mem_size >> 4);
/*disable vbi*/
W_VBI_APB_REG(CVD2_VBI_FRAME_CODE_CTL, 0x10);
cvd_vbi_config();
/*enable vbi*/
W_VBI_APB_REG(CVD2_VBI_FRAME_CODE_CTL, 0x11);
tvafe_pr_info("%s: vbi hw init done.\n", __func__);
}
#define vbi_get_byte(rdptr, retbyte) \
do {\
retbyte = *(rdptr); \
rdptr += 1; \
} while (0)
#define vbi_skip_bytes(rdptr, total_buffer, devp, nbytes) \
do {\
rdptr += nbytes;\
if (rdptr > devp->pac_addr_end)\
rdptr = devp->pac_addr_start;\
total_buffer -= nbytes;\
} while (0)
ssize_t vbi_ringbuffer_free(struct vbi_ringbuffer_s *rbuf)
{
ssize_t free;
free = rbuf->pread - rbuf->pwrite;
if (free <= 0) {
free += rbuf->size;
if (capture_print_en)
tvafe_pr_info("%s: pread: %6d pwrite: %6d\n",
__func__, rbuf->pread, rbuf->pwrite);
}
return free-1;
}
ssize_t vbi_ringbuffer_write(struct vbi_ringbuffer_s *rbuf,
const struct vbi_data_s *buf, size_t len)
{
size_t todo = len;
size_t split;
if (rbuf->data_wmode == 1)
len = rbuf->size;
split =
(rbuf->pwrite + len > rbuf->size) ? (rbuf->size - rbuf->pwrite) : 0;
if (split > 0) {
if (capture_print_en)
tvafe_pr_info("%s: pwrite: %6d\n",
__func__, rbuf->pwrite);
memcpy((char *)rbuf->data+rbuf->pwrite, (char *)buf, split);
todo -= split;
rbuf->pwrite = 0;
}
memcpy((char *)rbuf->data+rbuf->pwrite, (char *)buf+split, todo);
rbuf->pwrite = (rbuf->pwrite + todo) % rbuf->size;
if (capture_print_en)
tvafe_pr_info("%s: write finish return: %6Zd\n",
__func__, len);
return len;
}
static int vbi_buffer_write(struct vbi_ringbuffer_s *buf,
const struct vbi_data_s *src, size_t len)
{
ssize_t free;
if (!len) {
if (capture_print_en)
tvafe_pr_info("%s: buffer len is zero\n", __func__);
return 0;
}
if ((buf == NULL) || (buf->data == NULL)) {
if (capture_print_en)
tvafe_pr_info("%s: buffer data pointer is zero\n",
__func__);
return 0;
}
free = vbi_ringbuffer_free(buf);
if (len > free) {
if (capture_print_en)
tvafe_pr_info("%s: buffer overflow ,len: %6Zd, free: %6Zd\n",
__func__, len, free);
return -EOVERFLOW;
}
return vbi_ringbuffer_write(buf, src, len);
}
static irqreturn_t vbi_isr(int irq, void *dev_id)
{
ulong flags;
struct vbi_dev_s *devp = (struct vbi_dev_s *)dev_id;
spin_lock_irqsave(&devp->vbi_isr_lock, flags);
if (devp->vs_delay > 0) {
devp->vs_delay--;
spin_unlock_irqrestore(&devp->vbi_isr_lock, flags);
return IRQ_HANDLED;
}
if (devp->vbi_start == false) {
spin_unlock_irqrestore(&devp->vbi_isr_lock, flags);
return IRQ_HANDLED;
}
if (devp->tasklet_enable)
tasklet_schedule(&devp->tsklt_slicer);
spin_unlock_irqrestore(&devp->vbi_isr_lock, flags);
return IRQ_HANDLED;
}
void vbi_ringbuffer_flush(struct vbi_ringbuffer_s *rbuf)
{
rbuf->pread = rbuf->pwrite = 0;
rbuf->error = 0;
}
/*--------------------------mem data----------------------------
* all vbi memory size:0x100000(get from dts)
* 0 ~ 0x3ffff: original vbi data --- VBI_BUFF1_EA
* 0x40000 ~ 0x40fff: for original data compare --- VBI_BUFF2_EA
* 0x41000 ~ 0x41fff: vbi data which will be integration
* to vbi_data_s --- VBI_BUFF3_EA
* 0x42000 ~ 0x42fff: for vbi_data_s data compare--- VBI_BUFF4_EA
*--------------------------------------------------------------
*/
#define VBI_BUFF1_EA 0x0
#define VBI_BUFF2_EA 0x40000
#define VBI_BUFF2_SIZE 0x1000
/*limit data size which be copied to VBI_BUFF3_EA */
#define VBI_BUFF3_EA 0x41000
#define VBI_BUFF3_SIZE 0x1000
static int copy_vbi_to(unsigned char *table_start_addr,
unsigned char *table_end_addr, unsigned char *desc,
unsigned char *source, int size)
{
if ((!table_start_addr) || (!table_end_addr) ||
(!desc) || (!source)) {
tvafe_pr_info("copy_vbi_to addr NULL.\n");
return -1;
} else if ((table_end_addr <= table_start_addr) ||
(desc <= table_end_addr) || (source > table_end_addr) ||
(source < table_start_addr)) {
tvafe_pr_info("copy_vbi_to addr error.\n");
return -1;
} else if ((size <= 0) || (size > VBI_BUFF3_SIZE)) {
tvafe_pr_info("copy_vbi_to size error.\n");
return -1;
}
if (source + size > table_end_addr)
memcpy((table_end_addr+1), table_start_addr, size);
memcpy(desc, source, size);
return 0;
}
/*--------------------search_table()-------------------------
* len = (the found addr) - (search_point addr) + 1
* return the addr where find the search_content
*
* Example:
* table: 0x00 0x11 0x22 0x33 0x44 0x55 0x66 0x77 0x88 0x99
* search_point = 0x22's addr
* search contet = 0x44 0x55 0x66 0x77
* so, len = 6
* return 0x77's addr
*-----------------------------------------------------------
*/
unsigned char *search_table(unsigned char *table_start_addr,
unsigned char *search_point, unsigned char *table_end_addr,
unsigned char *search_content, unsigned char search_size,
unsigned int *len)
{
unsigned char cflag = 0;
unsigned int count = 0;
unsigned int table_size = table_end_addr - table_start_addr + 1;
unsigned char *p = search_point;
if (!table_start_addr || !search_point || !table_end_addr ||
!search_content || (search_size <= 0)) {
tvafe_pr_info("search_table point error\n");
return NULL;
} else if ((table_end_addr <= table_start_addr) ||
(search_point > table_end_addr)) {
tvafe_pr_info("search_table add err: start=%p,search=%p,end=%p\n",
table_start_addr, search_point, table_end_addr);
return NULL;
}
while (count++ < table_size) {
if (((table_end_addr-p+1) < search_size) && (!cflag)) {
cflag = 1;
memcpy((table_end_addr+1),
table_start_addr, search_size);
} else if (p > table_end_addr) {
p = table_start_addr;
}
if (!memcmp(p, search_content, search_size))
break;
p++;
}
*len = count - 1 + search_size;
if (count < table_size) {
if (p + search_size - 1 > table_end_addr)
return (p+search_size-1-table_end_addr-1+
table_start_addr);
else
return p+search_size-1;
} else {
return NULL;
}
}
static void force_set_vcnt(unsigned char *rptr)
{
unsigned char h_val, l_val;
l_val = *rptr & 0xff;
h_val = *(rptr+1) & 0xff;
vcnt = (h_val << 8) | l_val;
if (vbi_pr_en)
tvafe_pr_info("force set vcnt:0x%x\n", vcnt);
}
static void set_vbi_new_vcnt(struct vbi_dev_s *devp, unsigned char *rptr)
{
unsigned char *ret_addr;
unsigned int len, temp;
unsigned char burst_data_vcnt[3] = {0x0, 0xff, 0xff};
unsigned int tval;
unsigned char h_val, l_val;
temp = vcnt;
if ((rptr < devp->pac_addr_start) ||
(rptr > devp->pac_addr_end)) {
tvafe_pr_info("addr error, vbi cannot search vcnt\n");
return;
}
ret_addr = search_table(devp->pac_addr_start, rptr,
devp->pac_addr_end, burst_data_vcnt,
3, &len);
if (!ret_addr) {
force_set_vcnt(rptr);
return;
}
if (ret_addr - devp->pac_addr_start >= 18) {
l_val = *(ret_addr - 18) & 0xff;
h_val = *(ret_addr - 17) & 0xff;
} else {
tval = ret_addr - devp->pac_addr_start;
l_val = devp->pac_addr_start[devp->mem_size-18+tval] & 0xff;
h_val = devp->pac_addr_start[devp->mem_size-17+tval] & 0xff;
}
vcnt = (h_val << 8) | l_val;
if (vbi_pr_en)
tvafe_pr_info("pre vcnt=0x%x, current vcnt=0x%x\n", temp, vcnt);
if (vcnt > 0xffff)
vcnt = temp;
}
static unsigned char *search_vbi_new_addr(struct vbi_dev_s *devp)
{
uint32_t paddr;
unsigned char *vaddr;
unsigned long temp;
/*take out the real-time addr*/
paddr = R_VBI_APB_REG(ACD_REG_43);
/*this addr is 128bit format,need <<4*/
paddr <<= 4;
if (devp->mem_start > paddr)
return NULL;
vaddr = devp->pac_addr_start + paddr - devp->mem_start;
temp = (unsigned long)vaddr;
vaddr = (unsigned char *)((temp >> 4) << 4);
if (vbi_pr_en)
tvafe_pr_info("vbi search new addr\n");
return vaddr;
}
static int init_cc_data_sync(struct vbi_dev_s *devp)
{
static int flag;
static unsigned char *addr;
if (init_cc_data_flag)
return 1;
if (flag == 0) {
addr = search_vbi_new_addr(devp);
flag++;
return 0;
} else if (flag == 4) {
set_vbi_new_vcnt(devp, addr);
init_cc_data_flag = 1;
tvafe_pr_info("init cc data ok\n");
return 1;
}
flag++;
return 0;
}
static int check_if_sync_cc_data(struct vbi_dev_s *devp, unsigned char *addr)
{
static unsigned char *new_addr;
if (!addr) {
field_data_flag++;
if (field_data_flag == 3) {
new_addr = search_vbi_new_addr(devp);
} else if (field_data_flag >= 6) {
field_data_flag = 0;
set_vbi_new_vcnt(devp, new_addr);
}
if (vbi_pr_en)
tvafe_pr_info("not find vbi data.\n");
return -1;
} else if (addr > devp->pac_addr_end) {
if (vbi_pr_en)
tvafe_pr_info("vbi ret_addr error.\n");
return -1;
}
field_data_flag = 0;
return 0;
}
static void vbi_slicer_task(unsigned long arg)
{
struct vbi_dev_s *devp = (struct vbi_dev_s *)arg;
struct vbi_data_s vbi_data;
unsigned char rbyte = 0;
unsigned char i = 0;
unsigned short pre_val = 0;
unsigned char *local_rptr = NULL;
unsigned char burst_data_vcnt[VBI_WRITE_BURST_BYTE] = {0};
unsigned char vbi_head[3] = {0x00, 0xFF, 0xFF};
unsigned char *rptr, *ret_addr, *vbi_addr;
unsigned int len, chlen, vbihlen, datalen;
int ret, ret_cpvbi, ret_sync;
if (devp->vbi_start == false)
return;
if (tvafe_clk_status == false) {
vbi_ringbuffer_flush(&devp->slicer->buffer);
return;
}
ret = init_cc_data_sync(devp);
if (!ret)
return;
if (devp->pac_addr > devp->pac_addr_end)
devp->pac_addr = devp->pac_addr_start;
rptr = devp->pac_addr; /* backup package data pointer */
burst_data_vcnt[0] = vcnt&0xff;
burst_data_vcnt[1] = (vcnt >> 8)&0xff;
ret_addr = search_table(devp->pac_addr_start, rptr,
devp->pac_addr_end, burst_data_vcnt,
VBI_WRITE_BURST_BYTE, &len);
if (vcnt >= 0xffff)
vcnt = 1;
else
vcnt++;
ret_sync = check_if_sync_cc_data(devp, ret_addr);
if (ret_sync < 0)
return;
if ((len == VBI_WRITE_BURST_BYTE) ||
(len > VBI_BUFF3_EA - VBI_BUFF2_EA)) {
if (ret_addr + 1 > devp->pac_addr_end)
devp->pac_addr = devp->pac_addr_start;
else
devp->pac_addr = ret_addr + 1;
return;
}
ret_cpvbi = copy_vbi_to(devp->pac_addr_start, devp->pac_addr_end,
devp->pac_addr_start + VBI_BUFF3_EA, rptr,
(len - VBI_WRITE_BURST_BYTE));
if (ret_cpvbi < 0) {
if (ret_addr + 1 > devp->pac_addr_end)
devp->pac_addr = devp->pac_addr_start;
else
devp->pac_addr = ret_addr + 1;
return;
}
local_rptr = devp->pac_addr_start + VBI_BUFF3_EA;
chlen = len;
while (chlen > 0) {
if (!devp->vbi_start || !tvafe_clk_status)
return;
if ((local_rptr > devp->pac_addr_start+
VBI_BUFF3_EA+VBI_BUFF3_SIZE-1)
|| (local_rptr < devp->pac_addr_start + VBI_BUFF3_EA))
goto err_exit;
vbi_addr = search_table(local_rptr, local_rptr,
devp->pac_addr_start+
VBI_BUFF3_EA+len-VBI_WRITE_BURST_BYTE,
vbi_head, 3, &vbihlen);
if (!vbi_addr) {
/*tvafe_pr_info("not find vbi buff data\n");*/
goto err_exit;
}
chlen -= (vbihlen + 3);
local_rptr = vbi_addr + 1;
/* vbi_type & field_id */
vbi_get_byte(local_rptr, rbyte);
chlen--;
vbi_data.vbi_type = (rbyte>>1) & 0x7;
vbi_data.field_id = rbyte & 1;
#if defined(VBI_TT_SUPPORT)
vbi_data.tt_sys = rbyte >> 5;
#endif
if (vbi_data.vbi_type > MAX_PACKET_TYPE) {
tvafe_pr_info("[vbi..]: unsupport vbi_type_id:%d\n",
vbi_data.vbi_type);
continue;
}
/* byte counter */
vbi_get_byte(local_rptr, rbyte);
chlen--;
vbi_data.nbytes = rbyte;
/* line number */
vbi_get_byte(local_rptr, rbyte);
chlen--;
pre_val = (u16)rbyte;
vbi_get_byte(local_rptr, rbyte);
chlen--;
pre_val |= ((u16)rbyte & 0x3) << 8;
vbi_data.line_num = pre_val;
/* data */
memcpy(&(vbi_data.b[0]), local_rptr, vbi_data.nbytes);
local_rptr += vbi_data.nbytes;
chlen -= vbi_data.nbytes;
/* capture data to vbi buffer */
datalen = sizeof(struct vbi_data_s);
vbi_buffer_write(&devp->slicer->buffer, &vbi_data, datalen);
/* go to search next package */
if (data_print_en) {
tvafe_pr_info("[vbi..]: field_id:%d, line_num:%4d;",
vbi_data.field_id,
vbi_data.line_num);
for (i = 0; i < vbi_data.nbytes ; i++) {
if (i%16 == 0)
tvafe_pr_info("\n");
tvafe_pr_info("%2x ", vbi_data.b[i]);
}
tvafe_pr_info("\n");
}
}
if ((devp->slicer->buffer.pread != devp->slicer->buffer.pwrite) &&
(wakeup_cnt++ % vbi_wakeup_interval == 0))
wake_up(&devp->slicer->buffer.queue);
err_exit:
if (ret_addr + 1 > devp->pac_addr_end)
devp->pac_addr = devp->pac_addr_start;
else
devp->pac_addr = ret_addr + 1;
return;
}
static inline void vbi_slicer_state_set(struct vbi_dev_s *dev,
int state)
{
spin_lock_irq(&dev->lock);
dev->slicer->state = state;
spin_unlock_irq(&dev->lock);
}
static inline int vbi_slicer_reset(struct vbi_dev_s *dev)
{
if (dev->slicer->state < VBI_STATE_SET)
return 0;
dev->slicer->type = VBI_TYPE_NULL;
vbi_slicer_state_set(dev, VBI_STATE_ALLOCATED);
return 0;
}
static int vbi_slicer_stop(struct vbi_slicer_s *vbi_slicer)
{
if (vbi_slicer->state < VBI_STATE_GO)
return 0;
vbi_slicer->state = VBI_STATE_DONE;
vbi_ringbuffer_flush(&vbi_slicer->buffer);
return 0;
}
static int vbi_slicer_free(struct vbi_dev_s *vbi_dev,
struct vbi_slicer_s *vbi_slicer)
{
mutex_lock(&vbi_dev->mutex);
mutex_lock(&vbi_slicer->mutex);
vbi_slicer_stop(vbi_slicer);
vbi_slicer_reset(vbi_dev);
if (vbi_slicer->buffer.data) {
void *mem = vbi_slicer->buffer.data;
spin_lock_irq(&vbi_dev->lock);
vbi_slicer->buffer.data = NULL;
spin_unlock_irq(&vbi_dev->lock);
vfree(mem);
}
vbi_slicer_state_set(vbi_dev, VBI_STATE_FREE);
wake_up(&vbi_slicer->buffer.queue);
mutex_unlock(&vbi_slicer->mutex);
mutex_unlock(&vbi_dev->mutex);
return 0;
}
static void vbi_ringbuffer_init(struct vbi_ringbuffer_s *rbuf,
void *data, size_t len)
{
rbuf->pread = rbuf->pwrite = 0;
if (data == NULL)
rbuf->data = vmalloc(len*sizeof(struct vbi_data_s));
else
rbuf->data = data;
rbuf->size = len*sizeof(struct vbi_data_s);
rbuf->data_num = len;
rbuf->error = 0;
}
void vbi_ringbuffer_reset(struct vbi_ringbuffer_s *rbuf)
{
rbuf->pread = rbuf->pwrite = 0;
rbuf->error = 0;
}
static int vbi_set_buffer_size(struct vbi_dev_s *dev,
unsigned int size)
{
struct vbi_slicer_s *vbi_slicer = dev->slicer;
struct vbi_ringbuffer_s *buf = &vbi_slicer->buffer;
void *newmem;
void *oldmem;
if (buf->size == size) {
tvafe_pr_info("%s: buf->size == size\n", __func__);
return 0;
}
if (!size) {
tvafe_pr_info("%s: buffer size is 0!!!\n", __func__);
return -EINVAL;
}
if (vbi_slicer->state >= VBI_STATE_GO) {
tvafe_pr_info("%s: vbi_slicer busy!!!\n", __func__);
return -EBUSY;
}
newmem = vmalloc(size);
if (!newmem)
return -ENOMEM;
oldmem = buf->data;
spin_lock_irq(&dev->lock);
buf->data = newmem;
buf->size = size;
/* reset and not flush in case the buffer shrinks */
vbi_ringbuffer_reset(buf);
spin_unlock_irq(&dev->lock);
vfree(oldmem);
return 0;
}
static int vbi_slicer_start(struct vbi_dev_s *dev)
{
struct vbi_slicer_s *vbi_slicer = dev->slicer;
void *mem;
if (vbi_slicer->state < VBI_STATE_SET)
return -EINVAL;
if (vbi_slicer->state >= VBI_STATE_GO)
vbi_slicer_stop(vbi_slicer);
if (!vbi_slicer->buffer.data) {
mem = vmalloc(vbi_slicer->buffer.size);
if (!mem) {
tvafe_pr_info("[vbi..] %s: get memory error!!!\n",
__func__);
return -ENOMEM;
}
spin_lock_irq(&dev->lock);
vbi_slicer->buffer.data = mem;
spin_unlock_irq(&dev->lock);
}
vbi_ringbuffer_flush(&vbi_slicer->buffer);
vbi_slicer_state_set(dev, VBI_STATE_GO);
return 0;
}
static int vbi_slicer_set(struct vbi_dev_s *vbi_dev,
struct vbi_slicer_s *vbi_slicer)
{
vbi_slicer_stop(vbi_slicer);
vbi_slicer_state_set(vbi_dev, VBI_STATE_SET);
vbi_slicer_type_set(vbi_dev);
return 0;/* vbi_slicer_start(vbi_dev); */
}
ssize_t vbi_ringbuffer_avail(struct vbi_ringbuffer_s *rbuf)
{
ssize_t avail;
avail = (rbuf->pwrite >= rbuf->pread) ?
(rbuf->pwrite - rbuf->pread) :
(rbuf->size - (rbuf->pread - rbuf->pwrite));
return avail;
}
int vbi_ringbuffer_empty(struct vbi_ringbuffer_s *rbuf)
{
return (int)(rbuf->pread == rbuf->pwrite);
}
ssize_t vbi_ringbuffer_read_user(struct vbi_ringbuffer_s *rbuf,
u8 __user *buf, size_t len)
{
size_t todo = len;
size_t split;
split = (rbuf->pread + len > rbuf->size) ? rbuf->size - rbuf->pread : 0;
if (split > 0) {
if (copy_to_user(buf, (char *)rbuf->data+rbuf->pread, split))
return -EFAULT;
buf += split;
todo -= split;
rbuf->pread = 0;
}
if (copy_to_user(buf, (char *)rbuf->data+rbuf->pread, todo))
return -EFAULT;
rbuf->pread = (rbuf->pread + todo) % rbuf->size;
return len;
}
static ssize_t vbi_buffer_read(struct vbi_ringbuffer_s *src,
int non_blocking, char __user *buf,
size_t count, loff_t *ppos)
{
ssize_t avail;
ssize_t ret = 0;
ssize_t timeout = 0;
if ((src == NULL) || (src->data == NULL)) {
tvafe_pr_info("%s: data null\n", __func__);
return 0;
}
if (src->error) {
ret = src->error;
vbi_ringbuffer_flush(src);
tvafe_pr_info("%s: data error\n", __func__);
return ret;
}
while (vbi_ringbuffer_empty(src)) {
if ((non_blocking || vbi_nonblock_en) ||
(tvafe_clk_status == false) || (timeout++ > 50)) {
ret = -EWOULDBLOCK;
tvafe_pr_info("%s:nonblock|closed|timeout return.\n",
__func__);
return ret;
}
msleep(20);
}
if (tvafe_clk_status == false) {
ret = -EWOULDBLOCK;
vbi_ringbuffer_flush(src);
if (vbi_dbg_en)
tvafe_pr_info("[vbi..] %s: tvafe is closed.pread = %d;pwrite = %d\n",
__func__, src->pread, src->pwrite);
return ret;
}
if (src->error) {
ret = src->error;
vbi_ringbuffer_flush(src);
if (vbi_dbg_en)
tvafe_pr_info("%s: read buffer error\n", __func__);
return ret;
}
if (vbi_dbg_en)
tvafe_pr_info("%s:src->pread = %d;src->pwrite = %d\n",
__func__, src->pread, src->pwrite);
avail = vbi_ringbuffer_avail(src);
if (avail > count)
avail = count;
ret = vbi_ringbuffer_read_user(src, buf, avail);
if (ret < 0) {
tvafe_pr_info("%s: vbi_ringbuffer_read_user error\n",
__func__);
}
if (vbi_dbg_en)
tvafe_pr_info("%s: counter:%Zx, return:%Zx\n",
__func__, count, ret);
return ret;
}
static ssize_t vbi_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
struct vbi_dev_s *vbi_dev = file->private_data;
struct vbi_slicer_s *vbi_slicer = vbi_dev->slicer;
int ret = 0;
if (mutex_lock_interruptible(&vbi_slicer->mutex)) {
tvafe_pr_info("%s: slicer mutex error\n", __func__);
return -ERESTARTSYS;
}
ret = vbi_buffer_read(&vbi_slicer->buffer,
file->f_flags & O_NONBLOCK,
buf, count, ppos);
mutex_unlock(&vbi_slicer->mutex);
return ret;
}
static int vbi_open(struct inode *inode, struct file *file)
{
struct vbi_dev_s *vbi_dev;
int ret = 0;
tvafe_pr_info("%s: open start.\n", __func__);
vbi_dev = container_of(inode->i_cdev, struct vbi_dev_s, cdev);
file->private_data = vbi_dev;
if (mutex_lock_interruptible(&vbi_dev->mutex)) {
tvafe_pr_info("%s: dev mutex_lock_interruptible error\n",
__func__);
return -ERESTARTSYS;
}
tasklet_enable(&vbi_dev->tsklt_slicer);
vbi_ringbuffer_init(&vbi_dev->slicer->buffer, NULL,
VBI_DEFAULT_BUFFER_PACKAGE_NUM);
vbi_dev->slicer->type = VBI_TYPE_NULL;
vbi_slicer_state_set(vbi_dev, VBI_STATE_ALLOCATED);
/*disable data capture function*/
vbi_dev->tasklet_enable = false;
vbi_dev->vbi_start = false;
/* request irq */
ret = request_irq(vbi_dev->vs_irq, vbi_isr, IRQF_SHARED,
vbi_dev->irq_name, (void *)vbi_dev);
vbi_dev->irq_free_status = 1;
if (ret < 0)
tvafe_pr_err("%s: request_irq fail\n", __func__);
mutex_unlock(&vbi_dev->mutex);
tvafe_pr_info("%s: open device ok.\n", __func__);
return 0;
}
static int vbi_release(struct inode *inode, struct file *file)
{
struct vbi_dev_s *vbi_dev = file->private_data;
struct vbi_slicer_s *vbi_slicer = vbi_dev->slicer;
int ret = 0;
vbi_dev->tasklet_enable = false;
vbi_dev->vbi_start = false; /*disable data capture function*/
tasklet_disable(&vbi_dev->tsklt_slicer);
ret = vbi_slicer_free(vbi_dev, vbi_slicer);
/* free irq */
if (vbi_dev->irq_free_status == 1)
free_irq(vbi_dev->vs_irq, (void *)vbi_dev);
vbi_dev->irq_free_status = 0;
vcnt = 1;
if (tvafe_clk_status) {
/* vbi reset release, vbi agent enable */
/*W_VBI_APB_REG(ACD_REG_22, 0x06080000);*/
W_VBI_APB_REG(CVD2_VBI_FRAME_CODE_CTL, 0x10);
}
tvafe_pr_info("[vbi..]%s: device release OK.\n", __func__);
return ret;
}
static long vbi_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
int ret = 0;
void __user *argp = (void __user *)arg;
unsigned int buffer_size_t;
struct vbi_dev_s *vbi_dev = file->private_data;
struct vbi_slicer_s *vbi_slicer = vbi_dev->slicer;
if (mutex_lock_interruptible(&vbi_dev->mutex))
return -ERESTARTSYS;
switch (cmd) {
case VBI_IOC_START:
if (mutex_lock_interruptible(&vbi_slicer->mutex)) {
mutex_unlock(&vbi_dev->mutex);
tvafe_pr_err("%s: slicer mutex error\n", __func__);
return -ERESTARTSYS;
}
if (tvafe_clk_status)
vbi_hw_init(vbi_dev);
else {
tvafe_pr_err("tvafe not opend.ioctl start err\n");
ret = -EINVAL;
mutex_unlock(&vbi_slicer->mutex);
break;
}
if (vbi_slicer->state < VBI_STATE_SET)
ret = -EINVAL;
else
ret = vbi_slicer_start(vbi_dev);
if (ret) {
pr_err("[vbi..] tvafe not opend.ioctl start err\n");
mutex_unlock(&vbi_slicer->mutex);
break;
}
/* enable data capture function */
vbi_dev->vs_delay = VBI_VS_DELAY;
vbi_dev->vbi_start = true;
vbi_dev->tasklet_enable = true;
mutex_unlock(&vbi_slicer->mutex);
mdelay(VBI_START_DELAY);
tvafe_pr_info("[vbi..] %s: start slicer state:%d\n",
__func__, vbi_slicer->state);
break;
case VBI_IOC_STOP:
tvafe_pr_info("[vbi..] %s: stop vbi function\n", __func__);
if (mutex_lock_interruptible(&vbi_slicer->mutex)) {
mutex_unlock(&vbi_dev->mutex);
tvafe_pr_err("%s: slicer mutex error\n", __func__);
return -ERESTARTSYS;
}
/* disable data capture function */
vbi_dev->tasklet_enable = false;
vbi_dev->vbi_start = false;
init_cc_data_flag = 0;
ret = vbi_slicer_stop(vbi_slicer);
if (tvafe_clk_status) {
/* manuel reset vbi */
/*W_VBI_APB_REG(ACD_REG_22, 0x82080000);*/
/* vbi reset release, vbi agent enable*/
/*W_VBI_APB_REG(ACD_REG_22, 0x06080000);*/
/*WAPB_REG(CVD2_VBI_CC_START, 0x00000054);*/
W_VBI_APB_REG(CVD2_VBI_FRAME_CODE_CTL, 0x10);
}
mutex_unlock(&vbi_slicer->mutex);
tvafe_pr_info("%s: stop slicer state:%d\n",
__func__, vbi_slicer->state);
break;
case VBI_IOC_SET_TYPE:
if (!tvafe_clk_status) {
mutex_unlock(&vbi_dev->mutex);
tvafe_pr_info("[vbi..] %s: afe not open,SET_TYPE return\n",
__func__);
return -ERESTARTSYS;
}
tvafe_pr_info("[vbi..] %s: VBI_IOC_SET_TYPE\n", __func__);
if (mutex_lock_interruptible(&vbi_slicer->mutex)) {
mutex_unlock(&vbi_dev->mutex);
tvafe_pr_info("%s: slicer mutex error\n", __func__);
return -ERESTARTSYS;
}
if (copy_from_user(&vbi_slicer->type, argp, sizeof(int))) {
tvafe_pr_info("[vbi..] %s: copy err\n", __func__);
ret = -EFAULT;
mutex_unlock(&vbi_slicer->mutex);
break;
}
if (tvafe_clk_status) {
ret = vbi_slicer_set(vbi_dev, vbi_slicer);
} else {
ret = -EFAULT;
tvafe_pr_err("[vbi..] %s: tvafeclose, no vbi_slicer_set\n",
__func__);
}
mutex_unlock(&vbi_slicer->mutex);
tvafe_pr_info("%s: set slicer type to %d ,state:%d\n",
__func__, vbi_slicer->type, vbi_slicer->state);
break;
case VBI_IOC_S_BUF_SIZE:
if (mutex_lock_interruptible(&vbi_slicer->mutex)) {
mutex_unlock(&vbi_dev->mutex);
tvafe_pr_info("%s: slicer mutex error\n", __func__);
return -ERESTARTSYS;
}
if (copy_from_user(&buffer_size_t, argp, sizeof(int))) {
ret = -EFAULT;
mutex_unlock(&vbi_slicer->mutex);
break;
}
ret = vbi_set_buffer_size(vbi_dev, buffer_size_t);
mutex_unlock(&vbi_slicer->mutex);
tvafe_pr_info("%s: set buf size to %d ,state:%d\n",
__func__, vbi_slicer->buffer.size, vbi_slicer->state);
break;
default:
ret = -EINVAL;
break;
}
mutex_unlock(&vbi_dev->mutex);
return ret;
}
#ifdef CONFIG_COMPAT
static long vbi_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned long ret;
arg = (unsigned long)compat_ptr(arg);
ret = vbi_ioctl(file, cmd, arg);
return ret;
}
#endif
static unsigned int vbi_poll(struct file *file, poll_table *wait)
{
struct vbi_dev_s *devp = file->private_data;
struct vbi_slicer_s *vbi_slicer = devp->slicer;
unsigned int mask = 0;
if (!vbi_slicer) {
tvafe_pr_info("%s: slicer null\n", __func__);
return -EINVAL;
}
poll_wait(file, &vbi_slicer->buffer.queue, wait);
if (vbi_slicer->state != VBI_STATE_GO &&
vbi_slicer->state != VBI_STATE_DONE &&
vbi_slicer->state != VBI_STATE_TIMEDOUT)
return 0;
if (vbi_slicer->buffer.error)
mask |= (POLLIN | POLLRDNORM | POLLPRI | POLLERR);
if (!vbi_ringbuffer_empty(&vbi_slicer->buffer))
mask |= (POLLIN | POLLRDNORM | POLLPRI);
return mask;
}
/* File operations structure. Defined in linux/fs.h */
static const struct file_operations vbi_fops = {
.owner = THIS_MODULE, /* Owner */
.open = vbi_open, /* Open method */
.release = vbi_release, /* Release method */
.unlocked_ioctl = vbi_ioctl, /* Ioctl method */
#ifdef CONFIG_COMPAT
.compat_ioctl = vbi_compat_ioctl,
#endif
.read = vbi_read,
.poll = vbi_poll,
/* ... */
};
static struct resource vbi_memobj;
static void vbi_parse_param(char *buf_orig, char **parm)
{
char *ps, *token;
char delim1[3] = " ";
char delim2[2] = "\n";
unsigned int n = 0;
ps = buf_orig;
strcat(delim1, delim2);
while (1) {
token = strsep(&ps, delim1);
if (token == NULL)
break;
if (*token == '\0')
continue;
parm[n++] = token;
}
}
static void vbi_dump_mem(char *path, struct vbi_dev_s *devp)
{
struct file *filp = NULL;
loff_t pos = 0;
void *buf = NULL;
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open(path, O_RDWR|O_CREAT, 0666);
if (IS_ERR(filp)) {
tvafe_pr_info("create %s error.\n", path);
return;
}
buf = phys_to_virt(devp->mem_start);
if (buf == NULL) {
tvafe_pr_info("buf is null!!!.\n");
return;
}
vfs_write(filp, buf, devp->mem_size, &pos);
tvafe_pr_info("write buffer addr:0x%p size: %2u to %s.\n",
buf, devp->mem_size, path);
vfs_fsync(filp, 0);
filp_close(filp, NULL);
set_fs(old_fs);
}
static void vbi_dump_buf(char *path, struct vbi_dev_s *devp)
{
struct file *filp = NULL;
loff_t pos = 0;
void *buf = NULL;
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
filp = filp_open(path, O_RDWR|O_CREAT, 0666);
if (IS_ERR(filp)) {
tvafe_pr_info("create %s error.\n", path);
return;
}
buf = (void *)devp->slicer->buffer.data;
if (buf == NULL) {
tvafe_pr_info("buf is null!!!.\n");
return;
}
vfs_write(filp, buf, devp->slicer->buffer.size, &pos);
tvafe_pr_info("write buffer addr:0x%p size: %2u to %s.\n",
buf, devp->slicer->buffer.size, path);
vfs_fsync(filp, 0);
filp_close(filp, NULL);
set_fs(old_fs);
}
static const char *vbi_debug_usage_str = {
"Usage:\n"
"echo dumpmem /udisk-path/***/xxx.txt > /sys/class/vbi/vbi/debug; dump vbi mem\n"
"echo dumpbuf /udisk-path/***/xxx.txt > /sys/class/vbi/vbi/debug; dump vbi mem\n"
"echo status > /sys/class/vbi/vbi/debug; dump vbi status\n"
"echo enable_tasklet > /sys/class/vbi/vbi/debug; enable vbi tasklet\n"
"echo data_wmode val(d) > /sys/class/vbi/vbi/debug; set vbi data write mode\n"
"echo start > /sys/class/vbi/vbi/debug; start vbi\n"
"echo stop > /sys/class/vbi/vbi/debug; stop vbi\n"
"echo set_size val(d) > /sys/class/vbi/vbi/debug; set vbi buf size\n"
"echo set_type val(x) > /sys/class/vbi/vbi/debug; set vbi type\n"
"echo open > /sys/class/vbi/vbi/debug; open vbi device\n"
"echo release > /sys/class/vbi/vbi/debug; release vbi device\n"
};
static ssize_t vbi_show(struct device *dev,
struct device_attribute *attr, char *buff)
{
return sprintf(buff, "%s\n", vbi_debug_usage_str);
}
static ssize_t vbi_store(struct device *dev,
struct device_attribute *attr, const char *buff, size_t len)
{
char *buf_orig;
char *parm[6] = {NULL};
struct vbi_dev_s *devp = dev_get_drvdata(dev);
struct vbi_slicer_s *vbi_slicer;
struct vbi_ringbuffer_s *vbi_buffer;
unsigned int val;
int ret = 0;
if (!buff || !devp)
return len;
vbi_slicer = devp->slicer;
vbi_buffer = &(vbi_slicer->buffer);
buf_orig = kstrdup(buff, GFP_KERNEL);
vbi_parse_param(buf_orig, (char **)&parm);
if (!strncmp(parm[0], "dumpmem", strlen("dumpmem"))) {
vbi_dump_mem(parm[1], devp);
tvafe_pr_info("dump mem done!!\n");
} else if (!strncmp(parm[0], "dumpbuf", strlen("dumpbuf"))) {
vbi_dump_buf(parm[1], devp);
tvafe_pr_info("dump buf done!!\n");
} else if (!strncmp(parm[0], "status", strlen("status"))) {
tvafe_pr_info("vcnt:0x%x\n", vcnt);
tvafe_pr_info("mem_start:0x%x,mem_size:0x%x\n",
devp->mem_start, devp->mem_size);
tvafe_pr_info("vbi_start:%d,vbi_data_type:0x%x,vbi_start_code:0x%x,tasklet_enable:%d\n",
devp->vbi_start, devp->vbi_data_type,
devp->vbi_start_code, devp->tasklet_enable);
tvafe_pr_info("vbi_dto_cc:0x%x,vbi_dto_tt:0x%x\n",
devp->vbi_dto_cc, devp->vbi_dto_tt);
tvafe_pr_info("vbi_dto_wss:0x%x,vbi_dto_vps:0x%x\n",
devp->vbi_dto_wss, devp->vbi_dto_vps);
tvafe_pr_info("mem_start:0x%p,pac_addr_start:0x%p,pac_addr_end:0x%p\n",
devp->pac_addr, devp->pac_addr_start,
devp->pac_addr_end);
if (vbi_slicer)
tvafe_pr_info("vbi_slicer:type:%d,state:%d\n",
vbi_slicer->type, vbi_slicer->state);
if (vbi_buffer)
tvafe_pr_info("vbi_buffer:size:%d,data_num:%d,pread:%d,pwrite:%d,data_wmode:%d\n",
vbi_buffer->size, vbi_buffer->data_num,
vbi_buffer->pread, vbi_buffer->pwrite,
vbi_buffer->data_wmode);
tvafe_pr_info("dump satus done!!\n");
} else if (!strncmp(parm[0], "vbi_pr_en",
strlen("vbi_pr_en"))) {
if (kstrtouint(parm[1], 10, &val) < 0)
return -EINVAL;
vbi_pr_en = val;
tvafe_pr_info("vbi_pr_en:%d\n", vbi_pr_en);
} else if (!strncmp(parm[0], "enable_tasklet",
strlen("enable_tasklet"))) {
if (kstrtouint(parm[1], 10, &val) < 0)
return -EINVAL;
devp->tasklet_enable = val;
tvafe_pr_info("tasklet_enable:%d\n", devp->tasklet_enable);
} else if (!strncmp(parm[0], "data_wmode",
strlen("data_wmode"))) {
if (kstrtouint(parm[1], 10, &val) < 0)
return -EINVAL;
vbi_buffer->data_wmode = val;
tvafe_pr_info("data_wmode:%d\n", vbi_buffer->data_wmode);
} else if (!strncmp(parm[0], "start", strlen("start"))) {
vbi_hw_init(devp);
vbi_slicer_start(devp);
/* enable data capture function */
devp->tasklet_enable = true;
devp->vbi_start = true;
devp->vs_delay = VBI_VS_DELAY;
tvafe_pr_info("start done!!!\n");
} else if (!strncmp(parm[0], "stop", strlen("stop"))) {
vbi_slicer_stop(vbi_slicer);
/* disable data capture function */
devp->tasklet_enable = false;
devp->vbi_start = false;
/* manuel reset vbi */
/* vbi reset release, vbi agent enable*/
W_VBI_APB_REG(ACD_REG_22, 0x06080000);
W_VBI_APB_REG(CVD2_VBI_FRAME_CODE_CTL, 0x10);
tvafe_pr_info(" disable vbi function\n");
tvafe_pr_info("stop done!!!\n");
} else if (!strncmp(parm[0], "set_size", strlen("set_size"))) {
if (kstrtouint(parm[1], 10, &val) < 0)
return -EINVAL;
vbi_set_buffer_size(devp, val);
tvafe_pr_info(" set buf size to %d\n",
vbi_slicer->buffer.size);
} else if (!strncmp(parm[0], "set_type", strlen("set_type"))) {
if (kstrtouint(parm[1], 16, &val) < 0)
return -EINVAL;
vbi_slicer->type = val;
vbi_slicer_set(devp, vbi_slicer);
tvafe_pr_info(" set slicer type to %d\n",
vbi_slicer->type);
} else if (!strncmp(parm[0], "open", strlen("open"))) {
vbi_ringbuffer_init(vbi_buffer, NULL,
VBI_DEFAULT_BUFFER_PACKAGE_NUM);
devp->slicer->type = VBI_TYPE_NULL;
vbi_slicer_state_set(devp, VBI_STATE_ALLOCATED);
/*disable data capture function*/
devp->tasklet_enable = false;
devp->vbi_start = false;
/* request irq */
ret = request_irq(devp->vs_irq, vbi_isr, IRQF_SHARED,
devp->irq_name, (void *)devp);
devp->irq_free_status = 1;
if (ret < 0)
tvafe_pr_err("request_irq fail\n");
tvafe_pr_info(" open ok.\n");
} else if (!strncmp(parm[0], "release", strlen("release"))) {
ret = vbi_slicer_free(devp, vbi_slicer);
devp->tasklet_enable = false;
devp->vbi_start = false; /*disable data capture function*/
/* free irq */
if (devp->irq_free_status == 1)
free_irq(devp->vs_irq, (void *)devp);
devp->irq_free_status = 0;
/* vbi reset release, vbi agent enable */
W_VBI_APB_REG(ACD_REG_22, 0x06080000);
W_VBI_APB_REG(CVD2_VBI_FRAME_CODE_CTL, 0x10);
tvafe_pr_info("[vbi..]device release OK.\n");
} else {
tvafe_pr_info("[vbi..]unsupport cmd!!!\n");
}
return len;
}
static DEVICE_ATTR(debug, 0644, vbi_show, vbi_store);
static int vbi_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
struct vbi_dev_s *vbi_dev;
dma_addr_t vbi_dma_addr;
/* allocate memory for the per-device structure */
vbi_dev = kzalloc(sizeof(struct vbi_dev_s), GFP_KERNEL);
if (!vbi_dev) {
ret = -ENOMEM;
goto fail_kzalloc_mem;
}
memset(vbi_dev, 0, sizeof(struct vbi_dev_s));
vbi_mem_start = 0;
/* connect the file operations with cdev */
cdev_init(&vbi_dev->cdev, &vbi_fops);
vbi_dev->cdev.owner = THIS_MODULE;
/* connect the major/minor number to the cdev */
ret = cdev_add(&vbi_dev->cdev, vbi_id, 1);
if (ret) {
tvafe_pr_err(": failed to add device\n");
goto fail_add_cdev;
}
/* create /dev nodes */
vbi_dev->dev = device_create(vbi_clsp, &pdev->dev,
MKDEV(MAJOR(vbi_id), 0), NULL, "%s", VBI_NAME);
if (IS_ERR(vbi_dev->dev)) {
tvafe_pr_err(": failed to create device node\n");
ret = PTR_ERR(vbi_dev->dev);
goto fail_create_device;
}
/*create sysfs attribute files*/
ret = device_create_file(vbi_dev->dev, &dev_attr_debug);
if (ret < 0) {
tvafe_pr_err("tvafe: fail to create dbg attribute file\n");
goto fail_create_dbg_file;
}
/*vbi memory alloc*/
vbi_dev->mem_size = DECODER_VBI_SIZE;
vbi_dev->pac_addr_start = dma_alloc_coherent(&pdev->dev,
vbi_dev->mem_size, &vbi_dma_addr, GFP_KERNEL);
vbi_dev->mem_start = (unsigned int)vbi_dma_addr;
if (vbi_dev->pac_addr_start == NULL) {
tvafe_pr_err(": dma_alloc_coherent failed!!!\n");
goto fail_alloc_mem;
}
tvafe_pr_info("vbi: dma_alloc phy start_addr is:0x%x, size is:0x%x\n",
vbi_dev->mem_start, vbi_dev->mem_size);
memset(vbi_dev->pac_addr_start, 0, vbi_dev->mem_size);
vbi_dev->mem_size = vbi_dev->mem_size/2;
vbi_dev->mem_size >>= 4;
vbi_dev->mem_size <<= 4;
vbi_mem_start = vbi_dev->mem_start;
vbi_dev->pac_addr_end = vbi_dev->pac_addr_start + vbi_dev->mem_size - 1;
tvafe_pr_info(": vbi_dev->pac_addr_start=0x%p, end:0x%p, size:0x%x\n",
vbi_dev->pac_addr_start, vbi_dev->pac_addr_end,
vbi_dev->mem_size);
vbi_dev->pac_addr = vbi_dev->pac_addr_start;
mutex_init(&vbi_dev->mutex);
spin_lock_init(&vbi_dev->lock);
spin_lock_init(&vbi_dev->vbi_isr_lock);
/* init drv data */
dev_set_drvdata(vbi_dev->dev, vbi_dev);
platform_set_drvdata(pdev, vbi_dev);
vbi_dev->tasklet_enable = false;
vbi_dev->vbi_start = false;
/* Initialize tasklet */
tasklet_init(&vbi_dev->tsklt_slicer, vbi_slicer_task,
(unsigned long)vbi_dev);
tasklet_disable(&vbi_dev->tsklt_slicer);
vbi_dev->vs_delay = VBI_VS_DELAY;
vbi_dev->slicer = vmalloc(sizeof(struct vbi_slicer_s));
if (!vbi_dev->slicer) {
ret = -ENOMEM;
goto fail_alloc_mem;
}
mutex_init(&vbi_dev->slicer->mutex);
init_waitqueue_head(&vbi_dev->slicer->buffer.queue);
spin_lock_init(&(vbi_dev->slicer->buffer.lock));
vbi_dev->slicer->buffer.data = NULL;
vbi_dev->slicer->state = VBI_STATE_FREE;
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
tvafe_pr_err("%s: can't get irq resource\n", __func__);
ret = -ENXIO;
goto fail_get_resource_irq;
}
vbi_dev->vs_irq = res->start;
snprintf(vbi_dev->irq_name, sizeof(vbi_dev->irq_name),
"vbi-irq");
tvafe_pr_info("vbi irq: %d\n", vbi_dev->vs_irq);
tvafe_pr_info(": driver probe ok\n");
return 0;
fail_get_resource_irq:
fail_alloc_mem:
tvafe_pr_err(": vmalloc error!!!\n");
fail_create_dbg_file:
device_destroy(vbi_clsp, MKDEV(MAJOR(vbi_id), 0));
fail_create_device:
cdev_del(&vbi_dev->cdev);
fail_add_cdev:
kfree(vbi_dev);
fail_kzalloc_mem:
tvafe_pr_err(": failed to allocate memory for vbi device\n");
return ret;
}
static int vbi_remove(struct platform_device *pdev)
{
struct vbi_dev_s *vbi_dev;
vbi_dev = platform_get_drvdata(pdev);
mutex_destroy(&vbi_dev->slicer->mutex);
mutex_destroy(&vbi_dev->mutex);
tasklet_kill(&vbi_dev->tsklt_slicer);
if (vbi_dev->pac_addr_start)
dma_free_coherent(vbi_dev->dev, vbi_dev->mem_size,
vbi_dev->pac_addr_start,
(dma_addr_t)&vbi_dev->mem_start);
vfree(vbi_dev->slicer);
device_destroy(vbi_clsp, MKDEV(MAJOR(vbi_id), 0));
cdev_del(&vbi_dev->cdev);
kfree(vbi_dev);
class_destroy(vbi_clsp);
unregister_chrdev_region(vbi_id, 0);
tvafe_pr_info(": driver removed ok.\n");
return 0;
}
#ifdef CONFIG_PM
static int vbi_drv_suspend(struct platform_device *pdev, pm_message_t state)
{
return 0;
}
static int vbi_drv_resume(struct platform_device *pdev)
{
return 0;
}
#endif
static void vbi_drv_shutdown(struct platform_device *pdev)
{
struct vbi_dev_s *vbi_dev;
vbi_dev = platform_get_drvdata(pdev);
vbi_dev->tasklet_enable = false;
vbi_dev->vbi_start = false;
tasklet_kill(&vbi_dev->tsklt_slicer);
if (vbi_dev->irq_free_status == 1)
free_irq(vbi_dev->vs_irq, (void *)vbi_dev);
vbi_dev->irq_free_status = 0;
tvafe_pr_info("%s ok.\n", __func__);
}
static const struct of_device_id vbi_dt_match[] = {
{
.compatible = "amlogic, vbi",
},
{},
};
static struct platform_driver vbi_driver = {
.probe = vbi_probe,
.remove = vbi_remove,
#ifdef CONFIG_PM
.suspend = vbi_drv_suspend,
.resume = vbi_drv_resume,
#endif
.shutdown = vbi_drv_shutdown,
.driver = {
.name = VBI_DRIVER_NAME,
.of_match_table = vbi_dt_match,
}
};
static int __init vbi_init(void)
{
int ret = 0;
ret = alloc_chrdev_region(&vbi_id, 0, 1, VBI_NAME);
if (ret < 0) {
tvafe_pr_err(": failed to allocate major number\n");
goto fail_alloc_cdev_region;
}
vbi_clsp = class_create(THIS_MODULE, VBI_NAME);
if (IS_ERR(vbi_clsp)) {
tvafe_pr_err(": can't get vbi_clsp\n");
goto fail_class_create;
}
ret = platform_driver_register(&vbi_driver);
if (ret != 0) {
tvafe_pr_err("failed to register vbi module, error %d\n",
ret);
goto fail_pdrv_register;
return -ENODEV;
}
tvafe_pr_info("vbi: vbi_init.\n");
return 0;
fail_pdrv_register:
class_destroy(vbi_clsp);
fail_class_create:
unregister_chrdev_region(vbi_id, 1);
fail_alloc_cdev_region:
return ret;
}
static void __exit vbi_exit(void)
{
platform_driver_unregister(&vbi_driver);
}
static int vbi_mem_device_init(struct reserved_mem *rmem,
struct device *dev)
{
s32 ret = 0;
struct resource *res = NULL;
if (!rmem) {
tvafe_pr_info("Can't get reverse mem!\n");
ret = -EFAULT;
return ret;
}
res = &vbi_memobj;
res->start = rmem->base;
res->end = rmem->base + rmem->size - 1;
tvafe_pr_info("init vbi memsource 0x%lx->0x%lx\n",
(unsigned long)res->start, (unsigned long)res->end);
return 0;
}
static const struct reserved_mem_ops rmem_vbi_ops = {
.device_init = vbi_mem_device_init,
};
static int __init vbi_mem_setup(struct reserved_mem *rmem)
{
rmem->ops = &rmem_vbi_ops;
tvafe_pr_info("vbi share mem setup\n");
return 0;
}
module_init(vbi_init);
module_exit(vbi_exit);
RESERVEDMEM_OF_DECLARE(vbi, "amlogic, vbi-mem",
vbi_mem_setup);
MODULE_DESCRIPTION("AMLOGIC vbi driver");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("frank <frank.zhao@amlogic.com>");