162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * adv7180.c Analog Devices ADV7180 video decoder driver 462306a36Sopenharmony_ci * Copyright (c) 2009 Intel Corporation 562306a36Sopenharmony_ci * Copyright (C) 2013 Cogent Embedded, Inc. 662306a36Sopenharmony_ci * Copyright (C) 2013 Renesas Solutions Corp. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/init.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/i2c.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/of.h> 1662306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1762306a36Sopenharmony_ci#include <linux/videodev2.h> 1862306a36Sopenharmony_ci#include <media/v4l2-ioctl.h> 1962306a36Sopenharmony_ci#include <media/v4l2-event.h> 2062306a36Sopenharmony_ci#include <media/v4l2-device.h> 2162306a36Sopenharmony_ci#include <media/v4l2-ctrls.h> 2262306a36Sopenharmony_ci#include <linux/mutex.h> 2362306a36Sopenharmony_ci#include <linux/delay.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM 0x0 2662306a36Sopenharmony_ci#define ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM_PED 0x1 2762306a36Sopenharmony_ci#define ADV7180_STD_AD_PAL_N_NTSC_J_SECAM 0x2 2862306a36Sopenharmony_ci#define ADV7180_STD_AD_PAL_N_NTSC_M_SECAM 0x3 2962306a36Sopenharmony_ci#define ADV7180_STD_NTSC_J 0x4 3062306a36Sopenharmony_ci#define ADV7180_STD_NTSC_M 0x5 3162306a36Sopenharmony_ci#define ADV7180_STD_PAL60 0x6 3262306a36Sopenharmony_ci#define ADV7180_STD_NTSC_443 0x7 3362306a36Sopenharmony_ci#define ADV7180_STD_PAL_BG 0x8 3462306a36Sopenharmony_ci#define ADV7180_STD_PAL_N 0x9 3562306a36Sopenharmony_ci#define ADV7180_STD_PAL_M 0xa 3662306a36Sopenharmony_ci#define ADV7180_STD_PAL_M_PED 0xb 3762306a36Sopenharmony_ci#define ADV7180_STD_PAL_COMB_N 0xc 3862306a36Sopenharmony_ci#define ADV7180_STD_PAL_COMB_N_PED 0xd 3962306a36Sopenharmony_ci#define ADV7180_STD_PAL_SECAM 0xe 4062306a36Sopenharmony_ci#define ADV7180_STD_PAL_SECAM_PED 0xf 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define ADV7180_REG_INPUT_CONTROL 0x0000 4362306a36Sopenharmony_ci#define ADV7180_INPUT_CONTROL_INSEL_MASK 0x0f 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define ADV7182_REG_INPUT_VIDSEL 0x0002 4662306a36Sopenharmony_ci#define ADV7182_REG_INPUT_RESERVED BIT(2) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define ADV7180_REG_OUTPUT_CONTROL 0x0003 4962306a36Sopenharmony_ci#define ADV7180_REG_EXTENDED_OUTPUT_CONTROL 0x0004 5062306a36Sopenharmony_ci#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define ADV7180_REG_AUTODETECT_ENABLE 0x0007 5362306a36Sopenharmony_ci#define ADV7180_AUTODETECT_DEFAULT 0x7f 5462306a36Sopenharmony_ci/* Contrast */ 5562306a36Sopenharmony_ci#define ADV7180_REG_CON 0x0008 /*Unsigned */ 5662306a36Sopenharmony_ci#define ADV7180_CON_MIN 0 5762306a36Sopenharmony_ci#define ADV7180_CON_DEF 128 5862306a36Sopenharmony_ci#define ADV7180_CON_MAX 255 5962306a36Sopenharmony_ci/* Brightness*/ 6062306a36Sopenharmony_ci#define ADV7180_REG_BRI 0x000a /*Signed */ 6162306a36Sopenharmony_ci#define ADV7180_BRI_MIN -128 6262306a36Sopenharmony_ci#define ADV7180_BRI_DEF 0 6362306a36Sopenharmony_ci#define ADV7180_BRI_MAX 127 6462306a36Sopenharmony_ci/* Hue */ 6562306a36Sopenharmony_ci#define ADV7180_REG_HUE 0x000b /*Signed, inverted */ 6662306a36Sopenharmony_ci#define ADV7180_HUE_MIN -127 6762306a36Sopenharmony_ci#define ADV7180_HUE_DEF 0 6862306a36Sopenharmony_ci#define ADV7180_HUE_MAX 128 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define ADV7180_REG_DEF_VALUE_Y 0x000c 7162306a36Sopenharmony_ci#define ADV7180_DEF_VAL_EN 0x1 7262306a36Sopenharmony_ci#define ADV7180_DEF_VAL_AUTO_EN 0x2 7362306a36Sopenharmony_ci#define ADV7180_REG_CTRL 0x000e 7462306a36Sopenharmony_ci#define ADV7180_CTRL_IRQ_SPACE 0x20 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define ADV7180_REG_PWR_MAN 0x0f 7762306a36Sopenharmony_ci#define ADV7180_PWR_MAN_ON 0x04 7862306a36Sopenharmony_ci#define ADV7180_PWR_MAN_OFF 0x24 7962306a36Sopenharmony_ci#define ADV7180_PWR_MAN_RES 0x80 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define ADV7180_REG_STATUS1 0x0010 8262306a36Sopenharmony_ci#define ADV7180_STATUS1_IN_LOCK 0x01 8362306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_MASK 0x70 8462306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 8562306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 8662306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_PAL_M 0x20 8762306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_PAL_60 0x30 8862306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_PAL_B_G 0x40 8962306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_SECAM 0x50 9062306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_PAL_COMB 0x60 9162306a36Sopenharmony_ci#define ADV7180_STATUS1_AUTOD_SECAM_525 0x70 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define ADV7180_REG_IDENT 0x0011 9462306a36Sopenharmony_ci#define ADV7180_ID_7180 0x18 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define ADV7180_REG_STATUS3 0x0013 9762306a36Sopenharmony_ci#define ADV7180_REG_ANALOG_CLAMP_CTL 0x0014 9862306a36Sopenharmony_ci#define ADV7180_REG_SHAP_FILTER_CTL_1 0x0017 9962306a36Sopenharmony_ci#define ADV7180_REG_CTRL_2 0x001d 10062306a36Sopenharmony_ci#define ADV7180_REG_VSYNC_FIELD_CTL_1 0x0031 10162306a36Sopenharmony_ci#define ADV7180_VSYNC_FIELD_CTL_1_NEWAV 0x12 10262306a36Sopenharmony_ci#define ADV7180_REG_MANUAL_WIN_CTL_1 0x003d 10362306a36Sopenharmony_ci#define ADV7180_REG_MANUAL_WIN_CTL_2 0x003e 10462306a36Sopenharmony_ci#define ADV7180_REG_MANUAL_WIN_CTL_3 0x003f 10562306a36Sopenharmony_ci#define ADV7180_REG_LOCK_CNT 0x0051 10662306a36Sopenharmony_ci#define ADV7180_REG_CVBS_TRIM 0x0052 10762306a36Sopenharmony_ci#define ADV7180_REG_CLAMP_ADJ 0x005a 10862306a36Sopenharmony_ci#define ADV7180_REG_RES_CIR 0x005f 10962306a36Sopenharmony_ci#define ADV7180_REG_DIFF_MODE 0x0060 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci#define ADV7180_REG_ICONF1 0x2040 11262306a36Sopenharmony_ci#define ADV7180_ICONF1_ACTIVE_LOW 0x01 11362306a36Sopenharmony_ci#define ADV7180_ICONF1_PSYNC_ONLY 0x10 11462306a36Sopenharmony_ci#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 11562306a36Sopenharmony_ci/* Saturation */ 11662306a36Sopenharmony_ci#define ADV7180_REG_SD_SAT_CB 0x00e3 /*Unsigned */ 11762306a36Sopenharmony_ci#define ADV7180_REG_SD_SAT_CR 0x00e4 /*Unsigned */ 11862306a36Sopenharmony_ci#define ADV7180_SAT_MIN 0 11962306a36Sopenharmony_ci#define ADV7180_SAT_DEF 128 12062306a36Sopenharmony_ci#define ADV7180_SAT_MAX 255 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define ADV7180_IRQ1_LOCK 0x01 12362306a36Sopenharmony_ci#define ADV7180_IRQ1_UNLOCK 0x02 12462306a36Sopenharmony_ci#define ADV7180_REG_ISR1 0x2042 12562306a36Sopenharmony_ci#define ADV7180_REG_ICR1 0x2043 12662306a36Sopenharmony_ci#define ADV7180_REG_IMR1 0x2044 12762306a36Sopenharmony_ci#define ADV7180_REG_IMR2 0x2048 12862306a36Sopenharmony_ci#define ADV7180_IRQ3_AD_CHANGE 0x08 12962306a36Sopenharmony_ci#define ADV7180_REG_ISR3 0x204A 13062306a36Sopenharmony_ci#define ADV7180_REG_ICR3 0x204B 13162306a36Sopenharmony_ci#define ADV7180_REG_IMR3 0x204C 13262306a36Sopenharmony_ci#define ADV7180_REG_IMR4 0x2050 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define ADV7180_REG_NTSC_V_BIT_END 0x00E6 13562306a36Sopenharmony_ci#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define ADV7180_REG_VPP_SLAVE_ADDR 0xFD 13862306a36Sopenharmony_ci#define ADV7180_REG_CSI_SLAVE_ADDR 0xFE 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define ADV7180_REG_ACE_CTRL1 0x4080 14162306a36Sopenharmony_ci#define ADV7180_REG_ACE_CTRL5 0x4084 14262306a36Sopenharmony_ci#define ADV7180_REG_FLCONTROL 0x40e0 14362306a36Sopenharmony_ci#define ADV7180_FLCONTROL_FL_ENABLE 0x1 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci#define ADV7180_REG_RST_CLAMP 0x809c 14662306a36Sopenharmony_ci#define ADV7180_REG_AGC_ADJ1 0x80b6 14762306a36Sopenharmony_ci#define ADV7180_REG_AGC_ADJ2 0x80c0 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#define ADV7180_CSI_REG_PWRDN 0x00 15062306a36Sopenharmony_ci#define ADV7180_CSI_PWRDN 0x80 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define ADV7180_INPUT_CVBS_AIN1 0x00 15362306a36Sopenharmony_ci#define ADV7180_INPUT_CVBS_AIN2 0x01 15462306a36Sopenharmony_ci#define ADV7180_INPUT_CVBS_AIN3 0x02 15562306a36Sopenharmony_ci#define ADV7180_INPUT_CVBS_AIN4 0x03 15662306a36Sopenharmony_ci#define ADV7180_INPUT_CVBS_AIN5 0x04 15762306a36Sopenharmony_ci#define ADV7180_INPUT_CVBS_AIN6 0x05 15862306a36Sopenharmony_ci#define ADV7180_INPUT_SVIDEO_AIN1_AIN2 0x06 15962306a36Sopenharmony_ci#define ADV7180_INPUT_SVIDEO_AIN3_AIN4 0x07 16062306a36Sopenharmony_ci#define ADV7180_INPUT_SVIDEO_AIN5_AIN6 0x08 16162306a36Sopenharmony_ci#define ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3 0x09 16262306a36Sopenharmony_ci#define ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0a 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN1 0x00 16562306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN2 0x01 16662306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN3 0x02 16762306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN4 0x03 16862306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN5 0x04 16962306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN6 0x05 17062306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN7 0x06 17162306a36Sopenharmony_ci#define ADV7182_INPUT_CVBS_AIN8 0x07 17262306a36Sopenharmony_ci#define ADV7182_INPUT_SVIDEO_AIN1_AIN2 0x08 17362306a36Sopenharmony_ci#define ADV7182_INPUT_SVIDEO_AIN3_AIN4 0x09 17462306a36Sopenharmony_ci#define ADV7182_INPUT_SVIDEO_AIN5_AIN6 0x0a 17562306a36Sopenharmony_ci#define ADV7182_INPUT_SVIDEO_AIN7_AIN8 0x0b 17662306a36Sopenharmony_ci#define ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3 0x0c 17762306a36Sopenharmony_ci#define ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6 0x0d 17862306a36Sopenharmony_ci#define ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2 0x0e 17962306a36Sopenharmony_ci#define ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4 0x0f 18062306a36Sopenharmony_ci#define ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6 0x10 18162306a36Sopenharmony_ci#define ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8 0x11 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci#define ADV7180_DEFAULT_CSI_I2C_ADDR 0x44 18462306a36Sopenharmony_ci#define ADV7180_DEFAULT_VPP_I2C_ADDR 0x42 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci#define V4L2_CID_ADV_FAST_SWITCH (V4L2_CID_USER_ADV7180_BASE + 0x00) 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci/* Initial number of frames to skip to avoid possible garbage */ 18962306a36Sopenharmony_ci#define ADV7180_NUM_OF_SKIP_FRAMES 2 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistruct adv7180_state; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci#define ADV7180_FLAG_RESET_POWERED BIT(0) 19462306a36Sopenharmony_ci#define ADV7180_FLAG_V2 BIT(1) 19562306a36Sopenharmony_ci#define ADV7180_FLAG_MIPI_CSI2 BIT(2) 19662306a36Sopenharmony_ci#define ADV7180_FLAG_I2P BIT(3) 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistruct adv7180_chip_info { 19962306a36Sopenharmony_ci unsigned int flags; 20062306a36Sopenharmony_ci unsigned int valid_input_mask; 20162306a36Sopenharmony_ci int (*set_std)(struct adv7180_state *st, unsigned int std); 20262306a36Sopenharmony_ci int (*select_input)(struct adv7180_state *st, unsigned int input); 20362306a36Sopenharmony_ci int (*init)(struct adv7180_state *state); 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistruct adv7180_state { 20762306a36Sopenharmony_ci struct v4l2_ctrl_handler ctrl_hdl; 20862306a36Sopenharmony_ci struct v4l2_subdev sd; 20962306a36Sopenharmony_ci struct media_pad pad; 21062306a36Sopenharmony_ci struct mutex mutex; /* mutual excl. when accessing chip */ 21162306a36Sopenharmony_ci int irq; 21262306a36Sopenharmony_ci struct gpio_desc *pwdn_gpio; 21362306a36Sopenharmony_ci struct gpio_desc *rst_gpio; 21462306a36Sopenharmony_ci v4l2_std_id curr_norm; 21562306a36Sopenharmony_ci bool powered; 21662306a36Sopenharmony_ci bool streaming; 21762306a36Sopenharmony_ci u8 input; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci struct i2c_client *client; 22062306a36Sopenharmony_ci unsigned int register_page; 22162306a36Sopenharmony_ci struct i2c_client *csi_client; 22262306a36Sopenharmony_ci struct i2c_client *vpp_client; 22362306a36Sopenharmony_ci const struct adv7180_chip_info *chip_info; 22462306a36Sopenharmony_ci enum v4l2_field field; 22562306a36Sopenharmony_ci bool force_bt656_4; 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci#define to_adv7180_sd(_ctrl) (&container_of(_ctrl->handler, \ 22862306a36Sopenharmony_ci struct adv7180_state, \ 22962306a36Sopenharmony_ci ctrl_hdl)->sd) 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_cistatic int adv7180_select_page(struct adv7180_state *state, unsigned int page) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci if (state->register_page != page) { 23462306a36Sopenharmony_ci i2c_smbus_write_byte_data(state->client, ADV7180_REG_CTRL, 23562306a36Sopenharmony_ci page); 23662306a36Sopenharmony_ci state->register_page = page; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci return 0; 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cistatic int adv7180_write(struct adv7180_state *state, unsigned int reg, 24362306a36Sopenharmony_ci unsigned int value) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci lockdep_assert_held(&state->mutex); 24662306a36Sopenharmony_ci adv7180_select_page(state, reg >> 8); 24762306a36Sopenharmony_ci return i2c_smbus_write_byte_data(state->client, reg & 0xff, value); 24862306a36Sopenharmony_ci} 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_cistatic int adv7180_read(struct adv7180_state *state, unsigned int reg) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci lockdep_assert_held(&state->mutex); 25362306a36Sopenharmony_ci adv7180_select_page(state, reg >> 8); 25462306a36Sopenharmony_ci return i2c_smbus_read_byte_data(state->client, reg & 0xff); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_cistatic int adv7180_csi_write(struct adv7180_state *state, unsigned int reg, 25862306a36Sopenharmony_ci unsigned int value) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci return i2c_smbus_write_byte_data(state->csi_client, reg, value); 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic int adv7180_set_video_standard(struct adv7180_state *state, 26462306a36Sopenharmony_ci unsigned int std) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return state->chip_info->set_std(state, std); 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int adv7180_vpp_write(struct adv7180_state *state, unsigned int reg, 27062306a36Sopenharmony_ci unsigned int value) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci return i2c_smbus_write_byte_data(state->vpp_client, reg, value); 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic v4l2_std_id adv7180_std_to_v4l2(u8 status1) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci /* in case V4L2_IN_ST_NO_SIGNAL */ 27862306a36Sopenharmony_ci if (!(status1 & ADV7180_STATUS1_IN_LOCK)) 27962306a36Sopenharmony_ci return V4L2_STD_UNKNOWN; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { 28262306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_NTSM_M_J: 28362306a36Sopenharmony_ci return V4L2_STD_NTSC; 28462306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_NTSC_4_43: 28562306a36Sopenharmony_ci return V4L2_STD_NTSC_443; 28662306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_PAL_M: 28762306a36Sopenharmony_ci return V4L2_STD_PAL_M; 28862306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_PAL_60: 28962306a36Sopenharmony_ci return V4L2_STD_PAL_60; 29062306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_PAL_B_G: 29162306a36Sopenharmony_ci return V4L2_STD_PAL; 29262306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_SECAM: 29362306a36Sopenharmony_ci return V4L2_STD_SECAM; 29462306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_PAL_COMB: 29562306a36Sopenharmony_ci return V4L2_STD_PAL_Nc | V4L2_STD_PAL_N; 29662306a36Sopenharmony_ci case ADV7180_STATUS1_AUTOD_SECAM_525: 29762306a36Sopenharmony_ci return V4L2_STD_SECAM; 29862306a36Sopenharmony_ci default: 29962306a36Sopenharmony_ci return V4L2_STD_UNKNOWN; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci} 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_cistatic int v4l2_std_to_adv7180(v4l2_std_id std) 30462306a36Sopenharmony_ci{ 30562306a36Sopenharmony_ci if (std == V4L2_STD_PAL_60) 30662306a36Sopenharmony_ci return ADV7180_STD_PAL60; 30762306a36Sopenharmony_ci if (std == V4L2_STD_NTSC_443) 30862306a36Sopenharmony_ci return ADV7180_STD_NTSC_443; 30962306a36Sopenharmony_ci if (std == V4L2_STD_PAL_N) 31062306a36Sopenharmony_ci return ADV7180_STD_PAL_N; 31162306a36Sopenharmony_ci if (std == V4L2_STD_PAL_M) 31262306a36Sopenharmony_ci return ADV7180_STD_PAL_M; 31362306a36Sopenharmony_ci if (std == V4L2_STD_PAL_Nc) 31462306a36Sopenharmony_ci return ADV7180_STD_PAL_COMB_N; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (std & V4L2_STD_PAL) 31762306a36Sopenharmony_ci return ADV7180_STD_PAL_BG; 31862306a36Sopenharmony_ci if (std & V4L2_STD_NTSC) 31962306a36Sopenharmony_ci return ADV7180_STD_NTSC_M; 32062306a36Sopenharmony_ci if (std & V4L2_STD_SECAM) 32162306a36Sopenharmony_ci return ADV7180_STD_PAL_SECAM; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return -EINVAL; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic u32 adv7180_status_to_v4l2(u8 status1) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci if (!(status1 & ADV7180_STATUS1_IN_LOCK)) 32962306a36Sopenharmony_ci return V4L2_IN_ST_NO_SIGNAL; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci return 0; 33262306a36Sopenharmony_ci} 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_cistatic int __adv7180_status(struct adv7180_state *state, u32 *status, 33562306a36Sopenharmony_ci v4l2_std_id *std) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci int status1 = adv7180_read(state, ADV7180_REG_STATUS1); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci if (status1 < 0) 34062306a36Sopenharmony_ci return status1; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci if (status) 34362306a36Sopenharmony_ci *status = adv7180_status_to_v4l2(status1); 34462306a36Sopenharmony_ci if (std) 34562306a36Sopenharmony_ci *std = adv7180_std_to_v4l2(status1); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic inline struct adv7180_state *to_state(struct v4l2_subdev *sd) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci return container_of(sd, struct adv7180_state, sd); 35362306a36Sopenharmony_ci} 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_cistatic int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) 35662306a36Sopenharmony_ci{ 35762306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 35862306a36Sopenharmony_ci int err = mutex_lock_interruptible(&state->mutex); 35962306a36Sopenharmony_ci if (err) 36062306a36Sopenharmony_ci return err; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci if (state->streaming) { 36362306a36Sopenharmony_ci err = -EBUSY; 36462306a36Sopenharmony_ci goto unlock; 36562306a36Sopenharmony_ci } 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci err = adv7180_set_video_standard(state, 36862306a36Sopenharmony_ci ADV7180_STD_AD_PAL_BG_NTSC_J_SECAM); 36962306a36Sopenharmony_ci if (err) 37062306a36Sopenharmony_ci goto unlock; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci msleep(100); 37362306a36Sopenharmony_ci __adv7180_status(state, NULL, std); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci err = v4l2_std_to_adv7180(state->curr_norm); 37662306a36Sopenharmony_ci if (err < 0) 37762306a36Sopenharmony_ci goto unlock; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci err = adv7180_set_video_standard(state, err); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ciunlock: 38262306a36Sopenharmony_ci mutex_unlock(&state->mutex); 38362306a36Sopenharmony_ci return err; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, 38762306a36Sopenharmony_ci u32 output, u32 config) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 39062306a36Sopenharmony_ci int ret = mutex_lock_interruptible(&state->mutex); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (ret) 39362306a36Sopenharmony_ci return ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci if (input > 31 || !(BIT(input) & state->chip_info->valid_input_mask)) { 39662306a36Sopenharmony_ci ret = -EINVAL; 39762306a36Sopenharmony_ci goto out; 39862306a36Sopenharmony_ci } 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = state->chip_info->select_input(state, input); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (ret == 0) 40362306a36Sopenharmony_ci state->input = input; 40462306a36Sopenharmony_ciout: 40562306a36Sopenharmony_ci mutex_unlock(&state->mutex); 40662306a36Sopenharmony_ci return ret; 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_cistatic int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) 41062306a36Sopenharmony_ci{ 41162306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 41262306a36Sopenharmony_ci int ret = mutex_lock_interruptible(&state->mutex); 41362306a36Sopenharmony_ci if (ret) 41462306a36Sopenharmony_ci return ret; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci ret = __adv7180_status(state, status, NULL); 41762306a36Sopenharmony_ci mutex_unlock(&state->mutex); 41862306a36Sopenharmony_ci return ret; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int adv7180_program_std(struct adv7180_state *state) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci int ret; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = v4l2_std_to_adv7180(state->curr_norm); 42662306a36Sopenharmony_ci if (ret < 0) 42762306a36Sopenharmony_ci return ret; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci ret = adv7180_set_video_standard(state, ret); 43062306a36Sopenharmony_ci if (ret < 0) 43162306a36Sopenharmony_ci return ret; 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 43862306a36Sopenharmony_ci int ret = mutex_lock_interruptible(&state->mutex); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci if (ret) 44162306a36Sopenharmony_ci return ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Make sure we can support this std */ 44462306a36Sopenharmony_ci ret = v4l2_std_to_adv7180(std); 44562306a36Sopenharmony_ci if (ret < 0) 44662306a36Sopenharmony_ci goto out; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci state->curr_norm = std; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci ret = adv7180_program_std(state); 45162306a36Sopenharmony_ciout: 45262306a36Sopenharmony_ci mutex_unlock(&state->mutex); 45362306a36Sopenharmony_ci return ret; 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int adv7180_g_std(struct v4l2_subdev *sd, v4l2_std_id *norm) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci *norm = state->curr_norm; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return 0; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int adv7180_g_frame_interval(struct v4l2_subdev *sd, 46662306a36Sopenharmony_ci struct v4l2_subdev_frame_interval *fi) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (state->curr_norm & V4L2_STD_525_60) { 47162306a36Sopenharmony_ci fi->interval.numerator = 1001; 47262306a36Sopenharmony_ci fi->interval.denominator = 30000; 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci fi->interval.numerator = 1; 47562306a36Sopenharmony_ci fi->interval.denominator = 25; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci return 0; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic void adv7180_set_power_pin(struct adv7180_state *state, bool on) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci if (!state->pwdn_gpio) 48462306a36Sopenharmony_ci return; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci if (on) { 48762306a36Sopenharmony_ci gpiod_set_value_cansleep(state->pwdn_gpio, 0); 48862306a36Sopenharmony_ci usleep_range(5000, 10000); 48962306a36Sopenharmony_ci } else { 49062306a36Sopenharmony_ci gpiod_set_value_cansleep(state->pwdn_gpio, 1); 49162306a36Sopenharmony_ci } 49262306a36Sopenharmony_ci} 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_cistatic void adv7180_set_reset_pin(struct adv7180_state *state, bool on) 49562306a36Sopenharmony_ci{ 49662306a36Sopenharmony_ci if (!state->rst_gpio) 49762306a36Sopenharmony_ci return; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (on) { 50062306a36Sopenharmony_ci gpiod_set_value_cansleep(state->rst_gpio, 1); 50162306a36Sopenharmony_ci } else { 50262306a36Sopenharmony_ci gpiod_set_value_cansleep(state->rst_gpio, 0); 50362306a36Sopenharmony_ci usleep_range(5000, 10000); 50462306a36Sopenharmony_ci } 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int adv7180_set_power(struct adv7180_state *state, bool on) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci u8 val; 51062306a36Sopenharmony_ci int ret; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (on) 51362306a36Sopenharmony_ci val = ADV7180_PWR_MAN_ON; 51462306a36Sopenharmony_ci else 51562306a36Sopenharmony_ci val = ADV7180_PWR_MAN_OFF; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_PWR_MAN, val); 51862306a36Sopenharmony_ci if (ret) 51962306a36Sopenharmony_ci return ret; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { 52262306a36Sopenharmony_ci if (on) { 52362306a36Sopenharmony_ci adv7180_csi_write(state, 0xDE, 0x02); 52462306a36Sopenharmony_ci adv7180_csi_write(state, 0xD2, 0xF7); 52562306a36Sopenharmony_ci adv7180_csi_write(state, 0xD8, 0x65); 52662306a36Sopenharmony_ci adv7180_csi_write(state, 0xE0, 0x09); 52762306a36Sopenharmony_ci adv7180_csi_write(state, 0x2C, 0x00); 52862306a36Sopenharmony_ci if (state->field == V4L2_FIELD_NONE) 52962306a36Sopenharmony_ci adv7180_csi_write(state, 0x1D, 0x80); 53062306a36Sopenharmony_ci adv7180_csi_write(state, 0x00, 0x00); 53162306a36Sopenharmony_ci } else { 53262306a36Sopenharmony_ci adv7180_csi_write(state, 0x00, 0x80); 53362306a36Sopenharmony_ci } 53462306a36Sopenharmony_ci } 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci return 0; 53762306a36Sopenharmony_ci} 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_cistatic int adv7180_s_power(struct v4l2_subdev *sd, int on) 54062306a36Sopenharmony_ci{ 54162306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 54262306a36Sopenharmony_ci int ret; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci ret = mutex_lock_interruptible(&state->mutex); 54562306a36Sopenharmony_ci if (ret) 54662306a36Sopenharmony_ci return ret; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci ret = adv7180_set_power(state, on); 54962306a36Sopenharmony_ci if (ret == 0) 55062306a36Sopenharmony_ci state->powered = on; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci mutex_unlock(&state->mutex); 55362306a36Sopenharmony_ci return ret; 55462306a36Sopenharmony_ci} 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic const char * const test_pattern_menu[] = { 55762306a36Sopenharmony_ci "Single color", 55862306a36Sopenharmony_ci "Color bars", 55962306a36Sopenharmony_ci "Luma ramp", 56062306a36Sopenharmony_ci "Boundary box", 56162306a36Sopenharmony_ci "Disable", 56262306a36Sopenharmony_ci}; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int adv7180_test_pattern(struct adv7180_state *state, int value) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci unsigned int reg = 0; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci /* Map menu value into register value */ 56962306a36Sopenharmony_ci if (value < 3) 57062306a36Sopenharmony_ci reg = value; 57162306a36Sopenharmony_ci if (value == 3) 57262306a36Sopenharmony_ci reg = 5; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_ANALOG_CLAMP_CTL, reg); 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (value == ARRAY_SIZE(test_pattern_menu) - 1) { 57762306a36Sopenharmony_ci reg = adv7180_read(state, ADV7180_REG_DEF_VALUE_Y); 57862306a36Sopenharmony_ci reg &= ~ADV7180_DEF_VAL_EN; 57962306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_DEF_VALUE_Y, reg); 58062306a36Sopenharmony_ci return 0; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci reg = adv7180_read(state, ADV7180_REG_DEF_VALUE_Y); 58462306a36Sopenharmony_ci reg |= ADV7180_DEF_VAL_EN | ADV7180_DEF_VAL_AUTO_EN; 58562306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_DEF_VALUE_Y, reg); 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci return 0; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int adv7180_s_ctrl(struct v4l2_ctrl *ctrl) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct v4l2_subdev *sd = to_adv7180_sd(ctrl); 59362306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 59462306a36Sopenharmony_ci int ret = mutex_lock_interruptible(&state->mutex); 59562306a36Sopenharmony_ci int val; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci if (ret) 59862306a36Sopenharmony_ci return ret; 59962306a36Sopenharmony_ci val = ctrl->val; 60062306a36Sopenharmony_ci switch (ctrl->id) { 60162306a36Sopenharmony_ci case V4L2_CID_BRIGHTNESS: 60262306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_BRI, val); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case V4L2_CID_HUE: 60562306a36Sopenharmony_ci /*Hue is inverted according to HSL chart */ 60662306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_HUE, -val); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case V4L2_CID_CONTRAST: 60962306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_CON, val); 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case V4L2_CID_SATURATION: 61262306a36Sopenharmony_ci /* 61362306a36Sopenharmony_ci *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE 61462306a36Sopenharmony_ci *Let's not confuse the user, everybody understands saturation 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_SD_SAT_CB, val); 61762306a36Sopenharmony_ci if (ret < 0) 61862306a36Sopenharmony_ci break; 61962306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_SD_SAT_CR, val); 62062306a36Sopenharmony_ci break; 62162306a36Sopenharmony_ci case V4L2_CID_ADV_FAST_SWITCH: 62262306a36Sopenharmony_ci if (ctrl->val) { 62362306a36Sopenharmony_ci /* ADI required write */ 62462306a36Sopenharmony_ci adv7180_write(state, 0x80d9, 0x44); 62562306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_FLCONTROL, 62662306a36Sopenharmony_ci ADV7180_FLCONTROL_FL_ENABLE); 62762306a36Sopenharmony_ci } else { 62862306a36Sopenharmony_ci /* ADI required write */ 62962306a36Sopenharmony_ci adv7180_write(state, 0x80d9, 0xc4); 63062306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_FLCONTROL, 0x00); 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci case V4L2_CID_TEST_PATTERN: 63462306a36Sopenharmony_ci ret = adv7180_test_pattern(state, val); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci default: 63762306a36Sopenharmony_ci ret = -EINVAL; 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci mutex_unlock(&state->mutex); 64162306a36Sopenharmony_ci return ret; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops adv7180_ctrl_ops = { 64562306a36Sopenharmony_ci .s_ctrl = adv7180_s_ctrl, 64662306a36Sopenharmony_ci}; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic const struct v4l2_ctrl_config adv7180_ctrl_fast_switch = { 64962306a36Sopenharmony_ci .ops = &adv7180_ctrl_ops, 65062306a36Sopenharmony_ci .id = V4L2_CID_ADV_FAST_SWITCH, 65162306a36Sopenharmony_ci .name = "Fast Switching", 65262306a36Sopenharmony_ci .type = V4L2_CTRL_TYPE_BOOLEAN, 65362306a36Sopenharmony_ci .min = 0, 65462306a36Sopenharmony_ci .max = 1, 65562306a36Sopenharmony_ci .step = 1, 65662306a36Sopenharmony_ci}; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int adv7180_init_controls(struct adv7180_state *state) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci v4l2_ctrl_handler_init(&state->ctrl_hdl, 4); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 66362306a36Sopenharmony_ci V4L2_CID_BRIGHTNESS, ADV7180_BRI_MIN, 66462306a36Sopenharmony_ci ADV7180_BRI_MAX, 1, ADV7180_BRI_DEF); 66562306a36Sopenharmony_ci v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 66662306a36Sopenharmony_ci V4L2_CID_CONTRAST, ADV7180_CON_MIN, 66762306a36Sopenharmony_ci ADV7180_CON_MAX, 1, ADV7180_CON_DEF); 66862306a36Sopenharmony_ci v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 66962306a36Sopenharmony_ci V4L2_CID_SATURATION, ADV7180_SAT_MIN, 67062306a36Sopenharmony_ci ADV7180_SAT_MAX, 1, ADV7180_SAT_DEF); 67162306a36Sopenharmony_ci v4l2_ctrl_new_std(&state->ctrl_hdl, &adv7180_ctrl_ops, 67262306a36Sopenharmony_ci V4L2_CID_HUE, ADV7180_HUE_MIN, 67362306a36Sopenharmony_ci ADV7180_HUE_MAX, 1, ADV7180_HUE_DEF); 67462306a36Sopenharmony_ci v4l2_ctrl_new_custom(&state->ctrl_hdl, &adv7180_ctrl_fast_switch, NULL); 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_ci v4l2_ctrl_new_std_menu_items(&state->ctrl_hdl, &adv7180_ctrl_ops, 67762306a36Sopenharmony_ci V4L2_CID_TEST_PATTERN, 67862306a36Sopenharmony_ci ARRAY_SIZE(test_pattern_menu) - 1, 67962306a36Sopenharmony_ci 0, ARRAY_SIZE(test_pattern_menu) - 1, 68062306a36Sopenharmony_ci test_pattern_menu); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci state->sd.ctrl_handler = &state->ctrl_hdl; 68362306a36Sopenharmony_ci if (state->ctrl_hdl.error) { 68462306a36Sopenharmony_ci int err = state->ctrl_hdl.error; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci v4l2_ctrl_handler_free(&state->ctrl_hdl); 68762306a36Sopenharmony_ci return err; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci v4l2_ctrl_handler_setup(&state->ctrl_hdl); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci return 0; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_cistatic void adv7180_exit_controls(struct adv7180_state *state) 69462306a36Sopenharmony_ci{ 69562306a36Sopenharmony_ci v4l2_ctrl_handler_free(&state->ctrl_hdl); 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int adv7180_enum_mbus_code(struct v4l2_subdev *sd, 69962306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 70062306a36Sopenharmony_ci struct v4l2_subdev_mbus_code_enum *code) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci if (code->index != 0) 70362306a36Sopenharmony_ci return -EINVAL; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci code->code = MEDIA_BUS_FMT_UYVY8_2X8; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return 0; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int adv7180_mbus_fmt(struct v4l2_subdev *sd, 71162306a36Sopenharmony_ci struct v4l2_mbus_framefmt *fmt) 71262306a36Sopenharmony_ci{ 71362306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci fmt->code = MEDIA_BUS_FMT_UYVY8_2X8; 71662306a36Sopenharmony_ci fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; 71762306a36Sopenharmony_ci fmt->width = 720; 71862306a36Sopenharmony_ci fmt->height = state->curr_norm & V4L2_STD_525_60 ? 480 : 576; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (state->field == V4L2_FIELD_ALTERNATE) 72162306a36Sopenharmony_ci fmt->height /= 2; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci return 0; 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cistatic int adv7180_set_field_mode(struct adv7180_state *state) 72762306a36Sopenharmony_ci{ 72862306a36Sopenharmony_ci if (!(state->chip_info->flags & ADV7180_FLAG_I2P)) 72962306a36Sopenharmony_ci return 0; 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (state->field == V4L2_FIELD_NONE) { 73262306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { 73362306a36Sopenharmony_ci adv7180_csi_write(state, 0x01, 0x20); 73462306a36Sopenharmony_ci adv7180_csi_write(state, 0x02, 0x28); 73562306a36Sopenharmony_ci adv7180_csi_write(state, 0x03, 0x38); 73662306a36Sopenharmony_ci adv7180_csi_write(state, 0x04, 0x30); 73762306a36Sopenharmony_ci adv7180_csi_write(state, 0x05, 0x30); 73862306a36Sopenharmony_ci adv7180_csi_write(state, 0x06, 0x80); 73962306a36Sopenharmony_ci adv7180_csi_write(state, 0x07, 0x70); 74062306a36Sopenharmony_ci adv7180_csi_write(state, 0x08, 0x50); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci adv7180_vpp_write(state, 0xa3, 0x00); 74362306a36Sopenharmony_ci adv7180_vpp_write(state, 0x5b, 0x00); 74462306a36Sopenharmony_ci adv7180_vpp_write(state, 0x55, 0x80); 74562306a36Sopenharmony_ci } else { 74662306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { 74762306a36Sopenharmony_ci adv7180_csi_write(state, 0x01, 0x18); 74862306a36Sopenharmony_ci adv7180_csi_write(state, 0x02, 0x18); 74962306a36Sopenharmony_ci adv7180_csi_write(state, 0x03, 0x30); 75062306a36Sopenharmony_ci adv7180_csi_write(state, 0x04, 0x20); 75162306a36Sopenharmony_ci adv7180_csi_write(state, 0x05, 0x28); 75262306a36Sopenharmony_ci adv7180_csi_write(state, 0x06, 0x40); 75362306a36Sopenharmony_ci adv7180_csi_write(state, 0x07, 0x58); 75462306a36Sopenharmony_ci adv7180_csi_write(state, 0x08, 0x30); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci adv7180_vpp_write(state, 0xa3, 0x70); 75762306a36Sopenharmony_ci adv7180_vpp_write(state, 0x5b, 0x80); 75862306a36Sopenharmony_ci adv7180_vpp_write(state, 0x55, 0x00); 75962306a36Sopenharmony_ci } 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return 0; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic int adv7180_get_pad_format(struct v4l2_subdev *sd, 76562306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 76662306a36Sopenharmony_ci struct v4l2_subdev_format *format) 76762306a36Sopenharmony_ci{ 76862306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_TRY) { 77162306a36Sopenharmony_ci format->format = *v4l2_subdev_get_try_format(sd, sd_state, 0); 77262306a36Sopenharmony_ci } else { 77362306a36Sopenharmony_ci adv7180_mbus_fmt(sd, &format->format); 77462306a36Sopenharmony_ci format->format.field = state->field; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int adv7180_set_pad_format(struct v4l2_subdev *sd, 78162306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state, 78262306a36Sopenharmony_ci struct v4l2_subdev_format *format) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 78562306a36Sopenharmony_ci struct v4l2_mbus_framefmt *framefmt; 78662306a36Sopenharmony_ci int ret; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci switch (format->format.field) { 78962306a36Sopenharmony_ci case V4L2_FIELD_NONE: 79062306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_I2P) 79162306a36Sopenharmony_ci break; 79262306a36Sopenharmony_ci fallthrough; 79362306a36Sopenharmony_ci default: 79462306a36Sopenharmony_ci format->format.field = V4L2_FIELD_ALTERNATE; 79562306a36Sopenharmony_ci break; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci ret = adv7180_mbus_fmt(sd, &format->format); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (format->which == V4L2_SUBDEV_FORMAT_ACTIVE) { 80162306a36Sopenharmony_ci if (state->field != format->format.field) { 80262306a36Sopenharmony_ci state->field = format->format.field; 80362306a36Sopenharmony_ci adv7180_set_power(state, false); 80462306a36Sopenharmony_ci adv7180_set_field_mode(state); 80562306a36Sopenharmony_ci adv7180_set_power(state, true); 80662306a36Sopenharmony_ci } 80762306a36Sopenharmony_ci } else { 80862306a36Sopenharmony_ci framefmt = v4l2_subdev_get_try_format(sd, sd_state, 0); 80962306a36Sopenharmony_ci *framefmt = format->format; 81062306a36Sopenharmony_ci } 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci return ret; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic int adv7180_init_cfg(struct v4l2_subdev *sd, 81662306a36Sopenharmony_ci struct v4l2_subdev_state *sd_state) 81762306a36Sopenharmony_ci{ 81862306a36Sopenharmony_ci struct v4l2_subdev_format fmt = { 81962306a36Sopenharmony_ci .which = sd_state ? V4L2_SUBDEV_FORMAT_TRY 82062306a36Sopenharmony_ci : V4L2_SUBDEV_FORMAT_ACTIVE, 82162306a36Sopenharmony_ci }; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return adv7180_set_pad_format(sd, sd_state, &fmt); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic int adv7180_get_mbus_config(struct v4l2_subdev *sd, 82762306a36Sopenharmony_ci unsigned int pad, 82862306a36Sopenharmony_ci struct v4l2_mbus_config *cfg) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { 83362306a36Sopenharmony_ci cfg->type = V4L2_MBUS_CSI2_DPHY; 83462306a36Sopenharmony_ci cfg->bus.mipi_csi2.num_data_lanes = 1; 83562306a36Sopenharmony_ci cfg->bus.mipi_csi2.flags = 0; 83662306a36Sopenharmony_ci } else { 83762306a36Sopenharmony_ci /* 83862306a36Sopenharmony_ci * The ADV7180 sensor supports BT.601/656 output modes. 83962306a36Sopenharmony_ci * The BT.656 is default and not yet configurable by s/w. 84062306a36Sopenharmony_ci */ 84162306a36Sopenharmony_ci cfg->bus.parallel.flags = V4L2_MBUS_MASTER | 84262306a36Sopenharmony_ci V4L2_MBUS_PCLK_SAMPLE_RISING | 84362306a36Sopenharmony_ci V4L2_MBUS_DATA_ACTIVE_HIGH; 84462306a36Sopenharmony_ci cfg->type = V4L2_MBUS_BT656; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci return 0; 84862306a36Sopenharmony_ci} 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_cistatic int adv7180_get_skip_frames(struct v4l2_subdev *sd, u32 *frames) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci *frames = ADV7180_NUM_OF_SKIP_FRAMES; 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci return 0; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_cistatic int adv7180_g_pixelaspect(struct v4l2_subdev *sd, struct v4l2_fract *aspect) 85862306a36Sopenharmony_ci{ 85962306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (state->curr_norm & V4L2_STD_525_60) { 86262306a36Sopenharmony_ci aspect->numerator = 11; 86362306a36Sopenharmony_ci aspect->denominator = 10; 86462306a36Sopenharmony_ci } else { 86562306a36Sopenharmony_ci aspect->numerator = 54; 86662306a36Sopenharmony_ci aspect->denominator = 59; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci} 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cistatic int adv7180_g_tvnorms(struct v4l2_subdev *sd, v4l2_std_id *norm) 87362306a36Sopenharmony_ci{ 87462306a36Sopenharmony_ci *norm = V4L2_STD_ALL; 87562306a36Sopenharmony_ci return 0; 87662306a36Sopenharmony_ci} 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cistatic int adv7180_s_stream(struct v4l2_subdev *sd, int enable) 87962306a36Sopenharmony_ci{ 88062306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 88162306a36Sopenharmony_ci int ret; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci /* It's always safe to stop streaming, no need to take the lock */ 88462306a36Sopenharmony_ci if (!enable) { 88562306a36Sopenharmony_ci state->streaming = enable; 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci } 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci /* Must wait until querystd released the lock */ 89062306a36Sopenharmony_ci ret = mutex_lock_interruptible(&state->mutex); 89162306a36Sopenharmony_ci if (ret) 89262306a36Sopenharmony_ci return ret; 89362306a36Sopenharmony_ci state->streaming = enable; 89462306a36Sopenharmony_ci mutex_unlock(&state->mutex); 89562306a36Sopenharmony_ci return 0; 89662306a36Sopenharmony_ci} 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_cistatic int adv7180_subscribe_event(struct v4l2_subdev *sd, 89962306a36Sopenharmony_ci struct v4l2_fh *fh, 90062306a36Sopenharmony_ci struct v4l2_event_subscription *sub) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci switch (sub->type) { 90362306a36Sopenharmony_ci case V4L2_EVENT_SOURCE_CHANGE: 90462306a36Sopenharmony_ci return v4l2_src_change_event_subdev_subscribe(sd, fh, sub); 90562306a36Sopenharmony_ci case V4L2_EVENT_CTRL: 90662306a36Sopenharmony_ci return v4l2_ctrl_subdev_subscribe_event(sd, fh, sub); 90762306a36Sopenharmony_ci default: 90862306a36Sopenharmony_ci return -EINVAL; 90962306a36Sopenharmony_ci } 91062306a36Sopenharmony_ci} 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_cistatic const struct v4l2_subdev_video_ops adv7180_video_ops = { 91362306a36Sopenharmony_ci .s_std = adv7180_s_std, 91462306a36Sopenharmony_ci .g_std = adv7180_g_std, 91562306a36Sopenharmony_ci .g_frame_interval = adv7180_g_frame_interval, 91662306a36Sopenharmony_ci .querystd = adv7180_querystd, 91762306a36Sopenharmony_ci .g_input_status = adv7180_g_input_status, 91862306a36Sopenharmony_ci .s_routing = adv7180_s_routing, 91962306a36Sopenharmony_ci .g_pixelaspect = adv7180_g_pixelaspect, 92062306a36Sopenharmony_ci .g_tvnorms = adv7180_g_tvnorms, 92162306a36Sopenharmony_ci .s_stream = adv7180_s_stream, 92262306a36Sopenharmony_ci}; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_cistatic const struct v4l2_subdev_core_ops adv7180_core_ops = { 92562306a36Sopenharmony_ci .s_power = adv7180_s_power, 92662306a36Sopenharmony_ci .subscribe_event = adv7180_subscribe_event, 92762306a36Sopenharmony_ci .unsubscribe_event = v4l2_event_subdev_unsubscribe, 92862306a36Sopenharmony_ci}; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic const struct v4l2_subdev_pad_ops adv7180_pad_ops = { 93162306a36Sopenharmony_ci .init_cfg = adv7180_init_cfg, 93262306a36Sopenharmony_ci .enum_mbus_code = adv7180_enum_mbus_code, 93362306a36Sopenharmony_ci .set_fmt = adv7180_set_pad_format, 93462306a36Sopenharmony_ci .get_fmt = adv7180_get_pad_format, 93562306a36Sopenharmony_ci .get_mbus_config = adv7180_get_mbus_config, 93662306a36Sopenharmony_ci}; 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_cistatic const struct v4l2_subdev_sensor_ops adv7180_sensor_ops = { 93962306a36Sopenharmony_ci .g_skip_frames = adv7180_get_skip_frames, 94062306a36Sopenharmony_ci}; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cistatic const struct v4l2_subdev_ops adv7180_ops = { 94362306a36Sopenharmony_ci .core = &adv7180_core_ops, 94462306a36Sopenharmony_ci .video = &adv7180_video_ops, 94562306a36Sopenharmony_ci .pad = &adv7180_pad_ops, 94662306a36Sopenharmony_ci .sensor = &adv7180_sensor_ops, 94762306a36Sopenharmony_ci}; 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_cistatic irqreturn_t adv7180_irq(int irq, void *devid) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct adv7180_state *state = devid; 95262306a36Sopenharmony_ci u8 isr3; 95362306a36Sopenharmony_ci 95462306a36Sopenharmony_ci mutex_lock(&state->mutex); 95562306a36Sopenharmony_ci isr3 = adv7180_read(state, ADV7180_REG_ISR3); 95662306a36Sopenharmony_ci /* clear */ 95762306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_ICR3, isr3); 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci if (isr3 & ADV7180_IRQ3_AD_CHANGE) { 96062306a36Sopenharmony_ci static const struct v4l2_event src_ch = { 96162306a36Sopenharmony_ci .type = V4L2_EVENT_SOURCE_CHANGE, 96262306a36Sopenharmony_ci .u.src_change.changes = V4L2_EVENT_SRC_CH_RESOLUTION, 96362306a36Sopenharmony_ci }; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci v4l2_subdev_notify_event(&state->sd, &src_ch); 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci mutex_unlock(&state->mutex); 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return IRQ_HANDLED; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int adv7180_init(struct adv7180_state *state) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci int ret; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* ITU-R BT.656-4 compatible */ 97762306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 97862306a36Sopenharmony_ci ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); 97962306a36Sopenharmony_ci if (ret < 0) 98062306a36Sopenharmony_ci return ret; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci /* Manually set V bit end position in NTSC mode */ 98362306a36Sopenharmony_ci return adv7180_write(state, ADV7180_REG_NTSC_V_BIT_END, 98462306a36Sopenharmony_ci ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); 98562306a36Sopenharmony_ci} 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic int adv7180_set_std(struct adv7180_state *state, unsigned int std) 98862306a36Sopenharmony_ci{ 98962306a36Sopenharmony_ci return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, 99062306a36Sopenharmony_ci (std << 4) | state->input); 99162306a36Sopenharmony_ci} 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_cistatic int adv7180_select_input(struct adv7180_state *state, unsigned int input) 99462306a36Sopenharmony_ci{ 99562306a36Sopenharmony_ci int ret; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci ret = adv7180_read(state, ADV7180_REG_INPUT_CONTROL); 99862306a36Sopenharmony_ci if (ret < 0) 99962306a36Sopenharmony_ci return ret; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; 100262306a36Sopenharmony_ci ret |= input; 100362306a36Sopenharmony_ci return adv7180_write(state, ADV7180_REG_INPUT_CONTROL, ret); 100462306a36Sopenharmony_ci} 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int adv7182_init(struct adv7180_state *state) 100762306a36Sopenharmony_ci{ 100862306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) 100962306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_CSI_SLAVE_ADDR, 101062306a36Sopenharmony_ci ADV7180_DEFAULT_CSI_I2C_ADDR << 1); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_I2P) 101362306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_VPP_SLAVE_ADDR, 101462306a36Sopenharmony_ci ADV7180_DEFAULT_VPP_I2C_ADDR << 1); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_V2) { 101762306a36Sopenharmony_ci /* ADI recommended writes for improved video quality */ 101862306a36Sopenharmony_ci adv7180_write(state, 0x0080, 0x51); 101962306a36Sopenharmony_ci adv7180_write(state, 0x0081, 0x51); 102062306a36Sopenharmony_ci adv7180_write(state, 0x0082, 0x68); 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci /* ADI required writes */ 102462306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { 102562306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x4e); 102662306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 0x57); 102762306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_CTRL_2, 0xc0); 102862306a36Sopenharmony_ci } else { 102962306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_V2) { 103062306a36Sopenharmony_ci if (state->force_bt656_4) { 103162306a36Sopenharmony_ci /* ITU-R BT.656-4 compatible */ 103262306a36Sopenharmony_ci adv7180_write(state, 103362306a36Sopenharmony_ci ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 103462306a36Sopenharmony_ci ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); 103562306a36Sopenharmony_ci /* Manually set NEWAVMODE */ 103662306a36Sopenharmony_ci adv7180_write(state, 103762306a36Sopenharmony_ci ADV7180_REG_VSYNC_FIELD_CTL_1, 103862306a36Sopenharmony_ci ADV7180_VSYNC_FIELD_CTL_1_NEWAV); 103962306a36Sopenharmony_ci /* Manually set V bit end position in NTSC mode */ 104062306a36Sopenharmony_ci adv7180_write(state, 104162306a36Sopenharmony_ci ADV7180_REG_NTSC_V_BIT_END, 104262306a36Sopenharmony_ci ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); 104362306a36Sopenharmony_ci } else { 104462306a36Sopenharmony_ci adv7180_write(state, 104562306a36Sopenharmony_ci ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 104662306a36Sopenharmony_ci 0x17); 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci } 104962306a36Sopenharmony_ci else 105062306a36Sopenharmony_ci adv7180_write(state, 105162306a36Sopenharmony_ci ADV7180_REG_EXTENDED_OUTPUT_CONTROL, 105262306a36Sopenharmony_ci 0x07); 105362306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_OUTPUT_CONTROL, 0x0c); 105462306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_CTRL_2, 0x40); 105562306a36Sopenharmony_ci } 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci adv7180_write(state, 0x0013, 0x00); 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci return 0; 106062306a36Sopenharmony_ci} 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_cistatic int adv7182_set_std(struct adv7180_state *state, unsigned int std) 106362306a36Sopenharmony_ci{ 106462306a36Sopenharmony_ci /* Failing to set the reserved bit can result in increased video noise */ 106562306a36Sopenharmony_ci return adv7180_write(state, ADV7182_REG_INPUT_VIDSEL, 106662306a36Sopenharmony_ci (std << 4) | ADV7182_REG_INPUT_RESERVED); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cienum adv7182_input_type { 107062306a36Sopenharmony_ci ADV7182_INPUT_TYPE_CVBS, 107162306a36Sopenharmony_ci ADV7182_INPUT_TYPE_DIFF_CVBS, 107262306a36Sopenharmony_ci ADV7182_INPUT_TYPE_SVIDEO, 107362306a36Sopenharmony_ci ADV7182_INPUT_TYPE_YPBPR, 107462306a36Sopenharmony_ci}; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic enum adv7182_input_type adv7182_get_input_type(unsigned int input) 107762306a36Sopenharmony_ci{ 107862306a36Sopenharmony_ci switch (input) { 107962306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN1: 108062306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN2: 108162306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN3: 108262306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN4: 108362306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN5: 108462306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN6: 108562306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN7: 108662306a36Sopenharmony_ci case ADV7182_INPUT_CVBS_AIN8: 108762306a36Sopenharmony_ci return ADV7182_INPUT_TYPE_CVBS; 108862306a36Sopenharmony_ci case ADV7182_INPUT_SVIDEO_AIN1_AIN2: 108962306a36Sopenharmony_ci case ADV7182_INPUT_SVIDEO_AIN3_AIN4: 109062306a36Sopenharmony_ci case ADV7182_INPUT_SVIDEO_AIN5_AIN6: 109162306a36Sopenharmony_ci case ADV7182_INPUT_SVIDEO_AIN7_AIN8: 109262306a36Sopenharmony_ci return ADV7182_INPUT_TYPE_SVIDEO; 109362306a36Sopenharmony_ci case ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3: 109462306a36Sopenharmony_ci case ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6: 109562306a36Sopenharmony_ci return ADV7182_INPUT_TYPE_YPBPR; 109662306a36Sopenharmony_ci case ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2: 109762306a36Sopenharmony_ci case ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4: 109862306a36Sopenharmony_ci case ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6: 109962306a36Sopenharmony_ci case ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8: 110062306a36Sopenharmony_ci return ADV7182_INPUT_TYPE_DIFF_CVBS; 110162306a36Sopenharmony_ci default: /* Will never happen */ 110262306a36Sopenharmony_ci return 0; 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_ci/* ADI recommended writes to registers 0x52, 0x53, 0x54 */ 110762306a36Sopenharmony_cistatic unsigned int adv7182_lbias_settings[][3] = { 110862306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_CVBS] = { 0xCB, 0x4E, 0x80 }, 110962306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 }, 111062306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 }, 111162306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 }, 111262306a36Sopenharmony_ci}; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic unsigned int adv7280_lbias_settings[][3] = { 111562306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_CVBS] = { 0xCD, 0x4E, 0x80 }, 111662306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_DIFF_CVBS] = { 0xC0, 0x4E, 0x80 }, 111762306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_SVIDEO] = { 0x0B, 0xCE, 0x80 }, 111862306a36Sopenharmony_ci [ADV7182_INPUT_TYPE_YPBPR] = { 0x0B, 0x4E, 0xC0 }, 111962306a36Sopenharmony_ci}; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_cistatic int adv7182_select_input(struct adv7180_state *state, unsigned int input) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci enum adv7182_input_type input_type; 112462306a36Sopenharmony_ci unsigned int *lbias; 112562306a36Sopenharmony_ci unsigned int i; 112662306a36Sopenharmony_ci int ret; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_INPUT_CONTROL, input); 112962306a36Sopenharmony_ci if (ret) 113062306a36Sopenharmony_ci return ret; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* Reset clamp circuitry - ADI recommended writes */ 113362306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_RST_CLAMP, 0x00); 113462306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_RST_CLAMP, 0xff); 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci input_type = adv7182_get_input_type(input); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci switch (input_type) { 113962306a36Sopenharmony_ci case ADV7182_INPUT_TYPE_CVBS: 114062306a36Sopenharmony_ci case ADV7182_INPUT_TYPE_DIFF_CVBS: 114162306a36Sopenharmony_ci /* ADI recommends to use the SH1 filter */ 114262306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x41); 114362306a36Sopenharmony_ci break; 114462306a36Sopenharmony_ci default: 114562306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_SHAP_FILTER_CTL_1, 0x01); 114662306a36Sopenharmony_ci break; 114762306a36Sopenharmony_ci } 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_V2) 115062306a36Sopenharmony_ci lbias = adv7280_lbias_settings[input_type]; 115162306a36Sopenharmony_ci else 115262306a36Sopenharmony_ci lbias = adv7182_lbias_settings[input_type]; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(adv7182_lbias_settings[0]); i++) 115562306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_CVBS_TRIM + i, lbias[i]); 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci if (input_type == ADV7182_INPUT_TYPE_DIFF_CVBS) { 115862306a36Sopenharmony_ci /* ADI required writes to make differential CVBS work */ 115962306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_RES_CIR, 0xa8); 116062306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0x90); 116162306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_DIFF_MODE, 0xb0); 116262306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x08); 116362306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0xa0); 116462306a36Sopenharmony_ci } else { 116562306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_RES_CIR, 0xf0); 116662306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_CLAMP_ADJ, 0xd0); 116762306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_DIFF_MODE, 0x10); 116862306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_AGC_ADJ1, 0x9c); 116962306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_AGC_ADJ2, 0x00); 117062306a36Sopenharmony_ci } 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci return 0; 117362306a36Sopenharmony_ci} 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7180_info = { 117662306a36Sopenharmony_ci .flags = ADV7180_FLAG_RESET_POWERED, 117762306a36Sopenharmony_ci /* We cannot discriminate between LQFP and 40-pin LFCSP, so accept 117862306a36Sopenharmony_ci * all inputs and let the card driver take care of validation 117962306a36Sopenharmony_ci */ 118062306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7180_INPUT_CVBS_AIN1) | 118162306a36Sopenharmony_ci BIT(ADV7180_INPUT_CVBS_AIN2) | 118262306a36Sopenharmony_ci BIT(ADV7180_INPUT_CVBS_AIN3) | 118362306a36Sopenharmony_ci BIT(ADV7180_INPUT_CVBS_AIN4) | 118462306a36Sopenharmony_ci BIT(ADV7180_INPUT_CVBS_AIN5) | 118562306a36Sopenharmony_ci BIT(ADV7180_INPUT_CVBS_AIN6) | 118662306a36Sopenharmony_ci BIT(ADV7180_INPUT_SVIDEO_AIN1_AIN2) | 118762306a36Sopenharmony_ci BIT(ADV7180_INPUT_SVIDEO_AIN3_AIN4) | 118862306a36Sopenharmony_ci BIT(ADV7180_INPUT_SVIDEO_AIN5_AIN6) | 118962306a36Sopenharmony_ci BIT(ADV7180_INPUT_YPRPB_AIN1_AIN2_AIN3) | 119062306a36Sopenharmony_ci BIT(ADV7180_INPUT_YPRPB_AIN4_AIN5_AIN6), 119162306a36Sopenharmony_ci .init = adv7180_init, 119262306a36Sopenharmony_ci .set_std = adv7180_set_std, 119362306a36Sopenharmony_ci .select_input = adv7180_select_input, 119462306a36Sopenharmony_ci}; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7182_info = { 119762306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 119862306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 119962306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN3) | 120062306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN4) | 120162306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 120262306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | 120362306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | 120462306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | 120562306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4), 120662306a36Sopenharmony_ci .init = adv7182_init, 120762306a36Sopenharmony_ci .set_std = adv7182_set_std, 120862306a36Sopenharmony_ci .select_input = adv7182_select_input, 120962306a36Sopenharmony_ci}; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7280_info = { 121262306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, 121362306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 121462306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 121562306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN3) | 121662306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN4) | 121762306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 121862306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | 121962306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3), 122062306a36Sopenharmony_ci .init = adv7182_init, 122162306a36Sopenharmony_ci .set_std = adv7182_set_std, 122262306a36Sopenharmony_ci .select_input = adv7182_select_input, 122362306a36Sopenharmony_ci}; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7280_m_info = { 122662306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, 122762306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 122862306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 122962306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN3) | 123062306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN4) | 123162306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN5) | 123262306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN6) | 123362306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN7) | 123462306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN8) | 123562306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 123662306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | 123762306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) | 123862306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | 123962306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | 124062306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6), 124162306a36Sopenharmony_ci .init = adv7182_init, 124262306a36Sopenharmony_ci .set_std = adv7182_set_std, 124362306a36Sopenharmony_ci .select_input = adv7182_select_input, 124462306a36Sopenharmony_ci}; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7281_info = { 124762306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, 124862306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 124962306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 125062306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN7) | 125162306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN8) | 125262306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 125362306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | 125462306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | 125562306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), 125662306a36Sopenharmony_ci .init = adv7182_init, 125762306a36Sopenharmony_ci .set_std = adv7182_set_std, 125862306a36Sopenharmony_ci .select_input = adv7182_select_input, 125962306a36Sopenharmony_ci}; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7281_m_info = { 126262306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, 126362306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 126462306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 126562306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN3) | 126662306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN4) | 126762306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN7) | 126862306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN8) | 126962306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 127062306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | 127162306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | 127262306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | 127362306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | 127462306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | 127562306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), 127662306a36Sopenharmony_ci .init = adv7182_init, 127762306a36Sopenharmony_ci .set_std = adv7182_set_std, 127862306a36Sopenharmony_ci .select_input = adv7182_select_input, 127962306a36Sopenharmony_ci}; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7281_ma_info = { 128262306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2, 128362306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 128462306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 128562306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN3) | 128662306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN4) | 128762306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN5) | 128862306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN6) | 128962306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN7) | 129062306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN8) | 129162306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 129262306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | 129362306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN5_AIN6) | 129462306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | 129562306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN1_AIN2_AIN3) | 129662306a36Sopenharmony_ci BIT(ADV7182_INPUT_YPRPB_AIN4_AIN5_AIN6) | 129762306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | 129862306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | 129962306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN5_AIN6) | 130062306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), 130162306a36Sopenharmony_ci .init = adv7182_init, 130262306a36Sopenharmony_ci .set_std = adv7182_set_std, 130362306a36Sopenharmony_ci .select_input = adv7182_select_input, 130462306a36Sopenharmony_ci}; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7282_info = { 130762306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_I2P, 130862306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 130962306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 131062306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN7) | 131162306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN8) | 131262306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 131362306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | 131462306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | 131562306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), 131662306a36Sopenharmony_ci .init = adv7182_init, 131762306a36Sopenharmony_ci .set_std = adv7182_set_std, 131862306a36Sopenharmony_ci .select_input = adv7182_select_input, 131962306a36Sopenharmony_ci}; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_cistatic const struct adv7180_chip_info adv7282_m_info = { 132262306a36Sopenharmony_ci .flags = ADV7180_FLAG_V2 | ADV7180_FLAG_MIPI_CSI2 | ADV7180_FLAG_I2P, 132362306a36Sopenharmony_ci .valid_input_mask = BIT(ADV7182_INPUT_CVBS_AIN1) | 132462306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN2) | 132562306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN3) | 132662306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN4) | 132762306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN7) | 132862306a36Sopenharmony_ci BIT(ADV7182_INPUT_CVBS_AIN8) | 132962306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN1_AIN2) | 133062306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN3_AIN4) | 133162306a36Sopenharmony_ci BIT(ADV7182_INPUT_SVIDEO_AIN7_AIN8) | 133262306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN1_AIN2) | 133362306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN3_AIN4) | 133462306a36Sopenharmony_ci BIT(ADV7182_INPUT_DIFF_CVBS_AIN7_AIN8), 133562306a36Sopenharmony_ci .init = adv7182_init, 133662306a36Sopenharmony_ci .set_std = adv7182_set_std, 133762306a36Sopenharmony_ci .select_input = adv7182_select_input, 133862306a36Sopenharmony_ci}; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_cistatic int init_device(struct adv7180_state *state) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci int ret; 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci mutex_lock(&state->mutex); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci adv7180_set_power_pin(state, true); 134762306a36Sopenharmony_ci adv7180_set_reset_pin(state, false); 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci adv7180_write(state, ADV7180_REG_PWR_MAN, ADV7180_PWR_MAN_RES); 135062306a36Sopenharmony_ci usleep_range(5000, 10000); 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_ci ret = state->chip_info->init(state); 135362306a36Sopenharmony_ci if (ret) 135462306a36Sopenharmony_ci goto out_unlock; 135562306a36Sopenharmony_ci 135662306a36Sopenharmony_ci ret = adv7180_program_std(state); 135762306a36Sopenharmony_ci if (ret) 135862306a36Sopenharmony_ci goto out_unlock; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci adv7180_set_field_mode(state); 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci /* register for interrupts */ 136362306a36Sopenharmony_ci if (state->irq > 0) { 136462306a36Sopenharmony_ci /* config the Interrupt pin to be active low */ 136562306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_ICONF1, 136662306a36Sopenharmony_ci ADV7180_ICONF1_ACTIVE_LOW | 136762306a36Sopenharmony_ci ADV7180_ICONF1_PSYNC_ONLY); 136862306a36Sopenharmony_ci if (ret < 0) 136962306a36Sopenharmony_ci goto out_unlock; 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_IMR1, 0); 137262306a36Sopenharmony_ci if (ret < 0) 137362306a36Sopenharmony_ci goto out_unlock; 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_IMR2, 0); 137662306a36Sopenharmony_ci if (ret < 0) 137762306a36Sopenharmony_ci goto out_unlock; 137862306a36Sopenharmony_ci 137962306a36Sopenharmony_ci /* enable AD change interrupts interrupts */ 138062306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_IMR3, 138162306a36Sopenharmony_ci ADV7180_IRQ3_AD_CHANGE); 138262306a36Sopenharmony_ci if (ret < 0) 138362306a36Sopenharmony_ci goto out_unlock; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci ret = adv7180_write(state, ADV7180_REG_IMR4, 0); 138662306a36Sopenharmony_ci if (ret < 0) 138762306a36Sopenharmony_ci goto out_unlock; 138862306a36Sopenharmony_ci } 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ciout_unlock: 139162306a36Sopenharmony_ci mutex_unlock(&state->mutex); 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci return ret; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_cistatic int adv7180_probe(struct i2c_client *client) 139762306a36Sopenharmony_ci{ 139862306a36Sopenharmony_ci const struct i2c_device_id *id = i2c_client_get_device_id(client); 139962306a36Sopenharmony_ci struct device_node *np = client->dev.of_node; 140062306a36Sopenharmony_ci struct adv7180_state *state; 140162306a36Sopenharmony_ci struct v4l2_subdev *sd; 140262306a36Sopenharmony_ci int ret; 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci /* Check if the adapter supports the needed features */ 140562306a36Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 140662306a36Sopenharmony_ci return -EIO; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci state = devm_kzalloc(&client->dev, sizeof(*state), GFP_KERNEL); 140962306a36Sopenharmony_ci if (state == NULL) 141062306a36Sopenharmony_ci return -ENOMEM; 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci state->client = client; 141362306a36Sopenharmony_ci state->field = V4L2_FIELD_ALTERNATE; 141462306a36Sopenharmony_ci state->chip_info = (struct adv7180_chip_info *)id->driver_data; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci state->pwdn_gpio = devm_gpiod_get_optional(&client->dev, "powerdown", 141762306a36Sopenharmony_ci GPIOD_OUT_HIGH); 141862306a36Sopenharmony_ci if (IS_ERR(state->pwdn_gpio)) { 141962306a36Sopenharmony_ci ret = PTR_ERR(state->pwdn_gpio); 142062306a36Sopenharmony_ci v4l_err(client, "request for power pin failed: %d\n", ret); 142162306a36Sopenharmony_ci return ret; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci state->rst_gpio = devm_gpiod_get_optional(&client->dev, "reset", 142562306a36Sopenharmony_ci GPIOD_OUT_HIGH); 142662306a36Sopenharmony_ci if (IS_ERR(state->rst_gpio)) { 142762306a36Sopenharmony_ci ret = PTR_ERR(state->rst_gpio); 142862306a36Sopenharmony_ci v4l_err(client, "request for reset pin failed: %d\n", ret); 142962306a36Sopenharmony_ci return ret; 143062306a36Sopenharmony_ci } 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci if (of_property_read_bool(np, "adv,force-bt656-4")) 143362306a36Sopenharmony_ci state->force_bt656_4 = true; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_MIPI_CSI2) { 143662306a36Sopenharmony_ci state->csi_client = i2c_new_dummy_device(client->adapter, 143762306a36Sopenharmony_ci ADV7180_DEFAULT_CSI_I2C_ADDR); 143862306a36Sopenharmony_ci if (IS_ERR(state->csi_client)) 143962306a36Sopenharmony_ci return PTR_ERR(state->csi_client); 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_I2P) { 144362306a36Sopenharmony_ci state->vpp_client = i2c_new_dummy_device(client->adapter, 144462306a36Sopenharmony_ci ADV7180_DEFAULT_VPP_I2C_ADDR); 144562306a36Sopenharmony_ci if (IS_ERR(state->vpp_client)) { 144662306a36Sopenharmony_ci ret = PTR_ERR(state->vpp_client); 144762306a36Sopenharmony_ci goto err_unregister_csi_client; 144862306a36Sopenharmony_ci } 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci state->irq = client->irq; 145262306a36Sopenharmony_ci mutex_init(&state->mutex); 145362306a36Sopenharmony_ci state->curr_norm = V4L2_STD_NTSC; 145462306a36Sopenharmony_ci if (state->chip_info->flags & ADV7180_FLAG_RESET_POWERED) 145562306a36Sopenharmony_ci state->powered = true; 145662306a36Sopenharmony_ci else 145762306a36Sopenharmony_ci state->powered = false; 145862306a36Sopenharmony_ci state->input = 0; 145962306a36Sopenharmony_ci sd = &state->sd; 146062306a36Sopenharmony_ci v4l2_i2c_subdev_init(sd, client, &adv7180_ops); 146162306a36Sopenharmony_ci sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; 146262306a36Sopenharmony_ci 146362306a36Sopenharmony_ci ret = adv7180_init_controls(state); 146462306a36Sopenharmony_ci if (ret) 146562306a36Sopenharmony_ci goto err_unregister_vpp_client; 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci state->pad.flags = MEDIA_PAD_FL_SOURCE; 146862306a36Sopenharmony_ci sd->entity.function = MEDIA_ENT_F_ATV_DECODER; 146962306a36Sopenharmony_ci ret = media_entity_pads_init(&sd->entity, 1, &state->pad); 147062306a36Sopenharmony_ci if (ret) 147162306a36Sopenharmony_ci goto err_free_ctrl; 147262306a36Sopenharmony_ci 147362306a36Sopenharmony_ci ret = init_device(state); 147462306a36Sopenharmony_ci if (ret) 147562306a36Sopenharmony_ci goto err_media_entity_cleanup; 147662306a36Sopenharmony_ci 147762306a36Sopenharmony_ci if (state->irq) { 147862306a36Sopenharmony_ci ret = request_threaded_irq(client->irq, NULL, adv7180_irq, 147962306a36Sopenharmony_ci IRQF_ONESHOT | IRQF_TRIGGER_FALLING, 148062306a36Sopenharmony_ci KBUILD_MODNAME, state); 148162306a36Sopenharmony_ci if (ret) 148262306a36Sopenharmony_ci goto err_media_entity_cleanup; 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_ci ret = v4l2_async_register_subdev(sd); 148662306a36Sopenharmony_ci if (ret) 148762306a36Sopenharmony_ci goto err_free_irq; 148862306a36Sopenharmony_ci 148962306a36Sopenharmony_ci mutex_lock(&state->mutex); 149062306a36Sopenharmony_ci ret = adv7180_read(state, ADV7180_REG_IDENT); 149162306a36Sopenharmony_ci mutex_unlock(&state->mutex); 149262306a36Sopenharmony_ci if (ret < 0) 149362306a36Sopenharmony_ci goto err_v4l2_async_unregister; 149462306a36Sopenharmony_ci 149562306a36Sopenharmony_ci v4l_info(client, "chip id 0x%x found @ 0x%02x (%s)\n", 149662306a36Sopenharmony_ci ret, client->addr, client->adapter->name); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci return 0; 149962306a36Sopenharmony_ci 150062306a36Sopenharmony_cierr_v4l2_async_unregister: 150162306a36Sopenharmony_ci v4l2_async_unregister_subdev(sd); 150262306a36Sopenharmony_cierr_free_irq: 150362306a36Sopenharmony_ci if (state->irq > 0) 150462306a36Sopenharmony_ci free_irq(client->irq, state); 150562306a36Sopenharmony_cierr_media_entity_cleanup: 150662306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 150762306a36Sopenharmony_cierr_free_ctrl: 150862306a36Sopenharmony_ci adv7180_exit_controls(state); 150962306a36Sopenharmony_cierr_unregister_vpp_client: 151062306a36Sopenharmony_ci i2c_unregister_device(state->vpp_client); 151162306a36Sopenharmony_cierr_unregister_csi_client: 151262306a36Sopenharmony_ci i2c_unregister_device(state->csi_client); 151362306a36Sopenharmony_ci mutex_destroy(&state->mutex); 151462306a36Sopenharmony_ci return ret; 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_cistatic void adv7180_remove(struct i2c_client *client) 151862306a36Sopenharmony_ci{ 151962306a36Sopenharmony_ci struct v4l2_subdev *sd = i2c_get_clientdata(client); 152062306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci v4l2_async_unregister_subdev(sd); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci if (state->irq > 0) 152562306a36Sopenharmony_ci free_irq(client->irq, state); 152662306a36Sopenharmony_ci 152762306a36Sopenharmony_ci media_entity_cleanup(&sd->entity); 152862306a36Sopenharmony_ci adv7180_exit_controls(state); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci i2c_unregister_device(state->vpp_client); 153162306a36Sopenharmony_ci i2c_unregister_device(state->csi_client); 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci adv7180_set_reset_pin(state, true); 153462306a36Sopenharmony_ci adv7180_set_power_pin(state, false); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci mutex_destroy(&state->mutex); 153762306a36Sopenharmony_ci} 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_cistatic const struct i2c_device_id adv7180_id[] = { 154062306a36Sopenharmony_ci { "adv7180", (kernel_ulong_t)&adv7180_info }, 154162306a36Sopenharmony_ci { "adv7180cp", (kernel_ulong_t)&adv7180_info }, 154262306a36Sopenharmony_ci { "adv7180st", (kernel_ulong_t)&adv7180_info }, 154362306a36Sopenharmony_ci { "adv7182", (kernel_ulong_t)&adv7182_info }, 154462306a36Sopenharmony_ci { "adv7280", (kernel_ulong_t)&adv7280_info }, 154562306a36Sopenharmony_ci { "adv7280-m", (kernel_ulong_t)&adv7280_m_info }, 154662306a36Sopenharmony_ci { "adv7281", (kernel_ulong_t)&adv7281_info }, 154762306a36Sopenharmony_ci { "adv7281-m", (kernel_ulong_t)&adv7281_m_info }, 154862306a36Sopenharmony_ci { "adv7281-ma", (kernel_ulong_t)&adv7281_ma_info }, 154962306a36Sopenharmony_ci { "adv7282", (kernel_ulong_t)&adv7282_info }, 155062306a36Sopenharmony_ci { "adv7282-m", (kernel_ulong_t)&adv7282_m_info }, 155162306a36Sopenharmony_ci {}, 155262306a36Sopenharmony_ci}; 155362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adv7180_id); 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 155662306a36Sopenharmony_cistatic int adv7180_suspend(struct device *dev) 155762306a36Sopenharmony_ci{ 155862306a36Sopenharmony_ci struct v4l2_subdev *sd = dev_get_drvdata(dev); 155962306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci return adv7180_set_power(state, false); 156262306a36Sopenharmony_ci} 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cistatic int adv7180_resume(struct device *dev) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci struct v4l2_subdev *sd = dev_get_drvdata(dev); 156762306a36Sopenharmony_ci struct adv7180_state *state = to_state(sd); 156862306a36Sopenharmony_ci int ret; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci ret = init_device(state); 157162306a36Sopenharmony_ci if (ret < 0) 157262306a36Sopenharmony_ci return ret; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci ret = adv7180_set_power(state, state->powered); 157562306a36Sopenharmony_ci if (ret) 157662306a36Sopenharmony_ci return ret; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci return 0; 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(adv7180_pm_ops, adv7180_suspend, adv7180_resume); 158262306a36Sopenharmony_ci#define ADV7180_PM_OPS (&adv7180_pm_ops) 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci#else 158562306a36Sopenharmony_ci#define ADV7180_PM_OPS NULL 158662306a36Sopenharmony_ci#endif 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci#ifdef CONFIG_OF 158962306a36Sopenharmony_cistatic const struct of_device_id adv7180_of_id[] = { 159062306a36Sopenharmony_ci { .compatible = "adi,adv7180", }, 159162306a36Sopenharmony_ci { .compatible = "adi,adv7180cp", }, 159262306a36Sopenharmony_ci { .compatible = "adi,adv7180st", }, 159362306a36Sopenharmony_ci { .compatible = "adi,adv7182", }, 159462306a36Sopenharmony_ci { .compatible = "adi,adv7280", }, 159562306a36Sopenharmony_ci { .compatible = "adi,adv7280-m", }, 159662306a36Sopenharmony_ci { .compatible = "adi,adv7281", }, 159762306a36Sopenharmony_ci { .compatible = "adi,adv7281-m", }, 159862306a36Sopenharmony_ci { .compatible = "adi,adv7281-ma", }, 159962306a36Sopenharmony_ci { .compatible = "adi,adv7282", }, 160062306a36Sopenharmony_ci { .compatible = "adi,adv7282-m", }, 160162306a36Sopenharmony_ci { }, 160262306a36Sopenharmony_ci}; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, adv7180_of_id); 160562306a36Sopenharmony_ci#endif 160662306a36Sopenharmony_ci 160762306a36Sopenharmony_cistatic struct i2c_driver adv7180_driver = { 160862306a36Sopenharmony_ci .driver = { 160962306a36Sopenharmony_ci .name = KBUILD_MODNAME, 161062306a36Sopenharmony_ci .pm = ADV7180_PM_OPS, 161162306a36Sopenharmony_ci .of_match_table = of_match_ptr(adv7180_of_id), 161262306a36Sopenharmony_ci }, 161362306a36Sopenharmony_ci .probe = adv7180_probe, 161462306a36Sopenharmony_ci .remove = adv7180_remove, 161562306a36Sopenharmony_ci .id_table = adv7180_id, 161662306a36Sopenharmony_ci}; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cimodule_i2c_driver(adv7180_driver); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ciMODULE_DESCRIPTION("Analog Devices ADV7180 video decoder driver"); 162162306a36Sopenharmony_ciMODULE_AUTHOR("Mocean Laboratories"); 162262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1623