1*594e9c04SKuoHsiang Chou // SPDX-License-Identifier: GPL-2.0 2*594e9c04SKuoHsiang Chou // Copyright (c) 2021, ASPEED Technology Inc. 3*594e9c04SKuoHsiang Chou // Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> 4*594e9c04SKuoHsiang Chou 5*594e9c04SKuoHsiang Chou #include <linux/firmware.h> 6*594e9c04SKuoHsiang Chou #include <linux/delay.h> 7*594e9c04SKuoHsiang Chou #include <drm/drm_print.h> 8*594e9c04SKuoHsiang Chou #include "ast_drv.h" 9*594e9c04SKuoHsiang Chou 10*594e9c04SKuoHsiang Chou int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata) 11*594e9c04SKuoHsiang Chou { 12*594e9c04SKuoHsiang Chou struct ast_private *ast = to_ast_private(dev); 13*594e9c04SKuoHsiang Chou u8 i = 0, j = 0; 14*594e9c04SKuoHsiang Chou 15*594e9c04SKuoHsiang Chou /* 16*594e9c04SKuoHsiang Chou * CRD1[b5]: DP MCU FW is executing 17*594e9c04SKuoHsiang Chou * CRDC[b0]: DP link success 18*594e9c04SKuoHsiang Chou * CRDF[b0]: DP HPD 19*594e9c04SKuoHsiang Chou * CRE5[b0]: Host reading EDID process is done 20*594e9c04SKuoHsiang Chou */ 21*594e9c04SKuoHsiang Chou if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING) && 22*594e9c04SKuoHsiang Chou ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) && 23*594e9c04SKuoHsiang Chou ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD) && 24*594e9c04SKuoHsiang Chou ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, 25*594e9c04SKuoHsiang Chou ASTDP_HOST_EDID_READ_DONE_MASK))) { 26*594e9c04SKuoHsiang Chou goto err_astdp_edid_not_ready; 27*594e9c04SKuoHsiang Chou } 28*594e9c04SKuoHsiang Chou 29*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, 30*594e9c04SKuoHsiang Chou 0x00); 31*594e9c04SKuoHsiang Chou 32*594e9c04SKuoHsiang Chou for (i = 0; i < 32; i++) { 33*594e9c04SKuoHsiang Chou /* 34*594e9c04SKuoHsiang Chou * CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64 35*594e9c04SKuoHsiang Chou */ 36*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE4, 37*594e9c04SKuoHsiang Chou (u8) ~ASTDP_EDID_READ_POINTER_MASK, (u8) i); 38*594e9c04SKuoHsiang Chou j = 0; 39*594e9c04SKuoHsiang Chou 40*594e9c04SKuoHsiang Chou /* 41*594e9c04SKuoHsiang Chou * CRD7[b0]: valid flag for EDID 42*594e9c04SKuoHsiang Chou * CRD6[b0]: mirror read pointer for EDID 43*594e9c04SKuoHsiang Chou */ 44*594e9c04SKuoHsiang Chou while ((ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD7, 45*594e9c04SKuoHsiang Chou ASTDP_EDID_VALID_FLAG_MASK) != 0x01) || 46*594e9c04SKuoHsiang Chou (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD6, 47*594e9c04SKuoHsiang Chou ASTDP_EDID_READ_POINTER_MASK) != i)) { 48*594e9c04SKuoHsiang Chou /* 49*594e9c04SKuoHsiang Chou * Delay are getting longer with each retry. 50*594e9c04SKuoHsiang Chou * 1. The Delays are often 2 loops when users request "Display Settings" 51*594e9c04SKuoHsiang Chou * of right-click of mouse. 52*594e9c04SKuoHsiang Chou * 2. The Delays are often longer a lot when system resume from S3/S4. 53*594e9c04SKuoHsiang Chou */ 54*594e9c04SKuoHsiang Chou mdelay(j+1); 55*594e9c04SKuoHsiang Chou 56*594e9c04SKuoHsiang Chou if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, 57*594e9c04SKuoHsiang Chou ASTDP_MCU_FW_EXECUTING) && 58*594e9c04SKuoHsiang Chou ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, 59*594e9c04SKuoHsiang Chou ASTDP_LINK_SUCCESS) && 60*594e9c04SKuoHsiang Chou ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))) { 61*594e9c04SKuoHsiang Chou goto err_astdp_jump_out_loop_of_edid; 62*594e9c04SKuoHsiang Chou } 63*594e9c04SKuoHsiang Chou 64*594e9c04SKuoHsiang Chou j++; 65*594e9c04SKuoHsiang Chou if (j > 200) 66*594e9c04SKuoHsiang Chou goto err_astdp_jump_out_loop_of_edid; 67*594e9c04SKuoHsiang Chou } 68*594e9c04SKuoHsiang Chou 69*594e9c04SKuoHsiang Chou *(ediddata) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 70*594e9c04SKuoHsiang Chou 0xD8, ASTDP_EDID_READ_DATA_MASK); 71*594e9c04SKuoHsiang Chou *(ediddata + 1) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD9, 72*594e9c04SKuoHsiang Chou ASTDP_EDID_READ_DATA_MASK); 73*594e9c04SKuoHsiang Chou *(ediddata + 2) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDA, 74*594e9c04SKuoHsiang Chou ASTDP_EDID_READ_DATA_MASK); 75*594e9c04SKuoHsiang Chou *(ediddata + 3) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDB, 76*594e9c04SKuoHsiang Chou ASTDP_EDID_READ_DATA_MASK); 77*594e9c04SKuoHsiang Chou 78*594e9c04SKuoHsiang Chou if (i == 31) { 79*594e9c04SKuoHsiang Chou /* 80*594e9c04SKuoHsiang Chou * For 128-bytes EDID_1.3, 81*594e9c04SKuoHsiang Chou * 1. Add the value of Bytes-126 to Bytes-127. 82*594e9c04SKuoHsiang Chou * The Bytes-127 is Checksum. Sum of all 128bytes should 83*594e9c04SKuoHsiang Chou * equal 0 (mod 256). 84*594e9c04SKuoHsiang Chou * 2. Modify Bytes-126 to be 0. 85*594e9c04SKuoHsiang Chou * The Bytes-126 indicates the Number of extensions to 86*594e9c04SKuoHsiang Chou * follow. 0 represents noextensions. 87*594e9c04SKuoHsiang Chou */ 88*594e9c04SKuoHsiang Chou *(ediddata + 3) = *(ediddata + 3) + *(ediddata + 2); 89*594e9c04SKuoHsiang Chou *(ediddata + 2) = 0; 90*594e9c04SKuoHsiang Chou } 91*594e9c04SKuoHsiang Chou 92*594e9c04SKuoHsiang Chou ediddata += 4; 93*594e9c04SKuoHsiang Chou } 94*594e9c04SKuoHsiang Chou 95*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, 96*594e9c04SKuoHsiang Chou ASTDP_HOST_EDID_READ_DONE); 97*594e9c04SKuoHsiang Chou 98*594e9c04SKuoHsiang Chou return 0; 99*594e9c04SKuoHsiang Chou 100*594e9c04SKuoHsiang Chou err_astdp_jump_out_loop_of_edid: 101*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, 102*594e9c04SKuoHsiang Chou (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, 103*594e9c04SKuoHsiang Chou ASTDP_HOST_EDID_READ_DONE); 104*594e9c04SKuoHsiang Chou return (~(j+256) + 1); 105*594e9c04SKuoHsiang Chou 106*594e9c04SKuoHsiang Chou err_astdp_edid_not_ready: 107*594e9c04SKuoHsiang Chou if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING))) 108*594e9c04SKuoHsiang Chou return (~0xD1 + 1); 109*594e9c04SKuoHsiang Chou if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS))) 110*594e9c04SKuoHsiang Chou return (~0xDC + 1); 111*594e9c04SKuoHsiang Chou if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))) 112*594e9c04SKuoHsiang Chou return (~0xDF + 1); 113*594e9c04SKuoHsiang Chou if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, ASTDP_HOST_EDID_READ_DONE_MASK))) 114*594e9c04SKuoHsiang Chou return (~0xE5 + 1); 115*594e9c04SKuoHsiang Chou 116*594e9c04SKuoHsiang Chou return 0; 117*594e9c04SKuoHsiang Chou } 118*594e9c04SKuoHsiang Chou 119*594e9c04SKuoHsiang Chou /* 120*594e9c04SKuoHsiang Chou * Launch Aspeed DP 121*594e9c04SKuoHsiang Chou */ 122*594e9c04SKuoHsiang Chou void ast_dp_launch(struct drm_device *dev, u8 bPower) 123*594e9c04SKuoHsiang Chou { 124*594e9c04SKuoHsiang Chou u32 i = 0, j = 0, WaitCount = 1; 125*594e9c04SKuoHsiang Chou u8 bDPTX = 0; 126*594e9c04SKuoHsiang Chou u8 bDPExecute = 1; 127*594e9c04SKuoHsiang Chou 128*594e9c04SKuoHsiang Chou struct ast_private *ast = to_ast_private(dev); 129*594e9c04SKuoHsiang Chou // S3 come back, need more time to wait BMC ready. 130*594e9c04SKuoHsiang Chou if (bPower) 131*594e9c04SKuoHsiang Chou WaitCount = 300; 132*594e9c04SKuoHsiang Chou 133*594e9c04SKuoHsiang Chou 134*594e9c04SKuoHsiang Chou // Wait total count by different condition. 135*594e9c04SKuoHsiang Chou for (j = 0; j < WaitCount; j++) { 136*594e9c04SKuoHsiang Chou bDPTX = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, TX_TYPE_MASK); 137*594e9c04SKuoHsiang Chou 138*594e9c04SKuoHsiang Chou if (bDPTX) 139*594e9c04SKuoHsiang Chou break; 140*594e9c04SKuoHsiang Chou 141*594e9c04SKuoHsiang Chou msleep(100); 142*594e9c04SKuoHsiang Chou } 143*594e9c04SKuoHsiang Chou 144*594e9c04SKuoHsiang Chou // 0xE : ASTDP with DPMCU FW handling 145*594e9c04SKuoHsiang Chou if (bDPTX == ASTDP_DPMCU_TX) { 146*594e9c04SKuoHsiang Chou // Wait one second then timeout. 147*594e9c04SKuoHsiang Chou i = 0; 148*594e9c04SKuoHsiang Chou 149*594e9c04SKuoHsiang Chou while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, COPROCESSOR_LAUNCH) != 150*594e9c04SKuoHsiang Chou COPROCESSOR_LAUNCH) { 151*594e9c04SKuoHsiang Chou i++; 152*594e9c04SKuoHsiang Chou // wait 100 ms 153*594e9c04SKuoHsiang Chou msleep(100); 154*594e9c04SKuoHsiang Chou 155*594e9c04SKuoHsiang Chou if (i >= 10) { 156*594e9c04SKuoHsiang Chou // DP would not be ready. 157*594e9c04SKuoHsiang Chou bDPExecute = 0; 158*594e9c04SKuoHsiang Chou break; 159*594e9c04SKuoHsiang Chou } 160*594e9c04SKuoHsiang Chou } 161*594e9c04SKuoHsiang Chou 162*594e9c04SKuoHsiang Chou if (bDPExecute) 163*594e9c04SKuoHsiang Chou ast->tx_chip_type = AST_TX_ASTDP; 164*594e9c04SKuoHsiang Chou 165*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, 166*594e9c04SKuoHsiang Chou (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, 167*594e9c04SKuoHsiang Chou ASTDP_HOST_EDID_READ_DONE); 168*594e9c04SKuoHsiang Chou } else 169*594e9c04SKuoHsiang Chou ast->tx_chip_type = AST_TX_NONE; 170*594e9c04SKuoHsiang Chou } 171*594e9c04SKuoHsiang Chou 172*594e9c04SKuoHsiang Chou 173*594e9c04SKuoHsiang Chou 174*594e9c04SKuoHsiang Chou void ast_dp_power_on_off(struct drm_device *dev, bool on) 175*594e9c04SKuoHsiang Chou { 176*594e9c04SKuoHsiang Chou struct ast_private *ast = to_ast_private(dev); 177*594e9c04SKuoHsiang Chou // Read and Turn off DP PHY sleep 178*594e9c04SKuoHsiang Chou u8 bE3 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, AST_DP_VIDEO_ENABLE); 179*594e9c04SKuoHsiang Chou 180*594e9c04SKuoHsiang Chou // Turn on DP PHY sleep 181*594e9c04SKuoHsiang Chou if (!on) 182*594e9c04SKuoHsiang Chou bE3 |= AST_DP_PHY_SLEEP; 183*594e9c04SKuoHsiang Chou 184*594e9c04SKuoHsiang Chou // DP Power on/off 185*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_PHY_SLEEP, bE3); 186*594e9c04SKuoHsiang Chou } 187*594e9c04SKuoHsiang Chou 188*594e9c04SKuoHsiang Chou 189*594e9c04SKuoHsiang Chou 190*594e9c04SKuoHsiang Chou void ast_dp_set_on_off(struct drm_device *dev, bool on) 191*594e9c04SKuoHsiang Chou { 192*594e9c04SKuoHsiang Chou struct ast_private *ast = to_ast_private(dev); 193*594e9c04SKuoHsiang Chou u8 video_on_off = on; 194*594e9c04SKuoHsiang Chou 195*594e9c04SKuoHsiang Chou // Video On/Off 196*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_VIDEO_ENABLE, on); 197*594e9c04SKuoHsiang Chou 198*594e9c04SKuoHsiang Chou // If DP plug in and link successful then check video on / off status 199*594e9c04SKuoHsiang Chou if (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) && 200*594e9c04SKuoHsiang Chou ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)) { 201*594e9c04SKuoHsiang Chou video_on_off <<= 4; 202*594e9c04SKuoHsiang Chou while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, 203*594e9c04SKuoHsiang Chou ASTDP_MIRROR_VIDEO_ENABLE) != video_on_off) { 204*594e9c04SKuoHsiang Chou // wait 1 ms 205*594e9c04SKuoHsiang Chou mdelay(1); 206*594e9c04SKuoHsiang Chou } 207*594e9c04SKuoHsiang Chou } 208*594e9c04SKuoHsiang Chou } 209*594e9c04SKuoHsiang Chou 210*594e9c04SKuoHsiang Chou void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode) 211*594e9c04SKuoHsiang Chou { 212*594e9c04SKuoHsiang Chou struct ast_private *ast = to_ast_private(crtc->dev); 213*594e9c04SKuoHsiang Chou 214*594e9c04SKuoHsiang Chou u32 ulRefreshRateIndex; 215*594e9c04SKuoHsiang Chou u8 ModeIdx; 216*594e9c04SKuoHsiang Chou 217*594e9c04SKuoHsiang Chou ulRefreshRateIndex = vbios_mode->enh_table->refresh_rate_index - 1; 218*594e9c04SKuoHsiang Chou 219*594e9c04SKuoHsiang Chou switch (crtc->mode.crtc_hdisplay) { 220*594e9c04SKuoHsiang Chou case 320: 221*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_320x240_60; 222*594e9c04SKuoHsiang Chou break; 223*594e9c04SKuoHsiang Chou case 400: 224*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_400x300_60; 225*594e9c04SKuoHsiang Chou break; 226*594e9c04SKuoHsiang Chou case 512: 227*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_512x384_60; 228*594e9c04SKuoHsiang Chou break; 229*594e9c04SKuoHsiang Chou case 640: 230*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_640x480_60 + (u8) ulRefreshRateIndex); 231*594e9c04SKuoHsiang Chou break; 232*594e9c04SKuoHsiang Chou case 800: 233*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_800x600_56 + (u8) ulRefreshRateIndex); 234*594e9c04SKuoHsiang Chou break; 235*594e9c04SKuoHsiang Chou case 1024: 236*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_1024x768_60 + (u8) ulRefreshRateIndex); 237*594e9c04SKuoHsiang Chou break; 238*594e9c04SKuoHsiang Chou case 1152: 239*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_1152x864_75; 240*594e9c04SKuoHsiang Chou break; 241*594e9c04SKuoHsiang Chou case 1280: 242*594e9c04SKuoHsiang Chou if (crtc->mode.crtc_vdisplay == 800) 243*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_1280x800_60_RB - (u8) ulRefreshRateIndex); 244*594e9c04SKuoHsiang Chou else // 1024 245*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_1280x1024_60 + (u8) ulRefreshRateIndex); 246*594e9c04SKuoHsiang Chou break; 247*594e9c04SKuoHsiang Chou case 1360: 248*594e9c04SKuoHsiang Chou case 1366: 249*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_1366x768_60; 250*594e9c04SKuoHsiang Chou break; 251*594e9c04SKuoHsiang Chou case 1440: 252*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_1440x900_60_RB - (u8) ulRefreshRateIndex); 253*594e9c04SKuoHsiang Chou break; 254*594e9c04SKuoHsiang Chou case 1600: 255*594e9c04SKuoHsiang Chou if (crtc->mode.crtc_vdisplay == 900) 256*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_1600x900_60_RB - (u8) ulRefreshRateIndex); 257*594e9c04SKuoHsiang Chou else //1200 258*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_1600x1200_60; 259*594e9c04SKuoHsiang Chou break; 260*594e9c04SKuoHsiang Chou case 1680: 261*594e9c04SKuoHsiang Chou ModeIdx = (ASTDP_1680x1050_60_RB - (u8) ulRefreshRateIndex); 262*594e9c04SKuoHsiang Chou break; 263*594e9c04SKuoHsiang Chou case 1920: 264*594e9c04SKuoHsiang Chou if (crtc->mode.crtc_vdisplay == 1080) 265*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_1920x1080_60; 266*594e9c04SKuoHsiang Chou else //1200 267*594e9c04SKuoHsiang Chou ModeIdx = ASTDP_1920x1200_60; 268*594e9c04SKuoHsiang Chou break; 269*594e9c04SKuoHsiang Chou default: 270*594e9c04SKuoHsiang Chou return; 271*594e9c04SKuoHsiang Chou } 272*594e9c04SKuoHsiang Chou 273*594e9c04SKuoHsiang Chou /* 274*594e9c04SKuoHsiang Chou * CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp) 275*594e9c04SKuoHsiang Chou * CRE1[7:0]: MISC1 (default: 0x00) 276*594e9c04SKuoHsiang Chou * CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50) 277*594e9c04SKuoHsiang Chou */ 278*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE0, (u8) ~ASTDP_CLEAR_MASK, 279*594e9c04SKuoHsiang Chou ASTDP_MISC0_24bpp); 280*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE1, (u8) ~ASTDP_CLEAR_MASK, ASTDP_MISC1); 281*594e9c04SKuoHsiang Chou ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE2, (u8) ~ASTDP_CLEAR_MASK, ModeIdx); 282*594e9c04SKuoHsiang Chou } 283