1d76271d2SHyun Kwon // SPDX-License-Identifier: GPL-2.0
2d76271d2SHyun Kwon /*
3d76271d2SHyun Kwon * ZynqMP DisplayPort Driver
4d76271d2SHyun Kwon *
5d76271d2SHyun Kwon * Copyright (C) 2017 - 2020 Xilinx, Inc.
6d76271d2SHyun Kwon *
7d76271d2SHyun Kwon * Authors:
8d76271d2SHyun Kwon * - Hyun Woo Kwon <hyun.kwon@xilinx.com>
9d76271d2SHyun Kwon * - Laurent Pinchart <laurent.pinchart@ideasonboard.com>
10d76271d2SHyun Kwon */
11d76271d2SHyun Kwon
12da68386dSThomas Zimmermann #include <drm/display/drm_dp_helper.h>
13d76271d2SHyun Kwon #include <drm/drm_atomic_helper.h>
14d76271d2SHyun Kwon #include <drm/drm_crtc.h>
15d76271d2SHyun Kwon #include <drm/drm_device.h>
16d76271d2SHyun Kwon #include <drm/drm_edid.h>
1756167161SLaurent Pinchart #include <drm/drm_fourcc.h>
18d76271d2SHyun Kwon #include <drm/drm_modes.h>
19d76271d2SHyun Kwon #include <drm/drm_of.h>
20d76271d2SHyun Kwon
21d76271d2SHyun Kwon #include <linux/clk.h>
22d76271d2SHyun Kwon #include <linux/delay.h>
23d76271d2SHyun Kwon #include <linux/device.h>
243915f8bdSRandy Dunlap #include <linux/io.h>
25d76271d2SHyun Kwon #include <linux/module.h>
26d76271d2SHyun Kwon #include <linux/platform_device.h>
27d76271d2SHyun Kwon #include <linux/pm_runtime.h>
28d76271d2SHyun Kwon #include <linux/phy/phy.h>
29d76271d2SHyun Kwon #include <linux/reset.h>
306ca91bb4SLaurent Pinchart #include <linux/slab.h>
31d76271d2SHyun Kwon
32d76271d2SHyun Kwon #include "zynqmp_disp.h"
33d76271d2SHyun Kwon #include "zynqmp_dp.h"
34d76271d2SHyun Kwon #include "zynqmp_dpsub.h"
3583a956d3SLaurent Pinchart #include "zynqmp_kms.h"
36d76271d2SHyun Kwon
37d76271d2SHyun Kwon static uint zynqmp_dp_aux_timeout_ms = 50;
38d76271d2SHyun Kwon module_param_named(aux_timeout_ms, zynqmp_dp_aux_timeout_ms, uint, 0444);
39d76271d2SHyun Kwon MODULE_PARM_DESC(aux_timeout_ms, "DP aux timeout value in msec (default: 50)");
40d76271d2SHyun Kwon
41d76271d2SHyun Kwon /*
42d76271d2SHyun Kwon * Some sink requires a delay after power on request
43d76271d2SHyun Kwon */
44d76271d2SHyun Kwon static uint zynqmp_dp_power_on_delay_ms = 4;
45d76271d2SHyun Kwon module_param_named(power_on_delay_ms, zynqmp_dp_power_on_delay_ms, uint, 0444);
462d889db7SWei Yongjun MODULE_PARM_DESC(power_on_delay_ms, "DP power on delay in msec (default: 4)");
47d76271d2SHyun Kwon
48d76271d2SHyun Kwon /* Link configuration registers */
49d76271d2SHyun Kwon #define ZYNQMP_DP_LINK_BW_SET 0x0
50d76271d2SHyun Kwon #define ZYNQMP_DP_LANE_COUNT_SET 0x4
51d76271d2SHyun Kwon #define ZYNQMP_DP_ENHANCED_FRAME_EN 0x8
52d76271d2SHyun Kwon #define ZYNQMP_DP_TRAINING_PATTERN_SET 0xc
53d76271d2SHyun Kwon #define ZYNQMP_DP_SCRAMBLING_DISABLE 0x14
54d76271d2SHyun Kwon #define ZYNQMP_DP_DOWNSPREAD_CTL 0x18
55d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET 0x1c
56d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET_STREAM1 BIT(0)
57d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET_STREAM2 BIT(1)
58d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET_STREAM3 BIT(2)
59d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET_STREAM4 BIT(3)
60d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET_AUX BIT(7)
61d76271d2SHyun Kwon #define ZYNQMP_DP_SOFTWARE_RESET_ALL (ZYNQMP_DP_SOFTWARE_RESET_STREAM1 | \
62d76271d2SHyun Kwon ZYNQMP_DP_SOFTWARE_RESET_STREAM2 | \
63d76271d2SHyun Kwon ZYNQMP_DP_SOFTWARE_RESET_STREAM3 | \
64d76271d2SHyun Kwon ZYNQMP_DP_SOFTWARE_RESET_STREAM4 | \
65d76271d2SHyun Kwon ZYNQMP_DP_SOFTWARE_RESET_AUX)
66d76271d2SHyun Kwon
67d76271d2SHyun Kwon /* Core enable registers */
68d76271d2SHyun Kwon #define ZYNQMP_DP_TRANSMITTER_ENABLE 0x80
69d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_ENABLE 0x84
70d76271d2SHyun Kwon #define ZYNQMP_DP_FORCE_SCRAMBLER_RESET 0xc0
71d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION 0xf8
72d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_MAJOR_MASK GENMASK(31, 24)
73d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_MAJOR_SHIFT 24
74d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_MINOR_MASK GENMASK(23, 16)
75d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_MINOR_SHIFT 16
76d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_REVISION_MASK GENMASK(15, 12)
77d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_REVISION_SHIFT 12
78d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_PATCH_MASK GENMASK(11, 8)
79d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_PATCH_SHIFT 8
80d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_INTERNAL_MASK GENMASK(7, 0)
81d76271d2SHyun Kwon #define ZYNQMP_DP_VERSION_INTERNAL_SHIFT 0
82d76271d2SHyun Kwon
83d76271d2SHyun Kwon /* Core ID registers */
84d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID 0xfc
85d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_MAJOR_MASK GENMASK(31, 24)
86d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_MAJOR_SHIFT 24
87d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_MINOR_MASK GENMASK(23, 16)
88d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_MINOR_SHIFT 16
89d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_REVISION_MASK GENMASK(15, 8)
90d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_REVISION_SHIFT 8
91d76271d2SHyun Kwon #define ZYNQMP_DP_CORE_ID_DIRECTION GENMASK(1)
92d76271d2SHyun Kwon
93d76271d2SHyun Kwon /* AUX channel interface registers */
94d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_COMMAND 0x100
95d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_COMMAND_CMD_SHIFT 8
96d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_COMMAND_ADDRESS_ONLY BIT(12)
97d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_COMMAND_BYTES_SHIFT 0
98d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_WRITE_FIFO 0x104
99d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_ADDRESS 0x108
100d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_CLK_DIVIDER 0x10c
101d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_CLK_DIVIDER_AUX_FILTER_SHIFT 8
102d76271d2SHyun Kwon #define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE 0x130
103d76271d2SHyun Kwon #define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD BIT(0)
104d76271d2SHyun Kwon #define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REQUEST BIT(1)
105d76271d2SHyun Kwon #define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY BIT(2)
106d76271d2SHyun Kwon #define ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT BIT(3)
107d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_DATA 0x134
108d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE 0x138
109d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE_AUX_ACK (0)
110d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE_AUX_NACK BIT(0)
111d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE_AUX_DEFER BIT(1)
112d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE_I2C_ACK (0)
113d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE_I2C_NACK BIT(2)
114d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_CODE_I2C_DEFER BIT(3)
115d76271d2SHyun Kwon #define ZYNQMP_DP_AUX_REPLY_COUNT 0x13c
116d76271d2SHyun Kwon #define ZYNQMP_DP_REPLY_DATA_COUNT 0x148
117d76271d2SHyun Kwon #define ZYNQMP_DP_REPLY_DATA_COUNT_MASK 0xff
118d76271d2SHyun Kwon #define ZYNQMP_DP_INT_STATUS 0x3a0
119d76271d2SHyun Kwon #define ZYNQMP_DP_INT_MASK 0x3a4
120d76271d2SHyun Kwon #define ZYNQMP_DP_INT_EN 0x3a8
121d76271d2SHyun Kwon #define ZYNQMP_DP_INT_DS 0x3ac
122d76271d2SHyun Kwon #define ZYNQMP_DP_INT_HPD_IRQ BIT(0)
123d76271d2SHyun Kwon #define ZYNQMP_DP_INT_HPD_EVENT BIT(1)
124d76271d2SHyun Kwon #define ZYNQMP_DP_INT_REPLY_RECEIVED BIT(2)
125d76271d2SHyun Kwon #define ZYNQMP_DP_INT_REPLY_TIMEOUT BIT(3)
126d76271d2SHyun Kwon #define ZYNQMP_DP_INT_HPD_PULSE_DET BIT(4)
127d76271d2SHyun Kwon #define ZYNQMP_DP_INT_EXT_PKT_TXD BIT(5)
128d76271d2SHyun Kwon #define ZYNQMP_DP_INT_LIV_ABUF_UNDRFLW BIT(12)
129d76271d2SHyun Kwon #define ZYNQMP_DP_INT_VBLANK_START BIT(13)
130d76271d2SHyun Kwon #define ZYNQMP_DP_INT_PIXEL1_MATCH BIT(14)
131d76271d2SHyun Kwon #define ZYNQMP_DP_INT_PIXEL0_MATCH BIT(15)
132d76271d2SHyun Kwon #define ZYNQMP_DP_INT_CHBUF_UNDERFLW_MASK 0x3f0000
133d76271d2SHyun Kwon #define ZYNQMP_DP_INT_CHBUF_OVERFLW_MASK 0xfc00000
134d76271d2SHyun Kwon #define ZYNQMP_DP_INT_CUST_TS_2 BIT(28)
135d76271d2SHyun Kwon #define ZYNQMP_DP_INT_CUST_TS BIT(29)
136d76271d2SHyun Kwon #define ZYNQMP_DP_INT_EXT_VSYNC_TS BIT(30)
137d76271d2SHyun Kwon #define ZYNQMP_DP_INT_VSYNC_TS BIT(31)
138d76271d2SHyun Kwon #define ZYNQMP_DP_INT_ALL (ZYNQMP_DP_INT_HPD_IRQ | \
139d76271d2SHyun Kwon ZYNQMP_DP_INT_HPD_EVENT | \
140d76271d2SHyun Kwon ZYNQMP_DP_INT_CHBUF_UNDERFLW_MASK | \
141d76271d2SHyun Kwon ZYNQMP_DP_INT_CHBUF_OVERFLW_MASK)
142d76271d2SHyun Kwon
143d76271d2SHyun Kwon /* Main stream attribute registers */
144d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_HTOTAL 0x180
145d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_VTOTAL 0x184
146d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_POLARITY 0x188
147d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_POLARITY_HSYNC_SHIFT 0
148d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_POLARITY_VSYNC_SHIFT 1
149d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_HSWIDTH 0x18c
150d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_VSWIDTH 0x190
151d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_HRES 0x194
152d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_VRES 0x198
153d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_HSTART 0x19c
154d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_VSTART 0x1a0
155d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0 0x1a4
156d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK BIT(0)
157d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_RGB (0 << 1)
158d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_422 (5 << 1)
159d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_444 (6 << 1)
160d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_MASK (7 << 1)
161d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_DYNAMIC_RANGE BIT(3)
162d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_YCBCR_COLR BIT(4)
163d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_6 (0 << 5)
164d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_8 (1 << 5)
165d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_10 (2 << 5)
166d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_12 (3 << 5)
167d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_16 (4 << 5)
168d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_MASK (7 << 5)
169d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC1 0x1a8
170d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_MISC1_Y_ONLY_EN BIT(7)
171d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_M_VID 0x1ac
172d76271d2SHyun Kwon #define ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE 0x1b0
173d76271d2SHyun Kwon #define ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE_TU_SIZE_DEF 64
174d76271d2SHyun Kwon #define ZYNQMP_DP_MAIN_STREAM_N_VID 0x1b4
175d76271d2SHyun Kwon #define ZYNQMP_DP_USER_PIX_WIDTH 0x1b8
176d76271d2SHyun Kwon #define ZYNQMP_DP_USER_DATA_COUNT_PER_LANE 0x1bc
177d76271d2SHyun Kwon #define ZYNQMP_DP_MIN_BYTES_PER_TU 0x1c4
178d76271d2SHyun Kwon #define ZYNQMP_DP_FRAC_BYTES_PER_TU 0x1c8
179d76271d2SHyun Kwon #define ZYNQMP_DP_INIT_WAIT 0x1cc
180d76271d2SHyun Kwon
181d76271d2SHyun Kwon /* PHY configuration and status registers */
182d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_RESET 0x200
183d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_RESET_PHY_RESET BIT(0)
184d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_RESET_GTTX_RESET BIT(1)
185d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_RESET_PHY_PMA_RESET BIT(8)
186d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_RESET_PHY_PCS_RESET BIT(9)
187d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_RESET_ALL_RESET (ZYNQMP_DP_PHY_RESET_PHY_RESET | \
188d76271d2SHyun Kwon ZYNQMP_DP_PHY_RESET_GTTX_RESET | \
189d76271d2SHyun Kwon ZYNQMP_DP_PHY_RESET_PHY_PMA_RESET | \
190d76271d2SHyun Kwon ZYNQMP_DP_PHY_RESET_PHY_PCS_RESET)
191d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_0 0x210
192d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_1 0x214
193d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_2 0x218
194d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PREEMPHASIS_LANE_3 0x21c
195d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_0 0x220
196d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_1 0x224
197d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_2 0x228
198d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_VOLTAGE_DIFF_LANE_3 0x22c
199d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_CLOCK_SELECT 0x234
200d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_CLOCK_SELECT_1_62G 0x1
201d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_CLOCK_SELECT_2_70G 0x3
202d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_CLOCK_SELECT_5_40G 0x5
203d76271d2SHyun Kwon #define ZYNQMP_DP_TX_PHY_POWER_DOWN 0x238
204d76271d2SHyun Kwon #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_0 BIT(0)
205d76271d2SHyun Kwon #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_1 BIT(1)
206d76271d2SHyun Kwon #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_2 BIT(2)
207d76271d2SHyun Kwon #define ZYNQMP_DP_TX_PHY_POWER_DOWN_LANE_3 BIT(3)
208d76271d2SHyun Kwon #define ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL 0xf
209d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PRECURSOR_LANE_0 0x23c
210d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PRECURSOR_LANE_1 0x240
211d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PRECURSOR_LANE_2 0x244
212d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_PRECURSOR_LANE_3 0x248
213d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_POSTCURSOR_LANE_0 0x24c
214d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_POSTCURSOR_LANE_1 0x250
215d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_POSTCURSOR_LANE_2 0x254
216d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_POSTCURSOR_LANE_3 0x258
217d76271d2SHyun Kwon #define ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 0x24c
218d76271d2SHyun Kwon #define ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_1 0x250
219d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_STATUS 0x280
220d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_STATUS_PLL_LOCKED_SHIFT 4
221d76271d2SHyun Kwon #define ZYNQMP_DP_PHY_STATUS_FPGA_PLL_LOCKED BIT(6)
222d76271d2SHyun Kwon
223d76271d2SHyun Kwon /* Audio registers */
224d76271d2SHyun Kwon #define ZYNQMP_DP_TX_AUDIO_CONTROL 0x300
225d76271d2SHyun Kwon #define ZYNQMP_DP_TX_AUDIO_CHANNELS 0x304
226d76271d2SHyun Kwon #define ZYNQMP_DP_TX_AUDIO_INFO_DATA 0x308
227d76271d2SHyun Kwon #define ZYNQMP_DP_TX_M_AUD 0x328
228d76271d2SHyun Kwon #define ZYNQMP_DP_TX_N_AUD 0x32c
229d76271d2SHyun Kwon #define ZYNQMP_DP_TX_AUDIO_EXT_DATA 0x330
230d76271d2SHyun Kwon
231d76271d2SHyun Kwon #define ZYNQMP_DP_MAX_LANES 2
232d76271d2SHyun Kwon #define ZYNQMP_MAX_FREQ 3000000
233d76271d2SHyun Kwon
234d76271d2SHyun Kwon #define DP_REDUCED_BIT_RATE 162000
235d76271d2SHyun Kwon #define DP_HIGH_BIT_RATE 270000
236d76271d2SHyun Kwon #define DP_HIGH_BIT_RATE2 540000
237d76271d2SHyun Kwon #define DP_MAX_TRAINING_TRIES 5
238d76271d2SHyun Kwon #define DP_V1_2 0x12
239d76271d2SHyun Kwon
240d76271d2SHyun Kwon /**
241d76271d2SHyun Kwon * struct zynqmp_dp_link_config - Common link config between source and sink
242d76271d2SHyun Kwon * @max_rate: maximum link rate
243d76271d2SHyun Kwon * @max_lanes: maximum number of lanes
244d76271d2SHyun Kwon */
245d76271d2SHyun Kwon struct zynqmp_dp_link_config {
246d76271d2SHyun Kwon int max_rate;
247d76271d2SHyun Kwon u8 max_lanes;
248d76271d2SHyun Kwon };
249d76271d2SHyun Kwon
250d76271d2SHyun Kwon /**
251d76271d2SHyun Kwon * struct zynqmp_dp_mode - Configured mode of DisplayPort
252d76271d2SHyun Kwon * @bw_code: code for bandwidth(link rate)
253d76271d2SHyun Kwon * @lane_cnt: number of lanes
254d76271d2SHyun Kwon * @pclock: pixel clock frequency of current mode
255d76271d2SHyun Kwon * @fmt: format identifier string
256d76271d2SHyun Kwon */
257d76271d2SHyun Kwon struct zynqmp_dp_mode {
258d76271d2SHyun Kwon u8 bw_code;
259d76271d2SHyun Kwon u8 lane_cnt;
260d76271d2SHyun Kwon int pclock;
261d76271d2SHyun Kwon const char *fmt;
262d76271d2SHyun Kwon };
263d76271d2SHyun Kwon
264d76271d2SHyun Kwon /**
265d76271d2SHyun Kwon * struct zynqmp_dp_config - Configuration of DisplayPort from DTS
266d76271d2SHyun Kwon * @misc0: misc0 configuration (per DP v1.2 spec)
267d76271d2SHyun Kwon * @misc1: misc1 configuration (per DP v1.2 spec)
268d76271d2SHyun Kwon * @bpp: bits per pixel
269d76271d2SHyun Kwon */
270d76271d2SHyun Kwon struct zynqmp_dp_config {
271d76271d2SHyun Kwon u8 misc0;
272d76271d2SHyun Kwon u8 misc1;
273d76271d2SHyun Kwon u8 bpp;
274d76271d2SHyun Kwon };
275d76271d2SHyun Kwon
276d76271d2SHyun Kwon /**
277d76271d2SHyun Kwon * struct zynqmp_dp - Xilinx DisplayPort core
278d76271d2SHyun Kwon * @dev: device structure
279d76271d2SHyun Kwon * @dpsub: Display subsystem
280d76271d2SHyun Kwon * @iomem: device I/O memory for register access
281d76271d2SHyun Kwon * @reset: reset controller
282d76271d2SHyun Kwon * @irq: irq
28347e801bdSLaurent Pinchart * @bridge: DRM bridge for the DP encoder
284bd68b9b3SLaurent Pinchart * @next_bridge: The downstream bridge
285d76271d2SHyun Kwon * @config: IP core configuration from DTS
286d76271d2SHyun Kwon * @aux: aux channel
287d76271d2SHyun Kwon * @phy: PHY handles for DP lanes
288d76271d2SHyun Kwon * @num_lanes: number of enabled phy lanes
289d76271d2SHyun Kwon * @hpd_work: hot plug detection worker
290d76271d2SHyun Kwon * @status: connection status
291d76271d2SHyun Kwon * @enabled: flag to indicate if the device is enabled
292d76271d2SHyun Kwon * @dpcd: DP configuration data from currently connected sink device
293d76271d2SHyun Kwon * @link_config: common link configuration between IP core and sink device
294d76271d2SHyun Kwon * @mode: current mode between IP core and sink device
295d76271d2SHyun Kwon * @train_set: set of training data
296d76271d2SHyun Kwon */
297d76271d2SHyun Kwon struct zynqmp_dp {
298d76271d2SHyun Kwon struct device *dev;
299d76271d2SHyun Kwon struct zynqmp_dpsub *dpsub;
300d76271d2SHyun Kwon void __iomem *iomem;
301d76271d2SHyun Kwon struct reset_control *reset;
302d76271d2SHyun Kwon int irq;
303d76271d2SHyun Kwon
30447e801bdSLaurent Pinchart struct drm_bridge bridge;
305bd68b9b3SLaurent Pinchart struct drm_bridge *next_bridge;
30647e801bdSLaurent Pinchart
307d76271d2SHyun Kwon struct zynqmp_dp_config config;
308d76271d2SHyun Kwon struct drm_dp_aux aux;
309d76271d2SHyun Kwon struct phy *phy[ZYNQMP_DP_MAX_LANES];
310d76271d2SHyun Kwon u8 num_lanes;
311d76271d2SHyun Kwon struct delayed_work hpd_work;
312d76271d2SHyun Kwon enum drm_connector_status status;
313d76271d2SHyun Kwon bool enabled;
314d76271d2SHyun Kwon
315d76271d2SHyun Kwon u8 dpcd[DP_RECEIVER_CAP_SIZE];
316d76271d2SHyun Kwon struct zynqmp_dp_link_config link_config;
317d76271d2SHyun Kwon struct zynqmp_dp_mode mode;
318d76271d2SHyun Kwon u8 train_set[ZYNQMP_DP_MAX_LANES];
319d76271d2SHyun Kwon };
320d76271d2SHyun Kwon
bridge_to_dp(struct drm_bridge * bridge)32147e801bdSLaurent Pinchart static inline struct zynqmp_dp *bridge_to_dp(struct drm_bridge *bridge)
32247e801bdSLaurent Pinchart {
32347e801bdSLaurent Pinchart return container_of(bridge, struct zynqmp_dp, bridge);
32447e801bdSLaurent Pinchart }
32547e801bdSLaurent Pinchart
zynqmp_dp_write(struct zynqmp_dp * dp,int offset,u32 val)326d76271d2SHyun Kwon static void zynqmp_dp_write(struct zynqmp_dp *dp, int offset, u32 val)
327d76271d2SHyun Kwon {
328d76271d2SHyun Kwon writel(val, dp->iomem + offset);
329d76271d2SHyun Kwon }
330d76271d2SHyun Kwon
zynqmp_dp_read(struct zynqmp_dp * dp,int offset)331d76271d2SHyun Kwon static u32 zynqmp_dp_read(struct zynqmp_dp *dp, int offset)
332d76271d2SHyun Kwon {
333d76271d2SHyun Kwon return readl(dp->iomem + offset);
334d76271d2SHyun Kwon }
335d76271d2SHyun Kwon
zynqmp_dp_clr(struct zynqmp_dp * dp,int offset,u32 clr)336d76271d2SHyun Kwon static void zynqmp_dp_clr(struct zynqmp_dp *dp, int offset, u32 clr)
337d76271d2SHyun Kwon {
338d76271d2SHyun Kwon zynqmp_dp_write(dp, offset, zynqmp_dp_read(dp, offset) & ~clr);
339d76271d2SHyun Kwon }
340d76271d2SHyun Kwon
zynqmp_dp_set(struct zynqmp_dp * dp,int offset,u32 set)341d76271d2SHyun Kwon static void zynqmp_dp_set(struct zynqmp_dp *dp, int offset, u32 set)
342d76271d2SHyun Kwon {
343d76271d2SHyun Kwon zynqmp_dp_write(dp, offset, zynqmp_dp_read(dp, offset) | set);
344d76271d2SHyun Kwon }
345d76271d2SHyun Kwon
346d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
347d76271d2SHyun Kwon * PHY Handling
348d76271d2SHyun Kwon */
349d76271d2SHyun Kwon
350d76271d2SHyun Kwon #define RST_TIMEOUT_MS 1000
351d76271d2SHyun Kwon
zynqmp_dp_reset(struct zynqmp_dp * dp,bool assert)352d76271d2SHyun Kwon static int zynqmp_dp_reset(struct zynqmp_dp *dp, bool assert)
353d76271d2SHyun Kwon {
354d76271d2SHyun Kwon unsigned long timeout;
355d76271d2SHyun Kwon
356d76271d2SHyun Kwon if (assert)
357d76271d2SHyun Kwon reset_control_assert(dp->reset);
358d76271d2SHyun Kwon else
359d76271d2SHyun Kwon reset_control_deassert(dp->reset);
360d76271d2SHyun Kwon
361d76271d2SHyun Kwon /* Wait for the (de)assert to complete. */
362d76271d2SHyun Kwon timeout = jiffies + msecs_to_jiffies(RST_TIMEOUT_MS);
363d76271d2SHyun Kwon while (!time_after_eq(jiffies, timeout)) {
364d76271d2SHyun Kwon bool status = !!reset_control_status(dp->reset);
365d76271d2SHyun Kwon
366d76271d2SHyun Kwon if (assert == status)
367d76271d2SHyun Kwon return 0;
368d76271d2SHyun Kwon
369d76271d2SHyun Kwon cpu_relax();
370d76271d2SHyun Kwon }
371d76271d2SHyun Kwon
372d76271d2SHyun Kwon dev_err(dp->dev, "reset %s timeout\n", assert ? "assert" : "deassert");
373d76271d2SHyun Kwon return -ETIMEDOUT;
374d76271d2SHyun Kwon }
375d76271d2SHyun Kwon
376d76271d2SHyun Kwon /**
377d76271d2SHyun Kwon * zynqmp_dp_phy_init - Initialize the phy
378d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
379d76271d2SHyun Kwon *
380d76271d2SHyun Kwon * Initialize the phy.
381d76271d2SHyun Kwon *
382d76271d2SHyun Kwon * Return: 0 if the phy instances are initialized correctly, or the error code
383d76271d2SHyun Kwon * returned from the callee functions.
384d76271d2SHyun Kwon */
zynqmp_dp_phy_init(struct zynqmp_dp * dp)385d76271d2SHyun Kwon static int zynqmp_dp_phy_init(struct zynqmp_dp *dp)
386d76271d2SHyun Kwon {
387d76271d2SHyun Kwon int ret;
388d76271d2SHyun Kwon int i;
389d76271d2SHyun Kwon
390d76271d2SHyun Kwon for (i = 0; i < dp->num_lanes; i++) {
391d76271d2SHyun Kwon ret = phy_init(dp->phy[i]);
392d76271d2SHyun Kwon if (ret) {
393d76271d2SHyun Kwon dev_err(dp->dev, "failed to init phy lane %d\n", i);
394d76271d2SHyun Kwon return ret;
395d76271d2SHyun Kwon }
396d76271d2SHyun Kwon }
397d76271d2SHyun Kwon
398d76271d2SHyun Kwon zynqmp_dp_clr(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET);
399d76271d2SHyun Kwon
400d76271d2SHyun Kwon /*
401d76271d2SHyun Kwon * Power on lanes in reverse order as only lane 0 waits for the PLL to
402d76271d2SHyun Kwon * lock.
403d76271d2SHyun Kwon */
404d76271d2SHyun Kwon for (i = dp->num_lanes - 1; i >= 0; i--) {
405d76271d2SHyun Kwon ret = phy_power_on(dp->phy[i]);
406d76271d2SHyun Kwon if (ret) {
407d76271d2SHyun Kwon dev_err(dp->dev, "failed to power on phy lane %d\n", i);
408d76271d2SHyun Kwon return ret;
409d76271d2SHyun Kwon }
410d76271d2SHyun Kwon }
411d76271d2SHyun Kwon
412d76271d2SHyun Kwon return 0;
413d76271d2SHyun Kwon }
414d76271d2SHyun Kwon
415d76271d2SHyun Kwon /**
416d76271d2SHyun Kwon * zynqmp_dp_phy_exit - Exit the phy
417d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
418d76271d2SHyun Kwon *
419d76271d2SHyun Kwon * Exit the phy.
420d76271d2SHyun Kwon */
zynqmp_dp_phy_exit(struct zynqmp_dp * dp)421d76271d2SHyun Kwon static void zynqmp_dp_phy_exit(struct zynqmp_dp *dp)
422d76271d2SHyun Kwon {
423d76271d2SHyun Kwon unsigned int i;
424d76271d2SHyun Kwon int ret;
425d76271d2SHyun Kwon
426d76271d2SHyun Kwon for (i = 0; i < dp->num_lanes; i++) {
427d76271d2SHyun Kwon ret = phy_power_off(dp->phy[i]);
428d76271d2SHyun Kwon if (ret)
429d76271d2SHyun Kwon dev_err(dp->dev, "failed to power off phy(%d) %d\n", i,
430d76271d2SHyun Kwon ret);
431d76271d2SHyun Kwon }
432d76271d2SHyun Kwon
433d76271d2SHyun Kwon for (i = 0; i < dp->num_lanes; i++) {
434d76271d2SHyun Kwon ret = phy_exit(dp->phy[i]);
435d76271d2SHyun Kwon if (ret)
436d76271d2SHyun Kwon dev_err(dp->dev, "failed to exit phy(%d) %d\n", i, ret);
437d76271d2SHyun Kwon }
438d76271d2SHyun Kwon }
439d76271d2SHyun Kwon
440d76271d2SHyun Kwon /**
441d76271d2SHyun Kwon * zynqmp_dp_phy_probe - Probe the PHYs
442d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
443d76271d2SHyun Kwon *
444d76271d2SHyun Kwon * Probe PHYs for all lanes. Less PHYs may be available than the number of
445d76271d2SHyun Kwon * lanes, which is not considered an error as long as at least one PHY is
446d76271d2SHyun Kwon * found. The caller can check dp->num_lanes to check how many PHYs were found.
447d76271d2SHyun Kwon *
448d76271d2SHyun Kwon * Return:
449d76271d2SHyun Kwon * * 0 - Success
450d76271d2SHyun Kwon * * -ENXIO - No PHY found
451d76271d2SHyun Kwon * * -EPROBE_DEFER - Probe deferral requested
452d76271d2SHyun Kwon * * Other negative value - PHY retrieval failure
453d76271d2SHyun Kwon */
zynqmp_dp_phy_probe(struct zynqmp_dp * dp)454d76271d2SHyun Kwon static int zynqmp_dp_phy_probe(struct zynqmp_dp *dp)
455d76271d2SHyun Kwon {
456d76271d2SHyun Kwon unsigned int i;
457d76271d2SHyun Kwon
458d76271d2SHyun Kwon for (i = 0; i < ZYNQMP_DP_MAX_LANES; i++) {
459d76271d2SHyun Kwon char phy_name[16];
460d76271d2SHyun Kwon struct phy *phy;
461d76271d2SHyun Kwon
462d76271d2SHyun Kwon snprintf(phy_name, sizeof(phy_name), "dp-phy%d", i);
463d76271d2SHyun Kwon phy = devm_phy_get(dp->dev, phy_name);
464d76271d2SHyun Kwon
465d76271d2SHyun Kwon if (IS_ERR(phy)) {
466d76271d2SHyun Kwon switch (PTR_ERR(phy)) {
467d76271d2SHyun Kwon case -ENODEV:
468d76271d2SHyun Kwon if (dp->num_lanes)
469d76271d2SHyun Kwon return 0;
470d76271d2SHyun Kwon
471d76271d2SHyun Kwon dev_err(dp->dev, "no PHY found\n");
472d76271d2SHyun Kwon return -ENXIO;
473d76271d2SHyun Kwon
474d76271d2SHyun Kwon case -EPROBE_DEFER:
475d76271d2SHyun Kwon return -EPROBE_DEFER;
476d76271d2SHyun Kwon
477d76271d2SHyun Kwon default:
478d76271d2SHyun Kwon dev_err(dp->dev, "failed to get PHY lane %u\n",
479d76271d2SHyun Kwon i);
480d76271d2SHyun Kwon return PTR_ERR(phy);
481d76271d2SHyun Kwon }
482d76271d2SHyun Kwon }
483d76271d2SHyun Kwon
484d76271d2SHyun Kwon dp->phy[i] = phy;
485d76271d2SHyun Kwon dp->num_lanes++;
486d76271d2SHyun Kwon }
487d76271d2SHyun Kwon
488d76271d2SHyun Kwon return 0;
489d76271d2SHyun Kwon }
490d76271d2SHyun Kwon
491d76271d2SHyun Kwon /**
492d76271d2SHyun Kwon * zynqmp_dp_phy_ready - Check if PHY is ready
493d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
494d76271d2SHyun Kwon *
495d76271d2SHyun Kwon * Check if PHY is ready. If PHY is not ready, wait 1ms to check for 100 times.
496d76271d2SHyun Kwon * This amount of delay was suggested by IP designer.
497d76271d2SHyun Kwon *
498d76271d2SHyun Kwon * Return: 0 if PHY is ready, or -ENODEV if PHY is not ready.
499d76271d2SHyun Kwon */
zynqmp_dp_phy_ready(struct zynqmp_dp * dp)500d76271d2SHyun Kwon static int zynqmp_dp_phy_ready(struct zynqmp_dp *dp)
501d76271d2SHyun Kwon {
502d76271d2SHyun Kwon u32 i, reg, ready;
503d76271d2SHyun Kwon
504d76271d2SHyun Kwon ready = (1 << dp->num_lanes) - 1;
505d76271d2SHyun Kwon
506d76271d2SHyun Kwon /* Wait for 100 * 1ms. This should be enough time for PHY to be ready */
507d76271d2SHyun Kwon for (i = 0; ; i++) {
508d76271d2SHyun Kwon reg = zynqmp_dp_read(dp, ZYNQMP_DP_PHY_STATUS);
509d76271d2SHyun Kwon if ((reg & ready) == ready)
510d76271d2SHyun Kwon return 0;
511d76271d2SHyun Kwon
512d76271d2SHyun Kwon if (i == 100) {
513d76271d2SHyun Kwon dev_err(dp->dev, "PHY isn't ready\n");
514d76271d2SHyun Kwon return -ENODEV;
515d76271d2SHyun Kwon }
516d76271d2SHyun Kwon
517d76271d2SHyun Kwon usleep_range(1000, 1100);
518d76271d2SHyun Kwon }
519d76271d2SHyun Kwon
520d76271d2SHyun Kwon return 0;
521d76271d2SHyun Kwon }
522d76271d2SHyun Kwon
523d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
524d76271d2SHyun Kwon * DisplayPort Link Training
525d76271d2SHyun Kwon */
526d76271d2SHyun Kwon
527d76271d2SHyun Kwon /**
528d76271d2SHyun Kwon * zynqmp_dp_max_rate - Calculate and return available max pixel clock
529d76271d2SHyun Kwon * @link_rate: link rate (Kilo-bytes / sec)
530d76271d2SHyun Kwon * @lane_num: number of lanes
531d76271d2SHyun Kwon * @bpp: bits per pixel
532d76271d2SHyun Kwon *
533d76271d2SHyun Kwon * Return: max pixel clock (KHz) supported by current link config.
534d76271d2SHyun Kwon */
zynqmp_dp_max_rate(int link_rate,u8 lane_num,u8 bpp)535d76271d2SHyun Kwon static inline int zynqmp_dp_max_rate(int link_rate, u8 lane_num, u8 bpp)
536d76271d2SHyun Kwon {
537d76271d2SHyun Kwon return link_rate * lane_num * 8 / bpp;
538d76271d2SHyun Kwon }
539d76271d2SHyun Kwon
540d76271d2SHyun Kwon /**
541d76271d2SHyun Kwon * zynqmp_dp_mode_configure - Configure the link values
542d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
543d76271d2SHyun Kwon * @pclock: pixel clock for requested display mode
544d76271d2SHyun Kwon * @current_bw: current link rate
545d76271d2SHyun Kwon *
546d76271d2SHyun Kwon * Find the link configuration values, rate and lane count for requested pixel
547d76271d2SHyun Kwon * clock @pclock. The @pclock is stored in the mode to be used in other
548d76271d2SHyun Kwon * functions later. The returned rate is downshifted from the current rate
549d76271d2SHyun Kwon * @current_bw.
550d76271d2SHyun Kwon *
551d76271d2SHyun Kwon * Return: Current link rate code, or -EINVAL.
552d76271d2SHyun Kwon */
zynqmp_dp_mode_configure(struct zynqmp_dp * dp,int pclock,u8 current_bw)553d76271d2SHyun Kwon static int zynqmp_dp_mode_configure(struct zynqmp_dp *dp, int pclock,
554d76271d2SHyun Kwon u8 current_bw)
555d76271d2SHyun Kwon {
556d76271d2SHyun Kwon int max_rate = dp->link_config.max_rate;
55770c8b4b8SHyun Kwon u8 bw_code;
558d76271d2SHyun Kwon u8 max_lanes = dp->link_config.max_lanes;
559d76271d2SHyun Kwon u8 max_link_rate_code = drm_dp_link_rate_to_bw_code(max_rate);
560d76271d2SHyun Kwon u8 bpp = dp->config.bpp;
561d76271d2SHyun Kwon u8 lane_cnt;
562d76271d2SHyun Kwon
56370c8b4b8SHyun Kwon /* Downshift from current bandwidth */
56470c8b4b8SHyun Kwon switch (current_bw) {
56570c8b4b8SHyun Kwon case DP_LINK_BW_5_4:
56670c8b4b8SHyun Kwon bw_code = DP_LINK_BW_2_7;
56770c8b4b8SHyun Kwon break;
56870c8b4b8SHyun Kwon case DP_LINK_BW_2_7:
56970c8b4b8SHyun Kwon bw_code = DP_LINK_BW_1_62;
57070c8b4b8SHyun Kwon break;
57170c8b4b8SHyun Kwon case DP_LINK_BW_1_62:
572d76271d2SHyun Kwon dev_err(dp->dev, "can't downshift. already lowest link rate\n");
573d76271d2SHyun Kwon return -EINVAL;
57470c8b4b8SHyun Kwon default:
57570c8b4b8SHyun Kwon /* If not given, start with max supported */
57670c8b4b8SHyun Kwon bw_code = max_link_rate_code;
577d76271d2SHyun Kwon break;
578d76271d2SHyun Kwon }
579d76271d2SHyun Kwon
580d76271d2SHyun Kwon for (lane_cnt = 1; lane_cnt <= max_lanes; lane_cnt <<= 1) {
581d76271d2SHyun Kwon int bw;
582d76271d2SHyun Kwon u32 rate;
583d76271d2SHyun Kwon
58470c8b4b8SHyun Kwon bw = drm_dp_bw_code_to_link_rate(bw_code);
585d76271d2SHyun Kwon rate = zynqmp_dp_max_rate(bw, lane_cnt, bpp);
586d76271d2SHyun Kwon if (pclock <= rate) {
58770c8b4b8SHyun Kwon dp->mode.bw_code = bw_code;
588d76271d2SHyun Kwon dp->mode.lane_cnt = lane_cnt;
589d76271d2SHyun Kwon dp->mode.pclock = pclock;
590d76271d2SHyun Kwon return dp->mode.bw_code;
591d76271d2SHyun Kwon }
592d76271d2SHyun Kwon }
593d76271d2SHyun Kwon
594d76271d2SHyun Kwon dev_err(dp->dev, "failed to configure link values\n");
595d76271d2SHyun Kwon
596d76271d2SHyun Kwon return -EINVAL;
597d76271d2SHyun Kwon }
598d76271d2SHyun Kwon
599d76271d2SHyun Kwon /**
600d76271d2SHyun Kwon * zynqmp_dp_adjust_train - Adjust train values
601d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
602d76271d2SHyun Kwon * @link_status: link status from sink which contains requested training values
603d76271d2SHyun Kwon */
zynqmp_dp_adjust_train(struct zynqmp_dp * dp,u8 link_status[DP_LINK_STATUS_SIZE])604d76271d2SHyun Kwon static void zynqmp_dp_adjust_train(struct zynqmp_dp *dp,
605d76271d2SHyun Kwon u8 link_status[DP_LINK_STATUS_SIZE])
606d76271d2SHyun Kwon {
607d76271d2SHyun Kwon u8 *train_set = dp->train_set;
608d76271d2SHyun Kwon u8 voltage = 0, preemphasis = 0;
609d76271d2SHyun Kwon u8 i;
610d76271d2SHyun Kwon
611d76271d2SHyun Kwon for (i = 0; i < dp->mode.lane_cnt; i++) {
612d76271d2SHyun Kwon u8 v = drm_dp_get_adjust_request_voltage(link_status, i);
613d76271d2SHyun Kwon u8 p = drm_dp_get_adjust_request_pre_emphasis(link_status, i);
614d76271d2SHyun Kwon
615d76271d2SHyun Kwon if (v > voltage)
616d76271d2SHyun Kwon voltage = v;
617d76271d2SHyun Kwon
618d76271d2SHyun Kwon if (p > preemphasis)
619d76271d2SHyun Kwon preemphasis = p;
620d76271d2SHyun Kwon }
621d76271d2SHyun Kwon
622d76271d2SHyun Kwon if (voltage >= DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
623d76271d2SHyun Kwon voltage |= DP_TRAIN_MAX_SWING_REACHED;
624d76271d2SHyun Kwon
625d76271d2SHyun Kwon if (preemphasis >= DP_TRAIN_PRE_EMPH_LEVEL_2)
626d76271d2SHyun Kwon preemphasis |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
627d76271d2SHyun Kwon
628d76271d2SHyun Kwon for (i = 0; i < dp->mode.lane_cnt; i++)
629d76271d2SHyun Kwon train_set[i] = voltage | preemphasis;
630d76271d2SHyun Kwon }
631d76271d2SHyun Kwon
632d76271d2SHyun Kwon /**
633d76271d2SHyun Kwon * zynqmp_dp_update_vs_emph - Update the training values
634d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
635d76271d2SHyun Kwon *
636d76271d2SHyun Kwon * Update the training values based on the request from sink. The mapped values
637d76271d2SHyun Kwon * are predefined, and values(vs, pe, pc) are from the device manual.
638d76271d2SHyun Kwon *
639d76271d2SHyun Kwon * Return: 0 if vs and emph are updated successfully, or the error code returned
640d76271d2SHyun Kwon * by drm_dp_dpcd_write().
641d76271d2SHyun Kwon */
zynqmp_dp_update_vs_emph(struct zynqmp_dp * dp)642d76271d2SHyun Kwon static int zynqmp_dp_update_vs_emph(struct zynqmp_dp *dp)
643d76271d2SHyun Kwon {
644d76271d2SHyun Kwon unsigned int i;
645d76271d2SHyun Kwon int ret;
646d76271d2SHyun Kwon
647d76271d2SHyun Kwon ret = drm_dp_dpcd_write(&dp->aux, DP_TRAINING_LANE0_SET, dp->train_set,
648d76271d2SHyun Kwon dp->mode.lane_cnt);
649d76271d2SHyun Kwon if (ret < 0)
650d76271d2SHyun Kwon return ret;
651d76271d2SHyun Kwon
652d76271d2SHyun Kwon for (i = 0; i < dp->mode.lane_cnt; i++) {
653d76271d2SHyun Kwon u32 reg = ZYNQMP_DP_SUB_TX_PHY_PRECURSOR_LANE_0 + i * 4;
654d76271d2SHyun Kwon union phy_configure_opts opts = { 0 };
655d76271d2SHyun Kwon u8 train = dp->train_set[i];
656d76271d2SHyun Kwon
657d76271d2SHyun Kwon opts.dp.voltage[0] = (train & DP_TRAIN_VOLTAGE_SWING_MASK)
658d76271d2SHyun Kwon >> DP_TRAIN_VOLTAGE_SWING_SHIFT;
659d76271d2SHyun Kwon opts.dp.pre[0] = (train & DP_TRAIN_PRE_EMPHASIS_MASK)
660d76271d2SHyun Kwon >> DP_TRAIN_PRE_EMPHASIS_SHIFT;
661d76271d2SHyun Kwon
662d76271d2SHyun Kwon phy_configure(dp->phy[i], &opts);
663d76271d2SHyun Kwon
664d76271d2SHyun Kwon zynqmp_dp_write(dp, reg, 0x2);
665d76271d2SHyun Kwon }
666d76271d2SHyun Kwon
667d76271d2SHyun Kwon return 0;
668d76271d2SHyun Kwon }
669d76271d2SHyun Kwon
670d76271d2SHyun Kwon /**
671d76271d2SHyun Kwon * zynqmp_dp_link_train_cr - Train clock recovery
672d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
673d76271d2SHyun Kwon *
674d76271d2SHyun Kwon * Return: 0 if clock recovery train is done successfully, or corresponding
675d76271d2SHyun Kwon * error code.
676d76271d2SHyun Kwon */
zynqmp_dp_link_train_cr(struct zynqmp_dp * dp)677d76271d2SHyun Kwon static int zynqmp_dp_link_train_cr(struct zynqmp_dp *dp)
678d76271d2SHyun Kwon {
679d76271d2SHyun Kwon u8 link_status[DP_LINK_STATUS_SIZE];
680d76271d2SHyun Kwon u8 lane_cnt = dp->mode.lane_cnt;
681d76271d2SHyun Kwon u8 vs = 0, tries = 0;
682d76271d2SHyun Kwon u16 max_tries, i;
683d76271d2SHyun Kwon bool cr_done;
684d76271d2SHyun Kwon int ret;
685d76271d2SHyun Kwon
686d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET,
687d76271d2SHyun Kwon DP_TRAINING_PATTERN_1);
688d76271d2SHyun Kwon ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
689d76271d2SHyun Kwon DP_TRAINING_PATTERN_1 |
690d76271d2SHyun Kwon DP_LINK_SCRAMBLING_DISABLE);
691d76271d2SHyun Kwon if (ret < 0)
692d76271d2SHyun Kwon return ret;
693d76271d2SHyun Kwon
694d76271d2SHyun Kwon /*
695d76271d2SHyun Kwon * 256 loops should be maximum iterations for 4 lanes and 4 values.
696d76271d2SHyun Kwon * So, This loop should exit before 512 iterations
697d76271d2SHyun Kwon */
698d76271d2SHyun Kwon for (max_tries = 0; max_tries < 512; max_tries++) {
699d76271d2SHyun Kwon ret = zynqmp_dp_update_vs_emph(dp);
700d76271d2SHyun Kwon if (ret)
701d76271d2SHyun Kwon return ret;
702d76271d2SHyun Kwon
7039e986666SLyude Paul drm_dp_link_train_clock_recovery_delay(&dp->aux, dp->dpcd);
704d76271d2SHyun Kwon ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status);
705d76271d2SHyun Kwon if (ret < 0)
706d76271d2SHyun Kwon return ret;
707d76271d2SHyun Kwon
708d76271d2SHyun Kwon cr_done = drm_dp_clock_recovery_ok(link_status, lane_cnt);
709d76271d2SHyun Kwon if (cr_done)
710d76271d2SHyun Kwon break;
711d76271d2SHyun Kwon
712d76271d2SHyun Kwon for (i = 0; i < lane_cnt; i++)
713d76271d2SHyun Kwon if (!(dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED))
714d76271d2SHyun Kwon break;
715d76271d2SHyun Kwon if (i == lane_cnt)
716d76271d2SHyun Kwon break;
717d76271d2SHyun Kwon
718d76271d2SHyun Kwon if ((dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == vs)
719d76271d2SHyun Kwon tries++;
720d76271d2SHyun Kwon else
721d76271d2SHyun Kwon tries = 0;
722d76271d2SHyun Kwon
723d76271d2SHyun Kwon if (tries == DP_MAX_TRAINING_TRIES)
724d76271d2SHyun Kwon break;
725d76271d2SHyun Kwon
726d76271d2SHyun Kwon vs = dp->train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
727d76271d2SHyun Kwon zynqmp_dp_adjust_train(dp, link_status);
728d76271d2SHyun Kwon }
729d76271d2SHyun Kwon
730d76271d2SHyun Kwon if (!cr_done)
731d76271d2SHyun Kwon return -EIO;
732d76271d2SHyun Kwon
733d76271d2SHyun Kwon return 0;
734d76271d2SHyun Kwon }
735d76271d2SHyun Kwon
736d76271d2SHyun Kwon /**
737d76271d2SHyun Kwon * zynqmp_dp_link_train_ce - Train channel equalization
738d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
739d76271d2SHyun Kwon *
740d76271d2SHyun Kwon * Return: 0 if channel equalization train is done successfully, or
741d76271d2SHyun Kwon * corresponding error code.
742d76271d2SHyun Kwon */
zynqmp_dp_link_train_ce(struct zynqmp_dp * dp)743d76271d2SHyun Kwon static int zynqmp_dp_link_train_ce(struct zynqmp_dp *dp)
744d76271d2SHyun Kwon {
745d76271d2SHyun Kwon u8 link_status[DP_LINK_STATUS_SIZE];
746d76271d2SHyun Kwon u8 lane_cnt = dp->mode.lane_cnt;
747d76271d2SHyun Kwon u32 pat, tries;
748d76271d2SHyun Kwon int ret;
749d76271d2SHyun Kwon bool ce_done;
750d76271d2SHyun Kwon
751d76271d2SHyun Kwon if (dp->dpcd[DP_DPCD_REV] >= DP_V1_2 &&
752d76271d2SHyun Kwon dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED)
753d76271d2SHyun Kwon pat = DP_TRAINING_PATTERN_3;
754d76271d2SHyun Kwon else
755d76271d2SHyun Kwon pat = DP_TRAINING_PATTERN_2;
756d76271d2SHyun Kwon
757d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET, pat);
758d76271d2SHyun Kwon ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
759d76271d2SHyun Kwon pat | DP_LINK_SCRAMBLING_DISABLE);
760d76271d2SHyun Kwon if (ret < 0)
761d76271d2SHyun Kwon return ret;
762d76271d2SHyun Kwon
763d76271d2SHyun Kwon for (tries = 0; tries < DP_MAX_TRAINING_TRIES; tries++) {
764d76271d2SHyun Kwon ret = zynqmp_dp_update_vs_emph(dp);
765d76271d2SHyun Kwon if (ret)
766d76271d2SHyun Kwon return ret;
767d76271d2SHyun Kwon
7680c4fada6SLyude Paul drm_dp_link_train_channel_eq_delay(&dp->aux, dp->dpcd);
769d76271d2SHyun Kwon ret = drm_dp_dpcd_read_link_status(&dp->aux, link_status);
770d76271d2SHyun Kwon if (ret < 0)
771d76271d2SHyun Kwon return ret;
772d76271d2SHyun Kwon
773d76271d2SHyun Kwon ce_done = drm_dp_channel_eq_ok(link_status, lane_cnt);
774d76271d2SHyun Kwon if (ce_done)
775d76271d2SHyun Kwon break;
776d76271d2SHyun Kwon
777d76271d2SHyun Kwon zynqmp_dp_adjust_train(dp, link_status);
778d76271d2SHyun Kwon }
779d76271d2SHyun Kwon
780d76271d2SHyun Kwon if (!ce_done)
781d76271d2SHyun Kwon return -EIO;
782d76271d2SHyun Kwon
783d76271d2SHyun Kwon return 0;
784d76271d2SHyun Kwon }
785d76271d2SHyun Kwon
786d76271d2SHyun Kwon /**
787*ea45025dSLee Jones * zynqmp_dp_train - Train the link
788d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
789d76271d2SHyun Kwon *
790d76271d2SHyun Kwon * Return: 0 if all trains are done successfully, or corresponding error code.
791d76271d2SHyun Kwon */
zynqmp_dp_train(struct zynqmp_dp * dp)792d76271d2SHyun Kwon static int zynqmp_dp_train(struct zynqmp_dp *dp)
793d76271d2SHyun Kwon {
794d76271d2SHyun Kwon u32 reg;
795d76271d2SHyun Kwon u8 bw_code = dp->mode.bw_code;
796d76271d2SHyun Kwon u8 lane_cnt = dp->mode.lane_cnt;
797d76271d2SHyun Kwon u8 aux_lane_cnt = lane_cnt;
798d76271d2SHyun Kwon bool enhanced;
799d76271d2SHyun Kwon int ret;
800d76271d2SHyun Kwon
801d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_LANE_COUNT_SET, lane_cnt);
802d76271d2SHyun Kwon enhanced = drm_dp_enhanced_frame_cap(dp->dpcd);
803d76271d2SHyun Kwon if (enhanced) {
804d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_ENHANCED_FRAME_EN, 1);
805d76271d2SHyun Kwon aux_lane_cnt |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
806d76271d2SHyun Kwon }
807d76271d2SHyun Kwon
808d76271d2SHyun Kwon if (dp->dpcd[3] & 0x1) {
809d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 1);
810d76271d2SHyun Kwon drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL,
811d76271d2SHyun Kwon DP_SPREAD_AMP_0_5);
812d76271d2SHyun Kwon } else {
813d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_DOWNSPREAD_CTL, 0);
814d76271d2SHyun Kwon drm_dp_dpcd_writeb(&dp->aux, DP_DOWNSPREAD_CTRL, 0);
815d76271d2SHyun Kwon }
816d76271d2SHyun Kwon
817d76271d2SHyun Kwon ret = drm_dp_dpcd_writeb(&dp->aux, DP_LANE_COUNT_SET, aux_lane_cnt);
818d76271d2SHyun Kwon if (ret < 0) {
819d76271d2SHyun Kwon dev_err(dp->dev, "failed to set lane count\n");
820d76271d2SHyun Kwon return ret;
821d76271d2SHyun Kwon }
822d76271d2SHyun Kwon
823d76271d2SHyun Kwon ret = drm_dp_dpcd_writeb(&dp->aux, DP_MAIN_LINK_CHANNEL_CODING_SET,
824d76271d2SHyun Kwon DP_SET_ANSI_8B10B);
825d76271d2SHyun Kwon if (ret < 0) {
826d76271d2SHyun Kwon dev_err(dp->dev, "failed to set ANSI 8B/10B encoding\n");
827d76271d2SHyun Kwon return ret;
828d76271d2SHyun Kwon }
829d76271d2SHyun Kwon
830d76271d2SHyun Kwon ret = drm_dp_dpcd_writeb(&dp->aux, DP_LINK_BW_SET, bw_code);
831d76271d2SHyun Kwon if (ret < 0) {
832d76271d2SHyun Kwon dev_err(dp->dev, "failed to set DP bandwidth\n");
833d76271d2SHyun Kwon return ret;
834d76271d2SHyun Kwon }
835d76271d2SHyun Kwon
836d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_LINK_BW_SET, bw_code);
837d76271d2SHyun Kwon switch (bw_code) {
838d76271d2SHyun Kwon case DP_LINK_BW_1_62:
839d76271d2SHyun Kwon reg = ZYNQMP_DP_PHY_CLOCK_SELECT_1_62G;
840d76271d2SHyun Kwon break;
841d76271d2SHyun Kwon case DP_LINK_BW_2_7:
842d76271d2SHyun Kwon reg = ZYNQMP_DP_PHY_CLOCK_SELECT_2_70G;
843d76271d2SHyun Kwon break;
844d76271d2SHyun Kwon case DP_LINK_BW_5_4:
845d76271d2SHyun Kwon default:
846d76271d2SHyun Kwon reg = ZYNQMP_DP_PHY_CLOCK_SELECT_5_40G;
847d76271d2SHyun Kwon break;
848d76271d2SHyun Kwon }
849d76271d2SHyun Kwon
850d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_PHY_CLOCK_SELECT, reg);
851d76271d2SHyun Kwon ret = zynqmp_dp_phy_ready(dp);
852d76271d2SHyun Kwon if (ret < 0)
853d76271d2SHyun Kwon return ret;
854d76271d2SHyun Kwon
855d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 1);
8565842ab76SDan Carpenter memset(dp->train_set, 0, sizeof(dp->train_set));
857d76271d2SHyun Kwon ret = zynqmp_dp_link_train_cr(dp);
858d76271d2SHyun Kwon if (ret)
859d76271d2SHyun Kwon return ret;
860d76271d2SHyun Kwon
861d76271d2SHyun Kwon ret = zynqmp_dp_link_train_ce(dp);
862d76271d2SHyun Kwon if (ret)
863d76271d2SHyun Kwon return ret;
864d76271d2SHyun Kwon
865d76271d2SHyun Kwon ret = drm_dp_dpcd_writeb(&dp->aux, DP_TRAINING_PATTERN_SET,
866d76271d2SHyun Kwon DP_TRAINING_PATTERN_DISABLE);
867d76271d2SHyun Kwon if (ret < 0) {
868d76271d2SHyun Kwon dev_err(dp->dev, "failed to disable training pattern\n");
869d76271d2SHyun Kwon return ret;
870d76271d2SHyun Kwon }
871d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TRAINING_PATTERN_SET,
872d76271d2SHyun Kwon DP_TRAINING_PATTERN_DISABLE);
873d76271d2SHyun Kwon
874d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_SCRAMBLING_DISABLE, 0);
875d76271d2SHyun Kwon
876d76271d2SHyun Kwon return 0;
877d76271d2SHyun Kwon }
878d76271d2SHyun Kwon
879d76271d2SHyun Kwon /**
880d76271d2SHyun Kwon * zynqmp_dp_train_loop - Downshift the link rate during training
881d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
882d76271d2SHyun Kwon *
883d76271d2SHyun Kwon * Train the link by downshifting the link rate if training is not successful.
884d76271d2SHyun Kwon */
zynqmp_dp_train_loop(struct zynqmp_dp * dp)885d76271d2SHyun Kwon static void zynqmp_dp_train_loop(struct zynqmp_dp *dp)
886d76271d2SHyun Kwon {
887d76271d2SHyun Kwon struct zynqmp_dp_mode *mode = &dp->mode;
888d76271d2SHyun Kwon u8 bw = mode->bw_code;
889d76271d2SHyun Kwon int ret;
890d76271d2SHyun Kwon
891d76271d2SHyun Kwon do {
892d76271d2SHyun Kwon if (dp->status == connector_status_disconnected ||
893d76271d2SHyun Kwon !dp->enabled)
894d76271d2SHyun Kwon return;
895d76271d2SHyun Kwon
896d76271d2SHyun Kwon ret = zynqmp_dp_train(dp);
897d76271d2SHyun Kwon if (!ret)
898d76271d2SHyun Kwon return;
899d76271d2SHyun Kwon
900d76271d2SHyun Kwon ret = zynqmp_dp_mode_configure(dp, mode->pclock, bw);
901d76271d2SHyun Kwon if (ret < 0)
902d76271d2SHyun Kwon goto err_out;
903d76271d2SHyun Kwon
904d76271d2SHyun Kwon bw = ret;
905d76271d2SHyun Kwon } while (bw >= DP_LINK_BW_1_62);
906d76271d2SHyun Kwon
907d76271d2SHyun Kwon err_out:
908d76271d2SHyun Kwon dev_err(dp->dev, "failed to train the DP link\n");
909d76271d2SHyun Kwon }
910d76271d2SHyun Kwon
911d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
912d76271d2SHyun Kwon * DisplayPort AUX
913d76271d2SHyun Kwon */
914d76271d2SHyun Kwon
915d76271d2SHyun Kwon #define AUX_READ_BIT 0x1
916d76271d2SHyun Kwon
917d76271d2SHyun Kwon /**
918d76271d2SHyun Kwon * zynqmp_dp_aux_cmd_submit - Submit aux command
919d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
920d76271d2SHyun Kwon * @cmd: aux command
921d76271d2SHyun Kwon * @addr: aux address
922d76271d2SHyun Kwon * @buf: buffer for command data
923d76271d2SHyun Kwon * @bytes: number of bytes for @buf
924d76271d2SHyun Kwon * @reply: reply code to be returned
925d76271d2SHyun Kwon *
926d76271d2SHyun Kwon * Submit an aux command. All aux related commands, native or i2c aux
927d76271d2SHyun Kwon * read/write, are submitted through this function. The function is mapped to
928d76271d2SHyun Kwon * the transfer function of struct drm_dp_aux. This function involves in
929d76271d2SHyun Kwon * multiple register reads/writes, thus synchronization is needed, and it is
930d76271d2SHyun Kwon * done by drm_dp_helper using @hw_mutex. The calling thread goes into sleep
931d76271d2SHyun Kwon * if there's no immediate reply to the command submission. The reply code is
932d76271d2SHyun Kwon * returned at @reply if @reply != NULL.
933d76271d2SHyun Kwon *
934d76271d2SHyun Kwon * Return: 0 if the command is submitted properly, or corresponding error code:
935d76271d2SHyun Kwon * -EBUSY when there is any request already being processed
936d76271d2SHyun Kwon * -ETIMEDOUT when receiving reply is timed out
937d76271d2SHyun Kwon * -EIO when received bytes are less than requested
938d76271d2SHyun Kwon */
zynqmp_dp_aux_cmd_submit(struct zynqmp_dp * dp,u32 cmd,u16 addr,u8 * buf,u8 bytes,u8 * reply)939d76271d2SHyun Kwon static int zynqmp_dp_aux_cmd_submit(struct zynqmp_dp *dp, u32 cmd, u16 addr,
940d76271d2SHyun Kwon u8 *buf, u8 bytes, u8 *reply)
941d76271d2SHyun Kwon {
942d76271d2SHyun Kwon bool is_read = (cmd & AUX_READ_BIT) ? true : false;
943d76271d2SHyun Kwon u32 reg, i;
944d76271d2SHyun Kwon
945d76271d2SHyun Kwon reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE);
946d76271d2SHyun Kwon if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REQUEST)
947d76271d2SHyun Kwon return -EBUSY;
948d76271d2SHyun Kwon
949d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_AUX_ADDRESS, addr);
950d76271d2SHyun Kwon if (!is_read)
951d76271d2SHyun Kwon for (i = 0; i < bytes; i++)
952d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_AUX_WRITE_FIFO,
953d76271d2SHyun Kwon buf[i]);
954d76271d2SHyun Kwon
955d76271d2SHyun Kwon reg = cmd << ZYNQMP_DP_AUX_COMMAND_CMD_SHIFT;
956d76271d2SHyun Kwon if (!buf || !bytes)
957d76271d2SHyun Kwon reg |= ZYNQMP_DP_AUX_COMMAND_ADDRESS_ONLY;
958d76271d2SHyun Kwon else
959d76271d2SHyun Kwon reg |= (bytes - 1) << ZYNQMP_DP_AUX_COMMAND_BYTES_SHIFT;
960d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_AUX_COMMAND, reg);
961d76271d2SHyun Kwon
962d76271d2SHyun Kwon /* Wait for reply to be delivered upto 2ms */
963d76271d2SHyun Kwon for (i = 0; ; i++) {
964d76271d2SHyun Kwon reg = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE);
965d76271d2SHyun Kwon if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY)
966d76271d2SHyun Kwon break;
967d76271d2SHyun Kwon
968d76271d2SHyun Kwon if (reg & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_REPLY_TIMEOUT ||
969d76271d2SHyun Kwon i == 2)
970d76271d2SHyun Kwon return -ETIMEDOUT;
971d76271d2SHyun Kwon
972d76271d2SHyun Kwon usleep_range(1000, 1100);
973d76271d2SHyun Kwon }
974d76271d2SHyun Kwon
975d76271d2SHyun Kwon reg = zynqmp_dp_read(dp, ZYNQMP_DP_AUX_REPLY_CODE);
976d76271d2SHyun Kwon if (reply)
977d76271d2SHyun Kwon *reply = reg;
978d76271d2SHyun Kwon
979d76271d2SHyun Kwon if (is_read &&
980d76271d2SHyun Kwon (reg == ZYNQMP_DP_AUX_REPLY_CODE_AUX_ACK ||
981d76271d2SHyun Kwon reg == ZYNQMP_DP_AUX_REPLY_CODE_I2C_ACK)) {
982d76271d2SHyun Kwon reg = zynqmp_dp_read(dp, ZYNQMP_DP_REPLY_DATA_COUNT);
983d76271d2SHyun Kwon if ((reg & ZYNQMP_DP_REPLY_DATA_COUNT_MASK) != bytes)
984d76271d2SHyun Kwon return -EIO;
985d76271d2SHyun Kwon
986d76271d2SHyun Kwon for (i = 0; i < bytes; i++)
987d76271d2SHyun Kwon buf[i] = zynqmp_dp_read(dp, ZYNQMP_DP_AUX_REPLY_DATA);
988d76271d2SHyun Kwon }
989d76271d2SHyun Kwon
990d76271d2SHyun Kwon return 0;
991d76271d2SHyun Kwon }
992d76271d2SHyun Kwon
993d76271d2SHyun Kwon static ssize_t
zynqmp_dp_aux_transfer(struct drm_dp_aux * aux,struct drm_dp_aux_msg * msg)994d76271d2SHyun Kwon zynqmp_dp_aux_transfer(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
995d76271d2SHyun Kwon {
996d76271d2SHyun Kwon struct zynqmp_dp *dp = container_of(aux, struct zynqmp_dp, aux);
997d76271d2SHyun Kwon int ret;
998d76271d2SHyun Kwon unsigned int i, iter;
999d76271d2SHyun Kwon
1000d76271d2SHyun Kwon /* Number of loops = timeout in msec / aux delay (400 usec) */
1001d76271d2SHyun Kwon iter = zynqmp_dp_aux_timeout_ms * 1000 / 400;
1002d76271d2SHyun Kwon iter = iter ? iter : 1;
1003d76271d2SHyun Kwon
1004d76271d2SHyun Kwon for (i = 0; i < iter; i++) {
1005d76271d2SHyun Kwon ret = zynqmp_dp_aux_cmd_submit(dp, msg->request, msg->address,
1006d76271d2SHyun Kwon msg->buffer, msg->size,
1007d76271d2SHyun Kwon &msg->reply);
1008d76271d2SHyun Kwon if (!ret) {
1009d76271d2SHyun Kwon dev_dbg(dp->dev, "aux %d retries\n", i);
1010d76271d2SHyun Kwon return msg->size;
1011d76271d2SHyun Kwon }
1012d76271d2SHyun Kwon
1013d76271d2SHyun Kwon if (dp->status == connector_status_disconnected) {
1014d76271d2SHyun Kwon dev_dbg(dp->dev, "no connected aux device\n");
1015d76271d2SHyun Kwon return -ENODEV;
1016d76271d2SHyun Kwon }
1017d76271d2SHyun Kwon
1018d76271d2SHyun Kwon usleep_range(400, 500);
1019d76271d2SHyun Kwon }
1020d76271d2SHyun Kwon
1021d76271d2SHyun Kwon dev_dbg(dp->dev, "failed to do aux transfer (%d)\n", ret);
1022d76271d2SHyun Kwon
1023d76271d2SHyun Kwon return ret;
1024d76271d2SHyun Kwon }
1025d76271d2SHyun Kwon
1026d76271d2SHyun Kwon /**
1027d76271d2SHyun Kwon * zynqmp_dp_aux_init - Initialize and register the DP AUX
1028d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1029d76271d2SHyun Kwon *
1030d76271d2SHyun Kwon * Program the AUX clock divider and filter and register the DP AUX adapter.
1031d76271d2SHyun Kwon *
1032d76271d2SHyun Kwon * Return: 0 on success, error value otherwise
1033d76271d2SHyun Kwon */
zynqmp_dp_aux_init(struct zynqmp_dp * dp)1034d76271d2SHyun Kwon static int zynqmp_dp_aux_init(struct zynqmp_dp *dp)
1035d76271d2SHyun Kwon {
1036d76271d2SHyun Kwon unsigned long rate;
1037d76271d2SHyun Kwon unsigned int w;
1038d76271d2SHyun Kwon
1039d76271d2SHyun Kwon /*
1040d76271d2SHyun Kwon * The AUX_SIGNAL_WIDTH_FILTER is the number of APB clock cycles
1041d76271d2SHyun Kwon * corresponding to the AUX pulse. Allowable values are 8, 16, 24, 32,
1042d76271d2SHyun Kwon * 40 and 48. The AUX pulse width must be between 0.4µs and 0.6µs,
1043d76271d2SHyun Kwon * compute the w / 8 value corresponding to 0.4µs rounded up, and make
1044d76271d2SHyun Kwon * sure it stays below 0.6µs and within the allowable values.
1045d76271d2SHyun Kwon */
1046d76271d2SHyun Kwon rate = clk_get_rate(dp->dpsub->apb_clk);
1047d76271d2SHyun Kwon w = DIV_ROUND_UP(4 * rate, 1000 * 1000 * 10 * 8) * 8;
1048d76271d2SHyun Kwon if (w > 6 * rate / (1000 * 1000 * 10) || w > 48) {
1049d76271d2SHyun Kwon dev_err(dp->dev, "aclk frequency too high\n");
1050d76271d2SHyun Kwon return -EINVAL;
1051d76271d2SHyun Kwon }
1052d76271d2SHyun Kwon
1053d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_AUX_CLK_DIVIDER,
1054d76271d2SHyun Kwon (w << ZYNQMP_DP_AUX_CLK_DIVIDER_AUX_FILTER_SHIFT) |
1055d76271d2SHyun Kwon (rate / (1000 * 1000)));
1056d76271d2SHyun Kwon
1057d76271d2SHyun Kwon dp->aux.name = "ZynqMP DP AUX";
1058d76271d2SHyun Kwon dp->aux.dev = dp->dev;
10592dfd045cSLaurent Pinchart dp->aux.drm_dev = dp->bridge.dev;
1060d76271d2SHyun Kwon dp->aux.transfer = zynqmp_dp_aux_transfer;
1061d76271d2SHyun Kwon
1062d76271d2SHyun Kwon return drm_dp_aux_register(&dp->aux);
1063d76271d2SHyun Kwon }
1064d76271d2SHyun Kwon
1065d76271d2SHyun Kwon /**
1066d76271d2SHyun Kwon * zynqmp_dp_aux_cleanup - Cleanup the DP AUX
1067d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1068d76271d2SHyun Kwon *
1069d76271d2SHyun Kwon * Unregister the DP AUX adapter.
1070d76271d2SHyun Kwon */
zynqmp_dp_aux_cleanup(struct zynqmp_dp * dp)1071d76271d2SHyun Kwon static void zynqmp_dp_aux_cleanup(struct zynqmp_dp *dp)
1072d76271d2SHyun Kwon {
1073d76271d2SHyun Kwon drm_dp_aux_unregister(&dp->aux);
1074d76271d2SHyun Kwon }
1075d76271d2SHyun Kwon
1076d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
1077d76271d2SHyun Kwon * DisplayPort Generic Support
1078d76271d2SHyun Kwon */
1079d76271d2SHyun Kwon
1080d76271d2SHyun Kwon /**
1081d76271d2SHyun Kwon * zynqmp_dp_update_misc - Write the misc registers
1082d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1083d76271d2SHyun Kwon *
1084d76271d2SHyun Kwon * The misc register values are stored in the structure, and this
1085d76271d2SHyun Kwon * function applies the values into the registers.
1086d76271d2SHyun Kwon */
zynqmp_dp_update_misc(struct zynqmp_dp * dp)1087d76271d2SHyun Kwon static void zynqmp_dp_update_misc(struct zynqmp_dp *dp)
1088d76271d2SHyun Kwon {
1089d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_MISC0, dp->config.misc0);
1090d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_MISC1, dp->config.misc1);
1091d76271d2SHyun Kwon }
1092d76271d2SHyun Kwon
1093d76271d2SHyun Kwon /**
1094d76271d2SHyun Kwon * zynqmp_dp_set_format - Set the input format
1095d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
10965827398bSLaurent Pinchart * @info: Display info
1097d76271d2SHyun Kwon * @format: input format
1098d76271d2SHyun Kwon * @bpc: bits per component
1099d76271d2SHyun Kwon *
1100d76271d2SHyun Kwon * Update misc register values based on input @format and @bpc.
1101d76271d2SHyun Kwon *
1102d76271d2SHyun Kwon * Return: 0 on success, or -EINVAL.
1103d76271d2SHyun Kwon */
zynqmp_dp_set_format(struct zynqmp_dp * dp,const struct drm_display_info * info,enum zynqmp_dpsub_format format,unsigned int bpc)1104d76271d2SHyun Kwon static int zynqmp_dp_set_format(struct zynqmp_dp *dp,
11055827398bSLaurent Pinchart const struct drm_display_info *info,
1106d76271d2SHyun Kwon enum zynqmp_dpsub_format format,
1107d76271d2SHyun Kwon unsigned int bpc)
1108d76271d2SHyun Kwon {
1109d76271d2SHyun Kwon struct zynqmp_dp_config *config = &dp->config;
1110d76271d2SHyun Kwon unsigned int num_colors;
1111d76271d2SHyun Kwon
1112d76271d2SHyun Kwon config->misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_MASK;
1113d76271d2SHyun Kwon config->misc1 &= ~ZYNQMP_DP_MAIN_STREAM_MISC1_Y_ONLY_EN;
1114d76271d2SHyun Kwon
1115d76271d2SHyun Kwon switch (format) {
1116d76271d2SHyun Kwon case ZYNQMP_DPSUB_FORMAT_RGB:
1117d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_RGB;
1118d76271d2SHyun Kwon num_colors = 3;
1119d76271d2SHyun Kwon break;
1120d76271d2SHyun Kwon
1121d76271d2SHyun Kwon case ZYNQMP_DPSUB_FORMAT_YCRCB444:
1122d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_444;
1123d76271d2SHyun Kwon num_colors = 3;
1124d76271d2SHyun Kwon break;
1125d76271d2SHyun Kwon
1126d76271d2SHyun Kwon case ZYNQMP_DPSUB_FORMAT_YCRCB422:
1127d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_COMP_FORMAT_YCRCB_422;
1128d76271d2SHyun Kwon num_colors = 2;
1129d76271d2SHyun Kwon break;
1130d76271d2SHyun Kwon
1131d76271d2SHyun Kwon case ZYNQMP_DPSUB_FORMAT_YONLY:
1132d76271d2SHyun Kwon config->misc1 |= ZYNQMP_DP_MAIN_STREAM_MISC1_Y_ONLY_EN;
1133d76271d2SHyun Kwon num_colors = 1;
1134d76271d2SHyun Kwon break;
1135d76271d2SHyun Kwon
1136d76271d2SHyun Kwon default:
1137d76271d2SHyun Kwon dev_err(dp->dev, "Invalid colormetry in DT\n");
1138d76271d2SHyun Kwon return -EINVAL;
1139d76271d2SHyun Kwon }
1140d76271d2SHyun Kwon
11415827398bSLaurent Pinchart if (info && info->bpc && bpc > info->bpc) {
1142d76271d2SHyun Kwon dev_warn(dp->dev,
1143d76271d2SHyun Kwon "downgrading requested %ubpc to display limit %ubpc\n",
11445827398bSLaurent Pinchart bpc, info->bpc);
11455827398bSLaurent Pinchart bpc = info->bpc;
1146d76271d2SHyun Kwon }
1147d76271d2SHyun Kwon
1148d76271d2SHyun Kwon config->misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_MASK;
1149d76271d2SHyun Kwon
1150d76271d2SHyun Kwon switch (bpc) {
1151d76271d2SHyun Kwon case 6:
1152d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_6;
1153d76271d2SHyun Kwon break;
1154d76271d2SHyun Kwon case 8:
1155d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_8;
1156d76271d2SHyun Kwon break;
1157d76271d2SHyun Kwon case 10:
1158d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_10;
1159d76271d2SHyun Kwon break;
1160d76271d2SHyun Kwon case 12:
1161d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_12;
1162d76271d2SHyun Kwon break;
1163d76271d2SHyun Kwon case 16:
1164d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_16;
1165d76271d2SHyun Kwon break;
1166d76271d2SHyun Kwon default:
1167d76271d2SHyun Kwon dev_warn(dp->dev, "Not supported bpc (%u). fall back to 8bpc\n",
1168d76271d2SHyun Kwon bpc);
1169d76271d2SHyun Kwon config->misc0 |= ZYNQMP_DP_MAIN_STREAM_MISC0_BPC_8;
1170d76271d2SHyun Kwon bpc = 8;
1171d76271d2SHyun Kwon break;
1172d76271d2SHyun Kwon }
1173d76271d2SHyun Kwon
1174d76271d2SHyun Kwon /* Update the current bpp based on the format. */
1175d76271d2SHyun Kwon config->bpp = bpc * num_colors;
1176d76271d2SHyun Kwon
1177d76271d2SHyun Kwon return 0;
1178d76271d2SHyun Kwon }
1179d76271d2SHyun Kwon
1180d76271d2SHyun Kwon /**
1181d76271d2SHyun Kwon * zynqmp_dp_encoder_mode_set_transfer_unit - Set the transfer unit values
1182d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1183d76271d2SHyun Kwon * @mode: requested display mode
1184d76271d2SHyun Kwon *
1185d76271d2SHyun Kwon * Set the transfer unit, and calculate all transfer unit size related values.
1186d76271d2SHyun Kwon * Calculation is based on DP and IP core specification.
1187d76271d2SHyun Kwon */
1188d76271d2SHyun Kwon static void
zynqmp_dp_encoder_mode_set_transfer_unit(struct zynqmp_dp * dp,const struct drm_display_mode * mode)1189d76271d2SHyun Kwon zynqmp_dp_encoder_mode_set_transfer_unit(struct zynqmp_dp *dp,
1190d693bd3bSLaurent Pinchart const struct drm_display_mode *mode)
1191d76271d2SHyun Kwon {
1192d76271d2SHyun Kwon u32 tu = ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE_TU_SIZE_DEF;
1193d76271d2SHyun Kwon u32 bw, vid_kbytes, avg_bytes_per_tu, init_wait;
1194d76271d2SHyun Kwon
1195d76271d2SHyun Kwon /* Use the max transfer unit size (default) */
1196d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MSA_TRANSFER_UNIT_SIZE, tu);
1197d76271d2SHyun Kwon
1198d76271d2SHyun Kwon vid_kbytes = mode->clock * (dp->config.bpp / 8);
1199d76271d2SHyun Kwon bw = drm_dp_bw_code_to_link_rate(dp->mode.bw_code);
1200d76271d2SHyun Kwon avg_bytes_per_tu = vid_kbytes * tu / (dp->mode.lane_cnt * bw / 1000);
1201d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MIN_BYTES_PER_TU,
1202d76271d2SHyun Kwon avg_bytes_per_tu / 1000);
1203d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_FRAC_BYTES_PER_TU,
1204d76271d2SHyun Kwon avg_bytes_per_tu % 1000);
1205d76271d2SHyun Kwon
1206d76271d2SHyun Kwon /* Configure the initial wait cycle based on transfer unit size */
1207d76271d2SHyun Kwon if (tu < (avg_bytes_per_tu / 1000))
1208d76271d2SHyun Kwon init_wait = 0;
1209d76271d2SHyun Kwon else if ((avg_bytes_per_tu / 1000) <= 4)
1210d76271d2SHyun Kwon init_wait = tu;
1211d76271d2SHyun Kwon else
1212d76271d2SHyun Kwon init_wait = tu - avg_bytes_per_tu / 1000;
1213d76271d2SHyun Kwon
1214d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INIT_WAIT, init_wait);
1215d76271d2SHyun Kwon }
1216d76271d2SHyun Kwon
1217d76271d2SHyun Kwon /**
1218d76271d2SHyun Kwon * zynqmp_dp_encoder_mode_set_stream - Configure the main stream
1219d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1220d76271d2SHyun Kwon * @mode: requested display mode
1221d76271d2SHyun Kwon *
1222d76271d2SHyun Kwon * Configure the main stream based on the requested mode @mode. Calculation is
1223d76271d2SHyun Kwon * based on IP core specification.
1224d76271d2SHyun Kwon */
zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp * dp,const struct drm_display_mode * mode)1225d76271d2SHyun Kwon static void zynqmp_dp_encoder_mode_set_stream(struct zynqmp_dp *dp,
1226d76271d2SHyun Kwon const struct drm_display_mode *mode)
1227d76271d2SHyun Kwon {
1228d76271d2SHyun Kwon u8 lane_cnt = dp->mode.lane_cnt;
1229d76271d2SHyun Kwon u32 reg, wpl;
1230d76271d2SHyun Kwon unsigned int rate;
1231d76271d2SHyun Kwon
1232d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HTOTAL, mode->htotal);
1233d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VTOTAL, mode->vtotal);
1234d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_POLARITY,
1235d76271d2SHyun Kwon (!!(mode->flags & DRM_MODE_FLAG_PVSYNC) <<
1236d76271d2SHyun Kwon ZYNQMP_DP_MAIN_STREAM_POLARITY_VSYNC_SHIFT) |
1237d76271d2SHyun Kwon (!!(mode->flags & DRM_MODE_FLAG_PHSYNC) <<
1238d76271d2SHyun Kwon ZYNQMP_DP_MAIN_STREAM_POLARITY_HSYNC_SHIFT));
1239d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HSWIDTH,
1240d76271d2SHyun Kwon mode->hsync_end - mode->hsync_start);
1241d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VSWIDTH,
1242d76271d2SHyun Kwon mode->vsync_end - mode->vsync_start);
1243d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HRES, mode->hdisplay);
1244d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VRES, mode->vdisplay);
1245d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_HSTART,
1246d76271d2SHyun Kwon mode->htotal - mode->hsync_start);
1247d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_VSTART,
1248d76271d2SHyun Kwon mode->vtotal - mode->vsync_start);
1249d76271d2SHyun Kwon
125047e801bdSLaurent Pinchart /* In synchronous mode, set the dividers */
1251d76271d2SHyun Kwon if (dp->config.misc0 & ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK) {
1252d76271d2SHyun Kwon reg = drm_dp_bw_code_to_link_rate(dp->mode.bw_code);
1253d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_N_VID, reg);
1254d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_M_VID, mode->clock);
1255c979296eSLaurent Pinchart rate = zynqmp_dpsub_get_audio_clk_rate(dp->dpsub);
1256d76271d2SHyun Kwon if (rate) {
1257d76271d2SHyun Kwon dev_dbg(dp->dev, "Audio rate: %d\n", rate / 512);
1258d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TX_N_AUD, reg);
1259d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TX_M_AUD, rate / 1000);
1260d76271d2SHyun Kwon }
1261d76271d2SHyun Kwon }
1262d76271d2SHyun Kwon
1263d76271d2SHyun Kwon /* Only 2 channel audio is supported now */
1264c979296eSLaurent Pinchart if (zynqmp_dpsub_audio_enabled(dp->dpsub))
1265d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CHANNELS, 1);
1266d76271d2SHyun Kwon
1267d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_USER_PIX_WIDTH, 1);
1268d76271d2SHyun Kwon
1269d76271d2SHyun Kwon /* Translate to the native 16 bit datapath based on IP core spec */
1270d76271d2SHyun Kwon wpl = (mode->hdisplay * dp->config.bpp + 15) / 16;
1271d76271d2SHyun Kwon reg = wpl + wpl % lane_cnt - lane_cnt;
1272d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_USER_DATA_COUNT_PER_LANE, reg);
1273d76271d2SHyun Kwon }
1274d76271d2SHyun Kwon
1275d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
127656167161SLaurent Pinchart * DISP Configuration
127756167161SLaurent Pinchart */
127856167161SLaurent Pinchart
zynqmp_dp_disp_enable(struct zynqmp_dp * dp,struct drm_bridge_state * old_bridge_state)127956167161SLaurent Pinchart static void zynqmp_dp_disp_enable(struct zynqmp_dp *dp,
128056167161SLaurent Pinchart struct drm_bridge_state *old_bridge_state)
128156167161SLaurent Pinchart {
128256167161SLaurent Pinchart enum zynqmp_dpsub_layer_id layer_id;
128356167161SLaurent Pinchart struct zynqmp_disp_layer *layer;
128456167161SLaurent Pinchart const struct drm_format_info *info;
128556167161SLaurent Pinchart
128656167161SLaurent Pinchart if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
128756167161SLaurent Pinchart layer_id = ZYNQMP_DPSUB_LAYER_VID;
128856167161SLaurent Pinchart else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
128956167161SLaurent Pinchart layer_id = ZYNQMP_DPSUB_LAYER_GFX;
129056167161SLaurent Pinchart else
129156167161SLaurent Pinchart return;
129256167161SLaurent Pinchart
129356167161SLaurent Pinchart layer = dp->dpsub->layers[layer_id];
129456167161SLaurent Pinchart
129556167161SLaurent Pinchart /* TODO: Make the format configurable. */
129656167161SLaurent Pinchart info = drm_format_info(DRM_FORMAT_YUV422);
129756167161SLaurent Pinchart zynqmp_disp_layer_set_format(layer, info);
129856167161SLaurent Pinchart zynqmp_disp_layer_enable(layer, ZYNQMP_DPSUB_LAYER_LIVE);
129956167161SLaurent Pinchart
130056167161SLaurent Pinchart if (layer_id == ZYNQMP_DPSUB_LAYER_GFX)
130156167161SLaurent Pinchart zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, true, 255);
130256167161SLaurent Pinchart else
130356167161SLaurent Pinchart zynqmp_disp_blend_set_global_alpha(dp->dpsub->disp, false, 0);
130456167161SLaurent Pinchart
130556167161SLaurent Pinchart zynqmp_disp_enable(dp->dpsub->disp);
130656167161SLaurent Pinchart }
130756167161SLaurent Pinchart
zynqmp_dp_disp_disable(struct zynqmp_dp * dp,struct drm_bridge_state * old_bridge_state)130856167161SLaurent Pinchart static void zynqmp_dp_disp_disable(struct zynqmp_dp *dp,
130956167161SLaurent Pinchart struct drm_bridge_state *old_bridge_state)
131056167161SLaurent Pinchart {
131156167161SLaurent Pinchart struct zynqmp_disp_layer *layer;
131256167161SLaurent Pinchart
131356167161SLaurent Pinchart if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_VIDEO))
131456167161SLaurent Pinchart layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_VID];
131556167161SLaurent Pinchart else if (dp->dpsub->connected_ports & BIT(ZYNQMP_DPSUB_PORT_LIVE_GFX))
131656167161SLaurent Pinchart layer = dp->dpsub->layers[ZYNQMP_DPSUB_LAYER_GFX];
131756167161SLaurent Pinchart else
131856167161SLaurent Pinchart return;
131956167161SLaurent Pinchart
132056167161SLaurent Pinchart zynqmp_disp_disable(dp->dpsub->disp);
132156167161SLaurent Pinchart zynqmp_disp_layer_disable(layer);
132256167161SLaurent Pinchart }
132356167161SLaurent Pinchart
132456167161SLaurent Pinchart /* -----------------------------------------------------------------------------
132547e801bdSLaurent Pinchart * DRM Bridge
1326d76271d2SHyun Kwon */
1327d76271d2SHyun Kwon
zynqmp_dp_bridge_attach(struct drm_bridge * bridge,enum drm_bridge_attach_flags flags)132847e801bdSLaurent Pinchart static int zynqmp_dp_bridge_attach(struct drm_bridge *bridge,
132947e801bdSLaurent Pinchart enum drm_bridge_attach_flags flags)
1330d76271d2SHyun Kwon {
13312374b6eaSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
13322374b6eaSLaurent Pinchart int ret;
13332374b6eaSLaurent Pinchart
13342dfd045cSLaurent Pinchart /* Initialize and register the AUX adapter. */
13352dfd045cSLaurent Pinchart ret = zynqmp_dp_aux_init(dp);
13362dfd045cSLaurent Pinchart if (ret) {
13372dfd045cSLaurent Pinchart dev_err(dp->dev, "failed to initialize DP aux\n");
13382dfd045cSLaurent Pinchart return ret;
13392dfd045cSLaurent Pinchart }
13402dfd045cSLaurent Pinchart
1341bd68b9b3SLaurent Pinchart if (dp->next_bridge) {
1342bd68b9b3SLaurent Pinchart ret = drm_bridge_attach(bridge->encoder, dp->next_bridge,
1343cbb11ef9SLaurent Pinchart bridge, flags);
1344bd68b9b3SLaurent Pinchart if (ret < 0)
13452dfd045cSLaurent Pinchart goto error;
13462dfd045cSLaurent Pinchart }
13472dfd045cSLaurent Pinchart
13482dfd045cSLaurent Pinchart /* Now that initialisation is complete, enable interrupts. */
13492dfd045cSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_ALL);
13502dfd045cSLaurent Pinchart
13512dfd045cSLaurent Pinchart return 0;
13522dfd045cSLaurent Pinchart
13532dfd045cSLaurent Pinchart error:
13542dfd045cSLaurent Pinchart zynqmp_dp_aux_cleanup(dp);
1355bd68b9b3SLaurent Pinchart return ret;
1356bd68b9b3SLaurent Pinchart }
1357bd68b9b3SLaurent Pinchart
zynqmp_dp_bridge_detach(struct drm_bridge * bridge)13582dfd045cSLaurent Pinchart static void zynqmp_dp_bridge_detach(struct drm_bridge *bridge)
13592dfd045cSLaurent Pinchart {
13602dfd045cSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
13612dfd045cSLaurent Pinchart
13622dfd045cSLaurent Pinchart zynqmp_dp_aux_cleanup(dp);
136347e801bdSLaurent Pinchart }
136447e801bdSLaurent Pinchart
1365cec9e59cSNathan Huckleberry static enum drm_mode_status
zynqmp_dp_bridge_mode_valid(struct drm_bridge * bridge,const struct drm_display_info * info,const struct drm_display_mode * mode)1366cec9e59cSNathan Huckleberry zynqmp_dp_bridge_mode_valid(struct drm_bridge *bridge,
136747e801bdSLaurent Pinchart const struct drm_display_info *info,
136847e801bdSLaurent Pinchart const struct drm_display_mode *mode)
136947e801bdSLaurent Pinchart {
137047e801bdSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
137147e801bdSLaurent Pinchart int rate;
137247e801bdSLaurent Pinchart
137347e801bdSLaurent Pinchart if (mode->clock > ZYNQMP_MAX_FREQ) {
137447e801bdSLaurent Pinchart dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n",
137547e801bdSLaurent Pinchart mode->name);
137647e801bdSLaurent Pinchart drm_mode_debug_printmodeline(mode);
137747e801bdSLaurent Pinchart return MODE_CLOCK_HIGH;
137847e801bdSLaurent Pinchart }
137947e801bdSLaurent Pinchart
138047e801bdSLaurent Pinchart /* Check with link rate and lane count */
138147e801bdSLaurent Pinchart rate = zynqmp_dp_max_rate(dp->link_config.max_rate,
138247e801bdSLaurent Pinchart dp->link_config.max_lanes, dp->config.bpp);
138347e801bdSLaurent Pinchart if (mode->clock > rate) {
138447e801bdSLaurent Pinchart dev_dbg(dp->dev, "filtered mode %s for high pixel rate\n",
138547e801bdSLaurent Pinchart mode->name);
138647e801bdSLaurent Pinchart drm_mode_debug_printmodeline(mode);
138747e801bdSLaurent Pinchart return MODE_CLOCK_HIGH;
138847e801bdSLaurent Pinchart }
138947e801bdSLaurent Pinchart
139047e801bdSLaurent Pinchart return MODE_OK;
139147e801bdSLaurent Pinchart }
139247e801bdSLaurent Pinchart
zynqmp_dp_bridge_atomic_enable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)139347e801bdSLaurent Pinchart static void zynqmp_dp_bridge_atomic_enable(struct drm_bridge *bridge,
139447e801bdSLaurent Pinchart struct drm_bridge_state *old_bridge_state)
139547e801bdSLaurent Pinchart {
139647e801bdSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
139747e801bdSLaurent Pinchart struct drm_atomic_state *state = old_bridge_state->base.state;
139847e801bdSLaurent Pinchart const struct drm_crtc_state *crtc_state;
139947e801bdSLaurent Pinchart const struct drm_display_mode *adjusted_mode;
140047e801bdSLaurent Pinchart const struct drm_display_mode *mode;
140147e801bdSLaurent Pinchart struct drm_connector *connector;
140247e801bdSLaurent Pinchart struct drm_crtc *crtc;
140347e801bdSLaurent Pinchart unsigned int i;
140447e801bdSLaurent Pinchart int rate;
140547e801bdSLaurent Pinchart int ret;
140647e801bdSLaurent Pinchart
140747e801bdSLaurent Pinchart pm_runtime_get_sync(dp->dev);
140847e801bdSLaurent Pinchart
140956167161SLaurent Pinchart zynqmp_dp_disp_enable(dp, old_bridge_state);
141056167161SLaurent Pinchart
141147e801bdSLaurent Pinchart /*
141247e801bdSLaurent Pinchart * Retrieve the CRTC mode and adjusted mode. This requires a little
141347e801bdSLaurent Pinchart * dance to go from the bridge to the encoder, to the connector and to
141447e801bdSLaurent Pinchart * the CRTC.
141547e801bdSLaurent Pinchart */
141647e801bdSLaurent Pinchart connector = drm_atomic_get_new_connector_for_encoder(state,
141747e801bdSLaurent Pinchart bridge->encoder);
141847e801bdSLaurent Pinchart crtc = drm_atomic_get_new_connector_state(state, connector)->crtc;
141947e801bdSLaurent Pinchart crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
142047e801bdSLaurent Pinchart adjusted_mode = &crtc_state->adjusted_mode;
142147e801bdSLaurent Pinchart mode = &crtc_state->mode;
142247e801bdSLaurent Pinchart
14235827398bSLaurent Pinchart zynqmp_dp_set_format(dp, &connector->display_info,
14245827398bSLaurent Pinchart ZYNQMP_DPSUB_FORMAT_RGB, 8);
142547e801bdSLaurent Pinchart
142647e801bdSLaurent Pinchart /* Check again as bpp or format might have been changed */
142747e801bdSLaurent Pinchart rate = zynqmp_dp_max_rate(dp->link_config.max_rate,
142847e801bdSLaurent Pinchart dp->link_config.max_lanes, dp->config.bpp);
142947e801bdSLaurent Pinchart if (mode->clock > rate) {
143047e801bdSLaurent Pinchart dev_err(dp->dev, "mode %s has too high pixel rate\n",
143147e801bdSLaurent Pinchart mode->name);
143247e801bdSLaurent Pinchart drm_mode_debug_printmodeline(mode);
143347e801bdSLaurent Pinchart }
143447e801bdSLaurent Pinchart
143547e801bdSLaurent Pinchart /* Configure the mode */
143647e801bdSLaurent Pinchart ret = zynqmp_dp_mode_configure(dp, adjusted_mode->clock, 0);
143747e801bdSLaurent Pinchart if (ret < 0) {
143847e801bdSLaurent Pinchart pm_runtime_put_sync(dp->dev);
143947e801bdSLaurent Pinchart return;
144047e801bdSLaurent Pinchart }
144147e801bdSLaurent Pinchart
144247e801bdSLaurent Pinchart zynqmp_dp_encoder_mode_set_transfer_unit(dp, adjusted_mode);
144347e801bdSLaurent Pinchart zynqmp_dp_encoder_mode_set_stream(dp, adjusted_mode);
144447e801bdSLaurent Pinchart
144547e801bdSLaurent Pinchart /* Enable the encoder */
144647e801bdSLaurent Pinchart dp->enabled = true;
144747e801bdSLaurent Pinchart zynqmp_dp_update_misc(dp);
1448c979296eSLaurent Pinchart if (zynqmp_dpsub_audio_enabled(dp->dpsub))
144947e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 1);
145047e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN, 0);
145147e801bdSLaurent Pinchart if (dp->status == connector_status_connected) {
145247e801bdSLaurent Pinchart for (i = 0; i < 3; i++) {
145347e801bdSLaurent Pinchart ret = drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER,
145447e801bdSLaurent Pinchart DP_SET_POWER_D0);
145547e801bdSLaurent Pinchart if (ret == 1)
145647e801bdSLaurent Pinchart break;
145747e801bdSLaurent Pinchart usleep_range(300, 500);
145847e801bdSLaurent Pinchart }
145947e801bdSLaurent Pinchart /* Some monitors take time to wake up properly */
146047e801bdSLaurent Pinchart msleep(zynqmp_dp_power_on_delay_ms);
146147e801bdSLaurent Pinchart }
146247e801bdSLaurent Pinchart if (ret != 1)
146347e801bdSLaurent Pinchart dev_dbg(dp->dev, "DP aux failed\n");
146447e801bdSLaurent Pinchart else
146547e801bdSLaurent Pinchart zynqmp_dp_train_loop(dp);
146647e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_SOFTWARE_RESET,
146747e801bdSLaurent Pinchart ZYNQMP_DP_SOFTWARE_RESET_ALL);
146847e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 1);
146947e801bdSLaurent Pinchart }
147047e801bdSLaurent Pinchart
zynqmp_dp_bridge_atomic_disable(struct drm_bridge * bridge,struct drm_bridge_state * old_bridge_state)147147e801bdSLaurent Pinchart static void zynqmp_dp_bridge_atomic_disable(struct drm_bridge *bridge,
147247e801bdSLaurent Pinchart struct drm_bridge_state *old_bridge_state)
147347e801bdSLaurent Pinchart {
147447e801bdSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
147547e801bdSLaurent Pinchart
147647e801bdSLaurent Pinchart dp->enabled = false;
147747e801bdSLaurent Pinchart cancel_delayed_work(&dp->hpd_work);
147847e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_MAIN_STREAM_ENABLE, 0);
147947e801bdSLaurent Pinchart drm_dp_dpcd_writeb(&dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
148047e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
148147e801bdSLaurent Pinchart ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
1482c979296eSLaurent Pinchart if (zynqmp_dpsub_audio_enabled(dp->dpsub))
148347e801bdSLaurent Pinchart zynqmp_dp_write(dp, ZYNQMP_DP_TX_AUDIO_CONTROL, 0);
148456167161SLaurent Pinchart
148556167161SLaurent Pinchart zynqmp_dp_disp_disable(dp, old_bridge_state);
148656167161SLaurent Pinchart
148747e801bdSLaurent Pinchart pm_runtime_put_sync(dp->dev);
148847e801bdSLaurent Pinchart }
148947e801bdSLaurent Pinchart
149047e801bdSLaurent Pinchart #define ZYNQMP_DP_MIN_H_BACKPORCH 20
149147e801bdSLaurent Pinchart
zynqmp_dp_bridge_atomic_check(struct drm_bridge * bridge,struct drm_bridge_state * bridge_state,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)149247e801bdSLaurent Pinchart static int zynqmp_dp_bridge_atomic_check(struct drm_bridge *bridge,
149347e801bdSLaurent Pinchart struct drm_bridge_state *bridge_state,
149447e801bdSLaurent Pinchart struct drm_crtc_state *crtc_state,
149547e801bdSLaurent Pinchart struct drm_connector_state *conn_state)
149647e801bdSLaurent Pinchart {
149747e801bdSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
149847e801bdSLaurent Pinchart struct drm_display_mode *mode = &crtc_state->mode;
149947e801bdSLaurent Pinchart struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
150047e801bdSLaurent Pinchart int diff = mode->htotal - mode->hsync_end;
150147e801bdSLaurent Pinchart
150247e801bdSLaurent Pinchart /*
150347e801bdSLaurent Pinchart * ZynqMP DP requires horizontal backporch to be greater than 12.
150447e801bdSLaurent Pinchart * This limitation may not be compatible with the sink device.
150547e801bdSLaurent Pinchart */
150647e801bdSLaurent Pinchart if (diff < ZYNQMP_DP_MIN_H_BACKPORCH) {
150747e801bdSLaurent Pinchart int vrefresh = (adjusted_mode->clock * 1000) /
150847e801bdSLaurent Pinchart (adjusted_mode->vtotal * adjusted_mode->htotal);
150947e801bdSLaurent Pinchart
151047e801bdSLaurent Pinchart dev_dbg(dp->dev, "hbackporch adjusted: %d to %d",
151147e801bdSLaurent Pinchart diff, ZYNQMP_DP_MIN_H_BACKPORCH - diff);
151247e801bdSLaurent Pinchart diff = ZYNQMP_DP_MIN_H_BACKPORCH - diff;
151347e801bdSLaurent Pinchart adjusted_mode->htotal += diff;
151447e801bdSLaurent Pinchart adjusted_mode->clock = adjusted_mode->vtotal *
151547e801bdSLaurent Pinchart adjusted_mode->htotal * vrefresh / 1000;
151647e801bdSLaurent Pinchart }
151747e801bdSLaurent Pinchart
151847e801bdSLaurent Pinchart return 0;
151947e801bdSLaurent Pinchart }
152047e801bdSLaurent Pinchart
zynqmp_dp_bridge_detect(struct drm_bridge * bridge)152147e801bdSLaurent Pinchart static enum drm_connector_status zynqmp_dp_bridge_detect(struct drm_bridge *bridge)
152247e801bdSLaurent Pinchart {
152347e801bdSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
1524d76271d2SHyun Kwon struct zynqmp_dp_link_config *link_config = &dp->link_config;
1525d76271d2SHyun Kwon u32 state, i;
1526d76271d2SHyun Kwon int ret;
1527d76271d2SHyun Kwon
1528d76271d2SHyun Kwon /*
1529d76271d2SHyun Kwon * This is from heuristic. It takes some delay (ex, 100 ~ 500 msec) to
1530d76271d2SHyun Kwon * get the HPD signal with some monitors.
1531d76271d2SHyun Kwon */
1532d76271d2SHyun Kwon for (i = 0; i < 10; i++) {
1533d76271d2SHyun Kwon state = zynqmp_dp_read(dp, ZYNQMP_DP_INTERRUPT_SIGNAL_STATE);
1534d76271d2SHyun Kwon if (state & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD)
1535d76271d2SHyun Kwon break;
1536d76271d2SHyun Kwon msleep(100);
1537d76271d2SHyun Kwon }
1538d76271d2SHyun Kwon
1539d76271d2SHyun Kwon if (state & ZYNQMP_DP_INTERRUPT_SIGNAL_STATE_HPD) {
1540d76271d2SHyun Kwon ret = drm_dp_dpcd_read(&dp->aux, 0x0, dp->dpcd,
1541d76271d2SHyun Kwon sizeof(dp->dpcd));
1542d76271d2SHyun Kwon if (ret < 0) {
1543be13d94bSColin Ian King dev_dbg(dp->dev, "DPCD read failed");
1544d76271d2SHyun Kwon goto disconnected;
1545d76271d2SHyun Kwon }
1546d76271d2SHyun Kwon
1547d76271d2SHyun Kwon link_config->max_rate = min_t(int,
1548d76271d2SHyun Kwon drm_dp_max_link_rate(dp->dpcd),
1549d76271d2SHyun Kwon DP_HIGH_BIT_RATE2);
1550d76271d2SHyun Kwon link_config->max_lanes = min_t(u8,
1551d76271d2SHyun Kwon drm_dp_max_lane_count(dp->dpcd),
1552d76271d2SHyun Kwon dp->num_lanes);
1553d76271d2SHyun Kwon
1554d76271d2SHyun Kwon dp->status = connector_status_connected;
1555d76271d2SHyun Kwon return connector_status_connected;
1556d76271d2SHyun Kwon }
1557d76271d2SHyun Kwon
1558d76271d2SHyun Kwon disconnected:
1559d76271d2SHyun Kwon dp->status = connector_status_disconnected;
1560d76271d2SHyun Kwon return connector_status_disconnected;
1561d76271d2SHyun Kwon }
1562d76271d2SHyun Kwon
zynqmp_dp_bridge_get_edid(struct drm_bridge * bridge,struct drm_connector * connector)156347e801bdSLaurent Pinchart static struct edid *zynqmp_dp_bridge_get_edid(struct drm_bridge *bridge,
156447e801bdSLaurent Pinchart struct drm_connector *connector)
156547e801bdSLaurent Pinchart {
156647e801bdSLaurent Pinchart struct zynqmp_dp *dp = bridge_to_dp(bridge);
156747e801bdSLaurent Pinchart
156847e801bdSLaurent Pinchart return drm_get_edid(connector, &dp->aux.ddc);
156947e801bdSLaurent Pinchart }
157047e801bdSLaurent Pinchart
157147e801bdSLaurent Pinchart static const struct drm_bridge_funcs zynqmp_dp_bridge_funcs = {
157247e801bdSLaurent Pinchart .attach = zynqmp_dp_bridge_attach,
15732dfd045cSLaurent Pinchart .detach = zynqmp_dp_bridge_detach,
157447e801bdSLaurent Pinchart .mode_valid = zynqmp_dp_bridge_mode_valid,
157547e801bdSLaurent Pinchart .atomic_enable = zynqmp_dp_bridge_atomic_enable,
157647e801bdSLaurent Pinchart .atomic_disable = zynqmp_dp_bridge_atomic_disable,
157747e801bdSLaurent Pinchart .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
157847e801bdSLaurent Pinchart .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
157947e801bdSLaurent Pinchart .atomic_reset = drm_atomic_helper_bridge_reset,
158047e801bdSLaurent Pinchart .atomic_check = zynqmp_dp_bridge_atomic_check,
158147e801bdSLaurent Pinchart .detect = zynqmp_dp_bridge_detect,
158247e801bdSLaurent Pinchart .get_edid = zynqmp_dp_bridge_get_edid,
158347e801bdSLaurent Pinchart };
158447e801bdSLaurent Pinchart
158547e801bdSLaurent Pinchart /* -----------------------------------------------------------------------------
1586d76271d2SHyun Kwon * Interrupt Handling
1587d76271d2SHyun Kwon */
1588d76271d2SHyun Kwon
1589d76271d2SHyun Kwon /**
1590d76271d2SHyun Kwon * zynqmp_dp_enable_vblank - Enable vblank
1591d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1592d76271d2SHyun Kwon *
1593d76271d2SHyun Kwon * Enable vblank interrupt
1594d76271d2SHyun Kwon */
zynqmp_dp_enable_vblank(struct zynqmp_dp * dp)1595d76271d2SHyun Kwon void zynqmp_dp_enable_vblank(struct zynqmp_dp *dp)
1596d76271d2SHyun Kwon {
1597d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INT_EN, ZYNQMP_DP_INT_VBLANK_START);
1598d76271d2SHyun Kwon }
1599d76271d2SHyun Kwon
1600d76271d2SHyun Kwon /**
1601d76271d2SHyun Kwon * zynqmp_dp_disable_vblank - Disable vblank
1602d76271d2SHyun Kwon * @dp: DisplayPort IP core structure
1603d76271d2SHyun Kwon *
1604d76271d2SHyun Kwon * Disable vblank interrupt
1605d76271d2SHyun Kwon */
zynqmp_dp_disable_vblank(struct zynqmp_dp * dp)1606d76271d2SHyun Kwon void zynqmp_dp_disable_vblank(struct zynqmp_dp *dp)
1607d76271d2SHyun Kwon {
1608d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_VBLANK_START);
1609d76271d2SHyun Kwon }
1610d76271d2SHyun Kwon
zynqmp_dp_hpd_work_func(struct work_struct * work)1611d76271d2SHyun Kwon static void zynqmp_dp_hpd_work_func(struct work_struct *work)
1612d76271d2SHyun Kwon {
1613eb2d64bfSLaurent Pinchart struct zynqmp_dp *dp = container_of(work, struct zynqmp_dp,
1614eb2d64bfSLaurent Pinchart hpd_work.work);
1615eb2d64bfSLaurent Pinchart enum drm_connector_status status;
1616d76271d2SHyun Kwon
1617eb2d64bfSLaurent Pinchart status = zynqmp_dp_bridge_detect(&dp->bridge);
1618eb2d64bfSLaurent Pinchart drm_bridge_hpd_notify(&dp->bridge, status);
1619d76271d2SHyun Kwon }
1620d76271d2SHyun Kwon
zynqmp_dp_irq_handler(int irq,void * data)1621d76271d2SHyun Kwon static irqreturn_t zynqmp_dp_irq_handler(int irq, void *data)
1622d76271d2SHyun Kwon {
1623d76271d2SHyun Kwon struct zynqmp_dp *dp = (struct zynqmp_dp *)data;
1624d76271d2SHyun Kwon u32 status, mask;
1625d76271d2SHyun Kwon
1626d76271d2SHyun Kwon status = zynqmp_dp_read(dp, ZYNQMP_DP_INT_STATUS);
1627d76271d2SHyun Kwon mask = zynqmp_dp_read(dp, ZYNQMP_DP_INT_MASK);
1628d76271d2SHyun Kwon if (!(status & ~mask))
1629d76271d2SHyun Kwon return IRQ_NONE;
1630d76271d2SHyun Kwon
1631d76271d2SHyun Kwon /* dbg for diagnostic, but not much that the driver can do */
1632d76271d2SHyun Kwon if (status & ZYNQMP_DP_INT_CHBUF_UNDERFLW_MASK)
1633d76271d2SHyun Kwon dev_dbg_ratelimited(dp->dev, "underflow interrupt\n");
1634d76271d2SHyun Kwon if (status & ZYNQMP_DP_INT_CHBUF_OVERFLW_MASK)
1635d76271d2SHyun Kwon dev_dbg_ratelimited(dp->dev, "overflow interrupt\n");
1636d76271d2SHyun Kwon
1637d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INT_STATUS, status);
1638d76271d2SHyun Kwon
1639d76271d2SHyun Kwon if (status & ZYNQMP_DP_INT_VBLANK_START)
16404ce6ecd4SLaurent Pinchart zynqmp_dpsub_drm_handle_vblank(dp->dpsub);
1641d76271d2SHyun Kwon
1642d76271d2SHyun Kwon if (status & ZYNQMP_DP_INT_HPD_EVENT)
1643d76271d2SHyun Kwon schedule_delayed_work(&dp->hpd_work, 0);
1644d76271d2SHyun Kwon
1645d76271d2SHyun Kwon if (status & ZYNQMP_DP_INT_HPD_IRQ) {
1646d76271d2SHyun Kwon int ret;
1647d76271d2SHyun Kwon u8 status[DP_LINK_STATUS_SIZE + 2];
1648d76271d2SHyun Kwon
1649d76271d2SHyun Kwon ret = drm_dp_dpcd_read(&dp->aux, DP_SINK_COUNT, status,
1650d76271d2SHyun Kwon DP_LINK_STATUS_SIZE + 2);
1651d76271d2SHyun Kwon if (ret < 0)
1652d76271d2SHyun Kwon goto handled;
1653d76271d2SHyun Kwon
1654d76271d2SHyun Kwon if (status[4] & DP_LINK_STATUS_UPDATED ||
1655d76271d2SHyun Kwon !drm_dp_clock_recovery_ok(&status[2], dp->mode.lane_cnt) ||
1656d76271d2SHyun Kwon !drm_dp_channel_eq_ok(&status[2], dp->mode.lane_cnt)) {
1657d76271d2SHyun Kwon zynqmp_dp_train_loop(dp);
1658d76271d2SHyun Kwon }
1659d76271d2SHyun Kwon }
1660d76271d2SHyun Kwon
1661d76271d2SHyun Kwon handled:
1662d76271d2SHyun Kwon return IRQ_HANDLED;
1663d76271d2SHyun Kwon }
1664d76271d2SHyun Kwon
1665d76271d2SHyun Kwon /* -----------------------------------------------------------------------------
1666d76271d2SHyun Kwon * Initialization & Cleanup
1667d76271d2SHyun Kwon */
1668d76271d2SHyun Kwon
zynqmp_dp_probe(struct zynqmp_dpsub * dpsub)16696ca91bb4SLaurent Pinchart int zynqmp_dp_probe(struct zynqmp_dpsub *dpsub)
1670d76271d2SHyun Kwon {
1671d76271d2SHyun Kwon struct platform_device *pdev = to_platform_device(dpsub->dev);
16725889ee59SLaurent Pinchart struct drm_bridge *bridge;
1673d76271d2SHyun Kwon struct zynqmp_dp *dp;
1674d76271d2SHyun Kwon struct resource *res;
1675d76271d2SHyun Kwon int ret;
1676d76271d2SHyun Kwon
16776ca91bb4SLaurent Pinchart dp = kzalloc(sizeof(*dp), GFP_KERNEL);
1678d76271d2SHyun Kwon if (!dp)
1679d76271d2SHyun Kwon return -ENOMEM;
1680d76271d2SHyun Kwon
1681d76271d2SHyun Kwon dp->dev = &pdev->dev;
1682d76271d2SHyun Kwon dp->dpsub = dpsub;
1683d76271d2SHyun Kwon dp->status = connector_status_disconnected;
1684d76271d2SHyun Kwon
1685d76271d2SHyun Kwon INIT_DELAYED_WORK(&dp->hpd_work, zynqmp_dp_hpd_work_func);
1686d76271d2SHyun Kwon
1687d76271d2SHyun Kwon /* Acquire all resources (IOMEM, IRQ and PHYs). */
1688d76271d2SHyun Kwon res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dp");
1689d76271d2SHyun Kwon dp->iomem = devm_ioremap_resource(dp->dev, res);
16906ca91bb4SLaurent Pinchart if (IS_ERR(dp->iomem)) {
16916ca91bb4SLaurent Pinchart ret = PTR_ERR(dp->iomem);
16926ca91bb4SLaurent Pinchart goto err_free;
16936ca91bb4SLaurent Pinchart }
1694d76271d2SHyun Kwon
1695d76271d2SHyun Kwon dp->irq = platform_get_irq(pdev, 0);
16966ca91bb4SLaurent Pinchart if (dp->irq < 0) {
16976ca91bb4SLaurent Pinchart ret = dp->irq;
16986ca91bb4SLaurent Pinchart goto err_free;
16996ca91bb4SLaurent Pinchart }
1700d76271d2SHyun Kwon
1701d76271d2SHyun Kwon dp->reset = devm_reset_control_get(dp->dev, NULL);
1702d76271d2SHyun Kwon if (IS_ERR(dp->reset)) {
1703d76271d2SHyun Kwon if (PTR_ERR(dp->reset) != -EPROBE_DEFER)
1704d76271d2SHyun Kwon dev_err(dp->dev, "failed to get reset: %ld\n",
1705d76271d2SHyun Kwon PTR_ERR(dp->reset));
17066ca91bb4SLaurent Pinchart ret = PTR_ERR(dp->reset);
17076ca91bb4SLaurent Pinchart goto err_free;
1708d76271d2SHyun Kwon }
1709d76271d2SHyun Kwon
1710a338619bSQuanyang Wang ret = zynqmp_dp_reset(dp, false);
1711a338619bSQuanyang Wang if (ret < 0)
17126ca91bb4SLaurent Pinchart goto err_free;
1713a338619bSQuanyang Wang
1714d76271d2SHyun Kwon ret = zynqmp_dp_phy_probe(dp);
1715d76271d2SHyun Kwon if (ret)
1716a338619bSQuanyang Wang goto err_reset;
1717d76271d2SHyun Kwon
17185889ee59SLaurent Pinchart /* Initialize the bridge. */
17195889ee59SLaurent Pinchart bridge = &dp->bridge;
17205889ee59SLaurent Pinchart bridge->funcs = &zynqmp_dp_bridge_funcs;
17215889ee59SLaurent Pinchart bridge->ops = DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID
17225889ee59SLaurent Pinchart | DRM_BRIDGE_OP_HPD;
17235889ee59SLaurent Pinchart bridge->type = DRM_MODE_CONNECTOR_DisplayPort;
17245889ee59SLaurent Pinchart dpsub->bridge = bridge;
17255889ee59SLaurent Pinchart
1726bd68b9b3SLaurent Pinchart /*
1727bd68b9b3SLaurent Pinchart * Acquire the next bridge in the chain. Ignore errors caused by port@5
1728bd68b9b3SLaurent Pinchart * not being connected for backward-compatibility with older DTs.
1729bd68b9b3SLaurent Pinchart */
1730bd68b9b3SLaurent Pinchart ret = drm_of_find_panel_or_bridge(dp->dev->of_node, 5, 0, NULL,
1731bd68b9b3SLaurent Pinchart &dp->next_bridge);
1732bd68b9b3SLaurent Pinchart if (ret < 0 && ret != -ENODEV)
1733bd68b9b3SLaurent Pinchart goto err_reset;
1734bd68b9b3SLaurent Pinchart
1735d76271d2SHyun Kwon /* Initialize the hardware. */
17365889ee59SLaurent Pinchart dp->config.misc0 &= ~ZYNQMP_DP_MAIN_STREAM_MISC0_SYNC_LOCK;
17375889ee59SLaurent Pinchart zynqmp_dp_set_format(dp, NULL, ZYNQMP_DPSUB_FORMAT_RGB, 8);
17385889ee59SLaurent Pinchart
1739d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TX_PHY_POWER_DOWN,
1740d76271d2SHyun Kwon ZYNQMP_DP_TX_PHY_POWER_DOWN_ALL);
1741d76271d2SHyun Kwon zynqmp_dp_set(dp, ZYNQMP_DP_PHY_RESET, ZYNQMP_DP_PHY_RESET_ALL_RESET);
1742d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_FORCE_SCRAMBLER_RESET, 1);
1743d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0);
1744d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0xffffffff);
1745d76271d2SHyun Kwon
1746d76271d2SHyun Kwon ret = zynqmp_dp_phy_init(dp);
1747d76271d2SHyun Kwon if (ret)
1748a338619bSQuanyang Wang goto err_reset;
1749d76271d2SHyun Kwon
1750d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 1);
1751d76271d2SHyun Kwon
1752d76271d2SHyun Kwon /*
1753d76271d2SHyun Kwon * Now that the hardware is initialized and won't generate spurious
1754d76271d2SHyun Kwon * interrupts, request the IRQ.
1755d76271d2SHyun Kwon */
1756d76271d2SHyun Kwon ret = devm_request_threaded_irq(dp->dev, dp->irq, NULL,
1757d76271d2SHyun Kwon zynqmp_dp_irq_handler, IRQF_ONESHOT,
1758d76271d2SHyun Kwon dev_name(dp->dev), dp);
1759d76271d2SHyun Kwon if (ret < 0)
1760a338619bSQuanyang Wang goto err_phy_exit;
1761d76271d2SHyun Kwon
17626ca91bb4SLaurent Pinchart dpsub->dp = dp;
17636ca91bb4SLaurent Pinchart
1764d76271d2SHyun Kwon dev_dbg(dp->dev, "ZynqMP DisplayPort Tx probed with %u lanes\n",
1765d76271d2SHyun Kwon dp->num_lanes);
1766d76271d2SHyun Kwon
1767d76271d2SHyun Kwon return 0;
1768d76271d2SHyun Kwon
1769a338619bSQuanyang Wang err_phy_exit:
1770d76271d2SHyun Kwon zynqmp_dp_phy_exit(dp);
1771a338619bSQuanyang Wang err_reset:
1772a338619bSQuanyang Wang zynqmp_dp_reset(dp, true);
17736ca91bb4SLaurent Pinchart err_free:
17746ca91bb4SLaurent Pinchart kfree(dp);
1775d76271d2SHyun Kwon return ret;
1776d76271d2SHyun Kwon }
1777d76271d2SHyun Kwon
zynqmp_dp_remove(struct zynqmp_dpsub * dpsub)1778d76271d2SHyun Kwon void zynqmp_dp_remove(struct zynqmp_dpsub *dpsub)
1779d76271d2SHyun Kwon {
1780d76271d2SHyun Kwon struct zynqmp_dp *dp = dpsub->dp;
1781d76271d2SHyun Kwon
1782d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, ZYNQMP_DP_INT_ALL);
1783d76271d2SHyun Kwon disable_irq(dp->irq);
1784d76271d2SHyun Kwon
1785d76271d2SHyun Kwon cancel_delayed_work_sync(&dp->hpd_work);
1786d76271d2SHyun Kwon
1787d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_TRANSMITTER_ENABLE, 0);
1788d76271d2SHyun Kwon zynqmp_dp_write(dp, ZYNQMP_DP_INT_DS, 0xffffffff);
1789d76271d2SHyun Kwon
1790d76271d2SHyun Kwon zynqmp_dp_phy_exit(dp);
1791a338619bSQuanyang Wang zynqmp_dp_reset(dp, true);
1792d76271d2SHyun Kwon }
1793