xref: /openbsd-src/sys/dev/pci/drm/i915/display/intel_lspcon.c (revision f005ef32267c16bdb134f0e9fa4477dbe07c263a)
1c349dbc7Sjsg /*
2c349dbc7Sjsg  * Copyright © 2016 Intel Corporation
3c349dbc7Sjsg  *
4c349dbc7Sjsg  * Permission is hereby granted, free of charge, to any person obtaining a
5c349dbc7Sjsg  * copy of this software and associated documentation files (the "Software"),
6c349dbc7Sjsg  * to deal in the Software without restriction, including without limitation
7c349dbc7Sjsg  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8c349dbc7Sjsg  * and/or sell copies of the Software, and to permit persons to whom the
9c349dbc7Sjsg  * Software is furnished to do so, subject to the following conditions:
10c349dbc7Sjsg  *
11c349dbc7Sjsg  * The above copyright notice and this permission notice (including the next
12c349dbc7Sjsg  * paragraph) shall be included in all copies or substantial portions of the
13c349dbc7Sjsg  * Software.
14c349dbc7Sjsg  *
15c349dbc7Sjsg  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16c349dbc7Sjsg  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17c349dbc7Sjsg  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18c349dbc7Sjsg  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19c349dbc7Sjsg  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20c349dbc7Sjsg  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21c349dbc7Sjsg  * DEALINGS IN THE SOFTWARE.
22c349dbc7Sjsg  *
23c349dbc7Sjsg  *
24c349dbc7Sjsg  */
25c349dbc7Sjsg 
261bb76ff1Sjsg #include <drm/display/drm_dp_dual_mode_helper.h>
271bb76ff1Sjsg #include <drm/display/drm_hdmi_helper.h>
28c349dbc7Sjsg #include <drm/drm_atomic_helper.h>
29c349dbc7Sjsg #include <drm/drm_edid.h>
30c349dbc7Sjsg 
31*f005ef32Sjsg #include "i915_reg.h"
325ca02815Sjsg #include "intel_de.h"
33c349dbc7Sjsg #include "intel_display_types.h"
34c349dbc7Sjsg #include "intel_dp.h"
35c349dbc7Sjsg #include "intel_lspcon.h"
365ca02815Sjsg #include "intel_hdmi.h"
37c349dbc7Sjsg 
38c349dbc7Sjsg /* LSPCON OUI Vendor ID(signatures) */
39c349dbc7Sjsg #define LSPCON_VENDOR_PARADE_OUI 0x001CF8
40c349dbc7Sjsg #define LSPCON_VENDOR_MCA_OUI 0x0060AD
41c349dbc7Sjsg 
425ca02815Sjsg #define DPCD_MCA_LSPCON_HDR_STATUS	0x70003
435ca02815Sjsg #define DPCD_PARADE_LSPCON_HDR_STATUS	0x00511
445ca02815Sjsg 
45c349dbc7Sjsg /* AUX addresses to write MCA AVI IF */
46c349dbc7Sjsg #define LSPCON_MCA_AVI_IF_WRITE_OFFSET 0x5C0
47c349dbc7Sjsg #define LSPCON_MCA_AVI_IF_CTRL 0x5DF
48c349dbc7Sjsg #define  LSPCON_MCA_AVI_IF_KICKOFF (1 << 0)
49c349dbc7Sjsg #define  LSPCON_MCA_AVI_IF_HANDLED (1 << 1)
50c349dbc7Sjsg 
51c349dbc7Sjsg /* AUX addresses to write Parade AVI IF */
52c349dbc7Sjsg #define LSPCON_PARADE_AVI_IF_WRITE_OFFSET 0x516
53c349dbc7Sjsg #define LSPCON_PARADE_AVI_IF_CTRL 0x51E
54c349dbc7Sjsg #define  LSPCON_PARADE_AVI_IF_KICKOFF (1 << 7)
55c349dbc7Sjsg #define LSPCON_PARADE_AVI_IF_DATA_SIZE 32
56c349dbc7Sjsg 
lspcon_to_intel_dp(struct intel_lspcon * lspcon)57c349dbc7Sjsg static struct intel_dp *lspcon_to_intel_dp(struct intel_lspcon *lspcon)
58c349dbc7Sjsg {
59c349dbc7Sjsg 	struct intel_digital_port *dig_port =
60c349dbc7Sjsg 		container_of(lspcon, struct intel_digital_port, lspcon);
61c349dbc7Sjsg 
62c349dbc7Sjsg 	return &dig_port->dp;
63c349dbc7Sjsg }
64c349dbc7Sjsg 
lspcon_mode_name(enum drm_lspcon_mode mode)65c349dbc7Sjsg static const char *lspcon_mode_name(enum drm_lspcon_mode mode)
66c349dbc7Sjsg {
67c349dbc7Sjsg 	switch (mode) {
68c349dbc7Sjsg 	case DRM_LSPCON_MODE_PCON:
69c349dbc7Sjsg 		return "PCON";
70c349dbc7Sjsg 	case DRM_LSPCON_MODE_LS:
71c349dbc7Sjsg 		return "LS";
72c349dbc7Sjsg 	case DRM_LSPCON_MODE_INVALID:
73c349dbc7Sjsg 		return "INVALID";
74c349dbc7Sjsg 	default:
75c349dbc7Sjsg 		MISSING_CASE(mode);
76c349dbc7Sjsg 		return "INVALID";
77c349dbc7Sjsg 	}
78c349dbc7Sjsg }
79c349dbc7Sjsg 
lspcon_detect_vendor(struct intel_lspcon * lspcon)80c349dbc7Sjsg static bool lspcon_detect_vendor(struct intel_lspcon *lspcon)
81c349dbc7Sjsg {
82c349dbc7Sjsg 	struct intel_dp *dp = lspcon_to_intel_dp(lspcon);
831bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(dp);
84c349dbc7Sjsg 	struct drm_dp_dpcd_ident *ident;
85c349dbc7Sjsg 	u32 vendor_oui;
86c349dbc7Sjsg 
87c349dbc7Sjsg 	if (drm_dp_read_desc(&dp->aux, &dp->desc, drm_dp_is_branch(dp->dpcd))) {
881bb76ff1Sjsg 		drm_err(&i915->drm, "Can't read description\n");
89c349dbc7Sjsg 		return false;
90c349dbc7Sjsg 	}
91c349dbc7Sjsg 
92c349dbc7Sjsg 	ident = &dp->desc.ident;
93c349dbc7Sjsg 	vendor_oui = (ident->oui[0] << 16) | (ident->oui[1] << 8) |
94c349dbc7Sjsg 		      ident->oui[2];
95c349dbc7Sjsg 
96c349dbc7Sjsg 	switch (vendor_oui) {
97c349dbc7Sjsg 	case LSPCON_VENDOR_MCA_OUI:
98c349dbc7Sjsg 		lspcon->vendor = LSPCON_VENDOR_MCA;
991bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "Vendor: Mega Chips\n");
100c349dbc7Sjsg 		break;
101c349dbc7Sjsg 
102c349dbc7Sjsg 	case LSPCON_VENDOR_PARADE_OUI:
103c349dbc7Sjsg 		lspcon->vendor = LSPCON_VENDOR_PARADE;
1041bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "Vendor: Parade Tech\n");
105c349dbc7Sjsg 		break;
106c349dbc7Sjsg 
107c349dbc7Sjsg 	default:
1081bb76ff1Sjsg 		drm_err(&i915->drm, "Invalid/Unknown vendor OUI\n");
109c349dbc7Sjsg 		return false;
110c349dbc7Sjsg 	}
111c349dbc7Sjsg 
112c349dbc7Sjsg 	return true;
113c349dbc7Sjsg }
114c349dbc7Sjsg 
get_hdr_status_reg(struct intel_lspcon * lspcon)1155ca02815Sjsg static u32 get_hdr_status_reg(struct intel_lspcon *lspcon)
1165ca02815Sjsg {
1175ca02815Sjsg 	if (lspcon->vendor == LSPCON_VENDOR_MCA)
1185ca02815Sjsg 		return DPCD_MCA_LSPCON_HDR_STATUS;
1195ca02815Sjsg 	else
1205ca02815Sjsg 		return DPCD_PARADE_LSPCON_HDR_STATUS;
1215ca02815Sjsg }
1225ca02815Sjsg 
lspcon_detect_hdr_capability(struct intel_lspcon * lspcon)1235ca02815Sjsg void lspcon_detect_hdr_capability(struct intel_lspcon *lspcon)
1245ca02815Sjsg {
1251bb76ff1Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
1261bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
1275ca02815Sjsg 	u8 hdr_caps;
1285ca02815Sjsg 	int ret;
1295ca02815Sjsg 
1301bb76ff1Sjsg 	ret = drm_dp_dpcd_read(&intel_dp->aux, get_hdr_status_reg(lspcon),
1315ca02815Sjsg 			       &hdr_caps, 1);
1325ca02815Sjsg 
1335ca02815Sjsg 	if (ret < 0) {
1341bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "HDR capability detection failed\n");
1355ca02815Sjsg 		lspcon->hdr_supported = false;
1365ca02815Sjsg 	} else if (hdr_caps & 0x1) {
1371bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "LSPCON capable of HDR\n");
1385ca02815Sjsg 		lspcon->hdr_supported = true;
1395ca02815Sjsg 	}
1405ca02815Sjsg }
1415ca02815Sjsg 
lspcon_get_current_mode(struct intel_lspcon * lspcon)142c349dbc7Sjsg static enum drm_lspcon_mode lspcon_get_current_mode(struct intel_lspcon *lspcon)
143c349dbc7Sjsg {
1445ca02815Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
1451bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
146c349dbc7Sjsg 	enum drm_lspcon_mode current_mode;
1475ca02815Sjsg 	struct i2c_adapter *adapter = &intel_dp->aux.ddc;
148c349dbc7Sjsg 
1495ca02815Sjsg 	if (drm_lspcon_get_mode(intel_dp->aux.drm_dev, adapter, &current_mode)) {
1501bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "Error reading LSPCON mode\n");
151c349dbc7Sjsg 		return DRM_LSPCON_MODE_INVALID;
152c349dbc7Sjsg 	}
153c349dbc7Sjsg 	return current_mode;
154c349dbc7Sjsg }
155c349dbc7Sjsg 
lspcon_wait_mode(struct intel_lspcon * lspcon,enum drm_lspcon_mode mode)156c349dbc7Sjsg static enum drm_lspcon_mode lspcon_wait_mode(struct intel_lspcon *lspcon,
157c349dbc7Sjsg 					     enum drm_lspcon_mode mode)
158c349dbc7Sjsg {
1591bb76ff1Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
1601bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
161c349dbc7Sjsg 	enum drm_lspcon_mode current_mode;
162c349dbc7Sjsg 
163c349dbc7Sjsg 	current_mode = lspcon_get_current_mode(lspcon);
164c349dbc7Sjsg 	if (current_mode == mode)
165c349dbc7Sjsg 		goto out;
166c349dbc7Sjsg 
1671bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "Waiting for LSPCON mode %s to settle\n",
168c349dbc7Sjsg 		    lspcon_mode_name(mode));
169c349dbc7Sjsg 
170c349dbc7Sjsg 	wait_for((current_mode = lspcon_get_current_mode(lspcon)) == mode, 400);
171c349dbc7Sjsg 	if (current_mode != mode)
1721bb76ff1Sjsg 		drm_err(&i915->drm, "LSPCON mode hasn't settled\n");
173c349dbc7Sjsg 
174c349dbc7Sjsg out:
1751bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "Current LSPCON mode %s\n",
176c349dbc7Sjsg 		    lspcon_mode_name(current_mode));
177c349dbc7Sjsg 
178c349dbc7Sjsg 	return current_mode;
179c349dbc7Sjsg }
180c349dbc7Sjsg 
lspcon_change_mode(struct intel_lspcon * lspcon,enum drm_lspcon_mode mode)181c349dbc7Sjsg static int lspcon_change_mode(struct intel_lspcon *lspcon,
182c349dbc7Sjsg 			      enum drm_lspcon_mode mode)
183c349dbc7Sjsg {
1845ca02815Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
1851bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
186c349dbc7Sjsg 	int err;
187c349dbc7Sjsg 	enum drm_lspcon_mode current_mode;
1885ca02815Sjsg 	struct i2c_adapter *adapter = &intel_dp->aux.ddc;
189c349dbc7Sjsg 
1905ca02815Sjsg 	err = drm_lspcon_get_mode(intel_dp->aux.drm_dev, adapter, &current_mode);
191c349dbc7Sjsg 	if (err) {
1921bb76ff1Sjsg 		drm_err(&i915->drm, "Error reading LSPCON mode\n");
193c349dbc7Sjsg 		return err;
194c349dbc7Sjsg 	}
195c349dbc7Sjsg 
196c349dbc7Sjsg 	if (current_mode == mode) {
1971bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "Current mode = desired LSPCON mode\n");
198c349dbc7Sjsg 		return 0;
199c349dbc7Sjsg 	}
200c349dbc7Sjsg 
2015ca02815Sjsg 	err = drm_lspcon_set_mode(intel_dp->aux.drm_dev, adapter, mode);
202c349dbc7Sjsg 	if (err < 0) {
2031bb76ff1Sjsg 		drm_err(&i915->drm, "LSPCON mode change failed\n");
204c349dbc7Sjsg 		return err;
205c349dbc7Sjsg 	}
206c349dbc7Sjsg 
207c349dbc7Sjsg 	lspcon->mode = mode;
2081bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "LSPCON mode changed done\n");
209c349dbc7Sjsg 	return 0;
210c349dbc7Sjsg }
211c349dbc7Sjsg 
lspcon_wake_native_aux_ch(struct intel_lspcon * lspcon)212c349dbc7Sjsg static bool lspcon_wake_native_aux_ch(struct intel_lspcon *lspcon)
213c349dbc7Sjsg {
2141bb76ff1Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
2151bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
216c349dbc7Sjsg 	u8 rev;
217c349dbc7Sjsg 
218c349dbc7Sjsg 	if (drm_dp_dpcd_readb(&lspcon_to_intel_dp(lspcon)->aux, DP_DPCD_REV,
219c349dbc7Sjsg 			      &rev) != 1) {
2201bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "Native AUX CH down\n");
221c349dbc7Sjsg 		return false;
222c349dbc7Sjsg 	}
223c349dbc7Sjsg 
2241bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "Native AUX CH up, DPCD version: %d.%d\n",
225c349dbc7Sjsg 		    rev >> 4, rev & 0xf);
226c349dbc7Sjsg 
227c349dbc7Sjsg 	return true;
228c349dbc7Sjsg }
229c349dbc7Sjsg 
lspcon_probe(struct intel_lspcon * lspcon)230c349dbc7Sjsg static bool lspcon_probe(struct intel_lspcon *lspcon)
231c349dbc7Sjsg {
232c349dbc7Sjsg 	int retry;
233c349dbc7Sjsg 	enum drm_dp_dual_mode_type adaptor_type;
2345ca02815Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
2351bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
2365ca02815Sjsg 	struct i2c_adapter *adapter = &intel_dp->aux.ddc;
237c349dbc7Sjsg 	enum drm_lspcon_mode expected_mode;
238c349dbc7Sjsg 
239c349dbc7Sjsg 	expected_mode = lspcon_wake_native_aux_ch(lspcon) ?
240c349dbc7Sjsg 			DRM_LSPCON_MODE_PCON : DRM_LSPCON_MODE_LS;
241c349dbc7Sjsg 
242c349dbc7Sjsg 	/* Lets probe the adaptor and check its type */
243c349dbc7Sjsg 	for (retry = 0; retry < 6; retry++) {
244c349dbc7Sjsg 		if (retry)
245c349dbc7Sjsg 			usleep_range(500, 1000);
246c349dbc7Sjsg 
2475ca02815Sjsg 		adaptor_type = drm_dp_dual_mode_detect(intel_dp->aux.drm_dev, adapter);
248c349dbc7Sjsg 		if (adaptor_type == DRM_DP_DUAL_MODE_LSPCON)
249c349dbc7Sjsg 			break;
250c349dbc7Sjsg 	}
251c349dbc7Sjsg 
252c349dbc7Sjsg 	if (adaptor_type != DRM_DP_DUAL_MODE_LSPCON) {
2531bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "No LSPCON detected, found %s\n",
254c349dbc7Sjsg 			    drm_dp_get_dual_mode_type_name(adaptor_type));
255c349dbc7Sjsg 		return false;
256c349dbc7Sjsg 	}
257c349dbc7Sjsg 
258c349dbc7Sjsg 	/* Yay ... got a LSPCON device */
2591bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "LSPCON detected\n");
260c349dbc7Sjsg 	lspcon->mode = lspcon_wait_mode(lspcon, expected_mode);
261c349dbc7Sjsg 
262c349dbc7Sjsg 	/*
263c349dbc7Sjsg 	 * In the SW state machine, lets Put LSPCON in PCON mode only.
264c349dbc7Sjsg 	 * In this way, it will work with both HDMI 1.4 sinks as well as HDMI
265c349dbc7Sjsg 	 * 2.0 sinks.
266c349dbc7Sjsg 	 */
267c349dbc7Sjsg 	if (lspcon->mode != DRM_LSPCON_MODE_PCON) {
268c349dbc7Sjsg 		if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON) < 0) {
2691bb76ff1Sjsg 			drm_err(&i915->drm, "LSPCON mode change to PCON failed\n");
270c349dbc7Sjsg 			return false;
271c349dbc7Sjsg 		}
272c349dbc7Sjsg 	}
273c349dbc7Sjsg 	return true;
274c349dbc7Sjsg }
275c349dbc7Sjsg 
lspcon_resume_in_pcon_wa(struct intel_lspcon * lspcon)276c349dbc7Sjsg static void lspcon_resume_in_pcon_wa(struct intel_lspcon *lspcon)
277c349dbc7Sjsg {
278c349dbc7Sjsg 	struct intel_dp *intel_dp = lspcon_to_intel_dp(lspcon);
2791bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
280c349dbc7Sjsg 	struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp);
281c349dbc7Sjsg 	unsigned long start = jiffies;
282c349dbc7Sjsg 
283c349dbc7Sjsg 	while (1) {
284c349dbc7Sjsg 		if (intel_digital_port_connected(&dig_port->base)) {
2851bb76ff1Sjsg 			drm_dbg_kms(&i915->drm, "LSPCON recovering in PCON mode after %u ms\n",
286c349dbc7Sjsg 				    jiffies_to_msecs(jiffies - start));
287c349dbc7Sjsg 			return;
288c349dbc7Sjsg 		}
289c349dbc7Sjsg 
290c349dbc7Sjsg 		if (time_after(jiffies, start + msecs_to_jiffies(1000)))
291c349dbc7Sjsg 			break;
292c349dbc7Sjsg 
293c349dbc7Sjsg 		usleep_range(10000, 15000);
294c349dbc7Sjsg 	}
295c349dbc7Sjsg 
2961bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "LSPCON DP descriptor mismatch after resume\n");
297c349dbc7Sjsg }
298c349dbc7Sjsg 
lspcon_parade_fw_ready(struct drm_dp_aux * aux)299c349dbc7Sjsg static bool lspcon_parade_fw_ready(struct drm_dp_aux *aux)
300c349dbc7Sjsg {
301c349dbc7Sjsg 	u8 avi_if_ctrl;
302c349dbc7Sjsg 	u8 retry;
303c349dbc7Sjsg 	ssize_t ret;
304c349dbc7Sjsg 
305c349dbc7Sjsg 	/* Check if LSPCON FW is ready for data */
306c349dbc7Sjsg 	for (retry = 0; retry < 5; retry++) {
307c349dbc7Sjsg 		if (retry)
308c349dbc7Sjsg 			usleep_range(200, 300);
309c349dbc7Sjsg 
310c349dbc7Sjsg 		ret = drm_dp_dpcd_read(aux, LSPCON_PARADE_AVI_IF_CTRL,
311c349dbc7Sjsg 				       &avi_if_ctrl, 1);
312c349dbc7Sjsg 		if (ret < 0) {
3131bb76ff1Sjsg 			drm_err(aux->drm_dev, "Failed to read AVI IF control\n");
314c349dbc7Sjsg 			return false;
315c349dbc7Sjsg 		}
316c349dbc7Sjsg 
317c349dbc7Sjsg 		if ((avi_if_ctrl & LSPCON_PARADE_AVI_IF_KICKOFF) == 0)
318c349dbc7Sjsg 			return true;
319c349dbc7Sjsg 	}
320c349dbc7Sjsg 
3211bb76ff1Sjsg 	drm_err(aux->drm_dev, "Parade FW not ready to accept AVI IF\n");
322c349dbc7Sjsg 	return false;
323c349dbc7Sjsg }
324c349dbc7Sjsg 
_lspcon_parade_write_infoframe_blocks(struct drm_dp_aux * aux,u8 * avi_buf)325c349dbc7Sjsg static bool _lspcon_parade_write_infoframe_blocks(struct drm_dp_aux *aux,
326c349dbc7Sjsg 						  u8 *avi_buf)
327c349dbc7Sjsg {
328c349dbc7Sjsg 	u8 avi_if_ctrl;
329c349dbc7Sjsg 	u8 block_count = 0;
330c349dbc7Sjsg 	u8 *data;
331c349dbc7Sjsg 	u16 reg;
332c349dbc7Sjsg 	ssize_t ret;
333c349dbc7Sjsg 
334c349dbc7Sjsg 	while (block_count < 4) {
335c349dbc7Sjsg 		if (!lspcon_parade_fw_ready(aux)) {
3361bb76ff1Sjsg 			drm_dbg_kms(aux->drm_dev, "LSPCON FW not ready, block %d\n",
337c349dbc7Sjsg 				    block_count);
338c349dbc7Sjsg 			return false;
339c349dbc7Sjsg 		}
340c349dbc7Sjsg 
341c349dbc7Sjsg 		reg = LSPCON_PARADE_AVI_IF_WRITE_OFFSET;
342c349dbc7Sjsg 		data = avi_buf + block_count * 8;
343c349dbc7Sjsg 		ret = drm_dp_dpcd_write(aux, reg, data, 8);
344c349dbc7Sjsg 		if (ret < 0) {
3451bb76ff1Sjsg 			drm_err(aux->drm_dev, "Failed to write AVI IF block %d\n",
346c349dbc7Sjsg 				block_count);
347c349dbc7Sjsg 			return false;
348c349dbc7Sjsg 		}
349c349dbc7Sjsg 
350c349dbc7Sjsg 		/*
351c349dbc7Sjsg 		 * Once a block of data is written, we have to inform the FW
352c349dbc7Sjsg 		 * about this by writing into avi infoframe control register:
353c349dbc7Sjsg 		 * - set the kickoff bit[7] to 1
354c349dbc7Sjsg 		 * - write the block no. to bits[1:0]
355c349dbc7Sjsg 		 */
356c349dbc7Sjsg 		reg = LSPCON_PARADE_AVI_IF_CTRL;
357c349dbc7Sjsg 		avi_if_ctrl = LSPCON_PARADE_AVI_IF_KICKOFF | block_count;
358c349dbc7Sjsg 		ret = drm_dp_dpcd_write(aux, reg, &avi_if_ctrl, 1);
359c349dbc7Sjsg 		if (ret < 0) {
3601bb76ff1Sjsg 			drm_err(aux->drm_dev, "Failed to update (0x%x), block %d\n",
361c349dbc7Sjsg 				reg, block_count);
362c349dbc7Sjsg 			return false;
363c349dbc7Sjsg 		}
364c349dbc7Sjsg 
365c349dbc7Sjsg 		block_count++;
366c349dbc7Sjsg 	}
367c349dbc7Sjsg 
3681bb76ff1Sjsg 	drm_dbg_kms(aux->drm_dev, "Wrote AVI IF blocks successfully\n");
369c349dbc7Sjsg 	return true;
370c349dbc7Sjsg }
371c349dbc7Sjsg 
_lspcon_write_avi_infoframe_parade(struct drm_dp_aux * aux,const u8 * frame,ssize_t len)372c349dbc7Sjsg static bool _lspcon_write_avi_infoframe_parade(struct drm_dp_aux *aux,
373c349dbc7Sjsg 					       const u8 *frame,
374c349dbc7Sjsg 					       ssize_t len)
375c349dbc7Sjsg {
376c349dbc7Sjsg 	u8 avi_if[LSPCON_PARADE_AVI_IF_DATA_SIZE] = {1, };
377c349dbc7Sjsg 
378c349dbc7Sjsg 	/*
379c349dbc7Sjsg 	 * Parade's frames contains 32 bytes of data, divided
380c349dbc7Sjsg 	 * into 4 frames:
381c349dbc7Sjsg 	 *	Token byte (first byte of first frame, must be non-zero)
382c349dbc7Sjsg 	 *	HB0 to HB2	 from AVI IF (3 bytes header)
383c349dbc7Sjsg 	 *	PB0 to PB27 from AVI IF (28 bytes data)
384c349dbc7Sjsg 	 * So it should look like this
385c349dbc7Sjsg 	 *	first block: | <token> <HB0-HB2> <DB0-DB3> |
386c349dbc7Sjsg 	 *	next 3 blocks: |<DB4-DB11>|<DB12-DB19>|<DB20-DB28>|
387c349dbc7Sjsg 	 */
388c349dbc7Sjsg 
389c349dbc7Sjsg 	if (len > LSPCON_PARADE_AVI_IF_DATA_SIZE - 1) {
3901bb76ff1Sjsg 		drm_err(aux->drm_dev, "Invalid length of infoframes\n");
391c349dbc7Sjsg 		return false;
392c349dbc7Sjsg 	}
393c349dbc7Sjsg 
394c349dbc7Sjsg 	memcpy(&avi_if[1], frame, len);
395c349dbc7Sjsg 
396c349dbc7Sjsg 	if (!_lspcon_parade_write_infoframe_blocks(aux, avi_if)) {
3971bb76ff1Sjsg 		drm_dbg_kms(aux->drm_dev, "Failed to write infoframe blocks\n");
398c349dbc7Sjsg 		return false;
399c349dbc7Sjsg 	}
400c349dbc7Sjsg 
401c349dbc7Sjsg 	return true;
402c349dbc7Sjsg }
403c349dbc7Sjsg 
_lspcon_write_avi_infoframe_mca(struct drm_dp_aux * aux,const u8 * buffer,ssize_t len)404c349dbc7Sjsg static bool _lspcon_write_avi_infoframe_mca(struct drm_dp_aux *aux,
405c349dbc7Sjsg 					    const u8 *buffer, ssize_t len)
406c349dbc7Sjsg {
407c349dbc7Sjsg 	int ret;
408c349dbc7Sjsg 	u32 val = 0;
409c349dbc7Sjsg 	u32 retry;
410c349dbc7Sjsg 	u16 reg;
411c349dbc7Sjsg 	const u8 *data = buffer;
412c349dbc7Sjsg 
413c349dbc7Sjsg 	reg = LSPCON_MCA_AVI_IF_WRITE_OFFSET;
414c349dbc7Sjsg 	while (val < len) {
415c349dbc7Sjsg 		/* DPCD write for AVI IF can fail on a slow FW day, so retry */
416c349dbc7Sjsg 		for (retry = 0; retry < 5; retry++) {
417c349dbc7Sjsg 			ret = drm_dp_dpcd_write(aux, reg, (void *)data, 1);
418c349dbc7Sjsg 			if (ret == 1) {
419c349dbc7Sjsg 				break;
420c349dbc7Sjsg 			} else if (retry < 4) {
421c349dbc7Sjsg 				mdelay(50);
422c349dbc7Sjsg 				continue;
423c349dbc7Sjsg 			} else {
4241bb76ff1Sjsg 				drm_err(aux->drm_dev, "DPCD write failed at:0x%x\n", reg);
425c349dbc7Sjsg 				return false;
426c349dbc7Sjsg 			}
427c349dbc7Sjsg 		}
428c349dbc7Sjsg 		val++; reg++; data++;
429c349dbc7Sjsg 	}
430c349dbc7Sjsg 
431c349dbc7Sjsg 	val = 0;
432c349dbc7Sjsg 	reg = LSPCON_MCA_AVI_IF_CTRL;
433c349dbc7Sjsg 	ret = drm_dp_dpcd_read(aux, reg, &val, 1);
434c349dbc7Sjsg 	if (ret < 0) {
4351bb76ff1Sjsg 		drm_err(aux->drm_dev, "DPCD read failed, address 0x%x\n", reg);
436c349dbc7Sjsg 		return false;
437c349dbc7Sjsg 	}
438c349dbc7Sjsg 
439c349dbc7Sjsg 	/* Indicate LSPCON chip about infoframe, clear bit 1 and set bit 0 */
440c349dbc7Sjsg 	val &= ~LSPCON_MCA_AVI_IF_HANDLED;
441c349dbc7Sjsg 	val |= LSPCON_MCA_AVI_IF_KICKOFF;
442c349dbc7Sjsg 
443c349dbc7Sjsg 	ret = drm_dp_dpcd_write(aux, reg, &val, 1);
444c349dbc7Sjsg 	if (ret < 0) {
4451bb76ff1Sjsg 		drm_err(aux->drm_dev, "DPCD read failed, address 0x%x\n", reg);
446c349dbc7Sjsg 		return false;
447c349dbc7Sjsg 	}
448c349dbc7Sjsg 
449c349dbc7Sjsg 	val = 0;
450c349dbc7Sjsg 	ret = drm_dp_dpcd_read(aux, reg, &val, 1);
451c349dbc7Sjsg 	if (ret < 0) {
4521bb76ff1Sjsg 		drm_err(aux->drm_dev, "DPCD read failed, address 0x%x\n", reg);
453c349dbc7Sjsg 		return false;
454c349dbc7Sjsg 	}
455c349dbc7Sjsg 
456c349dbc7Sjsg 	if (val == LSPCON_MCA_AVI_IF_HANDLED)
4571bb76ff1Sjsg 		drm_dbg_kms(aux->drm_dev, "AVI IF handled by FW\n");
458c349dbc7Sjsg 
459c349dbc7Sjsg 	return true;
460c349dbc7Sjsg }
461c349dbc7Sjsg 
lspcon_write_infoframe(struct intel_encoder * encoder,const struct intel_crtc_state * crtc_state,unsigned int type,const void * frame,ssize_t len)462c349dbc7Sjsg void lspcon_write_infoframe(struct intel_encoder *encoder,
463c349dbc7Sjsg 			    const struct intel_crtc_state *crtc_state,
464c349dbc7Sjsg 			    unsigned int type,
465c349dbc7Sjsg 			    const void *frame, ssize_t len)
466c349dbc7Sjsg {
4675ca02815Sjsg 	bool ret = true;
468c349dbc7Sjsg 	struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
4691bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
470c349dbc7Sjsg 	struct intel_lspcon *lspcon = enc_to_intel_lspcon(encoder);
471c349dbc7Sjsg 
4725ca02815Sjsg 	switch (type) {
4735ca02815Sjsg 	case HDMI_INFOFRAME_TYPE_AVI:
474c349dbc7Sjsg 		if (lspcon->vendor == LSPCON_VENDOR_MCA)
475c349dbc7Sjsg 			ret = _lspcon_write_avi_infoframe_mca(&intel_dp->aux,
476c349dbc7Sjsg 							      frame, len);
477c349dbc7Sjsg 		else
478c349dbc7Sjsg 			ret = _lspcon_write_avi_infoframe_parade(&intel_dp->aux,
479c349dbc7Sjsg 								 frame, len);
4805ca02815Sjsg 		break;
4815ca02815Sjsg 	case HDMI_PACKET_TYPE_GAMUT_METADATA:
4821bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "Update HDR metadata for lspcon\n");
4835ca02815Sjsg 		/* It uses the legacy hsw implementation for the same */
4845ca02815Sjsg 		hsw_write_infoframe(encoder, crtc_state, type, frame, len);
4855ca02815Sjsg 		break;
4865ca02815Sjsg 	default:
487c349dbc7Sjsg 		return;
488c349dbc7Sjsg 	}
489c349dbc7Sjsg 
4905ca02815Sjsg 	if (!ret) {
4911bb76ff1Sjsg 		drm_err(&i915->drm, "Failed to write infoframes\n");
4925ca02815Sjsg 		return;
4935ca02815Sjsg 	}
494c349dbc7Sjsg }
495c349dbc7Sjsg 
lspcon_read_infoframe(struct intel_encoder * encoder,const struct intel_crtc_state * crtc_state,unsigned int type,void * frame,ssize_t len)496c349dbc7Sjsg void lspcon_read_infoframe(struct intel_encoder *encoder,
497c349dbc7Sjsg 			   const struct intel_crtc_state *crtc_state,
498c349dbc7Sjsg 			   unsigned int type,
499c349dbc7Sjsg 			   void *frame, ssize_t len)
500c349dbc7Sjsg {
5015ca02815Sjsg 	/* FIXME implement for AVI Infoframe as well */
5025ca02815Sjsg 	if (type == HDMI_PACKET_TYPE_GAMUT_METADATA)
5035ca02815Sjsg 		hsw_read_infoframe(encoder, crtc_state, type,
5045ca02815Sjsg 				   frame, len);
505c349dbc7Sjsg }
506c349dbc7Sjsg 
lspcon_set_infoframes(struct intel_encoder * encoder,bool enable,const struct intel_crtc_state * crtc_state,const struct drm_connector_state * conn_state)507c349dbc7Sjsg void lspcon_set_infoframes(struct intel_encoder *encoder,
508c349dbc7Sjsg 			   bool enable,
509c349dbc7Sjsg 			   const struct intel_crtc_state *crtc_state,
510c349dbc7Sjsg 			   const struct drm_connector_state *conn_state)
511c349dbc7Sjsg {
512c349dbc7Sjsg 	ssize_t ret;
513c349dbc7Sjsg 	union hdmi_infoframe frame;
514c349dbc7Sjsg 	u8 buf[VIDEO_DIP_DATA_SIZE];
515c349dbc7Sjsg 	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
516c349dbc7Sjsg 	struct intel_lspcon *lspcon = &dig_port->lspcon;
5171bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(encoder->base.dev);
518c349dbc7Sjsg 	const struct drm_display_mode *adjusted_mode =
519c349dbc7Sjsg 		&crtc_state->hw.adjusted_mode;
520c349dbc7Sjsg 
521c349dbc7Sjsg 	if (!lspcon->active) {
5221bb76ff1Sjsg 		drm_err(&i915->drm, "Writing infoframes while LSPCON disabled ?\n");
523c349dbc7Sjsg 		return;
524c349dbc7Sjsg 	}
525c349dbc7Sjsg 
526c349dbc7Sjsg 	/* FIXME precompute infoframes */
527c349dbc7Sjsg 
528c349dbc7Sjsg 	ret = drm_hdmi_avi_infoframe_from_display_mode(&frame.avi,
529c349dbc7Sjsg 						       conn_state->connector,
530c349dbc7Sjsg 						       adjusted_mode);
531c349dbc7Sjsg 	if (ret < 0) {
5321bb76ff1Sjsg 		drm_err(&i915->drm, "couldn't fill AVI infoframe\n");
533c349dbc7Sjsg 		return;
534c349dbc7Sjsg 	}
535c349dbc7Sjsg 
5365ca02815Sjsg 	/*
5375ca02815Sjsg 	 * Currently there is no interface defined to
5385ca02815Sjsg 	 * check user preference between RGB/YCBCR444
5395ca02815Sjsg 	 * or YCBCR420. So the only possible case for
5405ca02815Sjsg 	 * YCBCR444 usage is driving YCBCR420 output
5415ca02815Sjsg 	 * with LSPCON, when pipe is configured for
5425ca02815Sjsg 	 * YCBCR444 output and LSPCON takes care of
5435ca02815Sjsg 	 * downsampling it.
5445ca02815Sjsg 	 */
5455ca02815Sjsg 	if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_YCBCR444)
546c349dbc7Sjsg 		frame.avi.colorspace = HDMI_COLORSPACE_YUV420;
547c349dbc7Sjsg 	else
548c349dbc7Sjsg 		frame.avi.colorspace = HDMI_COLORSPACE_RGB;
549c349dbc7Sjsg 
5505ca02815Sjsg 	/* Set the Colorspace as per the HDMI spec */
5511bb76ff1Sjsg 	drm_hdmi_avi_infoframe_colorimetry(&frame.avi, conn_state);
5525ca02815Sjsg 
5535ca02815Sjsg 	/* nonsense combination */
5545ca02815Sjsg 	drm_WARN_ON(encoder->base.dev, crtc_state->limited_color_range &&
5555ca02815Sjsg 		    crtc_state->output_format != INTEL_OUTPUT_FORMAT_RGB);
5565ca02815Sjsg 
5575ca02815Sjsg 	if (crtc_state->output_format == INTEL_OUTPUT_FORMAT_RGB) {
558c349dbc7Sjsg 		drm_hdmi_avi_infoframe_quant_range(&frame.avi,
559c349dbc7Sjsg 						   conn_state->connector,
560c349dbc7Sjsg 						   adjusted_mode,
561c349dbc7Sjsg 						   crtc_state->limited_color_range ?
562c349dbc7Sjsg 						   HDMI_QUANTIZATION_RANGE_LIMITED :
563c349dbc7Sjsg 						   HDMI_QUANTIZATION_RANGE_FULL);
5645ca02815Sjsg 	} else {
5655ca02815Sjsg 		frame.avi.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
5665ca02815Sjsg 		frame.avi.ycc_quantization_range = HDMI_YCC_QUANTIZATION_RANGE_LIMITED;
5675ca02815Sjsg 	}
5685ca02815Sjsg 
5695ca02815Sjsg 	drm_hdmi_avi_infoframe_content_type(&frame.avi, conn_state);
570c349dbc7Sjsg 
571c349dbc7Sjsg 	ret = hdmi_infoframe_pack(&frame, buf, sizeof(buf));
572c349dbc7Sjsg 	if (ret < 0) {
5731bb76ff1Sjsg 		drm_err(&i915->drm, "Failed to pack AVI IF\n");
574c349dbc7Sjsg 		return;
575c349dbc7Sjsg 	}
576c349dbc7Sjsg 
577c349dbc7Sjsg 	dig_port->write_infoframe(encoder, crtc_state, HDMI_INFOFRAME_TYPE_AVI,
578c349dbc7Sjsg 				  buf, ret);
579c349dbc7Sjsg }
580c349dbc7Sjsg 
_lspcon_read_avi_infoframe_enabled_mca(struct drm_dp_aux * aux)5815ca02815Sjsg static bool _lspcon_read_avi_infoframe_enabled_mca(struct drm_dp_aux *aux)
5825ca02815Sjsg {
5835ca02815Sjsg 	int ret;
5845ca02815Sjsg 	u32 val = 0;
5855ca02815Sjsg 	u16 reg = LSPCON_MCA_AVI_IF_CTRL;
5865ca02815Sjsg 
5875ca02815Sjsg 	ret = drm_dp_dpcd_read(aux, reg, &val, 1);
5885ca02815Sjsg 	if (ret < 0) {
5891bb76ff1Sjsg 		drm_err(aux->drm_dev, "DPCD read failed, address 0x%x\n", reg);
5905ca02815Sjsg 		return false;
5915ca02815Sjsg 	}
5925ca02815Sjsg 
5935ca02815Sjsg 	return val & LSPCON_MCA_AVI_IF_KICKOFF;
5945ca02815Sjsg }
5955ca02815Sjsg 
_lspcon_read_avi_infoframe_enabled_parade(struct drm_dp_aux * aux)5965ca02815Sjsg static bool _lspcon_read_avi_infoframe_enabled_parade(struct drm_dp_aux *aux)
5975ca02815Sjsg {
5985ca02815Sjsg 	int ret;
5995ca02815Sjsg 	u32 val = 0;
6005ca02815Sjsg 	u16 reg = LSPCON_PARADE_AVI_IF_CTRL;
6015ca02815Sjsg 
6025ca02815Sjsg 	ret = drm_dp_dpcd_read(aux, reg, &val, 1);
6035ca02815Sjsg 	if (ret < 0) {
6041bb76ff1Sjsg 		drm_err(aux->drm_dev, "DPCD read failed, address 0x%x\n", reg);
6055ca02815Sjsg 		return false;
6065ca02815Sjsg 	}
6075ca02815Sjsg 
6085ca02815Sjsg 	return val & LSPCON_PARADE_AVI_IF_KICKOFF;
6095ca02815Sjsg }
6105ca02815Sjsg 
lspcon_infoframes_enabled(struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config)611c349dbc7Sjsg u32 lspcon_infoframes_enabled(struct intel_encoder *encoder,
612c349dbc7Sjsg 			      const struct intel_crtc_state *pipe_config)
613c349dbc7Sjsg {
6145ca02815Sjsg 	struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
6155ca02815Sjsg 	struct intel_lspcon *lspcon = enc_to_intel_lspcon(encoder);
6165ca02815Sjsg 	struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
6175ca02815Sjsg 	bool infoframes_enabled;
6185ca02815Sjsg 	u32 val = 0;
6195ca02815Sjsg 	u32 mask, tmp;
620c349dbc7Sjsg 
6215ca02815Sjsg 	if (lspcon->vendor == LSPCON_VENDOR_MCA)
6225ca02815Sjsg 		infoframes_enabled = _lspcon_read_avi_infoframe_enabled_mca(&intel_dp->aux);
623c349dbc7Sjsg 	else
6245ca02815Sjsg 		infoframes_enabled = _lspcon_read_avi_infoframe_enabled_parade(&intel_dp->aux);
6255ca02815Sjsg 
6265ca02815Sjsg 	if (infoframes_enabled)
6275ca02815Sjsg 		val |= intel_hdmi_infoframe_enable(HDMI_INFOFRAME_TYPE_AVI);
6285ca02815Sjsg 
6295ca02815Sjsg 	if (lspcon->hdr_supported) {
6305ca02815Sjsg 		tmp = intel_de_read(dev_priv,
6315ca02815Sjsg 				    HSW_TVIDEO_DIP_CTL(pipe_config->cpu_transcoder));
6325ca02815Sjsg 		mask = VIDEO_DIP_ENABLE_GMP_HSW;
6335ca02815Sjsg 
6345ca02815Sjsg 		if (tmp & mask)
6355ca02815Sjsg 			val |= intel_hdmi_infoframe_enable(HDMI_PACKET_TYPE_GAMUT_METADATA);
6365ca02815Sjsg 	}
6375ca02815Sjsg 
6385ca02815Sjsg 	return val;
639c349dbc7Sjsg }
640c349dbc7Sjsg 
lspcon_wait_pcon_mode(struct intel_lspcon * lspcon)641c349dbc7Sjsg void lspcon_wait_pcon_mode(struct intel_lspcon *lspcon)
642c349dbc7Sjsg {
643c349dbc7Sjsg 	lspcon_wait_mode(lspcon, DRM_LSPCON_MODE_PCON);
644c349dbc7Sjsg }
645c349dbc7Sjsg 
lspcon_init(struct intel_digital_port * dig_port)646ad8b1aafSjsg bool lspcon_init(struct intel_digital_port *dig_port)
647c349dbc7Sjsg {
6481bb76ff1Sjsg 	struct intel_dp *intel_dp = &dig_port->dp;
649ad8b1aafSjsg 	struct intel_lspcon *lspcon = &dig_port->lspcon;
6501bb76ff1Sjsg 	struct drm_i915_private *i915 = dp_to_i915(intel_dp);
6511bb76ff1Sjsg 	struct drm_connector *connector = &intel_dp->attached_connector->base;
652c349dbc7Sjsg 
653c349dbc7Sjsg 	lspcon->active = false;
654c349dbc7Sjsg 	lspcon->mode = DRM_LSPCON_MODE_INVALID;
655c349dbc7Sjsg 
656c349dbc7Sjsg 	if (!lspcon_probe(lspcon)) {
6571bb76ff1Sjsg 		drm_err(&i915->drm, "Failed to probe lspcon\n");
658c349dbc7Sjsg 		return false;
659c349dbc7Sjsg 	}
660c349dbc7Sjsg 
6611bb76ff1Sjsg 	if (drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd) != 0) {
6621bb76ff1Sjsg 		drm_err(&i915->drm, "LSPCON DPCD read failed\n");
663c349dbc7Sjsg 		return false;
664c349dbc7Sjsg 	}
665c349dbc7Sjsg 
666c349dbc7Sjsg 	if (!lspcon_detect_vendor(lspcon)) {
6671bb76ff1Sjsg 		drm_err(&i915->drm, "LSPCON vendor detection failed\n");
668c349dbc7Sjsg 		return false;
669c349dbc7Sjsg 	}
670c349dbc7Sjsg 
671c349dbc7Sjsg 	connector->ycbcr_420_allowed = true;
672c349dbc7Sjsg 	lspcon->active = true;
6731bb76ff1Sjsg 	drm_dbg_kms(&i915->drm, "Success: LSPCON init\n");
674c349dbc7Sjsg 	return true;
675c349dbc7Sjsg }
6765ca02815Sjsg 
intel_lspcon_infoframes_enabled(struct intel_encoder * encoder,const struct intel_crtc_state * pipe_config)6775ca02815Sjsg u32 intel_lspcon_infoframes_enabled(struct intel_encoder *encoder,
6785ca02815Sjsg 				    const struct intel_crtc_state *pipe_config)
6795ca02815Sjsg {
6805ca02815Sjsg 	struct intel_digital_port *dig_port = enc_to_dig_port(encoder);
6815ca02815Sjsg 
6825ca02815Sjsg 	return dig_port->infoframes_enabled(encoder, pipe_config);
6835ca02815Sjsg }
6845ca02815Sjsg 
lspcon_resume(struct intel_digital_port * dig_port)6855ca02815Sjsg void lspcon_resume(struct intel_digital_port *dig_port)
6865ca02815Sjsg {
6875ca02815Sjsg 	struct intel_lspcon *lspcon = &dig_port->lspcon;
6885ca02815Sjsg 	struct drm_device *dev = dig_port->base.base.dev;
6891bb76ff1Sjsg 	struct drm_i915_private *i915 = to_i915(dev);
6905ca02815Sjsg 	enum drm_lspcon_mode expected_mode;
6915ca02815Sjsg 
692*f005ef32Sjsg 	if (!intel_bios_encoder_is_lspcon(dig_port->base.devdata))
6935ca02815Sjsg 		return;
6945ca02815Sjsg 
6955ca02815Sjsg 	if (!lspcon->active) {
6965ca02815Sjsg 		if (!lspcon_init(dig_port)) {
6971bb76ff1Sjsg 			drm_err(&i915->drm, "LSPCON init failed on port %c\n",
6985ca02815Sjsg 				port_name(dig_port->base.port));
6995ca02815Sjsg 			return;
7005ca02815Sjsg 		}
7015ca02815Sjsg 	}
7025ca02815Sjsg 
7035ca02815Sjsg 	if (lspcon_wake_native_aux_ch(lspcon)) {
7045ca02815Sjsg 		expected_mode = DRM_LSPCON_MODE_PCON;
7055ca02815Sjsg 		lspcon_resume_in_pcon_wa(lspcon);
7065ca02815Sjsg 	} else {
7075ca02815Sjsg 		expected_mode = DRM_LSPCON_MODE_LS;
7085ca02815Sjsg 	}
7095ca02815Sjsg 
7105ca02815Sjsg 	if (lspcon_wait_mode(lspcon, expected_mode) == DRM_LSPCON_MODE_PCON)
7115ca02815Sjsg 		return;
7125ca02815Sjsg 
7135ca02815Sjsg 	if (lspcon_change_mode(lspcon, DRM_LSPCON_MODE_PCON))
7141bb76ff1Sjsg 		drm_err(&i915->drm, "LSPCON resume failed\n");
7155ca02815Sjsg 	else
7161bb76ff1Sjsg 		drm_dbg_kms(&i915->drm, "LSPCON resume success\n");
7175ca02815Sjsg }
718