Aspeed always report the display port as "connected", because it doesn't set a .detect_ctx callback. Fix this by providing the proper detect callback for astdp and dp501. This also fixes the following regression: Since commitfae7d18640
("drm/probe-helper: Default to 640x480 if no EDID on DP") The default resolution is now 640x480 when no monitor is connected. But Aspeed graphics is mostly used in servers, where no monitor is attached. This also affects the remote BMC resolution to 640x480, which is inconvenient, and breaks the anaconda installer. v2: Add .detect callback to the dp/dp501 connector (Jani Nikula) v3: Use .detect_ctx callback, and refactors (Thomas Zimmermann) Add a BMC virtual connector v4: Better indent detect_ctx() functions (Thomas Zimmermann) v5: Enable polling of the dp and dp501 connector status (Thomas Zimmermann) v6: Change check order in ast_astdp_is_connected (Jammy Huang) Fixes:fae7d18640
("drm/probe-helper: Default to 640x480 if no EDID on DP") Signed-off-by: Jocelyn Falempe <jfalempe@redhat.com> Reviewed-by: Thomas Zimmermann <tzimmermann@suse.de> Link: https://patchwork.freedesktop.org/patch/msgid/20230713134316.332502-2-jfalempe@redhat.com
271 lines
7.6 KiB
C
271 lines
7.6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) 2021, ASPEED Technology Inc.
|
|
// Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com>
|
|
|
|
#include <linux/firmware.h>
|
|
#include <linux/delay.h>
|
|
#include <drm/drm_print.h>
|
|
#include "ast_drv.h"
|
|
|
|
bool ast_astdp_is_connected(struct ast_device *ast)
|
|
{
|
|
if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING))
|
|
return false;
|
|
if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))
|
|
return false;
|
|
if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata)
|
|
{
|
|
struct ast_device *ast = to_ast_device(dev);
|
|
u8 i = 0, j = 0;
|
|
|
|
/*
|
|
* CRD1[b5]: DP MCU FW is executing
|
|
* CRDC[b0]: DP link success
|
|
* CRDF[b0]: DP HPD
|
|
* CRE5[b0]: Host reading EDID process is done
|
|
*/
|
|
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING) &&
|
|
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) &&
|
|
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD) &&
|
|
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
|
|
ASTDP_HOST_EDID_READ_DONE_MASK))) {
|
|
goto err_astdp_edid_not_ready;
|
|
}
|
|
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
|
0x00);
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
/*
|
|
* CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64
|
|
*/
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE4,
|
|
ASTDP_AND_CLEAR_MASK, (u8)i);
|
|
j = 0;
|
|
|
|
/*
|
|
* CRD7[b0]: valid flag for EDID
|
|
* CRD6[b0]: mirror read pointer for EDID
|
|
*/
|
|
while ((ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD7,
|
|
ASTDP_EDID_VALID_FLAG_MASK) != 0x01) ||
|
|
(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD6,
|
|
ASTDP_EDID_READ_POINTER_MASK) != i)) {
|
|
/*
|
|
* Delay are getting longer with each retry.
|
|
* 1. The Delays are often 2 loops when users request "Display Settings"
|
|
* of right-click of mouse.
|
|
* 2. The Delays are often longer a lot when system resume from S3/S4.
|
|
*/
|
|
mdelay(j+1);
|
|
|
|
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1,
|
|
ASTDP_MCU_FW_EXECUTING) &&
|
|
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC,
|
|
ASTDP_LINK_SUCCESS) &&
|
|
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))) {
|
|
goto err_astdp_jump_out_loop_of_edid;
|
|
}
|
|
|
|
j++;
|
|
if (j > 200)
|
|
goto err_astdp_jump_out_loop_of_edid;
|
|
}
|
|
|
|
*(ediddata) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT,
|
|
0xD8, ASTDP_EDID_READ_DATA_MASK);
|
|
*(ediddata + 1) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD9,
|
|
ASTDP_EDID_READ_DATA_MASK);
|
|
*(ediddata + 2) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDA,
|
|
ASTDP_EDID_READ_DATA_MASK);
|
|
*(ediddata + 3) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDB,
|
|
ASTDP_EDID_READ_DATA_MASK);
|
|
|
|
if (i == 31) {
|
|
/*
|
|
* For 128-bytes EDID_1.3,
|
|
* 1. Add the value of Bytes-126 to Bytes-127.
|
|
* The Bytes-127 is Checksum. Sum of all 128bytes should
|
|
* equal 0 (mod 256).
|
|
* 2. Modify Bytes-126 to be 0.
|
|
* The Bytes-126 indicates the Number of extensions to
|
|
* follow. 0 represents noextensions.
|
|
*/
|
|
*(ediddata + 3) = *(ediddata + 3) + *(ediddata + 2);
|
|
*(ediddata + 2) = 0;
|
|
}
|
|
|
|
ediddata += 4;
|
|
}
|
|
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
|
ASTDP_HOST_EDID_READ_DONE);
|
|
|
|
return 0;
|
|
|
|
err_astdp_jump_out_loop_of_edid:
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
|
|
(u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
|
ASTDP_HOST_EDID_READ_DONE);
|
|
return (~(j+256) + 1);
|
|
|
|
err_astdp_edid_not_ready:
|
|
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING)))
|
|
return (~0xD1 + 1);
|
|
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS)))
|
|
return (~0xDC + 1);
|
|
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)))
|
|
return (~0xDF + 1);
|
|
if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, ASTDP_HOST_EDID_READ_DONE_MASK)))
|
|
return (~0xE5 + 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Launch Aspeed DP
|
|
*/
|
|
void ast_dp_launch(struct drm_device *dev)
|
|
{
|
|
u32 i = 0;
|
|
u8 bDPExecute = 1;
|
|
struct ast_device *ast = to_ast_device(dev);
|
|
|
|
// Wait one second then timeout.
|
|
while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING) !=
|
|
ASTDP_MCU_FW_EXECUTING) {
|
|
i++;
|
|
// wait 100 ms
|
|
msleep(100);
|
|
|
|
if (i >= 10) {
|
|
// DP would not be ready.
|
|
bDPExecute = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bDPExecute)
|
|
drm_err(dev, "Wait DPMCU executing timeout\n");
|
|
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
|
|
(u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
|
|
ASTDP_HOST_EDID_READ_DONE);
|
|
}
|
|
|
|
|
|
|
|
void ast_dp_power_on_off(struct drm_device *dev, bool on)
|
|
{
|
|
struct ast_device *ast = to_ast_device(dev);
|
|
// Read and Turn off DP PHY sleep
|
|
u8 bE3 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, AST_DP_VIDEO_ENABLE);
|
|
|
|
// Turn on DP PHY sleep
|
|
if (!on)
|
|
bE3 |= AST_DP_PHY_SLEEP;
|
|
|
|
// DP Power on/off
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_PHY_SLEEP, bE3);
|
|
}
|
|
|
|
|
|
|
|
void ast_dp_set_on_off(struct drm_device *dev, bool on)
|
|
{
|
|
struct ast_device *ast = to_ast_device(dev);
|
|
u8 video_on_off = on;
|
|
|
|
// Video On/Off
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_VIDEO_ENABLE, on);
|
|
|
|
// If DP plug in and link successful then check video on / off status
|
|
if (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) &&
|
|
ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)) {
|
|
video_on_off <<= 4;
|
|
while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF,
|
|
ASTDP_MIRROR_VIDEO_ENABLE) != video_on_off) {
|
|
// wait 1 ms
|
|
mdelay(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode)
|
|
{
|
|
struct ast_device *ast = to_ast_device(crtc->dev);
|
|
|
|
u32 ulRefreshRateIndex;
|
|
u8 ModeIdx;
|
|
|
|
ulRefreshRateIndex = vbios_mode->enh_table->refresh_rate_index - 1;
|
|
|
|
switch (crtc->mode.crtc_hdisplay) {
|
|
case 320:
|
|
ModeIdx = ASTDP_320x240_60;
|
|
break;
|
|
case 400:
|
|
ModeIdx = ASTDP_400x300_60;
|
|
break;
|
|
case 512:
|
|
ModeIdx = ASTDP_512x384_60;
|
|
break;
|
|
case 640:
|
|
ModeIdx = (ASTDP_640x480_60 + (u8) ulRefreshRateIndex);
|
|
break;
|
|
case 800:
|
|
ModeIdx = (ASTDP_800x600_56 + (u8) ulRefreshRateIndex);
|
|
break;
|
|
case 1024:
|
|
ModeIdx = (ASTDP_1024x768_60 + (u8) ulRefreshRateIndex);
|
|
break;
|
|
case 1152:
|
|
ModeIdx = ASTDP_1152x864_75;
|
|
break;
|
|
case 1280:
|
|
if (crtc->mode.crtc_vdisplay == 800)
|
|
ModeIdx = (ASTDP_1280x800_60_RB - (u8) ulRefreshRateIndex);
|
|
else // 1024
|
|
ModeIdx = (ASTDP_1280x1024_60 + (u8) ulRefreshRateIndex);
|
|
break;
|
|
case 1360:
|
|
case 1366:
|
|
ModeIdx = ASTDP_1366x768_60;
|
|
break;
|
|
case 1440:
|
|
ModeIdx = (ASTDP_1440x900_60_RB - (u8) ulRefreshRateIndex);
|
|
break;
|
|
case 1600:
|
|
if (crtc->mode.crtc_vdisplay == 900)
|
|
ModeIdx = (ASTDP_1600x900_60_RB - (u8) ulRefreshRateIndex);
|
|
else //1200
|
|
ModeIdx = ASTDP_1600x1200_60;
|
|
break;
|
|
case 1680:
|
|
ModeIdx = (ASTDP_1680x1050_60_RB - (u8) ulRefreshRateIndex);
|
|
break;
|
|
case 1920:
|
|
if (crtc->mode.crtc_vdisplay == 1080)
|
|
ModeIdx = ASTDP_1920x1080_60;
|
|
else //1200
|
|
ModeIdx = ASTDP_1920x1200_60;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp)
|
|
* CRE1[7:0]: MISC1 (default: 0x00)
|
|
* CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50)
|
|
*/
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE0, ASTDP_AND_CLEAR_MASK,
|
|
ASTDP_MISC0_24bpp);
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE1, ASTDP_AND_CLEAR_MASK, ASTDP_MISC1);
|
|
ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE2, ASTDP_AND_CLEAR_MASK, ModeIdx);
|
|
}
|