Files

641 lines
20 KiB
C

/*
Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd.
*/
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#if LINUX_VERSION_CODE > KERNEL_VERSION(3, 3, 0)
#include <asm/switch_to.h>
#else
#include <asm/system.h>
#endif
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <linux/list.h>
#include <asm/delay.h>
#include <linux/timer.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/poll.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <linux/moduleparam.h>
#include <linux/ioport.h>
#include <linux/interrupt.h>
#include <linux/semaphore.h>
#include <linux/kthread.h>
#ifdef I2C_INTERNAL
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#endif
#include "video.h"
#include "coax_protocol.h"
#include "motion.h"
#include "common.h"
#include "audio.h"
#include "video_auto_detect.h"
#include "video_eq.h"
#include "securec.h"
#define AF_CNT 1
#ifdef I2C_INTERNAL
static struct i2c_board_info ot_info = {
I2C_BOARD_INFO("nvp6158", 0x62 >> 1),
};
static struct i2c_client *nvp6158_client;
#endif
#define DRIVER_VER "19.04.09.01"
#ifdef I2C_INTERNAL
void __I2CWriteByte8(unsigned char chip_addr, unsigned char reg_addr, unsigned char value)
{
unsigned char buf[2];
struct i2c_client *client = nvp6158_client;
nvp6158_client->addr = (chip_addr >> 1);
buf[0] = reg_addr;
buf[1] = value;
i2c_master_send(client, buf, 2);
udelay(300);
}
unsigned char __I2CReadByte8(unsigned char chip_addr, unsigned char reg_addr)
{
unsigned char ret_data = 0xFF;
int ret;
struct i2c_client *client = nvp6158_client;
static struct i2c_msg msg[2];
unsigned char buffer[2];
const unsigned int data_width = 1;
(void)chip_addr;
buffer[0] = reg_addr & 0xFF;
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = buffer;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
msg[1].len = 1;
msg[1].buf = buffer;
ret = i2c_transfer(client->adapter, msg, 2);
if (ret != 2) {
printk("[%s %d] ot_i2c_transfer error, ret=%d.\n", __FUNCTION__, __LINE__, ret);
return 0xff;
}
ret = memcpy_s(&ret_data, sizeof(unsigned char), buffer, data_width);
if (ret != EOK) {
printk("[%s %d] memcpy_s error, ret=%d.\n", __FUNCTION__, __LINE__, ret);
return 0xff;
}
return ret_data;
}
#endif
int g_soc_chiptype = 0x3521;
int chip_id[4];
int rev_id[4];
unsigned int nvp6158_mode = NTSC; // 0:ntsc, 1: pal
unsigned int nvp6158_cnt = 0;
unsigned int nvp6158_iic_addr[4] = {0x60, 0x62, 0x64, 0x66};
module_param(nvp6158_mode, uint, S_IRUGO);
unsigned int kthread = 0;
unsigned int detect_kthread = 1;
module_param(kthread, uint, S_IRUGO);
unsigned int chnl = TVI_HD_B_25P;
unsigned int port = NVP6158_OUTMODE_1MUX_HD;
module_param(chnl, uint, S_IRUGO);
module_param(port, uint, S_IRUGO);
struct semaphore nvp6158_lock;
unsigned char det_mode[16] = { NVP6158_DET_MODE_AUTO };
extern unsigned char ch_mode_status[16];
extern unsigned char ch_vfmt_status[16];
extern unsigned char acp_isp_wr_en[16];
unsigned int gCoaxFirmUpdateFlag[16] = { 0 };
static struct task_struct *nvp6158_kt = NULL;
static struct task_struct *nvp6158_detect_kt = NULL;
int check_rev(unsigned int dec)
{
int ret;
gpio_i2c_write(dec, 0xFF, 0x00);
ret = gpio_i2c_read(dec, 0xf5);
return ret;
}
int check_id(unsigned int dec)
{
int ret;
gpio_i2c_write(dec, 0xFF, 0x00);
ret = gpio_i2c_read(dec, 0xf4);
return ret;
}
int nvp6158_open(struct inode *inode, struct file *file)
{
(void)inode;
(void)file;
return 0;
}
int nvp6158_close(struct inode *inode, struct file *file)
{
(void)inode;
(void)file;
return 0;
}
unsigned int g_vloss = 0xFFFF;
long nvp6158_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int __user *argp = (unsigned int __user *)(uintptr_t)arg;
const int cpy2usr_ret = 0;
nvp6158_opt_mode optmode;
nvp6158_chn_mode schnmode;
nvp6158_audio_format audio_format;
if (argp == NULL) {
return -1;
}
(void)file;
down(&nvp6158_lock);
switch (cmd) {
case IOC_VDEC_SET_CHNMODE:
if (copy_from_user(&schnmode, argp, sizeof(nvp6158_chn_mode))) {
up(&nvp6158_lock);
return -1;
}
if (schnmode.ch >= nvp6158_cnt * CH_PER_CHIP) {
up(&nvp6158_lock);
return -1;
}
if (chip_id[0] == NVP6158C_R0_ID || chip_id[0] == NVP6158_R0_ID) {
if (0 == nvp6158_set_chnmode(schnmode.ch, schnmode.chmode)) {
}
} else {
if (0 == nvp6168_set_chnmode(schnmode.ch, schnmode.chmode)) {
}
}
break;
case IOC_VDEC_SET_OUTPORTMODE:
if (copy_from_user(&optmode, argp, sizeof(nvp6158_opt_mode))) {
up(&nvp6158_lock);
return -1;
}
if (optmode.chipsel >= nvp6158_cnt || optmode.chipsel >= 4 || optmode.chid >= CH_PER_CHIP) {
up(&nvp6158_lock);
return -1;
}
if (chip_id[optmode.chipsel] == NVP6158C_R0_ID) {
if (optmode.portsel != 1 && optmode.portsel != 2) {
up(&nvp6158_lock);
return -1;
}
} else {
if (optmode.portsel >= 4) {
up(&nvp6158_lock);
return -1;
}
}
nvp6158_set_portmode(optmode.chipsel, optmode.portsel, optmode.portmode, optmode.chid);
break;
case NVP6158_SET_AUDIO_PB_FORMAT:
if (copy_from_user(&audio_format, argp, sizeof(nvp6158_audio_format))) {
printk("NVP6158_SET_AUDIO_PB_FORMAT error\n");
up(&nvp6158_lock);
return -1;
}
nvp6158_audio_set_format(1, &audio_format);
break;
case NVP6158_SET_AUDIO_R_FORMAT:
if (copy_from_user(&audio_format, argp, sizeof(nvp6158_audio_format))) {
printk("NVP6158_SET_AUDIO_R_FORMAT error\n");
up(&nvp6158_lock);
return -1;
}
nvp6158_audio_set_format(0, &audio_format);
break;
default:
break;
}
if (cpy2usr_ret) {
printk("copy to user return 0x%x\n", cpy2usr_ret);
}
up(&nvp6158_lock);
return 0;
}
#ifdef I2C_INTERNAL
static int i2c_client_init(void)
{
struct i2c_adapter *i2c_adap;
i2c_adap = i2c_get_adapter(0);
nvp6158_client = i2c_new_device(i2c_adap, &ot_info);
if (nvp6158_client == NULL) {
return -1;
}
i2c_put_adapter(i2c_adap);
return 0;
}
static void i2c_client_exit(void)
{
if (nvp6158_client != NULL) {
i2c_unregister_device(nvp6158_client);
nvp6158_client = NULL;
}
}
#endif
static const struct file_operations nvp6158_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = nvp6158_ioctl,
.open = nvp6158_open,
.release = nvp6158_close,
};
static struct miscdevice nvp6158_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "nc_vdec",
.fops = &nvp6158_fops,
};
static int nvp6158_detect_kernel_thread(void *data)
{
unsigned char ch, val_5xf0;
int chip;
(void)data;
while (!kthread_should_stop()) {
down(&nvp6158_lock);
for (chip = 0; chip < 1; chip++) {
for (ch = 0; ch < 4; ch++) {
if (ch_mode_status[0] <= AHD20_SD_H960_2EX_Btype_PAL && ch_mode_status[0] >= AHD20_SD_H960_NT) {
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x05 + ch % 4);
val_5xf0 = gpio_i2c_read(nvp6158_iic_addr[chip], 0xf0);
if (val_5xf0 == 0xff) {
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x05 + ch % 4);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x69, 0x00);
} else {
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x05 + ch % 4);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x69, 0x01);
}
}
}
}
up(&nvp6158_lock);
schedule_timeout_interruptible(msecs_to_jiffies(200));
}
return 0;
}
static int nvp6158_kernel_thread(void *data)
{
NVP6158_INFORMATION_S s_nvp6158_vfmts;
video_equalizer_info_s s_eq_info;
unsigned char prefmt, curfmt, chvloss;
unsigned char ch = 0;
(void)data;
(void)memset_s(&s_nvp6158_vfmts, sizeof(NVP6158_INFORMATION_S), 0, sizeof(NVP6158_INFORMATION_S));
while (!kthread_should_stop()) {
down(&nvp6158_lock);
ch = ch % (nvp6158_cnt * 4);
nvp6158_getvideoloss();
if (chip_id[0] == NVP6158C_R0_ID || chip_id[0] == NVP6158_R0_ID) {
video_fmt_det(ch, &s_nvp6158_vfmts);
curfmt = s_nvp6158_vfmts.curvideofmt[ch];
prefmt = s_nvp6158_vfmts.prevideofmt[ch];
chvloss = s_nvp6158_vfmts.curvideoloss[ch];
if (chvloss == 0x00) {
if (ch_mode_status[ch] != prefmt) {
nvp6158_set_chnmode(ch, curfmt);
nvp6158_set_portmode(0, ch % 4, NVP6158_OUTMODE_1MUX_FHD, ch % 4);
s_eq_info.Ch = ch % 4;
s_eq_info.devnum = ch / 4;
s_eq_info.FmtDef = curfmt;
nvp6158_get_eq_dist(&s_eq_info);
s_nvp6158_vfmts.prevideofmt[ch] = curfmt;
nvp6158_set_equalizer(&s_eq_info);
}
} else {
if (ch_mode_status[ch] != NC_VIVO_CH_FORMATDEF_UNKNOWN) {
nvp6158_set_chnmode(ch, NC_VIVO_CH_FORMATDEF_UNKNOWN);
nvp6158_set_portmode(0, ch % 4, NVP6158_OUTMODE_1MUX_FHD, ch % 4);
}
}
} else {
nvp6168_video_fmt_det(ch, &s_nvp6158_vfmts);
curfmt = s_nvp6158_vfmts.curvideofmt[ch];
prefmt = s_nvp6158_vfmts.prevideofmt[ch];
chvloss = s_nvp6158_vfmts.curvideoloss[ch];
if (chvloss == 0x00) {
if (ch_mode_status[ch] != prefmt) {
nvp6168_set_chnmode(ch, curfmt);
nvp6158_set_portmode(0, ch % 4, NVP6158_OUTMODE_1MUX_FHD, ch % 4);
s_eq_info.Ch = ch % 4;
s_eq_info.devnum = ch / 4;
s_eq_info.FmtDef = curfmt;
nvp6158_get_eq_dist(&s_eq_info);
s_nvp6158_vfmts.prevideofmt[ch] = curfmt;
nvp6168_set_equalizer(&s_eq_info);
}
} else {
if (ch_mode_status[ch] != NC_VIVO_CH_FORMATDEF_UNKNOWN) {
nvp6168_set_chnmode(ch, NC_VIVO_CH_FORMATDEF_UNKNOWN);
nvp6158_set_portmode(0, ch % 4, NVP6158_OUTMODE_1MUX_FHD, ch % 4);
}
}
}
ch++;
up(&nvp6158_lock);
schedule_timeout_interruptible(msecs_to_jiffies(200));
}
return 0;
}
struct proc_dir_entry *nvp6158_dir;
#define NVP6158_PROC_ENTRY "nvp6158"
static int __init nvp6158_module_init(void)
{
int ret = 0;
unsigned int chip = 0;
unsigned char ch = 0;
video_input_auto_detect vin_auto_det;
ret = misc_register(&nvp6158_dev);
if (ret) {
printk("ERROR: could not register nvp6158_dev devices:%#x \n", ret);
return -1;
}
printk("NVP6158/68(C) ex Driver %s COMPILE TIME[%s %s]\n", DRIVER_VER, __DATE__, __TIME__);
#ifdef I2C_INTERNAL
if (i2c_client_init() != 0) {
goto dev_deregister;
}
#endif
/* check Device ID of maximum 4chip on the slave address,
* manage slave address. chip count. */
for (chip = 0; chip < 1; chip++) {
chip_id[chip] = check_id(nvp6158_iic_addr[chip]);
rev_id[chip] = check_rev(nvp6158_iic_addr[chip]);
if ((chip_id[chip] != NVP6158_R0_ID) && (chip_id[chip] != NVP6158C_R0_ID) && (chip_id[chip] != NVP6168_R0_ID) &&
(chip_id[chip] != NVP6168C_R0_ID)) {
} else {
nvp6158_iic_addr[nvp6158_cnt] = nvp6158_iic_addr[chip];
if (nvp6158_cnt < chip) {
nvp6158_iic_addr[chip] = 0xFF;
}
chip_id[nvp6158_cnt] = chip_id[chip];
rev_id[nvp6158_cnt] = rev_id[chip];
nvp6158_cnt++;
}
}
/* initialize semaphore */
sema_init(&nvp6158_lock, 1);
/* initialize common value of AHD */
for (chip = 0; (chip < nvp6158_cnt) && (chip < 4); chip++) {
nvp6158_common_init(chip);
if (chip_id[chip] == NVP6158C_R0_ID || chip_id[chip] == NVP6158_R0_ID) {
nvp6158_additional_for3MoverDef(chip);
} else {
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x01);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x97, 0x00); // CH_RST ON
gpio_i2c_write(nvp6158_iic_addr[chip], 0x97, 0x0f); // CH_RST OFF
gpio_i2c_write(nvp6158_iic_addr[chip], 0x7a, 0x0f); // Clock Auto ON
gpio_i2c_write(nvp6158_iic_addr[chip], 0xca, 0xff); // VCLK_EN, VDO_EN
for (ch = 0; ch < 4; ch++) {
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x05 + ch);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x00, 0xd0);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x05, 0x04);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x08, 0x55);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x47, 0xEE);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x59, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x76, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x77, 0x80);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x78, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x79, 0x11);
gpio_i2c_write(nvp6158_iic_addr[chip], 0xB8, 0xB8); // H_PLL_BYPASS
gpio_i2c_write(nvp6158_iic_addr[chip], 0x7B, 0x11); // v_rst_on
gpio_i2c_write(nvp6158_iic_addr[chip], 0xb9, 0x72);
gpio_i2c_write(nvp6158_iic_addr[chip], 0xB8, 0xB8); // No Video Set
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x00 + ch, 0x10);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x22 + (ch * 0x04), 0x0b);
}
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x13);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x12, 0x04);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x2E, 0x10);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x30, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x77, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x3a, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x3b, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x3c, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x3d, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x3e, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x3f, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x70, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x72, 0x05);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x7A, 0x10);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x07, 0x47);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x59, 0x24);
/* SAM Range */
gpio_i2c_write(nvp6158_iic_addr[chip], 0x74, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x76, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x78, 0x00);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x75, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x77, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x79, 0xff);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x01, 0x0c);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x2f, 0xc8);
// EQ Stage Get
gpio_i2c_write(nvp6158_iic_addr[chip], 0x73, 0x23);
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x09);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x96, 0x03);
gpio_i2c_write(nvp6158_iic_addr[chip], 0xB6, 0x03);
gpio_i2c_write(nvp6158_iic_addr[chip], 0xD6, 0x03);
gpio_i2c_write(nvp6158_iic_addr[chip], 0xF6, 0x03);
/* Audio Default Setting */
gpio_i2c_write(nvp6158_iic_addr[chip], 0xff, 0x01);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x05, 0x09);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x58, 0x02);
gpio_i2c_write(nvp6158_iic_addr[chip], 0x59, 0x00);
}
}
for (ch = 0; ch < nvp6158_cnt * 4; ch++) {
det_mode[ch] = NVP6158_DET_MODE_AUTO;
vin_auto_det.ch = ch % 4;
vin_auto_det.devnum = ch / 4;
if (chip_id[0] == NVP6158C_R0_ID || chip_id[0] == NVP6158_R0_ID) {
video_input_auto_detect_set(&vin_auto_det);
nvp6158_set_chnmode(ch, chnl);
} else {
nvp6168_video_input_auto_detect_set(&vin_auto_det);
nvp6168_set_chnmode(ch, TVI_HD_B_25P);
}
}
for (chip = 0; (chip < nvp6158_cnt) && (chip < 4); chip++) {
if (chip_id[chip] == NVP6158_R0_ID || chip_id[chip] == NVP6168_R0_ID) {
// nvp6158 4 port output: 0~3 port
if (port < NVP6158_OUTMODE_1MUX_BT1120S) {
nvp6158_set_portmode(chip, 0, port, 0);
nvp6158_set_portmode(chip, 1, port, 1);
nvp6158_set_portmode(chip, 2, port, 2);
nvp6158_set_portmode(chip, 3, port, 3);
} else {
nvp6158_set_portmode(chip, 0, port, 0);
nvp6158_set_portmode(chip, 1, port, 1);
}
} else {
// nvp6158C 2 port output: 1~2 port
nvp6158_set_portmode(chip, 1, NVP6158_OUTMODE_2MUX_FHD, 0);
nvp6158_set_portmode(chip, 2, NVP6158_OUTMODE_2MUX_FHD, 1);
}
}
/* initialize Audio
* recmaster, pbmaster, ch_num, samplerate, bits */
if (chip_id[0] == NVP6158C_R0_ID || chip_id[0] == NVP6158_R0_ID) {
audio_init(1, 0, 16, 0, 0);
} else {
nvp6168_audio_init(1, 0, 16, 0, 0);
}
/* create kernel thread for EQ, But Now not used. */
if (kthread == 1) {
nvp6158_kt = kthread_create(nvp6158_kernel_thread, NULL, "nvp6158_kt");
if (!IS_ERR(nvp6158_kt)) {
wake_up_process(nvp6158_kt);
} else {
printk("create nvp6158 watchdog thread failed!!\n");
nvp6158_kt = NULL;
goto i2c_exit;
}
}
if (detect_kthread == 1) {
nvp6158_detect_kt = kthread_create(nvp6158_detect_kernel_thread, NULL, "detect_kt");
if (!IS_ERR(nvp6158_detect_kt)) {
wake_up_process(nvp6158_detect_kt);
} else {
printk("create nvp6158 detect thread failed!!\n");
nvp6158_detect_kt = NULL;
goto stop_nvp6158_kt_thread;
}
}
return 0;
stop_nvp6158_kt_thread:
if (nvp6158_kt != NULL) {
kthread_stop(nvp6158_kt);
}
i2c_exit:
#ifdef I2C_INTERNAL
i2c_client_exit();
dev_deregister:
#endif
misc_deregister(&nvp6158_dev);
return -1;
}
static void __exit nvp6158_module_exit(void)
{
if (nvp6158_detect_kt != NULL) {
kthread_stop(nvp6158_detect_kt);
}
if (nvp6158_kt != NULL) {
kthread_stop(nvp6158_kt);
}
misc_deregister(&nvp6158_dev);
#ifdef I2C_INTERNAL
i2c_client_exit();
#endif
printk("NVP6158(C) ex Driver %s COMPILE TIME[%s %s] removed\n", DRIVER_VER, __DATE__, __TIME__);
}
module_init(nvp6158_module_init);
module_exit(nvp6158_module_exit);
MODULE_LICENSE("GPL");