android13/kernel-5.10/drivers/video/rockchip/vehicle/vehicle_ad_7181.c

609 lines
14 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* vehicle sensor adv7181
*
* Copyright (C) 2022 Rockchip Electronics Co.Ltd
* Authors:
* Zhiqin Wei <wzq@rock-chips.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/sysctl.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/proc_fs.h>
#include <linux/suspend.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/uaccess.h>
#include <linux/of_gpio.h>
#include <linux/of_irq.h>
#include <linux/videodev2.h>
#include "vehicle_cfg.h"
#include "vehicle_main.h"
#include "vehicle_ad.h"
#include "vehicle_ad_7181.h"
enum {
FORCE_PAL_WIDTH = 720,
FORCE_PAL_HEIGHT = 576,
FORCE_NTSC_WIDTH = 720,
FORCE_NTSC_HEIGHT = 480,
FORCE_CIF_OUTPUT_FORMAT = CIF_OUTPUT_FORMAT_420,
};
static struct vehicle_ad_dev *ad7181_g_addev;
static v4l2_std_id std_old = V4L2_STD_NTSC;
#define SENSOR_REGISTER_LEN 1 /* sensor register address bytes*/
#define SENSOR_VALUE_LEN 1 /* sensor register value bytes*/
struct rk_sensor_reg {
unsigned int reg;
unsigned int val;
};
#define ADV7181_STATUS1_REG 0x10
#define ADV7181_STATUS1_IN_LOCK 0x01
#define ADV7181_STATUS1_AUTOD_MASK 0x70
#define ADV7181_STATUS1_AUTOD_NTSM_M_J 0x00
#define ADV7181_STATUS1_AUTOD_NTSC_4_43 0x10
#define ADV7181_STATUS1_AUTOD_PAL_M 0x20
#define ADV7181_STATUS1_AUTOD_PAL_60 0x30
#define ADV7181_STATUS1_AUTOD_PAL_B_G 0x40
#define ADV7181_STATUS1_AUTOD_SECAM 0x50
#define ADV7181_STATUS1_AUTOD_PAL_COMB 0x60
#define ADV7181_STATUS1_AUTOD_SECAM_525 0x70
#define ADV7181_INPUT_CONTROL 0x00
#define ADV7181_INPUT_DEFAULT 0x00
#define ADV7181_INPUT_CVBS_AIN2 0x00
#define ADV7181_INPUT_CVBS_AIN3 0x01
#define ADV7181_INPUT_CVBS_AIN5 0x02
#define ADV7181_INPUT_CVBS_AIN6 0x03
#define ADV7181_INPUT_CVBS_AIN8 0x04
#define ADV7181_INPUT_CVBS_AIN10 0x05
#define ADV7181_INPUT_CVBS_AIN1 0x0B
#define ADV7181_INPUT_CVBS_AIN4 0x0D
#define ADV7181_INPUT_CVBS_AIN7 0x0F
#define ADV7181_INPUT_YPRPB_AIN6_8_10 0x00
#define SEQCMD_END 0xFF000000
#define SensorEnd {SEQCMD_END, 0x00}
#define SENSOR_DG VEHICLE_DG
/* Preview resolution setting*/
static struct rk_sensor_reg sensor_preview_data[] = {
/* autodetect cvbs in ntsc/pal/secam 8-bit 422 encode */
{0x00, 0x0B}, /*cvbs in AIN1*/
{0x04, 0x77},
{0x17, 0x41},
{0x1D, 0x47},
{0x31, 0x02},
{0x3A, 0x17},
{0x3B, 0x81},
{0x3D, 0xA2},
{0x3E, 0x6A},
{0x3F, 0xA0},
{0x86, 0x0B},
{0xF3, 0x01},
{0xF9, 0x03},
{0x0E, 0x80},
{0x52, 0x46},
{0x54, 0x80},
{0x7F, 0xFF},
{0x81, 0x30},
{0x90, 0xC9},
{0x91, 0x40},
{0x92, 0x3C},
{0x93, 0xCA},
{0x94, 0xD5},
{0xB1, 0xFF},
{0xB6, 0x08},
{0xC0, 0x9A},
{0xCF, 0x50},
{0xD0, 0x4E},
{0xD1, 0xB9},
{0xD6, 0xDD},
{0xD7, 0xE2},
{0xE5, 0x51},
{0xF6, 0x3B},
{0x0E, 0x00},
{0x03, 0x4C}, //stream off
{0xDF, 0X46},
{0xC9, 0x04},
{0xC5, 0x81},
{0xC4, 0x34},
{0xBf, 0x02},
{0xB5, 0x83},
{0xB6, 0x00},
{0xaf, 0x03},
{0xae, 0x00},
{0xac, 0x00},
{0xAB, 0x00},
{0xa1, 0xFF},
{0xA2, 0x00},
{0xA3, 0x00},
{0xA4, 0x00},
{0xa5, 0x01},
{0xA6, 0x00},
{0xA6, 0x00},
{0xA7, 0x00},
{0xA8, 0x00},
{0xa0, 0x03},
{0x98, 0X00},
{0x97, 0X00},
{0X90, 0X00},
{0X85, 0X02},
{0x7B, 0x1E},
{0x74, 0x04},
{0x75, 0x01},
{0x76, 0x00},
{0x6B, 0xC0},
{0x67, 0x03},
{0x3C, 0x58},
{0x30, 0x4C},
{0x2E, 0X9F},
{0x12, 0XC0},
{0x10, 0X0D},
{0x05, 0X00},
{0x06, 0X02},
{0x60, 0x01},
SensorEnd
};
static struct rk_sensor_reg sensor_preview_data_yprpb_p[] = {
{0x05, 0x01},
{0x06, 0x06},
{0xc3, 0x56},
{0xc4, 0xb4},
{0x1d, 0x47},
{0x3a, 0x11},
{0x3b, 0x81},
{0x3c, 0x3b},
{0x6b, 0x83},
{0xc9, 0x00},
{0x73, 0x10},
{0x74, 0xa3},
{0x75, 0xe8},
{0x76, 0xfa},
{0x7b, 0x1c},
{0x85, 0x19},
{0x86, 0x0b},
{0xbf, 0x06},
{0xc0, 0x40},
{0xc1, 0xf0},
{0xc2, 0x80},
{0xc5, 0x01},
{0xc9, 0x08},
{0x0e, 0x80},
{0x52, 0x46},
{0x54, 0x80},
{0x57, 0x01},
{0xf6, 0x3b},
{0x0e, 0x00},
{0x67, 0x2f},
{0x03, 0x4C}, //disable out put
SensorEnd
};
static v4l2_std_id adv7181_std_to_v4l2(u8 status1)
{
/* in case V4L2_IN_ST_NO_SIGNAL */
if (!(status1 & ADV7181_STATUS1_IN_LOCK))
return V4L2_STD_UNKNOWN;
switch (status1 & ADV7181_STATUS1_AUTOD_MASK) {
case ADV7181_STATUS1_AUTOD_PAL_M:
case ADV7181_STATUS1_AUTOD_NTSM_M_J:
return V4L2_STD_NTSC;
case ADV7181_STATUS1_AUTOD_NTSC_4_43:
return V4L2_STD_NTSC_443;
case ADV7181_STATUS1_AUTOD_PAL_60:
return V4L2_STD_PAL_60;
case ADV7181_STATUS1_AUTOD_PAL_B_G:
return V4L2_STD_PAL;
case ADV7181_STATUS1_AUTOD_SECAM:
return V4L2_STD_SECAM;
case ADV7181_STATUS1_AUTOD_PAL_COMB:
return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N;
case ADV7181_STATUS1_AUTOD_SECAM_525:
return V4L2_STD_SECAM;
default:
return V4L2_STD_UNKNOWN;
}
}
static u32 adv7181_status_to_v4l2(u8 status1)
{
if (!(status1 & ADV7181_STATUS1_IN_LOCK))
return V4L2_IN_ST_NO_SIGNAL;
return 0;
}
static int adv7181_vehicle_status(struct vehicle_ad_dev *ad,
u32 *status,
v4l2_std_id *std)
{
unsigned char status1 = 0;
status1 = vehicle_generic_sensor_read(ad, ADV7181_STATUS1_REG);
if (status1)
return status1;
if (status)
*status = adv7181_status_to_v4l2(status1);
if (std)
*std = adv7181_std_to_v4l2(status1);
return 0;
}
static void adv7181_reinit_parameter(struct vehicle_ad_dev *ad, v4l2_std_id std)
{
int i;
if (ad7181_g_addev->ad_chl == 0) {
ad->cfg.width = 1024;
ad->cfg.height = 500;
ad->cfg.start_x = 56;
ad->cfg.start_y = 0;
ad->cfg.input_format = CIF_INPUT_FORMAT_YUV;
ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT;
ad->cfg.field_order = 0;
ad->cfg.yuv_order = 1;
ad->cfg.href = 0;
ad->cfg.vsync = 0;
ad->cfg.frame_rate = 60;
ad->cfg.type = V4L2_MBUS_PARALLEL;
ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW |
V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_PCLK_SAMPLE_RISING;
} else if (std == V4L2_STD_PAL) {
ad->cfg.width = FORCE_PAL_WIDTH;
ad->cfg.height = FORCE_PAL_HEIGHT;
ad->cfg.start_x = 0;
ad->cfg.start_y = 0;
ad->cfg.input_format = CIF_INPUT_FORMAT_PAL;
ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT;
ad->cfg.field_order = 0;
ad->cfg.yuv_order = 0;
ad->cfg.href = 0;
ad->cfg.vsync = 0;
ad->cfg.frame_rate = 25;
ad->cfg.type = V4L2_MBUS_PARALLEL;
ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW |
V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_PCLK_SAMPLE_RISING;
} else {
ad->cfg.width = FORCE_NTSC_WIDTH;
ad->cfg.height = FORCE_NTSC_HEIGHT;
ad->cfg.start_x = 0;
ad->cfg.start_y = 0;
ad->cfg.input_format = CIF_INPUT_FORMAT_NTSC;
ad->cfg.output_format = FORCE_CIF_OUTPUT_FORMAT;
ad->cfg.field_order = 0;
ad->cfg.yuv_order = 2;
ad->cfg.href = 0;
ad->cfg.vsync = 0;
ad->cfg.frame_rate = 30;
ad->cfg.type = V4L2_MBUS_PARALLEL;
ad->cfg.mbus_flags = V4L2_MBUS_HSYNC_ACTIVE_LOW |
V4L2_MBUS_VSYNC_ACTIVE_LOW |
V4L2_MBUS_PCLK_SAMPLE_RISING;
}
/* fix crop info from dts config */
for (i = 0; i < 4; i++) {
if ((ad->defrects[i].width == ad->cfg.width) &&
(ad->defrects[i].height == ad->cfg.height)) {
ad->cfg.start_x = ad->defrects[i].crop_x;
ad->cfg.start_y = ad->defrects[i].crop_y;
ad->cfg.width = ad->defrects[i].crop_width;
ad->cfg.height = ad->defrects[i].crop_height;
}
}
SENSOR_DG("size %dx%d, crop(%d,%d)\n",
ad->cfg.width, ad->cfg.height,
ad->cfg.start_x, ad->cfg.start_y);
}
static void adv7181_reg_init(struct vehicle_ad_dev *ad, unsigned char cvstd)
{
struct rk_sensor_reg *sensor;
int i = 0;
unsigned char val[2];
switch (ad->ad_chl) {
case 0:
ad->ad_chl = ADV7181_INPUT_CVBS_AIN1;
break;
case 1:
ad->ad_chl = ADV7181_INPUT_CVBS_AIN6;
break;
case 2:
ad->ad_chl = ADV7181_INPUT_CVBS_AIN8;
break;
case 3:
ad->ad_chl = ADV7181_INPUT_CVBS_AIN10;
break;
case 4:
ad->ad_chl = ADV7181_INPUT_YPRPB_AIN6_8_10;
break;
default:
ad->ad_chl = ADV7181_INPUT_CVBS_AIN1;
}
val[0] = ad->ad_chl;
vehicle_generic_sensor_write(ad, ADV7181_INPUT_CONTROL, val);
if (ad->ad_chl == ADV7181_INPUT_YPRPB_AIN6_8_10) {
SENSOR_DG("%s %d set sensor_preview_data_yprpb_p/p", __func__, __LINE__);
sensor = sensor_preview_data_yprpb_p;
} else {
SENSOR_DG("%s %d set n/p", __func__, __LINE__);
sensor = sensor_preview_data;
}
while ((sensor[i].reg != SEQCMD_END) && (sensor[i].reg != 0xFC000000)) {
if (sensor[i].reg == ADV7181_INPUT_CONTROL) {
SENSOR_DG("%s %d lkg test ad channel = %d\n",
__func__, __LINE__, ad->ad_chl);
} else {
val[0] = sensor[i].val;
vehicle_generic_sensor_write(ad, sensor[i].reg, val);
}
i++;
}
val[0] = ad->ad_chl;
vehicle_generic_sensor_write(ad, ADV7181_INPUT_CONTROL, val);
}
int adv7181_ad_get_cfg(struct vehicle_cfg **cfg)
{
u32 status;
if (!ad7181_g_addev)
return -1;
adv7181_vehicle_status(ad7181_g_addev, &status, NULL);
ad7181_g_addev->cfg.ad_ready = true;
*cfg = &ad7181_g_addev->cfg;
return 0;
}
void adv7181_ad_check_cif_error(struct vehicle_ad_dev *ad, int last_line)
{
SENSOR_DG("%s, last_line %d\n", __func__, last_line);
if (last_line < 1)
return;
ad->cif_error_last_line = last_line;
if (std_old == V4L2_STD_PAL) {
if (last_line == FORCE_NTSC_HEIGHT) {
if (ad->state_check_work.state_check_wq)
queue_delayed_work(
ad->state_check_work.state_check_wq,
&ad->state_check_work.work,
msecs_to_jiffies(0));
}
} else if (std_old == V4L2_STD_NTSC) {
if (last_line == FORCE_PAL_HEIGHT) {
if (ad->state_check_work.state_check_wq)
queue_delayed_work(
ad->state_check_work.state_check_wq,
&ad->state_check_work.work,
msecs_to_jiffies(0));
}
}
}
int adv7181_check_id(struct vehicle_ad_dev *ad)
{
int ret = 0;
int val;
val = vehicle_generic_sensor_read(ad, 0x11);
SENSOR_DG("%s vehicle read 0x11 --> 0x%02x\n", ad->ad_name, val);
if (val != 0x20) {
SENSOR_DG("%s vehicle wrong camera ID, expected 0x20, detected 0x%02x\n",
ad->ad_name, val);
ret = -EINVAL;
}
return ret;
}
static int adv7181_check_std(struct vehicle_ad_dev *ad, v4l2_std_id *std)
{
u32 status = 0;
adv7181_vehicle_status(ad, &status, std);
if (status != 0) { /* No signal */
mdelay(30);
adv7181_vehicle_status(ad, &status, std);
SENSOR_DG("status 0x%x\n", status);
}
return 0;
}
void adv7181_channel_set(struct vehicle_ad_dev *ad, int channel)
{
static int channel_change = 11;
v4l2_std_id std = 0;
ad->ad_chl = channel;
adv7181_reg_init(ad, std);
adv7181_check_std(ad, &std);
adv7181_reinit_parameter(ad, std);
if (channel_change != ad->ad_chl) {
SENSOR_DG("%s %d channel changed now channel = %d old_channel = %d\n",
__func__, __LINE__, ad->ad_chl, channel);
channel_change = ad->ad_chl;
vehicle_ad_stat_change_notify();
}
}
int adv7181_stream(struct vehicle_ad_dev *ad, int value)
{
char val;
if (value)
val = 0x0c; //on
else
val = 0x4c;
SENSOR_DG("stream write 0x%x to reg 0x03\n", val);
vehicle_generic_sensor_write(ad, 0x03, &val);
if (value)
val = 0x47; //on
else
val = 0x87;
SENSOR_DG("stream write 0x%x to reg 0x01d\n", val);
vehicle_generic_sensor_write(ad, 0x1d, &val);
return 0;
}
static void power_on(struct vehicle_ad_dev *ad)
{
/* gpio_direction_output(ad->power, ad->pwr_active); */
if (gpio_is_valid(ad->powerdown)) {
gpio_request(ad->powerdown, "ad_powerdown");
gpio_direction_output(ad->powerdown, !ad->pwdn_active);
/* gpio_set_value(ad->powerdown, !ad->pwdn_active); */
}
if (gpio_is_valid(ad->power)) {
gpio_request(ad->power, "ad_power");
gpio_direction_output(ad->power, ad->pwr_active);
/* gpio_set_value(ad->power, ad->pwr_active); */
}
if (gpio_is_valid(ad->reset)) {
gpio_request(ad->reset, "ad_reset");
gpio_direction_output(ad->reset, 0);
usleep_range(10000, 12000);
gpio_set_value(ad->reset, 1);
usleep_range(10000, 12000);
}
}
static void power_off(struct vehicle_ad_dev *ad)
{
if (gpio_is_valid(ad->powerdown))
gpio_free(ad->powerdown);
if (gpio_is_valid(ad->power))
gpio_free(ad->power);
if (gpio_is_valid(ad->reset))
gpio_free(ad->reset);
}
static void adv7181_check_state_work(struct work_struct *work)
{
struct vehicle_ad_dev *ad;
v4l2_std_id std;
ad = ad7181_g_addev;
if (ad->cif_error_last_line > 0)
ad->cif_error_last_line = 0;
adv7181_check_std(ad, &std);
SENSOR_DG("%s:new std(%llx), std_old(%llx)\n", __func__, std, std_old);
if (std != std_old) {
std_old = std;
adv7181_reinit_parameter(ad, std);
SENSOR_DG("%s:ad signal change notify\n", __func__);
vehicle_ad_stat_change_notify();
}
queue_delayed_work(ad->state_check_work.state_check_wq,
&ad->state_check_work.work, msecs_to_jiffies(3000));
}
int adv7181_ad_deinit(void)
{
struct vehicle_ad_dev *ad;
ad = ad7181_g_addev;
if (!ad)
return -ENODEV;
if (ad->state_check_work.state_check_wq) {
cancel_delayed_work_sync(&ad->state_check_work.work);
flush_delayed_work(&ad->state_check_work.work);
flush_workqueue(ad->state_check_work.state_check_wq);
destroy_workqueue(ad->state_check_work.state_check_wq);
}
if (ad->irq)
free_irq(ad->irq, ad);
power_off(ad);
return 0;
}
int adv7181_ad_init(struct vehicle_ad_dev *ad)
{
v4l2_std_id std = V4L2_STD_NTSC;
if (!ad)
return -1;
ad7181_g_addev = ad;
/* 1. i2c init */
while (ad->adapter == NULL) {
ad->adapter = i2c_get_adapter(ad->i2c_chl);
usleep_range(10000, 12000);
}
if (!i2c_check_functionality(ad->adapter, I2C_FUNC_I2C))
return -EIO;
/* 2. ad power on sequence */
power_on(ad);
/* fix mode */
adv7181_check_std(ad, &std);
std_old = std;
SENSOR_DG("std: %s\n", (std == V4L2_STD_NTSC) ? "ntsc" : "pal");
SENSOR_DG("std_old: %s\n", (std_old == V4L2_STD_NTSC) ? "ntsc" : "pal");
/* 3 .init default format params */
adv7181_reg_init(ad, std);
adv7181_reinit_parameter(ad, std);
vehicle_ad_stat_change_notify();
/* 5. create workqueue to detect signal change */
INIT_DELAYED_WORK(&ad->state_check_work.work, adv7181_check_state_work);
ad->state_check_work.state_check_wq =
create_singlethread_workqueue("vehicle-ad-adv7181");
queue_delayed_work(ad->state_check_work.state_check_wq,
&ad->state_check_work.work, msecs_to_jiffies(100));
return 0;
}