18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h> 78c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h> 88c2ecf20Sopenharmony_ci#include <drm/drm_crtc.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "msm_drv.h" 118c2ecf20Sopenharmony_ci#include "msm_kms.h" 128c2ecf20Sopenharmony_ci#include "dp_drm.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistruct dp_connector { 158c2ecf20Sopenharmony_ci struct drm_connector base; 168c2ecf20Sopenharmony_ci struct msm_dp *dp_display; 178c2ecf20Sopenharmony_ci}; 188c2ecf20Sopenharmony_ci#define to_dp_connector(x) container_of(x, struct dp_connector, base) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/** 218c2ecf20Sopenharmony_ci * dp_connector_detect - callback to determine if connector is connected 228c2ecf20Sopenharmony_ci * @conn: Pointer to drm connector structure 238c2ecf20Sopenharmony_ci * @force: Force detect setting from drm framework 248c2ecf20Sopenharmony_ci * Returns: Connector 'is connected' status 258c2ecf20Sopenharmony_ci */ 268c2ecf20Sopenharmony_cistatic enum drm_connector_status dp_connector_detect(struct drm_connector *conn, 278c2ecf20Sopenharmony_ci bool force) 288c2ecf20Sopenharmony_ci{ 298c2ecf20Sopenharmony_ci struct msm_dp *dp; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci dp = to_dp_connector(conn)->dp_display; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci DRM_DEBUG_DP("is_connected = %s\n", 348c2ecf20Sopenharmony_ci (dp->is_connected) ? "true" : "false"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci return (dp->is_connected) ? connector_status_connected : 378c2ecf20Sopenharmony_ci connector_status_disconnected; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/** 418c2ecf20Sopenharmony_ci * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add() 428c2ecf20Sopenharmony_ci * @connector: Pointer to drm connector structure 438c2ecf20Sopenharmony_ci * Returns: Number of modes added 448c2ecf20Sopenharmony_ci */ 458c2ecf20Sopenharmony_cistatic int dp_connector_get_modes(struct drm_connector *connector) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci int rc = 0; 488c2ecf20Sopenharmony_ci struct msm_dp *dp; 498c2ecf20Sopenharmony_ci struct dp_display_mode *dp_mode = NULL; 508c2ecf20Sopenharmony_ci struct drm_display_mode *m, drm_mode; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (!connector) 538c2ecf20Sopenharmony_ci return 0; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci dp = to_dp_connector(connector)->dp_display; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci dp_mode = kzalloc(sizeof(*dp_mode), GFP_KERNEL); 588c2ecf20Sopenharmony_ci if (!dp_mode) 598c2ecf20Sopenharmony_ci return 0; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* pluggable case assumes EDID is read when HPD */ 628c2ecf20Sopenharmony_ci if (dp->is_connected) { 638c2ecf20Sopenharmony_ci /* 648c2ecf20Sopenharmony_ci *The get_modes() function might return one mode that is stored 658c2ecf20Sopenharmony_ci * in dp_mode when compliance test is in progress. If not, the 668c2ecf20Sopenharmony_ci * return value is equal to the total number of modes supported 678c2ecf20Sopenharmony_ci * by the sink 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_ci rc = dp_display_get_modes(dp, dp_mode); 708c2ecf20Sopenharmony_ci if (rc <= 0) { 718c2ecf20Sopenharmony_ci DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc); 728c2ecf20Sopenharmony_ci kfree(dp_mode); 738c2ecf20Sopenharmony_ci return rc; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci if (dp_mode->drm_mode.clock) { /* valid DP mode */ 768c2ecf20Sopenharmony_ci memset(&drm_mode, 0x0, sizeof(drm_mode)); 778c2ecf20Sopenharmony_ci drm_mode_copy(&drm_mode, &dp_mode->drm_mode); 788c2ecf20Sopenharmony_ci m = drm_mode_duplicate(connector->dev, &drm_mode); 798c2ecf20Sopenharmony_ci if (!m) { 808c2ecf20Sopenharmony_ci DRM_ERROR("failed to add mode %ux%u\n", 818c2ecf20Sopenharmony_ci drm_mode.hdisplay, 828c2ecf20Sopenharmony_ci drm_mode.vdisplay); 838c2ecf20Sopenharmony_ci kfree(dp_mode); 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci drm_mode_probed_add(connector, m); 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci } else { 898c2ecf20Sopenharmony_ci DRM_DEBUG_DP("No sink connected\n"); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci kfree(dp_mode); 928c2ecf20Sopenharmony_ci return rc; 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/** 968c2ecf20Sopenharmony_ci * dp_connector_mode_valid - callback to determine if specified mode is valid 978c2ecf20Sopenharmony_ci * @connector: Pointer to drm connector structure 988c2ecf20Sopenharmony_ci * @mode: Pointer to drm mode structure 998c2ecf20Sopenharmony_ci * Returns: Validity status for specified mode 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic enum drm_mode_status dp_connector_mode_valid( 1028c2ecf20Sopenharmony_ci struct drm_connector *connector, 1038c2ecf20Sopenharmony_ci struct drm_display_mode *mode) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct msm_dp *dp_disp; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci dp_disp = to_dp_connector(connector)->dp_display; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if ((dp_disp->max_pclk_khz <= 0) || 1108c2ecf20Sopenharmony_ci (dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) || 1118c2ecf20Sopenharmony_ci (mode->clock > dp_disp->max_pclk_khz)) 1128c2ecf20Sopenharmony_ci return MODE_BAD; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci return dp_display_validate_mode(dp_disp, mode->clock); 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs dp_connector_funcs = { 1188c2ecf20Sopenharmony_ci .detect = dp_connector_detect, 1198c2ecf20Sopenharmony_ci .fill_modes = drm_helper_probe_single_connector_modes, 1208c2ecf20Sopenharmony_ci .destroy = drm_connector_cleanup, 1218c2ecf20Sopenharmony_ci .reset = drm_atomic_helper_connector_reset, 1228c2ecf20Sopenharmony_ci .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, 1238c2ecf20Sopenharmony_ci .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, 1248c2ecf20Sopenharmony_ci}; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs dp_connector_helper_funcs = { 1278c2ecf20Sopenharmony_ci .get_modes = dp_connector_get_modes, 1288c2ecf20Sopenharmony_ci .mode_valid = dp_connector_mode_valid, 1298c2ecf20Sopenharmony_ci}; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci/* connector initialization */ 1328c2ecf20Sopenharmony_cistruct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct drm_connector *connector = NULL; 1358c2ecf20Sopenharmony_ci struct dp_connector *dp_connector; 1368c2ecf20Sopenharmony_ci int ret; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci dp_connector = devm_kzalloc(dp_display->drm_dev->dev, 1398c2ecf20Sopenharmony_ci sizeof(*dp_connector), 1408c2ecf20Sopenharmony_ci GFP_KERNEL); 1418c2ecf20Sopenharmony_ci if (!dp_connector) 1428c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci dp_connector->dp_display = dp_display; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci connector = &dp_connector->base; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci ret = drm_connector_init(dp_display->drm_dev, connector, 1498c2ecf20Sopenharmony_ci &dp_connector_funcs, 1508c2ecf20Sopenharmony_ci DRM_MODE_CONNECTOR_DisplayPort); 1518c2ecf20Sopenharmony_ci if (ret) 1528c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci drm_connector_helper_add(connector, &dp_connector_helper_funcs); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci /* 1578c2ecf20Sopenharmony_ci * Enable HPD to let hpd event is handled when cable is connected. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_ci connector->polled = DRM_CONNECTOR_POLL_HPD; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci drm_connector_attach_encoder(connector, dp_display->encoder); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci return connector; 1648c2ecf20Sopenharmony_ci} 165