[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20200810181554.GE292825@ravnborg.org>
Date: Mon, 10 Aug 2020 20:15:54 +0200
From: Sam Ravnborg <sam@...nborg.org>
To: allen <allen.chen@....com.tw>
Cc: Jernej Skrabec <jernej.skrabec@...l.net>,
Jitao Shi <jitao.shi@...iatek.com>,
Jau-Chih Tseng <Jau-Chih.Tseng@....com.tw>,
Yilun Lin <yllin@...gle.com>, David Airlie <airlied@...ux.ie>,
Neil Armstrong <narmstrong@...libre.com>,
open list <linux-kernel@...r.kernel.org>,
"open list:DRM DRIVERS" <dri-devel@...ts.freedesktop.org>,
Andrzej Hajda <a.hajda@...sung.com>,
Hermes Wu <hermes.wu@....com.tw>,
"moderated list:ARM/Mediatek SoC support"
<linux-mediatek@...ts.infradead.org>,
Laurent Pinchart <Laurent.pinchart@...asonboard.com>,
Pi-Hsun Shih <pihsun@...omium.org>,
Matthias Brugger <matthias.bgg@...il.com>,
"moderated list:ARM/Mediatek SoC support"
<linux-arm-kernel@...ts.infradead.org>,
Jonas Karlman <jonas@...boo.se>
Subject: Re: [PATCH v10] drm/bridge: add it6505 driver
Hi allen.
On Thu, Jul 16, 2020 at 05:47:31PM +0800, allen wrote:
> This adds support for the iTE IT6505.
> This device can convert DPI signal to DP output.
>
> Signed-off-by: Jitao Shi <jitao.shi@...iatek.com>
> Signed-off-by: Pi-Hsun Shih <pihsun@...omium.org>
> Signed-off-by: Yilun Lin <yllin@...gle.com>
> Signed-off-by: Hermes Wu <hermes.wu@....com.tw>
> Signed-off-by: Allen Chen <allen.chen@....com.tw>
> ---
Can you please add a From: line so it is possible for the tools to pick
who is the author.
Right now you are picked - which may or may not be OK.
I tried to apply the patch - but there is a few things to look at:
I got this with one of my configs (allyes or allmodconfigs):
CC [M] drivers/gpu/drm/bridge/ite-it6505.o
/home/sam/drm/linux.git/drivers/gpu/drm/bridge/ite-it6505.c:2589:12: warning: ‘it6505_bridge_suspend’ defined but not used [-Wunused-function]
2589 | static int it6505_bridge_suspend(struct device *dev)
| ^~~~~~~~~~~~~~~~~~~~~
/home/sam/drm/linux.git/drivers/gpu/drm/bridge/ite-it6505.c:2582:12: warning: ‘it6505_bridge_resume’ defined but not used [-Wunused-function]
2582 | static int it6505_bridge_resume(struct device *dev)
| ^~~~~~~~~~~~~~~~~~~~
Please fix.
Please try to run it through checkpatch with the following options:
./scripts/checkpatch -q --emacs --strict --show-types --codespell
Drop codespell if it does not work for you.
Codespell did not like afe - seems that it does not know this is a
shorthand for "analog front end". This is one of the warnings to ignore.
There are several valid warnings that needs fixing. For example indent.
The driver needs to support "flags" in attach to make connector creation
optional - support for this is mandatory in new drivers.
The driver also needs to support relevant bridge operations.
.get_edid
.detect
needs to be supported, maybe more.
See other bridge drivers - most of them are converted today.
Please fix so we can get this driver applied.
Sam
> drivers/gpu/drm/bridge/Kconfig | 7 +
> drivers/gpu/drm/bridge/Makefile | 1 +
> drivers/gpu/drm/bridge/ite-it6505.c | 2890 +++++++++++++++++++++++++++++++++++
> 3 files changed, 2898 insertions(+)
> create mode 100644 drivers/gpu/drm/bridge/ite-it6505.c
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 43271c2..59750b8 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -57,6 +57,13 @@ config DRM_LVDS_CODEC
> Support for transparent LVDS encoders and decoders that don't
> require any configuration.
>
> +config DRM_ITE_IT6505
> + tristate "ITE IT6505 DP bridge"
> + depends on OF
> + select DRM_KMS_HELPER
> + help
> + ITE IT6505 DP bridge chip driver.
> +
> config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW
> tristate "MegaChips stdp4028-ge-b850v3-fw and stdp2690-ge-b850v3-fw"
> depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index d63d4b7..86619c7 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_DRM_CDNS_DSI) += cdns-dsi.o
> obj-$(CONFIG_DRM_CHRONTEL_CH7033) += chrontel-ch7033.o
> obj-$(CONFIG_DRM_DISPLAY_CONNECTOR) += display-connector.o
> obj-$(CONFIG_DRM_LVDS_CODEC) += lvds-codec.o
> +obj-$(CONFIG_DRM_ITE_IT6505) += ite-it6505.o
> obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += megachips-stdpxxxx-ge-b850v3-fw.o
> obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
> obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
> diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
> new file mode 100644
> index 00000000..12a56cd
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/ite-it6505.c
> @@ -0,0 +1,2890 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +/*
> + * Copyright (c) 2020, The Linux Foundation. All rights reserved.
> + */
> +#include <linux/bits.h>
> +#include <linux/delay.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/extcon.h>
> +#include <linux/fs.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/i2c.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/types.h>
> +#include <linux/wait.h>
> +
> +#include <crypto/hash.h>
> +#include <crypto/sha.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_crtc.h>
> +#include <drm/drm_crtc_helper.h>
> +#include <drm/drm_dp_helper.h>
> +#include <drm/drm_edid.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include <sound/hdmi-codec.h>
> +
> +#define REG_IC_VER 0x04
> +
> +#define REG_RESET_CTRL 0x05
> +#define VIDEO_RESET BIT(0)
> +#define AUDIO_RESET BIT(1)
> +#define ALL_LOGIC_RESET BIT(2)
> +#define AUX_RESET BIT(3)
> +#define HDCP_RESET BIT(4)
> +
> +#define INT_STATUS_01 0x06
> +#define INT_MASK_01 0x09
> +#define INT_HPD_CHANGE BIT(0)
> +#define INT_RECEIVE_HPD_IRQ BIT(1)
> +#define INT_SCDT_CHANGE BIT(2)
> +
> +#define INT_STATUS_02 0x07
> +#define INT_MASK_02 0x0A
> +#define INT_AUX_CMD_FAIL BIT(0)
> +#define INT_AUDIO_FIFO_ERROR BIT(2)
> +
> +#define INT_STATUS_03 0x08
> +#define INT_MASK_03 0x0B
> +#define INT_LINK_TRAIN_FAIL BIT(4)
> +#define INT_VID_FIFO_ERROR BIT(5)
> +#define INT_IO_LATCH_FIFO_OVERFLOW BIT(7)
> +
> +#define REG_SYSTEM_STS 0x0D
> +#define INT_STS BIT(0)
> +#define HPD_STS BIT(1)
> +#define VIDEO_STB BIT(2)
> +
> +#define REG_LINK_TRAIN_STS 0x0E
> +#define LINK_STATE_CR BIT(2)
> +#define LINK_STATE_EQ BIT(3)
> +#define LINK_STATE_NORP BIT(4)
> +
> +#define REG_BANK_SEL 0x0F
> +#define REG_CLK_CTRL0 0x10
> +#define M_PCLK_DELAY 0x03
> +
> +#define REG_AUX_OPT 0x11
> +#define AUX_AUTO_RST BIT(0)
> +#define AUX_FIX_FREQ BIT(3)
> +
> +#define REG_DATA_CTRL0 0x12
> +#define VIDEO_LATCH_EDGE BIT(4)
> +#define ENABLE_PCLK_COUNTER BIT(7)
> +
> +#define REG_PCLK_COUNTER_VALUE 0x13
> +
> +#define REG_501_FIFO_CTRL 0x15
> +#define RST_501_FIFO BIT(1)
> +
> +#define REG_TRAIN_CTRL0 0x16
> +#define FORCE_LBR BIT(0)
> +#define LANE_COUNT_MASK 0x06
> +#define LANE_SWAP BIT(3)
> +#define SPREAD_AMP_5 BIT(4)
> +#define FORCE_CR_DONE BIT(5)
> +#define FORCE_EQ_DONE BIT(6)
> +
> +#define REG_TRAIN_CTRL1 0x17
> +#define AUTO_TRAIN BIT(0)
> +#define MANUAL_TRAIN BIT(1)
> +#define FORCE_RETRAIN BIT(2)
> +
> +#define REG_AUX_CTRL 0x23
> +#define CLR_EDID_FIFO BIT(0)
> +#define AUX_USER_MODE BIT(1)
> +#define AUX_NO_SEGMENT_WR BIT(6)
> +#define AUX_EN_FIFO_READ BIT(7)
> +
> +#define REG_AUX_ADR_0_7 0x24
> +#define REG_AUX_ADR_8_15 0x25
> +#define REG_AUX_ADR_16_19 0x26
> +#define REG_AUX_OUT_DATA0 0x27
> +
> +#define REG_AUX_CMD_REQ 0x2B
> +#define AUX_BUSY BIT(5)
> +
> +#define REG_AUX_DATA_0_7 0x2C
> +#define REG_AUX_DATA_8_15 0x2D
> +#define REG_AUX_DATA_16_23 0x2E
> +#define REG_AUX_DATA_24_31 0x2F
> +
> +#define REG_AUX_DATA_FIFO 0x2F
> +
> +#define REG_AUX_ERROR_STS 0x9F
> +#define M_AUX_REQ_FAIL 0x03
> +
> +#define REG_HDCP_CTRL1 0x38
> +#define HDCP_CP_ENABLE BIT(0)
> +
> +#define REG_HDCP_CTRL2 0x3A
> +#define HDCP_AN_SEL BIT(0)
> +#define HDCP_HW_HPDIRQ_ACT BIT(2)
> +
> +#define REG_M0_0_7 0x4C
> +#define REG_AN_0_7 0x4C
> +#define REG_SP_CTRL0 0x58
> +#define REG_IP_CTRL1 0x59
> +#define REG_IP_CTRL2 0x5A
> +
> +#define REG_LINK_DRV 0x5C
> +#define DRV_HS BIT(1)
> +
> +#define REG_DRV_LN_DATA_SEL 0x5D
> +
> +#define REG_AUX 0x5E
> +
> +#define REG_VID_BUS_CTRL0 0x60
> +#define IN_DDR BIT(2)
> +#define DDR_CD (0x01 << 6)
> +
> +#define REG_VID_BUS_CTRL1 0x61
> +#define TX_FIFO_RESET BIT(1)
> +
> +#define REG_INPUT_CTRL 0xA0
> +#define INPUT_HSYNC_POL BIT(0)
> +#define INPUT_VSYNC_POL BIT(2)
> +#define INPUT_INTERLACED BIT(4)
> +
> +#define REG_INPUT_HTOTAL 0xA1
> +#define REG_INPUT_HACTIVE_START 0xA3
> +#define REG_INPUT_HACTIVE_WIDTH 0xA5
> +#define REG_INPUT_HFRONT_PORCH 0xA7
> +#define REG_INPUT_HSYNC_WIDTH 0xA9
> +#define REG_INPUT_VTOTAL 0xAB
> +#define REG_INPUT_VACTIVE_START 0xAD
> +#define REG_INPUT_VACTIVE_WIDTH 0xAF
> +#define REG_INPUT_VFRONT_PORCH 0xB1
> +#define REG_INPUT_VSYNC_WIDTH 0xB3
> +
> +#define REG_AUDIO_SRC_CTRL 0xB8
> +#define M_AUDIO_I2S_EN 0x0F
> +#define EN_I2S0 BIT(0)
> +#define EN_I2S1 BIT(1)
> +#define EN_I2S2 BIT(2)
> +#define EN_I2S3 BIT(3)
> +#define AUDIO_FIFO_RESET BIT(7)
> +
> +#define REG_AUDIO_FMT 0xB9
> +#define REG_AUDIO_FIFO_SEL 0xBA
> +
> +#define REG_AUDIO_CTRL0 0xBB
> +#define AUDIO_FULL_PKT BIT(4)
> +#define AUDIO_16B_BOUND BIT(5)
> +
> +#define REG_AUDIO_CTRL1 0xBC
> +#define REG_AUDIO_INPUT_FREQ 0xBE
> +
> +#define REG_IEC958_STS0 0xBF
> +#define REG_IEC958_STS1 0xC0
> +#define REG_IEC958_STS2 0xC1
> +#define REG_IEC958_STS3 0xC2
> +#define REG_IEC958_STS4 0xC3
> +
> +#define REG_HPD_IRQ_TIME 0xC9
> +#define REG_AUX_DEBUG_MODE 0xCA
> +#define REG_AUX_OPT2 0xCB
> +#define REG_HDCP_OPT 0xCE
> +
> +#define REG_DATA_MUTE_CTRL 0xD3
> +#define ENABLE_ENHANCED_FRAME BIT(0)
> +#define ENABLE_AUTO_VIDEO_FIFO_RESET BIT(1)
> +#define EN_VID_MUTE BIT(4)
> +#define EN_AUD_MUTE BIT(5)
> +
> +#define REG_TIME_STMP_CTRL 0xD4
> +#define EN_ENHANCE_VID_STMP BIT(0)
> +#define EN_ENHANCE_AUD_STMP BIT(2)
> +#define M_STAMP_STEP 0x30
> +#define EN_SSC_GAT BIT(6)
> +
> +#define REG_INFOFRAME_CTRL 0xE8
> +#define EN_AVI_PKT BIT(0)
> +#define EN_AUD_PKT BIT(1)
> +#define EN_MPG_PKT BIT(2)
> +#define EN_GEN_PKT BIT(3)
> +#define EN_VID_TIME_STMP BIT(4)
> +#define EN_AUD_TIME_STMP BIT(5)
> +#define EN_VID_CTRL_PKT (EN_AVI_PKT | EN_VID_TIME_STMP)
> +#define EN_AUD_CTRL_PKT (EN_AUD_PKT | EN_AUD_TIME_STMP)
> +
> +#define REG_AUDIO_N_0_7 0xDE
> +#define REG_AUDIO_N_8_15 0xDF
> +#define REG_AUDIO_N_16_23 0xE0
> +
> +#define REG_AVI_INFO_DB1 0xE9
> +#define REG_AVI_INFO_DB2 0xEA
> +#define REG_AVI_INFO_DB3 0xEB
> +#define REG_AVI_INFO_DB4 0xEC
> +#define REG_AVI_INFO_DB5 0xED
> +#define REG_AVI_INFO_SUM 0xF6
> +
> +#define REG_AUD_INFOFRAM_DB1 0xF7
> +#define REG_AUD_INFOFRAM_DB2 0xF8
> +#define REG_AUD_INFOFRAM_DB3 0xF9
> +#define REG_AUD_INFOFRAM_DB4 0xFA
> +#define REG_AUD_INFOFRAM_SUM 0xFB
> +
> +/* the following six registers are in bank1 */
> +#define REG_DRV_0_DB_800_MV 0x7E
> +#define REG_PRE_0_DB_800_MV 0x7F
> +#define REG_PRE_3P5_DB_800_MV 0x81
> +#define REG_SSC_CTRL0 0x88
> +#define REG_SSC_CTRL1 0x89
> +#define REG_SSC_CTRL2 0x8A
> +
> +#define RBR DP_LINK_BW_1_62
> +#define HBR DP_LINK_BW_2_7
> +#define HBR2 DP_LINK_BW_5_4
> +#define HBR3 DP_LINK_BW_8_1
> +
> +/* Vendor option */
> +#define MAX_LANE_COUNT 4
> +#define MAX_LINK_RATE HBR
> +#define AUTO_TRAIN_RETRY 3
> +#define MAX_CR_LEVEL 0x03
> +#define MAX_EQ_LEVEL 0x03
> +#define AUX_WAIT_TIMEOUT_MS 15
> +#define AUX_FIFO_MAX_SIZE 32
> +#define PIXEL_CLK_DELAY 1
> +#define PIXEL_CLK_INVERSE 0
> +#define ADJUST_PHASE_THRESHOLD 80000
> +#define DPI_PIXEL_CLK_MAX 95000
> +#define DEFAULT_PWR_ON 0
> +#define DEFAULT_DRV_HOLD 0
> +
> +#define AUDIO_SELECT I2S
> +#define AUDIO_TYPE LPCM
> +#define AUDIO_SAMPLE_RATE SAMPLE_RATE_48K
> +#define AUDIO_CHANNEL_COUNT 2
> +
> +/*
> + * 0: Standard I2S
> + * 1: 32bit I2S
> + */
> +#define I2S_INPUT_FORMAT 1
> +
> +/*
> + * 0: Left-justified
> + * 1: Right-justified
> + */
> +#define I2S_JUSTIFIED 0
> +
> +/*
> + * 0: Data delay 1T correspond to WS
> + * 1: No data delay correspond to WS
> + */
> +#define I2S_DATA_DELAY 0
> +
> +/*
> + * 0: Left channel
> + * 1: Right channel
> + */
> +#define I2S_WS_CHANNEL 0
> +
> +/*
> + * 0: MSB shift first
> + * 1: LSB shift first
> + */
> +#define I2S_DATA_SEQUENCE 0
> +
> +/*
> + * Audio Sample Word Length
> + * WORD_LENGTH_16BIT
> + * WORD_LENGTH_18BIT
> + * WORD_LENGTH_20BIT
> + * WORD_LENGTH_24BIT
> + */
> +#define AUDIO_WORD_LENGTH WORD_LENGTH_24BIT
> +
> +enum aux_cmd_type {
> + CMD_AUX_NATIVE_READ = 0x0,
> + CMD_AUX_NATIVE_WRITE = 0x5,
> + CMD_AUX_I2C_EDID_READ = 0xB,
> +};
> +
> +enum aux_cmd_reply {
> + REPLY_ACK,
> + REPLY_NACK,
> + REPLY_DEFER,
> +};
> +
> +enum link_train_status {
> + LINK_IDLE,
> + LINK_BUSY,
> + LINK_OK,
> +};
> +
> +enum max_value_check {
> + VOLTAGE_SWING,
> + PRE_EMPHASIS,
> +};
> +
> +struct it6505_platform_data {
> + struct regulator *pwr18;
> + struct regulator *ovdd;
> + struct gpio_desc *gpiod_reset;
> +};
> +
> +enum it6505_audio_select {
> + I2S = 0,
> + SPDIF,
> +};
> +
> +enum it6505_audio_sample_rate {
> + SAMPLE_RATE_24K = 0x6,
> + SAMPLE_RATE_32K = 0x3,
> + SAMPLE_RATE_48K = 0x2,
> + SAMPLE_RATE_96K = 0xA,
> + SAMPLE_RATE_192K = 0xE,
> + SAMPLE_RATE_44_1K = 0x0,
> + SAMPLE_RATE_88_2K = 0x8,
> + SAMPLE_RATE_176_4K = 0xC,
> +};
> +
> +enum it6505_audio_type {
> + LPCM = 0,
> + NLPCM,
> + DSS,
> +};
> +
> +enum it6505_audio_word_length {
> + WORD_LENGTH_16BIT = 0,
> + WORD_LENGTH_18BIT,
> + WORD_LENGTH_20BIT,
> + WORD_LENGTH_24BIT,
> +};
> +
> +struct it6505_audio_data {
> + enum it6505_audio_select select;
> + enum it6505_audio_sample_rate sample_rate;
> + enum it6505_audio_type type;
> + enum it6505_audio_word_length word_length;
> + u8 channel_count;
> + u8 i2s_input_format;
> + u8 i2s_justified;
> + u8 i2s_data_delay;
> + u8 i2s_ws_channel;
> + u8 i2s_data_sequence;
> +};
> +
> +struct it6505_audio_sample_rate_map {
> + enum it6505_audio_sample_rate rate;
> + int sample_rate_value;
> +};
> +
> +struct it6505_drm_dp_link {
> + unsigned char revision;
> + unsigned int rate;
> + unsigned int num_lanes;
> + unsigned long capabilities;
> +};
> +
> +struct it6505 {
> + struct drm_dp_aux aux;
> + struct drm_bridge bridge;
> + struct i2c_client *client;
> + struct edid *edid;
> + struct drm_connector connector;
> + struct it6505_drm_dp_link link;
> + struct it6505_platform_data pdata;
> + struct mutex extcon_lock;
> + struct mutex mode_lock;
> + struct mutex aux_lock;
> + struct regmap *regmap;
> + struct drm_display_mode video_info;
> + struct notifier_block event_nb;
> + struct extcon_dev *extcon;
> + struct work_struct extcon_wq;
> + enum drm_connector_status connector_status;
> + int revision;
> + enum link_train_status link_state;
> + struct work_struct link_works;
> + u8 dpcd[DP_RECEIVER_CAP_SIZE];
> + u8 lane_count;
> + u8 link_rate_bw_code;
> + u8 sink_count;
> + bool step_train;
> + bool branch_device;
> + bool enable_ssc;
> + bool lane_swap_disabled;
> + bool lane_swap;
> + bool powered;
> + u32 afe_setting;
> + struct completion wait_edid_complete;
> + u8 auto_train_retry;
> + bool is_repeater;
> + bool enable_enhanced_frame;
> + hdmi_codec_plugged_cb plugged_cb;
> + struct device *codec_dev;
> + struct delayed_work delayed_audio;
> + bool enable_audio;
> + struct it6505_audio_data audio;
> +
> + /* it6505 driver hold option */
> + bool enable_drv_hold;
> +};
> +
> +struct it6505_step_train_para {
> + u8 voltage_swing[MAX_LANE_COUNT];
> + u8 pre_emphasis[MAX_LANE_COUNT];
> +};
> +
> +/*
> + * Vendor option afe settings for different platforms
> + * 0: for bitland 10e, quanta zde
> + * 1: for google kukui p1/p2, huaqin krane
> + */
> +
> +static u8 const afe_setting_table[][3] = {
> + {0x82, 0x00, 0xA6},
> + {0x93, 0x2A, 0x85}
> +};
> +
> +static const struct it6505_audio_sample_rate_map audio_sample_rate_map[] = {
> + {SAMPLE_RATE_24K, 24000},
> + {SAMPLE_RATE_32K, 32000},
> + {SAMPLE_RATE_48K, 48000},
> + {SAMPLE_RATE_96K, 96000},
> + {SAMPLE_RATE_192K, 192000},
> + {SAMPLE_RATE_44_1K, 44100},
> + {SAMPLE_RATE_88_2K, 88200},
> + {SAMPLE_RATE_176_4K, 176400},
> +};
> +
> +static const struct regmap_range it6505_bridge_volatile_ranges[] = {
> + { .range_min = 0, .range_max = 0xFF },
> +};
> +
> +static const struct regmap_access_table it6505_bridge_volatile_table = {
> + .yes_ranges = it6505_bridge_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(it6505_bridge_volatile_ranges),
> +};
> +
> +static const struct regmap_config it6505_regmap_config = {
> + .reg_bits = 8,
> + .val_bits = 8,
> + .volatile_table = &it6505_bridge_volatile_table,
> + .cache_type = REGCACHE_NONE,
> +};
> +
> +static int it6505_read(struct it6505 *it6505, unsigned int reg_addr)
> +{
> + unsigned int value;
> + int err;
> + struct device *dev = &it6505->client->dev;
> +
> + err = regmap_read(it6505->regmap, reg_addr, &value);
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "read failed reg[0x%x] err: %d", reg_addr,
> + err);
> + return err;
> + }
> +
> + return value;
> +}
> +
> +static int it6505_write(struct it6505 *it6505, unsigned int reg_addr,
> + unsigned int reg_val)
> +{
> + int err;
> + struct device *dev = &it6505->client->dev;
> +
> + err = regmap_write(it6505->regmap, reg_addr, reg_val);
> +
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "write failed reg[0x%x] = 0x%x err = %d",
> + reg_addr, reg_val, err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int it6505_set_bits(struct it6505 *it6505, unsigned int reg,
> + unsigned int mask, unsigned int value)
> +{
> + int err;
> + struct device *dev = &it6505->client->dev;
> +
> + err = regmap_update_bits(it6505->regmap, reg, mask, value);
> + if (err < 0) {
> + DRM_DEV_ERROR(
> + dev, "write reg[0x%x] = 0x%x mask = 0x%x failed err %d",
> + reg, value, mask, err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static int it6505_dpcd_read(struct it6505 *it6505, unsigned long offset)
> +{
> + u8 value;
> + int ret;
> + struct device *dev = &it6505->client->dev;
> +
> + ret = drm_dp_dpcd_readb(&it6505->aux, offset, &value);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "DPCD read failed [0x%lx] ret: %d", offset,
> + ret);
> + return ret;
> + }
> + return value;
> +}
> +
> +static int it6505_dpcd_write(struct it6505 *it6505, unsigned long offset,
> + unsigned long datain)
> +{
> + int ret;
> + struct device *dev = &it6505->client->dev;
> +
> + ret = drm_dp_dpcd_writeb(&it6505->aux, offset, datain);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "DPCD write failed [0x%lx] ret: %d", offset,
> + ret);
> + return ret;
> + }
> + return 0;
> +}
> +
> +static int it6505_get_dpcd(struct it6505 *it6505, int offset, u8 *dpcd, int num)
> +{
> + int ret;
> + struct device *dev = &it6505->client->dev;
> +
> + ret = drm_dp_dpcd_read(&it6505->aux, offset, dpcd, num);
> +
> + if (ret < 0)
> + return ret;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "ret = %d DPCD[0x%x] = 0x%*ph", ret, offset,
> + num, dpcd);
> +
> + return 0;
> +}
> +
> +static void it6505_dump(struct it6505 *it6505)
> +{
> + unsigned int i, j;
> + u8 regs[16];
> + struct device *dev = &it6505->client->dev;
> +
> + for (i = 0; i <= 0xff; i += 16) {
> + for (j = 0; j < 16; j++)
> + regs[j] = it6505_read(it6505, i + j);
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x] = %16ph", i, regs);
> + }
> +}
> +
> +static bool it6505_get_sink_hpd_status(struct it6505 *it6505)
> +{
> + int reg_0d;
> +
> + reg_0d = it6505_read(it6505, REG_SYSTEM_STS);
> +
> + if (reg_0d < 0)
> + return false;
> +
> + return (reg_0d & HPD_STS) ? true : false;
> +}
> +
> +static int it6505_read_word(struct it6505 *it6505, unsigned int reg)
> +{
> + int val0, val1;
> +
> + val0 = it6505_read(it6505, reg);
> + if (val0 < 0)
> + return val0;
> +
> + val1 = it6505_read(it6505, reg + 1);
> + if (val1 < 0)
> + return val1;
> +
> + return (val1 << 8) | val0;
> +}
> +
> +static void it6505_calc_video_info(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + int hsync_pol, vsync_pol, interlaced;
> + int htotal, hdes, hdew, hfph, hsyncw;
> + int vtotal, vdes, vdew, vfph, vsyncw;
> + int rddata, i, pclk, sum;
> +
> + usleep_range(10000, 15000);
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> + rddata = it6505_read(it6505, REG_INPUT_CTRL);
> + hsync_pol = rddata & INPUT_HSYNC_POL;
> + vsync_pol = (rddata & INPUT_VSYNC_POL) >> 2;
> + interlaced = (rddata & INPUT_INTERLACED) >> 4;
> +
> + htotal = it6505_read_word(it6505, REG_INPUT_HTOTAL) & 0x1FFF;
> + hdes = it6505_read_word(it6505, REG_INPUT_HACTIVE_START) & 0x1FFF;
> + hdew = it6505_read_word(it6505, REG_INPUT_HACTIVE_WIDTH) & 0x1FFF;
> + hfph = it6505_read_word(it6505, REG_INPUT_HFRONT_PORCH) & 0x1FFF;
> + hsyncw = it6505_read_word(it6505, REG_INPUT_HSYNC_WIDTH) & 0x1FFF;
> +
> + vtotal = it6505_read_word(it6505, REG_INPUT_VTOTAL) & 0xFFF;
> + vdes = it6505_read_word(it6505, REG_INPUT_VACTIVE_START) & 0xFFF;
> + vdew = it6505_read_word(it6505, REG_INPUT_VACTIVE_WIDTH) & 0xFFF;
> + vfph = it6505_read_word(it6505, REG_INPUT_VFRONT_PORCH) & 0xFFF;
> + vsyncw = it6505_read_word(it6505, REG_INPUT_VSYNC_WIDTH) & 0xFFF;
> +
> + sum = 0;
> + for (i = 0; i < 10; i++) {
> + it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER,
> + ENABLE_PCLK_COUNTER);
> + usleep_range(10000, 15000);
> + it6505_set_bits(it6505, REG_DATA_CTRL0, ENABLE_PCLK_COUNTER,
> + 0x00);
> + rddata = it6505_read_word(it6505, REG_PCLK_COUNTER_VALUE) &
> + 0xFFF;
> +
> + sum += rddata;
> + }
> +
> + if (sum == 0) {
> + DRM_DEV_DEBUG_DRIVER(dev, "calc video timing error");
> + return;
> + }
> +
> + sum /= 10;
> + pclk = 13500 * 2048 / sum;
> + it6505->video_info.clock = pclk;
> + it6505->video_info.hdisplay = hdew;
> + it6505->video_info.hsync_start = hdew + hfph;
> + it6505->video_info.hsync_end = hdew + hfph + hsyncw;
> + it6505->video_info.htotal = htotal;
> + it6505->video_info.vdisplay = vdew;
> + it6505->video_info.vsync_start = vdew + vfph;
> + it6505->video_info.vsync_end = vdew + vfph + vsyncw;
> + it6505->video_info.vtotal = vtotal;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, DRM_MODE_FMT,
> + DRM_MODE_ARG(&it6505->video_info));
> +}
> +
> +static int it6505_drm_dp_link_probe(struct drm_dp_aux *aux,
> + struct it6505_drm_dp_link *link)
> +{
> + u8 values[3];
> + int err;
> +
> + memset(link, 0, sizeof(*link));
> +
> + err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
> + if (err < 0)
> + return err;
> +
> + link->revision = values[0];
> + link->rate = drm_dp_bw_code_to_link_rate(values[1]);
> + link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
> +
> + if (values[2] & DP_ENHANCED_FRAME_CAP)
> + link->capabilities = 1;
> +
> + return 0;
> +}
> +
> +static int it6505_drm_dp_link_power_up(struct drm_dp_aux *aux,
> + struct it6505_drm_dp_link *link)
> +{
> + u8 value;
> + int err;
> +
> + /* DP_SET_POWER register is only available on DPCD v1.1 and later */
> + if (link->revision < 0x11)
> + return 0;
> +
> + err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
> + if (err < 0)
> + return err;
> +
> + value &= ~DP_SET_POWER_MASK;
> + value |= DP_SET_POWER_D0;
> +
> + err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
> + if (err < 0)
> + return err;
> +
> + /*
> + * According to the DP 1.1 specification, a "Sink Device must exit the
> + * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
> + * Control Field" (register 0x600).
> + */
> + usleep_range(1000, 2000);
> +
> + return 0;
> +}
> +
> +static void it6505_clear_int(struct it6505 *it6505)
> +{
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> + it6505_write(it6505, INT_STATUS_01, 0xFF);
> + it6505_write(it6505, INT_STATUS_02, 0xFF);
> + it6505_write(it6505, INT_STATUS_03, 0xFF);
> +}
> +
> +static void it6505_int_mask_enable(struct it6505 *it6505)
> +{
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> + it6505_write(it6505, INT_MASK_01, 0x1F);
> + it6505_write(it6505, INT_MASK_02, 0x07);
> + it6505_write(it6505, INT_MASK_03, 0xB0);
> +}
> +
> +static void it6505_int_mask_disable(struct it6505 *it6505)
> +{
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> + it6505_write(it6505, INT_MASK_01, 0x00);
> + it6505_write(it6505, INT_MASK_02, 0x00);
> + it6505_write(it6505, INT_MASK_03, 0x00);
> +}
> +
> +static void it6505_lane_termination_on(struct it6505 *it6505)
> +{
> + if (it6505_read(it6505, 0xCF) == 0xF0)
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x00);
> +
> + if (it6505_read(it6505, 0xCF) == 0x70) {
> + if (it6505->lane_swap) {
> + switch (it6505->lane_count) {
> + case 1:
> + case 2:
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
> + 0x0C, 0x08);
> + break;
> + default:
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
> + 0x0C, 0x0C);
> + break;
> + }
> + } else {
> + switch (it6505->lane_count) {
> + case 1:
> + case 2:
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
> + 0x0C, 0x04);
> + break;
> + default:
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL,
> + 0x0C, 0x0C);
> + break;
> + }
> + }
> + }
> +}
> +
> +static void it6505_lane_termination_off(struct it6505 *it6505)
> +{
> + if (it6505_read(it6505, 0xCF) == 0xF0)
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x80);
> +
> + if (it6505_read(it6505, 0xCF) == 0x70)
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x0C, 0x00);
> +}
> +
> +static void it6505_lane_power_on(struct it6505 *it6505)
> +{
> + it6505_set_bits(
> + it6505, REG_LINK_DRV, 0xF1,
> + ((BIT(it6505->lane_count) - 1)
> + << (it6505->lane_swap ? (8 - it6505->lane_count) : 4)) |
> + 0x01);
> +}
> +
> +static void it6505_lane_power_off(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_LINK_DRV, 0xF0, 0x00);
> +}
> +
> +static void it6505_lane_off(struct it6505 *it6505)
> +{
> + it6505_lane_power_off(it6505);
> + it6505_lane_termination_off(it6505);
> +}
> +
> +static void it6505_aux_termination_on(struct it6505 *it6505)
> +{
> + if (it6505_read(it6505, 0xCF) == 0xF0)
> + it6505_lane_termination_on(it6505);
> +
> + if (it6505_read(it6505, 0xCF) == 0x70)
> + it6505_set_bits(it6505, REG_DRV_LN_DATA_SEL, 0x80, 0x80);
> +}
> +
> +static void it6505_aux_power_on(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_AUX, 0x02, 0x02);
> +}
> +
> +static void it6505_aux_on(struct it6505 *it6505)
> +{
> + it6505_aux_power_on(it6505);
> + it6505_aux_termination_on(it6505);
> +}
> +
> +static void it6505_aux_reset(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_RESET_CTRL, AUX_RESET, AUX_RESET);
> + it6505_set_bits(it6505, REG_RESET_CTRL, AUX_RESET, 0x00);
> +}
> +
> +static bool it6505_reset_logic(struct it6505 *it6505)
> +{
> + regmap_write(it6505->regmap, REG_RESET_CTRL, ALL_LOGIC_RESET);
> + usleep_range(1000, 1500);
> +
> + it6505->revision = it6505_read(it6505, REG_IC_VER);
> +
> + if (it6505->revision < 0)
> + return false;
> +
> + return true;
> +}
> +
> +static bool it6505_aux_op_finished(struct it6505 *it6505)
> +{
> + int reg2b = it6505_read(it6505, REG_AUX_CMD_REQ);
> +
> + if (reg2b < 0)
> + return false;
> +
> + return (reg2b & AUX_BUSY) == 0;
> +}
> +
> +static int it6505_aux_wait(struct it6505 *it6505)
> +{
> + int status;
> + unsigned long timeout;
> + struct device *dev = &it6505->client->dev;
> +
> + timeout = jiffies + msecs_to_jiffies(AUX_WAIT_TIMEOUT_MS) + 1;
> +
> + while (!it6505_aux_op_finished(it6505)) {
> + if (time_after(jiffies, timeout)) {
> + DRM_DEV_ERROR(dev, "Timed out waiting AUX to finish");
> + return -ETIMEDOUT;
> + }
> + usleep_range(1000, 2000);
> + }
> +
> + status = it6505_read(it6505, REG_AUX_ERROR_STS);
> + if (status < 0) {
> + DRM_DEV_ERROR(dev, "Failed to read AUX channel: %d", status);
> + return status;
> + }
> +
> + return 0;
> +}
> +
> +static ssize_t it6505_aux_operation(struct it6505 *it6505,
> + enum aux_cmd_type cmd,
> + unsigned int address, u8 *buffer,
> + size_t size, enum aux_cmd_reply *reply)
> +{
> + int i, status;
> + bool aux_write_check = false;
> +
> + if (!it6505_get_sink_hpd_status(it6505))
> + return -EIO;
> +
> + /* set AUX user mode */
> + it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, AUX_USER_MODE);
> +
> +aux_op_start:
> +
> + if (cmd == CMD_AUX_I2C_EDID_READ) {
> + /* DP AUX EDID FIFO has maximum length of n bytes. */
> + size = min_t(size_t, size, AUX_FIFO_MAX_SIZE);
> + /* Enable AUX FIFO read back and clear FIFO */
> + it6505_set_bits(it6505, REG_AUX_CTRL,
> + AUX_EN_FIFO_READ | CLR_EDID_FIFO,
> + AUX_EN_FIFO_READ | CLR_EDID_FIFO);
> +
> + it6505_set_bits(it6505, REG_AUX_CTRL,
> + AUX_EN_FIFO_READ | CLR_EDID_FIFO,
> + AUX_EN_FIFO_READ);
> + } else {
> + /* The DP AUX transmit buffer has 4 bytes. */
> + size = min_t(size_t, size, 4);
> + it6505_set_bits(it6505, REG_AUX_CTRL, AUX_NO_SEGMENT_WR,
> + AUX_NO_SEGMENT_WR);
> + }
> +
> + /* Start Address[7:0] */
> + it6505_write(it6505, REG_AUX_ADR_0_7, (address >> 0) & 0xFF);
> + /* Start Address[15:8] */
> + it6505_write(it6505, REG_AUX_ADR_8_15, (address >> 8) & 0xFF);
> + /* WriteNum[3:0]+StartAdr[19:16] */
> + it6505_write(it6505, REG_AUX_ADR_16_19,
> + ((address >> 16) & 0x0F) | ((size - 1) << 4));
> +
> + if (cmd == CMD_AUX_NATIVE_WRITE)
> + regmap_bulk_write(it6505->regmap, REG_AUX_OUT_DATA0, buffer,
> + size);
> +
> + /* Aux Fire */
> + it6505_write(it6505, REG_AUX_CMD_REQ, cmd);
> +
> + status = it6505_aux_wait(it6505);
> + if (status < 0)
> + goto aux_op_err;
> +
> + status = it6505_read(it6505, REG_AUX_ERROR_STS);
> + if (status < 0)
> + goto aux_op_err;
> +
> + switch ((status >> 6) & 0x3) {
> + case 0:
> + *reply = REPLY_ACK;
> + break;
> + case 1:
> + *reply = REPLY_DEFER;
> + status = -EAGAIN;
> + goto aux_op_err;
> + case 2:
> + *reply = REPLY_NACK;
> + status = -EIO;
> + goto aux_op_err;
> + case 3:
> + status = -ETIMEDOUT;
> + goto aux_op_err;
> + }
> +
> + /* Read back Native Write data */
> + if (cmd == CMD_AUX_NATIVE_WRITE) {
> + aux_write_check = true;
> + cmd = CMD_AUX_NATIVE_READ;
> + goto aux_op_start;
> + }
> +
> + if (cmd == CMD_AUX_I2C_EDID_READ) {
> + for (i = 0; i < size; i++) {
> + status = it6505_read(it6505, REG_AUX_DATA_FIFO);
> + if (status < 0)
> + goto aux_op_err;
> + buffer[i] = status;
> + }
> + } else {
> + for (i = 0; i < size; i++) {
> + status = it6505_read(it6505, REG_AUX_DATA_0_7 + i);
> + if (status < 0)
> + goto aux_op_err;
> +
> + if (aux_write_check && buffer[size - 1 - i] != status)
> + break;
> +
> + buffer[size - 1 - i] = status;
> + }
> + }
> +
> + status = i;
> +
> +aux_op_err:
> +
> + if (cmd == CMD_AUX_I2C_EDID_READ) {
> + /*clear AUX FIFO */
> + it6505_set_bits(it6505, REG_AUX_CTRL,
> + AUX_EN_FIFO_READ | CLR_EDID_FIFO,
> + AUX_EN_FIFO_READ | CLR_EDID_FIFO);
> + it6505_set_bits(it6505, REG_AUX_CTRL,
> + AUX_EN_FIFO_READ | CLR_EDID_FIFO, 0x00);
> + }
> +
> + /* Leave AUX user mode */
> + it6505_set_bits(it6505, REG_AUX_CTRL, AUX_USER_MODE, 0);
> +
> + return status;
> +}
> +
> +static ssize_t it6505_aux_do_transfer(struct it6505 *it6505,
> + enum aux_cmd_type cmd,
> + unsigned int address, u8 *buffer,
> + size_t size, enum aux_cmd_reply *reply)
> +{
> + int i, ret, ret_size = 0, request_size;
> +
> + mutex_lock(&it6505->aux_lock);
> + for (i = 0; i < size; i += 4) {
> + request_size = min((int)size - i, 4);
> + ret = it6505_aux_operation(it6505, cmd, address + i,
> + buffer + i, request_size, reply);
> + if (ret < 0) {
> + ret_size = ret;
> + goto aux_op_err;
> + }
> +
> + ret_size += ret;
> + }
> +aux_op_err:
> +
> + mutex_unlock(&it6505->aux_lock);
> + return ret_size;
> +}
> +
> +static ssize_t it6505_aux_transfer(struct drm_dp_aux *aux,
> + struct drm_dp_aux_msg *msg)
> +{
> + struct it6505 *it6505 = container_of(aux, struct it6505, aux);
> + u8 cmd;
> + bool is_i2c = !(msg->request & DP_AUX_NATIVE_WRITE);
> + int ret;
> + enum aux_cmd_reply reply;
> +
> + /* IT6505 doesn't support arbitrary I2C read / write. */
> + if (is_i2c)
> + return -EINVAL;
> +
> + switch (msg->request) {
> + case DP_AUX_NATIVE_READ:
> + cmd = CMD_AUX_NATIVE_READ;
> + break;
> + case DP_AUX_NATIVE_WRITE:
> + cmd = CMD_AUX_NATIVE_WRITE;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + ret = it6505_aux_do_transfer(it6505, cmd, msg->address, msg->buffer,
> + msg->size, &reply);
> + if (ret < 0)
> + return ret;
> +
> + switch (reply) {
> + case REPLY_ACK:
> + msg->reply = DP_AUX_NATIVE_REPLY_ACK;
> + break;
> + case REPLY_NACK:
> + msg->reply = DP_AUX_NATIVE_REPLY_NACK;
> + break;
> + case REPLY_DEFER:
> + msg->reply = DP_AUX_NATIVE_REPLY_DEFER;
> + break;
> + }
> +
> + return ret;
> +}
> +
> +static int it6505_get_edid_block(void *data, u8 *buf, unsigned int block,
> + size_t len)
> +{
> + struct it6505 *it6505 = data;
> + struct device *dev = &it6505->client->dev;
> + enum aux_cmd_reply reply;
> + int offset, ret, aux_retry = 100;
> +
> + it6505_aux_reset(it6505);
> + DRM_DEV_DEBUG_DRIVER(dev, "block number = %d", block);
> +
> + for (offset = 0; offset < EDID_LENGTH;) {
> + ret = it6505_aux_do_transfer(it6505, CMD_AUX_I2C_EDID_READ,
> + block * EDID_LENGTH + offset,
> + buf + offset, 8, &reply);
> +
> + if (ret < 0 && ret != -EAGAIN)
> + return ret;
> +
> + switch (reply) {
> + case REPLY_ACK:
> + DRM_DEV_DEBUG_DRIVER(dev, "[0x%02x]: %8ph", offset,
> + buf + offset);
> + offset += 8;
> + aux_retry = 100;
> + break;
> + case REPLY_NACK:
> + return -EIO;
> + case REPLY_DEFER:
> + msleep(20);
> + if (!(--aux_retry))
> + return -EIO;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void it6505_variable_config(struct it6505 *it6505)
> +{
> + it6505->link_rate_bw_code = HBR;
> + it6505->lane_count = MAX_LANE_COUNT;
> + it6505->link_state = LINK_IDLE;
> + it6505->auto_train_retry = AUTO_TRAIN_RETRY;
> + it6505->audio.select = AUDIO_SELECT;
> + it6505->audio.sample_rate = AUDIO_SAMPLE_RATE;
> + it6505->audio.channel_count = AUDIO_CHANNEL_COUNT;
> + it6505->audio.type = AUDIO_TYPE;
> + it6505->audio.i2s_input_format = I2S_INPUT_FORMAT;
> + it6505->audio.i2s_justified = I2S_JUSTIFIED;
> + it6505->audio.i2s_data_delay = I2S_DATA_DELAY;
> + it6505->audio.i2s_ws_channel = I2S_WS_CHANNEL;
> + it6505->audio.i2s_data_sequence = I2S_DATA_SEQUENCE;
> + it6505->audio.word_length = AUDIO_WORD_LENGTH;
> +}
> +
> +static int it6505_send_video_infoframe(struct it6505 *it6505,
> + struct hdmi_avi_infoframe *frame)
> +{
> + u8 buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AVI_INFOFRAME_SIZE];
> + int err;
> + struct device *dev = &it6505->client->dev;
> +
> + err = hdmi_avi_infoframe_pack(frame, buffer, sizeof(buffer));
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "Failed to pack AVI infoframe: %d", err);
> + return err;
> + }
> +
> + err = it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AVI_PKT, 0x00);
> + if (err)
> + return err;
> +
> + err = regmap_bulk_write(it6505->regmap, REG_AVI_INFO_DB1,
> + buffer + HDMI_INFOFRAME_HEADER_SIZE,
> + frame->length);
> + if (err)
> + return err;
> +
> + err = it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AVI_PKT,
> + EN_AVI_PKT);
> + if (err)
> + return err;
> +
> + return 0;
> +}
> +
> +static void it6505_get_extcon_property(struct it6505 *it6505)
> +{
> + int err;
> + union extcon_property_value property;
> + struct device *dev = &it6505->client->dev;
> +
> + if (it6505->extcon && !it6505->lane_swap_disabled) {
> + err = extcon_get_property(it6505->extcon, EXTCON_DISP_DP,
> + EXTCON_PROP_USB_TYPEC_POLARITY,
> + &property);
> + if (err) {
> + DRM_DEV_ERROR(dev, "get property fail!");
> + return;
> + }
> + it6505->lane_swap = property.intval;
> + }
> +}
> +
> +static void it6505_clk_phase_adjustment(struct it6505 *it6505,
> + const struct drm_display_mode *mode)
> +{
> + it6505_set_bits(it6505, REG_CLK_CTRL0, M_PCLK_DELAY,
> + mode->clock < ADJUST_PHASE_THRESHOLD ? PIXEL_CLK_DELAY : 0);
> + it6505_set_bits(it6505, REG_DATA_CTRL0, VIDEO_LATCH_EDGE,
> + PIXEL_CLK_INVERSE << 4);
> +}
> +
> +static void it6505_init(struct it6505 *it6505)
> +{
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> + it6505_write(it6505, REG_AUX_OPT, AUX_AUTO_RST | AUX_FIX_FREQ);
> + it6505_write(it6505, REG_AUX_CTRL, AUX_NO_SEGMENT_WR);
> + it6505_write(it6505, REG_HDCP_CTRL2, HDCP_AN_SEL | HDCP_HW_HPDIRQ_ACT);
> + it6505_write(it6505, REG_VID_BUS_CTRL0, IN_DDR | DDR_CD);
> + it6505_write(it6505, REG_VID_BUS_CTRL1, 0x01);
> + it6505_write(it6505, REG_AUDIO_CTRL0, AUDIO_16B_BOUND);
> +
> + /* chip internal setting, don't modify */
> + it6505_write(it6505, REG_HPD_IRQ_TIME, 0xF5);
> + it6505_write(it6505, REG_AUX_DEBUG_MODE, 0x4D);
> + it6505_write(it6505, REG_AUX_OPT2, 0x17);
> + it6505_write(it6505, REG_HDCP_OPT, 0x60);
> + it6505_write(it6505, REG_DATA_MUTE_CTRL,
> + EN_VID_MUTE | EN_AUD_MUTE | ENABLE_AUTO_VIDEO_FIFO_RESET);
> + it6505_write(it6505, REG_TIME_STMP_CTRL,
> + EN_SSC_GAT | EN_ENHANCE_VID_STMP | EN_ENHANCE_AUD_STMP);
> + it6505_write(it6505, REG_INFOFRAME_CTRL, 0x00);
> + it6505_write(it6505, REG_BANK_SEL, 0x01);
> + it6505_write(it6505, REG_DRV_0_DB_800_MV,
> + afe_setting_table[it6505->afe_setting][0]);
> + it6505_write(it6505, REG_PRE_0_DB_800_MV,
> + afe_setting_table[it6505->afe_setting][1]);
> + it6505_write(it6505, REG_PRE_3P5_DB_800_MV,
> + afe_setting_table[it6505->afe_setting][2]);
> + it6505_write(it6505, REG_SSC_CTRL0, 0x9E);
> + it6505_write(it6505, REG_SSC_CTRL1, 0x1C);
> + it6505_write(it6505, REG_SSC_CTRL2, 0x42);
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> +}
> +
> +static void it6505_video_disable(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE);
> + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00);
> + it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET);
> +}
> +
> +static void it6505_video_reset(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, EN_VID_MUTE);
> + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_VID_CTRL_PKT, 0x00);
> + it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, VIDEO_RESET);
> + it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, RST_501_FIFO);
> + it6505_set_bits(it6505, REG_501_FIFO_CTRL, RST_501_FIFO, 0x00);
> + it6505_set_bits(it6505, REG_RESET_CTRL, VIDEO_RESET, 0x00);
> +}
> +
> +static void it6505_update_video_prarmeter(struct it6505 *it6505,
> + const struct drm_display_mode *mode)
> +{
> + it6505_clk_phase_adjustment(it6505, mode);
> + it6505_video_disable(it6505);
> +}
> +
> +static bool it6505_audio_input(struct it6505 *it6505)
> +{
> + int reg05, regbe;
> +
> + reg05 = it6505_read(it6505, REG_RESET_CTRL);
> + it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, 0x00);
> + usleep_range(3000, 4000);
> + regbe = it6505_read(it6505, REG_AUDIO_INPUT_FREQ);
> + it6505_write(it6505, REG_RESET_CTRL, reg05);
> +
> + return regbe != 0xFF;
> +}
> +
> +static void it6505_setup_audio_channel_status(struct it6505 *it6505)
> +{
> + enum it6505_audio_sample_rate sample_rate = it6505->audio.sample_rate;
> + u8 audio_word_length_map[] = {0x02, 0x04, 0x03, 0x0B};
> +
> + /* Channel Status */
> + it6505_write(it6505, REG_IEC958_STS0, it6505->audio.type << 1);
> + it6505_write(it6505, REG_IEC958_STS1, 0x00);
> + it6505_write(it6505, REG_IEC958_STS2, 0x00);
> + it6505_write(it6505, REG_IEC958_STS3, sample_rate);
> + it6505_write(it6505, REG_IEC958_STS4, (~sample_rate << 4) |
> + audio_word_length_map[it6505->audio.word_length]);
> +}
> +
> +static void it6505_setup_audio_format(struct it6505 *it6505)
> +{
> + /* I2S MODE */
> + it6505_write(it6505, REG_AUDIO_FMT,
> + (it6505->audio.word_length << 5) |
> + (it6505->audio.i2s_data_sequence << 4) |
> + (it6505->audio.i2s_ws_channel << 3) |
> + (it6505->audio.i2s_data_delay << 2) |
> + (it6505->audio.i2s_justified << 1) |
> + it6505->audio.i2s_input_format);
> + if (it6505->audio.select == SPDIF) {
> + it6505_write(it6505, REG_AUDIO_FIFO_SEL, 0x00);
> + /* 0x30 = 128*FS */
> + it6505_set_bits(it6505, REG_AUX_OPT, 0xF0, 0x30);
> + } else {
> + it6505_write(it6505, REG_AUDIO_FIFO_SEL, 0xE4);
> + }
> +
> + it6505_write(it6505, REG_AUDIO_CTRL0, 0x20);
> + it6505_write(it6505, REG_AUDIO_CTRL1, 0x00);
> +}
> +
> +static void it6505_enable_audio_source(struct it6505 *it6505)
> +{
> + unsigned int audio_source_count;
> +
> + audio_source_count = BIT(DIV_ROUND_UP(it6505->audio.channel_count, 2))
> + - 1;
> +
> + audio_source_count |= it6505->audio.select << 4;
> +
> + it6505_write(it6505, REG_AUDIO_SRC_CTRL, audio_source_count);
> +}
> +
> +static void it6505_enable_audio_infoframe(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + u8 audio_info_ca[] = { 0x00, 0x00, 0x01, 0x03, 0x07, 0x0B, 0x0F, 0x1F };
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "infoframe channel_allocation:0x%02x",
> + audio_info_ca[it6505->audio.channel_count - 1]);
> +
> + it6505_write(it6505, REG_AUD_INFOFRAM_DB1, it6505->audio.channel_count
> + - 1);
> + it6505_write(it6505, REG_AUD_INFOFRAM_DB2, 0x00);
> + it6505_write(it6505, REG_AUD_INFOFRAM_DB3,
> + audio_info_ca[it6505->audio.channel_count - 1]);
> + it6505_write(it6505, REG_AUD_INFOFRAM_DB4, 0x00);
> + it6505_write(it6505, REG_AUD_INFOFRAM_SUM, 0x00);
> +
> + /* Enable Audio InfoFrame */
> + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AUD_CTRL_PKT,
> + EN_AUD_CTRL_PKT);
> +}
> +
> +static void it6505_disable_audio(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, EN_AUD_MUTE);
> + it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, M_AUDIO_I2S_EN, 0x00);
> + it6505_set_bits(it6505, REG_INFOFRAME_CTRL, EN_AUD_CTRL_PKT, 0x00);
> + it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, AUDIO_RESET);
> + it6505->enable_audio = false;
> +}
> +
> +static void it6505_enable_audio(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + int regbe;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "start");
> + it6505_disable_audio(it6505);
> +
> + it6505_setup_audio_channel_status(it6505);
> + it6505_setup_audio_format(it6505);
> + it6505_enable_audio_source(it6505);
> + it6505_enable_audio_infoframe(it6505);
> +
> + it6505_write(it6505, REG_AUDIO_N_0_7, 0x00);
> + it6505_write(it6505, REG_AUDIO_N_8_15, 0x80);
> + it6505_write(it6505, REG_AUDIO_N_16_23, 0x00);
> +
> + it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, AUDIO_FIFO_RESET,
> + AUDIO_FIFO_RESET);
> + it6505_set_bits(it6505, REG_AUDIO_SRC_CTRL, AUDIO_FIFO_RESET, 0x00);
> + it6505_set_bits(it6505, REG_RESET_CTRL, AUDIO_RESET, 0x00);
> + regbe = it6505_read(it6505, REG_AUDIO_INPUT_FREQ);
> + DRM_DEV_DEBUG_DRIVER(dev, "regbe:0x%02x audio input fs: %d.%d kHz",
> + regbe, 6750 / regbe, 67500 % regbe);
> + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_AUD_MUTE, 0x00);
> +}
> +
> +static bool it6505_use_step_train_check(struct it6505 *it6505)
> +{
> + if (it6505->link.revision >= 0x12)
> + return it6505->dpcd[DP_TRAINING_AUX_RD_INTERVAL] >= 0x01;
> +
> + return true;
> +}
> +
> +static void it6505_parse_link_capabilities(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + struct it6505_drm_dp_link *link = &it6505->link;
> + int bcaps;
> +
> + if (it6505->dpcd[0] == 0) {
> + it6505_aux_on(it6505);
> + it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd,
> + ARRAY_SIZE(it6505->dpcd));
> + }
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "DPCD Rev.: %d.%d",
> + link->revision >> 4, link->revision & 0x0F);
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "Sink max link rate: %d.%d Gbps per lane",
> + link->rate / 100000, link->rate / 1000 % 100);
> +
> + it6505->link_rate_bw_code = drm_dp_link_rate_to_bw_code(link->rate);
> + DRM_DEV_DEBUG_DRIVER(dev, "link rate bw code:0x%02x",
> + it6505->link_rate_bw_code);
> + it6505->link_rate_bw_code = min((int)it6505->link_rate_bw_code,
> + MAX_LINK_RATE);
> +
> + it6505->lane_count = link->num_lanes;
> + DRM_DEV_DEBUG_DRIVER(dev, "Sink support %d lanes training",
> + it6505->lane_count);
> + it6505->lane_count = min((int)it6505->lane_count, MAX_LANE_COUNT);
> +
> + it6505->branch_device = drm_dp_is_branch(it6505->dpcd);
> + DRM_DEV_DEBUG_DRIVER(dev, "Sink %sbranch device",
> + it6505->branch_device ? "" : "Not ");
> +
> + it6505->enable_enhanced_frame = !!link->capabilities;
> + DRM_DEV_DEBUG_DRIVER(dev, "Sink %sSupport Enhanced Framing",
> + it6505->enable_enhanced_frame ? "" : "Not ");
> +
> + it6505->enable_ssc = !!(it6505->dpcd[DP_MAX_DOWNSPREAD] &
> + DP_MAX_DOWNSPREAD_0_5);
> + DRM_DEV_DEBUG_DRIVER(dev, "Maximum Down-Spread: %s, %ssupport SSC!",
> + it6505->enable_ssc ? "0.5" : "0",
> + it6505->enable_ssc ? "" : "Not ");
> +
> + it6505->step_train = it6505_use_step_train_check(it6505);
> + if (it6505->step_train)
> + DRM_DEV_DEBUG_DRIVER(dev, "auto train fail, will step train");
> +
> + bcaps = it6505_dpcd_read(it6505, DP_AUX_HDCP_BCAPS);
> + DRM_DEV_DEBUG_DRIVER(dev, "bcaps:0x%02x", bcaps);
> + if (bcaps & DP_BCAPS_HDCP_CAPABLE) {
> + it6505->is_repeater = !!(bcaps & DP_BCAPS_REPEATER_PRESENT);
> + DRM_DEV_DEBUG_DRIVER(dev, "Support HDCP! Downstream is %s!",
> + it6505->is_repeater ? "repeater" :
> + "receiver");
> + } else {
> + DRM_DEV_DEBUG_DRIVER(dev, "Sink not support HDCP!");
> + }
> +}
> +
> +static void it6505_setup_ssc(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, SPREAD_AMP_5,
> + it6505->enable_ssc ? SPREAD_AMP_5 : 0x00);
> + if (it6505->enable_ssc) {
> + it6505_write(it6505, REG_BANK_SEL, 0x01);
> + it6505_write(it6505, REG_SSC_CTRL0, 0x9E);
> + it6505_write(it6505, REG_SSC_CTRL1, 0x1C);
> + it6505_write(it6505, REG_SSC_CTRL2, 0x42);
> + it6505_write(it6505, REG_BANK_SEL, 0x00);
> + it6505_write(it6505, REG_SP_CTRL0, 0x07);
> + it6505_write(it6505, REG_IP_CTRL1, 0x29);
> + it6505_write(it6505, REG_IP_CTRL2, 0x03);
> + /* Stamp Interrupt Step */
> + it6505_set_bits(it6505, REG_TIME_STMP_CTRL, M_STAMP_STEP,
> + 0x10);
> + it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL,
> + DP_SPREAD_AMP_0_5);
> + } else {
> + it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL, 0x00);
> + it6505_set_bits(it6505, REG_TIME_STMP_CTRL, M_STAMP_STEP,
> + 0x00);
> + }
> +}
> +
> +static inline void it6505_link_rate_setup(struct it6505 *it6505)
> +{
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_LBR,
> + (it6505->link_rate_bw_code == RBR) ? FORCE_LBR : 0x00);
> + it6505_set_bits(it6505, REG_LINK_DRV, DRV_HS,
> + (it6505->link_rate_bw_code == RBR) ? 0x00 : DRV_HS);
> +}
> +
> +static void it6505_lane_count_setup(struct it6505 *it6505)
> +{
> + it6505_get_extcon_property(it6505);
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, LANE_SWAP,
> + it6505->lane_swap ? LANE_SWAP : 0x00);
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, LANE_COUNT_MASK,
> + (it6505->lane_count - 1) << 1);
> +}
> +
> +static void it6505_link_training_setup(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> +
> + if (it6505->enable_enhanced_frame)
> + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL,
> + ENABLE_ENHANCED_FRAME, ENABLE_ENHANCED_FRAME);
> +
> + it6505_link_rate_setup(it6505);
> + it6505_lane_count_setup(it6505);
> + it6505_setup_ssc(it6505);
> + DRM_DEV_DEBUG_DRIVER(dev,
> + "%s, %d lanes, %sable ssc, %sable enhanced frame",
> + it6505->link_rate_bw_code != RBR ? "HBR" : "RBR",
> + it6505->lane_count,
> + it6505->enable_ssc ? "en" : "dis",
> + it6505->enable_enhanced_frame ? "en" : "dis");
> +}
> +
> +static bool it6505_link_start_auto_train(struct it6505 *it6505)
> +{
> + int timeout = 500, link_training_state;
> +
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0,
> + FORCE_CR_DONE | FORCE_EQ_DONE, 0x00);
> + it6505_write(it6505, REG_TRAIN_CTRL1, FORCE_RETRAIN);
> + it6505_write(it6505, REG_TRAIN_CTRL1, AUTO_TRAIN);
> +
> + while (timeout > 0) {
> + usleep_range(1000, 2000);
> + link_training_state = it6505_read(it6505, REG_LINK_TRAIN_STS);
> +
> + if ((link_training_state > 0) &&
> + (link_training_state & LINK_STATE_NORP))
> + return true;
> +
> + timeout--;
> + }
> +
> + return false;
> +}
> +
> +static int it6505_drm_dp_link_configure(struct it6505 *it6505)
> +{
> + u8 values[2];
> + int err;
> + struct drm_dp_aux *aux = &it6505->aux;
> +
> + values[0] = it6505->link_rate_bw_code;
> + values[1] = it6505->lane_count;
> +
> + if (it6505->enable_enhanced_frame)
> + values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
> +
> + err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
> + if (err < 0)
> + return err;
> +
> + return 0;
> +}
> +
> +static u8 dp_link_status(const u8 link_status[DP_LINK_STATUS_SIZE], int r)
> +{
> + return link_status[r - DP_LANE0_1_STATUS];
> +}
> +
> +static bool it6505_check_max_value(enum max_value_check type,
> + u8 lane_voltage_swing_pre_emphasis)
> +{
> + if (type == VOLTAGE_SWING)
> + return ((lane_voltage_swing_pre_emphasis & 0x03) ==
> + MAX_CR_LEVEL);
> + else
> + return ((lane_voltage_swing_pre_emphasis & 0x03) ==
> + MAX_EQ_LEVEL);
> +}
> +
> +static bool it6505_check_max_voltage_swing_reached(u8 *lane_voltage_swing,
> + u8 lane_count)
> +{
> + u8 i;
> +
> + for (i = 0; i < lane_count; i++) {
> + if (lane_voltage_swing[i] & DP_TRAIN_MAX_SWING_REACHED)
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static bool it6505_step_train_lane_voltage_pre_emphasis_set(
> + struct it6505 *it6505,
> + struct it6505_step_train_para *lane_voltage_pre_emphasis,
> + u8 *lane_voltage_pre_emphasis_set)
> +{
> + u8 i;
> +
> + for (i = 0; i < it6505->lane_count; i++) {
> + lane_voltage_pre_emphasis->voltage_swing[i] &= 0x03;
> + lane_voltage_pre_emphasis_set[i] =
> + lane_voltage_pre_emphasis->voltage_swing[i];
> + if (it6505_check_max_value(
> + VOLTAGE_SWING,
> + lane_voltage_pre_emphasis->voltage_swing[i]))
> + lane_voltage_pre_emphasis_set[i] |=
> + DP_TRAIN_MAX_SWING_REACHED;
> +
> + lane_voltage_pre_emphasis->pre_emphasis[i] &= 0x03;
> + lane_voltage_pre_emphasis_set[i] |=
> + lane_voltage_pre_emphasis->pre_emphasis[i]
> + << DP_TRAIN_PRE_EMPHASIS_SHIFT;
> + if (it6505_check_max_value(
> + PRE_EMPHASIS,
> + lane_voltage_pre_emphasis->pre_emphasis[i]))
> + lane_voltage_pre_emphasis_set[i] |=
> + DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
> + it6505_dpcd_write(it6505, DP_TRAINING_LANE0_SET + i,
> + lane_voltage_pre_emphasis_set[i]);
> +
> + if (lane_voltage_pre_emphasis_set[i] !=
> + it6505_dpcd_read(it6505, DP_TRAINING_LANE0_SET + i))
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static bool
> +it6505_step_cr_train(struct it6505 *it6505,
> + struct it6505_step_train_para *lane_voltage_pre_emphasis)
> +{
> + u8 loop_count = 0, i = 0, j;
> + u8 link_status[DP_LINK_STATUS_SIZE] = { 0 };
> + u8 lane_level_config[MAX_LANE_COUNT] = { 0 };
> + int pre_emphasis_adjust = -1, voltage_swing_adjust = -1;
> +
> + it6505_dpcd_write(it6505, DP_DOWNSPREAD_CTRL,
> + it6505->enable_ssc ? DP_SPREAD_AMP_0_5 : 0x00);
> + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
> + DP_TRAINING_PATTERN_1);
> +
> + while (loop_count < 5 && i < 10) {
> + i++;
> + if (!it6505_step_train_lane_voltage_pre_emphasis_set(
> + it6505, lane_voltage_pre_emphasis,
> + lane_level_config))
> + continue;
> + drm_dp_link_train_clock_recovery_delay(it6505->dpcd);
> + drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
> +
> + if (drm_dp_clock_recovery_ok(link_status, it6505->lane_count)) {
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_CR_DONE,
> + FORCE_CR_DONE);
> + return true;
> + }
> + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "cr not done");
> +
> + if (it6505_check_max_voltage_swing_reached(lane_level_config,
> + it6505->lane_count))
> + goto cr_train_fail;
> +
> + for (j = 0; j < it6505->lane_count; j++) {
> + lane_voltage_pre_emphasis->voltage_swing[j] =
> + drm_dp_get_adjust_request_voltage(link_status,
> + j) >>
> + DP_TRAIN_VOLTAGE_SWING_SHIFT;
> + lane_voltage_pre_emphasis->pre_emphasis[j] =
> + drm_dp_get_adjust_request_pre_emphasis(
> + link_status, j) >>
> + DP_TRAIN_PRE_EMPHASIS_SHIFT;
> + if ((voltage_swing_adjust ==
> + lane_voltage_pre_emphasis->voltage_swing[j]) &&
> + (pre_emphasis_adjust ==
> + lane_voltage_pre_emphasis->pre_emphasis[j])) {
> + loop_count++;
> + continue;
> + }
> +
> + voltage_swing_adjust =
> + lane_voltage_pre_emphasis->voltage_swing[j];
> + pre_emphasis_adjust =
> + lane_voltage_pre_emphasis->pre_emphasis[j];
> + loop_count = 0;
> +
> + if (voltage_swing_adjust + pre_emphasis_adjust > 0x03)
> + lane_voltage_pre_emphasis->voltage_swing[j] =
> + MAX_EQ_LEVEL -
> + lane_voltage_pre_emphasis
> + ->pre_emphasis[j];
> + }
> + }
> +
> +cr_train_fail:
> + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
> + DP_TRAINING_PATTERN_DISABLE);
> +
> + return false;
> +}
> +
> +static bool
> +it6505_step_eq_train(struct it6505 *it6505,
> + struct it6505_step_train_para *lane_voltage_pre_emphasis)
> +{
> + u8 loop_count = 0, i, link_status[DP_LINK_STATUS_SIZE] = { 0 };
> + u8 lane_level_config[MAX_LANE_COUNT] = { 0 };
> +
> + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
> + DP_TRAINING_PATTERN_2);
> +
> + while (loop_count < 6) {
> + loop_count++;
> +
> + if (!it6505_step_train_lane_voltage_pre_emphasis_set(
> + it6505, lane_voltage_pre_emphasis,
> + lane_level_config))
> + continue;
> +
> + drm_dp_link_train_channel_eq_delay(it6505->dpcd);
> + drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
> +
> + if (!drm_dp_clock_recovery_ok(link_status, it6505->lane_count))
> + goto eq_train_fail;
> +
> + if (drm_dp_channel_eq_ok(link_status, it6505->lane_count)) {
> + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
> + DP_TRAINING_PATTERN_DISABLE);
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_EQ_DONE,
> + FORCE_EQ_DONE);
> + return true;
> + }
> + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "eq not done");
> +
> + for (i = 0; i < it6505->lane_count; i++) {
> + lane_voltage_pre_emphasis->voltage_swing[i] =
> + drm_dp_get_adjust_request_voltage(link_status,
> + i) >>
> + DP_TRAIN_VOLTAGE_SWING_SHIFT;
> + lane_voltage_pre_emphasis->pre_emphasis[i] =
> + drm_dp_get_adjust_request_pre_emphasis(
> + link_status, i) >>
> + DP_TRAIN_PRE_EMPHASIS_SHIFT;
> +
> + if (lane_voltage_pre_emphasis->voltage_swing[i] +
> + lane_voltage_pre_emphasis->pre_emphasis[i] >
> + 0x03)
> + lane_voltage_pre_emphasis->voltage_swing[i] =
> + 0x03 - lane_voltage_pre_emphasis
> + ->pre_emphasis[i];
> + }
> + }
> +
> +eq_train_fail:
> + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
> + DP_TRAINING_PATTERN_DISABLE);
> + return false;
> +}
> +
> +static bool it6505_link_start_step_train(struct it6505 *it6505)
> +{
> + int err;
> + struct it6505_step_train_para lane_voltage_pre_emphasis = {
> + .voltage_swing = { 0 },
> + .pre_emphasis = { 0 },
> + };
> +
> + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "start");
> + err = it6505_drm_dp_link_configure(it6505);
> +
> + if (err != 0)
> + return false;
> + if (!it6505_step_cr_train(it6505, &lane_voltage_pre_emphasis))
> + return false;
> + if (!it6505_step_eq_train(it6505, &lane_voltage_pre_emphasis))
> + return false;
> + return true;
> +}
> +
> +static bool it6505_get_video_status(struct it6505 *it6505)
> +{
> + int reg_0d;
> +
> + reg_0d = it6505_read(it6505, REG_SYSTEM_STS);
> +
> + if (reg_0d < 0)
> + return false;
> +
> + return (reg_0d & VIDEO_STB) ? true : false;
> +}
> +
> +static void it6505_reset_hdcp(struct it6505 *it6505)
> +{
> + /* Disable CP_Desired */
> + it6505_set_bits(it6505, REG_HDCP_CTRL1, HDCP_CP_ENABLE, 0x00);
> + it6505_set_bits(it6505, REG_RESET_CTRL, HDCP_RESET, HDCP_RESET);
> +}
> +
> +static void it6505_stop_link_train(struct it6505 *it6505)
> +{
> + it6505->link_state = LINK_IDLE;
> + cancel_work_sync(&it6505->link_works);
> + it6505_write(it6505, REG_TRAIN_CTRL1, FORCE_RETRAIN);
> +}
> +
> +static void it6505_link_training_work(struct work_struct *work)
> +{
> + struct it6505 *it6505 = container_of(work, struct it6505, link_works);
> + struct device *dev = &it6505->client->dev;
> + int err, i, step_retry = 3;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d",
> + it6505->sink_count);
> +
> +
> + if (!it6505_get_sink_hpd_status(it6505))
> + return;
> +
> + it6505_link_training_setup(it6505);
> +
> + if (it6505->auto_train_retry < 1)
> + goto step_train;
> +
> + it6505_reset_hdcp(it6505);
> + it6505_aux_reset(it6505);
> + err = it6505_link_start_auto_train(it6505);
> + DRM_DEV_DEBUG_DRIVER(dev, "auto train %s, auto_train_retry: %d",
> + err ? "pass" : "failed", it6505->auto_train_retry);
> + it6505->auto_train_retry--;
> +
> + if (err)
> + goto link_train_ok;
> +
> + it6505_dump(it6505);
> + return;
> +
> +step_train:
> + DRM_DEV_DEBUG_DRIVER(dev, "Start step train");
> +
> + if (it6505->sink_count == 0) {
> + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d, force eq",
> + it6505->sink_count);
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0, FORCE_EQ_DONE,
> + FORCE_EQ_DONE);
> + return;
> + }
> +
> + if (!it6505->step_train)
> + goto link_train_error;
> +
> + /* setp training start here */
> + for (i = 0; i < step_retry; i++) {
> + it6505_set_bits(it6505, REG_TRAIN_CTRL0,
> + FORCE_CR_DONE | FORCE_EQ_DONE, 0x00);
> + it6505_dpcd_write(it6505, DP_TRAINING_PATTERN_SET,
> + DP_TRAINING_PATTERN_DISABLE);
> + err = it6505_link_start_step_train(it6505);
> + DRM_DEV_DEBUG_DRIVER(dev, "step train %s, retry:%d times",
> + err ? "pass" : "failed", i + 1);
> + if (err)
> + goto link_train_ok;
> + }
> +
> +link_train_error:
> + DRM_DEV_DEBUG_DRIVER(dev, "training fail");
> + it6505->link_state = LINK_IDLE;
> +
> + if (!it6505_get_video_status(it6505))
> + it6505_video_reset(it6505);
> + return;
> +
> +link_train_ok:
> +
> + it6505->link_state = LINK_OK;
> + /* disalbe mute enable avi info frame */
> + it6505_set_bits(it6505, REG_DATA_MUTE_CTRL, EN_VID_MUTE, 0x00);
> + it6505_set_bits(it6505, REG_INFOFRAME_CTRL,
> + EN_VID_CTRL_PKT, EN_VID_CTRL_PKT);
> +
> + if (it6505_audio_input(it6505)) {
> + DRM_DEV_DEBUG_DRIVER(dev, "Enable audio!");
> + it6505_enable_audio(it6505);
> + }
> +}
> +
> +static void it6505_plugged_status_to_codec(struct it6505 *it6505)
> +{
> + enum drm_connector_status status = it6505->connector_status;
> +
> + if (it6505->plugged_cb && it6505->codec_dev)
> + it6505->plugged_cb(it6505->codec_dev,
> + status == connector_status_connected);
> +}
> +
> +static int it6505_procss_hpd_irq(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + int ret, dpcd_sink_count, dp_irq_vector;
> + u8 link_status[DP_LINK_STATUS_SIZE];
> +
> + if (!it6505_get_sink_hpd_status(it6505)) {
> + DRM_DEV_DEBUG_DRIVER(dev, "HPD_IRQ HPD low");
> + it6505->sink_count = 0;
> + return 0;
> + }
> +
> + ret = it6505_dpcd_read(it6505, DP_SINK_COUNT);
> + if (ret < 0)
> + return ret;
> +
> + dpcd_sink_count = DP_GET_SINK_COUNT(ret);
> + DRM_DEV_DEBUG_DRIVER(dev, "dpcd_sink_count: %d it6505->sink_count:%d",
> + dpcd_sink_count, it6505->sink_count);
> +
> + if (it6505->branch_device && dpcd_sink_count != it6505->sink_count) {
> + memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
> + it6505->sink_count = dpcd_sink_count;
> + kfree(it6505->edid);
> + it6505->edid = NULL;
> + it6505_reset_logic(it6505);
> + it6505_int_mask_enable(it6505);
> + it6505_init(it6505);
> + return 0;
> + }
> +
> + dp_irq_vector = it6505_dpcd_read(it6505, DP_DEVICE_SERVICE_IRQ_VECTOR);
> + if (dp_irq_vector < 0)
> + return dp_irq_vector;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "dp_irq_vector = 0x%02x", dp_irq_vector);
> +
> + /* no need to check link status if no training */
> + if (it6505->link_state == LINK_OK) {
> + ret = drm_dp_dpcd_read_link_status(&it6505->aux, link_status);
> + if (ret < 0) {
> + DRM_DEV_ERROR(dev, "Fail to read link status ret: %d",
> + ret);
> + return ret;
> + }
> +
> + if (dp_link_status(link_status, DP_LANE_ALIGN_STATUS_UPDATED) &
> + DP_LINK_STATUS_UPDATED) {
> + if (!drm_dp_channel_eq_ok(link_status,
> + it6505->lane_count))
> + it6505_video_reset(it6505);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static void it6505_check_reg06(struct it6505 *it6505, unsigned int reg06)
> +{
> + struct device *dev = &it6505->client->dev;
> + bool data;
> +
> + if (reg06 & INT_HPD_CHANGE) {
> + data = it6505_get_sink_hpd_status(it6505);
> + DRM_DEV_DEBUG_DRIVER(dev, "hpd change interrupt, change to %s",
> + data ? "high" : "low");
> +
> + drm_helper_hpd_irq_event(it6505->connector.dev);
> + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count: %d",
> + it6505->sink_count);
> +
> + if (data) {
> + wait_for_completion_timeout(&it6505->wait_edid_complete,
> + msecs_to_jiffies(6000));
> + it6505_lane_termination_on(it6505);
> + it6505_lane_power_on(it6505);
> +
> + /*
> + * for some dongle which issue HPD_irq
> + * when sink counnt change from 0->1
> + * it6505 not able to receive HPD_IRQ
> + * if HW never go into trainig done
> + */
> +
> + if (it6505->branch_device && it6505->sink_count == 0)
> + schedule_work(&it6505->link_works);
> +
> + if (!it6505_get_video_status(it6505))
> + it6505_video_reset(it6505);
> +
> + it6505_calc_video_info(it6505);
> + } else {
> + kfree(it6505->edid);
> + it6505->edid = NULL;
> + memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
> +
> + it6505_video_disable(it6505);
> + it6505_disable_audio(it6505);
> + it6505_stop_link_train(it6505);
> + it6505_lane_off(it6505);
> + return;
> + }
> + }
> +
> + if (reg06 & INT_RECEIVE_HPD_IRQ) {
> + DRM_DEV_DEBUG_DRIVER(dev, "hpd_irq interrupt");
> +
> + if (it6505_procss_hpd_irq(it6505) < 0)
> + DRM_DEV_DEBUG_DRIVER(dev, "process hpd_irq fail!");
> + }
> +
> + if (reg06 & INT_SCDT_CHANGE) {
> + data = it6505_get_video_status(it6505);
> + DRM_DEV_DEBUG_DRIVER(dev, "video stable change interrupt, %s",
> + data ? "stable" : "unstable");
> + it6505_calc_video_info(it6505);
> +
> + if (data)
> + schedule_work(&it6505->link_works);
> + }
> +}
> +
> +static void it6505_check_reg07(struct it6505 *it6505, unsigned int reg07)
> +{
> + struct device *dev = &it6505->client->dev;
> +
> + if (reg07 & INT_AUX_CMD_FAIL)
> + DRM_DEV_DEBUG_DRIVER(dev, "AUX PC Request Fail Interrupt");
> +
> + if (reg07 & INT_AUDIO_FIFO_ERROR) {
> + DRM_DEV_DEBUG_DRIVER(dev, "audio fifo error Interrupt");
> +
> + if (it6505_audio_input(it6505))
> + it6505_enable_audio(it6505);
> + }
> +
> +}
> +
> +static void it6505_check_reg08(struct it6505 *it6505, unsigned int reg08)
> +{
> + struct device *dev = &it6505->client->dev;
> +
> + if (reg08 & INT_LINK_TRAIN_FAIL) {
> + DRM_DEV_DEBUG_DRIVER(dev, "link training fail interrupt");
> + schedule_work(&it6505->link_works);
> + }
> +
> + if (reg08 & INT_VID_FIFO_ERROR) {
> + DRM_DEV_DEBUG_DRIVER(dev, "video fifo overflow interrupt");
> + it6505_video_reset(it6505);
> + it6505->auto_train_retry = AUTO_TRAIN_RETRY;
> + }
> +
> + if (reg08 & INT_IO_LATCH_FIFO_OVERFLOW) {
> + DRM_DEV_DEBUG_DRIVER(dev, "IO latch fifo overflow interrupt");
> + it6505_video_reset(it6505);
> + }
> +}
> +
> +static irqreturn_t it6505_int_threaded_handler(int unused, void *data)
> +{
> + struct it6505 *it6505 = data;
> + struct device *dev = &it6505->client->dev;
> + int reg06, reg07, reg08;
> +
> + msleep(100);
> + mutex_lock(&it6505->extcon_lock);
> +
> + if (it6505->enable_drv_hold || !it6505->powered)
> + goto unlock;
> +
> + reg06 = it6505_read(it6505, INT_STATUS_01);
> + reg07 = it6505_read(it6505, INT_STATUS_02);
> + reg08 = it6505_read(it6505, INT_STATUS_03);
> +
> + it6505_write(it6505, INT_STATUS_01, reg06);
> + it6505_write(it6505, INT_STATUS_02, reg07);
> + it6505_write(it6505, INT_STATUS_03, reg08);
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "reg06 = 0x%02x", reg06);
> + DRM_DEV_DEBUG_DRIVER(dev, "reg07 = 0x%02x", reg07);
> + DRM_DEV_DEBUG_DRIVER(dev, "reg08 = 0x%02x", reg08);
> +
> + if (reg06 != 0)
> + it6505_check_reg06(it6505, reg06);
> +
> + if (reg07 != 0)
> + it6505_check_reg07(it6505, reg07);
> +
> + if (reg08 != 0)
> + it6505_check_reg08(it6505, reg08);
> +
> +unlock:
> + mutex_unlock(&it6505->extcon_lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int it6505_poweron(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + struct it6505_platform_data *pdata = &it6505->pdata;
> + int err;
> +
> + if (it6505->powered) {
> + DRM_DEV_DEBUG_DRIVER(dev, "it6505 already powered on");
> + return 0;
> + }
> +
> + if (pdata->pwr18) {
> + err = regulator_enable(pdata->pwr18);
> + if (err) {
> + DRM_DEV_DEBUG_DRIVER(dev, "Failed to enable VDD18: %d",
> + err);
> + return err;
> + }
> + }
> +
> + if (pdata->ovdd) {
> + /* time interval between IVDD and OVDD at least be 1ms */
> + usleep_range(1000, 2000);
> + err = regulator_enable(pdata->ovdd);
> + if (err) {
> + regulator_disable(pdata->pwr18);
> + return err;
> + }
> + }
> + /* time interval between OVDD and SYSRSTN at least be 10ms */
> + if (pdata->gpiod_reset) {
> + usleep_range(10000, 20000);
> + gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
> + usleep_range(1000, 2000);
> + gpiod_set_value_cansleep(pdata->gpiod_reset, 1);
> + usleep_range(10000, 20000);
> + }
> +
> + it6505_reset_logic(it6505);
> + it6505_int_mask_enable(it6505);
> + it6505_init(it6505);
> + it6505_lane_off(it6505);
> +
> + it6505->powered = true;
> +
> + return 0;
> +}
> +
> +static int it6505_poweroff(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + struct it6505_platform_data *pdata = &it6505->pdata;
> + int err;
> +
> + if (!it6505->powered) {
> + DRM_DEV_DEBUG_DRIVER(dev, "power had been already off");
> + return 0;
> + }
> +
> + if (pdata->gpiod_reset)
> + gpiod_set_value_cansleep(pdata->gpiod_reset, 0);
> +
> + if (pdata->pwr18) {
> + err = regulator_disable(pdata->pwr18);
> + if (err)
> + return err;
> + }
> +
> + if (pdata->ovdd) {
> + err = regulator_disable(pdata->ovdd);
> + if (err)
> + return err;
> + }
> +
> + it6505->powered = false;
> + kfree(it6505->edid);
> + it6505->edid = NULL;
> + it6505->sink_count = 0;
> +
> + return 0;
> +}
> +
> +static inline struct it6505 *connector_to_it6505(struct drm_connector *c)
> +{
> + return container_of(c, struct it6505, connector);
> +}
> +
> +static int it6505_get_modes(struct drm_connector *connector)
> +{
> + struct it6505 *it6505 = connector_to_it6505(connector);
> + int err, num_modes = 0, i;
> + struct device *dev = &it6505->client->dev;
> +
> + if (it6505->edid)
> + return drm_add_edid_modes(connector, it6505->edid);
> +
> + mutex_lock(&it6505->mode_lock);
> + init_completion(&it6505->wait_edid_complete);
> +
> + for (i = 0; i < 3; i++) {
> + if (!it6505_get_sink_hpd_status(it6505))
> + goto unlock;
> +
> + it6505->edid =
> + drm_do_get_edid(&it6505->connector, it6505_get_edid_block,
> + it6505);
> +
> + if (it6505->edid)
> + break;
> + }
> + if (!it6505->edid) {
> + DRM_DEV_ERROR(dev, "Failed to read EDID");
> + goto unlock;
> + }
> +
> + err = drm_connector_update_edid_property(connector, it6505->edid);
> + if (err) {
> + DRM_DEV_ERROR(dev, "Failed to update EDID property: %d", err);
> + goto unlock;
> + }
> +
> + num_modes = drm_add_edid_modes(connector, it6505->edid);
> +
> +unlock:
> + complete(&it6505->wait_edid_complete);
> + mutex_unlock(&it6505->mode_lock);
> +
> + return num_modes;
> +}
> +
> +static const struct drm_connector_helper_funcs it6505_connector_helper_funcs = {
> + .get_modes = it6505_get_modes,
> +};
> +
> +static enum drm_connector_status it6505_detect(struct drm_connector *connector,
> + bool force)
> +{
> + struct it6505 *it6505 = connector_to_it6505(connector);
> + struct device *dev = &it6505->client->dev;
> + enum drm_connector_status status = connector_status_disconnected;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d powered:%d",
> + it6505->sink_count, it6505->powered);
> +
> + mutex_lock(&it6505->mode_lock);
> +
> + if (!it6505->powered)
> + goto unlock;
> +
> + if (it6505->enable_drv_hold) {
> + status = it6505_get_sink_hpd_status(it6505) ?
> + connector_status_connected :
> + connector_status_disconnected;
> + goto unlock;
> + }
> +
> + if (it6505_get_sink_hpd_status(it6505)) {
> + it6505_aux_on(it6505);
> + it6505_drm_dp_link_probe(&it6505->aux, &it6505->link);
> + it6505_drm_dp_link_power_up(&it6505->aux, &it6505->link);
> + it6505->auto_train_retry = AUTO_TRAIN_RETRY;
> +
> + if (it6505->dpcd[0] == 0) {
> + it6505_get_dpcd(it6505, DP_DPCD_REV, it6505->dpcd,
> + ARRAY_SIZE(it6505->dpcd));
> + it6505_variable_config(it6505);
> + it6505_parse_link_capabilities(it6505);
> + }
> +
> + it6505->sink_count = DP_GET_SINK_COUNT(it6505_dpcd_read(it6505,
> + DP_SINK_COUNT));
> + DRM_DEV_DEBUG_DRIVER(dev, "it6505->sink_count:%d branch:%d",
> + it6505->sink_count, it6505->branch_device);
> +
> + if (it6505->branch_device) {
> + status = (it6505->sink_count != 0) ?
> + connector_status_connected :
> + connector_status_disconnected;
> + } else {
> + status = connector_status_connected;
> + }
> + } else {
> + it6505->sink_count = 0;
> + memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
> + }
> +
> +unlock:
> + if (it6505->connector_status != status) {
> + it6505->connector_status = status;
> + it6505_plugged_status_to_codec(it6505);
> + }
> +
> + mutex_unlock(&it6505->mode_lock);
> +
> + return status;
> +}
> +
> +static const struct drm_connector_funcs it6505_connector_funcs = {
> + .fill_modes = drm_helper_probe_single_connector_modes,
> + .detect = it6505_detect,
> + .destroy = drm_connector_cleanup,
> + .reset = drm_atomic_helper_connector_reset,
> + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
> + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
> +};
> +
> +static int it6505_extcon_notifier(struct notifier_block *self,
> + unsigned long event, void *ptr)
> +{
> + struct it6505 *it6505 = container_of(self, struct it6505, event_nb);
> +
> + schedule_work(&it6505->extcon_wq);
> + return NOTIFY_DONE;
> +}
> +
> +static void it6505_extcon_work(struct work_struct *work)
> +{
> + struct it6505 *it6505 = container_of(work, struct it6505, extcon_wq);
> + struct device *dev = &it6505->client->dev;
> + int state = extcon_get_state(it6505->extcon, EXTCON_DISP_DP);
> + unsigned int pwroffretry = 0;
> +
> + if (it6505->enable_drv_hold)
> + return;
> +
> + mutex_lock(&it6505->extcon_lock);
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "EXTCON_DISP_DP = 0x%02x", state);
> + if (state > 0) {
> + DRM_DEV_DEBUG_DRIVER(dev, "start to power on");
> + msleep(100);
> + it6505_poweron(it6505);
> + } else {
> + DRM_DEV_DEBUG_DRIVER(dev, "start to power off");
> + while (it6505_poweroff(it6505) && pwroffretry++ < 5) {
> + DRM_DEV_DEBUG_DRIVER(dev, "power off fail %d times",
> + pwroffretry);
> + }
> +
> + drm_helper_hpd_irq_event(it6505->connector.dev);
> + memset(it6505->dpcd, 0, sizeof(it6505->dpcd));
> + DRM_DEV_DEBUG_DRIVER(dev, "power off it6505 success!");
> + }
> +
> + mutex_unlock(&it6505->extcon_lock);
> +}
> +
> +static int it6505_use_notifier_module(struct it6505 *it6505)
> +{
> + int ret;
> + struct device *dev = &it6505->client->dev;
> +
> + it6505->event_nb.notifier_call = it6505_extcon_notifier;
> + INIT_WORK(&it6505->extcon_wq, it6505_extcon_work);
> + ret = devm_extcon_register_notifier(&it6505->client->dev,
> + it6505->extcon, EXTCON_DISP_DP,
> + &it6505->event_nb);
> + if (ret) {
> + DRM_DEV_ERROR(dev, "failed to register notifier for DP");
> + return ret;
> + }
> +
> + schedule_work(&it6505->extcon_wq);
> +
> + return 0;
> +}
> +
> +static void it6505_remove_notifier_module(struct it6505 *it6505)
> +{
> + if (it6505->extcon) {
> + devm_extcon_unregister_notifier(&it6505->client->dev,
> + it6505->extcon, EXTCON_DISP_DP,
> + &it6505->event_nb);
> +
> + flush_work(&it6505->extcon_wq);
> + }
> +}
> +
> +static void __maybe_unused it6505_delayed_audio(struct work_struct *work)
> +{
> + struct it6505 *it6505 = container_of(work, struct it6505,
> + delayed_audio.work);
> +
> + DRM_DEV_DEBUG_DRIVER(&it6505->client->dev, "start");
> +
> + if (!it6505->powered)
> + return;
> +
> + if (!it6505->enable_drv_hold)
> + it6505_enable_audio(it6505);
> +}
> +
> +static int __maybe_unused it6505_audio_setup_hw_params(struct it6505 *it6505,
> + struct hdmi_codec_params *params)
> +{
> + struct device *dev = &it6505->client->dev;
> + int i = 0;
> +
> + DRM_DEV_DEBUG_DRIVER(dev, "%s %d Hz, %d bit, %d channels\n", __func__,
> + params->sample_rate, params->sample_width,
> + params->cea.channels);
> +
> + if (!it6505->bridge.encoder)
> + return -ENODEV;
> +
> + if (params->cea.channels <= 1 || params->cea.channels > 8) {
> + DRM_DEV_DEBUG_DRIVER(dev, "channel number: %d not support",
> + it6505->audio.channel_count);
> + return -EINVAL;
> + }
> +
> + it6505->audio.channel_count = params->cea.channels;
> +
> + while (i < ARRAY_SIZE(audio_sample_rate_map) &&
> + params->sample_rate !=
> + audio_sample_rate_map[i].sample_rate_value) {
> + i++;
> + }
> + if (i == ARRAY_SIZE(audio_sample_rate_map)) {
> + DRM_DEV_DEBUG_DRIVER(dev, "sample rate: %d Hz not support",
> + params->sample_rate);
> + return -EINVAL;
> + }
> + it6505->audio.sample_rate = audio_sample_rate_map[i].rate;
> +
> + switch (params->sample_width) {
> + case 16:
> + it6505->audio.word_length = WORD_LENGTH_16BIT;
> + break;
> + case 18:
> + it6505->audio.word_length = WORD_LENGTH_18BIT;
> + break;
> + case 20:
> + it6505->audio.word_length = WORD_LENGTH_20BIT;
> + break;
> + case 24:
> + case 32:
> + it6505->audio.word_length = WORD_LENGTH_24BIT;
> + break;
> + default:
> + DRM_DEV_DEBUG_DRIVER(dev, "wordlength: %d bit not support",
> + params->sample_width);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static void __maybe_unused it6505_audio_shutdown(struct device *dev, void *data)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> +
> + if (it6505->powered)
> + it6505_disable_audio(it6505);
> +}
> +
> +static int __maybe_unused it6505_audio_hook_plugged_cb(struct device *dev,
> + void *data,
> + hdmi_codec_plugged_cb fn,
> + struct device *codec_dev)
> +{
> + struct it6505 *it6505 = data;
> +
> + it6505->plugged_cb = fn;
> + it6505->codec_dev = codec_dev;
> + it6505_plugged_status_to_codec(it6505);
> +
> + return 0;
> +}
> +
> +static inline struct it6505 *bridge_to_it6505(struct drm_bridge *bridge)
> +{
> + return container_of(bridge, struct it6505, bridge);
> +}
> +
> +static void it6505_bridge_mode_set(struct drm_bridge *bridge,
> + const struct drm_display_mode *mode,
> + const struct drm_display_mode *adjusted_mode)
> +{
> + struct it6505 *it6505 = bridge_to_it6505(bridge);
> + struct hdmi_avi_infoframe frame;
> + int err;
> + struct device *dev = &it6505->client->dev;
> +
> + if (WARN_ON(!it6505->powered))
> + return;
> +
> + mutex_lock(&it6505->mode_lock);
> +
> + err = drm_hdmi_avi_infoframe_from_display_mode(&frame,
> + &it6505->connector,
> + adjusted_mode);
> + if (err) {
> + DRM_DEV_ERROR(dev, "Failed to setup AVI infoframe: %d", err);
> + goto unlock;
> + }
> +
> + it6505_update_video_prarmeter(it6505, adjusted_mode);
> +
> + err = it6505_send_video_infoframe(it6505, &frame);
> + if (err)
> + DRM_DEV_ERROR(dev, "Failed to send AVI infoframe: %d", err);
> +
> +unlock:
> + mutex_unlock(&it6505->mode_lock);
> +}
> +
> +static int it6505_bridge_attach(struct drm_bridge *bridge,
> + enum drm_bridge_attach_flags flags)
> +{
> + struct it6505 *it6505 = bridge_to_it6505(bridge);
> + struct device *dev = &it6505->client->dev;
> + int err;
> +
> + if (!bridge->encoder) {
> + DRM_DEV_ERROR(dev, "Parent encoder object not found");
> + return -ENODEV;
> + }
> +
> + err = drm_connector_init(bridge->dev, &it6505->connector,
> + &it6505_connector_funcs,
> + DRM_MODE_CONNECTOR_DisplayPort);
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "Failed to initialize connector: %d", err);
> + return err;
> + }
> +
> + drm_connector_helper_add(&it6505->connector,
> + &it6505_connector_helper_funcs);
> +
> + it6505->connector.polled = DRM_CONNECTOR_POLL_HPD;
> +
> + err = drm_connector_attach_encoder(&it6505->connector, bridge->encoder);
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "Failed to link up connector to encoder: %d",
> + err);
> + goto cleanup_connector;
> + }
> +
> + err = drm_connector_register(&it6505->connector);
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "Failed to register connector: %d", err);
> + goto cleanup_connector;
> + }
> +
> + if (it6505->extcon) {
> + err = it6505_use_notifier_module(it6505);
> + if (err < 0) {
> + drm_connector_unregister(&it6505->connector);
> + goto cleanup_connector;
> + }
> + }
> +
> + return 0;
> +
> +cleanup_connector:
> + drm_connector_cleanup(&it6505->connector);
> + return err;
> +}
> +
> +static void it6505_bridge_detach(struct drm_bridge *bridge)
> +{
> + struct it6505 *it6505 = bridge_to_it6505(bridge);
> +
> + flush_work(&it6505->link_works);
> + it6505_remove_notifier_module(it6505);
> + drm_connector_unregister(&it6505->connector);
> + drm_connector_cleanup(&it6505->connector);
> +}
> +
> +static enum drm_mode_status
> +it6505_bridge_mode_valid(struct drm_bridge *bridge,
> + const struct drm_display_info *info,
> + const struct drm_display_mode *mode)
> +{
> + struct it6505 *it6505 = bridge_to_it6505(bridge);
> +
> + if (mode->flags & DRM_MODE_FLAG_INTERLACE)
> + return MODE_NO_INTERLACE;
> +
> + if (mode->clock > DPI_PIXEL_CLK_MAX)
> + return MODE_CLOCK_HIGH;
> +
> + it6505->video_info.clock = mode->clock;
> +
> + return MODE_OK;
> +}
> +
> +static void it6505_bridge_enable(struct drm_bridge *bridge)
> +{
> + struct it6505 *it6505 = bridge_to_it6505(bridge);
> +
> + it6505_int_mask_enable(it6505);
> + it6505_video_reset(it6505);
> +}
> +
> +static void it6505_bridge_disable(struct drm_bridge *bridge)
> +{
> + struct it6505 *it6505 = bridge_to_it6505(bridge);
> +
> + if (it6505->powered)
> + it6505_video_disable(it6505);
> +}
> +
> +static const struct drm_bridge_funcs it6505_bridge_funcs = {
> + .attach = it6505_bridge_attach,
> + .detach = it6505_bridge_detach,
> + .mode_valid = it6505_bridge_mode_valid,
> + .mode_set = it6505_bridge_mode_set,
> + .enable = it6505_bridge_enable,
> + .disable = it6505_bridge_disable,
> +};
> +
> +static int it6505_bridge_resume(struct device *dev)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> +
> + return it6505_poweron(it6505);
> +}
> +
> +static int it6505_bridge_suspend(struct device *dev)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> +
> + return it6505_poweroff(it6505);
> +}
> +
> +static SIMPLE_DEV_PM_OPS(it6505_bridge_pm_ops, it6505_bridge_suspend,
> + it6505_bridge_resume);
> +
> +
> +
> +static int it6505_init_pdata(struct it6505 *it6505)
> +{
> + struct it6505_platform_data *pdata = &it6505->pdata;
> + struct device *dev = &it6505->client->dev;
> +
> + /* 1.0V digital core power regulator */
> + pdata->pwr18 = devm_regulator_get(dev, "pwr18");
> + if (IS_ERR(pdata->pwr18)) {
> + DRM_DEV_ERROR(dev, "pwr18 regulator not found");
> + return PTR_ERR(pdata->pwr18);
> + }
> +
> + pdata->ovdd = devm_regulator_get(dev, "ovdd");
> + if (IS_ERR(pdata->ovdd)) {
> + DRM_DEV_ERROR(dev, "ovdd regulator not found");
> + return PTR_ERR(pdata->ovdd);
> + }
> +
> + pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
> + if (IS_ERR(pdata->gpiod_reset)) {
> + DRM_DEV_ERROR(dev, "gpiod_reset gpio not found");
> + return PTR_ERR(pdata->gpiod_reset);
> + }
> +
> + return 0;
> +}
> +
> +static void it6505_parse_dt(struct it6505 *it6505)
> +{
> + struct device *dev = &it6505->client->dev;
> + u32 afe_setting = 0;
> +
> + it6505->lane_swap_disabled = true;
> + it6505->afe_setting = 0;
> +
> + it6505->lane_swap_disabled =
> + device_property_read_bool(dev, "no-laneswap");
> +
> + if (it6505->lane_swap_disabled)
> + it6505->lane_swap = false;
> +
> + if (device_property_read_u32(dev, "afe-setting", &afe_setting) == 0)
> + if (afe_setting >= ARRAY_SIZE(afe_setting_table)) {
> + DRM_DEV_ERROR(dev, "afe setting error, use default");
> + afe_setting = 0;
> + }
> +
> + it6505->afe_setting = afe_setting;
> +}
> +
> +static ssize_t print_timing_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> + struct drm_display_mode *vid = &it6505->video_info;
> + char *str = buf, *end = buf + PAGE_SIZE;
> +
> + it6505_calc_video_info(it6505);
> + str += scnprintf(str, end - str, "---video timing---\n");
> + str += scnprintf(str, end - str, "PCLK:%d.%03dMHz\n",
> + vid->clock / 1000, vid->clock % 1000);
> + str += scnprintf(str, end - str, "HTotal:%d\n", vid->htotal);
> + str += scnprintf(str, end - str, "HActive:%d\n", vid->hdisplay);
> + str += scnprintf(str, end - str, "HFrontPorch:%d\n",
> + vid->hsync_start - vid->hdisplay);
> + str += scnprintf(str, end - str, "HSyncWidth:%d\n",
> + vid->hsync_end - vid->hsync_start);
> + str += scnprintf(str, end - str, "HBackPorch:%d\n",
> + vid->htotal - vid->hsync_end);
> + str += scnprintf(str, end - str, "VTotal:%d\n", vid->vtotal);
> + str += scnprintf(str, end - str, "VActive:%d\n", vid->vdisplay);
> + str += scnprintf(str, end - str, "VFrontPorch:%d\n",
> + vid->vsync_start - vid->vdisplay);
> + str += scnprintf(str, end - str, "VSyncWidth:%d\n",
> + vid->vsync_end - vid->vsync_start);
> + str += scnprintf(str, end - str, "VBackPorch:%d\n",
> + vid->vtotal - vid->vsync_end);
> +
> + return str - buf;
> +}
> +
> +static ssize_t force_pwronoff_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> + int pwr;
> +
> + if (kstrtoint(buf, 10, &pwr) < 0)
> + return -EINVAL;
> +
> + if (pwr)
> + it6505_poweron(it6505);
> + else
> + it6505_poweroff(it6505);
> +
> + return count;
> +}
> +
> +static ssize_t enable_drv_hold_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> +
> + return scnprintf(buf, PAGE_SIZE, "%d\n", it6505->enable_drv_hold);
> +}
> +
> +static ssize_t enable_drv_hold_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + struct it6505 *it6505 = dev_get_drvdata(dev);
> + unsigned int drv_hold;
> +
> + if (kstrtoint(buf, 10, &drv_hold) < 0)
> + return -EINVAL;
> +
> + it6505->enable_drv_hold = !!drv_hold;
> +
> + if (it6505->enable_drv_hold) {
> + it6505_int_mask_disable(it6505);
> + } else {
> + it6505_clear_int(it6505);
> + it6505_int_mask_enable(it6505);
> +
> + if (it6505->powered) {
> + it6505->connector_status =
> + it6505_get_sink_hpd_status(it6505) ?
> + connector_status_connected :
> + connector_status_disconnected;
> + } else {
> + it6505->connector_status =
> + connector_status_disconnected;
> + }
> + }
> + return count;
> +}
> +
> +static DEVICE_ATTR_RO(print_timing);
> +static DEVICE_ATTR_WO(force_pwronoff);
> +static DEVICE_ATTR_RW(enable_drv_hold);
> +
> +static const struct attribute *it6505_attrs[] = {
> + &dev_attr_print_timing.attr,
> + &dev_attr_force_pwronoff.attr,
> + &dev_attr_enable_drv_hold.attr,
> + NULL,
> +};
> +
> +static int it6505_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct it6505 *it6505;
> + struct it6505_platform_data *pdata;
> + struct device *dev = &client->dev;
> + struct extcon_dev *extcon;
> + int err, intp_irq;
> +
> + it6505 = devm_kzalloc(&client->dev, sizeof(*it6505), GFP_KERNEL);
> + if (!it6505)
> + return -ENOMEM;
> +
> + mutex_init(&it6505->extcon_lock);
> + mutex_init(&it6505->mode_lock);
> + mutex_init(&it6505->aux_lock);
> +
> + pdata = &it6505->pdata;
> +
> + it6505->bridge.of_node = client->dev.of_node;
> + it6505->connector_status = connector_status_disconnected;
> + it6505->client = client;
> + i2c_set_clientdata(client, it6505);
> +
> + /* get extcon device from DTS */
> + extcon = extcon_get_edev_by_phandle(dev, 0);
> + if (PTR_ERR(extcon) == -EPROBE_DEFER)
> + return -EPROBE_DEFER;
> + if (IS_ERR(extcon)) {
> + DRM_DEV_ERROR(dev, "can not get extcon device!");
> + return -EINVAL;
> + }
> +
> + it6505->extcon = extcon;
> +
> + it6505->regmap = devm_regmap_init_i2c(client, &it6505_regmap_config);
> + if (IS_ERR(it6505->regmap)) {
> + DRM_DEV_ERROR(dev, "regmap i2c init failed");
> + err = PTR_ERR(it6505->regmap);
> + return err;
> + }
> +
> + err = it6505_init_pdata(it6505);
> + if (err) {
> + DRM_DEV_ERROR(dev, "Failed to initialize pdata: %d", err);
> + return err;
> + }
> +
> + it6505_parse_dt(it6505);
> +
> + intp_irq = client->irq;
> +
> + if (!intp_irq) {
> + DRM_DEV_ERROR(dev, "Failed to get INTP IRQ");
> + err = -ENODEV;
> + return err;
> + }
> +
> + err = devm_request_threaded_irq(&client->dev, intp_irq, NULL,
> + it6505_int_threaded_handler,
> + IRQF_TRIGGER_LOW | IRQF_ONESHOT,
> + "it6505-intp", it6505);
> + if (err) {
> + DRM_DEV_ERROR(dev, "Failed to request INTP threaded IRQ: %d",
> + err);
> + return err;
> + }
> +
> + /* Register aux channel */
> + it6505->aux.name = "DP-AUX";
> + it6505->aux.dev = dev;
> + it6505->aux.transfer = it6505_aux_transfer;
> +
> + err = drm_dp_aux_register(&it6505->aux);
> + if (err < 0) {
> + DRM_DEV_ERROR(dev, "Failed to register aux: %d", err);
> + return err;
> + }
> +
> + INIT_WORK(&it6505->link_works, it6505_link_training_work);
> + init_completion(&it6505->wait_edid_complete);
> + it6505->powered = false;
> + it6505->enable_drv_hold = DEFAULT_DRV_HOLD;
> +
> + if (DEFAULT_PWR_ON)
> + it6505_poweron(it6505);
> +
> + err = sysfs_create_files(&client->dev.kobj, it6505_attrs);
> + if (err) {
> + drm_dp_aux_unregister(&it6505->aux);
> + return err;
> + }
> +
> + it6505->bridge.funcs = &it6505_bridge_funcs;
> + drm_bridge_add(&it6505->bridge);
> +
> + return 0;
> +}
> +
> +static int it6505_i2c_remove(struct i2c_client *client)
> +{
> + struct it6505 *it6505 = i2c_get_clientdata(client);
> +
> + drm_connector_unregister(&it6505->connector);
> + drm_connector_cleanup(&it6505->connector);
> + drm_bridge_remove(&it6505->bridge);
> + drm_dp_aux_unregister(&it6505->aux);
> + sysfs_remove_files(&client->dev.kobj, it6505_attrs);
> + it6505_poweroff(it6505);
> +
> + return 0;
> +}
> +
> +static const struct i2c_device_id it6505_id[] = {
> + { "it6505", 0 },
> + { }
> +};
> +
> +MODULE_DEVICE_TABLE(i2c, it6505_id);
> +
> +static const struct of_device_id it6505_of_match[] = {
> + { .compatible = "ite,it6505" },
> + { }
> +};
> +
> +static struct i2c_driver it6505_i2c_driver = {
> + .driver = {
> + .name = "it6505",
> + .of_match_table = it6505_of_match,
> + .pm = &it6505_bridge_pm_ops,
> + },
> + .probe = it6505_i2c_probe,
> + .remove = it6505_i2c_remove,
> + .id_table = it6505_id,
> +};
> +
> +module_i2c_driver(it6505_i2c_driver);
> +
> +MODULE_AUTHOR("Hermes Wu <hermes.wu@....com.tw>");
> +MODULE_DESCRIPTION("IT6505 DisplayPort Transmitter driver");
> +MODULE_LICENSE("GPL v2");
> --
> 1.9.1
>
> _______________________________________________
> dri-devel mailing list
> dri-devel@...ts.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
Powered by blists - more mailing lists