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, ¤t_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, ¤t_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