blob: 0feac45d341bf122a3dc6d004021ca627ca1fbef [file] [log] [blame]
/*
* drivers/amlogic/media/vin/tvin/bt656/bt656_601_in.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/kernel.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/etherdevice.h>
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/major.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/of_irq.h>
/* #include <mach/am_regs.h> */
/* #include <mach/mod_gate.h> */
#include "../tvin_global.h"
#include "../tvin_format_table.h"
#include "../tvin_frontend.h"
#include "bt656_601_in.h"
#define BT656_DEV_NAME "amvdec_656in"
#define BT656_DRV_NAME "amvdec_656in"
#define BT656_CLS_NAME "amvdec_656in"
#define BT656_MOD_NAME "amvdec_656in"
/* #define HANDLE_BT656IN_IRQ */
#define BT656_MAX_DEVS 2
/* #define BT656IN_ANCI_DATA_SIZE 0x4000 */
#define BT656_VER "2017/08/23"
/* Per-device (per-bank) structure */
/* static struct struct am656in_dev_s am656in_dev_; */
static dev_t am656in_devno;
static struct class *am656in_clsp;
static unsigned char hw_cnt;
static struct am656in_dev_s *am656in_devp[BT656_MAX_DEVS];
#ifdef HANDLE_BT656IN_IRQ
static const char bt656in_dec_id[] = "bt656in-dev";
#endif
static struct mutex bt656_mutex;
/* bt656 debug function */
static ssize_t reg_store(struct device *dev,
struct device_attribute *attr,
const char *buff, size_t count)
{
unsigned int argn = 0, addr = 0, value = 0, end = 0;
char *p, *para, *buf_work, cmd = 0;
char *argv[3];
long val;
struct am656in_dev_s *devp = dev_get_drvdata(dev);
if ((devp->dec_status & TVIN_AM656_RUNNING) == 0) {
BT656ERR("bt656[%d] is not running.\n", devp->index);
return count;
}
buf_work = kstrdup(buff, GFP_KERNEL);
p = buf_work;
for (argn = 0; argn < 3; argn++) {
para = strsep(&p, " ");
if (para == NULL)
break;
argv[argn] = para;
}
if (argn < 1 || argn > 3)
goto reg_store_exit;
cmd = argv[0][0];
switch (cmd) {
case 'r':
case 'R':
if (argn < 2) {
pr_err("syntax error.\n");
} else{
/* addr = simple_strtol(argv[1], NULL, 16); */
if (kstrtol(argv[1], 16, &val) < 0)
break;
addr = val;
value = bt656_rd(devp->index, addr);
pr_info("reg[%d:0x%2x]=0x%08x\n",
devp->index, addr, value);
}
break;
case 'w':
case 'W':
if (argn < 3) {
pr_err("syntax error.\n");
} else{
/* value = simple_strtol(argv[1], NULL, 16); */
if (kstrtol(argv[1], 16, &val) < 0)
break;
value = val;
/* addr = simple_strtol(argv[2], NULL, 16); */
if (kstrtol(argv[2], 16, &val) < 0)
break;
addr = val;
bt656_wr(devp->index, addr, value);
pr_info("Write reg[%d:0x%2x]=0x%08x\n",
devp->index, addr,
bt656_rd(devp->index, addr));
}
break;
case 'd':
case 'D':
/* if (argn < 3) { */
/* pr_err("syntax error.\n"); */
/* } else { */
/* addr = simple_strtol(argv[1], NULL, 16); */
/* if (kstrtol(argv[1], 16, &val) < 0) */
/* break; */
/* addr = val; */
/* end = simple_strtol(argv[2], NULL, 16); */
/* if (kstrtol(argv[2], 16, &val) < 0) */
/* break; */
/* end = val; */
addr = 0;
end = 0x1f;
for (; addr <= end; addr++)
pr_info("reg[%d:0x%2x]=0x%08x\n",
devp->index, addr,
bt656_rd(devp->index, addr));
/* } */
break;
default:
pr_err("not support.\n");
break;
}
reg_store_exit:
kfree(buf_work);
return count;
}
static ssize_t reg_show(struct device *dev,
struct device_attribute *attr, char *buff)
{
ssize_t len = 0;
len += sprintf(buff+len, "Usage:\n");
len += sprintf(buff+len,
"\techo [read|write <data>] addr > reg;\n");
len += sprintf(buff+len,
"\techo dump > reg; Dump regs.\n");
len += sprintf(buff+len, "Address format:\n");
len += sprintf(buff+len, "\taddr : 0xXXXX, 8 bits register address\n");
return len;
}
static DEVICE_ATTR(reg, 0644, reg_show, reg_store);
static void init_656in_dec_parameter(struct am656in_dev_s *devp)
{
enum tvin_sig_fmt_e fmt;
const struct tvin_format_s *fmt_info_p;
fmt = devp->para.fmt;
fmt_info_p = tvin_get_fmt_info(fmt);
if (!fmt_info_p) {
BT656ERR("bt656[%d] %s:invaild fmt %d.\n",
devp->index, __func__, fmt);
return;
}
if (fmt < TVIN_SIG_FMT_MAX) {
devp->para.v_active = fmt_info_p->v_active;
devp->para.h_active = fmt_info_p->h_active;
devp->para.hsync_phase = 0;
devp->para.vsync_phase = 0;
devp->para.hs_bp = 0;
devp->para.vs_bp = 0;
}
}
static void init_656in_dec_hdmi_parameter(struct am656in_dev_s *devp)
{
enum tvin_sig_fmt_e fmt;
const struct tvin_format_s *fmt_info_p;
fmt = devp->para.fmt;
fmt_info_p = tvin_get_fmt_info(fmt);
if (!fmt_info_p) {
BT656ERR("bt656[%d] %s:invaild fmt %d.\n",
devp->index, __func__, fmt);
return;
}
if (fmt < TVIN_SIG_FMT_MAX) {
devp->para.v_active = fmt_info_p->v_active;
devp->para.h_active = fmt_info_p->h_active;
/*devp->para.hsync_phase = fmt_info_p->v_active;
*devp->para.vsync_phase = fmt_info_p->v_active;
*/
devp->para.hs_bp = fmt_info_p->hs_bp;
devp->para.vs_bp = fmt_info_p->vs_bp;
}
}
static void reset_bt656in_module(struct am656in_dev_s *devp)
{
int temp_data = 0;
unsigned int offset = devp->index;
temp_data = bt656_rd(offset, BT_CTRL);
temp_data &= ~(1 << BT_EN_BIT);
bt656_wr(offset, BT_CTRL, temp_data); /* disable BT656 input */
/* reset BT656in module. */
temp_data = bt656_rd(offset, BT_CTRL);
temp_data |= (1 << BT_SOFT_RESET);
bt656_wr(offset, BT_CTRL, temp_data);
temp_data = bt656_rd(offset, BT_CTRL);
temp_data &= ~(1 << BT_SOFT_RESET);
bt656_wr(offset, BT_CTRL, temp_data);
}
/*
* NTSC or PAL input(interlace mode): CLOCK + D0~D7(with SAV + EAV )
*/
static void reinit_bt656in_dec(struct am656in_dev_s *devp)
{
unsigned int offset = devp->index;
reset_bt656in_module(devp);
/* field 0/1 start lcnt: default value */
bt656_wr(offset, BT_FIELDSADR, (4 << 16) | 4);
/* configuration the BT PORT control */
/* For standaREAD_CBUS_REG bt656 in stream,
* there's no HSYNC VSYNC pins.
*/
/* So we don't need to configure the port. */
/* data itself is 8 bits. */
bt656_wr(offset, BT_PORT_CTRL, 1 << BT_D8B);
bt656_wr(offset, BT_SWAP_CTRL, (4 << 0) | /* POS_Y1_IN */
(5 << 4) | /* POS_Cr0_IN */
(6 << 8) | /* POS_Y0_IN */
(7 << 12)); /* POS_CB0_IN */
bt656_wr(offset, BT_LINECTRL, 0);
/* there is no use anci in m2 */
/* ANCI is the field blanking data, like close caption.
* If it connected to digital camara interface,
* the jpeg bitstream also use this ANCI FIFO.
*/
/* bt656_wr(BT_ANCISADR, devp->mem_start); */
/* bt656_wr(BT_ANCIEADR, 0); //devp->mem_start + devp->mem_size); */
bt656_wr(offset, BT_AFIFO_CTRL, (1 << 31) |
(1 << 6) | /* fill _en; */
(1 << 3)); /* urgent */
bt656_wr(offset, BT_INT_CTRL, /* (1 << 5) | //ancififo done int. */
/* (1 << 4) | //SOF interrupt enable. */
/* (1 << 3) | //EOF interrupt enable. */
(1 << 1)); /* | //input overflow interrupt enable. */
/* (1 << 0)); //bt656 controller error interrupt enable. */
bt656_wr(offset, BT_ERR_CNT, (626 << 16) | (1760));
if (devp->para.fmt == TVIN_SIG_FMT_BT656IN_576I_50HZ) {
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBIEND, 22 | (22 << 16));
/* Line number of the first video start line in field 0/1. */
bt656_wr(offset, BT_VIDEOSTART, 23 | (23 << 16));
/* Line number of the last video line in field 1.
* added video end for avoid overflow.
*/
bt656_wr(offset, BT_VIDEOEND, 312 |
(312 << 16));
/* Update bt656 status register when end of frame. */
bt656_wr(offset, BT_CTRL, (0 << BT_UPDATE_ST_SEL) |
/* Repeated the color data when
* do 4:2:2 -> 4:4:4 data transfer.
*/
(1 << BT_COLOR_REPEAT) |
/* use haREAD_CBUS_REGware to check
* the PAL/NTSC format
* input format if it's
* standaREAD_CBUS_REG BT656 input format.
*/
(1 << BT_AUTO_FMT) |
/* BT656 standaREAD_CBUS_REG interface. */
(1 << BT_MODE_BIT) |
(1 << BT_EN_BIT) | /* enable BT moduale. */
/* timing reference is from bit stream. */
(1 << BT_REF_MODE_BIT) |
/* use external xclk27. */
(1 << BT_CLK27_SEL_BIT) |
(1 << BT_XCLK27_EN_BIT)); /* xclk27 is input. */
/* wr(VDIN_WR_V_START_END, 287 | */
/* (0 << 16)); */
} else {
/* if(am656in_dec_info.para.fmt == TVIN_SIG_FMT_BT656IN_480I)
* //input is NTSC
*/
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBIEND, 21 | (21 << 16));
/* Line number of the first video start line in field 0/1. */
bt656_wr(offset, BT_VIDEOSTART, 18 | (18 << 16));
/* Line number of the last video line in field 1.
* added video end for avoid overflow.
*/
bt656_wr(offset, BT_VIDEOEND, 257 |
(257 << 16));
/* Update bt656 status register when end of frame. */
bt656_wr(offset, BT_CTRL, (0 << BT_UPDATE_ST_SEL) |
/* Repeated the color data when
* do 4:2:2 -> 4:4:4 data transfer.
*/
(1 << BT_COLOR_REPEAT) |
/* use haREAD_CBUS_REGware to check the
* PAL/NTSC format input format
* if it's standaREAD_CBUS_REG
* BT656 input format.
*/
(1 << BT_AUTO_FMT) |
/* BT656 standaREAD_CBUS_REG interface. */
(1 << BT_MODE_BIT) |
(1 << BT_EN_BIT) | /* enable BT moduale. */
/* timing reference is from bit stream. */
(1 << BT_REF_MODE_BIT) |
(1 << BT_CLK27_SEL_BIT) | /* use external xclk27. */
(1 << BT_XCLK27_EN_BIT) | /* xclk27 is input. */
(1 << BT_FMT_MODE_BIT)); /* input format is NTSC */
/* wr(VDIN_WR_V_START_END, 239 | */
/* (0 << 16)); */
}
}
/* NTSC or PAL input(interlace mode): CLOCK + D0~D7 + HSYNC + VSYNC + FID */
static void reinit_bt601in_dec(struct am656in_dev_s *devp)
{
unsigned int offset = devp->index;
reset_bt656in_module(devp);
/* use external idq pin. */
bt656_wr(offset, BT_PORT_CTRL, (0 << BT_IDQ_EN) |
(1 << BT_IDQ_PHASE) |
/* FID came from HS VS. */
(1 << BT_FID_HSVS) |
(1 << BT_HSYNC_PHASE) |
(1 << BT_D8B) |
(4 << BT_FID_DELAY) |
(5 << BT_VSYNC_DELAY) |
(5 << BT_HSYNC_DELAY));
/* FID field check done point. */
bt656_wr(offset, BT_601_CTRL2, (10 << 16));
/* suppose the input bitstream format is Cb0 Y0 Cr0 Y1. */
bt656_wr(offset, BT_SWAP_CTRL, (4 << 0) |
(5 << 4) |
(6 << 8) |
(7 << 13));
bt656_wr(offset, BT_LINECTRL, (1 << 31) | /*software line ctrl enable*/
(1644 << 16) | /* 1440 + 204 */
220);
/* ANCI is the field blanking data, like close caption.
* If it connected to digital camara interface,
* the jpeg bitstream also use this ANCI FIFO.
*/
/* wr(BT_ANCISADR, devp->mem_start); */
/* wr(BT_ANCIEADR, 0);//devp->mem_start + devp->mem_size); */
bt656_wr(offset, BT_AFIFO_CTRL, (1 << 31) |
(1 << 6) | /* fill _en; */
(1 << 3)); /* urgent */
bt656_wr(offset, BT_INT_CTRL, /* (1 << 5) | //ancififo done int. */
/* (1 << 4) | //SOF interrupt enable. */
/* (1 << 3) | //EOF interrupt enable. */
(1 << 1)); /* | //input overflow interrupt enable. */
/* (1 << 0)); //bt656 controller error interrupt enable. */
bt656_wr(offset, BT_ERR_CNT, (626 << 16) | (2000));
/* otherwise there is always error flag, */
/* because the camera input use HREF ont HSYNC, */
/* there are some lines without HREF sometime */
bt656_wr(offset, BT_FIELDSADR, (1 << 16) | 1);/* field 0/1 start lcnt */
if (devp->para.fmt == TVIN_SIG_FMT_BT601IN_576I_50HZ) {
/* input is PAL */
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBIEND, 22 | (22 << 16));
/* Line number of the first video start line in field 0/1. */
bt656_wr(offset, BT_VIDEOSTART, 23 | (23 << 16));
/* Line number of the last video line in field 1.
* added video end for avoid overflow.
*/
bt656_wr(offset, BT_VIDEOEND, 312 |
(312 << 16));
/* BT656 standaREAD_CBUS_REG interface. */
bt656_wr(offset, BT_CTRL, (0 << BT_MODE_BIT) |
(1 << BT_AUTO_FMT) |
/* enable BT moduale. */
(1 << BT_EN_BIT) |
/* timing reference is from bit stream. */
(0 << BT_REF_MODE_BIT) |
(0 << BT_FMT_MODE_BIT) | /* PAL */
(1 << BT_SLICE_MODE_BIT) |
/* use external fid pin. */
(0 << BT_FID_EN_BIT) |
/* use external xclk27. */
(1 << BT_CLK27_SEL_BIT) |
/* xclk27 is input. */
(1 << BT_XCLK27_EN_BIT));
/* wr(VDIN_WR_V_START_END, 287 | */
/* (0 << 16)); */
} else {
/* if(am656in_dec_info.para.fmt == TVIN_SIG_FMT_BT601IN_480I)
* input is NTSC
*/
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBIEND, 21 | (21 << 16));
/* Line number of the first video start line in field 0/1. */
bt656_wr(offset, BT_VIDEOSTART, 18 | (18 << 16));
/* Line number of the last video line in field 1.
* added video end for avoid overflow.
*/
bt656_wr(offset, BT_VIDEOEND, 257 |
(257 << 16));
bt656_wr(offset, BT_CTRL, (0 << BT_MODE_BIT) |
(1 << BT_AUTO_FMT) |
/* enablem656in_star BT moduale. */
(1 << BT_EN_BIT) |
/* timing reference is from bit stream. */
(0 << BT_REF_MODE_BIT) |
(1 << BT_FMT_MODE_BIT) | /* NTSC */
(1 << BT_SLICE_MODE_BIT) |
/* use external fid pin. */
(0 << BT_FID_EN_BIT) |
/* use external xclk27. */
(1 << BT_CLK27_SEL_BIT) |
/* xclk27 is input. */
(1 << BT_XCLK27_EN_BIT));
/* bt656_wr(VDIN_WR_V_START_END, 239 | */
/* (0 << 16)); */
}
}
/* CAMERA input(progressive mode): CLOCK + D0~D7 + HREF + VSYNC */
static void reinit_camera_dec(struct am656in_dev_s *devp)
{
unsigned int offset = devp->index;
/* reset_bt656in_module(); */
unsigned int temp_data;
unsigned char hsync_enable = devp->para.hsync_phase;
unsigned char vsync_enable = devp->para.vsync_phase;
unsigned short hs_bp = devp->para.hs_bp;
unsigned short vs_bp = devp->para.vs_bp;
unsigned int h_active = devp->para.h_active;
unsigned int v_active = devp->para.v_active;
if (is_meson_m8b_cpu()) {
/* top reset for bt656 */
/* WRITE_CBUS_REG_BITS(RESET1_REGISTER, 1, 5, 1); */
/* WRITE_CBUS_REG_BITS(RESET1_REGISTER, 0, 5, 1); */
aml_cbus_update_bits(RESET1_REGISTER, 0x1<<5, 1);
aml_cbus_update_bits(RESET1_REGISTER, 0x1<<5, 0);
}
/* disable 656,reset */
bt656_wr(offset, BT_CTRL, 1<<31);
/*wr(BT_VIDEOSTART, 1 | (1 << 16));
* //Line number of the first video start line in field 0/1.
* there is a blank
* wr(BT_VIDEOEND , (am656in_dec_info.active_line ));
* //Line number of the last video line in field 1.
* added video end for avoid overflow.
* ((am656in_dec_info.active_line ) << 16));
*/
/* Line number of the last video line in field 0 */
/* use external idq pin. */
bt656_wr(offset, BT_PORT_CTRL, (0 << BT_IDQ_EN) |
(0 << BT_IDQ_PHASE) |
/* FID came from HS VS. */
(0 << BT_FID_HSVS) |
(1 << BT_ACTIVE_HMODE) |
(vsync_enable << BT_VSYNC_PHASE) |
(hsync_enable << BT_HSYNC_PHASE) |
(0 << BT_D8B) |
(4 << BT_FID_DELAY) | /* 4 */
(0 << BT_VSYNC_DELAY) |
(2 << BT_HSYNC_DELAY)); /* 2 */
/* WRITE_CBUS_REG(BT_PORT_CTRL,0x421001); */
/* FID field check done point. */
bt656_wr(offset, BT_601_CTRL2, (10 << 16));
bt656_wr(offset, BT_SWAP_CTRL,
(5 << 0) | /* POS_Cb0_IN */
(4 << 4) | /* POS_Y0_IN */
(7 << 8) | /* POS_Cr0_IN */
(6 << 12)); /* POS_Y1_IN */
/* horizontal active data start offset, *2 for 422 sampling */
bt656_wr(offset, BT_LINECTRL, (1 << 31) |
(((h_active + hs_bp) << 1) << 16) |
(hs_bp << 1));/* horizontal active data start offset */
/* ANCI is the field blanking data, like close caption.
* If it connected to digital camara interface,
* the jpeg bitstream also use this ANCI FIFO.
*/
/* bt656_wr(BT_ANCISADR, devp->mem_start); */
/* bt656_wr(BT_ANCIEADR, 0);//devp->mem_start + devp->mem_size); */
bt656_wr(offset, BT_AFIFO_CTRL, (1 << 31) |
(1 << 6) | /* fill _en; */
(1 << 3)); /* urgent */
bt656_wr(offset, BT_INT_CTRL, /* (1 << 5) | //ancififo done int. */
/* (1 << 4) | //SOF interrupt enable. */
/* (1 << 3) | //EOF interrupt enable. */
(1 << 1)); /* input overflow interrupt enable. */
/* (1 << 0)); //bt656 controller error interrupt enable. */
/* total lines per frame and total pixel per line */
bt656_wr(offset, BT_ERR_CNT, ((2000) << 16) | (2000 * 10));
/* otherwise there is always error flag, */
/* because the camera input use HREF ont HSYNC, */
/* there are some lines without HREF sometime */
/* field 0/1 start lcnt */
bt656_wr(offset, BT_FIELDSADR, (1 << 16) | 1);
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBISTART, 1 | (1 << 16));
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBIEND, 1 | (1 << 16));
/* Line number of the first video start
* line in field 0/1.there is a blank
*/
bt656_wr(offset, BT_VIDEOSTART, vs_bp | (vs_bp << 16));
/* Line number of the last video line in field 1.
* added video end for avoid overflow.
*/
/* Line number of the last video line in field 0 */
bt656_wr(offset, BT_VIDEOEND,
((v_active + vs_bp - 1) |
((v_active + vs_bp - 1) << 16)));
bt656_wr(offset, BT_DELAY_CTRL, 0x22222222);
/* enable BTR656 interface */
if (devp->para.isp_fe_port == TVIN_PORT_CAMERA) {
temp_data = (1 << BT_EN_BIT) /* enable BT moduale. */
/* timing reference is from bit stream. */
|(0 << BT_REF_MODE_BIT)
|(0 << BT_FMT_MODE_BIT) /* PAL */
|(0 << BT_SLICE_MODE_BIT) /* no ancillay flag. */
/* BT656 standard interface. */
|(0 << BT_MODE_BIT)
|(1 << BT_CLOCK_ENABLE) /* enable 656 clock. */
|(0 << BT_FID_EN_BIT) /* use external fid pin. */
/* xclk27 is input. change to Raw_mode setting from M8*/
/* |(0 << BT_XCLK27_EN_BIT) */
|(0 << BT_PROG_MODE)
|(0 << BT_AUTO_FMT)
|(0 << BT_CAMERA_MODE) /* enable camera mode */
|(1 << BT_656CLOCK_RESET)
|(1 << BT_SYSCLOCK_RESET)
|(1 << BT_RAW_MODE) /* enable raw data output */
|(1 << BT_RAW_ISP) /* enable raw data to isp */
|(0 << 28) /* enable csi2 pin */
;
} else {
temp_data =
/* BT656 standard interface. */
(0 << BT_MODE_BIT)
/* camera mode */
|(1 << BT_CAMERA_MODE)
/* enable BT data input */
|(1 << BT_EN_BIT)
/* timing reference is from bit stream. */
|(0 << BT_REF_MODE_BIT)
|(0 << BT_FMT_MODE_BIT) /* NTSC */
|(0 << BT_SLICE_MODE_BIT)
/* use external fid pin. */
|(0 << BT_FID_EN_BIT)
/* enable bt656 clock input */
|(1 << 7)
|(0 << 18) /* eol_delay //4 */
|(1 << 16) /* update status for debug */
/* enable sys clock input */
|(1 << BT_XCLK27_EN_BIT)
|(0x60000000); /* out of reset for BT_CTRL. */
}
if ((devp->para.bt_path == BT_PATH_GPIO) ||
(devp->para.bt_path == BT_PATH_GPIO_B)) {
temp_data &= (~(1<<28));
bt656_wr(offset, BT_CTRL, temp_data);
} else if (devp->para.bt_path == BT_PATH_CSI2) {
#ifdef CONFIG_TVIN_ISP
temp_data |= (1<<28);
bt656_wr(offset, BT_CTRL, temp_data);
/* power on mipi csi phy */
aml_write_cbus(HHI_CSI_PHY_CNTL0, 0xfdc1ff81);
aml_write_cbus(HHI_CSI_PHY_CNTL1, 0x3fffff);
temp_data = aml_read_cbus(HHI_CSI_PHY_CNTL2);
temp_data &= 0x7ff00000;
temp_data |= 0x80000fc0;
aml_write_cbus(HHI_CSI_PHY_CNTL2, temp_data);
#endif
}
}
/* bt601_hdmi input(progressive or interlace): CLOCK + D0~D7 + HSYNC + VSYNC */
static void reinit_bt601_hdmi_dec(struct am656in_dev_s *devp)
{
unsigned int offset = devp->index;
/* reset_bt656in_module(); */
unsigned int temp_data;
unsigned char hsync_enable = devp->para.hsync_phase;
unsigned char vsync_enable = devp->para.vsync_phase;
unsigned short hs_bp = devp->para.hs_bp;
unsigned short vs_bp = devp->para.vs_bp;
unsigned int h_active = devp->para.h_active;
unsigned int v_active = devp->para.v_active;
unsigned int fid_check_cnt = devp->para.fid_check_cnt;
#if 0
pr_info("para hsync_phase: %d\n"
"para vsync_phase: %d\n"
"para hs_bp: %d\n"
"para vs_bp: %d\n"
"para h_active: %d\n"
"para v_active: %d\n",
hsync_enable, vsync_enable,
hs_bp, vs_bp, h_active, v_active);
#endif
/* disable 656,reset */
bt656_wr(offset, BT_CTRL, 1<<31);
bt656_wr(offset, BT_PORT_CTRL, (0 << BT_IDQ_EN) |
(0 << BT_IDQ_PHASE) |
(1 << BT_ACTIVE_HMODE) |
(vsync_enable << BT_VSYNC_PHASE) |
(hsync_enable << BT_HSYNC_PHASE) |
(0 << BT_D8B) |
(0 << BT_FID_DELAY) |
(0 << BT_FID_HSVS_PCNT) | /* use hs/vs */
(1 << BT_FID_HSVS) | /* generate fid */
(0 << BT_VSYNC_DELAY) |
(0 << BT_HSYNC_DELAY));
/* FID field check done point. */
bt656_wr(offset, BT_601_CTRL2, (fid_check_cnt << 16));
bt656_wr(offset, BT_SWAP_CTRL,
(4 << 0) | /* POS_Cb0_IN */
(5 << 4) | /* POS_Y0_IN */
(6 << 8) | /* POS_Cr0_IN */
(7 << 12)); /* POS_Y1_IN */
/* horizontal active data start offset, *2 for 422 sampling */
bt656_wr(offset, BT_LINECTRL, (1 << 31) |
(((h_active + hs_bp) << 1) << 16) |
(hs_bp << 1));
bt656_wr(offset, BT_AFIFO_CTRL, (1 << 31) |
(1 << 6) | /* fill _en; */
(1 << 3)); /* urgent */
bt656_wr(offset, BT_INT_CTRL, /* (1 << 5) | //ancififo done int. */
/* (1 << 4) | //SOF interrupt enable. */
/* (1 << 3) | //EOF interrupt enable. */
(1 << 1)); /* input overflow interrupt enable. */
/* (1 << 0)); //bt656 controller error interrupt enable. */
/* total lines per frame and total pixel per line */
bt656_wr(offset, BT_ERR_CNT, ((2000) << 16) | (2000 * 10));
/* otherwise there is always error flag, */
/* because the camera input use HREF ont HSYNC, */
/* there are some lines without HREF sometime */
/* field 0/1 start lcnt */
bt656_wr(offset, BT_FIELDSADR, (0 << 16) | 0);
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBISTART, 0 | (0 << 16));
/* field 0/1 VBI last line number */
bt656_wr(offset, BT_VBIEND, 0 | (0 << 16));
/* Line number of the first video start
* line in field 0/1.there is a blank
*/
bt656_wr(offset, BT_VIDEOSTART, vs_bp | (vs_bp << 16));
/* Line number of the last video line in field 1.
* added video end for avoid overflow.
*/
/* Line number of the last video line in field 0 */
bt656_wr(offset, BT_VIDEOEND,
((v_active + vs_bp - 1) |
((v_active + vs_bp - 1) << 16)));
bt656_wr(offset, BT_DELAY_CTRL, 0x22222222);
/* enable BTR656 interface */
temp_data =
/* BT656 standard interface. */
(0 << BT_MODE_BIT)
/* camera mode */
|(1 << BT_CAMERA_MODE)
/* enable BT data input */
|(1 << BT_EN_BIT)
/* timing reference is from bit stream. */
|(0 << BT_REF_MODE_BIT)
|(0 << BT_FMT_MODE_BIT)
|(0 << BT_SLICE_MODE_BIT)
/* use external fid pin. */
|(0 << BT_FID_EN_BIT)
/* enable bt656 clock input */
|(1 << 7)
|(0 << 18) /* eol_delay */
|(1 << 16) /* update status for debug */
/* enable sys clock input */
|(1 << BT_XCLK27_EN_BIT)
|(0x60000000); /* out of reset for BT_CTRL. */
temp_data &= (~(1<<28));
bt656_wr(offset, BT_CTRL, temp_data);
}
static void start_amvdec_656_601_camera_in(struct am656in_dev_s *devp)
{
enum tvin_port_e port = devp->para.port;
if (devp->dec_status & TVIN_AM656_RUNNING) {
BT656PR("%s: bt656 have started alreadly.\n", __func__);
return;
}
devp->dec_status = TVIN_AM656_RUNNING;
/* NTSC or PAL input(interlace mode): D0~D7(with SAV + EAV ) */
if (port == TVIN_PORT_BT656) {
devp->para.fmt = TVIN_SIG_FMT_BT656IN_576I_50HZ;
init_656in_dec_parameter(devp);
reinit_bt656in_dec(devp);
/* reset_656in_dec_parameter(); */
devp->dec_status = TVIN_AM656_RUNNING;
} else if (port == TVIN_PORT_BT601) {
devp->para.fmt = TVIN_SIG_FMT_BT601IN_576I_50HZ;
init_656in_dec_parameter(devp);
reinit_bt601in_dec(devp);
devp->dec_status = TVIN_AM656_RUNNING;
} else if (port == TVIN_PORT_CAMERA) {
init_656in_dec_parameter(devp);
reinit_camera_dec(devp);
devp->dec_status = TVIN_AM656_RUNNING;
} else if (port == TVIN_PORT_BT656_HDMI) {
init_656in_dec_hdmi_parameter(devp);
reinit_bt601_hdmi_dec(devp);
devp->dec_status = TVIN_AM656_RUNNING;
} else if (port == TVIN_PORT_BT601_HDMI) {
init_656in_dec_hdmi_parameter(devp);
reinit_bt601_hdmi_dec(devp);
devp->dec_status = TVIN_AM656_RUNNING;
} else {
devp->para.fmt = TVIN_SIG_FMT_NULL;
devp->para.port = TVIN_PORT_NULL;
BT656ERR("%s: input is not selected, please try again.\n",
__func__);
return;
}
BT656PR("bt656[%d](%s): %s input port: %s fmt: %s.\n",
devp->index, BT656_VER, __func__,
tvin_port_str(devp->para.port),
tvin_sig_fmt_str(devp->para.fmt));
}
static void stop_amvdec_bt656(struct am656in_dev_s *devp)
{
if (devp->dec_status & TVIN_AM656_RUNNING) {
reset_bt656in_module(devp);
devp->dec_status = TVIN_AM656_STOP;
} else {
BT656PR("bt656[%d]:%s device is not started yet.\n",
devp->index, __func__);
}
}
static void am656_clktree_control(struct am656in_dev_s *devp, int flag)
{
BT656PR("bt656[%d]:%s: flag=%d\n", devp->index, __func__, flag);
if (flag) { /* clk enable */
if (IS_ERR(devp->gate_bt656)) {
BT656ERR("bt656[%d] %s: gate_bt656\n",
devp->index, __func__);
} else {
clk_prepare_enable(devp->gate_bt656);
}
if (IS_ERR(devp->gate_bt656_pclk)) {
BT656ERR("bt656[%d] %s: gate_bt656_pclk\n",
devp->index, __func__);
} else {
clk_prepare_enable(devp->gate_bt656_pclk);
}
if (IS_ERR(devp->bt656_clk)) {
BT656ERR("bt656[%d] %s: bt656_clk\n",
devp->index, __func__);
} else {
clk_prepare_enable(devp->bt656_clk);
}
udelay(100);
} else { /* clk disable */
if (IS_ERR(devp->bt656_clk)) {
BT656ERR("bt656[%d] %s: bt656_clk\n",
devp->index, __func__);
} else {
clk_disable_unprepare(devp->bt656_clk);
}
if (IS_ERR(devp->gate_bt656_pclk)) {
BT656ERR("bt656[%d] %s: gate_bt656_pclk\n",
devp->index, __func__);
} else {
clk_disable_unprepare(devp->gate_bt656_pclk);
}
if (IS_ERR(devp->gate_bt656)) {
BT656ERR("bt656[%d] %s: gate_bt656\n",
devp->index, __func__);
} else {
clk_disable_unprepare(devp->gate_bt656);
}
}
}
/*
* return true when need skip frame otherwise return false
*/
static bool am656_check_skip_frame(struct tvin_frontend_s *fe)
{
struct am656in_dev_s *devp;
devp = container_of(fe, struct am656in_dev_s, frontend);
if (devp->skip_vdin_frame_count > 0) {
devp->skip_vdin_frame_count--;
return true;
} else
return false;
}
int am656in_support(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
if ((port < TVIN_PORT_BT656) || (port > TVIN_PORT_BT601_HDMI))
return -1;
else
return 0;
}
static int am656in_open(struct inode *node, struct file *file)
{
struct am656in_dev_s *devp;
/* Get the per-device structure that contains this cdev */
devp = container_of(node->i_cdev, struct am656in_dev_s, cdev);
if (devp->index >= hw_cnt)
return -ENXIO;
file->private_data = devp;
BT656PR("open device %s ok\n", dev_name(devp->dev));
return 0;
}
static int am656in_release(struct inode *node, struct file *file)
{
/* struct vdin_dev_s *devp = file->private_data; */
file->private_data = NULL;
/* BT656PR("close device %s ok\n", dev_name(devp->dev)); */
return 0;
}
static const struct file_operations am656in_fops = {
.owner = THIS_MODULE,
.open = am656in_open,
.release = am656in_release,
};
/*called by vdin && sever for v4l2 framework*/
void am656in_start(struct tvin_frontend_s *fe, enum tvin_sig_fmt_e fmt)
{
struct am656in_dev_s *devp;
devp = container_of(fe, struct am656in_dev_s, frontend);
mutex_lock(&bt656_mutex);
start_amvdec_656_601_camera_in(devp);
mutex_unlock(&bt656_mutex);
}
static void am656in_stop(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
struct am656in_dev_s *devp;
devp = container_of(fe, struct am656in_dev_s, frontend);
if ((port < TVIN_PORT_BT656) || (port > TVIN_PORT_BT601_HDMI)) {
BT656ERR("%s: invaild port %d.\n", __func__, port);
return;
}
mutex_lock(&bt656_mutex);
stop_amvdec_bt656(devp);
BT656PR("bt656[%d] %s stop device stop ok.\n", devp->index, __func__);
mutex_unlock(&bt656_mutex);
}
static void am656in_get_sig_property(struct tvin_frontend_s *fe,
struct tvin_sig_property_s *prop)
{
struct am656in_dev_s *devp;
devp = container_of(fe, struct am656in_dev_s, frontend);
prop->color_format = devp->para.cfmt;
prop->dest_cfmt = devp->para.dfmt;
prop->decimation_ratio = 0;
}
/*as use the spin_lock,
*1--there is no sleep,
*2--it is better to shorter the time,
*/
int am656in_isr(struct tvin_frontend_s *fe, unsigned int hcnt)
{
unsigned int ccir656_status = 0;
struct am656in_dev_s *devp;
unsigned int offset;
devp = container_of(fe, struct am656in_dev_s, frontend);
offset = devp->index;
ccir656_status = bt656_rd(offset, BT_STATUS);
if (ccir656_status & 0xf0) /* AFIFO OVERFLOW */
devp->overflow_cnt++;
if (devp->overflow_cnt > 5) {
devp->overflow_cnt = 0;
/* NTSC or PAL input(interlace mode): D0~D7(with SAV + EAV ) */
if (devp->para.port == TVIN_PORT_BT656)
reinit_bt656in_dec(devp);
else if (devp->para.port == TVIN_PORT_BT601)
reinit_bt601in_dec(devp);
else if (devp->para.port == TVIN_PORT_BT656_HDMI)
reinit_bt601_hdmi_dec(devp);
else if (devp->para.port == TVIN_PORT_BT601_HDMI)
reinit_bt601_hdmi_dec(devp);
else /* if(am656in_dec_info.para.port == TVIN_PORT_CAMERA) */
reinit_camera_dec(devp);
/* WRITE_CBUS_REGite 1 to clean the SOF interrupt bit */
bt656_wr(offset, BT_STATUS, ccir656_status | (1 << 9));
BT656PR("bt656[%d] %s bt656in fifo overflow.\n",
devp->index, __func__);
}
return 0;
}
/*
*power on 656 module&init the parameters,such as
*power color fmt...,will be used by vdin
*/
static int am656in_feopen(struct tvin_frontend_s *fe, enum tvin_port_e port)
{
struct am656in_dev_s *devp;
struct vdin_parm_s *parm = fe->private_data;
devp = container_of(fe, struct am656in_dev_s, frontend);
if ((port < TVIN_PORT_BT656) || (port > TVIN_PORT_BT601_HDMI)) {
BT656ERR("bt656[%d] %s:invaild port %d.\n",
devp->index, __func__, port);
return -1;
}
if (port == TVIN_PORT_CAMERA)
devp->skip_vdin_frame_count = parm->skip_count;
/*copy the param from vdin to bt656*/
if (!memcpy(&devp->para, parm, sizeof(struct vdin_parm_s))) {
BT656ERR("bt656[%d] %s memcpy error\n", devp->index, __func__);
return -1;
}
/*avoidint the param port is't equal with port*/
devp->para.port = port;
BT656PR("bt656[%d]:%s: color fmt:%s, hs phase:%u, vs phase:%u\n",
devp->index, __func__,
tvin_color_fmt_str(parm->cfmt),
parm->hsync_phase, parm->vsync_phase);
BT656PR("frame rate:%u, hs_bp:%u, vs_bp:%u\n",
parm->frame_rate, parm->hs_bp, parm->vs_bp);
/* bt656 clock enable */
am656_clktree_control(devp, 1);
return 0;
}
/*
*power off the 656 module,clear the parameters
*/
static void am656in_feclose(struct tvin_frontend_s *fe)
{
struct am656in_dev_s *devp = NULL;
enum tvin_port_e port = 0;
devp = container_of(fe, struct am656in_dev_s, frontend);
port = devp->para.port;
if ((port < TVIN_PORT_BT656) || (port > TVIN_PORT_BT601_HDMI)) {
BT656ERR("bt656[%d] %s:invaild port %d.\n",
devp->index, __func__, port);
return;
}
memset(&devp->para, 0, sizeof(struct vdin_parm_s));
/* bt656 clock disable */
am656_clktree_control(devp, 0);
}
static struct tvin_state_machine_ops_s am656_machine_ops = {
.nosig = NULL,
.fmt_changed = NULL,
.get_fmt = NULL,
.fmt_config = NULL,
.adc_cal = NULL,
.pll_lock = NULL,
.get_sig_property = am656in_get_sig_property,
.vga_set_param = NULL,
.vga_get_param = NULL,
.check_frame_skip = am656_check_skip_frame,
};
static struct tvin_decoder_ops_s am656_decoder_ops_s = {
.support = am656in_support,
.open = am656in_feopen,
.start = am656in_start,
.stop = am656in_stop,
.close = am656in_feclose,
.decode_isr = am656in_isr,
};
static int bt656_add_cdev(struct cdev *cdevp,
const struct file_operations *fops, int minor)
{
int ret;
dev_t devno = MKDEV(MAJOR(am656in_devno), minor);
cdev_init(cdevp, fops);
cdevp->owner = THIS_MODULE;
ret = cdev_add(cdevp, devno, 1);
return ret;
}
static struct device *bt656_create_device(struct device *parent, int minor)
{
dev_t devno = MKDEV(MAJOR(am656in_devno), minor);
return device_create(am656in_clsp, parent, devno, NULL, "%s%d",
BT656_DEV_NAME, minor);
}
static void bt656_delete_device(int minor)
{
dev_t devno = MKDEV(MAJOR(am656in_devno), minor);
device_destroy(am656in_clsp, devno);
}
static int amvdec_656in_probe(struct platform_device *pdev)
{
int ret = 0;
struct am656in_dev_s *devp = NULL;
int bt656_rate;
struct resource *res = 0;
int size = 0;
int index = 0;
struct device_node *child;
const char *str;
char bt656_clk_name[20];
BT656PR("%s: start probe .\n", __func__);
if (is_meson_gxtvbb_cpu() || is_meson_gxl_cpu() ||
is_meson_gxm_cpu() || is_meson_g12a_cpu() ||
is_meson_g12b_cpu() || is_meson_tl1_cpu()) {
hw_cnt = 1;
} else if (is_meson_gxbb_cpu()) {
hw_cnt = 2;
} else {
hw_cnt = 0;
}
if (hw_cnt == 0) {
BT656ERR("no bt656 support for current chip\n");
return -EFAULT;
}
BT656PR("%s. %d hardware detected!.\n", __func__, hw_cnt);
mutex_init(&bt656_mutex);
ret = alloc_chrdev_region(&am656in_devno, 0, hw_cnt, BT656_DEV_NAME);
if (ret < 0) {
BT656ERR("%s: failed to alloc major number\n", __func__);
goto fail_alloc_cdev_region;
}
BT656PR("%s: major %d\n", __func__, MAJOR(am656in_devno));
am656in_clsp = class_create(THIS_MODULE, BT656_CLS_NAME);
if (IS_ERR(am656in_clsp)) {
ret = PTR_ERR(am656in_clsp);
BT656ERR("%s: failed to create class\n", __func__);
goto fail_class_create;
}
for_each_child_of_node(pdev->dev.of_node, child) {
ret = of_property_read_u32(child, "bt656_id", &index);
if (ret) {
BT656ERR("%s: don't find bt656 id.\n", __func__);
goto fail_kmalloc_dev;
}
if ((index == 0) || (index > 2)) {
BT656ERR("%s: invalid bt656 index %d.\n",
__func__, index);
goto fail_kmalloc_dev;
}
ret = of_property_read_string(child, "status", &str);
if (ret) {
BT656ERR("%s: bt656[%d] don't find status, disabled\n",
__func__, index);
continue;
} else {
if (strncmp(str, "okay", 2))
continue;
}
/* malloc dev */
am656in_devp[index-1] =
kmalloc(sizeof(struct am656in_dev_s), GFP_KERNEL);
if (!am656in_devp[index-1]) {
BT656ERR("%s: devp malloc error\n", __func__);
goto fail_kmalloc_dev;
}
memset(am656in_devp[index-1], 0, sizeof(struct am656in_dev_s));
devp = am656in_devp[index-1];
devp->index = index;
BT656PR("%s: bt656 devp->index =%d\n", __func__, devp->index);
/* create cdev and register with sysfs */
ret = bt656_add_cdev(&devp->cdev, &am656in_fops, devp->index);
if (ret) {
BT656ERR("%s: failed to add cdev\n", __func__);
goto fail_add_cdev;
}
devp->dev = bt656_create_device(&pdev->dev, devp->index);
if (IS_ERR(devp->dev)) {
BT656ERR("%s: failed to create device\n", __func__);
ret = PTR_ERR(devp->dev);
goto fail_create_device;
}
ret = device_create_file(devp->dev, &dev_attr_reg);
if (ret < 0) {
BT656ERR("fail to create dbg attribute file\n");
goto fail_create_file;
}
/* set clktree */
sprintf(bt656_clk_name, "cts_bt656_clk%d", devp->index);
devp->bt656_clk = clk_get(&pdev->dev, bt656_clk_name);
clk_set_rate(devp->bt656_clk, 333333333);
if (!IS_ERR(devp->bt656_clk)) {
bt656_rate = clk_get_rate(devp->bt656_clk);
/*clk_put(devp->bt656_clk);*/
BT656PR("%s: bt656[%d] clock is %d MHZ\n",
__func__,
devp->index, bt656_rate/1000000);
} else {
BT656ERR("%s: bt656[%d] cannot get %s !!!\n",
__func__, devp->index, bt656_clk_name);
ret = -ENOENT;
goto fail_get_clktree;
}
devp->gate_bt656 = clk_get(&pdev->dev, "clk_gate_bt656");
if (IS_ERR(devp->gate_bt656)) {
BT656ERR("%s: cannot get clk_gate_bt656 !!!\n",
__func__);
ret = -ENOENT;
goto fail_get_clktree;
}
devp->gate_bt656_pclk = clk_get(&pdev->dev,
"clk_gate_bt656_pclk1");
if (IS_ERR(devp->gate_bt656_pclk)) {
BT656ERR("%s: cannot get clk_gate_bt656_pclk1 !!!\n",
__func__);
//ret = -ENOENT;
//goto fail_get_clktree;
}
/* set regmap */
BT656PR("%s: bt656[%d] start get ioremap .\n",
__func__, devp->index);
res = platform_get_resource(pdev, IORESOURCE_MEM,
(devp->index - 1));
if (!res) {
BT656ERR("missing memory resource\n");
ret = -ENOMEM;
goto fail_get_clktree;
}
size = resource_size(res);
bt656_reg_base[devp->index-1] =
devm_ioremap_nocache(&pdev->dev, res->start, size);
if (!bt656_reg_base[devp->index-1]) {
BT656ERR("bt656[%d] ioremap failed\n", devp->index);
ret = -ENOMEM;
goto fail_get_clktree;
}
BT656PR("%s: bt656[%d] mapped reg_base=0x%p, size=0x%x\n",
__func__, devp->index,
bt656_reg_base[devp->index-1], size);
/* set drvdata */
dev_set_drvdata(devp->dev, devp);
/*register frontend */
sprintf(devp->frontend.name, "%s%d",
BT656_DEV_NAME, devp->index);
/* tvin_frontend_init(&devp->frontend,
* &am656_decoder_ops_s, &am656_machine_ops, pdev->id);
*/
if (!tvin_frontend_init(&devp->frontend, &am656_decoder_ops_s,
&am656_machine_ops, devp->index-1)) {
BT656PR("%s: tvin_frontend_init done :%d\n",
__func__, devp->index);
if (tvin_reg_frontend(&devp->frontend)) {
BT656ERR("%s register frontend error\n",
__func__);
}
}
}
BT656PR("driver probe ok.\n");
return ret;
/*
*fail_get_resource_mem:
* devm_release_mem_region(&pdev->dev, res->start, size);
*/
fail_get_clktree:
device_remove_file(devp->dev, &dev_attr_reg);
fail_create_file:
bt656_delete_device(pdev->id);
fail_create_device:
cdev_del(&devp->cdev);
fail_add_cdev:
kfree(devp);
fail_kmalloc_dev:
class_destroy(am656in_clsp);
fail_class_create:
unregister_chrdev_region(am656in_devno, hw_cnt);
fail_alloc_cdev_region:
return ret;
}
static int amvdec_656in_remove(struct platform_device *pdev)
{
struct am656in_dev_s *devp;
int index;
for (index = 0; index < hw_cnt; index++) {
devp = am656in_devp[index];
if (devp == NULL)
continue;
device_remove_file(devp->dev, &dev_attr_reg);
tvin_unreg_frontend(&devp->frontend);
bt656_delete_device(pdev->id);
cdev_del(&devp->cdev);
kfree((const void *)devp);
}
class_destroy(am656in_clsp);
unregister_chrdev_region(am656in_devno, hw_cnt);
return 0;
}
static int amvdec_656in_resume(struct platform_device *pdev)
{
BT656PR("%s\n", __func__);
return 0;
}
static int amvdec_656in_suspend(struct platform_device *pdev,
pm_message_t state)
{
struct am656in_dev_s *devp;
int index;
mutex_lock(&bt656_mutex);
BT656PR("%s\n", __func__);
for (index = 0; index < hw_cnt; index++) {
devp = am656in_devp[index];
if (devp == NULL)
continue;
if (devp->dec_status & TVIN_AM656_RUNNING) {
reset_bt656in_module(devp);
devp->dec_status = TVIN_AM656_STOP;
am656_clktree_control(devp, 0);
}
}
mutex_unlock(&bt656_mutex);
return 0;
}
static void amvdec_656in_shutdown(struct platform_device *pdev)
{
struct am656in_dev_s *devp;
int index;
BT656PR("%s\n", __func__);
for (index = 0; index < hw_cnt; index++) {
devp = am656in_devp[index];
if (devp == NULL)
continue;
if (devp->dec_status & TVIN_AM656_RUNNING) {
reset_bt656in_module(devp);
devp->dec_status = TVIN_AM656_STOP;
am656_clktree_control(devp, 0);
}
}
}
#ifdef CONFIG_OF
static const struct of_device_id bt656_dt_match[] = {
{
.compatible = "amlogic, amvdec_656in",
},
{},
};
#endif
static struct platform_driver amvdec_656in_driver = {
.probe = amvdec_656in_probe,
.remove = amvdec_656in_remove,
.suspend = amvdec_656in_suspend,
.resume = amvdec_656in_resume,
.shutdown = amvdec_656in_shutdown,
.driver = {
.name = BT656_DRV_NAME,
.owner = THIS_MODULE,
#ifdef CONFIG_OF
.of_match_table = bt656_dt_match,
#endif
}
};
static int __init amvdec_656in_init_module(void)
{
int ret = 0;
ret = platform_driver_register(&amvdec_656in_driver);
if (ret != 0) {
BT656ERR("%s: failed to register driver\n", __func__);
return -1;
}
return 0;
}
static void __exit amvdec_656in_exit_module(void)
{
platform_driver_unregister(&amvdec_656in_driver);
}
module_init(amvdec_656in_init_module);
module_exit(amvdec_656in_exit_module);
MODULE_DESCRIPTION("AMLOGIC BT656_601 input driver");
MODULE_LICENSE("GPL");
MODULE_VERSION("2.0.0");