new file mode 100644
@@ -0,0 +1,5038 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Samsung ExynosAuto DRM Display Port driver
+ *
+ * Copyright (C) 2018 Samsung Electronics Co.Ltd
+ */
+#include <linux/console.h>
+#include <linux/component.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/of_device.h>
+#include <video/of_display_timing.h>
+#include <video/videomode.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#include <drm/drm_atomic_helper.h> /* drm_atomic_helperxxx */
+
+#include <drm/drm_edid.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/display/drm_dp_mst_helper.h>
+#include <drm/display/drm_dsc_helper.h>
+
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_dp.h"
+
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2019 Samsung Electronics Co.Ltd
+ */
+#include <linux/delay.h> /* udelay */
+#include <linux/err.h> /* EBUSY, EINVAL */
+#include <linux/export.h> /* EXPORT_SYMBOL */
+#include <linux/io.h> /* for use 'readl()', 'writel()' functions */
+#include <linux/kernel.h> /* DIV_ROUND_CLOSEST */
+#include <linux/printk.h> /* pr_xxx */
+#include <linux/types.h> /* uint32_t, __iomem, ... */
+#include <video/mipi_display.h>
+#include <drm/drm_mipi_dsi.h> /* MIPI_DSI_* */
+#include <linux/iopoll.h> /* readx_poll_timeout_atomic */
+
+#define MAX_LANE 4
+
+#define SYSTEM_DEVICE_VERSION (0x0000)
+#define DEVICE_VERSION (0xFFFFFFFF << 0)
+
+#define SYSTEM_SW_RESET_CONTROL (0x0004)
+#define SW_RESET (0x01 << 0)
+
+#define SYSTEM_CLK_CONTROL (0x0008)
+#define GFMUX_STATUS_TXCLK (0x03 << 8)
+#define GFMUX_STATUS_TXCLK_ON (0x02)
+#define TXCLK_SEL (0x01 << 5)
+#define TXCLK_SEL_MODE (0x01 << 4)
+#define OSC_CLK_SEL (0x01 << 0)
+
+#define SYSTEM_MAIN_LINK_BANDWIDTH (0x000C)
+#define LINK_BW_SET (0x1F << 0)
+
+#define SYSTEM_MAIN_LINK_LANE_COUNT (0x0010)
+#define LANE_COUNT_SET (0x07 << 0)
+
+#define SYSTEM_SW_FUNCTION_ENABLE (0x0014)
+#define SW_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_COMMON_FUNCTION_ENABLE (0x0018)
+#define HDCP22_FUNC_EN (0x01 << 4)
+#define HDCP13_FUNC_EN (0x01 << 3)
+#define GTC_FUNC_EN (0x01 << 2)
+#define PCS_FUNC_EN (0x01 << 1)
+#define AUX_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_SST1_FUNCTION_ENABLE (0x001C)
+#define SST1_LH_PWR_ON_STATUS (0x01 << 5)
+#define SST1_LH_PWR_ON (0x01 << 4)
+#define SST1_AUDIO_FIFO_FUNC_EN (0x01 << 2)
+#define SST1_AUDIO_FUNC_EN (0x01 << 1)
+#define SST1_VIDEO_FUNC_EN (0x01 << 0)
+
+#define SYSTEM_SST2_FUNCTION_ENABLE (0x0020)
+
+#define SYSTEM_SST3_FUNCTION_ENABLE (0x0030)
+
+#define SYSTEM_SST4_FUNCTION_ENABLE (0x0034)
+
+#define SYSTEM_MISC_CONTROL (0x0024)
+#define MISC_CTRL_EN (0x01 << 1)
+#define HDCP_HPD_RST (0x01 << 0)
+
+#define SYSTEM_HPD_CONTROL (0x0028)
+#define HPD_HDCP (0x01 << 30)
+#define HPD_DEGLITCH_COUNT (0x3FFF << 16)
+#define HPD_STATUS (0x01 << 8)
+#define HPD_EVENT_UNPLUG (0x01 << 7)
+#define HPD_EVENT_PLUG (0x01 << 6)
+#define HPD_EVENT_IRQ (0x01 << 5)
+#define HPD_EVENT_CTRL_EN (0x01 << 4)
+#define HPD_FORCE (0x01 << 1)
+#define HPD_FORCE_EN (0x01 << 0)
+
+#define SYSTEM_PLL_LOCK_CONTROL (0x002C)
+#define PLL_LOCK_STATUS (0x01 << 4)
+#define PLL_LOCK_FORCE (0x01 << 3)
+#define PLL_LOCK_FORCE_EN (0x01 << 2)
+
+#define SYSTEM_INTERRUPT_CONTROL (0x0100)
+#define SW_INTR_CTRL (0x01 << 1)
+#define INTR_POL (0x01 << 0)
+
+#define SYSTEM_INTERRUPT_REQUEST (0x0104)
+#define IRQ_MST (0x01 << 3)
+#define IRQ_STR2 (0x01 << 2)
+#define IRQ_STR1 (0x01 << 1)
+#define IRQ_COMMON (0x01 << 0)
+
+#define SYSTEM_IRQ_COMMON_STATUS (0x0108)
+#define HDCP_ENC_EN_CHG (0x01 << 22)
+#define HDCP_LINK_CHK_FAIL (0x01 << 21)
+#define HDCP_R0_CHECK_FLAG (0x01 << 20)
+#define HDCP_BKSV_RDY (0x01 << 19)
+#define HDCP_SHA_DONE (0x01 << 18)
+#define HDCP_AUTH_STATE_CHG (0x01 << 17)
+#define HDCP_AUTH_DONE (0x01 << 16)
+#define HPD_IRQ_FLAG (0x01 << 11)
+#define HPD_CHG (0x01 << 10)
+#define HPD_LOST (0x01 << 9)
+#define HPD_PLUG_INT (0x01 << 8)
+#define AUX_REPLY_RECEIVED (0x01 << 5)
+#define AUX_ERR (0x01 << 4)
+#define PLL_LOCK_CHG (0x01 << 1)
+#define SW_INTR (0x01 << 0)
+
+#define SYSTEM_IRQ_COMMON_STATUS_MASK (0x010C)
+#define MST_BUF_OVERFLOW_MASK (0x01 << 23)
+#define HDCP_ENC_EN_CHG_MASK (0x01 << 22)
+#define HDCP_LINK_CHK_FAIL_MASK (0x01 << 21)
+#define HDCP_R0_CHECK_FLAG_MASK (0x01 << 20)
+#define HDCP_BKSV_RDY_MASK (0x01 << 19)
+#define HDCP_SHA_DONE_MASK (0x01 << 18)
+#define HDCP_AUTH_STATE_CHG_MASK (0x01 << 17)
+#define HDCP_AUTH_DONE_MASK (0x01 << 16)
+#define HPD_IRQ_MASK (0x01 << 11)
+#define HPD_CHG_MASK (0x01 << 10)
+#define HPD_LOST_MASK (0x01 << 9)
+#define HPD_PLUG_MASK (0x01 << 8)
+#define AUX_REPLY_RECEIVED_MASK (0x01 << 5)
+#define AUX_ERR_MASK (0x01 << 4)
+#define PLL_LOCK_CHG_MASK (0x01 << 1)
+#define SW_INTR_MASK (0x01 << 0)
+
+#define SYSTEM_DEBUG (0x0200)
+#define AUX_HPD_CONTROL (0x01 << 2)
+#define CLKGATE_DISABLE (0x01 << 1)
+
+#define SYSTEM_DEBUG_LH_PCH (0x0204)
+#define SST4_LH_PSTATE (0x01 << 28)
+#define SST4_LH_PCH_FSM_STATE (0x0F << 24)
+#define SST3_LH_PSTATE (0x01 << 20)
+#define SST3_LH_PCH_FSM_STATE (0x0F << 16)
+#define SST2_LH_PSTATE (0x01 << 12)
+#define SST2_LH_PCH_FSM_STATE (0x0F << 8)
+#define SST1_LH_PSTATE (0x01 << 4)
+#define SST1_LH_PCH_FSM_STATE (0x0F << 0)
+
+#define AUX_CONTROL (0x1000)
+#define AUX_POWER_DOWN (0x01 << 16)
+#define AUX_REPLY_TIMER_MODE (0x03 << 12)
+#define AUX_REPLY_TIMER_VAL(_v_) ((_v_) << 12)
+#define AUX_RETRY_TIMER (0x07 << 8)
+#define AUX_PN_INV (0x01 << 1)
+#define REG_MODE_SEL (0x01 << 0)
+
+#define AUX_TRANSACTION_START (0x1004)
+#define AUX_TRAN_START (0x01 << 0)
+
+#define AUX_BUFFER_CLEAR (0x1008)
+#define AUX_BUF_CLR (0x01 << 0)
+
+#define AUX_ADDR_ONLY_COMMAND (0x100C)
+#define ADDR_ONLY_CMD (0x01 << 0)
+
+#define AUX_REQUEST_CONTROL (0x1010)
+#define REQ_COMM (0x0F << 28)
+#define REQ_COMM_VAL(_v_) ((_v_) << 28)
+#define REQ_ADDR (0xFFFFF << 8)
+#define REQ_ADDR_VAL(_v_) ((_v_) << 8)
+#define REQ_LENGTH (0x3F << 0)
+
+#define AUX_COMMAND_CONTROL (0x1014)
+#define DEFER_CTRL_EN (0x01 << 8)
+#define DEFER_COUNT (0x7F << 0)
+
+#define AUX_MONITOR_1 (0x1018)
+#define AUX_BUF_DATA_COUNT (0x7F << 24)
+#define AUX_DETECTED_PERIOD_MON (0x1FF << 12)
+#define AUX_CMD_STATUS (0x0F << 8)
+#define AUX_RX_COMM (0x0F << 4)
+#define AUX_LAST_MODE (0x01 << 3)
+#define AUX_BUSY (0x01 << 2)
+#define AUX_REQ_WAIT_GRANT (0x01 << 1)
+#define AUX_REQ_SIGNAL (0x01 << 0)
+
+#define AUX_MONITOR_2 (0x101C)
+#define AUX_ERROR_NUMBER (0xFF << 0)
+
+#define AUX_TX_DATA_SET0 (0x1030)
+#define TX_DATA_3 (0xFF << 24)
+#define TX_DATA_2 (0xFF << 16)
+#define TX_DATA_1 (0xFF << 8)
+#define TX_DATA_0 (0xFF << 0)
+
+#define AUX_TX_DATA_SET1 (0x1034)
+#define TX_DATA_7 (0xFF << 24)
+#define TX_DATA_6 (0xFF << 16)
+#define TX_DATA_5 (0xFF << 8)
+#define TX_DATA_4 (0xFF << 0)
+
+#define AUX_TX_DATA_SET2 (0x1038)
+#define TX_DATA_11 (0xFF << 24)
+#define TX_DATA_10 (0xFF << 16)
+#define TX_DATA_9 (0xFF << 8)
+#define TX_DATA_8 (0xFF << 0)
+
+#define AUX_TX_DATA_SET3 (0x103C)
+#define TX_DATA_15 (0xFF << 24)
+#define TX_DATA_14 (0xFF << 16)
+#define TX_DATA_13 (0xFF << 8)
+#define TX_DATA_12 (0xFF << 0)
+
+#define AUX_RX_DATA_SET0 (0x1040)
+#define RX_DATA_3 (0xFF << 24)
+#define RX_DATA_2 (0xFF << 16)
+#define RX_DATA_1 (0xFF << 8)
+#define RX_DATA_0 (0xFF << 0)
+
+#define AUX_RX_DATA_SET1 (0x1044)
+#define RX_DATA_7 (0xFF << 24)
+#define RX_DATA_6 (0xFF << 16)
+#define RX_DATA_5 (0xFF << 8)
+#define RX_DATA_4 (0xFF << 0)
+
+#define AUX_RX_DATA_SET2 (0x1048)
+#define RX_DATA_11 (0xFF << 24)
+#define RX_DATA_10 (0xFF << 16)
+#define RX_DATA_9 (0xFF << 8)
+#define RX_DATA_8 (0xFF << 0)
+
+#define AUX_RX_DATA_SET3 (0x104C)
+#define RX_DATA_15 (0xFF << 24)
+#define RX_DATA_14 (0xFF << 16)
+#define RX_DATA_13 (0xFF << 8)
+#define RX_DATA_12 (0xFF << 0)
+
+#define GTC_CONTROL (0x1100)
+#define GTC_DELTA_ADJ_EN (0x01 << 2)
+#define IMPL_OPTION (0x01 << 1)
+#define GTC_TX_MASTER (0x01 << 0)
+
+#define GTC_WORK_ENABLE (0x1104)
+#define GTC_WORK_EN (0x01 << 0)
+
+#define GTC_TIME_CONTROL (0x1108)
+#define TIME_PERIOD_SEL (0x03 << 28)
+#define TIME_PERIOD_FRACTIONAL (0xFFFFF << 8)
+#define TIME_PERIOD_INT1 (0x0F << 4)
+#define TIME_PERIOD_INT2 (0x0F << 0)
+
+#define GTC_ATTEMPT_CONTROL (0x110C)
+#define GTC_STATE_CHANGE_CTRL (0x01 << 8)
+#define NUM_GTC_ATTEMPT (0xFF << 0)
+
+#define GTC_TX_VALUE_MONITOR (0x1110)
+#define GTC_TX_VAL (0xFFFFFFFF << 0)
+
+#define GTC_RX_VALUE_MONITOR (0x1114)
+#define GTC_RX_VAL (0xFFFFFFFF << 0)
+
+#define GTC_STATUS_MONITOR (0x1118)
+#define FREQ_ADJ_10_3 (0xFF << 8)
+#define FREQ_ADJ_2_0 (0x07 << 5)
+#define TXGTC_LOCK_DONE_FLAG (0x01 << 1)
+#define RXGTC_LOCK_DONE_FLAG (0x01 << 0)
+
+#define AUX_GTC_DEBUG (0x1200)
+#define DEBUG_STATE_SEL (0xFF << 8)
+#define DEBUG_STATE (0xFF << 0)
+
+#define MST_ENABLE (0x2000)
+#define MST_EN (0x01 << 0)
+
+#define MST_VC_PAYLOAD_UPDATE_FLAG (0x2004)
+#define VC_PAYLOAD_UPDATE_FLAG (0x01 << 0)
+
+#define MST_STREAM_1_ENABLE (0x2010)
+#define STRM_1_EN (0x01 << 0)
+
+#define MST_STREAM_1_HDCP_CTRL (0x2014)
+#define STRM_1_HDCP22_TYPE (0x01 << 1)
+#define STRM_1_HDCP_EN (0x01 << 0)
+
+#define MST_STREAM_1_X_VALUE (0x2018)
+#define MST_STREAM_X_VALUE(sst_id) (MST_STREAM_1_X_VALUE + (0x10 * (sst_id)))
+#define STRM_VALUE (0xFF << 0)
+
+#define MST_STREAM_1_Y_VALUE (0x201C)
+#define MST_STREAM_Y_VALUE(sst_id) (MST_STREAM_1_Y_VALUE + (0x10 * (sst_id)))
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_01_08 (0x2050)
+#define VC_PAYLOAD_ID_TIMESLOT_01 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_02 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_03 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_04 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_05 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_06 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_07 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_08 (0x03 << 0)
+#define VC_PAYLOAD_ID_MASK(mask) (~(0x07 << (mask)))
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_09_16 (0x2054)
+#define VC_PAYLOAD_ID_TIMESLOT_09 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_10 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_11 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_12 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_13 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_14 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_15 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_16 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_17_24 (0x2058)
+#define VC_PAYLOAD_ID_TIMESLOT_17 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_18 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_19 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_20 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_21 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_22 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_23 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_24 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_25_32 (0x205C)
+#define VC_PAYLOAD_ID_TIMESLOT_25 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_26 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_27 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_28 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_29 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_30 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_31 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_32 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_33_40 (0x2060)
+#define VC_PAYLOAD_ID_TIMESLOT_33 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_34 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_35 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_36 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_37 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_38 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_39 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_40 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_41_48 (0x2064)
+#define VC_PAYLOAD_ID_TIMESLOT_41 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_42 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_43 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_44 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_45 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_46 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_47 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_48 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_49_56 (0x2068)
+#define VC_PAYLOAD_ID_TIMESLOT_49 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_50 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_51 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_52 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_53 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_54 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_55 (0x03 << 4)
+#define VC_PAYLOAD_ID_TIMESLOT_56 (0x03 << 0)
+
+#define MST_VC_PAYLOAD_ID_TIMESLOT_57_63 (0x206C)
+#define VC_PAYLOAD_ID_TIMESLOT_57 (0x03 << 28)
+#define VC_PAYLOAD_ID_TIMESLOT_58 (0x03 << 24)
+#define VC_PAYLOAD_ID_TIMESLOT_59 (0x03 << 20)
+#define VC_PAYLOAD_ID_TIMESLOT_60 (0x03 << 16)
+#define VC_PAYLOAD_ID_TIMESLOT_61 (0x03 << 12)
+#define VC_PAYLOAD_ID_TIMESLOT_62 (0x03 << 8)
+#define VC_PAYLOAD_ID_TIMESLOT_63 (0x03 << 4)
+
+#define MST_ETC_OPTION (0x2070)
+#define ALLOCATE_TIMESLOT_CHECK_TO_ACT (0x01 << 1)
+#define HDCP22_LVP_TYPE (0x01 << 0)
+
+#define MST_BUF_STATUS (0x2064)
+#define STRM_2_BUF_OVERFLOW (0x01 << 5)
+#define STRM_1_BUF_OVERFLOW (0x01 << 4)
+#define STRM_2_BUF_OVERFLOW_CLEAR (0x01 << 1)
+#define STRM_1_BUF_OVERFLOW_CLEAR (0x01 << 0)
+
+#define PCS_CONTROL (0x3000)
+#define FEC_READY (0x01 << 9)
+#define FEC_FUNC_EN (0x01 << 8)
+#define LINK_TRAINING_PATTERN_SET (0x07 << 4)
+#define LINK_TRAINING_PATTERN_SET_VAL(_v_) ((_v_) << 4)
+#define BYTE_SWAP (0x01 << 3)
+#define BIT_SWAP (0x01 << 2)
+#define SCRAMBLE_RESET_VALUE (0x01 << 1)
+#define SCRAMBLE_BYPASS (0x01 << 0)
+
+#define PCS_LANE_CONTROL (0x3004)
+#define LANE_DATA_INV_EN (0x01 << 20)
+#define LANE3_DATA_INV (0x01 << 19)
+#define LANE2_DATA_INV (0x01 << 18)
+#define LANE1_DATA_INV (0x01 << 17)
+#define LANE0_DATA_INV (0x01 << 16)
+#define LANE3_MAP (0x03 << 12)
+#define LANE3_MAP_VAL(_v_) ((_v_) << 12)
+#define LANE2_MAP (0x03 << 8)
+#define LANE2_MAP_VAL(_v_) ((_v_) << 8)
+#define LANE1_MAP (0x03 << 4)
+#define LANE1_MAP_VAL(_v_) ((_v_) << 4)
+#define LANE0_MAP (0x03 << 0)
+
+#define PCS_TEST_PATTERN_CONTROL (0x3008)
+#define TEST_PATTERN (0x3F << 8)
+#define LINK_QUALITY_PATTERN_SET (0x07 << 0)
+
+#define PCS_TEST_PATTERN_SET0 (0x300C)
+#define TEST_80BIT_PATTERN_SET0 (0xFFFFFFFF << 0)
+
+#define PCS_TEST_PATTERN_SET1 (0x3010)
+#define TEST_80BIT_PATTERN_SET1 (0xFFFFFFFF << 0)
+
+#define PCS_TEST_PATTERN_SET2 (0x3014)
+#define TEST_80BIT_PATTERN_SET2 (0xFFFF << 0)
+
+#define PCS_DEBUG_CONTROL (0x3018)
+#define FEC_FLIP_CDADJ_CODES_CASE4 (0x01 << 6)
+#define FEC_FLIP_CDADJ_CODES_CASE2 (0x01 << 5)
+#define FEC_DECODE_EN_4TH_SEL (0x01 << 4)
+#define FEC_DECODE_DIS_4TH_SEL (0x01 << 3)
+#define PRBS7_OPTION (0x01 << 2)
+#define DISABLE_AUTO_RESET_ENCODE (0x01 << 1)
+#define PRBS31_EN (0x01 << 0)
+
+#define PCS_HBR2_EYE_SR_CONTROL (0x3020)
+#define HBR2_EYE_SR_CTRL (0x03 << 16)
+#define HBR2_EYE_SR_COUNT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_1 (0x3100)
+#define SA_CRC_CLEAR (0x01 << 13)
+#define SA_CRC_SW_COMPARE (0x01 << 12)
+#define SA_CRC_LN3_PASS (0x01 << 11)
+#define SA_CRC_LN2_PASS (0x01 << 10)
+#define SA_CRC_LN1_PASS (0x01 << 9)
+#define SA_CRC_LN0_PASS (0x01 << 8)
+#define SA_CRC_LN3_FAIL (0x01 << 7)
+#define SA_CRC_LN2_FAIL (0x01 << 6)
+#define SA_CRC_LN1_FAIL (0x01 << 5)
+#define SA_CRC_LN0_FAIL (0x01 << 4)
+#define SA_CRC_LANE_3_ENABLE (0x01 << 3)
+#define SA_CRC_LANE_2_ENABLE (0x01 << 2)
+#define SA_CRC_LANE_1_ENABLE (0x01 << 1)
+#define SA_CRC_LANE_0_ENABLE (0x01 << 0)
+
+#define PCS_SA_CRC_CONTROL_2 (0x3104)
+#define SA_CRC_LN0_REF (0xFFFF << 16)
+#define SA_CRC_LN0_RESULT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_3 (0x3108)
+#define SA_CRC_LN1_REF (0xFFFF << 16)
+#define SA_CRC_LN1_RESULT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_4 (0x310C)
+#define SA_CRC_LN2_REF (0xFFFF << 16)
+#define SA_CRC_LN2_RESULT (0xFFFF << 0)
+
+#define PCS_SA_CRC_CONTROL_5 (0x3110)
+#define SA_CRC_LN3_REF (0xFFFF << 16)
+#define SA_CRC_LN3_RESULT (0xFFFF << 0)
+
+#define HDCP13_STATUS (0x4000)
+#define REAUTH_REQUEST (0x01 << 7)
+#define AUTH_FAIL (0x01 << 6)
+#define HW_1ST_AUTHEN_PASS (0x01 << 5)
+#define BKSV_VALID (0x01 << 3)
+#define ENCRYPT (0x01 << 2)
+#define HW_AUTHEN_PASS (0x01 << 1)
+#define AKSV_VALID (0x01 << 0)
+
+#define HDCP13_CONTROL_0 (0x4004)
+#define SW_STORE_AN (0x01 << 7)
+#define SW_RX_REPEATER (0x01 << 6)
+#define HW_RE_AUTHEN (0x01 << 5)
+#define SW_AUTH_OK (0x01 << 4)
+#define HW_AUTH_EN (0x01 << 3)
+#define HDCP13_ENC_EN (0x01 << 2)
+#define HW_1ST_PART_ATHENTICATION_EN (0x01 << 1)
+#define HW_2ND_PART_ATHENTICATION_EN (0x01 << 0)
+
+#define HDCP13_CONTROL_1 (0x4008)
+#define DPCD_REV_1_2 (0x01 << 3)
+#define HW_AUTH_POLLING_MODE (0x01 << 1)
+#define HDCP_INT (0x01 << 0)
+
+#define HDCP13_AKSV_0 (0x4010)
+#define AKSV0 (0xFFFFFFFF << 0)
+
+#define HDCP13_AKSV_1 (0x4014)
+#define AKSV1 (0xFF << 0)
+
+#define HDCP13_AN_0 (0x4018)
+#define AN0 (0xFFFFFFFF << 0)
+
+#define HDCP13_AN_1 (0x401C)
+#define AN1 (0xFFFFFFFF << 0)
+
+#define HDCP13_BKSV_0 (0x4020)
+#define BKSV0 (0xFFFFFFFF << 0)
+
+#define HDCP13_BKSV_1 (0x4024)
+#define BKSV1 (0xFF << 0)
+
+#define HDCP13_R0_REG (0x4028)
+#define R0 (0xFFFF << 0)
+
+#define HDCP13_BCAPS (0x4030)
+#define BCAPS (0xFF << 0)
+
+#define HDCP13_BINFO_REG (0x4034)
+#define BINFO (0xFF << 0)
+
+#define HDCP13_DEBUG_CONTROL (0x4038)
+#define CHECK_KSV (0x01 << 2)
+#define REVOCATION_CHK_DONE (0x01 << 1)
+#define HW_SKIP_RPT_ZERO_DEV (0x01 << 0)
+
+#define HDCP13_AUTH_DBG (0x4040)
+#define DDC_STATE (0x07 << 5)
+#define AUTH_STATE (0x1F << 0)
+
+#define HDCP13_ENC_DBG (0x4044)
+#define ENC_STATE (0x07 << 3)
+
+#define HDCP13_AM0_0 (0x4048)
+#define AM0_0 (0xFFFFFFFF << 0)
+
+#define HDCP13_AM0_1 (0x404C)
+#define AM0_1 (0xFFFFFFFF << 0)
+
+#define HDCP13_WAIT_R0_TIME (0x4054)
+#define HW_WRITE_AKSV_WAIT (0xFF << 0)
+
+#define HDCP13_LINK_CHK_TIME (0x4058)
+#define LINK_CHK_TIMER (0xFF << 0)
+
+#define HDCP13_REPEATER_READY_WAIT_TIME (0x405C)
+#define HW_RPTR_RDY_TIMER (0xFF << 0)
+
+#define HDCP13_READY_POLL_TIME (0x4060)
+#define POLLING_TIMER_TH (0xFF << 0)
+
+#define HDCP13_STREAM_ID_ENCRYPTION_CONTROL (0x4068)
+#define STRM_ID_ENC_UPDATE (0x01 << 7)
+#define STRM_ID_ENC (0x7F << 0)
+
+#define HDCP22_SYS_EN (0x4400)
+#define SYSTEM_ENABLE (0x01 << 0)
+
+#define HDCP22_CONTROL (0x4404)
+#define HDCP22_BYPASS_MODE (0x01 << 1)
+#define HDCP22_ENC_EN (0x01 << 0)
+
+#define HDCP22_CONTROL (0x4404)
+#define HDCP22_BYPASS_MODE (0x01 << 1)
+#define HDCP22_ENC_EN (0x01 << 0)
+
+#define HDCP22_STREAM_TYPE (0x4454)
+#define STREAM_TYPE (0x01 << 0)
+
+#define HDCP22_LVP (0x4460)
+#define LINK_VERIFICATION_PATTERN (0xFFFF << 0)
+
+#define HDCP22_LVP_GEN (0x4464)
+#define LVP_GEN (0x01 << 0)
+
+#define HDCP22_LVP_CNT_KEEP (0x4468)
+#define LVP_COUNT_KEEP_ENABLE (0x01 << 0)
+
+#define HDCP22_LANE_DECODE_CTRL (0x4470)
+#define ENHANCED_FRAMING_MODE (0x01 << 3)
+#define LVP_EN_DECODE_ENABLE (0x01 << 2)
+#define ENCRYPTION_SIGNAL_DECODE_ENABLE (0x01 << 1)
+#define LANE_DECODE_ENABLE (0x01 << 0)
+
+#define HDCP22_SR_VALUE (0x4480)
+#define SR_VALUE (0xFF << 0)
+
+#define HDCP22_CP_VALUE (0x4484)
+#define CP_VALUE (0xFF << 0)
+
+#define HDCP22_BF_VALUE (0x4488)
+#define BF_VALUE (0xFF << 0)
+
+#define HDCP22_BS_VALUE (0x448C)
+#define BS_VALUE (0xFF << 0)
+
+#define HDCP22_RIV_XOR (0x4490)
+#define RIV_XOR_LOCATION (0x01 << 0)
+
+#define HDCP22_RIV_0 (0x4500)
+#define RIV_KEY_0 (0xFFFFFFFF << 0)
+
+#define HDCP22_RIV_1 (0x4504)
+#define RIV_KEY_1 (0xFFFFFFFF << 0)
+
+#define SST1_MAIN_CONTROL (0x5000)
+#define MVID_MODE (0x01 << 11)
+#define MAUD_MODE (0x01 << 10)
+#define MVID_UPDATE_RATE (0x03 << 8)
+#define VIDEO_MODE (0x01 << 6)
+#define ENHANCED_MODE (0x01 << 5)
+#define ODD_TU_CONTROL (0x01 << 4)
+
+#define SST1_MAIN_FIFO_CONTROL (0x5004)
+#define CLEAR_AUDIO_FIFO (0x01 << 3)
+#define CLEAR_PIXEL_MAPPING_FIFO (0x01 << 2)
+#define CLEAR_MAPI_FIFO (0x01 << 1)
+#define CLEAR_GL_DATA_FIFO (0x01 << 0)
+
+#define SST1_GNS_CONTROL (0x5008)
+#define RS_CTRL (0x01 << 0)
+
+#define SST1_SR_CONTROL (0x500C)
+#define SR_COUNT_RESET_VALUE (0x1FF << 16)
+#define SR_REPLACE_BS_COUNT (0x1F << 4)
+#define SR_START_CTRL (0x01 << 1)
+#define SR_REPLACE_BS_EN (0x01 << 0)
+
+#define SST1_INTERRUPT_MONITOR (0x5020)
+#define INT_STATE (0x01 << 0)
+
+#define SST1_INTERRUPT_STATUS_SET0 (0x5024)
+#define VSC_SDP_TX_INCOMPLETE (0x01 << 9)
+#define MAPI_FIFO_UNDER_FLOW (0x01 << 8)
+#define VSYNC_DET (0x01 << 7)
+
+#define SST1_INTERRUPT_STATUS_SET1 (0x5028)
+#define AFIFO_UNDER (0x01 << 7)
+#define AFIFO_OVER (0x01 << 6)
+
+#define SST1_INTERRUPT_MASK_SET0 (0x502C)
+#define VSC_SDP_TX_INCOMPLETE_MASK (0x01 << 9)
+#define MAPI_FIFO_UNDER_FLOW_MASK (0x01 << 8)
+#define VSYNC_DET_MASK (0x01 << 7)
+
+#define SST1_INTERRUPT_MASK_SET1 (0x5030)
+#define AFIFO_UNDER_MASK (0x01 << 7)
+#define AFIFO_OVER_MASK (0x01 << 6)
+
+#define SST1_MVID_CALCULATION_CONTROL (0x5040)
+#define MVID_GEN_FILTER_TH (0xFF << 8)
+#define MVID_GEN_FILTER_EN (0x01 << 0)
+
+#define SST1_MVID_MASTER_MODE (0x5044)
+#define MVID_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_NVID_MASTER_MODE (0x5048)
+#define NVID_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_MVID_SFR_CONFIGURE (0x504C)
+#define MVID_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_NVID_SFR_CONFIGURE (0x5050)
+#define NVID_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_MVID_MONITOR (0x5054)
+#define MVID_MON (0xFFFFFF << 0)
+
+#define SST1_MAUD_CALCULATION_CONTROL (0x5058)
+#define M_AUD_GEN_FILTER_TH (0xFF << 8)
+#define M_AUD_GEN_FILTER_EN (0x01 << 0)
+
+#define SST1_MAUD_MASTER_MODE (0x505C)
+#define MAUD_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_NAUD_MASTER_MODE (0x5060)
+#define NAUD_MASTER (0xFFFFFF << 0)
+
+#define SST1_MAUD_SFR_CONFIGURE (0x5064)
+#define MAUD_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_NAUD_SFR_CONFIGURE (0x5068)
+#define NAUD_SFR_CONFIG (0xFFFFFF << 0)
+
+#define SST1_NARROW_BLANK_CONTROL (0x506C)
+#define NARROW_BLANK_EN (0x01 << 1)
+#define VIDEO_FIFO_FLUSH_DISABLE (0x01 << 0)
+
+#define SST1_LOW_TU_CONTROL (0x5070)
+#define NULL_TU_CONTROL (0x01 << 1)
+#define HALF_FREQUENCY_CONTROL (0x01 << 0)
+
+#define SST1_ACTIVE_SYMBOL_INTEGER_FEC_OFF (0x5080)
+#define ACTIVE_SYMBOL_INTEGER_FEC_OFF (0x3F << 0)
+
+#define SST1_ACTIVE_SYMBOL_FRACTION_FEC_OFF (0x5084)
+#define ACTIVE_SYMBOL_FRACTION_FEC_OFF (0x3FFFFFFF << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_OFF (0x5088)
+#define ACTIVE_SYMBOL_THRESHOLD_FEC_OFF (0x0F << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF (0x508C)
+#define ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_OFF (0x01 << 0)
+
+#define SST1_ACTIVE_SYMBOL_INTEGER_FEC_ON (0x5090)
+#define ACTIVE_SYMBOL_INTEGER_FEC_ON (0x3F << 0)
+
+#define SST1_ACTIVE_SYMBOL_FRACTION_FEC_ON (0x5094)
+#define ACTIVE_SYMBOL_FRACTION_FEC_ON (0x3FFFFFFF << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_ON (0x5098)
+#define ACTIVE_SYMBOL_THRESHOLD_FEC_ON (0x0F << 0)
+
+#define SST1_ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON (0x509C)
+#define ACTIVE_SYMBOL_THRESHOLD_SEL_FEC_ON (0x01 << 0)
+
+#define SST1_FEC_DISABLE_SEND_CONTROL (0x5100)
+#define FEC_DISABLE_SEND_CONTROL (0x01 << 0)
+
+#define SST1_VIDEO_CONTROL (0x5400)
+#define STRM_VALID_MON (0x01 << 10)
+#define STRM_VALID_FORCE (0x01 << 9)
+#define STRM_VALID_CTRL (0x01 << 8)
+#define DYNAMIC_RANGE_MODE (0x01 << 7)
+#define DYNAMIC_RANGE_VAL(_v_) ((_v_) << 7)
+#define BPC (0x07 << 4)
+#define BPC_VAL(_v_) ((_v_) << 4)
+#define COLOR_FORMAT (0x03 << 2)
+#define VSYNC_POLARITY (0x01 << 1)
+#define VSYNC_POLARITY_VAL(_v_) ((_v_) << 1)
+#define HSYNC_POLARITY (0x01 << 0)
+
+#define SST1_VIDEO_ENABLE (0x5404)
+#define VIDEO_EN (0x01 << 0)
+
+#define SST1_VIDEO_MASTER_TIMING_GEN (0x5408)
+#define VIDEO_MASTER_TIME_GEN (0x01 << 0)
+
+#define SST1_VIDEO_MUTE (0x540C)
+#define VIDEO_MUTE (0x01 << 0)
+
+#define SST1_VIDEO_FIFO_THRESHOLD_CONTROL (0x5410)
+#define GL_FIFO_TH_CTRL (0x01 << 5)
+#define GL_FIFO_TH_VALUE (0x1F << 0)
+
+#define SST1_VIDEO_HORIZONTAL_TOTAL_PIXELS (0x5414)
+#define H_TOTAL_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_TOTAL_PIXELS (0x5418)
+#define V_TOTAL_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_HORIZONTAL_FRONT_PORCH (0x541C)
+#define H_F_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_HORIZONTAL_BACK_PORCH (0x5420)
+#define H_B_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_HORIZONTAL_ACTIVE (0x5424)
+#define H_ACTIVE_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_FRONT_PORCH (0x5428)
+#define V_F_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_BACK_PORCH (0x542C)
+#define V_B_PORCH_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_VERTICAL_ACTIVE (0x5430)
+#define V_ACTIVE_MASTER (0xFFFFFFFF << 0)
+
+#define SST1_VIDEO_DSC_STREAM_CONTROL_0 (0x5434)
+#define DSC_ENABLE (0x01 << 4)
+#define SLICE_COUNT_PER_LINE (0x07 << 0)
+
+#define SST1_VIDEO_DSC_STREAM_CONTROL_1 (0x5438)
+#define CHUNK_SIZE_1 (0xFFFF << 16)
+#define CHUNK_SIZE_0 (0xFFFF << 0)
+
+#define SST1_VIDEO_DSC_STREAM_CONTROL_2 (0x543C)
+#define CHUNK_SIZE_3 (0xFFFF << 16)
+#define CHUNK_SIZE_2 (0xFFFF << 0)
+
+#define SST1_VIDEO_BIST_CONTROL (0x5450)
+#define CTS_BIST_EN (0x01 << 17)
+#define CTS_BIST_TYPE (0x03 << 15)
+#define CTS_BIST_TYPE_VAL(_v_) ((_v_) << 15)
+#define BIST_PRBS7_SEED (0x7F << 8)
+#define BIST_USER_DATA_EN (0x01 << 4)
+#define BIST_EN (0x01 << 3)
+#define BIST_WIDTH (0x01 << 2)
+#define BIST_TYPE (0x03 << 0)
+
+#define SST1_VIDEO_BIST_USER_DATA_R (0x5454)
+#define BIST_USER_DATA_R (0x3FF << 0)
+
+#define SST1_VIDEO_BIST_USER_DATA_G (0x5458)
+#define BIST_USER_DATA_G (0x3FF << 0)
+
+#define SST1_VIDEO_BIST_USER_DATA_B (0x545C)
+#define BIST_USER_DATA_B (0x3FF << 0)
+
+#define SST1_VIDEO_DEBUG_FSM_STATE (0x5460)
+#define DATA_PACK_FSM_STATE (0x3F << 16)
+#define LINE_FSM_STATE (0x07 << 8)
+#define PIXEL_FSM_STATE (0x07 << 0)
+
+#define SST1_VIDEO_DEBUG_MAPI (0x5464)
+#define MAPI_UNDERFLOW_STATUS (0x01 << 0)
+
+#define SST1_VIDEO_DEBUG_ACTV_SYM_STEP_CNTL (0x5468)
+#define ACTV_SYM_STEP_CNT_VAL (0x3FF << 1)
+#define ACTV_SYM_STEP_CNT_EN (0x01 << 0)
+
+#define SST1_VIDEO_DEBUG_HOR_BLANK_AUD_BW_ADJ (0x546C)
+#define HOR_BLANK_AUD_BW_ADJ (0x01 << 0)
+
+#define SST1_AUDIO_CONTROL (0x5800)
+#define DMA_REQ_GEN_EN (0x01 << 30)
+#define SW_AUD_CODING_TYPE (0x07 << 27)
+#define AUD_DMA_IF_LTNCY_TRG_MODE (0x01 << 26)
+#define AUD_DMA_IF_MODE_CONFIG (0x01 << 25)
+#define AUD_ODD_CHANNEL_DUMMY (0x01 << 24)
+#define AUD_M_VALUE_CMP_SPD_MASTER (0x07 << 21)
+#define DMA_BURST_SEL (0x07 << 18)
+#define AUDIO_BIT_MAPPING_TYPE (0x03 << 16)
+#define PCM_SIZE (0x03 << 13)
+#define AUDIO_CH_STATUS_SAME (0x01 << 5)
+#define AUD_GTC_CHST_EN (0x01 << 1)
+
+#define SST1_AUDIO_ENABLE (0x5804)
+#define AUDIO_EN (0x01 << 0)
+
+#define SST1_AUDIO_MASTER_TIMING_GEN (0x5808)
+#define AUDIO_MASTER_TIME_GEN (0x01 << 0)
+
+#define SST1_AUDIO_DMA_REQUEST_LATENCY_CONFIG (0x580C)
+#define AUD_DMA_ACK_STATUS (0x01 << 21)
+#define AUD_DMA_FORCE_ACK (0x01 << 20)
+#define AUD_DMA_FORCE_ACK_SEL (0x01 << 19)
+#define AUD_DMA_REQ_STATUS (0x01 << 18)
+#define AUD_DMA_FORCE_REQ_VAL (0x01 << 17)
+#define AUD_DMA_FORCE_REQ_SEL (0x01 << 16)
+#define MASTER_DMA_REQ_LTNCY_CONFIG (0xFF << 0)
+
+#define SST1_AUDIO_MUTE_CONTROL (0x5810)
+#define AUD_MUTE_UNDRUN_EN (0x01 << 5)
+#define AUD_MUTE_OVFLOW_EN (0x01 << 4)
+#define AUD_MUTE_CLKCHG_EN (0x01 << 1)
+
+#define SST1_AUDIO_MARGIN_CONTROL (0x5814)
+#define FORCE_AUDIO_MARGIN (0x01 << 16)
+#define AUDIO_MARGIN (0x1FFF << 0)
+
+#define SST1_AUDIO_DATA_WRITE_FIFO (0x5818)
+#define AUDIO_DATA_FIFO (0xFFFFFFFF << 0)
+
+#define SST1_AUDIO_GTC_CONTROL (0x5824)
+#define AUD_GTC_DELTA (0xFFFFFFFF << 0)
+
+#define SST1_AUDIO_GTC_VALID_BIT_CONTROL (0x5828)
+#define AUDIO_GTC_VALID_CONTROL (0x01 << 1)
+#define AUDIO_GTC_VALID (0x01 << 0)
+
+#define SST1_AUDIO_3DLPCM_PACKET_WAIT_TIMER (0x582C)
+#define AUDIO_3D_PKT_WAIT_TIMER (0x3F << 0)
+
+#define SST1_AUDIO_BIST_CONTROL (0x5830)
+#define SIN_AMPL (0x0F << 4)
+#define AUD_BIST_EN (0x01 << 0)
+
+#define SST1_AUDIO_BIST_CHANNEL_STATUS_SET0 (0x5834)
+#define CHNL_BIT1 (0x03 << 30)
+#define CLK_ACCUR (0x03 << 28)
+#define FS_FREQ (0x0F << 24)
+#define CH_NUM (0x0F << 20)
+#define SOURCE_NUM (0x0F << 16)
+#define CAT_CODE (0xFF << 8)
+#define MODE (0x03 << 6)
+#define PCM_MODE (0x07 << 3)
+#define SW_CPRGT (0x01 << 2)
+#define NON_PCM (0x01 << 1)
+#define PROF_APP (0x01 << 0)
+
+#define SST1_AUDIO_BIST_CHANNEL_STATUS_SET1 (0x5838)
+#define CHNL_BIT2 (0x0F << 4)
+#define WORD_LENGTH (0x07 << 1)
+#define WORD_MAX (0x01 << 0)
+
+#define SST1_AUDIO_BUFFER_CONTROL (0x583C)
+#define MASTER_AUDIO_INIT_BUFFER_THRD (0x7F << 24)
+#define MASTER_AUDIO_BUFFER_THRD (0x3F << 18)
+#define MASTER_AUDIO_BUFFER_EMPTY_INT_MASK (0x01 << 17)
+#define MASTER_AUDIO_CHANNEL_COUNT (0x1F << 12)
+#define MASTER_AUDIO_BUFFER_LEVEL (0x7F << 5)
+#define MASTER_AUDIO_BUFFER_LEVEL_BIT_POS (5)
+#define AUD_DMA_NOISE_INT_MASK (0x01 << 4)
+#define AUD_DMA_NOISE_INT (0x01 << 3)
+#define AUD_DMA_NOISE_INT_EN (0x01 << 2)
+#define MASTER_AUDIO_BUFFER_EMPTY_INT (0x01 << 1)
+#define MASTER_AUDIO_BUFFER_EMPTY_INT_EN (0x01 << 0)
+
+#define SST1_AUDIO_CHANNEL_1_4_REMAP (0x5840)
+#define AUD_CH_04_REMAP (0x3F << 24)
+#define AUD_CH_03_REMAP (0x3F << 16)
+#define AUD_CH_02_REMAP (0x3F << 8)
+#define AUD_CH_01_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_5_8_REMAP (0x5844)
+#define AUD_CH_08_REMAP (0x3F << 24)
+#define AUD_CH_07_REMAP (0x3F << 16)
+#define AUD_CH_06_REMAP (0x3F << 8)
+#define AUD_CH_05_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_9_12_REMAP (0x5848)
+#define AUD_CH_12_REMAP (0x3F << 24)
+#define AUD_CH_11_REMAP (0x3F << 16)
+#define AUD_CH_10_REMAP (0x3F << 8)
+#define AUD_CH_09_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_13_16_REMAP (0x584C)
+#define AUD_CH_16_REMAP (0x3F << 24)
+#define AUD_CH_15_REMAP (0x3F << 16)
+#define AUD_CH_14_REMAP (0x3F << 8)
+#define AUD_CH_13_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_17_20_REMAP (0x5850)
+#define AUD_CH_20_REMAP (0x3F << 24)
+#define AUD_CH_19_REMAP (0x3F << 16)
+#define AUD_CH_18_REMAP (0x3F << 8)
+#define AUD_CH_17_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_21_24_REMAP (0x5854)
+#define AUD_CH_24_REMAP (0x3F << 24)
+#define AUD_CH_23_REMAP (0x3F << 16)
+#define AUD_CH_22_REMAP (0x3F << 8)
+#define AUD_CH_21_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_25_28_REMAP (0x5858)
+#define AUD_CH_28_REMAP (0x3F << 24)
+#define AUD_CH_27_REMAP (0x3F << 16)
+#define AUD_CH_26_REMAP (0x3F << 8)
+#define AUD_CH_25_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_29_32_REMAP (0x585C)
+#define AUD_CH_32_REMAP (0x3F << 24)
+#define AUD_CH_31_REMAP (0x3F << 16)
+#define AUD_CH_30_REMAP (0x3F << 8)
+#define AUD_CH_29_REMAP (0x3F << 0)
+
+#define SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_0 (0x5860)
+#define MASTER_AUD_GP0_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP0_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP0_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP0_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_1_2_STATUS_CTRL_1 (0x5864)
+#define MASTER_AUD_GP0_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_3_4_STATUS_CTRL_0 (0x5868)
+#define MASTER_AUD_GP1_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP1_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP1_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP1_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_3_4_STATUS_CTRL_1 (0x586C)
+#define MASTER_AUD_GP1_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_5_6_STATUS_CTRL_0 (0x5870)
+#define MASTER_AUD_GP2_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP2_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP2_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP2_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_5_6_STATUS_CTRL_1 (0x5874)
+#define MASTER_AUD_GP2_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_7_8_STATUS_CTRL_0 (0x5878)
+#define MASTER_AUD_GP3_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP3_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP3_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP3_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_7_8_STATUS_CTRL_1 (0x587C)
+#define MASTER_AUD_GP3_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_9_10_STATUS_CTRL_0 (0x5880)
+#define MASTER_AUD_GP4_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP4_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP4_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP4_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_9_10_STATUS_CTRL_1 (0x5884)
+#define MASTER_AUD_GP4_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_11_12_STATUS_CTRL_0 (0x5888)
+#define MASTER_AUD_GP5_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP5_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP5_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP5_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_11_12_STATUS_CTRL_1 (0x588C)
+#define MASTER_AUD_GP5_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_13_14_STATUS_CTRL_0 (0x5890)
+#define MASTER_AUD_GP6_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP6_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP6_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP6_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_13_14_STATUS_CTRL_1 (0x5894)
+#define MASTER_AUD_GP6_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_15_16_STATUS_CTRL_0 (0x5898)
+#define MASTER_AUD_GP7_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP7_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP7_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP7_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_15_16_STATUS_CTRL_1 (0x589C)
+#define MASTER_AUD_GP7_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_17_18_STATUS_CTRL_0 (0x58A0)
+#define MASTER_AUD_GP8_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP8_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP8_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP8_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_17_18_STATUS_CTRL_1 (0x58A4)
+#define MASTER_AUD_GP8_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_19_20_STATUS_CTRL_0 (0x58A8)
+#define MASTER_AUD_GP9_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP9_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP9_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP9_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_19_20_STATUS_CTRL_1 (0x58AC)
+#define MASTER_AUD_GP9_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_21_22_STATUS_CTRL_0 (0x58B0)
+#define MASTER_AUD_GP10_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP10_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP10_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP10_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_21_22_STATUS_CTRL_1 (0x58B4)
+#define MASTER_AUD_GP10_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_23_24_STATUS_CTRL_0 (0x58B8)
+#define MASTER_AUD_GP11_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP11_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP11_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP11_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_23_24_STATUS_CTRL_1 (0x58BC)
+#define MASTER_AUD_GP11_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_25_26_STATUS_CTRL_0 (0x58C0)
+#define MASTER_AUD_GP12_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP12_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP12_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP12_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_25_26_STATUS_CTRL_1 (0x58C4)
+#define MASTER_AUD_GP12_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_27_28_STATUS_CTRL_0 (0x58C8)
+#define MASTER_AUD_GP13_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP13_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP13_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP13_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_27_28_STATUS_CTRL_1 (0x58CC)
+#define MASTER_AUD_GP13_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_29_30_STATUS_CTRL_0 (0x58D0)
+#define MASTER_AUD_GP14_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP14_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP14_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP14_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_29_30_STATUS_CTRL_1 (0x58D4)
+#define MASTER_AUD_GP14_STA_4 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_31_32_STATUS_CTRL_0 (0x58D8)
+#define MASTER_AUD_GP15_STA_3 (0xFF << 24)
+#define MASTER_AUD_GP15_STA_2 (0xFF << 16)
+#define MASTER_AUD_GP15_STA_1 (0xFF << 8)
+#define MASTER_AUD_GP15_STA_0 (0xFF << 0)
+
+#define SST1_AUDIO_CHANNEL_31_32_STATUS_CTRL_1 (0x58DC)
+#define MASTER_AUD_GP15_STA_4 (0xFF << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_1 (0x58E0)
+#define IF_CRC_CLEAR (0x01 << 13)
+#define IF_CRC_PASS (0x01 << 12)
+#define IF_CRC_FAIL (0x01 << 8)
+#define IF_CRC_SW_COMPARE (0x01 << 4)
+#define IF_CRC_EN (0x01 << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_2 (0x58E4)
+#define IF_CRC_R_REF (0xFF << 16)
+#define IF_CRC_R_RESULT (0xFF << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_3 (0x58E8)
+#define IF_CRC_G_REF (0xFF << 16)
+#define IF_CRC_G_RESULT (0xFF << 0)
+
+#define SST1_STREAM_IF_CRC_CONTROL_4 (0x58EC)
+#define IF_CRC_B_REF (0xFF << 16)
+#define IF_CRC_B_RESULT (0xFF << 0)
+
+#define SST1_AUDIO_DEBUG_MARGIN_CONTROL (0x5900)
+#define AUDIO_DEBUG_MARGIN_EN (0x01 << 6)
+#define AUDIO_DEBUG_MARGIN_VAL (0x3F << 0)
+
+#define SST1_SDP_SPLITTING_CONTROL (0x5C00)
+#define SDP_SPLITTING_EN (0x01 << 0)
+
+#define SST1_INFOFRAME_UPDATE_CONTROL (0x5C04)
+#define HDR_INFO_UPDATE (0x01 << 4)
+#define AUDIO_INFO_UPDATE (0x01 << 3)
+#define AVI_INFO_UPDATE (0x01 << 2)
+#define MPEG_INFO_UPDATE (0x01 << 1)
+#define SPD_INFO_UPDATE (0x01 << 0)
+
+#define SST1_INFOFRAME_SEND_CONTROL (0x5C08)
+#define HDR_INFO_SEND (0x01 << 4)
+#define AUDIO_INFO_SEND (0x01 << 3)
+#define AVI_INFO_SEND (0x01 << 2)
+#define MPEG_INFO_SEND (0x01 << 1)
+#define SPD_INFO_SEND (0x01 << 0)
+
+#define SST1_INFOFRAME_SDP_VERSION_CONTROL (0x5C0C)
+#define INFOFRAME_SDP_HB3_SEL (0x01 << 1)
+#define INFOFRAME_SDP_VERSION_SEL (0x01 << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_TYPE (0x5C10)
+#define SPD_TYPE (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_REUSE_PACKET_CONTROL (0x5C14)
+#define SPD_REUSE_EN (0x01 << 0)
+
+#define SST1_PPS_SDP_CONTROL (0x5C20)
+#define PPS_SDP_CHANGE_STATUS (0x01 << 2)
+#define PPS_SDP_FRAME_SEND_ENABLE (0x01 << 1)
+#define PPS_SDP_UPDATE (0x01 << 0)
+
+#define SST1_VSC_SDP_CONTROL_1 (0x5C24)
+#define VSC_TOTAL_BYTES_IN_SDP (0xFFF << 8)
+#define VSC_CHANGE_STATUS (0x01 << 5)
+#define VSC_FORCE_PACKET_MARGIN (0x01 << 4)
+#define VSC_FIX_PACKET_SEQUENCE (0x01 << 3)
+#define VSC_EXT_VESA_CEA (0x01 << 2)
+#define VSC_SDP_FRAME_SEND_ENABLE (0x01 << 1)
+#define VSC_SDP_UPDATE (0x01 << 0)
+
+#define SST1_VSC_SDP_CONTROL_2 (0x5C28)
+#define VSC_SETUP_TIME (0xFFF << 20)
+#define VSC_PACKET_MARGIN (0x1FFF << 0)
+
+#define SST1_MST_WAIT_TIMER_CONTROL_1 (0x5C2C)
+#define AUDIO_WAIT_TIMER (0x1FFF << 16)
+#define INFOFRAME_WAIT_TIMER (0x1FFF << 0)
+
+#define SST1_MST_WAIT_TIMER_CONTROL_2 (0x5C30)
+#define PPS_WAIT_TIMER (0x1FFF << 16)
+#define VSC_PACKET_WAIT_TIMER (0x1FFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET0 (0x5C40)
+#define AVI_DB4 (0xFF << 24)
+#define AVI_DB3 (0xFF << 16)
+#define AVI_DB2 (0xFF << 8)
+#define AVI_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET1 (0x5C44)
+#define AVI_DB8 (0xFF << 24)
+#define AVI_DB7 (0xFF << 16)
+#define AVI_DB6 (0xFF << 8)
+#define AVI_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET2 (0x5C48)
+#define AVI_DB12 (0xFF << 24)
+#define AVI_DB11 (0xFF << 16)
+#define AVI_DB10 (0xFF << 8)
+#define AVI_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_AVI_PACKET_DATA_SET3 (0x5C4C)
+#define AVI_DB13 (0xFF << 0)
+
+#define SST1_INFOFRAME_AUDIO_PACKET_DATA_SET0 (0x5C50)
+#define AUDIO_DB4 (0xFF << 24)
+#define AUDIO_DB3 (0xFF << 16)
+#define AUDIO_DB2 (0xFF << 8)
+#define AUDIO_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_AUDIO_PACKET_DATA_SET1 (0x5C54)
+#define AUDIO_DB8 (0xFF << 24)
+#define AUDIO_DB7 (0xFF << 16)
+#define AUDIO_DB6 (0xFF << 8)
+#define AUDIO_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_AUDIO_PACKET_DATA_SET2 (0x5C58)
+#define AVI_DB10 (0xFF << 8)
+#define AVI_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET0 (0x5C60)
+#define SPD_DB4 (0xFF << 24)
+#define SPD_DB3 (0xFF << 16)
+#define SPD_DB2 (0xFF << 8)
+#define SPD_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET1 (0x5C64)
+#define SPD_DB8 (0xFF << 24)
+#define SPD_DB7 (0xFF << 16)
+#define SPD_DB6 (0xFF << 8)
+#define SPD_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET2 (0x5C68)
+#define SPD_DB12 (0xFF << 24)
+#define SPD_DB11 (0xFF << 16)
+#define SPD_DB10 (0xFF << 8)
+#define SPD_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET3 (0x5C6C)
+#define SPD_DB16 (0xFF << 24)
+#define SPD_DB15 (0xFF << 16)
+#define SPD_DB14 (0xFF << 8)
+#define SPD_DB13 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET4 (0x5C70)
+#define SPD_DB20 (0xFF << 24)
+#define SPD_DB19 (0xFF << 16)
+#define SPD_DB18 (0xFF << 8)
+#define SPD_DB17 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET5 (0x5C74)
+#define SPD_DB24 (0xFF << 24)
+#define SPD_DB23 (0xFF << 16)
+#define SPD_DB22 (0xFF << 8)
+#define SPD_DB21 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_PACKET_DATA_SET6 (0x5C78)
+#define SPD_DB25 (0xFF << 0)
+
+#define SST1_INFOFRAME_MPEG_PACKET_DATA_SET0 (0x5C80)
+#define MPEG_DB4 (0xFF << 24)
+#define MPEG_DB3 (0xFF << 16)
+#define MPEG_DB2 (0xFF << 8)
+#define MPEG_DB1 (0xFF << 0)
+
+#define SST1_INFOFRAME_MPEG_PACKET_DATA_SET1 (0x5C84)
+#define MPEG_DB8 (0xFF << 24)
+#define MPEG_DB7 (0xFF << 16)
+#define MPEG_DB6 (0xFF << 8)
+#define MPEG_DB5 (0xFF << 0)
+
+#define SST1_INFOFRAME_MPEG_PACKET_DATA_SET2 (0x5C88)
+#define MPEG_DB10 (0xFF << 8)
+#define MPEG_DB9 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_REUSE_PACKET_HEADER_SET (0x5C90)
+#define SPD_REUSE_HB3 (0xFF << 24)
+#define SPD_REUSE_HB2 (0xFF << 16)
+#define SPD_REUSE_HB1 (0xFF << 8)
+#define SPD_REUSE_HB0 (0xFF << 0)
+
+#define SST1_INFOFRAME_SPD_REUSE_PACKET_PARITY_SET (0x5C94)
+#define SPD_REUSE_PB3 (0xFF << 24)
+#define SPD_REUSE_PB2 (0xFF << 16)
+#define SPD_REUSE_PB1 (0xFF << 8)
+#define SPD_REUSE_PB0 (0xFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_0 (0x5CA0)
+#define HDR_INFOFRAME_DATA_0 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_1 (0x5CA4)
+#define HDR_INFOFRAME_DATA_1 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_2 (0x5CA8)
+#define HDR_INFOFRAME_DATA_2 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_3 (0x5CAC)
+#define HDR_INFOFRAME_DATA_3 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_4 (0x5CB0)
+#define HDR_INFOFRAME_DATA_4 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_5 (0x5CB4)
+#define HDR_INFOFRAME_DATA_5 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_6 (0x5CB8)
+#define HDR_INFOFRAME_DATA_6 (0xFFFFFFFF << 0)
+
+#define SST1_HDR_PACKET_DATA_SET_7 (0x5CBC)
+#define HDR_INFOFRAME_DATA_7 (0xFFFFFFFF << 0)
+
+/* PPS03[31:24], PPS02[23:16], PPS01[15:8], PPS00[7:0] */
+#define PPS0xN(val) (val)
+#define PPS1xN(val) (val << 8)
+#define PPS2xN(val) (val << 16)
+#define PPS3xN(val) (val << 24)
+
+#define SST1_PPS_SDP_PAYLOAD_0 (0x5D00)
+#define PPS_SDP_DATA_0 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_1 (0x5D04)
+#define PPS_SDP_DATA_1 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_2 (0x5D08)
+#define PPS_SDP_DATA_2 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_3 (0x5D0C)
+#define PPS_SDP_DATA_3 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_4 (0x5D10)
+#define PPS_SDP_DATA_4 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_5 (0x5D14)
+#define PPS_SDP_DATA_5 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_6 (0x5D18)
+#define PPS_SDP_DATA_6 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_7 (0x5D1C)
+#define PPS_SDP_DATA_7 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_8 (0x5D20)
+#define PPS_SDP_DATA_8 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_9 (0x5D24)
+#define PPS_SDP_DATA_9 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_10 (0x5D28)
+#define PPS_SDP_DATA_10 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_11 (0x5D2C)
+#define PPS_SDP_DATA_11 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_12 (0x5D30)
+#define PPS_SDP_DATA_12 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_13 (0x5D34)
+#define PPS_SDP_DATA_13 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_14 (0x5D38)
+#define PPS_SDP_DATA_14 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_15 (0x5D3C)
+#define PPS_SDP_DATA_15 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_16 (0x5D40)
+#define PPS_SDP_DATA_16 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_17 (0x5D44)
+#define PPS_SDP_DATA_17 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_18 (0x5D48)
+#define PPS_SDP_DATA_18 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_19 (0x5D4C)
+#define PPS_SDP_DATA_19 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_20 (0x5D50)
+#define PPS_SDP_DATA_20 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_21 (0x5D54)
+#define PPS_SDP_DATA_21 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_22 (0x5D58)
+#define PPS_SDP_DATA_22 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_23 (0x5D5C)
+#define PPS_SDP_DATA_23 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_24 (0x5D60)
+#define PPS_SDP_DATA_24 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_25 (0x5D64)
+#define PPS_SDP_DATA_25 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_26 (0x5D68)
+#define PPS_SDP_DATA_26 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_27 (0x5D6C)
+#define PPS_SDP_DATA_27 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_28 (0x5D70)
+#define PPS_SDP_DATA_28 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_29 (0x5D74)
+#define PPS_SDP_DATA_29 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_30 (0x5D78)
+#define PPS_SDP_DATA_30 (0xFFFFFFFF << 0)
+
+#define SST1_PPS_SDP_PAYLOAD_31 (0x5D7C)
+#define PPS_SDP_DATA_31 (0xFFFFFFFF << 0)
+
+#define SST1_VSC_SDP_DATA_PAYLOAD_FIFO (0x5D80)
+#define VSC_SDP_DATA_PAYLOAD_FIFO (0xFFFFFFFF << 0)
+
+/* PHY register */
+#define DEFAULT_SFR_CNT 54
+
+#define CMN_REG0009 (0x0024)
+#define ANA_AUX_TX_LVL_CTRL (0x0F << 3)
+#define ANA_AUX_TX_LVL_CTRL_VAL(_v_) ((_v_) << 3)
+
+#define CMN_REG00A2 (0x0288)
+#define LANE_MUX_SEL_DP_LN3 (0x01 << 7)
+#define LANE_MUX_SEL_DP_LN2 (0x01 << 6)
+#define LANE_MUX_SEL_DP_LN1 (0x01 << 5)
+#define LANE_MUX_SEL_DP_LN0 (0x01 << 4)
+#define DP_LANE_EN_LN3 (0x01 << 3)
+#define DP_LANE_EN_LN2 (0x01 << 2)
+#define DP_LANE_EN_LN1 (0x01 << 1)
+#define DP_LANE_EN_LN0 (0x01 << 0)
+
+#define CMN_REG00A3 (0x028C)
+#define DP_TX_LINK_BW (0x03 << 5)
+#define DP_TX_LINK_BW_VAL(_v_) ((_v_) << 5)
+#define OVRD_RX_CDR_DATA_MODE_EXIT (0x01 << 4)
+
+#define CMN_REG00B4 (0x02D0)
+#define ROPLL_SSC_EN (0x01 << 1)
+
+#define CMN_REG00E3 (0x038C)
+#define DP_INIT_RSTN (0x01 << 3)
+#define DP_CMN_RSTN (0x01 << 2)
+#define DP_CMN_RSTN_VAL(_v_) ((_v_) << 2)
+#define CDR_WATCHDOG_EN (0x01 << 1)
+
+#define TRSV_REG0204 (0x0810)
+#define LN0_TX_DRV_LVL_CTRL (0x1F << 0)
+
+#define TRSV_REG0215 (0x0854)
+#define LN0_ANA_TX_SER_TXCLK_INV (0x01 << 1)
+
+#define TRSV_REG0400 (0x1000)
+#define OVRD_LN1_TX_DRV_BECON_LFPS_OUT_EN (0x01 << 5)
+
+#define TRSV_REG0404 (0x1010)
+#define LN1_TX_DRV_LVL_CTRL (0x1F << 0)
+
+#define TRSV_REG0415 (0x1054)
+#define LN1_ANA_TX_SER_TXCLK_INV (0x01 << 1)
+
+#define TRSV_REG0604 (0x1810)
+#define LN2_TX_DRV_LVL_CTRL (0x1F << 0)
+
+#define TRSV_REG0615 (0x1854)
+#define LN2_ANA_TX_SER_TXCLK_INV (0x01 << 1)
+
+#define TRSV_REG0800 (0x2000)
+#define OVRD_LN3_TX_DRV_BECON_LFPS_OUT_EN (0x01 << 5)
+
+#define TRSV_REG0804 (0x2010)
+#define LN3_TX_DRV_LVL_CTRL (0x1F << 0)
+
+#define TRSV_REG0815 (0x2054)
+#define LN3_ANA_TX_SER_TXCLK_INV (0x01 << 1)
+
+#define MAX_SLICE_COUNT 4
+#define SST_REG(reg) (reg + (0x1000 * sst_id))
+
+DEFINE_CAL_REGS_FUNCS(dp_link, MAX_DP_CNT);
+DEFINE_CAL_REGS_FUNCS(dp_phy, MAX_DP_CNT);
+
+const u32 phy_default_value[DEFAULT_SFR_CNT][2] = {
+ { 0x0830, 0x07 }, { 0x085C, 0x80 }, { 0x1030, 0x07 }, { 0x105C, 0x80 },
+ { 0x1830, 0x07 }, { 0x185C, 0x80 }, { 0x2030, 0x07 }, { 0x205C, 0x80 },
+ { 0x0228, 0x38 }, { 0x0104, 0x44 }, { 0x0248, 0x44 }, { 0x038C, 0x02 },
+ { 0x0878, 0x04 }, { 0x1878, 0x04 }, { 0x0898, 0x77 }, { 0x1898, 0x77 },
+ { 0x0054, 0x01 }, { 0x00E0, 0x38 }, { 0x0060, 0x24 }, { 0x0064, 0x77 },
+ { 0x0070, 0x76 }, { 0x0234, 0xE8 }, { 0x0AF4, 0x15 }, { 0x1AF4, 0x15 },
+ { 0x081C, 0xE5 }, { 0x181C, 0xE5 }, { 0x091C, 0x1F }, { 0x191C, 0x1F },
+ { 0x0928, 0x7C }, { 0x1928, 0x7C }, { 0x0994, 0x14 }, { 0x1994, 0x14 },
+ { 0x099C, 0x48 }, { 0x199C, 0x48 }, { 0x09A4, 0x0B }, { 0x09A8, 0x62 },
+ { 0x19A4, 0x0B }, { 0x19A8, 0x62 }, { 0x09B8, 0x3E }, { 0x19B8, 0x3E },
+ { 0x09E4, 0x05 }, { 0x09E8, 0x02 }, { 0x09EC, 0x02 }, { 0x19E4, 0x05 },
+ { 0x19E8, 0x02 }, { 0x19EC, 0x02 }, { 0x0A34, 0x1C }, { 0x1A34, 0x1C },
+ { 0x0A98, 0x2F }, { 0x1A98, 0x2F }, { 0x0C30, 0x0C }, { 0x0C48, 0x08 },
+ { 0x1C30, 0x0C }, { 0x1C48, 0x08 },
+};
+
+const u32 phy_tune_parameters[4][4][4] = {
+ /* {amp, post, pre, idrv} */
+ {
+ /* Swing Level_0 */
+ { 0x21, 0x10, 0x42, 0xE5 }, /* Pre-emphasis Level_0 */
+ { 0x25, 0x14, 0x42, 0xE5 }, /* Pre-emphasis Level_1 */
+ { 0x26, 0x17, 0x43, 0xE5 }, /* Pre-emphasis Level_2 */
+ { 0x2B, 0x1C, 0x43, 0xE7 }, /* Pre-emphasis Level_3 */
+ },
+ {
+ /* Swing Level_1 */
+ { 0x26, 0x10, 0x42, 0xE7 }, /* Pre-emphasis Level_0 */
+ { 0x2B, 0x15, 0x42, 0xE7 }, /* Pre-emphasis Level_1 */
+ { 0x2B, 0x18, 0x43, 0xE7 }, /* Pre-emphasis Level_2 */
+ { 0x2B, 0x18, 0x43, 0xE7 }, /* Pre-emphasis Level_3 */
+ },
+ {
+ /* Swing Level_2 */
+ { 0x2A, 0x10, 0x42, 0xE7 }, /* Pre-emphasis Level_0 */
+ { 0x2B, 0x15, 0x43, 0xE7 }, /* Pre-emphasis Level_1 */
+ { 0x2B, 0x15, 0x43, 0xE7 }, /* Pre-emphasis Level_2 */
+ { 0x2B, 0x15, 0x43, 0xE7 }, /* Pre-emphasis Level_3 */
+ },
+ {
+ /* Swing Level_3 */
+ { 0x2B, 0x10, 0x43, 0xE7 }, /* Pre-emphasis Level_0 */
+ { 0x2B, 0x10, 0x43, 0xE7 }, /* Pre-emphasis Level_1 */
+ { 0x2B, 0x10, 0x43, 0xE7 }, /* Pre-emphasis Level_2 */
+ { 0x2B, 0x10, 0x43, 0xE7 }, /* Pre-emphasis Level_3 */
+ },
+};
+
+const struct dp_support_video support_videos[] = {
+ /* width, hsync, height, vsync, vbackporch,
+ * hfrontporch,hbackporch, vfrontporch, pixel_clk, v_sync_pol, h_sync_pol, vic, name
+ */
+ { V640X480P60, 640, 16, 96, 48, 480, 10, 2, 33, 25200000, SYNC_NEGATIVE,
+ SYNC_NEGATIVE, 1, "V640X480P60" },
+ { V640X480P30, 640, 16, 96, 48, 480, 10, 2, 33, 12600000, SYNC_NEGATIVE,
+ SYNC_NEGATIVE, 1, "V640X480P30" },
+ { V720X480P60, 720, 16, 62, 60, 480, 9, 6, 30, 27027000, SYNC_NEGATIVE,
+ SYNC_NEGATIVE, 2, "V720X480P60" },
+ { V720X576P50, 720, 12, 64, 68, 576, 5, 5, 39, 27000000, SYNC_NEGATIVE,
+ SYNC_NEGATIVE, 17, "V720X576P50" },
+ { V1280X800P60RB, 1280, 48, 32, 80, 800, 3, 6, 14, 71000000,
+ SYNC_NEGATIVE, SYNC_POSITIVE, 0, "V1280X800P60RB" },
+ { V1280X720P60, 1280, 110, 40, 220, 720, 5, 5, 20, 74250000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 4, "V1280X720P60" },
+ { V1366X768P60, 1366, 70, 143, 213, 768, 3, 3, 24, 85500000,
+ SYNC_POSITIVE, SYNC_NEGATIVE, 0, "V1366X768P60" },
+ { V1280X1024P60, 1280, 48, 112, 248, 1024, 1, 3, 38, 108000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 0, "V1280X1024P60" },
+ { V1920X1080P24, 1920, 638, 44, 148, 1080, 4, 5, 36, 74250000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 32, "V1920X1080P24" },
+ { V1920X1080P25, 1920, 528, 44, 148, 1080, 4, 5, 36, 74250000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 33, "V1920X1080P25" },
+ { V1920X1080P30, 1920, 88, 44, 148, 1080, 4, 5, 36, 74250000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 34, "V1920X1080P30" },
+ { V1600X900P60RB, 1600, 24, 80, 96, 900, 1, 3, 96, 108000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 0, "V1600X900P60RB" },
+ { V1920X1080P60, 1920, 88, 44, 148, 1080, 4, 5, 36, 148500000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 16, "V1920X1080P60" },
+ { V1920X1200P60, 1920, 48, 32, 80, 1200, 3, 6, 26, 154000000,
+ SYNC_NEGATIVE, SYNC_POSITIVE, 16, "V1920X1200P60" },
+ { V1920X1200P60P, 1920, 48, 32, 80, 1200, 3, 6, 26, 154128000,
+ SYNC_NEGATIVE, SYNC_POSITIVE, 16, "V1920X1200P60P" },
+ { V1920X1200P30, 1920, 48, 32, 80, 1200, 3, 6, 26, 77000000,
+ SYNC_NEGATIVE, SYNC_POSITIVE, 16, "V1920X1200P30" },
+ { V1920X1200P30P, 1920, 48, 32, 80, 1200, 3, 6, 26, 77064000,
+ SYNC_NEGATIVE, SYNC_POSITIVE, 16, "V1920X1200P30P" },
+ { V3840X2160P24, 3840, 1276, 88, 296, 2160, 8, 10, 72, 297000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 93, "V3840X2160P24" },
+ { V3840X2160P25, 3840, 1056, 88, 296, 2160, 8, 10, 72, 297000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 94, "V3840X2160P25" },
+ { V3840X2160P30, 3840, 176, 88, 296, 2160, 8, 10, 72, 297000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 95, "V3840X2160P30" },
+ { V4096X2160P24, 4096, 1020, 88, 296, 2160, 8, 10, 72, 297000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 98, "V4096X2160P24" },
+ { V4096X2160P25, 4096, 968, 88, 128, 2160, 8, 10, 72, 297000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 99, "V4096X2160P25" },
+ { V4096X2160P30, 4096, 88, 88, 128, 2160, 8, 10, 72, 297000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 100, "V4096X2160P30" },
+ { V3840X2160P50, 3840, 1056, 88, 296, 2160, 8, 10, 72, 594000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 96, "V3840X2160P50" },
+ { V3840X2160P60, 3840, 176, 88, 296, 2160, 8, 10, 72, 594000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 97, "V3840X2160P60" },
+ { V4096X2160P50, 4096, 968, 88, 128, 2160, 8, 10, 72, 594000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 101, "V4096X2160P50" },
+ { V4096X2160P60, 4096, 88, 88, 128, 2160, 8, 10, 72, 594000000,
+ SYNC_POSITIVE, SYNC_POSITIVE, 102, "V4096X2160P60" },
+ /* Used for ICAS */
+ { V1560X700P60, 1560, 30, 40, 70, 700, 9, 2, 20, 74562000,
+ SYNC_NEGATIVE, SYNC_NEGATIVE, 20, "V1560X700P60" },
+ { V800X400P60, 800, 112, 128, 136, 400, 10, 10, 25, 31399200,
+ SYNC_NEGATIVE, SYNC_NEGATIVE, 20, "V800X400P60" },
+ { V1120X780P60, 1120, 125, 1, 16, 780, 77, 1, 10, 65600000,
+ SYNC_NEGATIVE, SYNC_NEGATIVE, 20, "V1120X780P60" },
+ /* Used for AR HUD */
+ { V1440X720P60, 1440, 32, 8, 32, 720, 16, 22, 2, 66000000,
+ SYNC_POSITIVE, SYNC_NEGATIVE, 20, "V1440X720P60" },
+ { V1440X2560P60, 1440, 96, 64, 160, 2560, 27, 24, 6, 276300000,
+ SYNC_POSITIVE, SYNC_NEGATIVE, 20, "V1440X2560P60" },
+ { V1800X900P60, 1800, 166, 101, 67, 900, 12, 10, 10, 119400000,
+ SYNC_POSITIVE, SYNC_NEGATIVE, 20, "V1800X900P60" },
+};
+
+int dp_regs_desc_init(u32 dp_id, struct dp_regs *regs)
+{
+ cal_regs_desc_check(dp_id, MAX_DP_CNT, __func__);
+
+ cal_regs_desc_save(dp_link, regs->link_addr, __func__, REGS_DP, dp_id);
+ cal_regs_desc_save(dp_phy, regs->phy_addr, __func__, REGS_DPPHY, dp_id);
+
+ return 0;
+}
+void dp_reg_sw_reset(u32 id)
+{
+ u32 cnt = 10;
+ u32 state;
+
+ dp_link_write_mask(id, SYSTEM_SW_RESET_CONTROL, ~0, SW_RESET);
+
+ do {
+ state = dp_link_read(id, SYSTEM_SW_RESET_CONTROL) & SW_RESET;
+ cnt--;
+ udelay(1);
+ } while (state && cnt);
+
+ if (!cnt)
+ cal_log_err(id, "%s is timeout.\n", __func__);
+}
+void dp_reg_phy_reset(u32 id, u32 en)
+{
+ if (en)
+ dp_phy_write_mask(id, CMN_REG00E3, DP_CMN_RSTN_VAL(0),
+ DP_CMN_RSTN);
+ else {
+ dp_phy_write(id, CMN_REG00E3, DP_INIT_RSTN | CDR_WATCHDOG_EN);
+
+ udelay(1);
+
+ dp_phy_write(id, CMN_REG00E3,
+ DP_INIT_RSTN | DP_CMN_RSTN | CDR_WATCHDOG_EN);
+ }
+}
+
+void dp_reg_phy_init_setting(u32 id)
+{
+ int i;
+
+ for (i = 0; i < DEFAULT_SFR_CNT; i++)
+ dp_phy_write(id, phy_default_value[i][0],
+ phy_default_value[i][1]);
+
+ dp_phy_write_mask(id, CMN_REG0009, DP_CMN_RSTN_VAL(0xD),
+ ANA_AUX_TX_LVL_CTRL);
+}
+
+u32 dp_reg_phy_get_link_bw(u32 id)
+{
+ u32 val = 0;
+
+ val = dp_phy_read_mask(id, CMN_REG00A3, DP_TX_LINK_BW) >> 5;
+
+ switch (val) {
+ case 0x03:
+ val = LINK_RATE_8_1Gbps;
+ break;
+ case 0x02:
+ default:
+ val = LINK_RATE_5_4Gbps;
+ break;
+ case 0x01:
+ val = LINK_RATE_2_7Gbps;
+ break;
+ case 0x00:
+ val = LINK_RATE_1_62Gbps;
+ break;
+ }
+
+ return val;
+}
+
+void dp_reg_phy_set_link_bw(u32 id, u8 link_rate)
+{
+ u32 val = 0;
+
+ switch (link_rate) {
+ case LINK_RATE_8_1Gbps:
+ val = 0x03;
+ break;
+ case LINK_RATE_5_4Gbps:
+ val = 0x02;
+ break;
+ case LINK_RATE_2_7Gbps:
+ val = 0x01;
+ break;
+ case LINK_RATE_1_62Gbps:
+ val = 0x00;
+ break;
+ default:
+ val = 0x02;
+ }
+
+ dp_phy_write_mask(id, CMN_REG00A3, DP_TX_LINK_BW_VAL(val),
+ DP_TX_LINK_BW);
+}
+
+void dp_reg_phy_mode_setting(u32 id)
+{
+ u32 lane_config_val = 0;
+ u32 lane_en_val = 0;
+
+ lane_config_val = LANE_MUX_SEL_DP_LN3 | LANE_MUX_SEL_DP_LN2 |
+ LANE_MUX_SEL_DP_LN1 | LANE_MUX_SEL_DP_LN0;
+ lane_en_val = DP_LANE_EN_LN3 | DP_LANE_EN_LN2 | DP_LANE_EN_LN1 |
+ DP_LANE_EN_LN0;
+
+ if (dp_reg_phy_get_link_bw(id) < LINK_RATE_5_4Gbps) {
+ dp_phy_write_mask(id, TRSV_REG0215, ~0,
+ LN0_ANA_TX_SER_TXCLK_INV);
+ dp_phy_write_mask(id, TRSV_REG0415, ~0,
+ LN1_ANA_TX_SER_TXCLK_INV);
+ dp_phy_write_mask(id, TRSV_REG0615, ~0,
+ LN2_ANA_TX_SER_TXCLK_INV);
+ dp_phy_write_mask(id, TRSV_REG0815, ~0,
+ LN3_ANA_TX_SER_TXCLK_INV);
+ } else {
+ dp_phy_write_mask(id, TRSV_REG0215, 0,
+ LN0_ANA_TX_SER_TXCLK_INV);
+ dp_phy_write_mask(id, TRSV_REG0415, 0,
+ LN1_ANA_TX_SER_TXCLK_INV);
+ dp_phy_write_mask(id, TRSV_REG0615, 0,
+ LN2_ANA_TX_SER_TXCLK_INV);
+ dp_phy_write_mask(id, TRSV_REG0815, 0,
+ LN3_ANA_TX_SER_TXCLK_INV);
+ }
+
+ dp_phy_write(id, CMN_REG00A2, lane_config_val | lane_en_val);
+}
+
+static void dp_reg_phy_ssc_enable(u32 id, u32 en)
+{
+ u32 val;
+
+ val = en ? ~0 : 0;
+ dp_phy_write_mask(id, CMN_REG00B4, val, ROPLL_SSC_EN);
+}
+
+void dp_reg_wait_phy_pll_lock(u32 id)
+{
+ u32 cnt = 165; /* wait for 150us + 10% margin */
+ u32 state;
+
+ do {
+ state = dp_link_read(id, SYSTEM_PLL_LOCK_CONTROL) &
+ PLL_LOCK_STATUS;
+ cnt--;
+ udelay(1);
+ } while (!state && cnt);
+
+ if (!cnt)
+ cal_log_err(id, "%s is timeout.\n", __func__);
+}
+
+void dp_reg_set_lane_count(u32 id, u8 lane_cnt)
+{
+ dp_link_write(id, SYSTEM_MAIN_LINK_LANE_COUNT, lane_cnt);
+}
+
+u32 dp_reg_get_lane_count(u32 id)
+{
+ return dp_link_read(id, SYSTEM_MAIN_LINK_LANE_COUNT);
+}
+
+void dp_reg_set_enhanced_mode(u32 id, u32 en)
+{
+ int i = 0;
+ u32 val;
+
+ val = en ? ~0 : 0;
+
+ for (i = 0; i < MAX_SST_CNT; i++)
+ dp_link_write_mask(id, SST1_MAIN_CONTROL + 0x1000 * i, val,
+ ENHANCED_MODE);
+}
+
+void dp_reg_set_training_pattern(u32 id, dp_training_pattern pattern)
+{
+ dp_link_write_mask(id, PCS_TEST_PATTERN_CONTROL, 0,
+ LINK_QUALITY_PATTERN_SET);
+ dp_link_write_mask(id, PCS_CONTROL,
+ LINK_TRAINING_PATTERN_SET_VAL(pattern),
+ LINK_TRAINING_PATTERN_SET);
+}
+
+void dp_reg_scrambling_enable(u32 id, bool enable)
+{
+ dp_link_write_mask(id, PCS_CONTROL, !enable, SCRAMBLE_BYPASS);
+}
+
+void dp_reg_set_phy_tune(u32 id, u32 phy_lane_num, u32 amp_lvl, u32 pre_emp_lvl)
+{
+ u32 addr = 0;
+ u32 val = 0;
+ int i;
+
+ switch (phy_lane_num) {
+ case 0:
+ addr = TRSV_REG0204;
+ break;
+ case 1:
+ addr = TRSV_REG0404;
+ break;
+ case 2:
+ addr = TRSV_REG0604;
+ break;
+ case 3:
+ addr = TRSV_REG0804;
+ break;
+ default:
+ addr = TRSV_REG0204;
+ break;
+ }
+
+ for (i = AMP; i <= IDRV; i++) {
+ val = phy_tune_parameters[amp_lvl][pre_emp_lvl][i];
+ dp_phy_write(id, addr + i * 4, val);
+ }
+}
+
+static void dp_reg_set_phy_voltage_and_pre_emphasis(u32 id, u8 *voltage,
+ u8 *pre_emphasis)
+{
+ dp_reg_set_phy_tune(id, 0, voltage[0], pre_emphasis[0]);
+ dp_reg_set_phy_tune(id, 1, voltage[1], pre_emphasis[1]);
+ dp_reg_set_phy_tune(id, 2, voltage[2], pre_emphasis[2]);
+ dp_reg_set_phy_tune(id, 3, voltage[3], pre_emphasis[3]);
+}
+
+void dp_reg_set_voltage_and_pre_emphasis(u32 id, u8 *voltage, u8 *pre_emphasis)
+{
+ dp_reg_set_phy_voltage_and_pre_emphasis(id, voltage, pre_emphasis);
+}
+
+static void dp_reg_common_function_enable(u32 id, u32 en)
+{
+ u32 val;
+
+ val = en ? ~0 : 0;
+
+ dp_link_write_mask(id, SYSTEM_COMMON_FUNCTION_ENABLE, val, PCS_FUNC_EN);
+ dp_link_write_mask(id, SYSTEM_COMMON_FUNCTION_ENABLE, val, AUX_FUNC_EN);
+}
+
+static void dp_reg_sst_function_enable(u32 id, u32 sst_id, u32 en)
+{
+ u32 reg_offset = 0;
+
+ switch (sst_id) {
+ case 0:
+ reg_offset = SYSTEM_SST1_FUNCTION_ENABLE;
+ break;
+ case 1:
+ reg_offset = SYSTEM_SST2_FUNCTION_ENABLE;
+ break;
+ case 2:
+ reg_offset = SYSTEM_SST3_FUNCTION_ENABLE;
+ break;
+ case 3:
+ reg_offset = SYSTEM_SST4_FUNCTION_ENABLE;
+ break;
+ default:
+ reg_offset = SYSTEM_SST1_FUNCTION_ENABLE;
+ }
+
+ dp_link_write_mask(id, reg_offset, en, SST1_VIDEO_FUNC_EN);
+}
+
+static void dp_reg_set_sst_stream_enable(u32 id, u32 sst_id, u32 en)
+{
+ dp_link_write_mask(id, MST_STREAM_1_ENABLE + 0x10 * sst_id, en,
+ STRM_1_EN);
+}
+
+static void
+dp_reg_set_common_interrupt_mask(u32 id, enum dp_interrupt_mask param, u8 set)
+{
+ u32 val = set ? ~0 : 0;
+
+ switch (param) {
+ case HOTPLUG_CHG_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HPD_CHG_MASK);
+ break;
+ case HPD_LOST_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HPD_LOST_MASK);
+ break;
+ case PLUG_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HPD_PLUG_MASK);
+ break;
+ case HPD_IRQ_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HPD_IRQ_MASK);
+ break;
+ case RPLY_RECEIV_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ AUX_REPLY_RECEIVED_MASK);
+ break;
+ case AUX_ERR_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ AUX_ERR_MASK);
+ break;
+ case HDCP_LINK_CHECK_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HDCP_R0_CHECK_FLAG_MASK);
+ break;
+ case HDCP_LINK_FAIL_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HDCP_LINK_CHK_FAIL_MASK);
+ break;
+ case HDCP_R0_READY_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ HDCP_R0_CHECK_FLAG_MASK);
+ break;
+ case PLL_LOCK_CHG_INT_MASK:
+ dp_link_write_mask(id, SYSTEM_IRQ_COMMON_STATUS_MASK, val,
+ PLL_LOCK_CHG_MASK);
+ break;
+ case ALL_INT_MASK:
+ dp_link_write(id, SYSTEM_IRQ_COMMON_STATUS_MASK, 0xFF);
+ break;
+ default:
+ break;
+ }
+}
+
+static void dp_reg_set_sst_interrupt_mask(u32 id, u32 sst_id,
+ enum dp_interrupt_mask param, u8 set)
+{
+ u32 val = set ? ~0 : 0;
+
+ switch (param) {
+ case VIDEO_FIFO_UNDER_FLOW_MASK:
+ dp_link_write_mask(id,
+ SST1_INTERRUPT_MASK_SET0 + 0x1000 * sst_id,
+ val, MAPI_FIFO_UNDER_FLOW_MASK);
+ break;
+ case VSYNC_DET_INT_MASK:
+ dp_link_write_mask(id,
+ SST1_INTERRUPT_MASK_SET0 + 0x1000 * sst_id,
+ val, VSYNC_DET_MASK);
+ break;
+ case ALL_INT_MASK:
+ dp_link_write(id, SST1_INTERRUPT_MASK_SET0 + 0x1000 * sst_id,
+ 0xFF);
+ dp_link_write(id, SST1_INTERRUPT_STATUS_SET1 + 0x1000 * sst_id,
+ 0xFF);
+ break;
+ default:
+ break;
+ }
+}
+
+void dp_reg_set_hpd_interrupt(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dp_link_write(id, SYSTEM_IRQ_COMMON_STATUS, ~0);
+ /*
+ * TODO : Check the PLUG_IN Interrupt of DP_LINK
+ * The role of PLUG_IN is by HPD_GPIO.
+ * So, Enabling DP's PLUG_IN IRQ is redundant.
+ * The Problem occurred because the PLUG_IN IRQ of DP_LINK
+ * was working in the reset sequence when boot.
+ * (The DP suspend and Link_training are overlapped.)
+ */
+
+ dp_reg_set_common_interrupt_mask(id, HPD_IRQ_INT_MASK, val);
+ /* dp_reg_set_common_interrupt_mask(id, HOTPLUG_CHG_INT_MASK, val); */
+ dp_reg_set_common_interrupt_mask(id, HPD_LOST_INT_MASK, val);
+ /* dp_reg_set_common_interrupt_mask(id, PLUG_INT_MASK, val); */
+}
+
+void dp_reg_set_plug_interrupt(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dp_link_write(id, SYSTEM_IRQ_COMMON_STATUS, ~0);
+ /*
+ * TODO : Check the PLUG_IN Interrupt of DP_LINK
+ * DP plug_in/Out is abnormal work after S2R without this code.
+ */
+
+ dp_reg_set_common_interrupt_mask(id, HPD_IRQ_INT_MASK, val);
+ dp_reg_set_common_interrupt_mask(id, HOTPLUG_CHG_INT_MASK, val);
+ dp_reg_set_common_interrupt_mask(id, HPD_LOST_INT_MASK, val);
+ dp_reg_set_common_interrupt_mask(id, PLUG_INT_MASK, val);
+}
+
+#define FIN_MHZ 24
+#define DEGLITCH_UDELAY 10
+
+u32 dp_reg_get_hpd_status(u32 id)
+{
+ u32 val, cnt = 2; /* minimum retry count of double */
+
+ /* try to wait until HPD_DEGLITCH_COUNT which is counted at 24 MHz */
+ val = dp_link_read_mask(id, SYSTEM_HPD_CONTROL, HPD_DEGLITCH_COUNT) >>
+ 16;
+ cnt *= (val / (FIN_MHZ * DEGLITCH_UDELAY));
+
+ do {
+ val = dp_link_read_mask(id, SYSTEM_HPD_CONTROL, HPD_STATUS);
+ if (val)
+ break;
+
+ udelay(DEGLITCH_UDELAY);
+ cnt--;
+ } while (!val && cnt);
+
+ if (!cnt && !val)
+ cal_log_err(id, "HPD_STATUS waiting timeout(%#x)\n",
+ dp_link_read(id, SYSTEM_HPD_CONTROL));
+
+ return val;
+}
+
+u32 dp_reg_get_int_and_clear(u32 id, dp_irq_reg_type_t irq_reg)
+{
+ u32 val = 0;
+ u32 irq_status_reg = 0;
+
+ switch (irq_reg) {
+ case DP_IRQ_REG_SST1_SET0:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET0;
+ break;
+ case DP_IRQ_REG_SST2_SET0:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET0 + 0x1000;
+ break;
+ case DP_IRQ_REG_SST3_SET0:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET0 + 0x2000;
+ break;
+ case DP_IRQ_REG_SST4_SET0:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET0 + 0x3000;
+ break;
+ case DP_IRQ_REG_SST1_SET1:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET1;
+ break;
+ case DP_IRQ_REG_SST2_SET1:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET1 + 0x1000;
+ break;
+ case DP_IRQ_REG_SST3_SET1:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET1 + 0x2000;
+ break;
+ case DP_IRQ_REG_SST4_SET1:
+ irq_status_reg = SST1_INTERRUPT_STATUS_SET1 + 0x3000;
+ break;
+ case DP_IRQ_REG_SYSTEM:
+ default:
+ irq_status_reg = SYSTEM_IRQ_COMMON_STATUS;
+ break;
+ }
+
+ val = dp_link_read(id, irq_status_reg);
+ dp_link_write(id, irq_status_reg, ~0);
+
+ return val;
+}
+
+static void dp_reg_set_daynamic_range(u32 id, u32 sst_id,
+ enum dynamic_range_type dynamic_range)
+{
+ dp_link_write_mask(id, SST1_VIDEO_CONTROL + 0x1000 * sst_id,
+ DYNAMIC_RANGE_VAL(dynamic_range),
+ DYNAMIC_RANGE_MODE);
+}
+
+static void dp_reg_set_video_bist_mode(u32 id, u32 sst_id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dp_link_write_mask(id, SST1_VIDEO_CONTROL + 0x1000 * sst_id, val,
+ STRM_VALID_FORCE | STRM_VALID_CTRL);
+ dp_link_write_mask(id, SST1_VIDEO_BIST_CONTROL + 0x1000 * sst_id, val,
+ BIST_EN);
+}
+
+static void dp_reg_video_format_setting(u32 id,
+ struct dp_video_info dp_video_info)
+{
+ u32 val = 0;
+ struct dp_support_video *vm = &dp_video_info.vm;
+
+ u32 sst_id = dp_video_info.sst_id;
+
+ val += vm->vactive;
+ val += vm->vfront_porch;
+ val += vm->vsync_len;
+ val += vm->vback_porch;
+ dp_link_write(id, SST1_VIDEO_VERTICAL_TOTAL_PIXELS + 0x1000 * sst_id,
+ val);
+
+ val = 0;
+ val += vm->hactive;
+ val += vm->hfront_porch;
+ val += vm->hsync_len;
+ val += vm->hback_porch;
+ dp_link_write(id, SST1_VIDEO_HORIZONTAL_TOTAL_PIXELS + 0x1000 * sst_id,
+ val);
+
+ val = vm->vactive;
+ dp_link_write(id, SST1_VIDEO_VERTICAL_ACTIVE + 0x1000 * sst_id, val);
+
+ val = vm->vfront_porch;
+ dp_link_write(id, SST1_VIDEO_VERTICAL_FRONT_PORCH + 0x1000 * sst_id,
+ val);
+
+ val = vm->vback_porch;
+ dp_link_write(id, SST1_VIDEO_VERTICAL_BACK_PORCH + 0x1000 * sst_id,
+ val);
+
+ val = vm->hactive;
+ dp_link_write(id, SST1_VIDEO_HORIZONTAL_ACTIVE + 0x1000 * sst_id, val);
+
+ val = vm->hfront_porch;
+ dp_link_write(id, SST1_VIDEO_HORIZONTAL_FRONT_PORCH + 0x1000 * sst_id,
+ val);
+
+ val = vm->hback_porch;
+ dp_link_write(id, SST1_VIDEO_HORIZONTAL_BACK_PORCH + 0x1000 * sst_id,
+ val);
+
+ val = vm->vsync_pol;
+ dp_link_write_mask(id, SST1_VIDEO_CONTROL + 0x1000 * sst_id,
+ VSYNC_POLARITY_VAL(val), VSYNC_POLARITY);
+
+ val = vm->hsync_pol;
+ dp_link_write_mask(id, SST1_VIDEO_CONTROL + 0x1000 * sst_id, val,
+ HSYNC_POLARITY);
+}
+
+static bool is_dsc_en(u32 id, u32 sst_id)
+{
+ u32 val = dp_link_read_mask(
+ id, SST_REG(SST1_VIDEO_DSC_STREAM_CONTROL_0), DSC_ENABLE);
+
+ return val ? true : false;
+}
+
+static void dp_reg_set_dsc_pps_sdp_control(u32 id, u32 sst_id)
+{
+ u32 val;
+
+ val = PPS_SDP_UPDATE;
+ dp_link_write(id, SST_REG(SST1_PPS_SDP_CONTROL), val);
+
+ val |= PPS_SDP_FRAME_SEND_ENABLE;
+ dp_link_write(id, SST_REG(SST1_PPS_SDP_CONTROL), val);
+}
+
+static void dp_reg_set_dsc_pps(u32 id, u32 sst_id, u8 *pps, u32 en)
+{
+ u32 pps_val;
+ int i;
+
+ /* DSC pps */
+ for (i = 0; i < MAX_PPS_NUM; i += SZ_4) {
+ /* Clear PPS */
+ if (!en) {
+ dp_link_write(id, SST_REG(SST1_PPS_SDP_PAYLOAD_0) + i,
+ 0);
+ continue;
+ }
+
+ pps_val = 0;
+ pps_val |= PPS0xN(pps[i + 0]);
+ pps_val |= PPS1xN(pps[i + 1]);
+ pps_val |= PPS2xN(pps[i + 2]);
+ pps_val |= PPS3xN(pps[i + 3]);
+
+ dp_link_write(id, SST_REG(SST1_PPS_SDP_PAYLOAD_0) + i, pps_val);
+ cal_log_debug(id, "PPS_SDP_PAYLOAD_%02d(%03d~%03d) 0x%.8X\n",
+ i / SZ_4, (i + SZ_4 - 1), i, pps_val);
+ }
+
+ if (en)
+ dp_reg_set_dsc_pps_sdp_control(id, sst_id);
+}
+
+static void dp_reg_set_dsc_en(u32 id, u32 sst_id, u32 slice_cnt, u32 en)
+{
+ u32 val = 0;
+
+ if (slice_cnt > MAX_SLICE_COUNT && en) {
+ cal_log_err(id, "slice count(%u) not supported\n", slice_cnt);
+ slice_cnt = MAX_SLICE_COUNT;
+ }
+
+ if (en) {
+ val |= DSC_ENABLE;
+ val |= slice_cnt & SLICE_COUNT_PER_LINE;
+ }
+
+ dp_link_write(id, SST_REG(SST1_VIDEO_DSC_STREAM_CONTROL_0), val);
+ cal_log_debug(id, "SST%u DSC_STREAM_CONTROL_0: %#x\n", sst_id + 1, val);
+
+ /* If FEC is not ready then ignore it */
+ if (!dp_link_read_mask(id, PCS_CONTROL, FEC_READY))
+ return;
+
+ val = en ? ~0 : 0;
+ dp_link_write_mask(id, PCS_DEBUG_CONTROL, ~val, FEC_DECODE_DIS_4TH_SEL);
+ dp_link_write_mask(id, PCS_DEBUG_CONTROL, val, FEC_DECODE_EN_4TH_SEL);
+ dp_link_write_mask(id, PCS_CONTROL, val, FEC_FUNC_EN);
+}
+
+static void dp_reg_set_dsc_stream(u32 id, u32 sst_id, struct dp_dsc dsc, u32 en)
+{
+ u32 val[2] = {
+ 0,
+ };
+ int i;
+ u8 slice_cnt = dsc.slice_count;
+ u16 chunk_size = dsc.chunk_size;
+
+ /* request disable but already disabled */
+ if (!en && !is_dsc_en(id, sst_id))
+ return;
+
+ /* request enable but dsc is not required */
+ if (en && !dsc.enable)
+ return;
+
+ for (i = 0; i < slice_cnt && en; i++) {
+ if (i < MAX_SLICE_COUNT / 2)
+ val[0] |= (chunk_size << (i % 2) * 16);
+ else
+ val[1] |= (chunk_size << (i % 2) * 16);
+ }
+ dp_link_write(id, SST_REG(SST1_VIDEO_DSC_STREAM_CONTROL_1), val[0]);
+ dp_link_write(id, SST_REG(SST1_VIDEO_DSC_STREAM_CONTROL_2), val[1]);
+
+ if (en) {
+ cal_log_info(id, "SST%u, slice count(%u), chunk_size(%u)\n",
+ sst_id + 1, slice_cnt, chunk_size);
+
+ cal_log_debug(id, "DSC_STREAM_CONTROL_1: %#x, CONTROL_2: %#x\n",
+ val[0], val[1]);
+ }
+
+ dp_reg_set_dsc_pps(id, sst_id, dsc.pps, en);
+ dp_reg_set_dsc_en(id, sst_id, slice_cnt, en);
+}
+
+void dp_reg_set_dsc_fec(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dp_link_write_mask(id, PCS_CONTROL, val, FEC_READY);
+}
+
+static u32 dp_reg_get_ls_clk(u32 id)
+{
+ u32 val;
+ u32 ls_clk;
+
+ val = dp_reg_phy_get_link_bw(id);
+
+ if (val == LINK_RATE_8_1Gbps)
+ ls_clk = 810000000;
+ else if (val == LINK_RATE_5_4Gbps)
+ ls_clk = 540000000;
+ else if (val == LINK_RATE_2_7Gbps)
+ ls_clk = 270000000;
+ else /* LINK_RATE_1_62Gbps */
+ ls_clk = 162000000;
+
+ return ls_clk;
+}
+
+static void dp_reg_set_video_clock(u32 id, u32 sst_id, u32 pixelclock)
+{
+ u32 stream_clk = 0;
+ u32 ls_clk = 0;
+ u32 mvid_master = 0;
+ u32 nvid_master = 0;
+
+ stream_clk = pixelclock / 1000;
+ ls_clk = dp_reg_get_ls_clk(id) / 1000;
+
+ mvid_master = stream_clk >> 1;
+ nvid_master = ls_clk;
+
+ dp_link_write(id, SST1_MVID_MASTER_MODE + 0x1000 * sst_id, mvid_master);
+ dp_link_write(id, SST1_NVID_MASTER_MODE + 0x1000 * sst_id, nvid_master);
+
+ dp_link_write(id, SST1_MVID_SFR_CONFIGURE + 0x1000 * sst_id,
+ stream_clk);
+ dp_link_write(id, SST1_NVID_SFR_CONFIGURE + 0x1000 * sst_id, ls_clk);
+}
+
+static void dp_reg_set_active_symbol(u32 id, u32 sst_id, u32 pixelclock, u8 bpc)
+{
+ u64 TU_off = 0; /* TU Size when FEC is off*/
+ u64 TU_on = 0; /* TU Size when FEC is on*/
+ u32 bpp = 0; /* Bit Per Pixel */
+ u32 lanecount = 0;
+ u32 bandwidth = 0;
+ u32 integer_fec_off = 0;
+ u32 fraction_fec_off = 0;
+ u32 threshold_fec_off = 0;
+ u32 integer_fec_on = 0;
+ u32 fraction_fec_on = 0;
+ u32 threshold_fec_on = 0;
+ u32 clk = 0;
+
+ switch (bpc) {
+ case BPC_8:
+ bpp = 24;
+ break;
+ case BPC_10:
+ bpp = 30;
+ break;
+ default:
+ bpp = 18;
+ break;
+ }
+
+ /* if DSC on, bpp / 3 */
+ if (is_dsc_en(id, sst_id))
+ bpp /= 3;
+
+ /* change to Mbps from bps of pixel clock*/
+ clk = pixelclock / 1000;
+
+ bandwidth = dp_reg_get_ls_clk(id) / 1000;
+ if (dp_link_read(id, MST_ENABLE) & MST_EN)
+ lanecount =
+ MAX_LANE; /* In MST mode, number of lanes become 4 */
+ else
+ lanecount = dp_reg_get_lane_count(id);
+
+ TU_off = ((clk * bpp * 32) * 10000000000) / (lanecount * bandwidth * 8);
+ TU_on = (TU_off * 1000) / 976;
+
+ integer_fec_off = (u32)(TU_off / 10000000000);
+ fraction_fec_off =
+ (u32)((TU_off - (integer_fec_off * 10000000000)) / 10);
+ integer_fec_on = (u32)(TU_on / 10000000000);
+ fraction_fec_on = (u32)((TU_on - (integer_fec_on * 10000000000)) / 10);
+
+ if (integer_fec_off <= 2)
+ threshold_fec_off = 7;
+ else if (integer_fec_off > 2 && integer_fec_off <= 5)
+ threshold_fec_off = 8;
+ else /* integer_fec_off > 5 */
+ threshold_fec_off = 9;
+
+ if (integer_fec_on <= 2)
+ threshold_fec_on = 7;
+ else if (integer_fec_on > 2 && integer_fec_on <= 5)
+ threshold_fec_on = 8;
+ else /* integer_fec_on > 5 */
+ threshold_fec_on = 9;
+
+ cal_log_debug(
+ id,
+ "fec_off(int: %d, frac: %d, thr: %d), fec_on(int: %d, frac: %d, thr: %d)\n",
+ integer_fec_off, fraction_fec_off, threshold_fec_off,
+ integer_fec_on, fraction_fec_on, threshold_fec_on);
+
+ dp_link_write_mask(id,
+ SST1_ACTIVE_SYMBOL_INTEGER_FEC_OFF + 0x1000 * sst_id,
+ integer_fec_off, ACTIVE_SYMBOL_INTEGER_FEC_OFF);
+ dp_link_write_mask(
+ id, SST1_ACTIVE_SYMBOL_FRACTION_FEC_OFF + 0x1000 * sst_id,
+ fraction_fec_off, ACTIVE_SYMBOL_FRACTION_FEC_OFF);
+ dp_link_write_mask(
+ id, SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_OFF + 0x1000 * sst_id,
+ threshold_fec_off, ACTIVE_SYMBOL_FRACTION_FEC_OFF);
+
+ dp_link_write_mask(id,
+ SST1_ACTIVE_SYMBOL_INTEGER_FEC_ON + 0x1000 * sst_id,
+ integer_fec_on, ACTIVE_SYMBOL_INTEGER_FEC_ON);
+ dp_link_write_mask(id,
+ SST1_ACTIVE_SYMBOL_FRACTION_FEC_ON + 0x1000 * sst_id,
+ fraction_fec_on, ACTIVE_SYMBOL_FRACTION_FEC_OFF);
+ dp_link_write_mask(
+ id, SST1_ACTIVE_SYMBOL_THRESHOLD_FEC_ON + 0x1000 * sst_id,
+ threshold_fec_on, ACTIVE_SYMBOL_THRESHOLD_FEC_ON);
+}
+
+static void dp_reg_aux_ch_buf_clr(u32 id)
+{
+ dp_link_write_mask(id, AUX_BUFFER_CLEAR, 1, AUX_BUF_CLR);
+}
+
+static void dp_reg_aux_defer_ctrl(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+
+ dp_link_write_mask(id, AUX_COMMAND_CONTROL, val, DEFER_CTRL_EN);
+}
+
+static void dp_reg_set_aux_reply_timeout(u32 id)
+{
+ dp_link_write_mask(id, AUX_CONTROL,
+ AUX_REPLY_TIMER_VAL(AUX_TIMEOUT_1800us),
+ AUX_REPLY_TIMER_MODE);
+}
+
+static void dp_reg_set_aux_ch_command(u32 id,
+ enum aux_ch_command_type aux_ch_mode)
+{
+ dp_link_write_mask(id, AUX_REQUEST_CONTROL, REQ_COMM_VAL(aux_ch_mode),
+ REQ_COMM);
+}
+
+static void dp_reg_set_aux_ch_address(u32 id, u32 aux_ch_address)
+{
+ dp_link_write_mask(id, AUX_REQUEST_CONTROL,
+ REQ_ADDR_VAL(aux_ch_address), REQ_ADDR);
+}
+
+static void dp_reg_set_aux_ch_length(u32 id, u32 aux_ch_length)
+{
+ dp_link_write_mask(id, AUX_REQUEST_CONTROL, aux_ch_length - 1,
+ REQ_LENGTH);
+}
+
+static void dp_reg_aux_ch_send_buf(u32 id, u8 *aux_ch_send_buf,
+ u32 aux_ch_length)
+{
+ int i;
+
+ for (i = 0; i < aux_ch_length; i++) {
+ dp_link_write_mask(id, AUX_TX_DATA_SET0 + ((i / 4) * 4),
+ (aux_ch_send_buf[i] << ((i % 4) * 8)),
+ (0x000000FF << ((i % 4) * 8)));
+ }
+}
+
+static void dp_reg_aux_ch_received_buf(u32 id, u8 *aux_ch_received_buf,
+ u32 aux_ch_length)
+{
+ int i;
+
+ for (i = 0; i < aux_ch_length; i++) {
+ aux_ch_received_buf[i] =
+ (dp_link_read_mask(id, AUX_RX_DATA_SET0 + ((i / 4) * 4),
+ 0xFF << ((i % 4) * 8)) >>
+ (i % 4) * 8);
+ }
+}
+
+static int dp_reg_set_aux_ch_operation_enable(u32 id)
+{
+ u32 cnt = 5000;
+ u32 state;
+ u32 val0, val1;
+
+ dp_link_write_mask(id, AUX_TRANSACTION_START, 1, AUX_TRAN_START);
+
+ do {
+ state = dp_link_read(id, AUX_TRANSACTION_START) &
+ AUX_TRAN_START;
+ cnt--;
+ udelay(10);
+ } while (state && cnt);
+
+ if (!cnt) {
+ cal_log_err(id, "AUX_TRAN_START waiting timeout.\n");
+ return -ETIME;
+ }
+
+ val0 = dp_link_read(id, AUX_MONITOR_1);
+ val1 = dp_link_read(id, AUX_MONITOR_2);
+
+ if ((val0 & AUX_CMD_STATUS) != 0x00 || val1 != 0x00) {
+ cal_log_err(id, "AUX_MONITOR_1 : 0x%X, AUX_MONITOR_2 : 0x%X\n",
+ val0, val1);
+ cal_log_err(
+ id,
+ "AUX_CONTROL : 0x%X, AUX_REQUEST_CONTROL : 0x%X, AUX_COMMAND_CONTROL : 0x%X\n",
+ dp_link_read(id, AUX_CONTROL),
+ dp_link_read(id, AUX_REQUEST_CONTROL),
+ dp_link_read(id, AUX_COMMAND_CONTROL));
+
+ udelay(400);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int dp_reg_aux_write(u32 id, u32 comm, u32 address, u32 length, u8 *data)
+{
+ int ret = 0;
+ int retry_cnt = AUX_RETRY_COUNT;
+
+ while (retry_cnt > 0) {
+ dp_reg_aux_ch_buf_clr(id);
+ dp_reg_aux_defer_ctrl(id, 1);
+ dp_reg_set_aux_reply_timeout(id);
+ dp_reg_set_aux_ch_command(id, comm);
+ dp_reg_set_aux_ch_address(id, address);
+ dp_reg_set_aux_ch_length(id, length);
+ if (comm == DPCD_WRITE)
+ dp_reg_aux_ch_send_buf(id, data, length);
+
+ ret = dp_reg_set_aux_ch_operation_enable(id);
+ if (ret == 0) {
+ ret = length;
+ break;
+ }
+ retry_cnt--;
+ }
+
+ return ret;
+}
+
+int dp_reg_aux_read(u32 id, u32 comm, u32 address, u32 length, u8 *data)
+{
+ int ret = 0;
+ int retry_cnt = AUX_RETRY_COUNT;
+
+ while (retry_cnt > 0) {
+ dp_link_write(id, AUX_REQUEST_CONTROL, 0);
+ dp_reg_set_aux_ch_command(id, comm);
+ dp_reg_set_aux_ch_address(id, address);
+ if (length)
+ dp_reg_set_aux_ch_length(id, length);
+ dp_reg_aux_ch_buf_clr(id);
+ dp_reg_aux_defer_ctrl(id, 1);
+ dp_reg_set_aux_reply_timeout(id);
+
+ ret = dp_reg_set_aux_ch_operation_enable(id);
+ if (ret == 0) {
+ dp_reg_aux_ch_received_buf(id, data, length);
+ ret = length;
+ break;
+ }
+ retry_cnt--;
+ }
+
+ return ret;
+}
+
+int dp_reg_dpcd_write_burst(u32 id, u32 address, u32 length, u8 *data)
+{
+ int ret = 0, transferred = 0;
+ u32 i, buf_length, length_calculation;
+
+ length_calculation = length;
+ for (i = 0; i < length; i += AUX_DATA_BUF_COUNT) {
+ if (length_calculation >= AUX_DATA_BUF_COUNT) {
+ buf_length = AUX_DATA_BUF_COUNT;
+ length_calculation -= AUX_DATA_BUF_COUNT;
+ } else {
+ buf_length = length % AUX_DATA_BUF_COUNT;
+ length_calculation = 0;
+ }
+
+ ret = dp_reg_aux_write(id, DPCD_WRITE, address + i, buf_length,
+ data + i);
+ if (ret <= 0) {
+ cal_log_err(id, "DPCD write burst fail\n");
+ break;
+ }
+
+ transferred += ret;
+ }
+
+ return transferred;
+}
+
+int dp_reg_dpcd_read_burst(u32 id, u32 address, u32 length, u8 *data)
+{
+ int ret = 0, transferred = 0;
+ u32 i, buf_length, length_calculation;
+
+ length_calculation = length;
+
+ for (i = 0; i < length; i += AUX_DATA_BUF_COUNT) {
+ if (length_calculation >= AUX_DATA_BUF_COUNT) {
+ buf_length = AUX_DATA_BUF_COUNT;
+ length_calculation -= AUX_DATA_BUF_COUNT;
+ } else {
+ buf_length = length % AUX_DATA_BUF_COUNT;
+ length_calculation = 0;
+ }
+
+ ret = dp_reg_aux_read(id, DPCD_READ, address + i, buf_length,
+ data + i);
+ if (ret <= 0) {
+ cal_log_err(id, "DPCD read burst fail\n");
+ break;
+ }
+
+ transferred += ret;
+ }
+
+ return transferred;
+}
+
+static void dp_reg_set_lane_map(u32 id, u32 lane0, u32 lane1, u32 lane2,
+ u32 lane3)
+{
+ dp_link_write_mask(id, PCS_LANE_CONTROL, lane0, LANE0_MAP);
+ dp_link_write_mask(id, PCS_LANE_CONTROL, LANE1_MAP_VAL(lane1),
+ LANE1_MAP);
+ dp_link_write_mask(id, PCS_LANE_CONTROL, LANE2_MAP_VAL(lane2),
+ LANE2_MAP);
+ dp_link_write_mask(id, PCS_LANE_CONTROL, LANE3_MAP_VAL(lane3),
+ LANE3_MAP);
+}
+
+static void dp_reg_set_lane_map_config(u32 id)
+{
+ dp_reg_set_lane_map(id, 0, 1, 2, 3);
+}
+
+static void dp_reg_lh_p_ch_power(u32 id, u32 sst_id, u32 en)
+{
+ u32 cnt = 20 * 1000; /* wait 1ms */
+ u32 state;
+ u32 reg_offset = 0;
+ u32 val;
+
+ val = en ? ~0 : 0;
+
+ switch (sst_id) {
+ case 3:
+ reg_offset = SYSTEM_SST4_FUNCTION_ENABLE;
+ break;
+ case 2:
+ reg_offset = SYSTEM_SST3_FUNCTION_ENABLE;
+ break;
+ case 1:
+ reg_offset = SYSTEM_SST2_FUNCTION_ENABLE;
+ break;
+ case 0:
+ default:
+ reg_offset = SYSTEM_SST1_FUNCTION_ENABLE;
+ }
+
+ dp_link_write_mask(id, reg_offset, val, SST1_LH_PWR_ON);
+
+ do {
+ state = dp_link_read_mask(id, reg_offset,
+ SST1_LH_PWR_ON_STATUS);
+ cnt--;
+ udelay(1);
+ } while ((en ^ state) && cnt);
+ /*
+ * en xor state
+ * if (en), do while (!state) = en(1), state(0)
+ * else, do while(state) = en(0), state(1)
+ */
+
+ if (!(en ^ state) && !cnt) {
+ cal_log_err(id, "%s on is timeout[%d].\n", __func__, state);
+ cal_log_err(id, "SYSTEM_CLK_CONTROL[0x%08x]\n",
+ dp_link_read(id, SYSTEM_CLK_CONTROL));
+ cal_log_err(id, "SYSTEM_PLL_LOCK_CONTROL[0x%08x]\n",
+ dp_link_read(id, SYSTEM_PLL_LOCK_CONTROL));
+ cal_log_err(id, "SYSTEM_DEBUG[0x%08x]\n",
+ dp_link_read(id, SYSTEM_DEBUG));
+ cal_log_err(id, "SYSTEM_DEBUG_LH_PCH[0x%08x]\n",
+ dp_link_read(id, SYSTEM_DEBUG_LH_PCH));
+ cal_log_err(id, "SST%d_VIDEO_CONTROL[0x%08x]\n", sst_id + 1,
+ dp_link_read(id,
+ SST1_VIDEO_CONTROL + 0x1000 * sst_id));
+ cal_log_err(id, "SST%d_VIDEO_DEBUG_FSM_STATE[0x%08x]\n",
+ sst_id + 1,
+ dp_link_read(id, SST1_VIDEO_DEBUG_FSM_STATE +
+ 0x1000 * sst_id));
+ cal_log_err(id, "SST%d_VIDEO_DEBUG_MAPI[0x%08x]\n", sst_id + 1,
+ dp_link_read(id, SST1_VIDEO_DEBUG_MAPI +
+ 0x1000 * sst_id));
+ cal_log_err(id, "SYSTEM_SW_FUNCTION_ENABLE[0x%08x]\n",
+ dp_link_read(id, SYSTEM_SW_FUNCTION_ENABLE));
+ cal_log_err(id, "SYSTEM_COMMON_FUNCTION_ENABLE[0x%08x]\n",
+ dp_link_read(id, SYSTEM_COMMON_FUNCTION_ENABLE));
+ cal_log_err(id, "SYSTEM_SST%d_FUNCTION_ENABLE[0x%08x]\n",
+ sst_id + 1, dp_link_read(id, reg_offset));
+ }
+}
+
+static void dp_reg_sw_function_en(u32 id, u32 en)
+{
+ dp_link_write_mask(id, SYSTEM_SW_FUNCTION_ENABLE, en, SW_FUNC_EN);
+}
+
+int dp_reg_get_link_clock(u32 id)
+{
+ int val = 0;
+
+ val = dp_link_read_mask(id, SYSTEM_CLK_CONTROL, GFMUX_STATUS_TXCLK);
+
+ /* If PHY_PLL goes to lock and video data is transferred from DECON
+ then GFMUX_STATUS_TXCLK changes from OSC_CLK to TXCLK */
+ return val & GFMUX_STATUS_TXCLK_ON;
+}
+
+bool dp_reg_get_sst_pstate(u32 id)
+{
+ u32 val = 0;
+
+ u32 mask = SST1_LH_PSTATE | SST2_LH_PSTATE | SST3_LH_PSTATE |
+ SST4_LH_PSTATE;
+ val = dp_link_read_mask(id, SYSTEM_DEBUG_LH_PCH, mask);
+
+ if (val)
+ return true;
+ else
+ return false;
+}
+
+static u32 dp_reg_get_lh_power_status(u32 id, u32 sst_id)
+{
+ u32 val = 0;
+ u32 reg_offset = 0;
+
+ switch (sst_id) {
+ case 0:
+ reg_offset = SYSTEM_SST1_FUNCTION_ENABLE;
+ break;
+ case 1:
+ reg_offset = SYSTEM_SST2_FUNCTION_ENABLE;
+ break;
+ case 2:
+ reg_offset = SYSTEM_SST3_FUNCTION_ENABLE;
+ break;
+ case 3:
+ reg_offset = SYSTEM_SST4_FUNCTION_ENABLE;
+ break;
+ default:
+ reg_offset = SYSTEM_SST1_FUNCTION_ENABLE;
+ }
+
+ val = dp_link_read_mask(id, reg_offset, SST1_LH_PWR_ON_STATUS);
+ val |= dp_link_read_mask(id, reg_offset, SST1_VIDEO_FUNC_EN);
+
+ return val;
+}
+
+static void dp_reg_phy_init(u32 id)
+{
+ dp_reg_phy_reset(id, 1);
+ dp_reg_phy_init_setting(id);
+ dp_reg_phy_mode_setting(id);
+ dp_reg_phy_ssc_enable(id, 0);
+ dp_reg_phy_reset(id, 0);
+ dp_reg_wait_phy_pll_lock(id);
+}
+
+void dp_reg_phy_disable(u32 id)
+{
+ dp_reg_phy_reset(id, 1);
+ dp_phy_write(id, CMN_REG00A2, 0x00);
+}
+
+void dp_reg_init(u32 id)
+{
+ dp_reg_phy_init(id);
+ dp_reg_common_function_enable(id, 1);
+ dp_reg_sw_function_en(id, 1);
+ dp_reg_set_lane_map_config(id);
+}
+
+void dp_reg_deinit(u32 id)
+{
+ dp_reg_common_function_enable(id, 0);
+ dp_reg_sw_function_en(id, 0);
+}
+
+static u8 dp_reg_find_hw_bpc_value(u8 bpc)
+{
+ u8 ret;
+ switch (bpc) {
+ case 6:
+ ret = BPC_6;
+ break;
+ case 8:
+ ret = BPC_8;
+ break;
+ case 10:
+ ret = BPC_10;
+ break;
+ default:
+ ret = BPC_8;
+ break;
+ }
+
+ return ret;
+}
+
+void dp_reg_set_video_config(u32 id, struct dp_video_info dp_video_info)
+{
+ u32 sst_id = -1;
+ u8 bpc = 0;
+ u8 range = 0;
+
+ sst_id = dp_video_info.sst_id;
+ bpc = dp_reg_find_hw_bpc_value(dp_video_info.bpc);
+ range = dp_video_info.dyn_range;
+
+ dp_reg_set_daynamic_range(id, sst_id, (range) ? CEA_RANGE : VESA_RANGE);
+ dp_link_write_mask(id, SST1_VIDEO_CONTROL + 0x1000 * sst_id,
+ BPC_VAL(bpc), BPC); /* 0 : 6bits, 1 : 8bits */
+ dp_link_write_mask(id, SST1_VIDEO_CONTROL + 0x1000 * sst_id, 0,
+ COLOR_FORMAT); /* RGB */
+ dp_reg_video_format_setting(id, dp_video_info);
+ dp_reg_set_video_clock(id, sst_id, dp_video_info.vm.pixelclock);
+ /* DSC: Note that dsc_en should be called first to set active_symbol properly */
+ dp_reg_set_dsc_stream(id, sst_id, dp_video_info.dsc, 1);
+ dp_reg_set_active_symbol(id, sst_id, dp_video_info.vm.pixelclock, bpc);
+ dp_link_write_mask(id, SST1_VIDEO_MASTER_TIMING_GEN + 0x1000 * sst_id,
+ ~0, VIDEO_MASTER_TIME_GEN);
+ dp_link_write_mask(id, SST1_MAIN_CONTROL + 0x1000 * sst_id, 0,
+ VIDEO_MODE);
+}
+
+void dp_reg_set_bist_video_config(u32 id, struct dp_video_info dp_video_info,
+ u8 type)
+{
+ u32 sst_id = -1;
+ u8 bpc = 0;
+ u8 range = 0;
+
+ sst_id = dp_video_info.sst_id;
+ bpc = dp_reg_find_hw_bpc_value(dp_video_info.bpc);
+ range = dp_video_info.dyn_range;
+
+ if (type < CTS_COLOR_RAMP) {
+ dp_reg_set_video_config(id, dp_video_info);
+ dp_link_write_mask(id,
+ SST1_VIDEO_BIST_CONTROL + 0x1000 * sst_id,
+ type, BIST_TYPE);
+ dp_link_write_mask(id,
+ SST1_VIDEO_BIST_CONTROL + 0x1000 * sst_id, 0,
+ CTS_BIST_EN);
+ } else {
+ if (type == CTS_COLOR_SQUARE_CEA)
+ dp_video_info.dyn_range = CEA_RANGE;
+ else
+ dp_video_info.dyn_range = VESA_RANGE;
+ dp_reg_set_video_config(id, dp_video_info);
+
+ dp_link_write_mask(id,
+ SST1_VIDEO_BIST_CONTROL + 0x1000 * sst_id,
+ CTS_BIST_TYPE_VAL(type - CTS_COLOR_RAMP),
+ CTS_BIST_TYPE);
+ dp_link_write_mask(id,
+ SST1_VIDEO_BIST_CONTROL + 0x1000 * sst_id,
+ ~0, CTS_BIST_EN);
+ }
+
+ dp_reg_set_video_bist_mode(id, sst_id, 1);
+
+ cal_log_info(
+ id, "set bist video config res:%dx%d range:%d bpc:%d type:%d\n",
+ dp_video_info.vm.hactive, dp_video_info.vm.vactive,
+ (range) ? 1 : 0, (bpc) ? 1 : 0, type);
+}
+
+void dp_reg_start(u32 id, u32 sst_id)
+{
+ dp_reg_sst_function_enable(id, sst_id, 1);
+ dp_reg_set_sst_stream_enable(id, sst_id, 1);
+ dp_reg_set_sst_interrupt_mask(id, sst_id, VIDEO_FIFO_UNDER_FLOW_MASK,
+ 0);
+ dp_link_write_mask(id, SST1_VIDEO_ENABLE + 0x1000 * sst_id, 1,
+ VIDEO_EN);
+ dp_reg_lh_p_ch_power(id, sst_id, 1);
+}
+
+static void dp_reg_wait_vsync_interrupt_status(u32 id, u32 sst_id)
+{
+ u32 cnt = 500; /* waiting time:50ms */
+ u32 state;
+ u32 irq_status_reg = SST1_INTERRUPT_STATUS_SET0 + (0x1000 * sst_id);
+
+ do {
+ state = dp_link_read_mask(id, irq_status_reg, VSYNC_DET_MASK);
+ cnt--;
+ udelay(100);
+ } while (!state && cnt);
+
+ if (!cnt)
+ cal_log_err(id, "%s is timeout.\n", __func__);
+}
+
+void dp_reg_stop(u32 id, u32 sst_id)
+{
+ u32 offset = 0x1000 * sst_id;
+ struct dp_video_info dp_vi;
+
+ memset(&dp_vi, 0, sizeof(struct dp_video_info));
+
+ if (!dp_reg_get_lh_power_status(id, sst_id)) {
+ cal_log_info(id, "already IDLE status(sst=%d)\n", sst_id);
+ return;
+ }
+
+ dp_reg_set_sst_interrupt_mask(id, sst_id, VIDEO_FIFO_UNDER_FLOW_MASK,
+ 0);
+ dp_link_write_mask(id, SST1_VIDEO_ENABLE + offset, 0, VIDEO_EN);
+ dp_reg_get_int_and_clear(id, DP_IRQ_REG_SST1_SET0 + sst_id);
+ dp_reg_wait_vsync_interrupt_status(id, sst_id);
+ dp_link_write_mask(id, SST1_VIDEO_MASTER_TIMING_GEN + offset, 0,
+ VIDEO_MASTER_TIME_GEN);
+ dp_link_write_mask(id, SST1_VIDEO_ENABLE + offset, 0, VIDEO_EN);
+ dp_reg_lh_p_ch_power(id, sst_id, 0);
+
+ /* DSC: DSC disable should be called after checking check LH_PWR_ON_STATUS[5] = 1'b0 */
+ dp_reg_set_dsc_stream(id, sst_id, dp_vi.dsc, 0);
+
+ dp_link_write_mask(id, SST1_MAIN_FIFO_CONTROL + offset, ~0,
+ CLEAR_MAPI_FIFO);
+ dp_reg_get_int_and_clear(id, DP_IRQ_REG_SST1_SET0 + sst_id);
+ dp_reg_set_sst_stream_enable(id, sst_id, 0);
+}
+
+void dp_reg_set_mst_en(u32 id, u32 en)
+{
+ u32 val = en ? ~0 : 0;
+ dp_link_write_mask(id, MST_ENABLE, val, MST_EN);
+}
+
+void dp_reg_set_strm_x_y(u32 id, u32 sst_id, u32 x_val, u32 y_val)
+{
+ cal_log_info(id, "SST:%d, x:%d, y:%d\n", sst_id + 1, x_val, y_val);
+ dp_link_write_mask(id, MST_STREAM_X_VALUE(sst_id), x_val, STRM_VALUE);
+ dp_link_write_mask(id, MST_STREAM_Y_VALUE(sst_id), y_val, STRM_VALUE);
+}
+
+void dp_reg_set_vcpi_timeslot(u32 id, u32 sst_id, u32 start, u32 size)
+{
+ int i;
+ u32 val[8] = {0, }, shift_val;
+ int timeslot_start = start - 1;
+ int timeslot_end = timeslot_start + size;
+ int start_offset = timeslot_start / 8;
+ int end_offset = timeslot_end / 8;
+
+ if (timeslot_end <= MAX_VC_PAYLOAD_TIMESLOT) {
+ val[start_offset] =
+ dp_link_read(id, MST_VC_PAYLOAD_ID_TIMESLOT_01_08 +
+ 4 * start_offset);
+
+ for (i = timeslot_start; i < timeslot_end; i++) {
+ /* bit masking first */
+ shift_val = 28 - ((i * 4) % 32);
+ val[i / 8] &= VC_PAYLOAD_ID_MASK(shift_val);
+ /* save sst_id into timeslot bit */
+ val[i / 8] |= sst_id << shift_val;
+ }
+
+ for (i = start_offset; i <= end_offset; i++)
+ dp_link_write(id,
+ MST_VC_PAYLOAD_ID_TIMESLOT_01_08 + 4 * i,
+ val[i]);
+ } else {
+ cal_log_err(id, "Over MAX_VC_PAYLOAD_TIMESLOT\n");
+ }
+
+ for (i = start_offset; i <= end_offset; i++)
+ cal_log_info(id, "MST_VC_PAYLOAD_ID_TIMESLOT_%d = 0x%8x\n", i,
+ dp_link_read(id, MST_VC_PAYLOAD_ID_TIMESLOT_01_08 +
+ 4 * i));
+}
+
+static void dp_reg_reset_vcpi_timeslot(u32 id)
+{
+ u32 i;
+
+ for (i = MST_VC_PAYLOAD_ID_TIMESLOT_01_08;
+ i <= MST_VC_PAYLOAD_ID_TIMESLOT_57_63; i += 4)
+ dp_link_write(id, i, 0);
+}
+
+void dp_reg_remove_vcpi_timeslot(u32 id, u32 sst_id)
+{
+ int i;
+ int idx = 0;
+ int vcpi_idx[5] = {
+ 0,
+ };
+ int vcpi_num[5] = {
+ 0,
+ };
+ int vcpi_start = 0;
+ u32 tmp;
+ u32 sst_idx = sst_id + 1;
+
+ if (!dp_link_read_mask(id, MST_ENABLE, MST_EN))
+ return;
+
+ for (i = 0; i <= MAX_VC_PAYLOAD_TIMESLOT; i++) {
+ tmp = dp_link_read_mask(id,
+ MST_VC_PAYLOAD_ID_TIMESLOT_01_08 +
+ (4 * (int)(i / 8)),
+ 0x7 << (28 - (i % 8) * 4)) >>
+ (28 - (i % 8) * 4);
+ if (sst_idx != tmp)
+ vcpi_num[tmp]++;
+
+ if (i == 0)
+ vcpi_idx[idx] = tmp;
+ else if (vcpi_idx[idx] != tmp) {
+ idx++;
+ vcpi_idx[idx] = tmp;
+ }
+ }
+
+ dp_reg_reset_vcpi_timeslot(id);
+ for (i = 0; i <= MAX_SST_CNT; i++) {
+ if (i > 0)
+ vcpi_start += vcpi_num[vcpi_idx[i - 1]];
+
+ if (vcpi_num[vcpi_idx[i]] && vcpi_idx[i]) {
+ dp_reg_set_vcpi_timeslot(id, vcpi_idx[i],
+ vcpi_start + 1,
+ vcpi_num[vcpi_idx[i]]);
+ }
+ }
+}
+
+int dp_reg_wait_for_vcpi_update(u32 id)
+{
+ u32 cnt = 3000;
+ u32 val;
+
+ dp_link_write(id, MST_VC_PAYLOAD_UPDATE_FLAG, VC_PAYLOAD_UPDATE_FLAG);
+
+ do {
+ val = dp_link_read(id, MST_VC_PAYLOAD_UPDATE_FLAG) &
+ VC_PAYLOAD_UPDATE_FLAG;
+ cnt--;
+ udelay(10);
+ } while (val && cnt);
+
+ if (!cnt) {
+ cal_log_err(id, "MST_VC_PAYLOAD_UPDATE_FLAG wait timeout.\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+void dp_reg_set_mst_always_sent_act(u32 id, u32 en)
+{
+ u32 val;
+
+ val = en ? ~0 : 0;
+ /*
+ * MST_ETC_OPTION : 0x2070 : ALLOCATE_TIMESLOT_CHECK_TO_ACT[1]
+ * L : if VC_PAYLOAD Table is not updated, ACT is not sent.
+ * H : 'Always sent ACT' after ECF change
+ */
+ dp_link_write_mask(id, MST_ETC_OPTION, val,
+ ALLOCATE_TIMESLOT_CHECK_TO_ACT);
+}
+
+u32 dp_reg_read_vcpi_timeslot(u32 id, u32 index)
+{
+ if (index >= NUM_VC_PAYLOAD_SLOT)
+ index = NUM_VC_PAYLOAD_SLOT - 1;
+
+ return dp_link_read(id, MST_VC_PAYLOAD_ID_TIMESLOT_01_08 + 4 * index);
+}
+
+
+#define DEV_NAME "exynos_dp"
+#define DEFAULT_BPC 8 /* DP supports at least 8 bpc */
+
+struct dp_dev_data {
+ const enum chip_version version;
+};
+
+// static void __exynos910_drm_dp_dump_sfr(struct exynos_dp_subdev *subdev)
+// {
+// struct device *dev = subdev->dev;
+// struct dp_regs *regs = &subdev->regs;
+// int i;
+
+// if (regs->link_addr) {
+// print_dump_info("DP_LINK BASE(%s) +", dev_name(dev));
+// print_hexdump(regs->link_addr, 0x210);
+// print_hexdump(regs->link_addr + 0x1000, 0x60);
+// print_hexdump(regs->link_addr + 0x1100, 0x20);
+// print_hexdump(regs->link_addr + 0x2000, 0x80);
+// print_hexdump(regs->link_addr + 0x3000, 0x120);
+// /* TODO: Should be added SSTx SFR */
+// print_dump_info("DP_LINK BASE(%s) -", dev_name(dev));
+// for (i = 0; i < DP_SST_MAX; i++) {
+// struct exynos_dp_video_info *vi = &subdev->vi[i];
+// print_dump_info("DP_LINK SST%d +", i + 1);
+// print_hexdump(regs->link_addr + 0x5000 + (0x1000 * i), 0x108);
+// print_hexdump(regs->link_addr + 0x5400 + (0x1000 * i), 0x110);
+// if (vi->dsc.enable) {
+// print_hexdump(regs->link_addr + 0x5C20 + (0x1000 * i), 0x4);
+// print_hexdump(regs->link_addr + 0x5D00 + (0x1000 * i), 0x80);
+// }
+
+// print_dump_info("DP_LINK SST%d -", i + 1);
+// }
+// }
+
+// if (regs->phy_addr) {
+// print_dump_info("DP__PHY BASE(%s) +", dev_name(dev));
+// print_hexdump(regs->phy_addr, 0x428);
+// print_hexdump(regs->phy_addr + 0x800, 0x4D4);
+// print_hexdump(regs->phy_addr + 0x1000, 0xD0);
+// print_hexdump(regs->phy_addr + 0x10D4, 0x4);
+// print_hexdump(regs->phy_addr + 0x1800, 0x4D4);
+// print_hexdump(regs->phy_addr + 0x2000, 0xD0);
+// print_hexdump(regs->phy_addr + 0x20D4, 0x4);
+// print_dump_info("DP__PHY BASE(%s) -", dev_name(dev));
+// }
+// }
+
+int exynos_drm_dp_dump_sfr(struct exynos_dp_subdev *subdev)
+{
+ int acquired;
+
+ if (!subdev || !subdev->dev)
+ return -ENXIO;
+
+ if (subdev->state == DP_STATE_OFF)
+ return -EBUSY;
+
+ acquired = console_trylock();
+
+ if (subdev->version == V910)
+ // __exynos910_drm_dp_dump_sfr(subdev);
+
+ if (acquired)
+ console_unlock();
+
+ return 0;
+}
+
+void exynos_drm_dp_dpcd_status_dump(struct exynos_dp_subdev *dp)
+{
+ u8 max_lane;
+ u8 link_lane = dp->lt_info.lane_cnt;
+ int max_rate;
+ int link_rate = drm_dp_bw_code_to_link_rate(dp->lt_info.link_rate);
+ struct device *dev = dp->dev;
+ bool enhance, tps3, tps4, dsc;
+ u8 info[DP_LINK_STATUS_SIZE];
+
+ max_rate = drm_dp_max_link_rate(dp->dpcd);
+ max_lane = drm_dp_max_lane_count(dp->dpcd);
+ enhance = drm_dp_enhanced_frame_cap(dp->dpcd);
+ tps3 = drm_dp_tps3_supported(dp->dpcd);
+ tps4 = drm_dp_tps4_supported(dp->dpcd);
+ dsc = drm_dp_sink_supports_dsc(dp->dsc_dpcd);
+
+ drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, info, DP_LINK_STATUS_SIZE);
+ dp_log_info(dev, "======= DP%d DPCD Link/Sink Dump =======\n", dp->id);
+ dp_log_info(dev, "Link:: RATE(%d) LANE(%d) ENHANCED(%d)\n",
+ max_rate, max_lane, enhance);
+ dp_log_info(dev, " TPS3(%d) TPS4(%d)\n",
+ tps3, tps4);
+ dp_log_info(dev, "SINK:: RATE(%d) LANE(%d)\n",
+ link_rate, link_lane);
+ dp_log_info(dev, "DPCD(0x200~205) %*ph\n", DP_LINK_STATUS_SIZE, info);
+
+ if (dsc) {
+ drm_dp_dpcd_readb(&dp->aux, 0x20F, &info[0]);
+ dp_log_info(dev, "DSC_STATUS(0x20f):: %#2x\n", info[0]);
+
+ drm_dp_dpcd_read(&dp->aux, DP_FEC_STATUS, info,
+ DP_FEC_ERROR_COUNT_MSB - DP_FEC_STATUS);
+ dp_log_info(dev, "FEC_STATUS(0x280~281):: %#2x, %#2x\n",
+ info[0], info[1]);
+ }
+ dp_log_info(dev, "======= DP%d =======\n", dp->id);
+}
+
+static bool exynos_drm_dp_check_dpcd_status(struct exynos_dp_subdev *dp)
+{
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ int link_rate = drm_dp_bw_code_to_link_rate(dp->lt_info.link_rate);
+ u8 link_lane = dp->lt_info.lane_cnt;
+ bool status_err = false;
+
+ drm_dp_dpcd_read_link_status(&dp->aux, link_status);
+
+ if (!drm_dp_clock_recovery_ok(link_status, link_lane)) {
+ dp_log_err(dp->dev, "failed w/ LANE_CR_DONE(0x202~0x203)\n");
+ status_err = true;
+ }
+
+ if (!drm_dp_channel_eq_ok(link_status, link_lane)) {
+ dp_log_err(dp->dev, "failed w/ INTERLANE_ALIGN_DONE(0x204)\n");
+ status_err = true;
+ }
+
+ if (link_rate > drm_dp_max_link_rate(dp->dpcd)) {
+ dp_log_err(dp->dev, "failed w/ MAX_LINK_RATE\n");
+ status_err = true;
+ }
+
+ if (link_lane > drm_dp_max_lane_count(dp->dpcd)) {
+ dp_log_err(dp->dev, "failed w/ MAX_LINK_LANE\n");
+ status_err = true;
+ }
+
+ if (status_err)
+ exynos_drm_dp_dpcd_status_dump(dp);
+
+ if (dp->dp_debug.debug_hpd_irq)
+ return true;
+
+ return status_err;
+}
+
+void exynos_drm_dp_set_normal_data(struct exynos_dp_subdev *dp)
+{
+ dp_reg_set_training_pattern(dp->id, NORAMAL_DATA);
+ dp_reg_scrambling_enable(dp->id, 1);
+}
+
+static void exynos_drm_dp_hpd_changed(struct exynos_dp_subdev *dp, int state)
+{
+ struct device *dev = dp->dev;
+ int ret;
+
+ if (dp->hpd_state == state) {
+ dp_log_info(dev, "hpd same state skip %x\n", state);
+ return;
+ }
+
+ if (dp->hpd_state == HPD_CHECK) {
+ dp_log_info(dev, "hpd is being checked\n");
+ return;
+ }
+
+ dp_log_info(dev, "hpd state cur:%d -> new:%d\n", dp->hpd_state, state);
+
+ disable_irq(dp->hpd_gpio_irq);
+ if (state) {
+ dp->hpd_state = HPD_CHECK;
+ if (!dp->training_state) {
+ ret = exynos_drm_dp_link_training(dp);
+ if (ret < 0)
+ goto HPD_FAIL;
+ }
+ dp->training_state = true;
+ dp->hpd_state = HPD_PLUG;
+
+ if (state == HPD_PLUG && dp->detect)
+ dp->detect(dev);
+ } else {
+ dp->hpd_state = HPD_UNPLUG;
+ dp->training_state = false;
+
+ if (dp->detect)
+ dp->detect(dev);
+ }
+
+ dp_log_info(dp->dev, "hpd state goto %s\n", state ? "plug" : "unplug");
+ enable_irq(dp->hpd_gpio_irq);
+ return;
+
+HPD_FAIL:
+ dp_log_err(dev, "link training is failed\n");
+ dp->training_state = false;
+ dp->hpd_state = HPD_LT_FAILED;
+
+ enable_irq(dp->hpd_gpio_irq);
+ return;
+}
+
+static irqreturn_t exynos_drm_dp_irq(int irq, void *dev_id)
+{
+ struct exynos_dp_subdev *dp = dev_id;
+ struct device *dev = dp->dev;
+ u32 irq_type = 0, val;
+ int i = 0;
+ bool valid_gpio = gpio_is_valid(dp->hpd_gpio);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dp->slock, flags);
+
+ /* common interrupt */
+ irq_type = dp_reg_get_int_and_clear(dp->id, DP_IRQ_REG_SYSTEM);
+
+ /* The priority for PLUG and UNPLUG behavior is HPD_GPIO than DPTX_IRQ.*/
+ if (!valid_gpio) {
+ if (irq_type & DP_IRQ_HPD_CHG)
+ dp_log_dbg(dev, "HPD_CHG detect\n");
+
+ if (irq_type & DP_IRQ_HPD_PLUG_INT) {
+ queue_delayed_work(dp->dp_wq, &dp->hpd_plug_work, 0);
+ dp_log_info(dev, "HPD_PLUG detect\n");
+ }
+ } else {
+ if (irq_type & DP_IRQ_HPD_LOST) {
+ queue_delayed_work(dp->dp_wq, &dp->hpd_unplug_work, 0);
+ dp_log_info(dev, "HPD_LOST detect\n");
+ }
+
+ if (irq_type & DP_IRQ_HPD_IRQ_FLAG) {
+ queue_delayed_work(dp->dp_wq, &dp->hpd_irq_work, 0);
+ dp_log_info(dev, "HPD_IRQ detect\n");
+ }
+
+ if (irq_type & DP_IRQ_HPD_PLUG_INT) {
+ queue_delayed_work(dp->dp_wq, &dp->hpd_plug_work, 0);
+ dp_log_info(dev, "HPD_PLUG detect\n");
+ }
+ }
+
+ if (irq_type)
+ dp_log_dbg(dev, "DP_IRQ_REG_SYSTEM=%#x\n", irq_type);
+
+ /* SST1~4 interrupt */
+ for (i = 0; i < MAX_SST_CNT; i++) {
+ val = dp_reg_get_int_and_clear(dp->id, DP_IRQ_REG_SST1_SET0 + i);
+
+ if ((val & DP_IRQ_MAPI_FIFO_UNDER_FLOW) && !irq_type)
+ dp_log_errl(dev, "SST%d FIFO_UNDER_FLOW=%#x\n",
+ i + 1, val);
+ }
+
+ spin_unlock_irqrestore(&dp->slock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t exynos_drm_dp_hpd_gpio_irq_thread(int irq, void *dev_id)
+{
+ struct exynos_dp_subdev *dp = dev_id;
+ struct device *dev = dp->dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dp->slock, flags);
+
+ if ((gpio_get_value(dp->hpd_gpio) == HPD_PLUG) &&
+ dp->hpd_state == HPD_UNPLUG) {
+ queue_delayed_work(dp->dp_wq, &dp->hpd_plug_work, 0);
+ dp_log_info(dev, "GPIO_HPD_PLUG detect\n");
+ }
+ spin_unlock_irqrestore(&dp->slock, flags);
+
+ return IRQ_HANDLED;
+}
+
+void exynos_drm_dp_hpd_en(struct exynos_dp_subdev *dp)
+{
+ if (dp_reg_get_hpd_status(dp->id) && !dp->training_state)
+ exynos_drm_dp_hpd_changed(dp, HPD_CHECK);
+}
+
+static void exynos_drm_dp_hpd_plug_work(struct work_struct *work)
+{
+ struct delayed_work *delay_work =
+ container_of(work, struct delayed_work, work);
+ struct exynos_dp_subdev *dp =
+ container_of(delay_work, struct exynos_dp_subdev, hpd_plug_work);
+
+ flush_delayed_work(&dp->hpd_unplug_work);
+ exynos_drm_dp_hpd_changed(dp, HPD_PLUG);
+}
+
+static void exynos_drm_dp_hpd_unplug_work(struct work_struct *work)
+{
+ struct delayed_work *delay_work =
+ container_of(work, struct delayed_work, work);
+ struct exynos_dp_subdev *dp =
+ container_of(delay_work, struct exynos_dp_subdev, hpd_unplug_work);
+
+ if (!dp->hpd_state) {
+ dp_log_err(dp->dev, "HPD is low(%d)\n", dp->hpd_state);
+ return;
+ }
+
+ exynos_drm_dp_hpd_changed(dp, HPD_UNPLUG);
+}
+
+static void exynos_drm_dp_hpd_irq_work(struct work_struct *work)
+{
+ struct delayed_work *delay_work =
+ container_of(work, struct delayed_work, work);
+ struct exynos_dp_subdev *dp =
+ container_of(delay_work, struct exynos_dp_subdev, hpd_irq_work);
+
+ flush_delayed_work(&dp->hpd_unplug_work);
+
+ if (dp->state == DP_STATE_OFF || dp->hpd_state == HPD_UNPLUG)
+ return;
+
+ if (exynos_drm_dp_check_dpcd_status(dp)) {
+ dp_log_info(dp->dev, "link training in HPD_IRQ work\n");
+ if (exynos_drm_dp_link_training(dp) < 0) {
+ dp_log_err(dp->dev, "failed link_training in HPD_IRQ work\n");
+ dp->hpd_state = HPD_IRQ;
+ dp->training_state = false;
+ if (dp->detect)
+ dp->detect(dp->dev);
+ dp_reg_set_plug_interrupt(dp->id, 1);
+ return;
+ }
+ if (dp->hpd_state == HPD_IRQ) {
+ dp->hpd_state = HPD_PLUG;
+ dp->training_state = true;
+ if (dp->detect)
+ dp->detect(dp->dev);
+ return;
+ }
+ if (dp_reg_get_sst_pstate(dp->id))
+ exynos_drm_dp_set_normal_data(dp);
+
+ if (dp->detect)
+ dp->detect(dp->dev);
+ }
+
+ if (dp->mst_irq)
+ dp->mst_irq(dp->dev);
+}
+
+static int exynos_drm_dp_init_resources(struct exynos_dp_subdev *dp, struct platform_device *pdev)
+{
+ struct resource *res;
+ struct device *dev = dp->dev;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "link_base");
+ if (res == NULL) {
+ dp_log_err(dev, "failed to find dp link memory resource for dp\n");
+ return -EINVAL;
+ }
+ dp->regs.link_addr = devm_ioremap_resource(dp->dev, res);
+
+ if (IS_ERR(dp->regs.link_addr)) {
+ dp_log_err(dev, "failed to remap io region\n");
+ return PTR_ERR(dp->regs.link_addr);
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_base");
+ if (res == NULL) {
+ dp_log_err(dev, "failed to find dp phy memory resource for dp\n");
+ return -EINVAL;
+ }
+ dp->regs.phy_addr = devm_ioremap_resource(dp->dev, res);
+
+ if (IS_ERR(dp->regs.phy_addr)) {
+ dp_log_err(dev, "failed to remap io region\n");
+ return PTR_ERR(dp->regs.phy_addr);
+ }
+
+ dp_regs_desc_init(dp->id, &dp->regs);
+
+ return 0;
+}
+
+static ssize_t exynos_drm_dp_show_sfr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_drm_dp *dp = dev_get_drvdata(dev);
+
+ exynos_drm_dp_dump_sfr(dp->subdev);
+
+ return 0;
+}
+
+static DEVICE_ATTR(dump_sfr, S_IRUGO, exynos_drm_dp_show_sfr, NULL);
+
+static ssize_t exynos_drm_dp_show_link_lane(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+ int rc;
+
+ rc = sprintf(buf, "%d\n", dp->lt_info.max_link_lane);
+
+ return rc;
+}
+
+static ssize_t exynos_drm_dp_store_link_lane(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+ int rc;
+
+ rc = kstrtou8(buf, 0, &dp->lt_info.max_link_lane);
+ if (rc < 0)
+ return rc;
+
+ return len;
+}
+
+static DEVICE_ATTR(link_lane, 0644,
+ exynos_drm_dp_show_link_lane, exynos_drm_dp_store_link_lane);
+
+static ssize_t exynos_drm_dp_show_hpd_irq(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+ int np;
+
+ np = sprintf(buf, "debug_hpd_irq = %d\n", dp->dp_debug.debug_hpd_irq);
+ np += sprintf(buf + np, "0: normal_op\n");
+ np += sprintf(buf + np, "1: forced occur HPD_IRQ with Link_training\n");
+ return np;
+}
+
+static ssize_t exynos_drm_dp_store_hpd_irq(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+
+ if (dp->hpd_state == HPD_UNPLUG) {
+ dp_log_err(dp->dev, "hpd state unplug\n");
+ return -EACCES;
+ }
+
+ kstrtobool(buf, &dp->dp_debug.debug_hpd_irq);
+
+ queue_delayed_work(dp->dp_wq, &dp->hpd_irq_work, 0);
+
+ return len;
+}
+
+static DEVICE_ATTR(debug_hpd_irq, 0644,
+ exynos_drm_dp_show_hpd_irq, exynos_drm_dp_store_hpd_irq);
+
+static ssize_t exynos_drm_dp_show_set_lt(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+ int np;
+
+ np = sprintf(buf, "debug_lt = %d\n", dp->dp_debug.debug_lt);
+ np += sprintf(buf + np, "0: normal_op\n");
+ np += sprintf(buf + np, "1: link_training - dpcd_read fail\n");
+ np += sprintf(buf + np, "2: link_training - EQ and CR fail, fixed BW by DP_Rx\n");
+ np += sprintf(buf + np, "3: link_training - EQ and CR fail, Try Step Down BW\n");
+ np += sprintf(buf + np, "4: link_training - Start Bandwidth one-step Lower from DP_Rx\n");
+ np += sprintf(buf + np, "\t\t (eq : DP_Rx(DPCD = 8.1Gbps) -> Start BW 5.4Gbps)\n");
+ np += sprintf(buf + np, "5: link_training - No Stepdown BW from DP_Rx\n");
+ return np;
+}
+
+static ssize_t exynos_drm_dp_store_set_lt(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+
+ if (dp->hpd_state == HPD_UNPLUG) {
+ dp_log_err(dp->dev, "hpd state unplug\n");
+ return -EACCES;
+ }
+
+ kstrtou32(buf, 0, &dp->dp_debug.debug_lt);
+
+ return len;
+}
+
+static DEVICE_ATTR(debug_lt, 0644,
+ exynos_drm_dp_show_set_lt, exynos_drm_dp_store_set_lt);
+
+static ssize_t sysfs_show_bist_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+
+ return sprintf(buf, "%#x(0x01.Color 0x11.Gray 0x21.Moving 0x31.PRBS)\n",
+ dp->bist_use);
+}
+
+static int bist_mode_set_config(struct exynos_drm_dp *drm_dp)
+{
+ struct drm_connector *connector = drm_dp->connector;
+ const struct drm_connector_helper_funcs *funcs
+ = connector->helper_private;
+ struct drm_display_mode *mode;
+ struct list_head *modes;
+ struct exynos_dp_video_info vi;
+
+ mutex_lock(&connector->dev->mode_config.mutex);
+
+ /* Get a list_head with modes */
+ modes = list_empty(&connector->probed_modes) ?
+ &connector->modes : &connector->probed_modes;
+ if (list_empty(modes)) {
+ /* Get a probed_modes via get_modes */
+ if (funcs && funcs->get_modes)
+ funcs->get_modes(connector);
+ modes = &connector->probed_modes;
+ }
+
+ list_for_each_entry(mode, modes, head)
+ if (mode->type & DRM_MODE_TYPE_PREFERRED)
+ break;
+
+ mutex_unlock(&connector->dev->mode_config.mutex);
+
+ exynos_drm_dp_to_videoinfo(drm_dp->encoder, mode, &vi);
+
+ exynos_drm_dp_videomode_set(drm_dp->subdev, &vi, DP_SST_1);
+
+ return 0;
+}
+
+static ssize_t sysfs_store_bist_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *dp = drm_dp->subdev;
+ u8 used = dp->bist_use;
+ int rc;
+
+ rc = kstrtou32(buf, 0, &dp->bist_use);
+ if (rc < 0)
+ return rc;
+
+ /* already disabled */
+ if (!used && !dp->bist_use)
+ return len;
+
+ exynos_drm_dp_hpd_en(dp);
+ if (dp->hpd_state == HPD_UNPLUG) {
+ dp_log_err(dp->dev, "hpd state unplug\n");
+ return -EACCES;
+ }
+
+ /* Disable used sst setting */
+ if (used)
+ exynos_drm_dp_stream_disable(dp, DP_SST_1);
+
+ dp->bist_type = dp->bist_use >> DP_SST_4;
+
+ if (dp->bist_use) {
+ if (bist_mode_set_config(drm_dp) < 0)
+ return -EINVAL;
+
+ exynos_drm_dp_stream_enable(dp, DP_SST_1);
+ }
+
+ dp_log_info(dp->dev, "DP_SST_1 %s w/ bist_type(%#x)\n",
+ dp->bist_use ? "on" : "off", dp->bist_type);
+ return len;
+}
+
+static DEVICE_ATTR(bist_mode, 0644,
+ sysfs_show_bist_mode, sysfs_store_bist_mode);
+
+/*** Exposed functions for exynos_drm_dp ***/
+static int exynos_drm_dp_subdev_probe(u32 id, struct device *dev,
+ struct drm_device *drm_dev, struct exynos_dp_subdev **subdev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct exynos_dp_subdev *dp = NULL;
+ const struct dp_dev_data *match;
+ int ret = 0;
+
+ dp = devm_kzalloc(dev, sizeof(struct exynos_dp_subdev), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ match = of_device_get_match_data(dev);
+ if (!match) {
+ dp_log_err(dev, "Failed to get match data\n");
+ ret = -ENODEV;
+ goto err_devm_alloc;
+ }
+ dp->version = match->version;
+
+ dp->dev = &pdev->dev;
+ dp->drm_dev = drm_dev;
+ *subdev = dp;
+ dp->id = id;
+ dp->state = DP_STATE_OFF;
+ dp->training_state = false;
+
+ ret = exynos_drm_dp_init_resources(dp, pdev);
+ if (ret)
+ goto err_devm_alloc;
+
+ dp->phy = devm_phy_get(dp->dev, "dp_phy");
+ if (IS_ERR(dp->phy)) {
+ dp_log_err(dev, "failed to get dp phy\n");
+ dp->phy = NULL;
+ ret = -EPROBE_DEFER;
+ goto err_devm_alloc;
+ }
+
+ spin_lock_init(&dp->slock);
+
+ dp->hpd_gpio = of_get_named_gpio(dev->of_node, "samsung,hpd-gpio", 0);
+
+ if (gpio_is_valid(dp->hpd_gpio)) {
+ ret = devm_gpio_request_one(dp->dev, dp->hpd_gpio, GPIOF_IN, "hpd_gpio");
+ if (ret) {
+ dp_log_err(dev, "failed to get HPD_GPIO\n");
+ goto err_devm_alloc;
+ }
+
+ dp->hpd_gpio_irq = gpio_to_irq(dp->hpd_gpio);
+
+ dp_log_dbg(dev, "GPIO_HPD has been registered. GPIO(%d), irq(%d)\n",
+ dp->hpd_gpio, dp->hpd_gpio_irq);
+
+ irq_set_status_flags(dp->hpd_gpio_irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(dp->dev, dp->hpd_gpio_irq,
+ NULL, exynos_drm_dp_hpd_gpio_irq_thread,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(dp->dev), dp);
+ if (ret) {
+ dp_log_err(dev, "failed to request dp hpd_gpio_irq\n");
+ goto err_devm_alloc;
+ }
+ }
+
+ dp->irq = platform_get_irq(pdev, 0);
+ if (dp->irq < 0) {
+ dp_log_err(dev, "failed to request dp irq resource\n");
+ ret = dp->irq;
+ goto err_devm_alloc;
+ }
+
+ irq_set_status_flags(dp->irq, IRQ_NOAUTOEN);
+ ret = devm_request_threaded_irq(dp->dev, dp->irq, NULL,
+ exynos_drm_dp_irq, IRQF_ONESHOT,
+ dev_name(dp->dev), dp);
+ if (ret) {
+ dp_log_err(dev, "failed to request dp irq\n");
+ goto err_devm_alloc;
+ }
+
+ dp->dp_wq = create_singlethread_workqueue(dev_name(&pdev->dev));
+ if (!dp->dp_wq) {
+ dp_log_err(dev, "create dp_wq failed.\n");
+ goto err_devm_alloc;
+ }
+
+ device_create_file(dev, &dev_attr_dump_sfr);
+ device_create_file(dev, &dev_attr_link_lane);
+ device_create_file(dev, &dev_attr_bist_mode);
+ device_create_file(dev, &dev_attr_debug_hpd_irq);
+ device_create_file(dev, &dev_attr_debug_lt);
+ dp->lt_info.max_link_lane = MAX_LANE_CNT;
+ dp->dp_debug.debug_hpd_irq = 0;
+ dp->dp_debug.debug_lt = DEBUG_LT_NORMAL;
+
+ INIT_DELAYED_WORK(&dp->hpd_plug_work, exynos_drm_dp_hpd_plug_work);
+ INIT_DELAYED_WORK(&dp->hpd_unplug_work, exynos_drm_dp_hpd_unplug_work);
+ INIT_DELAYED_WORK(&dp->hpd_irq_work, exynos_drm_dp_hpd_irq_work);
+ INIT_LIST_HEAD(&dp->mst_list);
+ mutex_init(&dp->lock);
+ mutex_init(&dp->pwlock);
+
+ dp_log_info(dev, "is successfully\n");
+ return ret;
+
+err_devm_alloc:
+ dp_log_err(dev, "Failed to alloc err(%d)\n", ret);
+ devm_kfree(dev, dp);
+ return ret;
+}
+
+/* H/W goes to stop or reset state when OS restarting during h/w operation */
+static void exynos_drm_dp_reset(struct exynos_dp_subdev *dp)
+{
+ int i;
+
+ for (i = DP_SST_1; i <= DP_SST_MAX; i++)
+ dp_reg_stop(dp->id, i);
+}
+
+static void exynos_drm_dp_mst_pre_disable(struct exynos_dp_subdev *dp,
+ dp_sst_idx_t sst_idx)
+{
+ struct device *dev = dp->dev;
+ struct dp_mst_config *cfg, *tmp_cfg;
+ u32 start_slot = MST_SLOT_INIT_NUM, del_num_slot;
+ bool find = false;
+ bool hpd_en = (dp->hpd_state == HPD_UNPLUG) ? false : true;
+
+ if (list_empty(&dp->mst_list))
+ return;
+
+ /* 1. find list that has sst_idx and rewrite for new timeslot */
+ list_for_each_entry_safe(cfg, tmp_cfg, &dp->mst_list, list) {
+ if (cfg->sst_id == sst_idx) {
+ del_num_slot = cfg->num_slots;
+ list_del(&cfg->list);
+ kfree(cfg);
+ find = true;
+ continue;
+ }
+ if (find && hpd_en)
+ dp_reg_set_vcpi_timeslot(dp->id, cfg->sst_id,
+ start_slot, cfg->num_slots);
+ start_slot += cfg->num_slots;
+ }
+
+ if (!hpd_en)
+ return;
+
+ /* 2. delete of vc timeslot */
+ if (find)
+ dp_reg_set_vcpi_timeslot(dp->id, 0, start_slot, del_num_slot);
+ else
+ dp_log_err(dev, "failed to delete payload of SST%d\n", sst_idx);
+
+ /* 3. wait for generating ACT sequence */
+ dp_reg_wait_for_vcpi_update(dp->id);
+}
+
+static void exynos_drm_dp_dt_to_dp_video_info(struct exynos_dp_video_info *vi,
+ struct dp_video_info *dp_video_info)
+{
+ struct videomode *vm = &vi->vm;
+
+ dp_video_info->vm.pixelclock = vm->pixelclock;
+ dp_video_info->vm.hfront_porch = vm->hfront_porch;
+ dp_video_info->vm.hactive = vm->hactive;
+ dp_video_info->vm.hback_porch = vm->hback_porch;
+ dp_video_info->vm.hsync_len = vm->hsync_len;
+
+ dp_video_info->vm.vfront_porch = vm->vfront_porch;
+ dp_video_info->vm.vactive = vm->vactive;
+ dp_video_info->vm.vback_porch = vm->vback_porch;
+ dp_video_info->vm.vsync_len = vm->vsync_len;
+
+ dp_video_info->vm.hsync_pol = vi->hsync_pol;
+ dp_video_info->vm.vsync_pol = vi->vsync_pol;
+
+ dp_video_info->bpc = vi->bpc;
+ dp_video_info->sst_id = vi->sst_id;
+ dp_video_info->dyn_range = vi->dyn_range;
+}
+
+/* DSC */
+static void exynos_drm_dp_dsc_set(struct exynos_dp_subdev *dp,
+ struct exynos_dp_video_info *vi,
+ struct dp_video_info *dp_vi)
+{
+ /* DSC */
+ if (!drm_dp_sink_supports_dsc(dp->dsc_dpcd)) {
+ dp_vi->dsc.enable = false;
+ return;
+ }
+
+ dp_vi->dsc.enable = vi->dsc.enable;
+ if (vi->dsc.enable) {
+ dp_vi->dsc.slice_count = vi->dsc.slice_count;
+ dp_vi->dsc.chunk_size = vi->dsc.chunk_size;
+ memcpy(dp_vi->dsc.pps, &vi->dsc.pps, sizeof(dp_vi->dsc.pps));
+ drm_dp_dpcd_writeb(&dp->aux, DP_DSC_ENABLE,
+ DP_DECOMPRESSION_EN);
+ }
+}
+
+static void exynos_drm_dp_dsc_unset(struct exynos_dp_subdev *dp,
+ struct exynos_dp_video_info *vi)
+{
+ vi->dsc.enable = false;
+ /* consider DSC mode switching without HPD unplug */
+ if (drm_dp_sink_supports_dsc(dp->dsc_dpcd))
+ drm_dp_dpcd_writeb(&dp->aux, DP_DSC_ENABLE, 0);
+}
+
+void exynos_drm_dp_stream_enable(struct exynos_dp_subdev *dp, dp_sst_idx_t sst_idx)
+{
+ struct device *dev = dp->dev;
+ struct exynos_dp_video_info *vi;
+ struct dp_video_info dp_video_info;
+
+ if (!dp->hpd_state)
+ dp_log_err(dev, "HPD is low(%d)\n", dp->hpd_state);
+
+ /* Set default sst_idx */
+ if (sst_idx == DP_SST_UNKNOWN) {
+ dp_log_err(dp->dev, "sst idx is unknown\n");
+ return;
+ }
+
+ vi = &dp->vi[sst_idx - 1];
+
+ exynos_drm_dp_dt_to_dp_video_info(vi, &dp_video_info);
+
+ dp_log_info(dev, "SST%d cur_video = %dx%d-%dHz(%dbpc) in displayport_enable!!!\n",
+ vi->sst_id + 1, vi->vm.hactive, vi->vm.vactive, vi->vrefresh, vi->bpc);
+
+ exynos_drm_dp_dsc_set(dp, vi, &dp_video_info);
+
+ if (dp->bist_use)
+ dp_reg_set_bist_video_config(dp->id, dp_video_info, dp->bist_type);
+ else
+ dp_reg_set_video_config(dp->id, dp_video_info);
+
+ dp_reg_start(dp->id, vi->sst_id);
+
+ exynos_drm_dp_set_normal_data(dp);
+}
+
+void exynos_drm_dp_stream_disable(struct exynos_dp_subdev *dp, dp_sst_idx_t sst_idx)
+{
+ struct device *dev = dp->dev;
+ struct exynos_dp_video_info *vi;
+
+ dp_log_info(dev, "+, state: %d, SST%d\n", dp->state, sst_idx);
+
+ /* Set default sst_idx */
+ if (sst_idx == DP_SST_UNKNOWN) {
+ dp_log_err(dp->dev, "sst idx is unknown\n");
+ return;
+ }
+
+ vi = &dp->vi[sst_idx - 1];
+
+ exynos_drm_dp_mst_pre_disable(dp, sst_idx);
+
+ dp_reg_stop(dp->id, vi->sst_id);
+ exynos_drm_dp_dsc_unset(dp, vi);
+
+ dp_log_info(dev, "-, state: %d, SST%d\n", dp->state, sst_idx);
+}
+
+/**
+ * @cnotice
+ * @prdcode
+ * @unit_name{Exynos_dp_drv}
+ * @purpose Trigering of DP work
+ * @logic Operation the DP according to the DP and HPD status
+ * @params
+ * @param{in, dp, struct* ::exynos_dp_subdev, None}
+ * @endparam
+ * @retval{dp->hpd_state, bool, None, [false:true], None}
+ */
+bool exynos_drm_dp_is_hpd_connected(struct exynos_dp_subdev *dp)
+{
+ int old_hpd = dp->hpd_state;
+
+ mutex_lock(&dp->lock);
+ if (gpio_get_value(dp->hpd_gpio)) {
+ mutex_unlock(&dp->lock);
+ if (dp->state == DP_STATE_LINKED)
+ exynos_drm_dp_hpd_en(dp);
+ } else {
+ mutex_unlock(&dp->lock);
+ dp->hpd_state = HPD_UNPLUG;
+ }
+
+ if (old_hpd != dp->hpd_state)
+ dp_log_info(dp->dev, "DP%d hpd = %d\n", dp->id, dp->hpd_state);
+
+ return dp->hpd_state & HPD_PLUG;
+}
+
+void exynos_drm_dp_videomode_set(struct exynos_dp_subdev *dp,
+ struct exynos_dp_video_info *vi, dp_sst_idx_t sst_idx)
+{
+ struct exynos_dp_video_info *cur_vi;
+
+ /* Set default sst_idx */
+ if (sst_idx == DP_SST_UNKNOWN) {
+ dp_log_err(dp->dev, "sst idx is unknown\n");
+ return;
+ }
+
+ cur_vi = &dp->vi[sst_idx - 1];
+
+ cur_vi->sst_id = sst_idx - 1;
+ cur_vi->dyn_range = vi->dyn_range;
+ cur_vi->bpc = vi->bpc;
+ cur_vi->vrefresh = vi->vrefresh;
+ cur_vi->hsync_pol = vi->hsync_pol;
+ cur_vi->vsync_pol = vi->vsync_pol;
+ memcpy(&cur_vi->vm, &vi->vm, sizeof(struct videomode));
+
+ /* DSC */
+ cur_vi->dsc.enable = vi->dsc.enable;
+ if (vi->dsc.enable) {
+ cur_vi->dsc.slice_count = vi->dsc.slice_count;
+ cur_vi->dsc.chunk_size = vi->dsc.chunk_size;
+ memcpy(&cur_vi->dsc.pps, &vi->dsc.pps, sizeof(vi->dsc.pps));
+ }
+}
+
+static int exynos_drm_dp_start(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ int ret = 0;
+
+ dp_log_info(dev, "+, state: %d\n", dp->state);
+ if (gpio_get_value(dp->hpd_gpio) == HPD_UNPLUG) {
+ dp_log_err(dev, "DP(%d) is unplug\n", dp->id);
+ return 0;
+ }
+
+ if (dp->state == DP_STATE_LINKED) {
+ dp_log_info(dev, "DP(%d), state : %d\n", dp->id, dp->state);
+ return 0;
+ }
+ mutex_lock(&dp->pwlock);
+
+ ret = phy_power_on(dp->phy);
+ if (ret < 0) {
+ dp_log_err(dev, "cannot enable DP_PHY[%d], %d\n", dp->id, ret);
+ mutex_unlock(&dp->pwlock);
+ return ret;
+ }
+
+ dp_reg_sw_reset(dp->id);
+ dp_reg_init(dp->id);
+
+ dp_reg_set_hpd_interrupt(dp->id, 1);
+
+ dp->training_state = false;
+
+ enable_irq(dp->irq);
+ disable_irq(dp->hpd_gpio_irq);
+
+ dp->state = DP_STATE_LINKED;
+
+ mutex_unlock(&dp->pwlock);
+
+ dp_log_info(dev, "-, state: %d\n", dp->state);
+
+ return ret;
+}
+
+/*
+ * Functions below are from exynos_dp_drv.c.
+ * It would be refactored more later.
+ */
+uint32_t exynos_drm_dp_find_possible_crtc(struct exynos_drm_dp *dp, int sst_idx)
+{
+ int possible_crtcs = 0;
+ struct device_node *dp_node = dp->dev->of_node;
+ struct device_node *endp, *remote_port;
+
+ endp = of_graph_get_endpoint_by_regs(dp_node, 0, sst_idx);
+ if (endp) {
+ remote_port = of_graph_get_remote_port(endp);
+ possible_crtcs = drm_of_crtc_port_mask(dp->drm_dev, remote_port);
+ of_node_put(remote_port);
+ }
+
+ of_node_put(endp);
+
+ return possible_crtcs;
+}
+
+dp_sst_idx_t exynos_drm_dp_get_sst_idx(struct drm_encoder *encoder)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
+ struct device_node *dp_node = dp->dev->of_node;
+ struct device_node *endp, *crtc_node;
+ struct of_endpoint of_endpoint;
+
+ for_each_endpoint_of_node(dp_node, endp) {
+ crtc_node = of_graph_get_remote_port(endp);
+ if (crtc->port == crtc_node) {
+ of_node_put(crtc_node);
+ goto out;
+ }
+ of_node_put(crtc_node);
+ }
+out:
+ of_graph_parse_endpoint(endp, &of_endpoint);
+ of_node_put(endp);
+ return of_endpoint.id;
+}
+
+/* DSC */
+static bool exynos_drm_dp_supports_dsc(struct exynos_drm_dp *dp)
+{
+ struct exynos_dp_subdev *subdev = dp->subdev;
+
+ /* This version returns true regardless of FEC capability */
+ return drm_dp_sink_supports_dsc(subdev->dsc_dpcd);
+}
+
+#define MAX_SOURCE_SLICE_COUNT 4
+#define INVALID_SOURCE_SLICE_COUNT 3
+static u8 exynos_drm_dsc_get_slice_count(struct exynos_drm_dp *dp,
+ struct drm_dsc_config *dsc_cfg)
+{
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ u32 max_slice_width;
+ u8 max_slice_count, source_slice_count;
+
+ max_slice_width = drm_dp_dsc_sink_max_slice_width(subdev->dsc_dpcd);
+ if (max_slice_width < DP_DSC_MIN_SLICE_WIDTH_VALUE) {
+ dp_log_info(dp->dev, "max_slice_width %d is out of range\n",
+ max_slice_width);
+ return 0;
+ }
+
+ max_slice_count =
+ drm_dp_dsc_sink_max_slice_count(subdev->dsc_dpcd, false);
+
+
+ /* FIXME: divide by 2 to use 4-slice in UHD picture */
+ source_slice_count = min_t(u8, MAX_SOURCE_SLICE_COUNT,
+ DIV_ROUND_UP(dsc_cfg->pic_width,
+ max_slice_width / 2));
+
+ if (source_slice_count == INVALID_SOURCE_SLICE_COUNT)
+ source_slice_count = MAX_SOURCE_SLICE_COUNT;
+
+ dp_log_kms(dp->dev, "max_slice_width %d count %d, src_slice_count %d\n",
+ max_slice_width, max_slice_count, source_slice_count);
+
+ return min_t(u8, source_slice_count, max_slice_count);
+}
+
+#define MIN_SOURCE_SLICE_HEIGHT 8
+static u16 exynos_drm_dsc_get_slice_height(struct exynos_drm_dp *dp,
+ struct drm_dsc_config *dsc_cfg)
+{
+ struct device *dev = dp->dev;
+ u16 slice_height;
+
+ if (dsc_cfg->pic_height <= 0) {
+ dp_log_info(dev, "invalid pic_height\n");
+ return 0;
+ }
+
+ /*
+ * Guideline for slice_height
+ * must be an integral divisor of picture height
+ * slice_height * slice_width >= 15,000 pixels
+ * slice_height * slice_width <= 100,000 pixels
+ */
+ if (dsc_cfg->pic_height % 16 == 0)
+ slice_height = dsc_cfg->pic_height / 16;
+ if (dsc_cfg->pic_height % 10 == 0)
+ slice_height = dsc_cfg->pic_height / 10;
+ else
+ slice_height = dsc_cfg->pic_height / 4;
+
+ return max_t(u16, slice_height, MIN_SOURCE_SLICE_HEIGHT);
+}
+
+static int exynos_drm_dp_dsc_compute(struct drm_encoder *encoder,
+ struct exynos_dp_video_info *vi,
+ dp_sst_idx_t sst_idx)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ u8 *dsc_dpcd = subdev->dsc_dpcd;
+ struct drm_dsc_picture_parameter_set *pps;
+ struct drm_dsc_config dsc_cfg;
+
+ /* Initialize */
+ memset(&dsc_cfg, 0x0, sizeof(dsc_cfg));
+
+ dsc_cfg.dsc_version_major =
+ (dsc_dpcd[DP_DSC_REV - DP_DSC_SUPPORT] &
+ DP_DSC_MAJOR_MASK) >> DP_DSC_MAJOR_SHIFT;
+ dsc_cfg.dsc_version_minor =
+ (dsc_dpcd[DP_DSC_REV - DP_DSC_SUPPORT] &
+ DP_DSC_MINOR_MASK) >> DP_DSC_MINOR_SHIFT;
+
+ dsc_cfg.line_buf_depth =
+ drm_dp_dsc_sink_line_buf_depth(dsc_dpcd);
+ dsc_cfg.convert_rgb =
+ dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]
+ & DP_DSC_RGB;
+ dsc_cfg.simple_422 =
+ dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]
+ & DP_DSC_YCbCr422_Simple;
+ dsc_cfg.native_420 =
+ dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]
+ & DP_DSC_YCbCr420_Native;
+ dsc_cfg.native_422 =
+ dsc_dpcd[DP_DSC_DEC_COLOR_FORMAT_CAP - DP_DSC_SUPPORT]
+ & DP_DSC_YCbCr422_Native;
+ dsc_cfg.block_pred_enable =
+ dsc_dpcd[DP_DSC_BLK_PREDICTION_SUPPORT - DP_DSC_SUPPORT]
+ & DP_DSC_BLK_PREDICTION_IS_SUPPORTED;
+
+ dsc_cfg.vbr_enable = false;
+ dsc_cfg.pic_width = vi->vm.hactive;
+ dsc_cfg.pic_height = vi->vm.vactive;
+ dsc_cfg.bits_per_component = vi->bpc;
+
+ /* TODO: This version only covers case of "BPC == BPP/3". */
+ /* Compressed BPP format is multiply by 16 */
+ dsc_cfg.bits_per_pixel = vi->bpc << 4;
+
+ dsc_cfg.slice_count = exynos_drm_dsc_get_slice_count(dp, &dsc_cfg);
+ if (dsc_cfg.slice_count == 0) {
+ dp_log_info(dp->dev, "slice_count is invalid\n");
+ return -EINVAL;
+ }
+
+ dsc_cfg.slice_height = exynos_drm_dsc_get_slice_height(dp, &dsc_cfg);
+
+ dsc_cfg.slice_width =
+ DIV_ROUND_UP(dsc_cfg.pic_width, dsc_cfg.slice_count);
+
+ vi->dsc.slice_count = dsc_cfg.slice_count;
+ vi->dsc.chunk_size = dsc_cfg.slice_chunk_size;
+
+ pps = &vi->dsc.pps;
+ drm_dsc_pps_payload_pack(pps, &dsc_cfg);
+
+ return 0;
+}
+
+bool exynos_drm_dp_dsc_enable(struct drm_encoder *encoder,
+ struct exynos_dp_video_info *vi, dp_sst_idx_t sst_idx)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+
+ if (!vi)
+ return false;
+
+ if (!exynos_drm_dp_supports_dsc(dp))
+ return false;
+
+ if (exynos_drm_dp_dsc_compute(encoder, vi, sst_idx) < 0)
+ return false;
+
+ return true;
+}
+
+void exynos_drm_dp_dsc_disable(struct drm_encoder *encoder, dp_sst_idx_t sst_idx)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+
+ /* Set default sst_idx */
+ if (sst_idx == DP_SST_UNKNOWN) {
+ dp_log_err(dp->dev, "sst idx is unknown\n");
+ return;
+ }
+}
+
+static void exynos_drm_dp_enable(struct drm_encoder *encoder)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ struct device *dev = dp->dev;
+ dp_sst_idx_t sst_idx;
+
+ dp_log_kms(dev, "exynos_drm_dp_enable\n");
+
+ if (subdev->state == DP_STATE_ON) {
+ dp_log_info(dev, "DP(%d), state : %d\n", dp->id, subdev->state);
+ return;
+ }
+
+ exynos_drm_dp_hpd_en(subdev);
+
+ sst_idx = exynos_drm_dp_get_sst_idx(encoder);
+
+ exynos_drm_dp_stream_enable(subdev, sst_idx);
+
+ subdev->state = DP_STATE_ON;
+}
+
+static void exynos_drm_dp_disable(struct drm_encoder *encoder)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ struct device *dev = dp->dev;
+ dp_sst_idx_t sst_idx;
+
+ dp_log_kms(dev, "\n");
+
+ mutex_lock(&subdev->pwlock);
+
+ if (subdev->state == DP_STATE_LINKED) {
+ dp_log_info(dev, "DP(%d), state : ON\n", dp->id);
+ mutex_unlock(&subdev->pwlock);
+ return;
+ }
+
+ sst_idx = exynos_drm_dp_get_sst_idx(encoder);
+ /* DSC */
+ exynos_drm_dp_dsc_disable(encoder, sst_idx);
+ exynos_drm_dp_stream_disable(subdev, sst_idx);
+
+ subdev->state = DP_STATE_LINKED;
+ mutex_unlock(&subdev->pwlock);
+}
+
+void exynos_drm_dp_to_videoinfo(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct exynos_dp_video_info *vi)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct device *dev = dp->dev;
+ struct dp_encoder *dp_encoder = to_encoder(encoder);
+ struct drm_display_info *dp_info =
+ &dp_encoder->dp_connector->base.display_info;
+
+ memset(vi, 0x0, sizeof(*vi));
+
+ drm_display_mode_to_videomode(mode, &vi->vm);
+ vi->vrefresh = drm_mode_vrefresh(mode);
+
+ /*
+ * The polarity of DP is set to the inverted value of DRM.
+ * DRM: DRM_MODE_FLAG_PHSYNC -> DISPLAY_FLAGS_HSYNC_HIGH
+ * DRM_MODE_FLAG_PVSYNC -> DISPLAY_FLAGS_VSYNC_HIGH
+ * DisplayPort: H/VSyncPolarity in HSP, VSP
+ * 0 = Active high pulse.
+ * 1 = Active low pulse.
+ */
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ vi->hsync_pol = false;
+ else
+ vi->hsync_pol = true;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ vi->vsync_pol = false;
+ else
+ vi->vsync_pol = true;
+
+ if (!dp_info->bpc)
+ dp_info->bpc = DEFAULT_BPC;
+
+ vi->bpc = dp_info->bpc;
+
+ dp_log_kms(dev, "%s, vrefresh(%d) pclock(%d khz) \
+ hsync_pol(%d) vsync_pol(%d) bpc(%d)\n",
+ mode->name, vi->vrefresh, mode->clock,
+ vi->hsync_pol, vi->vsync_pol, vi->bpc);
+}
+
+static void exynos_drm_dp_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ struct exynos_dp_video_info vi;
+ struct device *dev = dp->dev;
+ dp_sst_idx_t sst_idx;
+
+ sst_idx = exynos_drm_dp_get_sst_idx(encoder);
+ exynos_drm_dp_to_videoinfo(encoder, adjusted_mode, &vi);
+ /* DSC */
+ vi.dsc.enable = exynos_drm_dp_dsc_enable(encoder, &vi, sst_idx);
+ exynos_drm_dp_videomode_set(subdev, &vi, sst_idx);
+
+ drm_mode_copy(&dp->cur_mode, adjusted_mode);
+ dp_log_info(dev, "SST%u %s = %s-%dHz dsc %s\n", sst_idx,
+ encoder->name, mode->name, drm_mode_vrefresh(mode),
+ vi.dsc.enable ? "compression" : "bypass");
+}
+
+static bool exynos_drm_dp_connector_set_link_status(struct drm_device *dev,
+ struct exynos_drm_dp *dp)
+{
+ struct drm_connector *connector;
+ struct drm_connector_list_iter conn_iter;
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ struct exynos_drm_dp *drm_dp;
+ struct dp_connector *dp_connector;
+ enum drm_link_status set_link_status = DRM_LINK_STATUS_GOOD;
+ enum drm_link_status old_link_status;
+ int max_link_rate = 0;
+ int root_id = dp->connector->base.id;
+ bool changed = false;
+
+ if (subdev->dpcd[DP_MAX_LINK_RATE])
+ max_link_rate = subdev->dpcd[DP_MAX_LINK_RATE];
+
+ if ((subdev->hpd_state) &&
+ ((max_link_rate != subdev->lt_info.link_rate) || !subdev->training_state))
+ set_link_status = DRM_LINK_STATUS_BAD;
+
+ drm_connector_list_iter_begin(dev, &conn_iter);
+
+ drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
+ continue;
+
+ if (IS_ERR_OR_NULL(connector->state))
+ continue;
+
+ drm_dp = connector_to_dp(connector);
+ if (drm_dp->output_type != dp->output_type)
+ continue;
+
+ /* This is case of DP UNPLUG */
+ if (set_link_status == DRM_LINK_STATUS_GOOD)
+ goto CHECK_LINK_STATUS;
+
+ /* pdt = peer-device-type
+ * link_status is controled sink device in mst mode.
+ * pdt = 0 (no device connected
+ * ptd = 1 (Source device or SST branch device to UFP)
+ * pdt = 2 (Device with MST branch or SST branch device to DFP)
+ * pdt = 3 (SST Sink device or stream sink in MST)
+ * pdt = 4 (DP-to-Legacy converter)
+ */
+ dp_connector = to_connector(connector);
+ if (dp->is_mst && (!dp_connector->port ||
+ (dp_connector->port->pdt < DP_PEER_DEVICE_SST_SINK)))
+ continue;
+
+ if (!dp->is_mst && (connector->base.id != root_id))
+ continue;
+
+CHECK_LINK_STATUS:
+ old_link_status = connector->state->link_status;
+
+ if (set_link_status != old_link_status) {
+ dp_log_kms(dp->dev, "[CONNECTOR:%d:%s] link_status changed(%d) -> (%d)\n",
+ connector->base.id, connector->name,
+ old_link_status, set_link_status);
+ connector->state->link_status = set_link_status;
+ changed = true;
+ }
+ }
+
+ drm_connector_list_iter_end(&conn_iter);
+
+ return changed;
+}
+
+/*** connector functions ***/
+static enum drm_connector_status
+exynos_drm_dp_connector_detect(struct drm_connector *connector, bool force)
+{
+ enum drm_connector_status status = connector_status_disconnected;
+ enum drm_connector_status old_status = connector->status;
+ struct exynos_drm_dp *dp = connector_to_dp(connector);
+ struct drm_device *drm_dev = dp->drm_dev;
+ struct device *dev = dp->dev;
+ bool link_status_changed = false;
+
+ dp_log_kms(dev, "\n");
+
+ if (exynos_drm_dp_is_hpd_connected(dp->subdev))
+ status = connector_status_connected;
+
+ /* In case of MST, SST connector should be disconnected */
+ if (dp->is_mst)
+ status = connector_status_disconnected;
+
+ link_status_changed =
+ exynos_drm_dp_connector_set_link_status(drm_dev, dp);
+
+ if ((status == old_status) && link_status_changed)
+ drm_kms_helper_hotplug_event(dp->drm_dev);
+
+ dp_log_kms(dev, "status is %s\n",
+ status == connector_status_connected ? \
+ "connected" : "disconnected");
+ return status;
+};
+
+/* If native only is true, only native mode is used */
+#define for_each_dt_timings(num_timings, native_only, native_mode, __i) \
+ for ((__i) = (native_only) ? (native_mode) : 0; \
+ (__i) < (num_timings) && \
+ (!(native_only) || (__i) == (native_mode)); \
+ (__i)++) \
+
+static int exynos_drm_dp_add_mode_from_dt(struct drm_connector *connector,
+ struct display_timings *timings)
+{
+ struct dp_connector *dp_connector = to_connector(connector);
+ struct drm_device *drm_dev = connector->dev;
+ struct videomode vm;
+ struct drm_display_mode *mode;
+ int native_mode;
+ bool native_only;
+ int mode_num = 0;
+ int index;
+
+ if (!timings)
+ return mode_num;
+
+ /* Check a out of timings->num_timings */
+ native_mode = dp_connector->native_mode;
+ if (native_mode < 0 || native_mode >= timings->num_timings)
+ native_mode = timings->native_mode;
+
+ native_only = dp_connector->native_only;
+
+ for_each_dt_timings(timings->num_timings, native_only, native_mode, index) {
+ if (videomode_from_timings(timings, &vm, index)) {
+ dp_log_err(drm_dev->dev, "index(%d) is invalid\n",
+ index);
+ continue;
+ }
+
+ mode = drm_mode_create(drm_dev);
+ if (!mode) {
+ dp_log_err(drm_dev->dev, "failed to add mode %ux%u\n",
+ vm.hactive, vm.vactive);
+ continue;
+ }
+
+ drm_display_mode_from_videomode(&vm, mode);
+
+ mode->type = DRM_MODE_TYPE_DRIVER;
+
+ if (index == native_mode)
+ mode->type |= DRM_MODE_TYPE_PREFERRED;
+
+ dp_log_dbg(drm_dev->dev, "%s, vrefresh(%d), pclock(%d khz)\n",
+ mode->name, drm_mode_vrefresh(mode), mode->clock);
+
+ drm_mode_probed_add(connector, mode);
+ mode_num++;
+ }
+
+ return mode_num;
+}
+
+int exynos_drm_dp_add_timings(struct exynos_drm_dp *dp,
+ struct drm_connector *connector)
+{
+ struct display_timings *timings = dp->timings;
+ struct device *dev = dp->dev;
+ int num_modes;
+
+ num_modes = exynos_drm_dp_add_mode_from_dt(connector, timings);
+ if (!num_modes)
+ dp_log_dbg(dev, "failed to get modes from device tree");
+
+ return num_modes;
+}
+
+static int exynos_drm_dp_get_modes(struct drm_connector *connector)
+{
+ struct exynos_drm_dp *dp = connector_to_dp(connector);
+ struct exynos_dp_subdev *subdev = dp->subdev;
+ struct device *dev = dp->dev;
+ struct edid *edid = NULL;
+ int num_modes = 0;
+ u8 support_edid;
+
+ num_modes = exynos_drm_dp_add_timings(dp, connector);
+ if (num_modes)
+ goto ret;
+
+ drm_dp_dpcd_readb(&subdev->aux, DP_RECEIVE_PORT_0_CAP_0, &support_edid);
+
+ if (support_edid & DP_LOCAL_EDID_PRESENT)
+ edid = drm_get_edid(connector, &subdev->aux.ddc);
+
+ if (edid) {
+ drm_connector_update_edid_property(connector, edid);
+ num_modes = drm_add_edid_modes(connector, edid);
+ kfree(edid);
+ }
+
+ret:
+ dp_log_kms(dev, "num_modes %d\n", num_modes);
+ return num_modes;
+}
+static enum drm_mode_status exynos_drm_dp_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ struct exynos_drm_dp *dp = connector_to_dp(connector);
+ struct device *dev = dp->dev;
+
+ dp_log_kms(dev, "hdisplay=%d, vdisplay=%d, vrefresh=%d, clock=%d\n",
+ mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode),
+ mode->clock * 1000);
+
+ /* TODO: return MODE_BAD if exynos_dp_drv can't support it */
+
+ return MODE_OK;
+}
+
+static void exynos_drm_dp_destroy_encoder(struct drm_encoder *encoder)
+{
+ struct exynos_drm_dp *dp = encoder_to_dp(encoder);
+ struct dp_encoder *dp_encoder = to_encoder(encoder);
+ struct device *dev = dp->dev;
+
+ dp_log_kms(dev, "encoder %d\n", encoder->base.id);
+ drm_encoder_cleanup(encoder);
+
+ kfree(dp_encoder);
+}
+
+static void exynos_drm_dp_destroy_connector(struct drm_connector *connector)
+{
+ struct dp_connector *dp_connector = to_connector(connector);
+ struct exynos_drm_dp *dp = connector_to_dp(connector);
+ struct device *dev = dp->dev;
+
+ dp_log_kms(dev, "connector %d\n", connector->base.id);
+ drm_connector_cleanup(connector);
+ kfree(dp_connector);
+}
+
+static const struct drm_encoder_funcs exynos_drm_dp_encoder_funcs = {
+ .destroy = exynos_drm_dp_destroy_encoder,
+};
+
+static const struct drm_encoder_helper_funcs
+exynos_drm_dp_encoder_helper_funcs = {
+ .enable = exynos_drm_dp_enable,
+ .disable = exynos_drm_dp_disable,
+ .mode_fixup = NULL,
+ .mode_set = exynos_drm_dp_mode_set,
+};
+
+static const struct drm_connector_funcs exynos_drm_dp_connector_funcs = {
+ .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+ .detect = exynos_drm_dp_connector_detect,
+ .destroy = exynos_drm_dp_destroy_connector,
+ .dpms = drm_helper_connector_dpms,
+ .reset = drm_atomic_helper_connector_reset,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+};
+
+static const struct drm_connector_helper_funcs
+exynos_drm_dp_connector_helper_funcs = {
+ .get_modes = exynos_drm_dp_get_modes,
+ .mode_valid = exynos_drm_dp_mode_valid,
+};
+
+static struct dp_encoder *dp_encoder_alloc(struct exynos_drm_dp *dp)
+{
+ struct dp_encoder *dp_encoder;
+
+ dp_encoder = kzalloc(sizeof(*dp_encoder), GFP_KERNEL);
+ if (!dp_encoder)
+ return NULL;
+
+ dp_encoder->dp = dp;
+ dp_encoder->output_type = dp->output_type;
+ dp_encoder->sst_idx = DP_SST_UNKNOWN;
+
+ return dp_encoder;
+}
+
+static struct dp_connector *dp_connector_alloc(struct exynos_drm_dp *dp,
+ struct dp_encoder *dp_encoder)
+{
+ struct dp_connector *dp_connector;
+
+ dp_connector = kzalloc(sizeof(*dp_connector), GFP_KERNEL);
+ if (!dp_connector)
+ return NULL;
+
+ dp_connector->dp = dp;
+ dp_connector->dp_encoder = dp_encoder;
+ dp_encoder->dp_connector = dp_connector;
+
+ return dp_connector;
+}
+
+static int exynos_drm_dp_init_encoder(struct dp_encoder *dp_encoder)
+{
+ struct exynos_drm_dp *dp = dp_encoder->dp;
+ struct drm_device *drm_dev = dp->drm_dev;
+ struct drm_encoder *encoder = &dp_encoder->base;
+ dp_sst_idx_t sst_id;
+
+ drm_encoder_init(drm_dev, encoder, &exynos_drm_dp_encoder_funcs,
+ DRM_MODE_ENCODER_TMDS, "exynos_dp%u", dp->id);
+
+ drm_encoder_helper_add(encoder, &exynos_drm_dp_encoder_helper_funcs);
+
+ for (sst_id = DP_SST_1; sst_id <= DP_SST_MAX; sst_id++)
+ encoder->possible_crtcs |= exynos_drm_dp_find_possible_crtc(dp, sst_id);
+
+ dp_log_info(dp->dev, "Success to create %s(possible_crtcs:%#x)\n",
+ encoder->name, encoder->possible_crtcs);
+
+ return 0;
+}
+
+static int exynos_drm_dp_init_connector(struct dp_connector *dp_connector)
+{
+ struct exynos_drm_dp *dp = dp_connector->dp;
+ struct drm_connector *connector = &dp_connector->base;
+ struct drm_encoder *encoder = &dp_connector->dp_encoder->base;
+ struct device *dev = dp->dev;
+ int ret = 0;
+
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+
+ ret = drm_connector_init(dp->drm_dev, connector,
+ &exynos_drm_dp_connector_funcs,
+ DRM_MODE_CONNECTOR_DisplayPort);
+ if (ret) {
+ dp_log_err(dev, "Failed to create connector %d\n", ret);
+ drm_connector_cleanup(connector);
+ return ret;
+ }
+
+ drm_connector_helper_add(connector,
+ &exynos_drm_dp_connector_helper_funcs);
+ drm_connector_attach_encoder(connector, encoder);
+
+ dp_log_info(dev, "Success to create connector %s\n", connector->name);
+
+ return 0;
+}
+
+static int exynos_drm_dp_get_timings(struct device *dev)
+{
+ struct exynos_drm_dp *dp = dev_get_drvdata(dev);
+ struct device_node *np = dev->of_node;
+ struct device_node *timing_np;
+
+ timing_np = of_parse_phandle(np, "samsung,display-timings", 0);
+ if (!timing_np) {
+ dp_log_info(dev, "could not get display timings\n");
+ return 0;
+ }
+
+ dp->timings = of_get_display_timings(timing_np);
+ if (IS_ERR_OR_NULL(dp->timings)) {
+ dp_log_err(dev, "could not get native display timing\n");
+ of_node_put(timing_np);
+ return -EINVAL;
+ }
+ of_node_put(timing_np);
+
+ return 0;
+}
+
+static void exynos_drm_dp_get_native_mode(struct device *dev,
+ struct dp_connector *dp_connector)
+{
+ struct device_node *np = dev->of_node;
+ int idx;
+
+ if (of_property_read_s32(np, "samsung,native-mode,idx", &idx) < 0)
+ dp_connector->native_mode = -1;
+ else
+ dp_connector->native_mode = idx;
+
+ dp_connector->native_only =
+ of_property_read_bool(np, "samsung,native-only");
+
+ of_property_read_u32(np, "samsung,native-mode,bpc",
+ &dp_connector->base.display_info.bpc);
+}
+
+static ssize_t exynos_drm_dp_aux_transfer(struct drm_dp_aux *aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct exynos_dp_subdev *subdev = to_subdev(aux);
+ struct device *dev = subdev->dev;
+ u8 *buffer = msg->buffer;
+ int transferred = 0;
+ u32 id = subdev->id;
+
+ dp_log_enter(dev);
+
+ if (subdev->state == DP_STATE_OFF)
+ return -ETIMEDOUT;
+
+ if (subdev->hpd_state == HPD_UNPLUG)
+ return -ETIMEDOUT;
+
+ if (!dp_reg_get_hpd_status(subdev->id))
+ return -ETIMEDOUT;
+
+ switch (msg->request & ~DP_AUX_I2C_MOT) {
+ case DP_AUX_NATIVE_WRITE:
+ transferred = dp_reg_dpcd_write_burst(id, msg->address,
+ msg->size, buffer);
+ break;
+ case DP_AUX_NATIVE_READ:
+ transferred = dp_reg_dpcd_read_burst(id, msg->address,
+ msg->size, buffer);
+ break;
+ case DP_AUX_I2C_WRITE:
+ case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+ return dp_reg_aux_write(id, I2C_WRITE, msg->address,
+ msg->size, buffer);
+ case DP_AUX_I2C_READ:
+ return dp_reg_aux_read(id, I2C_READ, msg->address,
+ msg->size, buffer);
+ default:
+ dp_log_err(dev, "invalid msg request(%#x)\n", msg->request);
+ return -EINVAL;
+ }
+
+ return transferred > 0 ? transferred : -EBUSY;
+}
+
+static void exynos_drm_dp_subdev_detect(struct device *dev)
+{
+ struct exynos_drm_dp *dp = dev_get_drvdata(dev);
+ struct drm_device *drm_dev = dp->drm_dev;
+
+ dp_log_kms(dev, "\n");
+ drm_helper_hpd_irq_event(drm_dev);
+}
+
+static int exynos_drm_dp_subdev_bind(struct device *dev,
+ struct exynos_drm_dp *dp)
+{
+ struct exynos_dp_subdev *subdev = NULL;
+ struct drm_device *drm_dev = dp->drm_dev;
+ int ret = 0;
+
+ ret = exynos_drm_dp_subdev_probe(dp->id, dev, drm_dev, &subdev);
+ if (ret < 0)
+ return ret;
+
+ dp->subdev = subdev;
+ subdev->aux.name = kasprintf(GFP_KERNEL, "%s%d-aux", DEV_NAME, dp->id);
+ subdev->aux.transfer = exynos_drm_dp_aux_transfer;
+ subdev->aux.dev = dev;
+ subdev->aux.drm_dev = drm_dev;
+ subdev->detect = exynos_drm_dp_subdev_detect;
+ subdev->state = DP_STATE_OFF;
+
+ ret = drm_dp_aux_register(&subdev->aux);
+ if (ret) {
+ dp_log_err(dev, "failed to aux_register, %d\n", ret);
+ goto err_aux_register;
+ }
+
+ if (gpio_get_value(subdev->hpd_gpio))
+ exynos_drm_dp_reset(subdev);
+
+ dp_log_dbg(dev, "%s registered for aux_transfer\n", subdev->aux.name);
+
+ return 0;
+
+err_aux_register:
+ return ret;
+}
+
+static int exynos_drm_dp_bind(struct device *dev,
+ struct device *master, void *data)
+{
+ struct exynos_drm_dp *dp = dev_get_drvdata(dev);
+ struct dp_encoder *dp_encoder;
+ struct dp_connector *dp_connector;
+ struct drm_device *drm_dev = data;
+ int ret = 0;
+
+ dp_log_enter(dev);
+ dp->drm_dev = drm_dev;
+
+ dp_encoder = dp_encoder_alloc(dp);
+ if (IS_ERR_OR_NULL(dp_encoder))
+ goto err_encoder_init;
+ ret = exynos_drm_dp_init_encoder(dp_encoder);
+ if (ret < 0)
+ goto err_encoder_init;
+
+ dp_connector = dp_connector_alloc(dp, dp_encoder);
+ if (IS_ERR_OR_NULL(dp_connector))
+ goto err_connector_init;
+ ret = exynos_drm_dp_init_connector(dp_connector);
+ if (ret < 0)
+ goto err_connector_init;
+
+ /* Set base encoder/connector */
+ dp->encoder = &dp_encoder->base;
+ dp->connector = &dp_connector->base;
+
+ ret = exynos_drm_dp_get_timings(dev);
+ if (ret < 0)
+ goto err_connector_init;
+
+ exynos_drm_dp_get_native_mode(dev, dp_connector);
+
+ ret = exynos_drm_dp_subdev_bind(dev, dp);
+ if (ret < 0)
+ goto err_connector_init;
+
+ exynos_drm_dp_mst_init(dp_connector);
+
+ /* enable_irq end of binding */
+ if (dp->subdev)
+ enable_irq(dp->subdev->hpd_gpio_irq);
+
+ ret = exynos_drm_dp_start(dp->subdev);
+ if (ret < 0)
+ dp_log_err(dp->dev, "cannot start DP[%d], %d\n", dp->id, ret);
+
+ dp->is_mst = false;
+
+ dp_log_exit(dev);
+
+ return 0;
+
+err_connector_init:
+ if (dp_connector)
+ exynos_drm_dp_destroy_connector(&dp_connector->base);
+err_encoder_init:
+ if (dp_encoder)
+ exynos_drm_dp_destroy_encoder(&dp_encoder->base);
+ dp_log_err(dev, "failed: drm_encoder/connector_init() : %d\n", ret);
+ return ret;
+}
+
+static void exynos_drm_dp_unbind(struct device *dev, struct device *master,
+ void *data)
+{
+ struct exynos_drm_dp *dp = dev_get_drvdata(dev);
+ struct exynos_dp_subdev *subdev = dp->subdev;
+
+ dp_log_enter(dev);
+
+ kfree(subdev->aux.name);
+
+ destroy_workqueue(subdev->dp_wq);
+ mutex_destroy(&subdev->lock);
+ mutex_destroy(&subdev->pwlock);
+
+ drm_dp_aux_unregister(&subdev->aux);
+
+ display_timings_release(dp->timings);
+
+ flush_delayed_work(&subdev->hpd_irq_work);
+
+ disable_irq(subdev->irq);
+ disable_irq(subdev->hpd_gpio_irq);
+
+
+ dp_log_exit(dev);
+ return;
+}
+
+static const struct component_ops exynos_drm_dp_ops = {
+ .bind = exynos_drm_dp_bind,
+ .unbind = exynos_drm_dp_unbind,
+};
+
+static int exynos_drm_dp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_drm_dp *dp;
+
+ struct clk *aclk, *pclk;
+ int ret = 0;
+
+ dp = devm_kzalloc(dev, sizeof(struct exynos_drm_dp),
+ GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ dp->dev = dev;
+ dp->bridge.of_node = dev->of_node;
+ dp->bridge.driver_private = dp;
+ drm_bridge_add(&dp->bridge);
+
+ dp->id = 0;
+ // TODO: make it proper value
+ dp->output_type = EXYNOS_DISPLAY_TYPE_NONE;
+
+ platform_set_drvdata(pdev, dp);
+
+ aclk = devm_clk_get_enabled(dp->dev, "aclk");
+ if (IS_ERR(aclk))
+ return dev_err_probe(dp->dev, PTR_ERR(aclk),
+ "Could not get aclk clock\n");
+ pclk = devm_clk_get_enabled(dp->dev, "pclk");
+ if (IS_ERR(pclk))
+ return dev_err_probe(dp->dev, PTR_ERR(pclk),
+ "Could not get pclk clock\n");
+
+ dp_log_info(dev, "is successfully\n");
+
+ ret = component_add(dev, &exynos_drm_dp_ops);
+ if (ret)
+ goto fail_probe;
+
+ return 0;
+
+fail_probe:
+ dp_log_err(dev, "probe failed");
+ devm_kfree(dev, dp);
+
+ return ret;
+}
+
+static void
+exynos_drm_dp_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct exynos_drm_dp *dp = dev_get_drvdata(dev);
+
+ device_remove_file(dev, &dev_attr_dump_sfr);
+ device_remove_file(dev, &dev_attr_link_lane);
+ device_remove_file(dev, &dev_attr_bist_mode);
+ device_remove_file(dev, &dev_attr_debug_hpd_irq);
+ device_remove_file(dev, &dev_attr_debug_lt);
+
+ drm_bridge_remove(&dp->bridge);
+
+ component_del(&pdev->dev, &exynos_drm_dp_ops);
+
+}
+
+static const struct dp_dev_data exynos910_dp = {
+ .version = V910,
+};
+
+static const struct of_device_id exynos_drm_dp_match[] = {
+ { .compatible = "samsung,exynos910-dp", .data = (void *)&exynos910_dp },
+ {},
+};
+MODULE_DEVICE_TABLE(of, exynos_drm_dp_match);
+
+struct platform_driver dp_driver = {
+ .probe = exynos_drm_dp_probe,
+ .remove_new = exynos_drm_dp_remove,
+ .driver = {
+ .name = DEV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = exynos_drm_dp_match,
+ },
+};
+
+MODULE_DESCRIPTION("Samsung Specific DisplayPort Driver");
+MODULE_LICENSE("GPL v2");
new file mode 100644
@@ -0,0 +1,964 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Samsung ExynosAuto DRM Display Port driver Header
+ *
+ * Copyright (C) 2018 Samsung Electronics Co.Ltd
+ */
+
+#ifndef __EXYNOS_DRM_DP_H__
+#define __EXYNOS_DRM_DP_H__
+
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+
+#include <drm/display/drm_dsc.h>
+#include <drm/display/drm_dp_mst_helper.h>
+
+#include <drm/drm_bridge.h>
+#include <drm/drm_print.h>
+#include <video/videomode.h>
+
+#include "exynos_drm_drv.h"
+
+#include <linux/types.h>
+#include <linux/kernel.h> /* DIV_ROUND_CLOSEST */
+#include <linux/printk.h> /* pr_xxx */
+#include <linux/io.h> /* for use 'readl()', 'writel()' functions */
+
+#define CHECK_RANGE(param, min, max) ((min) <= (param) && (param) <= (max))
+
+enum dpu_pixel_format {
+ /* RGB 8bit display */
+ /* 4byte */
+ DPU_PIXEL_FORMAT_ARGB_8888 = 0,
+ DPU_PIXEL_FORMAT_ABGR_8888,
+ DPU_PIXEL_FORMAT_RGBA_8888,
+ DPU_PIXEL_FORMAT_BGRA_8888,
+ DPU_PIXEL_FORMAT_XRGB_8888,
+ DPU_PIXEL_FORMAT_XBGR_8888,
+ DPU_PIXEL_FORMAT_RGBX_8888,
+ DPU_PIXEL_FORMAT_BGRX_8888,
+ /* 2byte */
+ DPU_PIXEL_FORMAT_RGBA_5551,
+ DPU_PIXEL_FORMAT_BGRA_5551,
+ DPU_PIXEL_FORMAT_ABGR_4444,
+ DPU_PIXEL_FORMAT_RGBA_4444,
+ DPU_PIXEL_FORMAT_BGRA_4444,
+ DPU_PIXEL_FORMAT_RGB_565,
+ DPU_PIXEL_FORMAT_BGR_565,
+
+ /* RGB 10bit display */
+ /* 4byte */
+ DPU_PIXEL_FORMAT_ARGB_2101010,
+ DPU_PIXEL_FORMAT_ABGR_2101010,
+ DPU_PIXEL_FORMAT_RGBA_1010102,
+ DPU_PIXEL_FORMAT_BGRA_1010102,
+
+ /* YUV 8bit display */
+ /* YUV422 2P */
+ DPU_PIXEL_FORMAT_NV16,
+ DPU_PIXEL_FORMAT_NV61,
+ /* YUV422 3P */
+ DPU_PIXEL_FORMAT_YVU422_3P,
+ /* YUV420 2P */
+ DPU_PIXEL_FORMAT_NV12,
+ DPU_PIXEL_FORMAT_NV21,
+ DPU_PIXEL_FORMAT_NV12M,
+ DPU_PIXEL_FORMAT_NV21M,
+ /* YUV420 3P */
+ DPU_PIXEL_FORMAT_YUV420,
+ DPU_PIXEL_FORMAT_YVU420,
+ DPU_PIXEL_FORMAT_YUV420M,
+ DPU_PIXEL_FORMAT_YVU420M,
+ /* YUV - 2 planes but 1 buffer */
+ DPU_PIXEL_FORMAT_NV12N,
+ DPU_PIXEL_FORMAT_NV12N_10B,
+
+ /* YUV 10bit display */
+ /* YUV420 2P */
+ DPU_PIXEL_FORMAT_NV12M_P010,
+ DPU_PIXEL_FORMAT_NV21M_P010,
+
+ /* YUV420(P8+2) 4P */
+ DPU_PIXEL_FORMAT_NV12M_S10B,
+ DPU_PIXEL_FORMAT_NV21M_S10B,
+
+ /* YUV422 2P */
+ DPU_PIXEL_FORMAT_NV16M_P210,
+ DPU_PIXEL_FORMAT_NV61M_P210,
+
+ /* YUV422(P8+2) 4P */
+ DPU_PIXEL_FORMAT_NV16M_S10B,
+ DPU_PIXEL_FORMAT_NV61M_S10B,
+
+ DPU_PIXEL_FORMAT_NV12_P010,
+
+ DPU_PIXEL_FORMAT_MAX,
+};
+
+enum chip_version {
+ /* EXYNOS chip version */
+ V910,
+ V920,
+};
+
+struct dpu_panel_timing {
+ unsigned long pclk;
+ unsigned int vactive;
+ unsigned int vfp;
+ unsigned int vsa;
+ unsigned int vbp;
+
+ unsigned int hactive;
+ unsigned int hfp;
+ unsigned int hsa;
+ unsigned int hbp;
+ unsigned int fps;
+};
+
+struct dpu_format {
+ const char *name;
+ enum dpu_pixel_format dpu_fmt; /* user-interface dpu color format */
+ u32 dma_fmt_afbc; /* afbc color format to DPU_DMA */
+ u32 dma_fmt; /* color format to DPU_DMA */
+ u32 dpp_fmt; /* color format to DPP */
+};
+
+struct exynos_dsc {
+ bool enabled;
+ u32 dsc_count;
+ u32 slice_count;
+ u32 slice_width;
+ u32 slice_height;
+};
+
+/**
+ * DPU register types to be controlled
+ */
+typedef enum cal_regs_type {
+ REGS_NONE = 0, /* This is default type register */
+
+ REGS_IDMA, /* DPU_DMA Read/Write Layer + DPU_DMA Global(v920) */
+ REGS_ODMA, /* unused */
+ REGS_DMA_GLB, /* DPU_DMA Global(v910) */
+ REGS_DPP, /* DPP Layer(common CSC, SCL) */
+ REGS_SCL_COEF, /* SCL COEFx */
+ REGS_DPP_SRAMC, /* SRAM_CON Layer */
+ REGS_VOTF, /* unused */
+ REGS_HDR_COMM, /* HDR Common */
+
+ REGS_DECON,
+ REGS_DECON_CON,
+ REGS_DECON_SHD,
+ REGS_DECON_GLB,
+ REGS_DECON_DSC,
+ REGS_DECON_SRAMC_D,
+ REGS_DECON_SRAMC_G,
+ REGS_WIN,
+ REGS_WIN_CTRL,
+
+ REGS_DSI,
+ REGS_DSI_SYSR, /* system register */
+ REGS_DPHY,
+ REGS_DPHY_BIAS,
+
+ REGS_DP,
+ REGS_DPPHY,
+
+ REGS_TYPE_MAX
+} regs_type_t;
+
+/* This is for register dump descriptor */
+struct cal_dump_desc {
+ u32 offs;
+ u32 size;
+ const char *name; /* subpart has no name(=NULL) */
+ u32 shdw; /* shadow offset if device has */
+};
+
+struct cal_regs_desc {
+ const char *name; /* device name */
+ void __iomem *regs;
+ regs_type_t type;
+ u32 idx; /* device id */
+
+ const struct cal_dump_desc *dps; /* dumps */
+ u32 nr_dps; /* number of dumps array */
+};
+
+#define SFRDUMP_DEF(_start, _end, _shadow, _name) \
+ { \
+ .offs = _start, \
+ .size = ((_end) - (_start) + 0x4), \
+ .shdw = _shadow, \
+ .name = _name, \
+ }
+
+/* return compressed DSC slice width(unit: pixel cnt) */
+static inline u32 get_comp_dsc_width(const struct exynos_dsc *dsc, u32 bpc)
+{
+ unsigned int slice_width_pixels =
+ DIV_ROUND_UP(dsc->slice_width * bpc, 8);
+
+ return ALIGN(DIV_ROUND_UP(slice_width_pixels, 3), 4);
+}
+
+/* common function macro for register control file */
+/* to get cal_regs_desc */
+#define cal_regs_desc_check(id, max, name) \
+ ({ \
+ if (id >= max) { \
+ cal_log_err(id, "dev(%s) is bigger than max_id(%d)\n", \
+ name, max); \
+ BUG(); \
+ } \
+ })
+
+#define cal_regs_desc_save(desc_name, _regs, _name, _type, id) \
+ ({ \
+ regs_##desc_name[id].regs = _regs; \
+ regs_##desc_name[id].name = _name; \
+ regs_##desc_name[id].type = _type; \
+ regs_##desc_name[id].idx = id; \
+ cal_log_debug(id, "%s: type(%d) regs(0x%p)\n", _name, _type, \
+ &_regs); \
+ })
+
+#define cal_dump_desc_save(desc_name, id) \
+ ({ \
+ regs_##desc_name[id].dps = desc_name##_dumps; \
+ regs_##desc_name[id].nr_dps = ARRAY_SIZE(desc_name##_dumps); \
+ })
+
+/* SFR read/write */
+static inline uint32_t cal_read(struct cal_regs_desc *regs_desc,
+ uint32_t offset)
+{
+ uint32_t val = 0;
+ val = readl(regs_desc->regs + offset);
+ return val;
+}
+
+static inline void cal_write(struct cal_regs_desc *regs_desc, uint32_t offset,
+ uint32_t val)
+{
+ writel(val, regs_desc->regs + offset);
+}
+
+static inline uint32_t cal_read_mask(struct cal_regs_desc *regs_desc,
+ uint32_t offset, uint32_t mask)
+{
+ uint32_t val = cal_read(regs_desc, offset);
+ val &= (mask);
+ return val;
+}
+
+static inline void cal_write_mask(struct cal_regs_desc *regs_desc,
+ uint32_t offset, uint32_t val, uint32_t mask)
+{
+ uint32_t old = cal_read(regs_desc, offset);
+ val = (val & mask) | (old & ~mask);
+ cal_write(regs_desc, offset, val);
+}
+
+#define DEFINE_CAL_REGS_FUNCS(name, size) \
+ static struct cal_regs_desc regs_##name[size]; \
+ static inline uint32_t name##_read(u32 idx, u32 offset) \
+ { \
+ return cal_read(®s_##name[idx], offset); \
+ } \
+ static inline uint32_t name##_read_mask(u32 idx, u32 offset, u32 mask) \
+ { \
+ return cal_read_mask(®s_##name[idx], offset, mask); \
+ } \
+ static inline void name##_write(u32 idx, u32 offset, u32 val) \
+ { \
+ return cal_write(®s_##name[idx], offset, val); \
+ } \
+ static inline void name##_write_mask(u32 idx, u32 offset, u32 val, \
+ u32 mask) \
+ { \
+ return cal_write_mask(®s_##name[idx], offset, val, mask); \
+ }
+
+/* log messages */
+#define cal_msg(func, _id, fmt, ...) \
+ func("[drm:%s(#%d)] " fmt, __func__, _id, ##__VA_ARGS__)
+
+#define cal_log_enter(id) cal_msg(pr_debug, id, "%s", "+")
+#define cal_log_exit(id) cal_msg(pr_debug, id, "%s", "-")
+
+#define cal_log_debug(id, fmt, ...) cal_msg(pr_debug, id, fmt, ##__VA_ARGS__)
+#define cal_log_warn(id, fmt, ...) cal_msg(pr_warn, id, fmt, ##__VA_ARGS__)
+#define cal_log_info(id, fmt, ...) cal_msg(pr_info, id, fmt, ##__VA_ARGS__)
+#define cal_log_err(id, fmt, ...) \
+ cal_msg(pr_err_ratelimited, id, fmt, ##__VA_ARGS__)
+
+#define cal_ops(ctx, op, args...) \
+ (((ctx)->cal_ops && (ctx)->cal_ops->op) ? ((ctx)->cal_ops->op(args)) : \
+ 0)
+
+/* register dumps message */
+#define DUMP_PREFIX "[drm:DUMP] "
+#define print_dump_info(string, ...) \
+ pr_info("%s===== " string " =====\n", DUMP_PREFIX, ##__VA_ARGS__)
+
+static inline void __cal_dump_regs(struct cal_regs_desc *desc, bool shadow)
+{
+ int i;
+ u32 shdw_offs;
+
+ for (i = 0; i < desc->nr_dps; i++) {
+ const struct cal_dump_desc *dps = &desc->dps[i];
+
+ if (!dps)
+ continue;
+
+ if (shadow && !dps->shdw)
+ continue;
+
+ shdw_offs = shadow ? dps->shdw : 0x0;
+ if (dps->name)
+ print_dump_info("%s: %s %s(+0x%X)", desc->name,
+ dps->name, shadow ? "SHADOW " : "",
+ shdw_offs);
+ // print_hexdump(desc->regs + dps->offs + shdw_offs, dps->size);
+ }
+}
+
+static inline void cal_dump_regs(struct cal_regs_desc *desc)
+{
+ const struct cal_dump_desc *dps;
+
+ if (!desc || !desc->regs || !desc->dps)
+ return;
+
+ __cal_dump_regs(desc, false);
+
+ dps = &desc->dps[0];
+ if (dps->shdw)
+ __cal_dump_regs(desc, true);
+}
+
+
+#define MAX_DP_CNT 2
+#define MAX_SST_CNT 4
+#define MAX_VC_PAYLOAD_TIMESLOT 63
+#define NUM_VC_PAYLOAD_SLOT 8
+
+#define LINK_RATE_1_62Gbps 0x06
+#define LINK_RATE_2_7Gbps 0x0A
+#define LINK_RATE_5_4Gbps 0x14
+#define LINK_RATE_8_1Gbps 0x1E
+
+#define AUX_DATA_BUF_COUNT 16
+#define AUX_RETRY_COUNT 3
+#define AUX_TIMEOUT_1800us 0x03
+
+#define SYNC_POSITIVE 0
+#define SYNC_NEGATIVE 1
+
+typedef enum {
+ NORAMAL_DATA = 0,
+ TRAINING_PATTERN_1 = 1,
+ TRAINING_PATTERN_2 = 2,
+ TRAINING_PATTERN_3 = 3,
+ TRAINING_PATTERN_4 = 5,
+} dp_training_pattern;
+
+typedef enum {
+ DISABLE_PATTEN = 0,
+ D10_2_PATTERN = 1,
+ SERP_PATTERN = 2,
+ PRBS7 = 3,
+ CUSTOM_80BIT = 4,
+ HBR2_COMPLIANCE = 5,
+} dp_qual_pattern;
+
+enum aux_ch_command_type {
+ I2C_WRITE = 0x4,
+ I2C_READ = 0x5,
+ DPCD_WRITE = 0x8,
+ DPCD_READ = 0x9,
+};
+
+enum phy_tune_info {
+ AMP = 0,
+ POST_EMP = 1,
+ PRE_EMP = 2,
+ IDRV = 3,
+};
+
+enum test_pattern {
+ COLOR_BAR = 0,
+ WGB_BAR,
+ MW_BAR,
+ CTS_COLOR_RAMP,
+ CTS_BLACK_WHITE,
+ CTS_COLOR_SQUARE_VESA,
+ CTS_COLOR_SQUARE_CEA,
+};
+
+typedef enum {
+ ENABLE_SCRAM = 0,
+ DISABLE_SCRAM = 1,
+} dp_scrambling;
+
+enum dp_interrupt_mask {
+ PLL_LOCK_CHG_INT_MASK,
+ HOTPLUG_CHG_INT_MASK,
+ HPD_LOST_INT_MASK,
+ PLUG_INT_MASK,
+ HPD_IRQ_INT_MASK,
+ RPLY_RECEIV_INT_MASK,
+ AUX_ERR_INT_MASK,
+ HDCP_LINK_CHECK_INT_MASK,
+ HDCP_LINK_FAIL_INT_MASK,
+ HDCP_R0_READY_INT_MASK,
+ VIDEO_FIFO_UNDER_FLOW_MASK,
+ VSYNC_DET_INT_MASK,
+ AUDIO_FIFO_UNDER_RUN_INT_MASK,
+ AUDIO_FIFO_OVER_RUN_INT_MASK,
+
+ ALL_INT_MASK
+};
+
+enum dynamic_range_type {
+ VESA_RANGE = 0, /* (0 ~ 255) */
+ CEA_RANGE = 1, /* (16 ~ 235) */
+};
+
+enum bit_depth {
+ BPC_6 = 0,
+ BPC_8,
+ BPC_10,
+};
+
+typedef enum {
+ V640X480P60,
+ V640X480P30,
+ V720X480P60,
+ V720X576P50,
+ V1280X800P60RB,
+ V1280X720P60,
+ V1366X768P60,
+ V1280X1024P60,
+ V1920X1080P24,
+ V1920X1080P25,
+ V1920X1080P30,
+ V1600X900P60RB,
+ V1920X1080P60,
+ V1920X1200P60,
+ V1920X1200P60P,
+ V1920X1200P30,
+ V1920X1200P30P,
+ V3840X2160P24,
+ V3840X2160P25,
+ V3840X2160P30,
+ V4096X2160P24,
+ V4096X2160P25,
+ V4096X2160P30,
+ V3840X2160P50,
+ V3840X2160P60,
+ V4096X2160P50,
+ V4096X2160P60,
+ V1560X700P60,
+ V800X400P60,
+ V1120X780P60,
+ /* Used for AR HUD */
+ V1440X2560P60,
+ V1440X720P60,
+ V1800X900P60,
+ VIDEO_FORMAT_MAX,
+} videoformat;
+
+struct dp_support_video {
+ videoformat video_format;
+ u32 hactive;
+ u32 hfront_porch;
+ u32 hsync_len;
+ u32 hback_porch;
+ u32 vactive;
+ u32 vfront_porch;
+ u32 vsync_len;
+ u32 vback_porch;
+ u32 pixelclock;
+ u32 vsync_pol;
+ u32 hsync_pol;
+ u8 vic;
+ char *name;
+};
+
+#define MAX_PPS_NUM 96 /* PPS96 through PPS127 are reverved until DSC v1.2a */
+struct dp_dsc {
+ bool enable;
+ u8 slice_count;
+ u16 chunk_size;
+ u8 pps[MAX_PPS_NUM]; /* DSC Picture Paremeter Set */
+};
+
+struct dp_video_info {
+ struct dp_support_video vm;
+ unsigned int bpc; /* bits per component */
+
+ u32 sst_id; /* dp_sst_idx_t - 1 */
+ enum dynamic_range_type dyn_range;
+
+ /* DSC */
+ struct dp_dsc dsc;
+};
+
+extern const struct dp_support_video support_videos[];
+
+/*************** CP CAL APIs exposed to DP driver ***************/
+struct dp_regs {
+ void __iomem *link_addr;
+ void __iomem *phy_addr;
+};
+
+/* DP0, DP1 */
+typedef enum dp_regs_id {
+ REGS_DP0_ID = 0,
+ REGS_DP1_ID,
+ REGS_DP_ID_MAX
+} dp_regs_id_t;
+
+typedef enum dp_regs_type {
+ REGS_DP_LINK = 0,
+ REGS_DP_PHY,
+ REGS_DP_TYPE_MAX
+} dp_regs_type_t;
+
+typedef enum dp_irq_type {
+ DP_IRQ_HPD_IRQ_FLAG = (1 << 11),
+ DP_IRQ_HPD_CHG = (1 << 10),
+ DP_IRQ_HPD_LOST = (1 << 9),
+ DP_IRQ_HPD_PLUG_INT = (1 << 8),
+ DP_IRQ_MAPI_FIFO_UNDER_FLOW = (1 << 8),
+} dp_irq_type_t;
+
+typedef enum dp_irq_reg_type {
+ DP_IRQ_REG_SYSTEM,
+
+ DP_IRQ_REG_SST1_SET0,
+ DP_IRQ_REG_SST2_SET0,
+ DP_IRQ_REG_SST3_SET0,
+ DP_IRQ_REG_SST4_SET0,
+
+ DP_IRQ_REG_SST1_SET1,
+ DP_IRQ_REG_SST2_SET1,
+ DP_IRQ_REG_SST3_SET1,
+ DP_IRQ_REG_SST4_SET1,
+} dp_irq_reg_type_t;
+
+u32 dp_reg_read_vcpi_timeslot(u32 id, u32 index);
+
+void dp_reg_sw_reset(u32 id);
+void dp_reg_phy_reset(u32 id, u32 en);
+void dp_reg_phy_init_setting(u32 id);
+u32 dp_reg_phy_get_link_bw(u32 id);
+void dp_reg_phy_set_link_bw(u32 id, u8 link_rate);
+void dp_reg_phy_mode_setting(u32 id);
+void dp_reg_wait_phy_pll_lock(u32 id);
+void dp_reg_phy_disable(u32 id);
+void dp_reg_set_lane_count(u32 id, u8 lane_cnt);
+u32 dp_reg_get_lane_count(u32 id);
+void dp_reg_set_enhanced_mode(u32 id, u32 en);
+void dp_reg_set_training_pattern(u32 id, dp_training_pattern pattern);
+void dp_reg_scrambling_enable(u32 id, bool status);
+void dp_reg_set_voltage_and_pre_emphasis(u32 id, u8 *voltage, u8 *pre_emphasis);
+void dp_reg_set_phy_tune(u32 id, u32 phy_lane_num, u32 amp_lvl,
+ u32 pre_emp_lvl);
+void dp_reg_init(u32 id);
+void dp_reg_deinit(u32 id);
+void dp_reg_set_hpd_interrupt(u32 id, u32 en);
+void dp_reg_set_plug_interrupt(u32 id, u32 en);
+u32 dp_reg_get_hpd_status(u32 id);
+u32 dp_reg_get_int_and_clear(u32 id, u32 irq_reg);
+void dp_reg_set_video_config(u32 id, struct dp_video_info dp_video_info);
+void dp_reg_set_bist_video_config(u32 id, struct dp_video_info dp_video_info,
+ u8 type);
+void dp_reg_start(u32 id, u32 sst_id);
+void dp_reg_stop(u32 id, u32 sst_id);
+int dp_reg_aux_write(u32 id, u32 comm, u32 address, u32 length, u8 *data);
+int dp_reg_aux_read(u32 id, u32 comm, u32 address, u32 length, u8 *data);
+int dp_reg_dpcd_write_burst(u32 id, u32 address, u32 length, u8 *data);
+int dp_reg_dpcd_read_burst(u32 id, u32 address, u32 length, u8 *data);
+
+int dp_regs_desc_init(u32 dp_id, struct dp_regs *regs);
+
+void dp_reg_set_mst_en(u32 id, u32 en);
+void dp_reg_set_strm_x_y(u32 id, u32 sst_id, u32 x_val, u32 y_val);
+void dp_reg_set_vcpi_timeslot(u32 id, u32 sst_id, u32 start, u32 size);
+void dp_reg_remove_vcpi_timeslot(u32 id, u32 sst_id);
+int dp_reg_wait_for_vcpi_update(u32 id);
+void dp_reg_set_mst_always_sent_act(u32 id, u32 en);
+int dp_reg_get_link_clock(u32 id);
+bool dp_reg_get_sst_pstate(u32 id);
+void dp_reg_set_dsc_fec(u32 id, u32 en);
+
+/* log messages */
+#define dp_msg(func, dev, fmt, ...) \
+ func(dev, fmt, ##__VA_ARGS__)
+
+#define dp_log_enter(dev) dp_msg(DRM_DEV_DEBUG, dev, "%s", "+")
+#define dp_log_exit(dev) dp_msg(DRM_DEV_DEBUG, dev, "%s", "-")
+
+#define dp_log_dbg(dev, f, ...) dp_msg(DRM_DEV_DEBUG_DRIVER, dev, f, ##__VA_ARGS__)
+#define dp_log_kms(dev, f, ...) dp_msg(DRM_DEV_DEBUG_KMS, dev, f, ##__VA_ARGS__)
+#define dp_log_info(dev, f, ...) dp_msg(DRM_DEV_INFO, dev, f, ##__VA_ARGS__)
+#define dp_log_err(dev, f, ...) dp_msg(DRM_DEV_ERROR, dev, f, ##__VA_ARGS__)
+#define dp_log_errl(dev, f, ...) dp_msg(DRM_DEV_ERROR_RATELIMITED, dev, f, ##__VA_ARGS__)
+
+#define MAX_LANE_CNT 4
+
+enum dp_state {
+ DP_STATE_OFF,
+ DP_STATE_LINKED,
+ DP_STATE_ON,
+};
+
+enum hotplug_state {
+ HPD_UNPLUG = 0,
+ HPD_PLUG = BIT(0),
+ HPD_IRQ = BIT(1),
+ HPD_CHECK = BIT(2),
+ HPD_LT_FAILED = BIT(3),
+};
+
+typedef enum exynos_dp_sst_index {
+ DP_SST_UNKNOWN = 0,
+ DP_SST_1 = 1,
+ DP_SST_2 = 2,
+ DP_SST_3 = 3,
+ DP_SST_4 = 4,
+ DP_SST_MAX = DP_SST_4,
+} dp_sst_idx_t;
+
+typedef enum exynos_dp_debug_lt {
+ DEBUG_LT_NORMAL,
+ DEBUG_LT_DPCD_READ_FAIL,
+ DEBUG_LT_FAIL_FIXED_BW,
+ DEBUG_LT_FAIL_TRY_BW_DOWN,
+ DEBUG_LT_BW_LOWER,
+ DEBUG_LT_BW_NO_STEPDOWN,
+} dp_debug_lt;
+
+struct exynos_dp_video_info {
+ struct videomode vm; /* clock & resolution & porch */
+ bool hsync_pol; /* polarity */
+ bool vsync_pol; /* polarity */
+ unsigned int bpc; /* bits per component */
+ unsigned int vrefresh; /* vrefresh freq */
+
+ u32 sst_id; /* dp_sst_idx_t - 1 */
+ enum dynamic_range_type dyn_range;
+
+ /* DSC */
+ struct {
+ bool enable;
+ u8 slice_count;
+ u16 chunk_size;
+ struct drm_dsc_picture_parameter_set pps;
+ } dsc;
+};
+struct exynos_dp_lt_info {
+ int link_rate;
+ u8 max_link_lane;
+ u8 lane_cnt;
+ u8 enhanced_frame_cap;
+
+ u8 voltage_swing[MAX_LANE_CNT];
+ u8 pre_emphasis[MAX_LANE_CNT];
+};
+
+struct exynos_dp_subdev {
+ struct device *dev;
+
+ enum chip_version version;
+
+ /* Filled by me: drm related */
+ struct drm_device *drm_dev;
+ /* filled from super-dev: drm related */
+ struct drm_dp_aux aux;
+ void (*detect)(struct device *);
+ void (*mst_config)(struct device *, bool is_mst);
+ void (*mst_irq)(struct device *);
+ u8 dpcd[DP_RECEIVER_CAP_SIZE];
+ u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE];
+ u8 fec_capable;
+ bool force_dsc_dis; /* dsc disable forcibly */
+ bool mst_dsc_en; /* dsc enable w/ mst_hub over DP-SST */
+ u8 downstream_ports[DP_RECEIVER_CAP_SIZE];
+ struct list_head mst_list;
+
+ u32 id;
+ int irq;
+ int hpd_gpio;
+ int hpd_gpio_irq;
+
+ struct phy *phy;
+ spinlock_t slock;
+
+ struct workqueue_struct *dp_wq;
+ struct delayed_work hpd_plug_work;
+ struct delayed_work hpd_unplug_work;
+ struct delayed_work hpd_irq_work;
+
+ struct dp_regs regs;
+
+ enum dp_state state;
+ struct exynos_dp_lt_info lt_info;
+
+ int hpd_state;
+ bool training_state;
+ u32 bist_use;
+ enum test_pattern bist_type;
+ struct exynos_dp_video_info vi[DP_SST_MAX];
+
+ struct mutex lock;
+ struct mutex pwlock;
+
+ /* DEBUG */
+ struct {
+ bool debug_hpd_irq;
+ dp_debug_lt debug_lt;
+ } dp_debug;
+};
+
+#define MST_SLOT_INIT_NUM 1
+struct dp_mst_config {
+ struct list_head list;
+ int sst_id;
+ int num_slots;
+
+ u32 x_val;
+ u32 y_val;
+};
+
+
+enum msg_aux_client_type {
+ MSG_AUX_CLIENT, /* supported messaging aux client */
+ SKIP_MSG_AUX_CLIENT, /* not supported messaing aux client */
+ FIX_TOPOLOGY_WITH_MST_HUB,
+ /* This is the mode for testing
+ * not support messaing aux client with DP MST HUB.
+ * The MST topology contsructs by reading Device-tree,
+ * not using Messaing aux client.
+ * After then the sequence works as a device that
+ * supports Messaing aux client. it's just for test.
+ */
+};
+
+struct dp_encoder {
+ struct drm_encoder base;
+ struct drm_encoder *remote_base;
+ struct dp_connector *dp_connector;
+ struct exynos_drm_dp *dp;
+ dp_sst_idx_t sst_idx;
+ enum exynos_drm_output_type output_type;
+
+ int mst_pbn;
+ int mst_slots;
+};
+
+struct dp_connector {
+ struct drm_connector base;
+ struct drm_connector *remote_base;
+ struct drm_dp_mst_port *port;
+ struct dp_encoder *dp_encoder;
+ struct exynos_drm_dp *dp;
+ enum drm_connector_status status;
+
+ int native_mode;
+ bool native_only;
+};
+
+struct drm_remote {
+ struct drm_device *drm_dev;
+ int (*detect)(struct dp_connector *rdp_con,
+ enum drm_connector_status status,
+ struct drm_device *drm_dev);
+};
+
+struct exynos_drm_dp {
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ struct drm_device *drm_dev;
+ struct device *dev;
+
+ u32 id;
+ enum exynos_drm_output_type output_type;
+ struct display_timings *timings;
+ struct drm_display_mode cur_mode;
+ /* mst */
+ bool is_mst;
+ unsigned long used_sst;
+ struct drm_dp_mst_topology_mgr mst_mgr;
+ enum msg_aux_client_type skip_messaging_aux_client;
+
+ struct exynos_dp_subdev *subdev;
+
+ /* vmst */
+ struct drm_bridge bridge;
+ struct drm_remote remote[DP_SST_MAX];
+};
+
+#if IS_ENABLED(CONFIG_DRM_EXYNOS9_DP)
+int exynos_drm_dp_dump_sfr(struct exynos_dp_subdev *subdev);
+void exynos_drm_dp_stream_enable(struct exynos_dp_subdev *dp, dp_sst_idx_t sst_idx);
+void exynos_drm_dp_stream_disable(struct exynos_dp_subdev *dp, dp_sst_idx_t sst_idx);
+bool exynos_drm_dp_is_hpd_connected(struct exynos_dp_subdev *dp);
+void exynos_drm_dp_videomode_set(struct exynos_dp_subdev *dp,
+ struct exynos_dp_video_info *vi, dp_sst_idx_t sst_idx);
+void exynos_drm_dp_hpd_en(struct exynos_dp_subdev *dp);
+int exynos_drm_dp_link_training(struct exynos_dp_subdev *dp);
+void exynos_drm_dp_set_normal_data(struct exynos_dp_subdev *dp);
+void exynos_drm_dp_dpcd_status_dump(struct exynos_dp_subdev *dp);
+
+uint32_t exynos_drm_dp_find_possible_crtc(struct exynos_drm_dp *dp, int sst_idx);
+dp_sst_idx_t exynos_drm_dp_get_sst_idx(struct drm_encoder *encoder);
+int exynos_drm_dp_add_timings(struct exynos_drm_dp *dp,
+ struct drm_connector *connector);
+struct drm_connector *exynos_drm_dp_find_connector
+ (struct drm_encoder *encoder);
+void exynos_drm_dp_to_videoinfo(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct exynos_dp_video_info *vi);
+bool exynos_drm_dp_dsc_enable(struct drm_encoder *encoder,
+ struct exynos_dp_video_info *vi,
+ dp_sst_idx_t sst_idx);
+void exynos_drm_dp_dsc_disable(struct drm_encoder *encoder,
+ dp_sst_idx_t sst_idx);
+#else
+static inline int exynos_drm_dp_dump_sfr(struct exynos_dp_subdev *subdev)
+{
+ return 0;
+}
+static inline void exynos_drm_dp_stream_enable(struct exynos_dp_subdev *dp, dp_sst_idx_t sst_idx)
+{
+}
+static inline void exynos_drm_dp_stream_disable(struct exynos_dp_subdev *dp, dp_sst_idx_t sst_idx)
+{
+}
+static inline bool exynos_drm_dp_is_hpd_connected(struct exynos_dp_subdev *dp)
+{
+ return false;
+}
+static inline void exynos_drm_dp_videomode_set(struct exynos_dp_subdev *dp,
+ struct exynos_dp_video_info *vi, dp_sst_idx_t sst_idx)
+{
+}
+static inline void exynos_drm_dp_hpd_en(struct exynos_dp_subdev *dp)
+{
+}
+static inline int exynos_drm_dp_link_training(struct exynos_dp_subdev *dp)
+{
+ return 0;
+}
+static inline void exynos_drm_dp_set_normal_data(struct exynos_dp_subdev *dp)
+{
+}
+static inline void exynos_drm_dp_dpcd_status_dump(struct exynos_dp_subdev *dp)
+{
+}
+static inline uint32_t exynos_drm_dp_find_possible_crtc(struct exynos_drm_dp *dp, int sst_idx)
+{
+ return 0;
+}
+static inline dp_sst_idx_t exynos_drm_dp_get_sst_idx(struct drm_encoder *encoder)
+{
+ return DP_SST_UNKNOWN;
+}
+static inline int exynos_drm_dp_add_timings(struct exynos_drm_dp *dp,
+ struct drm_connector *connector)
+{
+ return 0;
+}
+static inline struct drm_connector *exynos_drm_dp_find_connector
+ (struct drm_encoder *encoder)
+{
+ return NULL;
+}
+static inline void exynos_drm_dp_to_videoinfo(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct exynos_dp_video_info *vi)
+{
+}
+static inline bool exynos_drm_dp_dsc_enable(struct drm_encoder *encoder,
+ struct exynos_dp_video_info *vi,
+ dp_sst_idx_t sst_idx)
+{
+ return false;
+}
+static inline void exynos_drm_dp_dsc_disable(struct drm_encoder *encoder,
+ dp_sst_idx_t sst_idx)
+{
+}
+
+#endif
+
+#if IS_ENABLED(CONFIG_DRM_EXYNOS9_DP_MST)
+extern int exynos_drm_dp_mst_init(struct dp_connector *dp_connector);
+extern void exynos_drm_dp_mst_dump_topology(struct seq_file *m,
+ struct drm_dp_mst_topology_mgr *mgr);
+extern bool exynos_dp_mst_cap(struct exynos_dp_subdev *dp);
+#else
+static inline int exynos_drm_dp_mst_init(struct dp_connector *dp_connector)
+{
+ return 0;
+}
+
+static inline void exynos_drm_dp_mst_dump_topology(struct seq_file *m,
+ struct drm_dp_mst_topology_mgr *mgr)
+{
+}
+
+static inline bool exynos_dp_mst_cap(struct exynos_dp_subdev *dp)
+{
+ return false;
+}
+#endif
+
+#if IS_ENABLED(CONFIG_DRM_EXYNOS9_DP_MST_TOPOLOGY)
+extern int exynos_drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_device *dev, struct drm_dp_aux *aux,
+ int max_dpcd_transaction_bytes, int max_payloads,
+ int max_lane_count, int max_link_rate,
+ int conn_base_id);
+extern void exynos_drm_dp_mst_mapping_sst_id_to_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port, int sst_idx);
+extern int exynos_drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr);
+extern int exynos_drm_dp_clear_update_payload(struct drm_dp_mst_topology_mgr *mgr);
+#else
+static inline int exynos_drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_device *dev, struct drm_dp_aux *aux,
+ int max_dpcd_transaction_bytes, int max_payloads,
+ int max_lane_count, int max_link_rate,
+ int conn_base_id)
+
+{
+ return 0;
+}
+static inline void exynos_drm_dp_mst_mapping_sst_id_to_vcpi(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_port *port, int sst_idx)
+{
+}
+static inline int exynos_drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
+{
+ return 0;
+}
+static inline int exynos_drm_dp_clear_update_payload(struct drm_dp_mst_topology_mgr *mgr)
+{
+ return 0;
+}
+#endif
+
+#define to_subdev(nm) container_of(nm, struct exynos_dp_subdev, nm)
+#define to_encoder(nm) container_of(nm, struct dp_encoder, base)
+#define to_connector(nm) container_of(nm, struct dp_connector, base)
+#define encoder_to_dp(nm) to_encoder(nm)->dp
+#define connector_to_dp(nm) to_connector(nm)->dp
+#define to_dp_sst(id, sst) ((id * DP_SST_4) + (sst - 1))
+
+#endif
new file mode 100644
@@ -0,0 +1,586 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Samsung ExynosAuto DRM Display Port Link_Training driver.
+ *
+ * Copyright (C) 2020 Samsung Electronics Co.Ltd
+ */
+
+#include <linux/err.h>
+
+#include "exynos_drm_dp.h"
+
+static void exynos_dp_dump_symbol_error(struct exynos_dp_subdev *dp)
+{
+ int i;
+ struct device *dev = dp->dev;
+ u8 info[MAX_LANE_CNT * 2] = {0, };
+ unsigned int offset = DP_SINK_COUNT + 0x10;
+ size_t size = sizeof(info);
+
+ drm_dp_dpcd_read(&dp->aux, offset, info, size);
+ for (i = 0; i < size; i = i + 2)
+ dp_log_info(dev, "SYMBOL_ERROR_COUNT(addr:%#4x), %02x, %02x\n",
+ offset, info[i], info[i + 1]);
+};
+
+static void exynos_dp_set_phy_training_lane_set(u32 id,
+ u8 *dpcd_buf, struct exynos_dp_lt_info *lt_info)
+{
+ int i;
+ u8 *drive_current = lt_info->voltage_swing;
+ u8 *pre_emphasis = lt_info->pre_emphasis;
+ u8 lane_cnt = lt_info->lane_cnt;
+ u8 max_reach_value = 0;
+
+ for (i = 0; i < lane_cnt; i++) {
+ dp_reg_set_phy_tune(id, i, drive_current[i],
+ pre_emphasis[i] >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
+
+ if (drive_current[i] >= DP_TRAIN_VOLTAGE_SWING_LEVEL_2)
+ max_reach_value |= (DP_TRAIN_MAX_SWING_REACHED);
+ else
+ max_reach_value &= ~(DP_TRAIN_MAX_SWING_REACHED);
+
+ if (pre_emphasis[i] >= DP_TRAIN_PRE_EMPH_LEVEL_2)
+ max_reach_value |= (DP_TRAIN_MAX_PRE_EMPHASIS_REACHED);
+ else
+ max_reach_value &= ~(DP_TRAIN_MAX_PRE_EMPHASIS_REACHED);
+
+ dpcd_buf[i] = drive_current[i] | pre_emphasis[i] | max_reach_value;
+ }
+}
+
+static void exynos_dp_dsc_prepare(struct exynos_dp_subdev *dp, bool enable)
+{
+ int ret;
+
+ if (!drm_dp_sink_supports_dsc(dp->dsc_dpcd))
+ return;
+
+ ret = drm_dp_dpcd_writeb(&dp->aux, DP_DSC_ENABLE,
+ enable ? DP_DECOMPRESSION_EN : 0);
+ if (ret < 0) {
+ dp_log_err(dp->dev,
+ "Failed to %s sink decompression\n",
+ enable ? "enable" : "disable");
+ dp->dsc_dpcd[0] = 0; /* deactivate DSC/FEC capable */
+ return;
+ } else {
+ dp_log_info(dp->dev,
+ "Success to %s sink decompression\n",
+ enable ? "enable" : "disable");
+ }
+
+ /* Set FEC ready of DP source device */
+ if (drm_dp_sink_supports_fec(dp->fec_capable)) {
+ /* Write 1 to clear the FEC_STATUS bit */
+ drm_dp_dpcd_writeb(&dp->aux, DP_FEC_STATUS,
+ DP_FEC_DECODE_EN_DETECTED | DP_FEC_DECODE_DIS_DETECTED);
+
+ ret = drm_dp_dpcd_writeb(&dp->aux, DP_FEC_CONFIGURATION,
+ enable ? DP_FEC_READY : 0);
+ if (ret < 0) {
+ dp_log_err(dp->dev,
+ "Failed to %s sink fec\n",
+ enable ? "enable" : "disable");
+ return;
+ }
+ dp_log_info(dp->dev,
+ "success to %s sink fec\n",
+ enable ? "enable" : "disable");
+ dp_reg_set_dsc_fec(dp->id, enable);
+ }
+}
+
+static void exynos_dp_phy_init(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ u32 id = dp->id;
+ u8 dpcd_val[2];
+
+ dpcd_val[0] = dp->lt_info.link_rate;
+ dpcd_val[1] = dp->lt_info.lane_cnt;
+
+ dp_reg_phy_reset(id, 1);
+ dp_reg_phy_init_setting(id);
+
+ dp_reg_phy_set_link_bw(id, dpcd_val[0]);
+
+ dp_reg_phy_mode_setting(id);
+
+ dp_reg_set_lane_count(id, dpcd_val[1]);
+
+ dp_log_info(dev, "link_rate = %d Mbps, lane_cnt = %x\n",
+ drm_dp_bw_code_to_link_rate(dpcd_val[0])/100, dpcd_val[1]);
+
+ if (dp->lt_info.enhanced_frame_cap) {
+ dp_reg_set_enhanced_mode(id, 1);
+ dpcd_val[1] |= DP_ENHANCED_FRAME_CAP;
+ }
+
+ /* wait for 60us - Exynosauto9 DPTX PHY spec */
+ udelay(60);
+
+ dp_reg_phy_reset(id, 0);
+
+ drm_dp_dpcd_write(&dp->aux, DP_LINK_BW_SET, dpcd_val, 2);
+
+ dp_reg_wait_phy_pll_lock(id);
+
+ /* SCRAMBLING_DISABLE, TRAINING_PATTERN_1 */
+ dp_reg_set_training_pattern(id, TRAINING_PATTERN_1);
+ dp_reg_scrambling_enable(id, 0);
+
+ dpcd_val[0] = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, dpcd_val[0]);
+}
+
+static int exynos_dp_reduced_link_rate(u8 link_rate, u8 test_mode)
+{
+ if (test_mode == DEBUG_LT_BW_NO_STEPDOWN)
+ link_rate = DP_LINK_BW_1_62;
+
+ switch (link_rate) {
+ case DP_LINK_BW_8_1:
+ return DP_LINK_BW_5_4;
+ case DP_LINK_BW_5_4:
+ return DP_LINK_BW_2_7;
+ case DP_LINK_BW_2_7:
+ return DP_LINK_BW_1_62;
+ case DP_LINK_BW_1_62:
+ default:
+ return -EINVAL;
+ }
+}
+
+static bool exynos_drm_dp_get_lt_info(struct exynos_dp_subdev *dp)
+{
+ int link_rate;
+ u8 lane_cnt = drm_dp_max_lane_count(dp->dpcd);
+ u8 enhanced_frame_cap;
+
+ link_rate = dp->dpcd[DP_MAX_LINK_RATE];
+ lane_cnt = min(lane_cnt, dp->lt_info.max_link_lane);
+
+ if (!link_rate || !lane_cnt)
+ return false;
+
+ if (dp->dp_debug.debug_lt == DEBUG_LT_BW_LOWER) {
+ link_rate = exynos_dp_reduced_link_rate(link_rate, dp->dp_debug.debug_lt);
+
+ if (link_rate < 0)
+ return false;
+ }
+
+ if (!exynos_dp_mst_cap(dp))
+ enhanced_frame_cap = drm_dp_enhanced_frame_cap(dp->dpcd);
+ else
+ enhanced_frame_cap = 0;
+
+ dp->lt_info.link_rate = (u8)link_rate;
+ dp->lt_info.lane_cnt = lane_cnt;
+ dp->lt_info.enhanced_frame_cap = enhanced_frame_cap;
+
+ return true;
+}
+
+/*
+ * About retry times.
+ * The DP 1.4 spec defines retry times in Figure 3-20 : Clock Recovery Sequence of Link Training.
+ * Any one of the following true: - LANEx_CR_DONE?
+ * #1: Maximum voltage swing reached?
+ * #2: AUX w/o LANEx_CR_DONE with the same ADJ_REQ 'five times'? - voltage_retry_no
+ * #3: AUX_ACK w/o LANEx_CR_DONE '10 times'? - cr_retry_no
+ * LT_CR_RETRY_CNT = Clock Recovery Retry
+ * LT_VS_RETRY_CNT = Voltage_Swing Retry
+ */
+#define LT_CR_RETRY_CNT 10
+#define LT_VS_RETRY_CNT 5
+static bool
+exynos_drm_dp_lt_clock_recovery(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ struct exynos_dp_lt_info lt_info;
+ u32 id = dp->id;
+
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ u8 lane_cnt = dp->lt_info.lane_cnt;
+ u8 dpcd_buf[MAX_LANE_CNT] = {0, };
+
+ int cr_retry_no;
+ int voltage_retry_no = 1;
+ int i;
+
+ dp_log_info(dev, "Start Clock Recovery(CR)\n");
+
+ for (i = 0; i < MAX_LANE_CNT; i++) {
+ lt_info.voltage_swing[i] = 0;
+ lt_info.pre_emphasis[i] = 0;
+ }
+ lt_info.lane_cnt = lane_cnt;
+
+ for (cr_retry_no = 0;
+ cr_retry_no < LT_CR_RETRY_CNT; cr_retry_no++) {
+
+ exynos_dp_set_phy_training_lane_set(id, dpcd_buf, <_info);
+
+ dp_log_dbg(dev, "(CR) Try TRAINING_LANEx_SET: %02x %02x %02x %02x\n",
+ dpcd_buf[0], dpcd_buf[1], dpcd_buf[2], dpcd_buf[3]);
+ drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dpcd_buf, lane_cnt);
+
+ drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd);
+
+ drm_dp_dpcd_read_link_status(&dp->aux, link_status);
+
+ if (drm_dp_clock_recovery_ok(link_status, lane_cnt)) {
+ goto LT_CR_DONE;
+ }
+
+ for (i = 0; i < lane_cnt; i++) {
+ if (lt_info.voltage_swing[i] == DP_TRAIN_VOLTAGE_SWING_LEVEL_3) {
+ dp_log_info(dev, "LANE_CR_FAIL - Maximum Voltage swing reached(%02x)\n",
+ lt_info.voltage_swing[i]);
+ goto LT_CR_FAIL;
+ }
+
+ lt_info.voltage_swing[i] = drm_dp_get_adjust_request_voltage(link_status, i);
+ lt_info.pre_emphasis[i] = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ }
+
+ for (i = 0; i < lane_cnt; i++) {
+ if (lt_info.voltage_swing[i] == dp->lt_info.voltage_swing[i]) {
+ if (voltage_retry_no == LT_VS_RETRY_CNT) {
+ dp_log_info(dev, "LANE_CR_FAIL - Same ADJ_REQ_Voltage %d times(%02x)\n",
+ voltage_retry_no, lt_info.voltage_swing[i]);
+ goto LT_CR_FAIL;
+ } else {
+ voltage_retry_no++;
+ break;
+ }
+ } else if (i == lane_cnt-1) {
+ voltage_retry_no = 1;
+ }
+ }
+ memcpy(&dp->lt_info.voltage_swing, lt_info.voltage_swing,
+ sizeof(dp->lt_info.voltage_swing));
+ }
+
+ dp_log_info(dev, "LANE_CR_FAIL - Maximum Retry %d times\n", cr_retry_no);
+
+LT_CR_FAIL:
+ dp_log_info(dev, "TRAINING_LANEx_SET: %02x %02x %02x %02x\n",
+ dpcd_buf[0], dpcd_buf[1], dpcd_buf[2], dpcd_buf[3]);
+ return false;
+
+LT_CR_DONE:
+ memcpy(&dp->lt_info.voltage_swing, lt_info.voltage_swing,
+ sizeof(dp->lt_info.voltage_swing));
+ memcpy(&dp->lt_info.pre_emphasis, lt_info.pre_emphasis,
+ sizeof(dp->lt_info.pre_emphasis));
+ dp_log_info(dev, "LANE_CR_DONE - TRAINING_LANEx_SET : %02x %02x %02x %02x\n",
+ dpcd_buf[0], dpcd_buf[1], dpcd_buf[2], dpcd_buf[3]);
+ return true;
+}
+
+static u8
+exynos_drm_dp_lt_training_pattern(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ u32 id = dp->id;
+ u8 link_rate = dp->dpcd[DP_MAX_LINK_RATE];
+
+ if (drm_dp_tps4_supported(dp->dpcd) && link_rate == DP_LINK_BW_8_1) {
+ dp_reg_set_training_pattern(id, TRAINING_PATTERN_4);
+ dp_reg_scrambling_enable(id, 1);
+ dp_log_dbg(dev, "TPS4_supported\n");
+ return DP_TRAINING_PATTERN_4;
+ }
+
+ dp_reg_scrambling_enable(id, 0);
+
+ if (drm_dp_tps3_supported(dp->dpcd) && link_rate >= DP_LINK_BW_5_4) {
+ dp_reg_set_training_pattern(id, TRAINING_PATTERN_3);
+ dp_log_dbg(dev, "TPS3_supported\n");
+ return DP_TRAINING_PATTERN_3;
+ }
+
+ dp_reg_set_training_pattern(id, TRAINING_PATTERN_2);
+ dp_log_dbg(dev, "TPS2_supported\n");
+ return DP_TRAINING_PATTERN_2;
+}
+
+#define EQ_RETRY_CNT 5
+static bool
+exynos_drm_dp_lt_equalization(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ struct exynos_dp_lt_info *lt_info = &dp->lt_info;
+ u32 id = dp->id;
+
+ u8 link_status[DP_LINK_STATUS_SIZE];
+ u8 lane_cnt = dp->lt_info.lane_cnt;
+ u8 dpcd_buf[MAX_LANE_CNT] = {0, };
+ u8 training_pattern;
+
+ int i;
+ int eq_retry_no;
+ bool cr_done, eq_done;
+
+ training_pattern = exynos_drm_dp_lt_training_pattern(dp);
+
+ dp_log_info(dev, "Start Equalization(EQ) - TPS%d supported\n",
+ (training_pattern > DP_TRAINING_PATTERN_3) ? 4 : training_pattern);
+
+ if (training_pattern != DP_TRAINING_PATTERN_4)
+ training_pattern |= DP_LINK_SCRAMBLING_DISABLE;
+
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, training_pattern);
+
+ for (eq_retry_no = 0; eq_retry_no < EQ_RETRY_CNT; eq_retry_no++) {
+
+ exynos_dp_set_phy_training_lane_set(id, dpcd_buf, lt_info);
+
+ dp_log_dbg(dev, "(EQ) Try TRAINING_LANEx_SET: %02x %02x %02x %02x\n",
+ dpcd_buf[0], dpcd_buf[1], dpcd_buf[2], dpcd_buf[3]);
+ drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dpcd_buf, lane_cnt);
+
+ drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd);
+
+ drm_dp_dpcd_read_link_status(&dp->aux, link_status);
+
+ cr_done = drm_dp_clock_recovery_ok(link_status, lane_cnt);
+
+ if (!cr_done) {
+ dp_log_info(dev, "LANE_CR_FAIL in LT_EQ\n");
+ goto LT_EQ_FAIL;
+ }
+
+ eq_done = drm_dp_channel_eq_ok(link_status, lane_cnt);
+
+ if (cr_done && eq_done)
+ goto LT_EQ_DONE;
+
+ exynos_dp_dump_symbol_error(dp);
+
+ for (i = 0; i < lane_cnt; i++)
+ lt_info->pre_emphasis[i] = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
+ }
+
+ dp_log_info(dev, "LANE_EQ_FAIL, Maximum Retry %d times\n", eq_retry_no);
+
+LT_EQ_FAIL:
+ dp_log_info(dev, "TRAINING_LANEx_SET: %02x %02x %02x %02x\n",
+ dpcd_buf[0], dpcd_buf[1], dpcd_buf[2], dpcd_buf[3]);
+ return false;
+
+LT_EQ_DONE:
+ dp_log_info(dev, "LANE_EQ_DONE - TRAINING_LANEx_SET: %02x %02x %02x %02x\n",
+ dpcd_buf[0], dpcd_buf[1], dpcd_buf[2], dpcd_buf[3]);
+ return true;
+}
+
+static bool exynos_dp_link_training_skip_reduce_bw_parse(struct device *dev)
+{
+ return of_property_read_bool(dev->of_node,
+ "samsung,lt_skip_reduce_bw");
+}
+
+#define LT_RETRY_CNT 4
+/**
+ * @cnotice
+ * @prdcode
+ * @unit_name{Exynos_dp_drv}
+ * @purpose Operate DP Full link training with RX
+ * @logic Operate DP Full link training with DP RX
+ * @params
+ * @param{in, dp, struct* ::exynos_dp_subdev, None}
+ * @endparam
+ * @retval{ret, int, 0, <= 0, < 0}
+ */
+static int exynos_dp_full_link_training(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ struct exynos_drm_dp *drm_dp = dev_get_drvdata(dev);
+ u32 id = dp->id;
+ int lt_retry_cnt;
+ int link_rate;
+ bool debug_lt =
+ ((dp->dp_debug.debug_lt == DEBUG_LT_FAIL_FIXED_BW) ||
+ (dp->dp_debug.debug_lt == DEBUG_LT_FAIL_TRY_BW_DOWN)) ? true : false;
+ bool cr_done = false;
+ bool eq_done = false;
+ bool lt_done = false;
+
+ if (!dp->hpd_state) {
+ dp_log_err(dev, "HPD is Low in Full Link Training\n");
+ return -EBUSY;
+ }
+
+ if (!exynos_drm_dp_get_lt_info(dp)) {
+ dp_log_err(dev, "Invalid values of link_rate(%x) or lane_cnt(%x)\n",
+ dp->dpcd[DP_MAX_LINK_RATE], drm_dp_max_lane_count(dp->dpcd));
+ return -EINVAL;
+ }
+ dp_log_info(dev, "Start Full Link Training + : DP_REV%02x\n", dp->dpcd[DP_DPCD_REV]);
+
+ /*
+ * This is work-around code when using DP MST serializer.
+ * Link-training succedds only in SST mode in DP MST with TPS4.
+ * Added this sequence for Link-Training called by HPD_IRQ event.
+ */
+ if (drm_dp_tps4_supported(dp->dpcd))
+ dp_reg_set_mst_en(id, 0);
+
+ for (lt_retry_cnt = 0; lt_retry_cnt < LT_RETRY_CNT; lt_retry_cnt++) {
+ if (!dp->hpd_state) {
+ dp_log_err(dev, "HPD is Low in Full Link_Training\n");
+ return -EINVAL;
+ }
+
+ exynos_dp_phy_init(dp);
+
+ cr_done = exynos_drm_dp_lt_clock_recovery(dp);
+
+ if (cr_done) {
+ eq_done = exynos_drm_dp_lt_equalization(dp);
+ if (eq_done && !debug_lt)
+ goto LINK_TRAINING_END;
+ }
+
+ if (exynos_dp_link_training_skip_reduce_bw_parse(dev) ||
+ (drm_dp->skip_messaging_aux_client == SKIP_MSG_AUX_CLIENT) ||
+ (dp->dp_debug.debug_lt == DEBUG_LT_FAIL_FIXED_BW))
+ goto LINK_TRAINING_END;
+
+ link_rate = exynos_dp_reduced_link_rate(dp->lt_info.link_rate, dp->dp_debug.debug_lt);
+
+ if (link_rate < 0)
+ goto LINK_TRAINING_END;
+ else
+ dp->lt_info.link_rate = link_rate;
+ }
+
+LINK_TRAINING_END:
+ lt_done = cr_done && eq_done && !debug_lt;
+ dp_log_info(dev, "%s Full Link Training -\n", lt_done ? "Finished" : "Failed");
+ drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET, 0);
+
+ return lt_done ? 0 : -EINVAL;
+}
+
+static void exynos_dp_mst_configure(struct exynos_dp_subdev *dp, bool is_mst)
+{
+ struct device *dev = dp->dev;
+
+ if (dp->mst_config && is_mst)
+ dp->mst_config(dev, is_mst);
+ dp_log_info(dev, "DP link use %s protocol\n", is_mst ? "MST" : "SST");
+}
+
+static int exynos_dp_get_dpcd_receiver_capability(struct exynos_dp_subdev *dp)
+{
+ u8 extend_val[DP_RECEIVER_CAP_SIZE] = {0,};
+ struct device_node *np = dp->dev->of_node;
+ int ret = 0;
+
+ ret = drm_dp_dpcd_read(&dp->aux, DP_DPCD_REV, dp->dpcd, sizeof(dp->dpcd));
+
+ if (ret < 0)
+ return ret;
+
+ if (dp->dp_debug.debug_lt == DEBUG_LT_DPCD_READ_FAIL)
+ return -EBUSY;
+
+ if (dp->dpcd[DP_TRAINING_AUX_RD_INTERVAL] & DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT) {
+ drm_dp_dpcd_read(&dp->aux, DP_DP13_DPCD_REV, extend_val, sizeof(extend_val));
+ if (extend_val[DP_DPCD_REV] > dp->dpcd[DP_DPCD_REV])
+ memcpy(dp->dpcd, extend_val, sizeof(extend_val));
+ }
+
+ /* clear first before read */
+ dp->dsc_dpcd[0] = 0;
+ dp->fec_capable = 0;
+ /* DSC and FEC DPCD if DP rev >= 1.4 but some MST_HUB uses 1.2 */
+ if (dp->dpcd[DP_DPCD_REV] >= DP_DPCD_REV_12) {
+ dp->force_dsc_dis =
+ of_property_read_bool(np, "samsung,force-dsc-dis");
+ dp->mst_dsc_en =
+ of_property_read_bool(np, "samsung,mst-dsc-en");
+ /* skip read DSC capability */
+ if (dp->force_dsc_dis)
+ return ret;
+
+ drm_dp_dpcd_read(&dp->aux, DP_DSC_SUPPORT, dp->dsc_dpcd,
+ sizeof(dp->dsc_dpcd));
+ drm_dp_dpcd_readb(&dp->aux, DP_FEC_CAPABILITY, &dp->fec_capable);
+ }
+ return ret;
+}
+
+/**
+ * @cnotice
+ * @prdcode
+ * @unit_name{Exynos_dp_drv}
+ * @purpose DP link training
+ * @logic Check the HPD state and disable DP irq<br>
+ * and than running the DP link_training<br>
+ * If the link_training is successful and in MST mode, excute MST opeation.
+ * @params
+ * @param{in, dp, struct* ::exynos_dp_subdev, None}
+ * @endparam
+ * @retval{ret, int, 0, <= 0, < 0}
+ */
+int exynos_drm_dp_link_training(struct exynos_dp_subdev *dp)
+{
+ struct device *dev = dp->dev;
+ bool is_mst;
+ int ret = 0;
+
+ if (!dp->hpd_state) {
+ dp_log_info(dev, "hpd is low in link training\n");
+ return 0;
+ }
+
+ dp_log_info(dev, "exynos_drm_dp_link_training\n");
+ dp_reg_set_plug_interrupt(dp->id, 0);
+
+ dp_log_info(dev, "exynos_drm_dp_link_training1\n");
+
+ ret = exynos_dp_get_dpcd_receiver_capability(dp);
+
+ if (ret < 0) {
+ dp_log_err(dev, "DPCD read error(ret=%d)\n", ret);
+ goto LT_END;
+ }
+
+ dp_log_info(dev, "exynos_drm_dp_link_training2\n");
+ mutex_lock(&dp->lock);
+ ret = exynos_dp_full_link_training(dp);
+ mutex_unlock(&dp->lock);
+
+ if (ret < 0) {
+ exynos_drm_dp_dpcd_status_dump(dp);
+ goto LT_END;
+ }
+
+ is_mst = exynos_dp_mst_cap(dp);
+ exynos_dp_dsc_prepare(dp, true);
+ dp_log_info(dev, "exynos_drm_dp_link_training3\n");
+
+ if (is_mst) {
+ exynos_drm_dp_set_normal_data(dp);
+
+ drm_dp_dpcd_read(&dp->aux, DP_DOWNSTREAM_PORT_0,
+ dp->downstream_ports,
+ sizeof(dp->downstream_ports));
+ dp_reg_set_mst_en(dp->id, 1);
+ }
+
+ if (dp->hpd_state == HPD_CHECK)
+ exynos_dp_mst_configure(dp, is_mst);
+
+LT_END:
+ dp_reg_set_plug_interrupt(dp->id, 1);
+ return ret;
+}
+
I didn't touch codes from vendor kernel. It's really bad form. Signed-off-by: Kwanghoon Son <k.son@samsung.com> --- drivers/gpu/drm/exynos/exynos_drm_dp.c | 5038 ++++++++++++++++++++ drivers/gpu/drm/exynos/exynos_drm_dp.h | 964 ++++ .../gpu/drm/exynos/exynos_drm_dp_link_training.c | 586 +++ 3 files changed, 6588 insertions(+)