/* Copyright (c), 2001-2022, Shenshu Tech. Co., Ltd. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if LINUX_VERSION_CODE > KERNEL_VERSION(3, 3, 0) #include #else #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef I2C_INTERNAL #include #include #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");