You've already forked linux-rockchip
mirror of
https://github.com/armbian/linux-rockchip.git
synced 2026-01-06 11:08:10 -08:00
drm/msm/dp: add displayPort driver support
Add the needed displayPort files to enable DP driver on msm target. "dp_display" module is the main module that calls into other sub-modules. "dp_drm" file represents the interface between DRM framework and DP driver. Changes in v12: -- Add support of pm ops in display port driver -- Clear bpp depth bits before writing to MISC register -- Fix edid read Previous Change log: https://lkml.kernel.org/lkml/20200818051137.21478-3-tanmay@codeaurora.org/ Signed-off-by: Chandan Uddaraju <chandanu@codeaurora.org> Signed-off-by: Vara Reddy <varar@codeaurora.org> Signed-off-by: Tanmay Shah <tanmay@codeaurora.org> Co-developed-by: Abhinav Kumar <abhinavk@codeaurora.org> Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org> Co-developed-by: Kuogee Hsieh <khsieh@codeaurora.org> Signed-off-by: Kuogee Hsieh <khsieh@codeaurora.org> Co-developed-by: Guenter Roeck <groeck@chromium.org> Signed-off-by: Guenter Roeck <groeck@chromium.org> Co-developed-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Stephen Boyd <swboyd@chromium.org> Signed-off-by: Rob Clark <robdclark@chromium.org>
This commit is contained in:
committed by
Rob Clark
parent
b22960b8f2
commit
c943b4948b
@@ -58,6 +58,14 @@ config DRM_MSM_HDMI_HDCP
|
||||
help
|
||||
Choose this option to enable HDCP state machine
|
||||
|
||||
config DRM_MSM_DP
|
||||
bool "Enable DisplayPort support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
help
|
||||
Compile in support for DP driver in MSM DRM driver. DP external
|
||||
display support is enabled through this config option. It can
|
||||
be primary or secondary display on device.
|
||||
|
||||
config DRM_MSM_DSI
|
||||
bool "Enable DSI support in MSM DRM driver"
|
||||
depends on DRM_MSM
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
ccflags-y := -I $(srctree)/$(src)
|
||||
ccflags-y += -I $(srctree)/$(src)/disp/dpu1
|
||||
ccflags-$(CONFIG_DRM_MSM_DSI) += -I $(srctree)/$(src)/dsi
|
||||
ccflags-$(CONFIG_DRM_MSM_DP) += -I $(srctree)/$(src)/dp
|
||||
|
||||
msm-y := \
|
||||
adreno/adreno_device.o \
|
||||
@@ -99,6 +100,17 @@ msm-$(CONFIG_DEBUG_FS) += adreno/a5xx_debugfs.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_GPU_STATE) += adreno/a6xx_gpu_state.o
|
||||
|
||||
msm-$(CONFIG_DRM_MSM_DP)+= dp/dp_aux.o \
|
||||
dp/dp_catalog.o \
|
||||
dp/dp_ctrl.o \
|
||||
dp/dp_display.o \
|
||||
dp/dp_drm.o \
|
||||
dp/dp_hpd.o \
|
||||
dp/dp_link.o \
|
||||
dp/dp_panel.o \
|
||||
dp/dp_parser.o \
|
||||
dp/dp_power.o
|
||||
|
||||
msm-$(CONFIG_DRM_FBDEV_EMULATION) += msm_fbdev.o
|
||||
msm-$(CONFIG_COMMON_CLK) += disp/mdp4/mdp4_lvds_pll.o
|
||||
msm-$(CONFIG_COMMON_CLK) += hdmi/hdmi_pll_8960.o
|
||||
|
||||
@@ -1001,6 +1001,9 @@ static void dpu_encoder_virt_mode_set(struct drm_encoder *drm_enc,
|
||||
|
||||
trace_dpu_enc_mode_set(DRMID(drm_enc));
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp)
|
||||
msm_dp_display_mode_set(priv->dp, drm_enc, mode, adj_mode);
|
||||
|
||||
list_for_each_entry(conn_iter, connector_list, head)
|
||||
if (conn_iter->encoder == drm_enc)
|
||||
conn = conn_iter;
|
||||
@@ -1146,6 +1149,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
|
||||
{
|
||||
struct dpu_encoder_virt *dpu_enc = NULL;
|
||||
int ret = 0;
|
||||
struct msm_drm_private *priv;
|
||||
struct drm_display_mode *cur_mode = NULL;
|
||||
|
||||
if (!drm_enc) {
|
||||
@@ -1156,6 +1160,7 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
|
||||
|
||||
mutex_lock(&dpu_enc->enc_lock);
|
||||
cur_mode = &dpu_enc->base.crtc->state->adjusted_mode;
|
||||
priv = drm_enc->dev->dev_private;
|
||||
|
||||
trace_dpu_enc_enable(DRMID(drm_enc), cur_mode->hdisplay,
|
||||
cur_mode->vdisplay);
|
||||
@@ -1176,6 +1181,15 @@ static void dpu_encoder_virt_enable(struct drm_encoder *drm_enc)
|
||||
|
||||
_dpu_encoder_virt_enable_helper(drm_enc);
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
|
||||
ret = msm_dp_display_enable(priv->dp,
|
||||
drm_enc);
|
||||
if (ret) {
|
||||
DPU_ERROR_ENC(dpu_enc, "dp display enable failed: %d\n",
|
||||
ret);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
dpu_enc->enabled = true;
|
||||
|
||||
out:
|
||||
@@ -1234,6 +1248,11 @@ static void dpu_encoder_virt_disable(struct drm_encoder *drm_enc)
|
||||
|
||||
DPU_DEBUG_ENC(dpu_enc, "encoder disabled\n");
|
||||
|
||||
if (drm_enc->encoder_type == DRM_MODE_ENCODER_TMDS && priv->dp) {
|
||||
if (msm_dp_display_disable(priv->dp, drm_enc))
|
||||
DPU_ERROR_ENC(dpu_enc, "dp display disable failed\n");
|
||||
}
|
||||
|
||||
mutex_unlock(&dpu_enc->enc_lock);
|
||||
}
|
||||
|
||||
|
||||
@@ -100,6 +100,14 @@ static void drm_mode_to_intf_timing_params(
|
||||
* display_v_end -= mode->hsync_start - mode->hdisplay;
|
||||
* }
|
||||
*/
|
||||
/* for DP/EDP, Shift timings to align it to bottom right */
|
||||
if ((phys_enc->hw_intf->cap->type == INTF_DP) ||
|
||||
(phys_enc->hw_intf->cap->type == INTF_EDP)) {
|
||||
timing->h_back_porch += timing->h_front_porch;
|
||||
timing->h_front_porch = 0;
|
||||
timing->v_back_porch += timing->v_front_porch;
|
||||
timing->v_front_porch = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u32 get_horizontal_total(const struct intf_timing_params *timing)
|
||||
|
||||
535
drivers/gpu/drm/msm/dp/dp_aux.c
Normal file
535
drivers/gpu/drm/msm/dp/dp_aux.c
Normal file
File diff suppressed because it is too large
Load Diff
30
drivers/gpu/drm/msm/dp/dp_aux.h
Normal file
30
drivers/gpu/drm/msm/dp/dp_aux.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_AUX_H_
|
||||
#define _DP_AUX_H_
|
||||
|
||||
#include "dp_catalog.h"
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
#define DP_AUX_ERR_NONE 0
|
||||
#define DP_AUX_ERR_ADDR -1
|
||||
#define DP_AUX_ERR_TOUT -2
|
||||
#define DP_AUX_ERR_NACK -3
|
||||
#define DP_AUX_ERR_DEFER -4
|
||||
#define DP_AUX_ERR_NACK_DEFER -5
|
||||
#define DP_AUX_ERR_PHY -6
|
||||
|
||||
int dp_aux_register(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_unregister(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_isr(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_init(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_deinit(struct drm_dp_aux *dp_aux);
|
||||
void dp_aux_reconfig(struct drm_dp_aux *dp_aux);
|
||||
|
||||
struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog);
|
||||
void dp_aux_put(struct drm_dp_aux *aux);
|
||||
|
||||
#endif /*__DP_AUX_H_*/
|
||||
1019
drivers/gpu/drm/msm/dp/dp_catalog.c
Normal file
1019
drivers/gpu/drm/msm/dp/dp_catalog.c
Normal file
File diff suppressed because it is too large
Load Diff
102
drivers/gpu/drm/msm/dp/dp_catalog.h
Normal file
102
drivers/gpu/drm/msm/dp/dp_catalog.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_CATALOG_H_
|
||||
#define _DP_CATALOG_H_
|
||||
|
||||
#include "dp_parser.h"
|
||||
|
||||
/* interrupts */
|
||||
#define DP_INTR_HPD BIT(0)
|
||||
#define DP_INTR_AUX_I2C_DONE BIT(3)
|
||||
#define DP_INTR_WRONG_ADDR BIT(6)
|
||||
#define DP_INTR_TIMEOUT BIT(9)
|
||||
#define DP_INTR_NACK_DEFER BIT(12)
|
||||
#define DP_INTR_WRONG_DATA_CNT BIT(15)
|
||||
#define DP_INTR_I2C_NACK BIT(18)
|
||||
#define DP_INTR_I2C_DEFER BIT(21)
|
||||
#define DP_INTR_PLL_UNLOCKED BIT(24)
|
||||
#define DP_INTR_AUX_ERROR BIT(27)
|
||||
|
||||
#define DP_INTR_READY_FOR_VIDEO BIT(0)
|
||||
#define DP_INTR_IDLE_PATTERN_SENT BIT(3)
|
||||
#define DP_INTR_FRAME_END BIT(6)
|
||||
#define DP_INTR_CRC_UPDATED BIT(9)
|
||||
|
||||
#define DP_AUX_CFG_MAX_VALUE_CNT 3
|
||||
|
||||
/* PHY AUX config registers */
|
||||
enum dp_phy_aux_config_type {
|
||||
PHY_AUX_CFG0,
|
||||
PHY_AUX_CFG1,
|
||||
PHY_AUX_CFG2,
|
||||
PHY_AUX_CFG3,
|
||||
PHY_AUX_CFG4,
|
||||
PHY_AUX_CFG5,
|
||||
PHY_AUX_CFG6,
|
||||
PHY_AUX_CFG7,
|
||||
PHY_AUX_CFG8,
|
||||
PHY_AUX_CFG9,
|
||||
PHY_AUX_CFG_MAX,
|
||||
};
|
||||
|
||||
struct dp_catalog {
|
||||
u32 aux_data;
|
||||
u32 total;
|
||||
u32 sync_start;
|
||||
u32 width_blanking;
|
||||
u32 dp_active;
|
||||
};
|
||||
|
||||
/* AUX APIs */
|
||||
u32 dp_catalog_aux_read_data(struct dp_catalog *dp_catalog);
|
||||
int dp_catalog_aux_write_data(struct dp_catalog *dp_catalog);
|
||||
int dp_catalog_aux_write_trans(struct dp_catalog *dp_catalog);
|
||||
int dp_catalog_aux_clear_trans(struct dp_catalog *dp_catalog, bool read);
|
||||
int dp_catalog_aux_clear_hw_interrupts(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_aux_reset(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable);
|
||||
void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog,
|
||||
enum dp_phy_aux_config_type type);
|
||||
void dp_catalog_aux_setup(struct dp_catalog *dp_catalog);
|
||||
int dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog);
|
||||
|
||||
/* DP Controller APIs */
|
||||
void dp_catalog_ctrl_state_ctrl(struct dp_catalog *dp_catalog, u32 state);
|
||||
void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 config);
|
||||
void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog, bool enable);
|
||||
void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb);
|
||||
void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate,
|
||||
u32 stream_rate_khz, bool fixed_nvid);
|
||||
int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog, u32 pattern);
|
||||
void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_usb_reset(struct dp_catalog *dp_catalog, bool flip);
|
||||
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog, bool enable);
|
||||
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog, bool enable);
|
||||
void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog *dp_catalog, bool flipped,
|
||||
u8 lane_cnt);
|
||||
int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level,
|
||||
u8 p_level);
|
||||
int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog,
|
||||
u32 dp_tu, u32 valid_boundary,
|
||||
u32 valid_boundary2);
|
||||
void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
|
||||
u32 pattern);
|
||||
u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog);
|
||||
|
||||
/* DP Panel APIs */
|
||||
int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_dump_regs(struct dp_catalog *dp_catalog);
|
||||
void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
|
||||
struct drm_display_mode *drm_mode);
|
||||
void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog);
|
||||
|
||||
struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io);
|
||||
|
||||
#endif /* _DP_CATALOG_H_ */
|
||||
1694
drivers/gpu/drm/msm/dp/dp_ctrl.c
Normal file
1694
drivers/gpu/drm/msm/dp/dp_ctrl.c
Normal file
File diff suppressed because it is too large
Load Diff
35
drivers/gpu/drm/msm/dp/dp_ctrl.h
Normal file
35
drivers/gpu/drm/msm/dp/dp_ctrl.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_CTRL_H_
|
||||
#define _DP_CTRL_H_
|
||||
|
||||
#include "dp_aux.h"
|
||||
#include "dp_panel.h"
|
||||
#include "dp_link.h"
|
||||
#include "dp_parser.h"
|
||||
#include "dp_power.h"
|
||||
#include "dp_catalog.h"
|
||||
|
||||
struct dp_ctrl {
|
||||
bool orientation;
|
||||
atomic_t aborted;
|
||||
u32 pixel_rate;
|
||||
};
|
||||
|
||||
int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip);
|
||||
void dp_ctrl_host_deinit(struct dp_ctrl *dp_ctrl);
|
||||
int dp_ctrl_on(struct dp_ctrl *dp_ctrl);
|
||||
int dp_ctrl_off(struct dp_ctrl *dp_ctrl);
|
||||
void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl);
|
||||
void dp_ctrl_isr(struct dp_ctrl *dp_ctrl);
|
||||
void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl);
|
||||
struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link,
|
||||
struct dp_panel *panel, struct drm_dp_aux *aux,
|
||||
struct dp_power *power, struct dp_catalog *catalog,
|
||||
struct dp_parser *parser);
|
||||
void dp_ctrl_put(struct dp_ctrl *dp_ctrl);
|
||||
|
||||
#endif /* _DP_CTRL_H_ */
|
||||
936
drivers/gpu/drm/msm/dp/dp_display.c
Normal file
936
drivers/gpu/drm/msm/dp/dp_display.c
Normal file
File diff suppressed because it is too large
Load Diff
28
drivers/gpu/drm/msm/dp/dp_display.h
Normal file
28
drivers/gpu/drm/msm/dp/dp_display.h
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_DISPLAY_H_
|
||||
#define _DP_DISPLAY_H_
|
||||
|
||||
#include "dp_panel.h"
|
||||
|
||||
struct msm_dp {
|
||||
struct drm_device *drm_dev;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
bool is_connected;
|
||||
struct mutex connect_mutex;
|
||||
u32 max_pclk_khz;
|
||||
u32 max_dp_lanes;
|
||||
};
|
||||
|
||||
int dp_display_validate_mode(struct msm_dp *dp_display, u32 mode_pclk_khz);
|
||||
int dp_display_get_modes(struct msm_dp *dp_display,
|
||||
struct dp_display_mode *dp_mode);
|
||||
int dp_display_request_irq(struct msm_dp *dp_display);
|
||||
bool dp_display_check_video_test(struct msm_dp *dp_display);
|
||||
int dp_display_get_test_bpp(struct msm_dp *dp_display);
|
||||
|
||||
#endif /* _DP_DISPLAY_H_ */
|
||||
168
drivers/gpu/drm/msm/dp/dp_drm.c
Normal file
168
drivers/gpu/drm/msm/dp/dp_drm.c
Normal file
@@ -0,0 +1,168 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <drm/drm_atomic_helper.h>
|
||||
#include <drm/drm_atomic.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_kms.h"
|
||||
#include "dp_drm.h"
|
||||
|
||||
struct dp_connector {
|
||||
struct drm_connector base;
|
||||
struct msm_dp *dp_display;
|
||||
};
|
||||
#define to_dp_connector(x) container_of(x, struct dp_connector, base)
|
||||
|
||||
/**
|
||||
* dp_connector_detect - callback to determine if connector is connected
|
||||
* @conn: Pointer to drm connector structure
|
||||
* @force: Force detect setting from drm framework
|
||||
* Returns: Connector 'is connected' status
|
||||
*/
|
||||
static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
|
||||
bool force)
|
||||
{
|
||||
struct msm_dp *dp;
|
||||
|
||||
dp = to_dp_connector(conn)->dp_display;
|
||||
|
||||
DRM_DEBUG_DP("is_connected = %s\n",
|
||||
(dp->is_connected) ? "true" : "false");
|
||||
|
||||
return (dp->is_connected) ? connector_status_connected :
|
||||
connector_status_disconnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
|
||||
* @connector: Pointer to drm connector structure
|
||||
* Returns: Number of modes added
|
||||
*/
|
||||
static int dp_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
int rc = 0;
|
||||
struct msm_dp *dp;
|
||||
struct dp_display_mode *dp_mode = NULL;
|
||||
struct drm_display_mode *m, drm_mode;
|
||||
|
||||
if (!connector)
|
||||
return 0;
|
||||
|
||||
dp = to_dp_connector(connector)->dp_display;
|
||||
|
||||
dp_mode = kzalloc(sizeof(*dp_mode), GFP_KERNEL);
|
||||
if (!dp_mode)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&dp->connect_mutex);
|
||||
/* pluggable case assumes EDID is read when HPD */
|
||||
if (dp->is_connected) {
|
||||
/*
|
||||
*The get_modes() function might return one mode that is stored
|
||||
* in dp_mode when compliance test is in progress. If not, the
|
||||
* return value is equal to the total number of modes supported
|
||||
* by the sink
|
||||
*/
|
||||
rc = dp_display_get_modes(dp, dp_mode);
|
||||
if (rc <= 0) {
|
||||
DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc);
|
||||
kfree(dp_mode);
|
||||
mutex_unlock(&dp->connect_mutex);
|
||||
return rc;
|
||||
}
|
||||
if (dp_mode->drm_mode.clock) { /* valid DP mode */
|
||||
memset(&drm_mode, 0x0, sizeof(drm_mode));
|
||||
drm_mode_copy(&drm_mode, &dp_mode->drm_mode);
|
||||
m = drm_mode_duplicate(connector->dev, &drm_mode);
|
||||
if (!m) {
|
||||
DRM_ERROR("failed to add mode %ux%u\n",
|
||||
drm_mode.hdisplay,
|
||||
drm_mode.vdisplay);
|
||||
kfree(dp_mode);
|
||||
mutex_unlock(&dp->connect_mutex);
|
||||
return 0;
|
||||
}
|
||||
drm_mode_probed_add(connector, m);
|
||||
}
|
||||
} else {
|
||||
DRM_DEBUG_DP("No sink connected\n");
|
||||
}
|
||||
mutex_unlock(&dp->connect_mutex);
|
||||
kfree(dp_mode);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_connector_mode_valid - callback to determine if specified mode is valid
|
||||
* @connector: Pointer to drm connector structure
|
||||
* @mode: Pointer to drm mode structure
|
||||
* Returns: Validity status for specified mode
|
||||
*/
|
||||
static enum drm_mode_status dp_connector_mode_valid(
|
||||
struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct msm_dp *dp_disp;
|
||||
|
||||
dp_disp = to_dp_connector(connector)->dp_display;
|
||||
|
||||
if ((dp_disp->max_pclk_khz <= 0) ||
|
||||
(dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) ||
|
||||
(mode->clock > dp_disp->max_pclk_khz))
|
||||
return MODE_BAD;
|
||||
|
||||
return dp_display_validate_mode(dp_disp, mode->clock);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs dp_connector_funcs = {
|
||||
.detect = dp_connector_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = drm_connector_cleanup,
|
||||
.reset = drm_atomic_helper_connector_reset,
|
||||
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
|
||||
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs dp_connector_helper_funcs = {
|
||||
.get_modes = dp_connector_get_modes,
|
||||
.mode_valid = dp_connector_mode_valid,
|
||||
};
|
||||
|
||||
/* connector initialization */
|
||||
struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
|
||||
{
|
||||
struct drm_connector *connector = NULL;
|
||||
struct dp_connector *dp_connector;
|
||||
int ret;
|
||||
|
||||
dp_connector = devm_kzalloc(dp_display->drm_dev->dev,
|
||||
sizeof(*dp_connector),
|
||||
GFP_KERNEL);
|
||||
if (!dp_connector)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dp_connector->dp_display = dp_display;
|
||||
|
||||
connector = &dp_connector->base;
|
||||
|
||||
ret = drm_connector_init(dp_display->drm_dev, connector,
|
||||
&dp_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
drm_connector_helper_add(connector, &dp_connector_helper_funcs);
|
||||
|
||||
/*
|
||||
* Enable HPD to let hpd event is handled when cable is connected.
|
||||
*/
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
drm_connector_attach_encoder(connector, dp_display->encoder);
|
||||
|
||||
return connector;
|
||||
}
|
||||
18
drivers/gpu/drm/msm/dp/dp_drm.h
Normal file
18
drivers/gpu/drm/msm/dp/dp_drm.h
Normal file
@@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_DRM_H_
|
||||
#define _DP_DRM_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "dp_display.h"
|
||||
|
||||
struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display);
|
||||
|
||||
#endif /* _DP_DRM_H_ */
|
||||
69
drivers/gpu/drm/msm/dp/dp_hpd.c
Normal file
69
drivers/gpu/drm/msm/dp/dp_hpd.c
Normal file
@@ -0,0 +1,69 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
#include "dp_hpd.h"
|
||||
|
||||
/* DP specific VDM commands */
|
||||
#define DP_USBPD_VDM_STATUS 0x10
|
||||
#define DP_USBPD_VDM_CONFIGURE 0x11
|
||||
|
||||
/* USBPD-TypeC specific Macros */
|
||||
#define VDM_VERSION 0x0
|
||||
#define USB_C_DP_SID 0xFF01
|
||||
|
||||
struct dp_hpd_private {
|
||||
struct device *dev;
|
||||
struct dp_usbpd_cb *dp_cb;
|
||||
struct dp_usbpd dp_usbpd;
|
||||
};
|
||||
|
||||
static int dp_hpd_connect(struct dp_usbpd *dp_usbpd, bool hpd)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_hpd_private *hpd_priv;
|
||||
|
||||
hpd_priv = container_of(dp_usbpd, struct dp_hpd_private,
|
||||
dp_usbpd);
|
||||
|
||||
dp_usbpd->hpd_high = hpd;
|
||||
|
||||
if (!hpd_priv->dp_cb && !hpd_priv->dp_cb->configure
|
||||
&& !hpd_priv->dp_cb->disconnect) {
|
||||
pr_err("hpd dp_cb not initialized\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (hpd)
|
||||
hpd_priv->dp_cb->configure(hpd_priv->dev);
|
||||
else
|
||||
hpd_priv->dp_cb->disconnect(hpd_priv->dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct dp_usbpd *dp_hpd_get(struct device *dev, struct dp_usbpd_cb *cb)
|
||||
{
|
||||
struct dp_hpd_private *dp_hpd;
|
||||
|
||||
if (!cb) {
|
||||
pr_err("invalid cb data\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
dp_hpd = devm_kzalloc(dev, sizeof(*dp_hpd), GFP_KERNEL);
|
||||
if (!dp_hpd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
dp_hpd->dev = dev;
|
||||
dp_hpd->dp_cb = cb;
|
||||
|
||||
dp_hpd->dp_usbpd.connect = dp_hpd_connect;
|
||||
|
||||
return &dp_hpd->dp_usbpd;
|
||||
}
|
||||
79
drivers/gpu/drm/msm/dp/dp_hpd.h
Normal file
79
drivers/gpu/drm/msm/dp/dp_hpd.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_HPD_H_
|
||||
#define _DP_HPD_H_
|
||||
|
||||
//#include <linux/usb/usbpd.h>
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
enum plug_orientation {
|
||||
ORIENTATION_NONE,
|
||||
ORIENTATION_CC1,
|
||||
ORIENTATION_CC2,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_usbpd - DisplayPort status
|
||||
*
|
||||
* @orientation: plug orientation configuration
|
||||
* @low_pow_st: low power state
|
||||
* @adaptor_dp_en: adaptor functionality enabled
|
||||
* @multi_func: multi-function preferred
|
||||
* @usb_config_req: request to switch to usb
|
||||
* @exit_dp_mode: request exit from displayport mode
|
||||
* @hpd_high: Hot Plug Detect signal is high.
|
||||
* @hpd_irq: Change in the status since last message
|
||||
* @alt_mode_cfg_done: bool to specify alt mode status
|
||||
* @debug_en: bool to specify debug mode
|
||||
* @connect: simulate disconnect or connect for debug mode
|
||||
*/
|
||||
struct dp_usbpd {
|
||||
enum plug_orientation orientation;
|
||||
bool low_pow_st;
|
||||
bool adaptor_dp_en;
|
||||
bool multi_func;
|
||||
bool usb_config_req;
|
||||
bool exit_dp_mode;
|
||||
bool hpd_high;
|
||||
bool hpd_irq;
|
||||
bool alt_mode_cfg_done;
|
||||
bool debug_en;
|
||||
|
||||
int (*connect)(struct dp_usbpd *dp_usbpd, bool hpd);
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_usbpd_cb - callback functions provided by the client
|
||||
*
|
||||
* @configure: called by usbpd module when PD communication has
|
||||
* been completed and the usb peripheral has been configured on
|
||||
* dp mode.
|
||||
* @disconnect: notify the cable disconnect issued by usb.
|
||||
* @attention: notify any attention message issued by usb.
|
||||
*/
|
||||
struct dp_usbpd_cb {
|
||||
int (*configure)(struct device *dev);
|
||||
int (*disconnect)(struct device *dev);
|
||||
int (*attention)(struct device *dev);
|
||||
};
|
||||
|
||||
/**
|
||||
* dp_hpd_get() - setup hpd module
|
||||
*
|
||||
* @dev: device instance of the caller
|
||||
* @cb: struct containing callback function pointers.
|
||||
*
|
||||
* This function allows the client to initialize the usbpd
|
||||
* module. The module will communicate with HPD module.
|
||||
*/
|
||||
struct dp_usbpd *dp_hpd_get(struct device *dev, struct dp_usbpd_cb *cb);
|
||||
|
||||
int dp_hpd_register(struct dp_usbpd *dp_usbpd);
|
||||
void dp_hpd_unregister(struct dp_usbpd *dp_usbpd);
|
||||
|
||||
#endif /* _DP_HPD_H_ */
|
||||
1214
drivers/gpu/drm/msm/dp/dp_link.c
Normal file
1214
drivers/gpu/drm/msm/dp/dp_link.c
Normal file
File diff suppressed because it is too large
Load Diff
132
drivers/gpu/drm/msm/dp/dp_link.h
Normal file
132
drivers/gpu/drm/msm/dp/dp_link.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_LINK_H_
|
||||
#define _DP_LINK_H_
|
||||
|
||||
#include "dp_aux.h"
|
||||
|
||||
#define DS_PORT_STATUS_CHANGED 0x200
|
||||
#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF
|
||||
#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
|
||||
|
||||
struct dp_link_info {
|
||||
unsigned char revision;
|
||||
unsigned int rate;
|
||||
unsigned int num_lanes;
|
||||
unsigned long capabilities;
|
||||
};
|
||||
|
||||
enum dp_link_voltage_level {
|
||||
DP_TRAIN_VOLTAGE_SWING_LVL_0 = 0,
|
||||
DP_TRAIN_VOLTAGE_SWING_LVL_1 = 1,
|
||||
DP_TRAIN_VOLTAGE_SWING_LVL_2 = 2,
|
||||
DP_TRAIN_VOLTAGE_SWING_MAX = DP_TRAIN_VOLTAGE_SWING_LVL_2,
|
||||
};
|
||||
|
||||
enum dp_link_preemaphasis_level {
|
||||
DP_TRAIN_PRE_EMPHASIS_LVL_0 = 0,
|
||||
DP_TRAIN_PRE_EMPHASIS_LVL_1 = 1,
|
||||
DP_TRAIN_PRE_EMPHASIS_LVL_2 = 2,
|
||||
DP_TRAIN_PRE_EMPHASIS_MAX = DP_TRAIN_PRE_EMPHASIS_LVL_2,
|
||||
};
|
||||
|
||||
struct dp_link_test_video {
|
||||
u32 test_video_pattern;
|
||||
u32 test_bit_depth;
|
||||
u32 test_dyn_range;
|
||||
u32 test_h_total;
|
||||
u32 test_v_total;
|
||||
u32 test_h_start;
|
||||
u32 test_v_start;
|
||||
u32 test_hsync_pol;
|
||||
u32 test_hsync_width;
|
||||
u32 test_vsync_pol;
|
||||
u32 test_vsync_width;
|
||||
u32 test_h_width;
|
||||
u32 test_v_height;
|
||||
u32 test_rr_d;
|
||||
u32 test_rr_n;
|
||||
};
|
||||
|
||||
struct dp_link_test_audio {
|
||||
u32 test_audio_sampling_rate;
|
||||
u32 test_audio_channel_count;
|
||||
u32 test_audio_pattern_type;
|
||||
u32 test_audio_period_ch_1;
|
||||
u32 test_audio_period_ch_2;
|
||||
u32 test_audio_period_ch_3;
|
||||
u32 test_audio_period_ch_4;
|
||||
u32 test_audio_period_ch_5;
|
||||
u32 test_audio_period_ch_6;
|
||||
u32 test_audio_period_ch_7;
|
||||
u32 test_audio_period_ch_8;
|
||||
};
|
||||
|
||||
struct dp_link_phy_params {
|
||||
u32 phy_test_pattern_sel;
|
||||
u8 v_level;
|
||||
u8 p_level;
|
||||
};
|
||||
|
||||
struct dp_link {
|
||||
u32 sink_request;
|
||||
u32 test_response;
|
||||
bool psm_enabled;
|
||||
|
||||
u8 sink_count;
|
||||
struct dp_link_test_video test_video;
|
||||
struct dp_link_test_audio test_audio;
|
||||
struct dp_link_phy_params phy_params;
|
||||
struct dp_link_info link_params;
|
||||
};
|
||||
|
||||
/**
|
||||
* mdss_dp_test_bit_depth_to_bpp() - convert test bit depth to bpp
|
||||
* @tbd: test bit depth
|
||||
*
|
||||
* Returns the bits per pixel (bpp) to be used corresponding to the
|
||||
* git bit depth value. This function assumes that bit depth has
|
||||
* already been validated.
|
||||
*/
|
||||
static inline u32 dp_link_bit_depth_to_bpp(u32 tbd)
|
||||
{
|
||||
/*
|
||||
* Few simplistic rules and assumptions made here:
|
||||
* 1. Bit depth is per color component
|
||||
* 2. If bit depth is unknown return 0
|
||||
* 3. Assume 3 color components
|
||||
*/
|
||||
switch (tbd) {
|
||||
case DP_TEST_BIT_DEPTH_6:
|
||||
return 18;
|
||||
case DP_TEST_BIT_DEPTH_8:
|
||||
return 24;
|
||||
case DP_TEST_BIT_DEPTH_10:
|
||||
return 30;
|
||||
case DP_TEST_BIT_DEPTH_UNKNOWN:
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp);
|
||||
int dp_link_process_request(struct dp_link *dp_link);
|
||||
int dp_link_get_colorimetry_config(struct dp_link *dp_link);
|
||||
int dp_link_adjust_levels(struct dp_link *dp_link, u8 *link_status);
|
||||
bool dp_link_send_test_response(struct dp_link *dp_link);
|
||||
int dp_link_psm_config(struct dp_link *dp_link,
|
||||
struct dp_link_info *link_info, bool enable);
|
||||
bool dp_link_send_edid_checksum(struct dp_link *dp_link, u8 checksum);
|
||||
|
||||
/**
|
||||
* dp_link_get() - get the functionalities of dp test module
|
||||
*
|
||||
*
|
||||
* return: a pointer to dp_link struct
|
||||
*/
|
||||
struct dp_link *dp_link_get(struct device *dev, struct drm_dp_aux *aux);
|
||||
|
||||
#endif /* _DP_LINK_H_ */
|
||||
486
drivers/gpu/drm/msm/dp/dp_panel.c
Normal file
486
drivers/gpu/drm/msm/dp/dp_panel.c
Normal file
@@ -0,0 +1,486 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include "dp_panel.h"
|
||||
|
||||
#include <drm/drm_connector.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#define DP_MAX_DS_PORT_COUNT 1
|
||||
|
||||
struct dp_panel_private {
|
||||
struct device *dev;
|
||||
struct dp_panel dp_panel;
|
||||
struct drm_dp_aux *aux;
|
||||
struct dp_link *link;
|
||||
struct dp_catalog *catalog;
|
||||
bool panel_on;
|
||||
bool aux_cfg_update_done;
|
||||
};
|
||||
|
||||
static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
|
||||
{
|
||||
int rc = 0;
|
||||
size_t rlen;
|
||||
struct dp_panel_private *panel;
|
||||
struct dp_link_info *link_info;
|
||||
u8 *dpcd, major = 0, minor = 0, temp;
|
||||
u32 dfp_count = 0, offset = DP_DPCD_REV;
|
||||
|
||||
dpcd = dp_panel->dpcd;
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
link_info = &dp_panel->link_info;
|
||||
|
||||
rlen = drm_dp_dpcd_read(panel->aux,
|
||||
DP_TRAINING_AUX_RD_INTERVAL, &temp, 1);
|
||||
if (rlen < 0) {
|
||||
DRM_ERROR("err reading DP_TRAINING_AUX_RD_INTERVAL,rlen=%zd\n",
|
||||
rlen);
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* check for EXTENDED_RECEIVER_CAPABILITY_FIELD_PRESENT */
|
||||
if (temp & BIT(7)) {
|
||||
DRM_DEBUG_DP("using EXTENDED_RECEIVER_CAPABILITY_FIELD\n");
|
||||
offset = DPRX_EXTENDED_DPCD_FIELD;
|
||||
}
|
||||
|
||||
rlen = drm_dp_dpcd_read(panel->aux, offset,
|
||||
dpcd, (DP_RECEIVER_CAP_SIZE + 1));
|
||||
if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
|
||||
DRM_ERROR("dpcd read failed, rlen=%zd\n", rlen);
|
||||
if (rlen == -ETIMEDOUT)
|
||||
rc = rlen;
|
||||
else
|
||||
rc = -EINVAL;
|
||||
|
||||
goto end;
|
||||
}
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "[drm-dp] SINK DPCD: ",
|
||||
DUMP_PREFIX_NONE, 8, 1, dp_panel->dpcd, rlen, false);
|
||||
|
||||
link_info->revision = dpcd[DP_DPCD_REV];
|
||||
major = (link_info->revision >> 4) & 0x0f;
|
||||
minor = link_info->revision & 0x0f;
|
||||
|
||||
link_info->rate = drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]);
|
||||
link_info->num_lanes = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
|
||||
|
||||
if (link_info->num_lanes > dp_panel->max_dp_lanes)
|
||||
link_info->num_lanes = dp_panel->max_dp_lanes;
|
||||
|
||||
/* Limit support upto HBR2 until HBR3 support is added */
|
||||
if (link_info->rate >= (drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4)))
|
||||
link_info->rate = drm_dp_bw_code_to_link_rate(DP_LINK_BW_5_4);
|
||||
|
||||
DRM_DEBUG_DP("version: %d.%d\n", major, minor);
|
||||
DRM_DEBUG_DP("link_rate=%d\n", link_info->rate);
|
||||
DRM_DEBUG_DP("lane_count=%d\n", link_info->num_lanes);
|
||||
|
||||
if (drm_dp_enhanced_frame_cap(dpcd))
|
||||
link_info->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
|
||||
|
||||
dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] &
|
||||
DP_DOWN_STREAM_PORT_COUNT;
|
||||
|
||||
if (dfp_count > DP_MAX_DS_PORT_COUNT) {
|
||||
DRM_ERROR("DS port count %d greater that max (%d) supported\n",
|
||||
dfp_count, DP_MAX_DS_PORT_COUNT);
|
||||
return -EINVAL;
|
||||
}
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static u32 dp_panel_get_supported_bpp(struct dp_panel *dp_panel,
|
||||
u32 mode_edid_bpp, u32 mode_pclk_khz)
|
||||
{
|
||||
struct dp_link_info *link_info;
|
||||
const u32 max_supported_bpp = 30, min_supported_bpp = 18;
|
||||
u32 bpp = 0, data_rate_khz = 0;
|
||||
|
||||
bpp = min_t(u32, mode_edid_bpp, max_supported_bpp);
|
||||
|
||||
link_info = &dp_panel->link_info;
|
||||
data_rate_khz = link_info->num_lanes * link_info->rate * 8;
|
||||
|
||||
while (bpp > min_supported_bpp) {
|
||||
if (mode_pclk_khz * bpp <= data_rate_khz)
|
||||
break;
|
||||
bpp -= 6;
|
||||
}
|
||||
|
||||
return bpp;
|
||||
}
|
||||
|
||||
static void dp_panel_set_test_mode(struct dp_panel_private *panel,
|
||||
struct dp_display_mode *mode)
|
||||
{
|
||||
struct drm_display_mode *drm_mode = NULL;
|
||||
struct dp_link_test_video *test_info = NULL;
|
||||
|
||||
drm_mode = &mode->drm_mode;
|
||||
test_info = &panel->link->test_video;
|
||||
|
||||
drm_mode->hdisplay = test_info->test_h_width;
|
||||
drm_mode->hsync_start = drm_mode->hdisplay + test_info->test_h_total -
|
||||
(test_info->test_h_start + test_info->test_h_width);
|
||||
drm_mode->hsync_end = drm_mode->hsync_start +
|
||||
test_info->test_hsync_width;
|
||||
drm_mode->htotal = drm_mode->hsync_end + test_info->test_h_start -
|
||||
test_info->test_hsync_width;
|
||||
|
||||
drm_mode->vdisplay = test_info->test_v_height;
|
||||
drm_mode->vsync_start = drm_mode->vdisplay + test_info->test_v_total -
|
||||
(test_info->test_v_start + test_info->test_v_height);
|
||||
drm_mode->vsync_end = drm_mode->vsync_start +
|
||||
test_info->test_vsync_width;
|
||||
drm_mode->vtotal = drm_mode->vsync_end + test_info->test_v_start -
|
||||
test_info->test_vsync_width;
|
||||
|
||||
drm_mode->clock = test_info->test_h_total *
|
||||
test_info->test_v_total * test_info->test_rr_n;
|
||||
|
||||
drm_mode->type = 0x48;
|
||||
drm_mode_set_name(drm_mode);
|
||||
|
||||
if (test_info->test_rr_d == 0)
|
||||
drm_mode->clock /= 1000;
|
||||
else
|
||||
drm_mode->clock /= 1001;
|
||||
|
||||
if (test_info->test_h_width == 640)
|
||||
drm_mode->clock = 25170;
|
||||
}
|
||||
|
||||
static int dp_panel_update_modes(struct drm_connector *connector,
|
||||
struct edid *edid)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (edid) {
|
||||
rc = drm_connector_update_edid_property(connector, edid);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to update edid property %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
rc = drm_add_edid_modes(connector, edid);
|
||||
DRM_DEBUG_DP("%s -", __func__);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = drm_connector_update_edid_property(connector, NULL);
|
||||
if (rc)
|
||||
DRM_ERROR("failed to update edid property %d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
int rc = 0, bw_code;
|
||||
struct dp_panel_private *panel;
|
||||
|
||||
if (!dp_panel || !connector) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
|
||||
rc = dp_panel_read_dpcd(dp_panel);
|
||||
bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate);
|
||||
if (rc || !is_link_rate_valid(bw_code) ||
|
||||
!is_lane_count_valid(dp_panel->link_info.num_lanes) ||
|
||||
(bw_code > dp_panel->max_bw_code)) {
|
||||
DRM_ERROR("read dpcd failed %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
rc = drm_dp_read_desc(panel->aux, &dp_panel->desc,
|
||||
drm_dp_is_branch(dp_panel->dpcd));
|
||||
if (rc) {
|
||||
DRM_ERROR("read sink/branch descriptor failed %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
kfree(dp_panel->edid);
|
||||
dp_panel->edid = NULL;
|
||||
|
||||
dp_panel->edid = drm_get_edid(connector,
|
||||
&panel->aux->ddc);
|
||||
if (!dp_panel->edid) {
|
||||
DRM_ERROR("panel edid read failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (panel->aux_cfg_update_done) {
|
||||
DRM_DEBUG_DP("read DPCD with updated AUX config\n");
|
||||
rc = dp_panel_read_dpcd(dp_panel);
|
||||
bw_code = drm_dp_link_rate_to_bw_code(dp_panel->link_info.rate);
|
||||
if (rc || !is_link_rate_valid(bw_code) ||
|
||||
!is_lane_count_valid(dp_panel->link_info.num_lanes)
|
||||
|| (bw_code > dp_panel->max_bw_code)) {
|
||||
DRM_ERROR("read dpcd failed %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
panel->aux_cfg_update_done = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel,
|
||||
u32 mode_edid_bpp, u32 mode_pclk_khz)
|
||||
{
|
||||
struct dp_panel_private *panel;
|
||||
u32 bpp = mode_edid_bpp;
|
||||
|
||||
if (!dp_panel || !mode_edid_bpp || !mode_pclk_khz) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
|
||||
if (dp_panel->video_test)
|
||||
bpp = dp_link_bit_depth_to_bpp(
|
||||
panel->link->test_video.test_bit_depth);
|
||||
else
|
||||
bpp = dp_panel_get_supported_bpp(dp_panel, mode_edid_bpp,
|
||||
mode_pclk_khz);
|
||||
|
||||
return bpp;
|
||||
}
|
||||
|
||||
int dp_panel_get_modes(struct dp_panel *dp_panel,
|
||||
struct drm_connector *connector, struct dp_display_mode *mode)
|
||||
{
|
||||
struct dp_panel_private *panel;
|
||||
|
||||
if (!dp_panel) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
|
||||
if (dp_panel->video_test) {
|
||||
dp_panel_set_test_mode(panel, mode);
|
||||
return 1;
|
||||
} else if (dp_panel->edid) {
|
||||
return dp_panel_update_modes(connector, dp_panel->edid);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 dp_panel_get_edid_checksum(struct edid *edid)
|
||||
{
|
||||
struct edid *last_block;
|
||||
u8 *raw_edid;
|
||||
bool is_edid_corrupt;
|
||||
|
||||
if (!edid) {
|
||||
DRM_ERROR("invalid edid input\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
raw_edid = (u8 *)edid;
|
||||
raw_edid += (edid->extensions * EDID_LENGTH);
|
||||
last_block = (struct edid *)raw_edid;
|
||||
|
||||
/* block type extension */
|
||||
drm_edid_block_valid(raw_edid, 1, false, &is_edid_corrupt);
|
||||
if (!is_edid_corrupt)
|
||||
return last_block->checksum;
|
||||
|
||||
DRM_ERROR("Invalid block, no checksum\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dp_panel_handle_sink_request(struct dp_panel *dp_panel)
|
||||
{
|
||||
struct dp_panel_private *panel;
|
||||
|
||||
if (!dp_panel) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return;
|
||||
}
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
|
||||
if (panel->link->sink_request & DP_TEST_LINK_EDID_READ) {
|
||||
u8 checksum = dp_panel_get_edid_checksum(dp_panel->edid);
|
||||
|
||||
dp_link_send_edid_checksum(panel->link, checksum);
|
||||
dp_link_send_test_response(panel->link);
|
||||
}
|
||||
}
|
||||
|
||||
void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable)
|
||||
{
|
||||
struct dp_catalog *catalog;
|
||||
struct dp_panel_private *panel;
|
||||
|
||||
if (!dp_panel) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return;
|
||||
}
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
catalog = panel->catalog;
|
||||
|
||||
if (!panel->panel_on) {
|
||||
DRM_DEBUG_DP("DP panel not enabled, handle TPG on next on\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!enable) {
|
||||
dp_catalog_panel_tpg_disable(catalog);
|
||||
return;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("%s: calling catalog tpg_enable\n", __func__);
|
||||
dp_catalog_panel_tpg_enable(catalog, &panel->dp_panel.dp_mode.drm_mode);
|
||||
}
|
||||
|
||||
void dp_panel_dump_regs(struct dp_panel *dp_panel)
|
||||
{
|
||||
struct dp_catalog *catalog;
|
||||
struct dp_panel_private *panel;
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
catalog = panel->catalog;
|
||||
|
||||
dp_catalog_dump_regs(catalog);
|
||||
}
|
||||
|
||||
int dp_panel_timing_cfg(struct dp_panel *dp_panel)
|
||||
{
|
||||
int rc = 0;
|
||||
u32 data, total_ver, total_hor;
|
||||
struct dp_catalog *catalog;
|
||||
struct dp_panel_private *panel;
|
||||
struct drm_display_mode *drm_mode;
|
||||
|
||||
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
|
||||
catalog = panel->catalog;
|
||||
drm_mode = &panel->dp_panel.dp_mode.drm_mode;
|
||||
|
||||
DRM_DEBUG_DP("width=%d hporch= %d %d %d\n",
|
||||
drm_mode->hdisplay, drm_mode->htotal - drm_mode->hsync_end,
|
||||
drm_mode->hsync_start - drm_mode->hdisplay,
|
||||
drm_mode->hsync_end - drm_mode->hsync_start);
|
||||
|
||||
DRM_DEBUG_DP("height=%d vporch= %d %d %d\n",
|
||||
drm_mode->vdisplay, drm_mode->vtotal - drm_mode->vsync_end,
|
||||
drm_mode->vsync_start - drm_mode->vdisplay,
|
||||
drm_mode->vsync_end - drm_mode->vsync_start);
|
||||
|
||||
total_hor = drm_mode->htotal;
|
||||
|
||||
total_ver = drm_mode->vtotal;
|
||||
|
||||
data = total_ver;
|
||||
data <<= 16;
|
||||
data |= total_hor;
|
||||
|
||||
catalog->total = data;
|
||||
|
||||
data = (drm_mode->vtotal - drm_mode->vsync_start);
|
||||
data <<= 16;
|
||||
data |= (drm_mode->htotal - drm_mode->hsync_start);
|
||||
|
||||
catalog->sync_start = data;
|
||||
|
||||
data = drm_mode->vsync_end - drm_mode->vsync_start;
|
||||
data <<= 16;
|
||||
data |= (panel->dp_panel.dp_mode.v_active_low << 31);
|
||||
data |= drm_mode->hsync_end - drm_mode->hsync_start;
|
||||
data |= (panel->dp_panel.dp_mode.h_active_low << 15);
|
||||
|
||||
catalog->width_blanking = data;
|
||||
|
||||
data = drm_mode->vdisplay;
|
||||
data <<= 16;
|
||||
data |= drm_mode->hdisplay;
|
||||
|
||||
catalog->dp_active = data;
|
||||
|
||||
dp_catalog_panel_timing_cfg(catalog);
|
||||
panel->panel_on = true;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dp_panel_init_panel_info(struct dp_panel *dp_panel)
|
||||
{
|
||||
int rc = 0;
|
||||
struct drm_display_mode *drm_mode;
|
||||
|
||||
drm_mode = &dp_panel->dp_mode.drm_mode;
|
||||
|
||||
/*
|
||||
* print resolution info as this is a result
|
||||
* of user initiated action of cable connection
|
||||
*/
|
||||
DRM_DEBUG_DP("SET NEW RESOLUTION:\n");
|
||||
DRM_DEBUG_DP("%dx%d@%dfps\n", drm_mode->hdisplay,
|
||||
drm_mode->vdisplay, drm_mode_vrefresh(drm_mode));
|
||||
DRM_DEBUG_DP("h_porches(back|front|width) = (%d|%d|%d)\n",
|
||||
drm_mode->htotal - drm_mode->hsync_end,
|
||||
drm_mode->hsync_start - drm_mode->hdisplay,
|
||||
drm_mode->hsync_end - drm_mode->hsync_start);
|
||||
DRM_DEBUG_DP("v_porches(back|front|width) = (%d|%d|%d)\n",
|
||||
drm_mode->vtotal - drm_mode->vsync_end,
|
||||
drm_mode->vsync_start - drm_mode->vdisplay,
|
||||
drm_mode->vsync_end - drm_mode->vsync_start);
|
||||
DRM_DEBUG_DP("pixel clock (KHz)=(%d)\n", drm_mode->clock);
|
||||
DRM_DEBUG_DP("bpp = %d\n", dp_panel->dp_mode.bpp);
|
||||
|
||||
dp_panel->dp_mode.bpp = max_t(u32, 18,
|
||||
min_t(u32, dp_panel->dp_mode.bpp, 30));
|
||||
DRM_DEBUG_DP("updated bpp = %d\n", dp_panel->dp_mode.bpp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct dp_panel *dp_panel_get(struct dp_panel_in *in)
|
||||
{
|
||||
struct dp_panel_private *panel;
|
||||
struct dp_panel *dp_panel;
|
||||
|
||||
if (!in->dev || !in->catalog || !in->aux || !in->link) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
panel = devm_kzalloc(in->dev, sizeof(*panel), GFP_KERNEL);
|
||||
if (!panel)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
panel->dev = in->dev;
|
||||
panel->aux = in->aux;
|
||||
panel->catalog = in->catalog;
|
||||
panel->link = in->link;
|
||||
|
||||
dp_panel = &panel->dp_panel;
|
||||
dp_panel->max_bw_code = DP_LINK_BW_8_1;
|
||||
panel->aux_cfg_update_done = false;
|
||||
|
||||
return dp_panel;
|
||||
}
|
||||
|
||||
void dp_panel_put(struct dp_panel *dp_panel)
|
||||
{
|
||||
if (!dp_panel)
|
||||
return;
|
||||
|
||||
kfree(dp_panel->edid);
|
||||
}
|
||||
95
drivers/gpu/drm/msm/dp/dp_panel.h
Normal file
95
drivers/gpu/drm/msm/dp/dp_panel.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_PANEL_H_
|
||||
#define _DP_PANEL_H_
|
||||
|
||||
#include <drm/msm_drm.h>
|
||||
|
||||
#include "dp_aux.h"
|
||||
#include "dp_link.h"
|
||||
#include "dp_hpd.h"
|
||||
|
||||
struct edid;
|
||||
|
||||
#define DP_MAX_DOWNSTREAM_PORTS 0x10
|
||||
#define DPRX_EXTENDED_DPCD_FIELD 0x2200
|
||||
|
||||
struct dp_display_mode {
|
||||
struct drm_display_mode drm_mode;
|
||||
u32 capabilities;
|
||||
u32 bpp;
|
||||
u32 h_active_low;
|
||||
u32 v_active_low;
|
||||
};
|
||||
|
||||
struct dp_panel_in {
|
||||
struct device *dev;
|
||||
struct drm_dp_aux *aux;
|
||||
struct dp_link *link;
|
||||
struct dp_catalog *catalog;
|
||||
};
|
||||
|
||||
struct dp_panel {
|
||||
/* dpcd raw data */
|
||||
u8 dpcd[DP_RECEIVER_CAP_SIZE + 1];
|
||||
|
||||
struct dp_link_info link_info;
|
||||
struct drm_dp_desc desc;
|
||||
struct edid *edid;
|
||||
struct drm_connector *connector;
|
||||
struct dp_display_mode dp_mode;
|
||||
bool video_test;
|
||||
|
||||
u32 vic;
|
||||
u32 max_pclk_khz;
|
||||
u32 max_dp_lanes;
|
||||
|
||||
u32 max_bw_code;
|
||||
};
|
||||
|
||||
int dp_panel_init_panel_info(struct dp_panel *dp_panel);
|
||||
int dp_panel_deinit(struct dp_panel *dp_panel);
|
||||
int dp_panel_timing_cfg(struct dp_panel *dp_panel);
|
||||
void dp_panel_dump_regs(struct dp_panel *dp_panel);
|
||||
int dp_panel_read_sink_caps(struct dp_panel *dp_panel,
|
||||
struct drm_connector *connector);
|
||||
u32 dp_panel_get_mode_bpp(struct dp_panel *dp_panel, u32 mode_max_bpp,
|
||||
u32 mode_pclk_khz);
|
||||
int dp_panel_get_modes(struct dp_panel *dp_panel,
|
||||
struct drm_connector *connector, struct dp_display_mode *mode);
|
||||
void dp_panel_handle_sink_request(struct dp_panel *dp_panel);
|
||||
void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable);
|
||||
|
||||
/**
|
||||
* is_link_rate_valid() - validates the link rate
|
||||
* @lane_rate: link rate requested by the sink
|
||||
*
|
||||
* Returns true if the requested link rate is supported.
|
||||
*/
|
||||
static inline bool is_link_rate_valid(u32 bw_code)
|
||||
{
|
||||
return (bw_code == DP_LINK_BW_1_62 ||
|
||||
bw_code == DP_LINK_BW_2_7 ||
|
||||
bw_code == DP_LINK_BW_5_4 ||
|
||||
bw_code == DP_LINK_BW_8_1);
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_link_is_lane_count_valid() - validates the lane count
|
||||
* @lane_count: lane count requested by the sink
|
||||
*
|
||||
* Returns true if the requested lane count is supported.
|
||||
*/
|
||||
static inline bool is_lane_count_valid(u32 lane_count)
|
||||
{
|
||||
return (lane_count == 1 ||
|
||||
lane_count == 2 ||
|
||||
lane_count == 4);
|
||||
}
|
||||
|
||||
struct dp_panel *dp_panel_get(struct dp_panel_in *in);
|
||||
void dp_panel_put(struct dp_panel *dp_panel);
|
||||
#endif /* _DP_PANEL_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user