282 lines
7.6 KiB
C
282 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"
|
||
|
|
||
|
int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata)
|
||
|
{
|
||
|
struct ast_private *ast = to_ast_private(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, u8 bPower)
|
||
|
{
|
||
|
u32 i = 0, j = 0, WaitCount = 1;
|
||
|
u8 bDPTX = 0;
|
||
|
u8 bDPExecute = 1;
|
||
|
|
||
|
struct ast_private *ast = to_ast_private(dev);
|
||
|
// S3 come back, need more time to wait BMC ready.
|
||
|
if (bPower)
|
||
|
WaitCount = 300;
|
||
|
|
||
|
|
||
|
// Wait total count by different condition.
|
||
|
for (j = 0; j < WaitCount; j++) {
|
||
|
bDPTX = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, TX_TYPE_MASK);
|
||
|
|
||
|
if (bDPTX)
|
||
|
break;
|
||
|
|
||
|
msleep(100);
|
||
|
}
|
||
|
|
||
|
// 0xE : ASTDP with DPMCU FW handling
|
||
|
if (bDPTX == ASTDP_DPMCU_TX) {
|
||
|
// Wait one second then timeout.
|
||
|
i = 0;
|
||
|
|
||
|
while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, COPROCESSOR_LAUNCH) !=
|
||
|
COPROCESSOR_LAUNCH) {
|
||
|
i++;
|
||
|
// wait 100 ms
|
||
|
msleep(100);
|
||
|
|
||
|
if (i >= 10) {
|
||
|
// DP would not be ready.
|
||
|
bDPExecute = 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bDPExecute)
|
||
|
ast->tx_chip_types |= BIT(AST_TX_ASTDP);
|
||
|
|
||
|
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_private *ast = to_ast_private(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_private *ast = to_ast_private(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_private *ast = to_ast_private(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);
|
||
|
}
|