blob: 2ba09775478f45029ccaba6ac27c9766bf3a0983 [file] [log] [blame]
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +05301/* SPDX-License-Identifier: GPL-2.0-only */
2
3#include <console/console.h>
4#include <delay.h>
5#include <endian.h>
6#include <device/i2c_simple.h>
7#include <edid.h>
8#include <timer.h>
9#include <types.h>
10#include <soc/addressmap.h>
11#include "sn65dsi86bridge.h"
12
Elyes HAOUAS2826cdc2021-01-16 13:57:58 +010013#define BRIDGE_GETHIGHERBYTE(x) ((uint8_t)((x & 0xff00) >> 8))
14#define BRIDGE_GETLOWERBYTE(x) ((uint8_t)(x & 0x00ff))
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +053015
16/* fudge factor required to account for 8b/10b encoding */
17#define DP_CLK_FUDGE_NUM 10
18#define DP_CLK_FUDGE_DEN 8
19
20/* DPCD */
21#define DP_BRIDGE_DPCD_REV 0x700
22#define DP_BRIDGE_11 0x00
23#define DP_BRIDGE_12 0x01
24#define DP_BRIDGE_13 0x02
25#define DP_BRIDGE_14 0x03
26#define DP_BRIDGE_CONFIGURATION_SET 0x10a
27#define DP_MAX_LINK_RATE 0x001
28#define DP_MAX_LANE_COUNT 0x002
29#define DP_SUPPORTED_LINK_RATES 0x010 /* eDP 1.4 */
30#define DP_MAX_LINK_RATE 0x001
31#define DP_MAX_SUPPORTED_RATES 8 /* 16-bit little-endian */
32#define DP_LANE_COUNT_MASK 0xf
33
Vinod Polimera6ea80332021-03-17 16:11:12 +053034/* Backlight configuration */
35#define DP_BACKLIGHT_MODE_SET 0x721
36#define DP_BACKLIGHT_CONTROL_MODE_MASK 0x3
37#define DP_BACKLIGHT_CONTROL_MODE_DPCD 0x2
38#define DP_DISPLAY_CONTROL_REGISTER 0x720
39#define DP_BACKLIGHT_ENABLE 0x1
40#define DP_BACKLIGHT_BRIGHTNESS_MSB 0x722
41
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +053042/* link configuration */
43#define DP_LINK_BW_SET 0x100
44#define DP_LINK_BW_1_62 0x06
45#define DP_LINK_BW_2_7 0x0a
46#define DP_LINK_BW_5_4 0x14
47
48#define AUX_CMD_SEND 0x1
49#define MIN_DSI_CLK_FREQ_MHZ 40
50#define MAX_DSI_CLK_FREQ_MHZ 750
51
52enum bridge_regs {
53 SN_DPPLL_SRC_REG = 0x0A,
54 SN_PLL_ENABLE_REG = 0x0D,
55 SN_DSI_LANES_REG = 0x10,
56 SN_DSIA_CLK_FREQ_REG = 0x12,
57 SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG = 0x20,
58 SN_CHA_ACTIVE_LINE_LENGTH_HIGH_REG = 0x21,
59 SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG = 0x24,
60 SN_CHA_VERTICAL_DISPLAY_SIZE_HIGH_REG = 0x25,
61 SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG = 0x2C,
62 SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG = 0x2D,
63 SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG = 0x30,
64 SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG = 0x31,
65 SN_CHA_HORIZONTAL_BACK_PORCH_REG = 0x34,
66 SN_CHA_VERTICAL_BACK_PORCH_REG = 0x36,
67 SN_CHA_HORIZONTAL_FRONT_PORCH_REG = 0x38,
68 SN_CHA_VERTICAL_FRONT_PORCH_REG = 0x3A,
69 SN_COLOR_BAR_REG = 0x3C,
70 SN_ENH_FRAME_REG = 0x5A,
71 SN_DATA_FORMAT_REG = 0x5B,
72 SN_HPD_DISABLE_REG = 0x5C,
73 SN_I2C_CLAIM_ADDR_EN1 = 0x60,
74 SN_AUX_WDATA_REG_0 = 0x64,
75 SN_AUX_WDATA_REG_1 = 0x65,
76 SN_AUX_WDATA_REG_2 = 0x66,
77 SN_AUX_WDATA_REG_3 = 0x67,
78 SN_AUX_WDATA_REG_4 = 0x68,
79 SN_AUX_WDATA_REG_5 = 0x69,
80 SN_AUX_WDATA_REG_6 = 0x6A,
81 SN_AUX_WDATA_REG_7 = 0x6B,
82 SN_AUX_WDATA_REG_8 = 0x6C,
83 SN_AUX_WDATA_REG_9 = 0x6D,
84 SN_AUX_WDATA_REG_10 = 0x6E,
85 SN_AUX_WDATA_REG_11 = 0x6F,
86 SN_AUX_WDATA_REG_12 = 0x70,
87 SN_AUX_WDATA_REG_13 = 0x71,
88 SN_AUX_WDATA_REG_14 = 0x72,
89 SN_AUX_WDATA_REG_15 = 0x73,
90 SN_AUX_ADDR_19_16_REG = 0x74,
91 SN_AUX_ADDR_15_8_REG = 0x75,
92 SN_AUX_ADDR_7_0_REG = 0x76,
93 SN_AUX_LENGTH_REG = 0x77,
94 SN_AUX_CMD_REG = 0x78,
95 SN_AUX_RDATA_REG_0 = 0x79,
96 SN_AUX_RDATA_REG_1 = 0x7A,
97 SN_AUX_RDATA_REG_2 = 0x7B,
98 SN_AUX_RDATA_REG_3 = 0x7C,
99 SN_AUX_RDATA_REG_4 = 0x7D,
100 SN_AUX_RDATA_REG_5 = 0x7E,
101 SN_AUX_RDATA_REG_6 = 0x7F,
102 SN_AUX_RDATA_REG_7 = 0x80,
103 SN_AUX_RDATA_REG_8 = 0x81,
104 SN_AUX_RDATA_REG_9 = 0x82,
105 SN_AUX_RDATA_REG_10 = 0x83,
106 SN_AUX_RDATA_REG_11 = 0x84,
107 SN_AUX_RDATA_REG_12 = 0x85,
108 SN_AUX_RDATA_REG_13 = 0x86,
109 SN_AUX_RDATA_REG_14 = 0x87,
110 SN_AUX_RDATA_REG_15 = 0x88,
111 SN_SSC_CONFIG_REG = 0x93,
112 SN_DATARATE_CONFIG_REG = 0x94,
113 SN_ML_TX_MODE_REG = 0x96,
114 SN_AUX_CMD_STATUS_REG = 0xF4,
115};
116
117enum {
118 HPD_ENABLE = 0x0,
119 HPD_DISABLE = 0x1,
120};
121
122enum {
123 SOT_ERR_TOL_DSI = 0x0,
124 CHB_DSI_LANES = 0x1,
125 CHA_DSI_LANES = 0x2,
126 DSI_CHANNEL_MODE = 0x3,
127 LEFT_RIGHT_PIXELS = 0x4,
128};
129
130enum vstream_config {
131 VSTREAM_DISABLE = 0,
132 VSTREAM_ENABLE = 1,
133};
134
135enum i2c_over_aux {
136 I2C_OVER_AUX_WRITE_MOT_0 = 0x0,
137 I2C_OVER_AUX_READ_MOT_0 = 0x1,
Julius Wernere0dbeee2021-05-05 17:41:10 -0700138 I2C_OVER_AUX_WRITE_STATUS_UPDATE_0 = 0x2,
139 I2C_OVER_AUX_WRITE_MOT_1 = 0x4,
140 I2C_OVER_AUX_READ_MOT_1 = 0x5,
141 I2C_OVER_AUX_WRITE_STATUS_UPDATE_1 = 0x6,
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530142 NATIVE_AUX_WRITE = 0x8,
143 NATIVE_AUX_READ = 0x9,
144};
145
Julius Wernere0dbeee2021-05-05 17:41:10 -0700146enum aux_cmd_status {
147 NAT_I2C_FAIL = 1 << 6,
148 AUX_SHORT = 1 << 5,
149 AUX_DFER = 1 << 4,
150 AUX_RPLY_TOUT = 1 << 3,
151 SEND_INT = 1 << 0,
152};
153
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530154enum ml_tx_mode {
155 MAIN_LINK_OFF = 0x0,
156 NORMAL_MODE = 0x1,
157 TPS1 = 0x2,
158 TPS2 = 0x3,
159 TPS3 = 0x4,
160 PRBS7 = 0x5,
161 HBR2_COMPLIANCE_EYE_PATTERN = 0x6,
162 SYMBOL_ERR_RATE_MEASUREMENT_PATTERN = 0x7,
163 CUTSOM_PATTERN = 0x8,
164 FAST_LINK_TRAINING = 0x9,
165 SEMI_AUTO_LINK_TRAINING = 0xa,
166 REDRIVER_SEMI_AUTO_LINK_TRAINING = 0xb,
167};
168
Julius Wernere0dbeee2021-05-05 17:41:10 -0700169enum aux_request {
170 DPCD_READ,
171 DPCD_WRITE,
172 I2C_RAW_READ,
173 I2C_RAW_WRITE,
174 I2C_RAW_READ_AND_STOP,
175 I2C_RAW_WRITE_AND_STOP,
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530176};
177
178enum {
179 EDID_LENGTH = 128,
180 EDID_I2C_ADDR = 0x50,
181 EDID_EXTENSION_FLAG = 0x7e,
182};
183
184/*
185 * LUT index corresponds to register value and LUT values corresponds
186 * to dp data rate supported by the bridge in Mbps unit.
187 */
188static const unsigned int sn65dsi86_bridge_dp_rate_lut[] = {
189 0, 1620, 2160, 2430, 2700, 3240, 4320, 5400
190};
191
Julius Wernere0dbeee2021-05-05 17:41:10 -0700192static bool request_is_write(enum aux_request request)
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530193{
Julius Wernere0dbeee2021-05-05 17:41:10 -0700194 switch (request) {
195 case I2C_RAW_WRITE_AND_STOP:
196 case I2C_RAW_WRITE:
197 case DPCD_WRITE:
198 return true;
199 default:
200 return false;
201 }
202}
203
204static enum i2c_over_aux get_aux_cmd(enum aux_request request, uint32_t remaining_after_this)
205{
206 switch (request) {
207 case I2C_RAW_WRITE_AND_STOP:
208 if (!remaining_after_this)
209 return I2C_OVER_AUX_WRITE_MOT_0;
210 /* fallthrough */
211 case I2C_RAW_WRITE:
212 return I2C_OVER_AUX_WRITE_MOT_1;
213 case I2C_RAW_READ_AND_STOP:
214 if (!remaining_after_this)
215 return I2C_OVER_AUX_READ_MOT_0;
216 /* fallthrough */
217 case I2C_RAW_READ:
218 return I2C_OVER_AUX_READ_MOT_1;
219 case DPCD_WRITE:
220 return NATIVE_AUX_WRITE;
221 case DPCD_READ:
222 default:
223 return NATIVE_AUX_READ;
224 }
225}
226
227static cb_err_t sn65dsi86_bridge_aux_request(uint8_t bus,
228 uint8_t chip,
229 unsigned int target_reg,
230 unsigned int total_size,
231 enum aux_request request,
232 uint8_t *data)
233{
234 int i;
235 uint32_t length;
236 uint8_t buf;
237 uint8_t reg;
238
239 /* Clear old status flags just in case they're left over from a previous transfer. */
240 i2c_writeb(bus, chip, SN_AUX_CMD_STATUS_REG,
241 NAT_I2C_FAIL | AUX_SHORT | AUX_DFER | AUX_RPLY_TOUT | SEND_INT);
242
243 while (total_size) {
244 length = MIN(total_size, 16);
245 total_size -= length;
246
247 enum i2c_over_aux cmd = get_aux_cmd(request, total_size);
248 if (i2c_writeb(bus, chip, SN_AUX_CMD_REG, (cmd << 4)) ||
249 i2c_writeb(bus, chip, SN_AUX_ADDR_19_16_REG, (target_reg >> 16) & 0xF) ||
250 i2c_writeb(bus, chip, SN_AUX_ADDR_15_8_REG, (target_reg >> 8) & 0xFF) ||
251 i2c_writeb(bus, chip, SN_AUX_ADDR_7_0_REG, (target_reg) & 0xFF) ||
252 i2c_writeb(bus, chip, SN_AUX_LENGTH_REG, length))
253 return CB_ERR;
254
255 if (request_is_write(request)) {
256 reg = SN_AUX_WDATA_REG_0;
257 for (i = 0; i < length; i++)
258 if (i2c_writeb(bus, chip, reg++, *data++))
259 return CB_ERR;
260 }
261
262 if (i2c_writeb(bus, chip, SN_AUX_CMD_REG, AUX_CMD_SEND | (cmd << 4)))
263 return CB_ERR;
264 if (!wait_ms(100, !i2c_readb(bus, chip, SN_AUX_CMD_REG, &buf) &&
265 !(buf & AUX_CMD_SEND))) {
266 printk(BIOS_ERR, "ERROR: AUX_CMD_SEND not acknowledged\n");
267 return CB_ERR;
268 }
269 if (i2c_readb(bus, chip, SN_AUX_CMD_STATUS_REG, &buf))
270 return CB_ERR;
271 if (buf & (NAT_I2C_FAIL | AUX_SHORT | AUX_DFER | AUX_RPLY_TOUT)) {
272 printk(BIOS_ERR, "ERROR: AUX command failed, status = %#x\n", buf);
273 return CB_ERR;
274 }
275
276 if (!request_is_write(request)) {
277 reg = SN_AUX_RDATA_REG_0;
278 for (i = 0; i < length; i++) {
279 if (i2c_readb(bus, chip, reg++, &buf))
280 return CB_ERR;
281 *data++ = buf;
282 }
283 }
284 }
285
286 return CB_SUCCESS;
287}
288
289cb_err_t sn65dsi86_bridge_read_edid(uint8_t bus, uint8_t chip, struct edid *out)
290{
291 cb_err_t err;
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530292 u8 edid[EDID_LENGTH * 2];
293 int edid_size = EDID_LENGTH;
294
Julius Wernere0dbeee2021-05-05 17:41:10 -0700295 uint8_t reg_addr = 0;
296 err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, 1,
297 I2C_RAW_WRITE, &reg_addr);
298 if (!err)
299 err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, EDID_LENGTH,
300 I2C_RAW_READ_AND_STOP, edid);
301 if (err) {
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530302 printk(BIOS_ERR, "ERROR: Failed to read EDID.\n");
Julius Wernere0dbeee2021-05-05 17:41:10 -0700303 return err;
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530304 }
305
306 if (edid[EDID_EXTENSION_FLAG]) {
307 edid_size += EDID_LENGTH;
Julius Wernere0dbeee2021-05-05 17:41:10 -0700308 reg_addr = EDID_LENGTH;
309 err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR, 1,
310 I2C_RAW_WRITE, &reg_addr);
311 if (!err)
312 err = sn65dsi86_bridge_aux_request(bus, chip, EDID_I2C_ADDR,
313 EDID_LENGTH, I2C_RAW_READ_AND_STOP, &edid[EDID_LENGTH]);
314 if (err) {
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530315 printk(BIOS_ERR, "Failed to read EDID ext block.\n");
Julius Wernere0dbeee2021-05-05 17:41:10 -0700316 return err;
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530317 }
318 }
319
320 if (decode_edid(edid, edid_size, out) != EDID_CONFORMANT) {
321 printk(BIOS_ERR, "ERROR: Failed to decode EDID.\n");
322 return CB_ERR;
323 }
324
325 return CB_SUCCESS;
326}
327
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530328static void sn65dsi86_bridge_valid_dp_rates(uint8_t bus, uint8_t chip, bool rate_valid[])
329{
330 unsigned int rate_per_200khz;
331 uint8_t dpcd_val;
332 int i, j;
333
Julius Wernere0dbeee2021-05-05 17:41:10 -0700334 sn65dsi86_bridge_aux_request(bus, chip,
335 DP_BRIDGE_DPCD_REV, 1, DPCD_READ, &dpcd_val);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530336 if (dpcd_val >= DP_BRIDGE_14) {
337 /* eDP 1.4 devices must provide a custom table */
Julius Werner6b8305d2020-10-12 15:32:52 -0700338 uint16_t sink_rates[DP_MAX_SUPPORTED_RATES] = {0};
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530339
Julius Wernere0dbeee2021-05-05 17:41:10 -0700340 sn65dsi86_bridge_aux_request(bus, chip, DP_SUPPORTED_LINK_RATES,
341 sizeof(sink_rates),
342 DPCD_READ, (void *)sink_rates);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530343 for (i = 0; i < ARRAY_SIZE(sink_rates); i++) {
344 rate_per_200khz = le16_to_cpu(sink_rates[i]);
345
346 if (!rate_per_200khz)
347 break;
348
349 for (j = 0;
350 j < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut);
351 j++) {
352 if (sn65dsi86_bridge_dp_rate_lut[j] * (MHz / KHz) ==
353 rate_per_200khz * 200)
354 rate_valid[j] = true;
355 }
356 }
357
358 for (i = 0; i < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut); i++) {
359 if (rate_valid[i])
360 return;
361 }
362
363 printk(BIOS_ERR, "No matching eDP rates in table; falling back\n");
364 }
365
366 /* On older versions best we can do is use DP_MAX_LINK_RATE */
Julius Wernere0dbeee2021-05-05 17:41:10 -0700367 sn65dsi86_bridge_aux_request(bus, chip, DP_MAX_LINK_RATE, 1, DPCD_READ, &dpcd_val);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530368
369 switch (dpcd_val) {
370 default:
Julius Werner6b8305d2020-10-12 15:32:52 -0700371 printk(BIOS_ERR, "Unexpected max rate (%#x); assuming 5.4 GHz\n",
372 (int)dpcd_val);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530373 /* fall through */
374 case DP_LINK_BW_5_4:
375 rate_valid[7] = 1;
376 /* fall through */
377 case DP_LINK_BW_2_7:
378 rate_valid[4] = 1;
379 /* fall through */
380 case DP_LINK_BW_1_62:
381 rate_valid[1] = 1;
382 break;
383 }
384}
385
386static void sn65dsi86_bridge_set_dsi_clock_range(uint8_t bus, uint8_t chip,
387 struct edid *edid,
388 uint32_t num_of_lanes, uint32_t bpp)
389{
390 uint64_t pixel_clk_hz;
391 uint64_t stream_bit_rate_mhz;
392 uint64_t min_req_dsi_clk;
393
394 pixel_clk_hz = edid->mode.pixel_clock * KHz;
395 stream_bit_rate_mhz = (pixel_clk_hz * bpp) / MHz;
396
397 /* For TI the clock frequencies are half the bit rates */
398 min_req_dsi_clk = stream_bit_rate_mhz / (num_of_lanes * 2);
399
400 /* for each increment in val, frequency increases by 5MHz */
401 min_req_dsi_clk = MAX(MIN_DSI_CLK_FREQ_MHZ,
402 MIN(MAX_DSI_CLK_FREQ_MHZ, min_req_dsi_clk)) / 5;
403 i2c_writeb(bus, chip, SN_DSIA_CLK_FREQ_REG, min_req_dsi_clk);
404}
405
406static void sn65dsi86_bridge_set_dp_clock_range(uint8_t bus, uint8_t chip,
407 struct edid *edid, uint32_t num_of_lanes)
408{
409 uint64_t stream_bit_rate_khz;
410 bool rate_valid[ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut)] = { };
411 uint64_t dp_rate_mhz;
412 int dp_rate_idx, i;
413
414 stream_bit_rate_khz = edid->mode.pixel_clock * 18;
415
416 /* Calculate minimum DP data rate, taking 80% as per DP spec */
417 dp_rate_mhz = DIV_ROUND_UP(stream_bit_rate_khz * DP_CLK_FUDGE_NUM,
418 KHz * num_of_lanes * DP_CLK_FUDGE_DEN);
419
420 for (i = 0; i < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut) - 1; i++)
421 if (sn65dsi86_bridge_dp_rate_lut[i] > dp_rate_mhz)
422 break;
423
424 sn65dsi86_bridge_valid_dp_rates(bus, chip, rate_valid);
425
426 /* Train until we run out of rates */
427 for (dp_rate_idx = i;
428 dp_rate_idx < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut);
429 dp_rate_idx++)
430 if (rate_valid[dp_rate_idx])
431 break;
432
433 if (dp_rate_idx < ARRAY_SIZE(sn65dsi86_bridge_dp_rate_lut))
434 i2c_write_field(bus, chip, SN_DATARATE_CONFIG_REG, dp_rate_idx, 8, 5);
435 else
436 printk(BIOS_ERR, "ERROR: valid dp rate not found");
437}
438
439static void sn65dsi86_bridge_set_bridge_active_timing(uint8_t bus,
440 uint8_t chip,
441 struct edid *edid)
442{
443 i2c_writeb(bus, chip, SN_CHA_ACTIVE_LINE_LENGTH_LOW_REG,
444 BRIDGE_GETLOWERBYTE(edid->mode.ha));
445 i2c_writeb(bus, chip, SN_CHA_ACTIVE_LINE_LENGTH_HIGH_REG,
446 BRIDGE_GETHIGHERBYTE(edid->mode.ha));
447 i2c_writeb(bus, chip, SN_CHA_VERTICAL_DISPLAY_SIZE_LOW_REG,
448 BRIDGE_GETLOWERBYTE(edid->mode.va));
449 i2c_writeb(bus, chip, SN_CHA_VERTICAL_DISPLAY_SIZE_HIGH_REG,
450 BRIDGE_GETHIGHERBYTE(edid->mode.va));
451 i2c_writeb(bus, chip, SN_CHA_HSYNC_PULSE_WIDTH_LOW_REG,
452 BRIDGE_GETLOWERBYTE(edid->mode.hspw));
453 i2c_writeb(bus, chip, SN_CHA_HSYNC_PULSE_WIDTH_HIGH_REG,
454 BRIDGE_GETHIGHERBYTE(edid->mode.hspw));
455 i2c_writeb(bus, chip, SN_CHA_VSYNC_PULSE_WIDTH_LOW_REG,
456 BRIDGE_GETLOWERBYTE(edid->mode.vspw));
457 i2c_writeb(bus, chip, SN_CHA_VSYNC_PULSE_WIDTH_HIGH_REG,
458 BRIDGE_GETHIGHERBYTE(edid->mode.vspw));
459 i2c_writeb(bus, chip, SN_CHA_HORIZONTAL_BACK_PORCH_REG,
460 edid->mode.hbl - edid->mode.hso - edid->mode.hspw);
461 i2c_writeb(bus, chip, SN_CHA_VERTICAL_BACK_PORCH_REG,
462 edid->mode.vbl - edid->mode.vso - edid->mode.vspw);
463 i2c_writeb(bus, chip, SN_CHA_HORIZONTAL_FRONT_PORCH_REG,
464 edid->mode.hso);
465 i2c_writeb(bus, chip, SN_CHA_VERTICAL_FRONT_PORCH_REG,
466 edid->mode.vso);
467}
468
469static void sn65dsi86_bridge_link_training(uint8_t bus, uint8_t chip)
470{
471 uint8_t buf;
472
473 /* enable pll lock */
474 i2c_writeb(bus, chip, SN_PLL_ENABLE_REG, 0x1);
475
476 if (!wait_ms(500,
477 !(i2c_readb(bus, chip, SN_DPPLL_SRC_REG, &buf)) &&
478 (buf & BIT(7)))) {
479 printk(BIOS_ERR, "ERROR: PLL lock failure\n");
480 }
481
482 /*
483 * The SN65DSI86 only supports ASSR Display Authentication method and
484 * this method is enabled by default. An eDP panel must support this
485 * authentication method. We need to enable this method in the eDP panel
486 * at DisplayPort address 0x0010A prior to link training.
487 */
488 buf = 0x1;
Julius Wernere0dbeee2021-05-05 17:41:10 -0700489 sn65dsi86_bridge_aux_request(bus, chip,
490 DP_BRIDGE_CONFIGURATION_SET, 1, DPCD_WRITE, &buf);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530491
Julius Werner411e7602020-09-30 15:23:46 -0700492 int i; /* Kernel driver suggests to retry this up to 10 times if it fails. */
493 for (i = 0; i < 10; i++) {
494 i2c_writeb(bus, chip, SN_ML_TX_MODE_REG, SEMI_AUTO_LINK_TRAINING);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530495
Julius Werner411e7602020-09-30 15:23:46 -0700496 if (!wait_ms(500, !(i2c_readb(bus, chip, SN_ML_TX_MODE_REG, &buf)) &&
497 (buf == NORMAL_MODE || buf == MAIN_LINK_OFF))) {
498 printk(BIOS_ERR, "ERROR: unexpected link training state: %#x\n", buf);
499 return;
500 }
501 if (buf == NORMAL_MODE)
502 return;
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530503 }
504
Julius Werner411e7602020-09-30 15:23:46 -0700505 printk(BIOS_ERR, "ERROR: Link training failed 10 times\n");
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530506}
507
Vinod Polimera6ea80332021-03-17 16:11:12 +0530508void sn65dsi86_backlight_enable(uint8_t bus, uint8_t chip)
509{
510 uint8_t val = DP_BACKLIGHT_CONTROL_MODE_DPCD;
511 sn65dsi86_bridge_aux_request(bus, chip, DP_BACKLIGHT_MODE_SET, 1, DPCD_WRITE, &val);
512
513 val = 0xff;
514 sn65dsi86_bridge_aux_request(bus, chip, DP_BACKLIGHT_BRIGHTNESS_MSB, 1,
515 DPCD_WRITE, &val);
516
517 val = DP_BACKLIGHT_ENABLE;
518 sn65dsi86_bridge_aux_request(bus, chip, DP_DISPLAY_CONTROL_REGISTER, 1,
519 DPCD_WRITE, &val);
520}
521
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530522static void sn65dsi86_bridge_assr_config(uint8_t bus, uint8_t chip, int enable)
523{
524 if (enable)
525 i2c_write_field(bus, chip, SN_ENH_FRAME_REG, VSTREAM_ENABLE, 1, 3);
526 else
527 i2c_write_field(bus, chip, SN_ENH_FRAME_REG, VSTREAM_DISABLE, 1, 3);
528}
529
530static int sn65dsi86_bridge_dp_lane_config(uint8_t bus, uint8_t chip)
531{
532 uint8_t lane_count;
533
Julius Wernere0dbeee2021-05-05 17:41:10 -0700534 sn65dsi86_bridge_aux_request(bus, chip, DP_MAX_LANE_COUNT, 1, DPCD_READ, &lane_count);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530535 lane_count &= DP_LANE_COUNT_MASK;
536 i2c_write_field(bus, chip, SN_SSC_CONFIG_REG, MIN(lane_count, 3), 3, 4);
537
538 return lane_count;
539}
540
541void sn65dsi86_bridge_init(uint8_t bus, uint8_t chip, enum dp_pll_clk_src ref_clk)
542{
Vinod Polimerab9a7d772020-09-21 08:55:16 +0530543 /* disable HPD */
544 i2c_write_field(bus, chip, SN_HPD_DISABLE_REG, HPD_DISABLE, 1, 0);
Vinod Polimerac4e0b0a2020-06-23 16:23:06 +0530545
546 /* set refclk to 19.2 MHZ */
547 i2c_write_field(bus, chip, SN_DPPLL_SRC_REG, ref_clk, 7, 1);
548}
549
550void sn65dsi86_bridge_configure(uint8_t bus, uint8_t chip,
551 struct edid *edid, uint32_t num_of_lanes,
552 uint32_t dsi_bpp)
553{
554 int dp_lanes;
555
556 /* DSI Lanes config */
557 i2c_write_field(bus, chip, SN_DSI_LANES_REG, (4 - num_of_lanes), 3, 3);
558
559 /* DP Lane config */
560 dp_lanes = sn65dsi86_bridge_dp_lane_config(bus, chip);
561
562 sn65dsi86_bridge_set_dsi_clock_range(bus, chip, edid, num_of_lanes, dsi_bpp);
563
564 sn65dsi86_bridge_set_dp_clock_range(bus, chip, edid, dp_lanes);
565
566 /* Disable vstream */
567 sn65dsi86_bridge_assr_config(bus, chip, 0);
568 sn65dsi86_bridge_link_training(bus, chip);
569 sn65dsi86_bridge_set_bridge_active_timing(bus, chip, edid);
570
571 /* DP BPP config */
572 i2c_writeb(bus, chip, SN_DATA_FORMAT_REG, 0x1);
573
574 /* color bar disabled */
575 i2c_writeb(bus, chip, SN_COLOR_BAR_REG, 0x5);
576
577 /* Enable vstream */
578 sn65dsi86_bridge_assr_config(bus, chip, 1);
579}