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:
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
@ -0,0 +1,535 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "dp_reg.h"
|
||||
#include "dp_aux.h"
|
||||
|
||||
#define DP_AUX_ENUM_STR(x) #x
|
||||
|
||||
struct dp_aux_private {
|
||||
struct device *dev;
|
||||
struct dp_catalog *catalog;
|
||||
|
||||
struct mutex mutex;
|
||||
struct completion comp;
|
||||
|
||||
u32 aux_error_num;
|
||||
u32 retry_cnt;
|
||||
bool cmd_busy;
|
||||
bool native;
|
||||
bool read;
|
||||
bool no_send_addr;
|
||||
bool no_send_stop;
|
||||
u32 offset;
|
||||
u32 segment;
|
||||
u32 isr;
|
||||
|
||||
struct drm_dp_aux dp_aux;
|
||||
};
|
||||
|
||||
static const char *dp_aux_get_error(u32 aux_error)
|
||||
{
|
||||
switch (aux_error) {
|
||||
case DP_AUX_ERR_NONE:
|
||||
return DP_AUX_ENUM_STR(DP_AUX_ERR_NONE);
|
||||
case DP_AUX_ERR_ADDR:
|
||||
return DP_AUX_ENUM_STR(DP_AUX_ERR_ADDR);
|
||||
case DP_AUX_ERR_TOUT:
|
||||
return DP_AUX_ENUM_STR(DP_AUX_ERR_TOUT);
|
||||
case DP_AUX_ERR_NACK:
|
||||
return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK);
|
||||
case DP_AUX_ERR_DEFER:
|
||||
return DP_AUX_ENUM_STR(DP_AUX_ERR_DEFER);
|
||||
case DP_AUX_ERR_NACK_DEFER:
|
||||
return DP_AUX_ENUM_STR(DP_AUX_ERR_NACK_DEFER);
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static u32 dp_aux_write(struct dp_aux_private *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
u32 data[4], reg, len;
|
||||
u8 *msgdata = msg->buffer;
|
||||
int const AUX_CMD_FIFO_LEN = 128;
|
||||
int i = 0;
|
||||
|
||||
if (aux->read)
|
||||
len = 4;
|
||||
else
|
||||
len = msg->size + 4;
|
||||
|
||||
/*
|
||||
* cmd fifo only has depth of 144 bytes
|
||||
* limit buf length to 128 bytes here
|
||||
*/
|
||||
if (len > AUX_CMD_FIFO_LEN) {
|
||||
DRM_ERROR("buf size greater than allowed size of 128 bytes\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pack cmd and write to HW */
|
||||
data[0] = (msg->address >> 16) & 0xf; /* addr[19:16] */
|
||||
if (aux->read)
|
||||
data[0] |= BIT(4); /* R/W */
|
||||
|
||||
data[1] = (msg->address >> 8) & 0xff; /* addr[15:8] */
|
||||
data[2] = msg->address & 0xff; /* addr[7:0] */
|
||||
data[3] = (msg->size - 1) & 0xff; /* len[7:0] */
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
reg = (i < 4) ? data[i] : msgdata[i - 4];
|
||||
/* index = 0, write */
|
||||
reg = (((reg) << DP_AUX_DATA_OFFSET)
|
||||
& DP_AUX_DATA_MASK) | DP_AUX_DATA_WRITE;
|
||||
if (i == 0)
|
||||
reg |= DP_AUX_DATA_INDEX_WRITE;
|
||||
aux->catalog->aux_data = reg;
|
||||
dp_catalog_aux_write_data(aux->catalog);
|
||||
}
|
||||
|
||||
dp_catalog_aux_clear_trans(aux->catalog, false);
|
||||
dp_catalog_aux_clear_hw_interrupts(aux->catalog);
|
||||
|
||||
reg = 0; /* Transaction number == 1 */
|
||||
if (!aux->native) { /* i2c */
|
||||
reg |= DP_AUX_TRANS_CTRL_I2C;
|
||||
|
||||
if (aux->no_send_addr)
|
||||
reg |= DP_AUX_TRANS_CTRL_NO_SEND_ADDR;
|
||||
|
||||
if (aux->no_send_stop)
|
||||
reg |= DP_AUX_TRANS_CTRL_NO_SEND_STOP;
|
||||
}
|
||||
|
||||
reg |= DP_AUX_TRANS_CTRL_GO;
|
||||
aux->catalog->aux_data = reg;
|
||||
dp_catalog_aux_write_trans(aux->catalog);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int dp_aux_cmd_fifo_tx(struct dp_aux_private *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
u32 ret, len, timeout;
|
||||
int aux_timeout_ms = HZ/4;
|
||||
|
||||
reinit_completion(&aux->comp);
|
||||
|
||||
len = dp_aux_write(aux, msg);
|
||||
if (len == 0) {
|
||||
DRM_ERROR("DP AUX write failed\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
timeout = wait_for_completion_timeout(&aux->comp, aux_timeout_ms);
|
||||
if (!timeout) {
|
||||
DRM_ERROR("aux %s timeout\n", (aux->read ? "read" : "write"));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
|
||||
ret = len;
|
||||
} else {
|
||||
DRM_ERROR_RATELIMITED("aux err: %s\n",
|
||||
dp_aux_get_error(aux->aux_error_num));
|
||||
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void dp_aux_cmd_fifo_rx(struct dp_aux_private *aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
u32 data;
|
||||
u8 *dp;
|
||||
u32 i, actual_i;
|
||||
u32 len = msg->size;
|
||||
|
||||
dp_catalog_aux_clear_trans(aux->catalog, true);
|
||||
|
||||
data = DP_AUX_DATA_INDEX_WRITE; /* INDEX_WRITE */
|
||||
data |= DP_AUX_DATA_READ; /* read */
|
||||
|
||||
aux->catalog->aux_data = data;
|
||||
dp_catalog_aux_write_data(aux->catalog);
|
||||
|
||||
dp = msg->buffer;
|
||||
|
||||
/* discard first byte */
|
||||
data = dp_catalog_aux_read_data(aux->catalog);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
data = dp_catalog_aux_read_data(aux->catalog);
|
||||
*dp++ = (u8)((data >> DP_AUX_DATA_OFFSET) & 0xff);
|
||||
|
||||
actual_i = (data >> DP_AUX_DATA_INDEX_OFFSET) & 0xFF;
|
||||
if (i != actual_i)
|
||||
DRM_ERROR("Index mismatch: expected %d, found %d\n",
|
||||
i, actual_i);
|
||||
}
|
||||
}
|
||||
|
||||
static void dp_aux_native_handler(struct dp_aux_private *aux)
|
||||
{
|
||||
u32 isr = aux->isr;
|
||||
|
||||
if (isr & DP_INTR_AUX_I2C_DONE)
|
||||
aux->aux_error_num = DP_AUX_ERR_NONE;
|
||||
else if (isr & DP_INTR_WRONG_ADDR)
|
||||
aux->aux_error_num = DP_AUX_ERR_ADDR;
|
||||
else if (isr & DP_INTR_TIMEOUT)
|
||||
aux->aux_error_num = DP_AUX_ERR_TOUT;
|
||||
if (isr & DP_INTR_NACK_DEFER)
|
||||
aux->aux_error_num = DP_AUX_ERR_NACK;
|
||||
if (isr & DP_INTR_AUX_ERROR) {
|
||||
aux->aux_error_num = DP_AUX_ERR_PHY;
|
||||
dp_catalog_aux_clear_hw_interrupts(aux->catalog);
|
||||
}
|
||||
|
||||
complete(&aux->comp);
|
||||
}
|
||||
|
||||
static void dp_aux_i2c_handler(struct dp_aux_private *aux)
|
||||
{
|
||||
u32 isr = aux->isr;
|
||||
|
||||
if (isr & DP_INTR_AUX_I2C_DONE) {
|
||||
if (isr & (DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER))
|
||||
aux->aux_error_num = DP_AUX_ERR_NACK;
|
||||
else
|
||||
aux->aux_error_num = DP_AUX_ERR_NONE;
|
||||
} else {
|
||||
if (isr & DP_INTR_WRONG_ADDR)
|
||||
aux->aux_error_num = DP_AUX_ERR_ADDR;
|
||||
else if (isr & DP_INTR_TIMEOUT)
|
||||
aux->aux_error_num = DP_AUX_ERR_TOUT;
|
||||
if (isr & DP_INTR_NACK_DEFER)
|
||||
aux->aux_error_num = DP_AUX_ERR_NACK_DEFER;
|
||||
if (isr & DP_INTR_I2C_NACK)
|
||||
aux->aux_error_num = DP_AUX_ERR_NACK;
|
||||
if (isr & DP_INTR_I2C_DEFER)
|
||||
aux->aux_error_num = DP_AUX_ERR_DEFER;
|
||||
if (isr & DP_INTR_AUX_ERROR) {
|
||||
aux->aux_error_num = DP_AUX_ERR_PHY;
|
||||
dp_catalog_aux_clear_hw_interrupts(aux->catalog);
|
||||
}
|
||||
}
|
||||
|
||||
complete(&aux->comp);
|
||||
}
|
||||
|
||||
static void dp_aux_update_offset_and_segment(struct dp_aux_private *aux,
|
||||
struct drm_dp_aux_msg *input_msg)
|
||||
{
|
||||
u32 edid_address = 0x50;
|
||||
u32 segment_address = 0x30;
|
||||
bool i2c_read = input_msg->request &
|
||||
(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
|
||||
u8 *data;
|
||||
|
||||
if (aux->native || i2c_read || ((input_msg->address != edid_address) &&
|
||||
(input_msg->address != segment_address)))
|
||||
return;
|
||||
|
||||
|
||||
data = input_msg->buffer;
|
||||
if (input_msg->address == segment_address)
|
||||
aux->segment = *data;
|
||||
else
|
||||
aux->offset = *data;
|
||||
}
|
||||
|
||||
/**
|
||||
* dp_aux_transfer_helper() - helper function for EDID read transactions
|
||||
*
|
||||
* @aux: DP AUX private structure
|
||||
* @input_msg: input message from DRM upstream APIs
|
||||
* @send_seg: send the segment to sink
|
||||
*
|
||||
* return: void
|
||||
*
|
||||
* This helper function is used to fix EDID reads for non-compliant
|
||||
* sinks that do not handle the i2c middle-of-transaction flag correctly.
|
||||
*/
|
||||
static void dp_aux_transfer_helper(struct dp_aux_private *aux,
|
||||
struct drm_dp_aux_msg *input_msg,
|
||||
bool send_seg)
|
||||
{
|
||||
struct drm_dp_aux_msg helper_msg;
|
||||
u32 message_size = 0x10;
|
||||
u32 segment_address = 0x30;
|
||||
u32 const edid_block_length = 0x80;
|
||||
bool i2c_mot = input_msg->request & DP_AUX_I2C_MOT;
|
||||
bool i2c_read = input_msg->request &
|
||||
(DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
|
||||
|
||||
if (!i2c_mot || !i2c_read || (input_msg->size == 0))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Sending the segment value and EDID offset will be performed
|
||||
* from the DRM upstream EDID driver for each block. Avoid
|
||||
* duplicate AUX transactions related to this while reading the
|
||||
* first 16 bytes of each block.
|
||||
*/
|
||||
if (!(aux->offset % edid_block_length) || !send_seg)
|
||||
goto end;
|
||||
|
||||
aux->read = false;
|
||||
aux->cmd_busy = true;
|
||||
aux->no_send_addr = true;
|
||||
aux->no_send_stop = true;
|
||||
|
||||
/*
|
||||
* Send the segment address for every i2c read in which the
|
||||
* middle-of-tranaction flag is set. This is required to support EDID
|
||||
* reads of more than 2 blocks as the segment address is reset to 0
|
||||
* since we are overriding the middle-of-transaction flag for read
|
||||
* transactions.
|
||||
*/
|
||||
|
||||
if (aux->segment) {
|
||||
memset(&helper_msg, 0, sizeof(helper_msg));
|
||||
helper_msg.address = segment_address;
|
||||
helper_msg.buffer = &aux->segment;
|
||||
helper_msg.size = 1;
|
||||
dp_aux_cmd_fifo_tx(aux, &helper_msg);
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the offset address for every i2c read in which the
|
||||
* middle-of-transaction flag is set. This will ensure that the sink
|
||||
* will update its read pointer and return the correct portion of the
|
||||
* EDID buffer in the subsequent i2c read trasntion triggered in the
|
||||
* native AUX transfer function.
|
||||
*/
|
||||
memset(&helper_msg, 0, sizeof(helper_msg));
|
||||
helper_msg.address = input_msg->address;
|
||||
helper_msg.buffer = &aux->offset;
|
||||
helper_msg.size = 1;
|
||||
dp_aux_cmd_fifo_tx(aux, &helper_msg);
|
||||
|
||||
end:
|
||||
aux->offset += message_size;
|
||||
if (aux->offset == 0x80 || aux->offset == 0x100)
|
||||
aux->segment = 0x0; /* reset segment at end of block */
|
||||
}
|
||||
|
||||
/*
|
||||
* This function does the real job to process an AUX transaction.
|
||||
* It will call aux_reset() function to reset the AUX channel,
|
||||
* if the waiting is timeout.
|
||||
*/
|
||||
static ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
ssize_t ret;
|
||||
int const aux_cmd_native_max = 16;
|
||||
int const aux_cmd_i2c_max = 128;
|
||||
int const retry_count = 5;
|
||||
struct dp_aux_private *aux = container_of(dp_aux,
|
||||
struct dp_aux_private, dp_aux);
|
||||
|
||||
mutex_lock(&aux->mutex);
|
||||
|
||||
aux->native = msg->request & (DP_AUX_NATIVE_WRITE & DP_AUX_NATIVE_READ);
|
||||
|
||||
/* Ignore address only message */
|
||||
if ((msg->size == 0) || (msg->buffer == NULL)) {
|
||||
msg->reply = aux->native ?
|
||||
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
|
||||
ret = msg->size;
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
/* msg sanity check */
|
||||
if ((aux->native && (msg->size > aux_cmd_native_max)) ||
|
||||
(msg->size > aux_cmd_i2c_max)) {
|
||||
DRM_ERROR("%s: invalid msg: size(%zu), request(%x)\n",
|
||||
__func__, msg->size, msg->request);
|
||||
ret = -EINVAL;
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
dp_aux_update_offset_and_segment(aux, msg);
|
||||
dp_aux_transfer_helper(aux, msg, true);
|
||||
|
||||
aux->read = msg->request & (DP_AUX_I2C_READ & DP_AUX_NATIVE_READ);
|
||||
aux->cmd_busy = true;
|
||||
|
||||
if (aux->read) {
|
||||
aux->no_send_addr = true;
|
||||
aux->no_send_stop = false;
|
||||
} else {
|
||||
aux->no_send_addr = true;
|
||||
aux->no_send_stop = true;
|
||||
}
|
||||
|
||||
ret = dp_aux_cmd_fifo_tx(aux, msg);
|
||||
|
||||
if (ret < 0) {
|
||||
if (aux->native) {
|
||||
aux->retry_cnt++;
|
||||
if (!(aux->retry_cnt % retry_count))
|
||||
dp_catalog_aux_update_cfg(aux->catalog,
|
||||
PHY_AUX_CFG1);
|
||||
dp_catalog_aux_reset(aux->catalog);
|
||||
}
|
||||
goto unlock_exit;
|
||||
}
|
||||
|
||||
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
|
||||
if (aux->read)
|
||||
dp_aux_cmd_fifo_rx(aux, msg);
|
||||
|
||||
msg->reply = aux->native ?
|
||||
DP_AUX_NATIVE_REPLY_ACK : DP_AUX_I2C_REPLY_ACK;
|
||||
} else {
|
||||
/* Reply defer to retry */
|
||||
msg->reply = aux->native ?
|
||||
DP_AUX_NATIVE_REPLY_DEFER : DP_AUX_I2C_REPLY_DEFER;
|
||||
}
|
||||
|
||||
/* Return requested size for success or retry */
|
||||
ret = msg->size;
|
||||
aux->retry_cnt = 0;
|
||||
|
||||
unlock_exit:
|
||||
aux->cmd_busy = false;
|
||||
mutex_unlock(&aux->mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void dp_aux_isr(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
if (!dp_aux) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return;
|
||||
}
|
||||
|
||||
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
|
||||
|
||||
aux->isr = dp_catalog_aux_get_irq(aux->catalog);
|
||||
|
||||
if (!aux->cmd_busy)
|
||||
return;
|
||||
|
||||
if (aux->native)
|
||||
dp_aux_native_handler(aux);
|
||||
else
|
||||
dp_aux_i2c_handler(aux);
|
||||
}
|
||||
|
||||
void dp_aux_reconfig(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
|
||||
|
||||
dp_catalog_aux_update_cfg(aux->catalog, PHY_AUX_CFG1);
|
||||
dp_catalog_aux_reset(aux->catalog);
|
||||
}
|
||||
|
||||
void dp_aux_init(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
if (!dp_aux) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return;
|
||||
}
|
||||
|
||||
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
|
||||
|
||||
dp_catalog_aux_setup(aux->catalog);
|
||||
dp_catalog_aux_enable(aux->catalog, true);
|
||||
aux->retry_cnt = 0;
|
||||
}
|
||||
|
||||
void dp_aux_deinit(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
|
||||
|
||||
dp_catalog_aux_enable(aux->catalog, false);
|
||||
}
|
||||
|
||||
int dp_aux_register(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
int ret;
|
||||
|
||||
if (!dp_aux) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
|
||||
|
||||
aux->dp_aux.name = "dpu_dp_aux";
|
||||
aux->dp_aux.dev = aux->dev;
|
||||
aux->dp_aux.transfer = dp_aux_transfer;
|
||||
ret = drm_dp_aux_register(&aux->dp_aux);
|
||||
if (ret) {
|
||||
DRM_ERROR("%s: failed to register drm aux: %d\n", __func__,
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dp_aux_unregister(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
drm_dp_aux_unregister(dp_aux);
|
||||
}
|
||||
|
||||
struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
if (!catalog) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
aux = devm_kzalloc(dev, sizeof(*aux), GFP_KERNEL);
|
||||
if (!aux)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init_completion(&aux->comp);
|
||||
aux->cmd_busy = false;
|
||||
mutex_init(&aux->mutex);
|
||||
|
||||
aux->dev = dev;
|
||||
aux->catalog = catalog;
|
||||
aux->retry_cnt = 0;
|
||||
|
||||
return &aux->dp_aux;
|
||||
}
|
||||
|
||||
void dp_aux_put(struct drm_dp_aux *dp_aux)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
if (!dp_aux)
|
||||
return;
|
||||
|
||||
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
|
||||
|
||||
mutex_destroy(&aux->mutex);
|
||||
|
||||
devm_kfree(aux->dev, aux);
|
||||
}
|
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
@ -0,0 +1,936 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_irq.h>
|
||||
|
||||
#include "msm_drv.h"
|
||||
#include "msm_kms.h"
|
||||
#include "dp_hpd.h"
|
||||
#include "dp_parser.h"
|
||||
#include "dp_power.h"
|
||||
#include "dp_catalog.h"
|
||||
#include "dp_aux.h"
|
||||
#include "dp_link.h"
|
||||
#include "dp_panel.h"
|
||||
#include "dp_ctrl.h"
|
||||
#include "dp_display.h"
|
||||
#include "dp_drm.h"
|
||||
|
||||
static struct msm_dp *g_dp_display;
|
||||
#define HPD_STRING_SIZE 30
|
||||
|
||||
struct dp_display_private {
|
||||
char *name;
|
||||
int irq;
|
||||
|
||||
/* state variables */
|
||||
bool core_initialized;
|
||||
bool power_on;
|
||||
bool hpd_irq_on;
|
||||
bool audio_supported;
|
||||
|
||||
struct platform_device *pdev;
|
||||
struct dentry *root;
|
||||
struct completion notification_comp;
|
||||
|
||||
struct dp_usbpd *usbpd;
|
||||
struct dp_parser *parser;
|
||||
struct dp_power *power;
|
||||
struct dp_catalog *catalog;
|
||||
struct drm_dp_aux *aux;
|
||||
struct dp_link *link;
|
||||
struct dp_panel *panel;
|
||||
struct dp_ctrl *ctrl;
|
||||
|
||||
struct dp_usbpd_cb usbpd_cb;
|
||||
struct dp_display_mode dp_mode;
|
||||
struct msm_dp dp_display;
|
||||
};
|
||||
|
||||
static const struct of_device_id dp_dt_match[] = {
|
||||
{.compatible = "qcom,sc7180-dp"},
|
||||
{}
|
||||
};
|
||||
|
||||
static irqreturn_t dp_display_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct dp_display_private *dp = dev_id;
|
||||
|
||||
/* DP controller isr */
|
||||
dp_ctrl_isr(dp->ctrl);
|
||||
|
||||
/* DP aux isr */
|
||||
dp_aux_isr(dp->aux);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int dp_display_bind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
struct drm_device *drm;
|
||||
struct msm_drm_private *priv;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
|
||||
drm = dev_get_drvdata(master);
|
||||
|
||||
dp = platform_get_drvdata(pdev);
|
||||
if (!dp) {
|
||||
DRM_ERROR("DP driver bind failed. Invalid driver data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp->dp_display.drm_dev = drm;
|
||||
priv = drm->dev_private;
|
||||
priv->dp = &(dp->dp_display);
|
||||
|
||||
rc = dp->parser->parse(dp->parser);
|
||||
if (rc) {
|
||||
DRM_ERROR("device tree parsing failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = dp_aux_register(dp->aux);
|
||||
if (rc) {
|
||||
DRM_ERROR("DRM DP AUX register failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
rc = dp_power_client_init(dp->power);
|
||||
if (rc) {
|
||||
DRM_ERROR("Power client create failed\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dp_display_unbind(struct device *dev, struct device *master,
|
||||
void *data)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = dev_get_drvdata(master);
|
||||
struct msm_drm_private *priv = drm->dev_private;
|
||||
|
||||
dp = platform_get_drvdata(pdev);
|
||||
if (!dp) {
|
||||
DRM_ERROR("Invalid DP driver data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dp_power_client_deinit(dp->power);
|
||||
dp_aux_unregister(dp->aux);
|
||||
priv->dp = NULL;
|
||||
}
|
||||
|
||||
static const struct component_ops dp_display_comp_ops = {
|
||||
.bind = dp_display_bind,
|
||||
.unbind = dp_display_unbind,
|
||||
};
|
||||
|
||||
static bool dp_display_is_ds_bridge(struct dp_panel *panel)
|
||||
{
|
||||
return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
|
||||
DP_DWN_STRM_PORT_PRESENT);
|
||||
}
|
||||
|
||||
static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
|
||||
{
|
||||
return dp_display_is_ds_bridge(dp->panel) &&
|
||||
(dp->link->sink_count == 0);
|
||||
}
|
||||
|
||||
static void dp_display_send_hpd_event(struct msm_dp *dp_display)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
struct drm_connector *connector;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
connector = dp->dp_display.connector;
|
||||
drm_helper_hpd_irq_event(connector->dev);
|
||||
}
|
||||
|
||||
static int dp_display_send_hpd_notification(struct dp_display_private *dp,
|
||||
bool hpd)
|
||||
{
|
||||
static bool encoder_mode_set;
|
||||
struct msm_drm_private *priv = dp->dp_display.drm_dev->dev_private;
|
||||
struct msm_kms *kms = priv->kms;
|
||||
|
||||
mutex_lock(&dp->dp_display.connect_mutex);
|
||||
if ((hpd && dp->dp_display.is_connected) ||
|
||||
(!hpd && !dp->dp_display.is_connected)) {
|
||||
DRM_DEBUG_DP("HPD already %s\n", (hpd ? "on" : "off"));
|
||||
mutex_unlock(&dp->dp_display.connect_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reset video pattern flag on disconnect */
|
||||
if (!hpd)
|
||||
dp->panel->video_test = false;
|
||||
|
||||
dp->dp_display.is_connected = hpd;
|
||||
reinit_completion(&dp->notification_comp);
|
||||
|
||||
if (dp->dp_display.is_connected && dp->dp_display.encoder
|
||||
&& !encoder_mode_set
|
||||
&& kms->funcs->set_encoder_mode) {
|
||||
kms->funcs->set_encoder_mode(kms,
|
||||
dp->dp_display.encoder, false);
|
||||
DRM_DEBUG_DP("set_encoder_mode() Completed\n");
|
||||
encoder_mode_set = true;
|
||||
}
|
||||
|
||||
dp_display_send_hpd_event(&dp->dp_display);
|
||||
|
||||
if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) {
|
||||
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
|
||||
mutex_unlock(&dp->dp_display.connect_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->dp_display.connect_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_process_hpd_high(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct edid *edid;
|
||||
|
||||
dp_aux_init(dp->aux);
|
||||
|
||||
if (dp->link->psm_enabled)
|
||||
goto notify;
|
||||
|
||||
dp->panel->max_dp_lanes = dp->parser->max_dp_lanes;
|
||||
|
||||
rc = dp_panel_read_sink_caps(dp->panel, dp->dp_display.connector);
|
||||
if (rc)
|
||||
goto notify;
|
||||
|
||||
dp_link_process_request(dp->link);
|
||||
|
||||
if (dp_display_is_sink_count_zero(dp)) {
|
||||
DRM_DEBUG_DP("no downstream devices connected\n");
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
edid = dp->panel->edid;
|
||||
|
||||
dp->audio_supported = drm_detect_monitor_audio(edid);
|
||||
|
||||
dp_panel_handle_sink_request(dp->panel);
|
||||
|
||||
dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
|
||||
dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes;
|
||||
notify:
|
||||
dp_display_send_hpd_notification(dp, true);
|
||||
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dp_display_host_init(struct dp_display_private *dp)
|
||||
{
|
||||
bool flip = false;
|
||||
|
||||
if (dp->core_initialized) {
|
||||
DRM_DEBUG_DP("DP core already initialized\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dp->usbpd->orientation == ORIENTATION_CC2)
|
||||
flip = true;
|
||||
|
||||
dp_power_init(dp->power, flip);
|
||||
dp_ctrl_host_init(dp->ctrl, flip);
|
||||
dp_aux_init(dp->aux);
|
||||
dp->core_initialized = true;
|
||||
}
|
||||
|
||||
static void dp_display_host_deinit(struct dp_display_private *dp)
|
||||
{
|
||||
if (!dp->core_initialized) {
|
||||
DRM_DEBUG_DP("DP core already off\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dp_ctrl_host_deinit(dp->ctrl);
|
||||
dp_aux_deinit(dp->aux);
|
||||
dp_power_deinit(dp->power);
|
||||
disable_irq(dp->irq);
|
||||
dp->core_initialized = false;
|
||||
}
|
||||
|
||||
static void dp_display_process_hpd_low(struct dp_display_private *dp)
|
||||
{
|
||||
dp_display_send_hpd_notification(dp, false);
|
||||
|
||||
dp_aux_deinit(dp->aux);
|
||||
}
|
||||
|
||||
static int dp_display_usbpd_configure_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev) {
|
||||
DRM_ERROR("invalid dev\n");
|
||||
rc = -EINVAL;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp = dev_get_drvdata(dev);
|
||||
if (!dp) {
|
||||
DRM_ERROR("no driver data found\n");
|
||||
rc = -ENODEV;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp_display_host_init(dp);
|
||||
|
||||
if (dp->usbpd->hpd_high)
|
||||
dp_display_process_hpd_high(dp);
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dp_display_clean(struct dp_display_private *dp)
|
||||
{
|
||||
dp_ctrl_push_idle(dp->ctrl);
|
||||
dp_ctrl_off(dp->ctrl);
|
||||
}
|
||||
|
||||
static int dp_display_usbpd_disconnect_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = dev_get_drvdata(dev);
|
||||
|
||||
rc = dp_display_send_hpd_notification(dp, false);
|
||||
|
||||
/* if cable is disconnected, reset psm_enabled flag */
|
||||
if (!dp->usbpd->alt_mode_cfg_done)
|
||||
dp->link->psm_enabled = false;
|
||||
|
||||
if ((rc < 0) && dp->power_on)
|
||||
dp_display_clean(dp);
|
||||
|
||||
dp_display_host_deinit(dp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dp_display_handle_video_request(struct dp_display_private *dp)
|
||||
{
|
||||
if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) {
|
||||
/* force disconnect followed by connect */
|
||||
dp->usbpd->connect(dp->usbpd, false);
|
||||
dp->panel->video_test = true;
|
||||
dp->usbpd->connect(dp->usbpd, true);
|
||||
dp_link_send_test_response(dp->link);
|
||||
}
|
||||
}
|
||||
|
||||
static int dp_display_handle_hpd_irq(struct dp_display_private *dp)
|
||||
{
|
||||
if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
|
||||
dp_display_send_hpd_notification(dp, false);
|
||||
|
||||
if (dp_display_is_sink_count_zero(dp)) {
|
||||
DRM_DEBUG_DP("sink count is zero, nothing to do\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return dp_display_process_hpd_high(dp);
|
||||
}
|
||||
|
||||
dp_ctrl_handle_sink_request(dp->ctrl);
|
||||
|
||||
dp_display_handle_video_request(dp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_usbpd_attention_cb(struct device *dev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dev) {
|
||||
DRM_ERROR("invalid dev\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = dev_get_drvdata(dev);
|
||||
if (!dp) {
|
||||
DRM_ERROR("no driver data found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (dp->usbpd->hpd_irq) {
|
||||
dp->hpd_irq_on = true;
|
||||
|
||||
rc = dp_link_process_request(dp->link);
|
||||
/* check for any test request issued by sink */
|
||||
if (!rc)
|
||||
dp_display_handle_hpd_irq(dp);
|
||||
|
||||
dp->hpd_irq_on = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!dp->usbpd->hpd_high) {
|
||||
dp_display_process_hpd_low(dp);
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dp->usbpd->alt_mode_cfg_done)
|
||||
dp_display_process_hpd_high(dp);
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dp_display_deinit_sub_modules(struct dp_display_private *dp)
|
||||
{
|
||||
dp_ctrl_put(dp->ctrl);
|
||||
dp_panel_put(dp->panel);
|
||||
dp_aux_put(dp->aux);
|
||||
}
|
||||
|
||||
static int dp_init_sub_modules(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
struct device *dev = &dp->pdev->dev;
|
||||
struct dp_usbpd_cb *cb = &dp->usbpd_cb;
|
||||
struct dp_panel_in panel_in = {
|
||||
.dev = dev,
|
||||
};
|
||||
|
||||
/* Callback APIs used for cable status change event */
|
||||
cb->configure = dp_display_usbpd_configure_cb;
|
||||
cb->disconnect = dp_display_usbpd_disconnect_cb;
|
||||
cb->attention = dp_display_usbpd_attention_cb;
|
||||
|
||||
dp->usbpd = dp_hpd_get(dev, cb);
|
||||
if (IS_ERR(dp->usbpd)) {
|
||||
rc = PTR_ERR(dp->usbpd);
|
||||
DRM_ERROR("failed to initialize hpd, rc = %d\n", rc);
|
||||
dp->usbpd = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp->parser = dp_parser_get(dp->pdev);
|
||||
if (IS_ERR(dp->parser)) {
|
||||
rc = PTR_ERR(dp->parser);
|
||||
DRM_ERROR("failed to initialize parser, rc = %d\n", rc);
|
||||
dp->parser = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp->catalog = dp_catalog_get(dev, &dp->parser->io);
|
||||
if (IS_ERR(dp->catalog)) {
|
||||
rc = PTR_ERR(dp->catalog);
|
||||
DRM_ERROR("failed to initialize catalog, rc = %d\n", rc);
|
||||
dp->catalog = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp->power = dp_power_get(dp->parser);
|
||||
if (IS_ERR(dp->power)) {
|
||||
rc = PTR_ERR(dp->power);
|
||||
DRM_ERROR("failed to initialize power, rc = %d\n", rc);
|
||||
dp->power = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp->aux = dp_aux_get(dev, dp->catalog);
|
||||
if (IS_ERR(dp->aux)) {
|
||||
rc = PTR_ERR(dp->aux);
|
||||
DRM_ERROR("failed to initialize aux, rc = %d\n", rc);
|
||||
dp->aux = NULL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp->link = dp_link_get(dev, dp->aux);
|
||||
if (IS_ERR(dp->link)) {
|
||||
rc = PTR_ERR(dp->link);
|
||||
DRM_ERROR("failed to initialize link, rc = %d\n", rc);
|
||||
dp->link = NULL;
|
||||
goto error_link;
|
||||
}
|
||||
|
||||
panel_in.aux = dp->aux;
|
||||
panel_in.catalog = dp->catalog;
|
||||
panel_in.link = dp->link;
|
||||
|
||||
dp->panel = dp_panel_get(&panel_in);
|
||||
if (IS_ERR(dp->panel)) {
|
||||
rc = PTR_ERR(dp->panel);
|
||||
DRM_ERROR("failed to initialize panel, rc = %d\n", rc);
|
||||
dp->panel = NULL;
|
||||
goto error_link;
|
||||
}
|
||||
|
||||
dp->ctrl = dp_ctrl_get(dev, dp->link, dp->panel, dp->aux,
|
||||
dp->power, dp->catalog, dp->parser);
|
||||
if (IS_ERR(dp->ctrl)) {
|
||||
rc = PTR_ERR(dp->ctrl);
|
||||
DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc);
|
||||
dp->ctrl = NULL;
|
||||
goto error_ctrl;
|
||||
}
|
||||
|
||||
return rc;
|
||||
error_ctrl:
|
||||
dp_panel_put(dp->panel);
|
||||
error_link:
|
||||
dp_aux_put(dp->aux);
|
||||
error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_set_mode(struct msm_dp *dp_display,
|
||||
struct dp_display_mode *mode)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
dp->panel->dp_mode.drm_mode = mode->drm_mode;
|
||||
dp->panel->dp_mode.bpp = mode->bpp;
|
||||
dp->panel->dp_mode.capabilities = mode->capabilities;
|
||||
dp_panel_init_panel_info(dp->panel);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_prepare(struct msm_dp *dp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dp_display_dump(struct msm_dp *dp_display)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
dp_panel_dump_regs(dp->panel);
|
||||
}
|
||||
|
||||
static int dp_display_enable(struct msm_dp *dp_display)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
bool dump_dp = false;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
if (dp->power_on) {
|
||||
DRM_DEBUG_DP("Link already setup, return\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = dp_ctrl_on(dp->ctrl);
|
||||
if (!rc)
|
||||
dp->power_on = true;
|
||||
|
||||
if (dump_dp != false)
|
||||
dp_display_dump(dp_display);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_post_enable(struct msm_dp *dp_display)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
complete_all(&dp->notification_comp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_pre_disable(struct msm_dp *dp_display)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
if (dp->usbpd->alt_mode_cfg_done)
|
||||
dp_link_psm_config(dp->link, &dp->panel->link_info, true);
|
||||
|
||||
dp_ctrl_push_idle(dp->ctrl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_disable(struct msm_dp *dp_display)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
if (!dp->power_on || !dp->core_initialized)
|
||||
return -EINVAL;
|
||||
|
||||
dp_ctrl_off(dp->ctrl);
|
||||
|
||||
dp->power_on = false;
|
||||
|
||||
complete_all(&dp->notification_comp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dp_display_request_irq(struct msm_dp *dp_display)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dp_display) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
dp->irq = irq_of_parse_and_map(dp->pdev->dev.of_node, 0);
|
||||
if (dp->irq < 0) {
|
||||
rc = dp->irq;
|
||||
DRM_ERROR("failed to get irq: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = devm_request_irq(&dp->pdev->dev, dp->irq, dp_display_irq,
|
||||
IRQF_TRIGGER_HIGH, "dp_display_isr", dp);
|
||||
if (rc < 0) {
|
||||
DRM_ERROR("failed to request IRQ%u: %d\n",
|
||||
dp->irq, rc);
|
||||
return rc;
|
||||
}
|
||||
disable_irq(dp->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_unprepare(struct msm_dp *dp)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dp_display_validate_mode(struct msm_dp *dp, u32 mode_pclk_khz)
|
||||
{
|
||||
const u32 num_components = 3, default_bpp = 24;
|
||||
struct dp_display_private *dp_display;
|
||||
struct dp_link_info *link_info;
|
||||
u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0;
|
||||
|
||||
if (!dp || !mode_pclk_khz || !dp->connector) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
link_info = &dp_display->panel->link_info;
|
||||
|
||||
mode_bpp = dp->connector->display_info.bpc * num_components;
|
||||
if (!mode_bpp)
|
||||
mode_bpp = default_bpp;
|
||||
|
||||
mode_bpp = dp_panel_get_mode_bpp(dp_display->panel,
|
||||
mode_bpp, mode_pclk_khz);
|
||||
|
||||
mode_rate_khz = mode_pclk_khz * mode_bpp;
|
||||
supported_rate_khz = link_info->num_lanes * link_info->rate * 8;
|
||||
|
||||
if (mode_rate_khz > supported_rate_khz)
|
||||
return MODE_BAD;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
int dp_display_get_modes(struct msm_dp *dp,
|
||||
struct dp_display_mode *dp_mode)
|
||||
{
|
||||
struct dp_display_private *dp_display;
|
||||
int ret = 0;
|
||||
|
||||
if (!dp) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
ret = dp_panel_get_modes(dp_display->panel,
|
||||
dp->connector, dp_mode);
|
||||
if (dp_mode->drm_mode.clock)
|
||||
dp->max_pclk_khz = dp_mode->drm_mode.clock;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool dp_display_check_video_test(struct msm_dp *dp)
|
||||
{
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
return dp_display->panel->video_test;
|
||||
}
|
||||
|
||||
int dp_display_get_test_bpp(struct msm_dp *dp)
|
||||
{
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
if (!dp) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
return dp_link_bit_depth_to_bpp(
|
||||
dp_display->link->test_video.test_bit_depth);
|
||||
}
|
||||
|
||||
static int dp_display_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!pdev || !pdev->dev.of_node) {
|
||||
DRM_ERROR("pdev not found\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL);
|
||||
if (!dp)
|
||||
return -ENOMEM;
|
||||
|
||||
init_completion(&dp->notification_comp);
|
||||
|
||||
dp->pdev = pdev;
|
||||
dp->name = "drm_dp";
|
||||
|
||||
rc = dp_init_sub_modules(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("init sub module failed\n");
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, dp);
|
||||
|
||||
mutex_init(&dp->dp_display.connect_mutex);
|
||||
g_dp_display = &dp->dp_display;
|
||||
|
||||
rc = component_add(&pdev->dev, &dp_display_comp_ops);
|
||||
if (rc) {
|
||||
DRM_ERROR("component add failed, rc=%d\n", rc);
|
||||
dp_display_deinit_sub_modules(dp);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
||||
dp = platform_get_drvdata(pdev);
|
||||
|
||||
dp_display_deinit_sub_modules(dp);
|
||||
|
||||
component_del(&pdev->dev, &dp_display_comp_ops);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_pm_resume(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_pm_suspend(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_pm_prepare(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dp_pm_complete(struct device *dev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops dp_pm_ops = {
|
||||
.suspend = dp_pm_suspend,
|
||||
.resume = dp_pm_resume,
|
||||
.prepare = dp_pm_prepare,
|
||||
.complete = dp_pm_complete,
|
||||
};
|
||||
|
||||
static struct platform_driver dp_display_driver = {
|
||||
.probe = dp_display_probe,
|
||||
.remove = dp_display_remove,
|
||||
.driver = {
|
||||
.name = "msm-dp-display",
|
||||
.of_match_table = dp_dt_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &dp_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
int __init msm_dp_register(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = platform_driver_register(&dp_display_driver);
|
||||
if (ret)
|
||||
DRM_ERROR("Dp display driver register failed");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void __exit msm_dp_unregister(void)
|
||||
{
|
||||
platform_driver_unregister(&dp_display_driver);
|
||||
}
|
||||
|
||||
int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
struct msm_drm_private *priv;
|
||||
int ret;
|
||||
|
||||
if (WARN_ON(!encoder) || WARN_ON(!dp_display) || WARN_ON(!dev))
|
||||
return -EINVAL;
|
||||
|
||||
priv = dev->dev_private;
|
||||
dp_display->drm_dev = dev;
|
||||
|
||||
ret = dp_display_request_irq(dp_display);
|
||||
if (ret) {
|
||||
DRM_ERROR("request_irq failed, ret=%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dp_display->encoder = encoder;
|
||||
|
||||
dp_display->connector = dp_drm_connector_init(dp_display);
|
||||
if (IS_ERR(dp_display->connector)) {
|
||||
ret = PTR_ERR(dp_display->connector);
|
||||
DRM_DEV_ERROR(dev->dev,
|
||||
"failed to create dp connector: %d\n", ret);
|
||||
dp_display->connector = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
priv->connectors[priv->num_connectors++] = dp_display->connector;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
if (!dp_display->dp_mode.drm_mode.clock) {
|
||||
DRM_ERROR("invalid params\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = dp_display_set_mode(dp, &dp_display->dp_mode);
|
||||
if (rc) {
|
||||
DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = dp_display_prepare(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("DP display prepare failed, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = dp_display_enable(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("DP display enable failed, rc=%d\n", rc);
|
||||
dp_display_unprepare(dp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = dp_display_post_enable(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("DP display post enable failed, rc=%d\n", rc);
|
||||
dp_display_disable(dp);
|
||||
dp_display_unprepare(dp);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
rc = dp_display_pre_disable(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("DP display pre disable failed, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = dp_display_disable(dp);
|
||||
if (rc) {
|
||||
DRM_ERROR("DP display disable failed, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = dp_display_unprepare(dp);
|
||||
if (rc)
|
||||
DRM_ERROR("DP display unprepare failed, rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct dp_display_private *dp_display;
|
||||
|
||||
dp_display = container_of(dp, struct dp_display_private, dp_display);
|
||||
|
||||
memset(&dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode));
|
||||
|
||||
if (dp_display_check_video_test(dp))
|
||||
dp_display->dp_mode.bpp = dp_display_get_test_bpp(dp);
|
||||
else /* Default num_components per pixel = 3 */
|
||||
dp_display->dp_mode.bpp = dp->connector->display_info.bpc * 3;
|
||||
|
||||
if (!dp_display->dp_mode.bpp)
|
||||
dp_display->dp_mode.bpp = 24; /* Default bpp */
|
||||
|
||||
drm_mode_copy(&dp_display->dp_mode.drm_mode, adjusted_mode);
|
||||
|
||||
dp_display->dp_mode.v_active_low =
|
||||
!!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC);
|
||||
|
||||
dp_display->dp_mode.h_active_low =
|
||||
!!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC);
|
||||
}
|
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_ */
|
265
drivers/gpu/drm/msm/dp/dp_parser.c
Normal file
265
drivers/gpu/drm/msm/dp/dp_parser.c
Normal file
@ -0,0 +1,265 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "dp_parser.h"
|
||||
|
||||
static const struct dp_regulator_cfg sdm845_dp_reg_cfg = {
|
||||
.num = 2,
|
||||
.regs = {
|
||||
{"vdda-1p2", 21800, 4 }, /* 1.2 V */
|
||||
{"vdda-0p9", 36000, 32 }, /* 0.9 V */
|
||||
},
|
||||
};
|
||||
|
||||
static int msm_dss_ioremap(struct platform_device *pdev,
|
||||
struct dss_io_data *io_data)
|
||||
{
|
||||
struct resource *res = NULL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
DRM_ERROR("%pS->%s: msm_dss_get_res failed\n",
|
||||
__builtin_return_address(0), __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
io_data->len = (u32)resource_size(res);
|
||||
io_data->base = ioremap(res->start, io_data->len);
|
||||
if (!io_data->base) {
|
||||
DRM_ERROR("%pS->%s: ioremap failed\n",
|
||||
__builtin_return_address(0), __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_dss_iounmap(struct dss_io_data *io_data)
|
||||
{
|
||||
if (io_data->base) {
|
||||
iounmap(io_data->base);
|
||||
io_data->base = NULL;
|
||||
}
|
||||
io_data->len = 0;
|
||||
}
|
||||
|
||||
static void dp_parser_unmap_io_resources(struct dp_parser *parser)
|
||||
{
|
||||
struct dp_io *io = &parser->io;
|
||||
|
||||
msm_dss_iounmap(&io->dp_controller);
|
||||
msm_dss_iounmap(&io->usb3_dp_com);
|
||||
}
|
||||
|
||||
static int dp_parser_ctrl_res(struct dp_parser *parser)
|
||||
{
|
||||
int rc = 0;
|
||||
struct platform_device *pdev = parser->pdev;
|
||||
struct dp_io *io = &parser->io;
|
||||
|
||||
rc = msm_dss_ioremap(pdev, &io->dp_controller);
|
||||
if (rc) {
|
||||
DRM_ERROR("unable to remap dp io resources, rc=%d\n", rc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dp_parser_unmap_io_resources(parser);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_parser_misc(struct dp_parser *parser)
|
||||
{
|
||||
struct device_node *of_node = parser->pdev->dev.of_node;
|
||||
int len = 0;
|
||||
const char *data_lane_property = "data-lanes";
|
||||
|
||||
len = of_property_count_elems_of_size(of_node,
|
||||
data_lane_property, sizeof(u32));
|
||||
if (len < 0) {
|
||||
DRM_WARN("Invalid property %s, default max DP lanes = %d\n",
|
||||
data_lane_property, DP_MAX_NUM_DP_LANES);
|
||||
len = DP_MAX_NUM_DP_LANES;
|
||||
}
|
||||
|
||||
parser->max_dp_lanes = len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool dp_parser_check_prefix(const char *clk_prefix,
|
||||
const char *clk_name)
|
||||
{
|
||||
return !strncmp(clk_prefix, clk_name, strlen(clk_prefix));
|
||||
}
|
||||
|
||||
static int dp_parser_init_clk_data(struct dp_parser *parser)
|
||||
{
|
||||
int num_clk, i, rc;
|
||||
int core_clk_count = 0, ctrl_clk_count = 0;
|
||||
const char *clk_name;
|
||||
struct device *dev = &parser->pdev->dev;
|
||||
struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
|
||||
struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
|
||||
|
||||
num_clk = of_property_count_strings(dev->of_node, "clock-names");
|
||||
if (num_clk <= 0) {
|
||||
DRM_ERROR("no clocks are defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < num_clk; i++) {
|
||||
rc = of_property_read_string_index(dev->of_node,
|
||||
"clock-names", i, &clk_name);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (dp_parser_check_prefix("core", clk_name))
|
||||
core_clk_count++;
|
||||
|
||||
if (dp_parser_check_prefix("ctrl", clk_name))
|
||||
ctrl_clk_count++;
|
||||
|
||||
if (dp_parser_check_prefix("stream", clk_name))
|
||||
ctrl_clk_count++;
|
||||
}
|
||||
|
||||
/* Initialize the CORE power module */
|
||||
if (core_clk_count == 0) {
|
||||
DRM_ERROR("no core clocks are defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
core_power->num_clk = core_clk_count;
|
||||
core_power->clk_config = devm_kzalloc(dev,
|
||||
sizeof(struct dss_clk) * core_power->num_clk,
|
||||
GFP_KERNEL);
|
||||
if (!core_power->clk_config)
|
||||
return -EINVAL;
|
||||
|
||||
/* Initialize the CTRL power module */
|
||||
if (ctrl_clk_count == 0) {
|
||||
DRM_ERROR("no ctrl clocks are defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctrl_power->num_clk = ctrl_clk_count;
|
||||
ctrl_power->clk_config = devm_kzalloc(dev,
|
||||
sizeof(struct dss_clk) * ctrl_power->num_clk,
|
||||
GFP_KERNEL);
|
||||
if (!ctrl_power->clk_config) {
|
||||
ctrl_power->num_clk = 0;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_parser_clock(struct dp_parser *parser)
|
||||
{
|
||||
int rc = 0, i = 0;
|
||||
int num_clk = 0;
|
||||
int core_clk_index = 0, ctrl_clk_index = 0;
|
||||
int core_clk_count = 0, ctrl_clk_count = 0;
|
||||
const char *clk_name;
|
||||
struct device *dev = &parser->pdev->dev;
|
||||
struct dss_module_power *core_power = &parser->mp[DP_CORE_PM];
|
||||
struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM];
|
||||
|
||||
core_power = &parser->mp[DP_CORE_PM];
|
||||
ctrl_power = &parser->mp[DP_CTRL_PM];
|
||||
|
||||
rc = dp_parser_init_clk_data(parser);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to initialize power data %d\n", rc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
core_clk_count = core_power->num_clk;
|
||||
ctrl_clk_count = ctrl_power->num_clk;
|
||||
|
||||
num_clk = core_clk_count + ctrl_clk_count;
|
||||
|
||||
for (i = 0; i < num_clk; i++) {
|
||||
rc = of_property_read_string_index(dev->of_node, "clock-names",
|
||||
i, &clk_name);
|
||||
if (rc) {
|
||||
DRM_ERROR("error reading clock-names %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
if (dp_parser_check_prefix("core", clk_name) &&
|
||||
core_clk_index < core_clk_count) {
|
||||
struct dss_clk *clk =
|
||||
&core_power->clk_config[core_clk_index];
|
||||
strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
|
||||
clk->type = DSS_CLK_AHB;
|
||||
core_clk_index++;
|
||||
} else if ((dp_parser_check_prefix("ctrl", clk_name) ||
|
||||
dp_parser_check_prefix("stream", clk_name)) &&
|
||||
ctrl_clk_index < ctrl_clk_count) {
|
||||
struct dss_clk *clk =
|
||||
&ctrl_power->clk_config[ctrl_clk_index];
|
||||
strlcpy(clk->clk_name, clk_name, sizeof(clk->clk_name));
|
||||
ctrl_clk_index++;
|
||||
|
||||
if (dp_parser_check_prefix("ctrl_link", clk_name) ||
|
||||
dp_parser_check_prefix("stream_pixel", clk_name))
|
||||
clk->type = DSS_CLK_PCLK;
|
||||
else
|
||||
clk->type = DSS_CLK_AHB;
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_DP("clock parsing successful\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_parser_parse(struct dp_parser *parser)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
if (!parser) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = dp_parser_ctrl_res(parser);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dp_parser_misc(parser);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dp_parser_clock(parser);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Map the corresponding regulator information according to
|
||||
* version. Currently, since we only have one supported platform,
|
||||
* mapping the regulator directly.
|
||||
*/
|
||||
parser->regulator_cfg = &sdm845_dp_reg_cfg;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dp_parser *dp_parser_get(struct platform_device *pdev)
|
||||
{
|
||||
struct dp_parser *parser;
|
||||
|
||||
parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL);
|
||||
if (!parser)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
parser->parse = dp_parser_parse;
|
||||
parser->pdev = pdev;
|
||||
|
||||
return parser;
|
||||
}
|
139
drivers/gpu/drm/msm/dp/dp_parser.h
Normal file
139
drivers/gpu/drm/msm/dp/dp_parser.h
Normal file
@ -0,0 +1,139 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_PARSER_H_
|
||||
#define _DP_PARSER_H_
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include "dpu_io_util.h"
|
||||
#include "msm_drv.h"
|
||||
|
||||
#define DP_LABEL "MDSS DP DISPLAY"
|
||||
#define DP_MAX_PIXEL_CLK_KHZ 675000
|
||||
#define DP_MAX_NUM_DP_LANES 4
|
||||
|
||||
enum dp_pm_type {
|
||||
DP_CORE_PM,
|
||||
DP_CTRL_PM,
|
||||
DP_PHY_PM,
|
||||
DP_MAX_PM
|
||||
};
|
||||
|
||||
struct dss_io_data {
|
||||
u32 len;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static inline const char *dp_parser_pm_name(enum dp_pm_type module)
|
||||
{
|
||||
switch (module) {
|
||||
case DP_CORE_PM: return "DP_CORE_PM";
|
||||
case DP_CTRL_PM: return "DP_CTRL_PM";
|
||||
case DP_PHY_PM: return "DP_PHY_PM";
|
||||
default: return "???";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* struct dp_display_data - display related device tree data.
|
||||
*
|
||||
* @ctrl_node: referece to controller device
|
||||
* @phy_node: reference to phy device
|
||||
* @is_active: is the controller currently active
|
||||
* @name: name of the display
|
||||
* @display_type: type of the display
|
||||
*/
|
||||
struct dp_display_data {
|
||||
struct device_node *ctrl_node;
|
||||
struct device_node *phy_node;
|
||||
bool is_active;
|
||||
const char *name;
|
||||
const char *display_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_ctrl_resource - controller's IO related data
|
||||
*
|
||||
* @dp_controller: Display Port controller mapped memory address
|
||||
* @phy_io: phy's mapped memory address
|
||||
* @ln_tx0_io: USB-DP lane TX0's mapped memory address
|
||||
* @ln_tx1_io: USB-DP lane TX1's mapped memory address
|
||||
* @dp_pll_io: DP PLL mapped memory address
|
||||
* @usb3_dp_com: USB3 DP PHY combo mapped memory address
|
||||
*/
|
||||
struct dp_io {
|
||||
struct dss_io_data dp_controller;
|
||||
struct dss_io_data phy_io;
|
||||
struct dss_io_data ln_tx0_io;
|
||||
struct dss_io_data ln_tx1_io;
|
||||
struct dss_io_data dp_pll_io;
|
||||
struct dss_io_data usb3_dp_com;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_pinctrl - DP's pin control
|
||||
*
|
||||
* @pin: pin-controller's instance
|
||||
* @state_active: active state pin control
|
||||
* @state_hpd_active: hpd active state pin control
|
||||
* @state_suspend: suspend state pin control
|
||||
*/
|
||||
struct dp_pinctrl {
|
||||
struct pinctrl *pin;
|
||||
struct pinctrl_state *state_active;
|
||||
struct pinctrl_state *state_hpd_active;
|
||||
struct pinctrl_state *state_suspend;
|
||||
};
|
||||
|
||||
#define DP_DEV_REGULATOR_MAX 4
|
||||
|
||||
/* Regulators for DP devices */
|
||||
struct dp_reg_entry {
|
||||
char name[32];
|
||||
int enable_load;
|
||||
int disable_load;
|
||||
};
|
||||
|
||||
struct dp_regulator_cfg {
|
||||
int num;
|
||||
struct dp_reg_entry regs[DP_DEV_REGULATOR_MAX];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct dp_parser - DP parser's data exposed to clients
|
||||
*
|
||||
* @pdev: platform data of the client
|
||||
* @mp: gpio, regulator and clock related data
|
||||
* @pinctrl: pin-control related data
|
||||
* @disp_data: controller's display related data
|
||||
* @parse: function to be called by client to parse device tree.
|
||||
*/
|
||||
struct dp_parser {
|
||||
struct platform_device *pdev;
|
||||
struct dss_module_power mp[DP_MAX_PM];
|
||||
struct dp_pinctrl pinctrl;
|
||||
struct dp_io io;
|
||||
struct dp_display_data disp_data;
|
||||
const struct dp_regulator_cfg *regulator_cfg;
|
||||
u32 max_dp_lanes;
|
||||
|
||||
int (*parse)(struct dp_parser *parser);
|
||||
};
|
||||
|
||||
/**
|
||||
* dp_parser_get() - get the DP's device tree parser module
|
||||
*
|
||||
* @pdev: platform data of the client
|
||||
* return: pointer to dp_parser structure.
|
||||
*
|
||||
* This function provides client capability to parse the
|
||||
* device tree and populate the data structures. The data
|
||||
* related to clock, regulators, pin-control and other
|
||||
* can be parsed using this module.
|
||||
*/
|
||||
struct dp_parser *dp_parser_get(struct platform_device *pdev);
|
||||
|
||||
#endif
|
363
drivers/gpu/drm/msm/dp/dp_power.c
Normal file
363
drivers/gpu/drm/msm/dp/dp_power.c
Normal file
@ -0,0 +1,363 @@
|
||||
// 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/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include "dp_power.h"
|
||||
|
||||
struct dp_power_private {
|
||||
struct dp_parser *parser;
|
||||
struct platform_device *pdev;
|
||||
struct clk *link_clk_src;
|
||||
struct clk *pixel_provider;
|
||||
struct clk *link_provider;
|
||||
struct regulator_bulk_data supplies[DP_DEV_REGULATOR_MAX];
|
||||
|
||||
struct dp_power dp_power;
|
||||
};
|
||||
|
||||
static void dp_power_regulator_disable(struct dp_power_private *power)
|
||||
{
|
||||
struct regulator_bulk_data *s = power->supplies;
|
||||
const struct dp_reg_entry *regs = power->parser->regulator_cfg->regs;
|
||||
int num = power->parser->regulator_cfg->num;
|
||||
int i;
|
||||
|
||||
DBG("");
|
||||
for (i = num - 1; i >= 0; i--)
|
||||
if (regs[i].disable_load >= 0)
|
||||
regulator_set_load(s[i].consumer,
|
||||
regs[i].disable_load);
|
||||
|
||||
regulator_bulk_disable(num, s);
|
||||
}
|
||||
|
||||
static int dp_power_regulator_enable(struct dp_power_private *power)
|
||||
{
|
||||
struct regulator_bulk_data *s = power->supplies;
|
||||
const struct dp_reg_entry *regs = power->parser->regulator_cfg->regs;
|
||||
int num = power->parser->regulator_cfg->num;
|
||||
int ret, i;
|
||||
|
||||
DBG("");
|
||||
for (i = 0; i < num; i++) {
|
||||
if (regs[i].enable_load >= 0) {
|
||||
ret = regulator_set_load(s[i].consumer,
|
||||
regs[i].enable_load);
|
||||
if (ret < 0) {
|
||||
pr_err("regulator %d set op mode failed, %d\n",
|
||||
i, ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = regulator_bulk_enable(num, s);
|
||||
if (ret < 0) {
|
||||
pr_err("regulator enable failed, %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
for (i--; i >= 0; i--)
|
||||
regulator_set_load(s[i].consumer, regs[i].disable_load);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dp_power_regulator_init(struct dp_power_private *power)
|
||||
{
|
||||
struct regulator_bulk_data *s = power->supplies;
|
||||
const struct dp_reg_entry *regs = power->parser->regulator_cfg->regs;
|
||||
struct platform_device *pdev = power->pdev;
|
||||
int num = power->parser->regulator_cfg->num;
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
s[i].supply = regs[i].name;
|
||||
|
||||
ret = devm_regulator_bulk_get(&pdev->dev, num, s);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to init regulator, ret=%d\n",
|
||||
__func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_power_clk_init(struct dp_power_private *power)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dss_module_power *core, *ctrl;
|
||||
struct device *dev = &power->pdev->dev;
|
||||
|
||||
core = &power->parser->mp[DP_CORE_PM];
|
||||
ctrl = &power->parser->mp[DP_CTRL_PM];
|
||||
|
||||
rc = msm_dss_get_clk(dev, core->clk_config, core->num_clk);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to get %s clk. err=%d\n",
|
||||
dp_parser_pm_name(DP_CORE_PM), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = msm_dss_get_clk(dev, ctrl->clk_config, ctrl->num_clk);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to get %s clk. err=%d\n",
|
||||
dp_parser_pm_name(DP_CTRL_PM), rc);
|
||||
msm_dss_put_clk(core->clk_config, core->num_clk);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_power_clk_deinit(struct dp_power_private *power)
|
||||
{
|
||||
struct dss_module_power *core, *ctrl;
|
||||
|
||||
core = &power->parser->mp[DP_CORE_PM];
|
||||
ctrl = &power->parser->mp[DP_CTRL_PM];
|
||||
|
||||
if (!core || !ctrl)
|
||||
return -EINVAL;
|
||||
|
||||
msm_dss_put_clk(ctrl->clk_config, ctrl->num_clk);
|
||||
msm_dss_put_clk(core->clk_config, core->num_clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_power_clk_set_rate(struct dp_power_private *power,
|
||||
enum dp_pm_type module, bool enable)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dss_module_power *mp = &power->parser->mp[module];
|
||||
|
||||
if (enable) {
|
||||
rc = msm_dss_clk_set_rate(mp->clk_config, mp->num_clk);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to set clks rate.\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, enable);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to %d clks, err: %d\n", enable, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dp_power_clk_enable(struct dp_power *dp_power,
|
||||
enum dp_pm_type pm_type, bool enable)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_power_private *power;
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
if (pm_type != DP_CORE_PM && pm_type != DP_CTRL_PM) {
|
||||
DRM_ERROR("unsupported power module: %s\n",
|
||||
dp_parser_pm_name(pm_type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
if (pm_type == DP_CORE_PM && dp_power->core_clks_on) {
|
||||
DRM_DEBUG_DP("core clks already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) {
|
||||
DRM_DEBUG_DP("links clks already enabled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) {
|
||||
DRM_DEBUG_DP("Enable core clks before link clks\n");
|
||||
|
||||
rc = dp_power_clk_set_rate(power, DP_CORE_PM, enable);
|
||||
if (rc) {
|
||||
DRM_ERROR("fail to enable clks: %s. err=%d\n",
|
||||
dp_parser_pm_name(DP_CORE_PM), rc);
|
||||
return rc;
|
||||
}
|
||||
dp_power->core_clks_on = true;
|
||||
}
|
||||
}
|
||||
|
||||
rc = dp_power_clk_set_rate(power, pm_type, enable);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to '%s' clks for: %s. err=%d\n",
|
||||
enable ? "enable" : "disable",
|
||||
dp_parser_pm_name(pm_type), rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (pm_type == DP_CORE_PM)
|
||||
dp_power->core_clks_on = enable;
|
||||
else
|
||||
dp_power->link_clks_on = enable;
|
||||
|
||||
DRM_DEBUG_DP("%s clocks for %s\n",
|
||||
enable ? "enable" : "disable",
|
||||
dp_parser_pm_name(pm_type));
|
||||
DRM_DEBUG_DP("link_clks:%s core_clks:%s\n",
|
||||
dp_power->link_clks_on ? "on" : "off",
|
||||
dp_power->core_clks_on ? "on" : "off");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dp_power_client_init(struct dp_power *dp_power)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_power_private *power;
|
||||
|
||||
if (!dp_power) {
|
||||
DRM_ERROR("invalid power data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
pm_runtime_enable(&power->pdev->dev);
|
||||
|
||||
rc = dp_power_regulator_init(power);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to init regulators %d\n", rc);
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = dp_power_clk_init(power);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to init clocks %d\n", rc);
|
||||
goto error;
|
||||
}
|
||||
return 0;
|
||||
|
||||
error:
|
||||
pm_runtime_disable(&power->pdev->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void dp_power_client_deinit(struct dp_power *dp_power)
|
||||
{
|
||||
struct dp_power_private *power;
|
||||
|
||||
if (!dp_power) {
|
||||
DRM_ERROR("invalid power data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
dp_power_clk_deinit(power);
|
||||
pm_runtime_disable(&power->pdev->dev);
|
||||
|
||||
}
|
||||
|
||||
int dp_power_set_link_clk_parent(struct dp_power *dp_power)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_power_private *power;
|
||||
u32 num;
|
||||
struct dss_clk *cfg;
|
||||
char *name = "ctrl_link";
|
||||
|
||||
if (!dp_power) {
|
||||
DRM_ERROR("invalid power data\n");
|
||||
rc = -EINVAL;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
num = power->parser->mp[DP_CTRL_PM].num_clk;
|
||||
cfg = power->parser->mp[DP_CTRL_PM].clk_config;
|
||||
|
||||
while (num && strcmp(cfg->clk_name, name)) {
|
||||
num--;
|
||||
cfg++;
|
||||
}
|
||||
|
||||
exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dp_power_init(struct dp_power *dp_power, bool flip)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_power_private *power = NULL;
|
||||
|
||||
if (!dp_power) {
|
||||
DRM_ERROR("invalid power data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
pm_runtime_get_sync(&power->pdev->dev);
|
||||
rc = dp_power_regulator_enable(power);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to enable regulators, %d\n", rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
rc = dp_power_clk_enable(dp_power, DP_CORE_PM, true);
|
||||
if (rc) {
|
||||
DRM_ERROR("failed to enable DP core clocks, %d\n", rc);
|
||||
goto err_clk;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_clk:
|
||||
dp_power_regulator_disable(power);
|
||||
exit:
|
||||
pm_runtime_put_sync(&power->pdev->dev);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int dp_power_deinit(struct dp_power *dp_power)
|
||||
{
|
||||
struct dp_power_private *power;
|
||||
|
||||
power = container_of(dp_power, struct dp_power_private, dp_power);
|
||||
|
||||
dp_power_clk_enable(dp_power, DP_CORE_PM, false);
|
||||
dp_power_regulator_disable(power);
|
||||
pm_runtime_put_sync(&power->pdev->dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dp_power *dp_power_get(struct dp_parser *parser)
|
||||
{
|
||||
struct dp_power_private *power;
|
||||
struct dp_power *dp_power;
|
||||
|
||||
if (!parser) {
|
||||
DRM_ERROR("invalid input\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
power = devm_kzalloc(&parser->pdev->dev, sizeof(*power), GFP_KERNEL);
|
||||
if (!power)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
power->parser = parser;
|
||||
power->pdev = parser->pdev;
|
||||
|
||||
dp_power = &power->dp_power;
|
||||
|
||||
return dp_power;
|
||||
}
|
65
drivers/gpu/drm/msm/dp/dp_power.h
Normal file
65
drivers/gpu/drm/msm/dp/dp_power.h
Normal file
@ -0,0 +1,65 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_POWER_H_
|
||||
#define _DP_POWER_H_
|
||||
|
||||
#include "dp_parser.h"
|
||||
|
||||
/**
|
||||
* sruct dp_power - DisplayPort's power related data
|
||||
*
|
||||
* @init: initializes the regulators/core clocks/GPIOs/pinctrl
|
||||
* @deinit: turns off the regulators/core clocks/GPIOs/pinctrl
|
||||
* @clk_enable: enable/disable the DP clocks
|
||||
* @set_link_clk_parent: set the parent of DP link clock
|
||||
* @set_pixel_clk_parent: set the parent of DP pixel clock
|
||||
*/
|
||||
struct dp_power {
|
||||
bool core_clks_on;
|
||||
bool link_clks_on;
|
||||
};
|
||||
|
||||
int dp_power_init(struct dp_power *power, bool flip);
|
||||
int dp_power_deinit(struct dp_power *power);
|
||||
int dp_power_clk_enable(struct dp_power *power, enum dp_pm_type pm_type,
|
||||
bool enable);
|
||||
int dp_power_set_link_clk_parent(struct dp_power *power);
|
||||
|
||||
/**
|
||||
* dp_power_client_init() - initialize clock and regulator modules
|
||||
*
|
||||
* @power: instance of power module
|
||||
* return: 0 for success, error for failure.
|
||||
*
|
||||
* This API will configure the DisplayPort's clocks and regulator
|
||||
* modules.
|
||||
*/
|
||||
int dp_power_client_init(struct dp_power *power);
|
||||
|
||||
/**
|
||||
* dp_power_clinet_deinit() - de-initialize clock and regulator modules
|
||||
*
|
||||
* @power: instance of power module
|
||||
* return: 0 for success, error for failure.
|
||||
*
|
||||
* This API will de-initialize the DisplayPort's clocks and regulator
|
||||
* modueles.
|
||||
*/
|
||||
void dp_power_client_deinit(struct dp_power *power);
|
||||
|
||||
/**
|
||||
* dp_power_get() - configure and get the DisplayPort power module data
|
||||
*
|
||||
* @parser: instance of parser module
|
||||
* return: pointer to allocated power module data
|
||||
*
|
||||
* This API will configure the DisplayPort's power module and provides
|
||||
* methods to be called by the client to configure the power related
|
||||
* modueles.
|
||||
*/
|
||||
struct dp_power *dp_power_get(struct dp_parser *parser);
|
||||
|
||||
#endif /* _DP_POWER_H_ */
|
490
drivers/gpu/drm/msm/dp/dp_reg.h
Normal file
490
drivers/gpu/drm/msm/dp/dp_reg.h
Normal file
@ -0,0 +1,490 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_REG_H_
|
||||
#define _DP_REG_H_
|
||||
|
||||
/* DP_TX Registers */
|
||||
#define REG_DP_HW_VERSION (0x00000000)
|
||||
|
||||
#define REG_DP_SW_RESET (0x00000010)
|
||||
#define DP_SW_RESET (0x00000001)
|
||||
|
||||
#define REG_DP_PHY_CTRL (0x00000014)
|
||||
#define DP_PHY_CTRL_SW_RESET_PLL (0x00000001)
|
||||
#define DP_PHY_CTRL_SW_RESET (0x00000004)
|
||||
|
||||
#define REG_DP_CLK_CTRL (0x00000018)
|
||||
#define REG_DP_CLK_ACTIVE (0x0000001C)
|
||||
#define REG_DP_INTR_STATUS (0x00000020)
|
||||
#define REG_DP_INTR_STATUS2 (0x00000024)
|
||||
#define REG_DP_INTR_STATUS3 (0x00000028)
|
||||
|
||||
#define REG_DP_DP_HPD_CTRL (0x00000000)
|
||||
#define DP_DP_HPD_CTRL_HPD_EN (0x00000001)
|
||||
|
||||
#define REG_DP_DP_HPD_INT_STATUS (0x00000004)
|
||||
|
||||
#define REG_DP_DP_HPD_INT_ACK (0x00000008)
|
||||
#define DP_DP_HPD_PLUG_INT_ACK (0x00000001)
|
||||
#define DP_DP_IRQ_HPD_INT_ACK (0x00000002)
|
||||
#define DP_DP_HPD_REPLUG_INT_ACK (0x00000004)
|
||||
#define DP_DP_HPD_UNPLUG_INT_ACK (0x00000008)
|
||||
|
||||
#define REG_DP_DP_HPD_INT_MASK (0x0000000C)
|
||||
#define DP_DP_HPD_PLUG_INT_MASK (0x00000001)
|
||||
#define DP_DP_IRQ_HPD_INT_MASK (0x00000002)
|
||||
#define DP_DP_HPD_REPLUG_INT_MASK (0x00000004)
|
||||
#define DP_DP_HPD_UNPLUG_INT_MASK (0x00000008)
|
||||
|
||||
#define REG_DP_DP_HPD_REFTIMER (0x00000018)
|
||||
#define REG_DP_DP_HPD_EVENT_TIME_0 (0x0000001C)
|
||||
#define REG_DP_DP_HPD_EVENT_TIME_1 (0x00000020)
|
||||
|
||||
#define REG_DP_AUX_CTRL (0x00000030)
|
||||
#define DP_AUX_CTRL_ENABLE (0x00000001)
|
||||
#define DP_AUX_CTRL_RESET (0x00000002)
|
||||
|
||||
#define REG_DP_AUX_DATA (0x00000034)
|
||||
#define DP_AUX_DATA_READ (0x00000001)
|
||||
#define DP_AUX_DATA_WRITE (0x00000000)
|
||||
#define DP_AUX_DATA_OFFSET (0x00000008)
|
||||
#define DP_AUX_DATA_INDEX_OFFSET (0x00000010)
|
||||
#define DP_AUX_DATA_MASK (0x0000ff00)
|
||||
#define DP_AUX_DATA_INDEX_WRITE (0x80000000)
|
||||
|
||||
#define REG_DP_AUX_TRANS_CTRL (0x00000038)
|
||||
#define DP_AUX_TRANS_CTRL_I2C (0x00000100)
|
||||
#define DP_AUX_TRANS_CTRL_GO (0x00000200)
|
||||
#define DP_AUX_TRANS_CTRL_NO_SEND_ADDR (0x00000400)
|
||||
#define DP_AUX_TRANS_CTRL_NO_SEND_STOP (0x00000800)
|
||||
|
||||
#define REG_DP_TIMEOUT_COUNT (0x0000003C)
|
||||
#define REG_DP_AUX_LIMITS (0x00000040)
|
||||
#define REG_DP_AUX_STATUS (0x00000044)
|
||||
|
||||
#define DP_DPCD_CP_IRQ (0x201)
|
||||
#define DP_DPCD_RXSTATUS (0x69493)
|
||||
|
||||
#define DP_INTERRUPT_TRANS_NUM (0x000000A0)
|
||||
|
||||
#define REG_DP_MAINLINK_CTRL (0x00000000)
|
||||
#define DP_MAINLINK_CTRL_ENABLE (0x00000001)
|
||||
#define DP_MAINLINK_CTRL_RESET (0x00000002)
|
||||
#define DP_MAINLINK_FB_BOUNDARY_SEL (0x02000000)
|
||||
|
||||
#define REG_DP_STATE_CTRL (0x00000004)
|
||||
#define DP_STATE_CTRL_LINK_TRAINING_PATTERN1 (0x00000001)
|
||||
#define DP_STATE_CTRL_LINK_TRAINING_PATTERN2 (0x00000002)
|
||||
#define DP_STATE_CTRL_LINK_TRAINING_PATTERN3 (0x00000004)
|
||||
#define DP_STATE_CTRL_LINK_TRAINING_PATTERN4 (0x00000008)
|
||||
#define DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE (0x00000010)
|
||||
#define DP_STATE_CTRL_LINK_PRBS7 (0x00000020)
|
||||
#define DP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN (0x00000040)
|
||||
#define DP_STATE_CTRL_SEND_VIDEO (0x00000080)
|
||||
#define DP_STATE_CTRL_PUSH_IDLE (0x00000100)
|
||||
|
||||
#define REG_DP_CONFIGURATION_CTRL (0x00000008)
|
||||
#define DP_CONFIGURATION_CTRL_SYNC_ASYNC_CLK (0x00000001)
|
||||
#define DP_CONFIGURATION_CTRL_STATIC_DYNAMIC_CN (0x00000002)
|
||||
#define DP_CONFIGURATION_CTRL_P_INTERLACED (0x00000004)
|
||||
#define DP_CONFIGURATION_CTRL_INTERLACED_BTF (0x00000008)
|
||||
#define DP_CONFIGURATION_CTRL_NUM_OF_LANES (0x00000010)
|
||||
#define DP_CONFIGURATION_CTRL_ENHANCED_FRAMING (0x00000040)
|
||||
#define DP_CONFIGURATION_CTRL_SEND_VSC (0x00000080)
|
||||
#define DP_CONFIGURATION_CTRL_BPC (0x00000100)
|
||||
#define DP_CONFIGURATION_CTRL_ASSR (0x00000400)
|
||||
#define DP_CONFIGURATION_CTRL_RGB_YUV (0x00000800)
|
||||
#define DP_CONFIGURATION_CTRL_LSCLK_DIV (0x00002000)
|
||||
#define DP_CONFIGURATION_CTRL_NUM_OF_LANES_SHIFT (0x04)
|
||||
#define DP_CONFIGURATION_CTRL_BPC_SHIFT (0x08)
|
||||
#define DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT (0x0D)
|
||||
|
||||
#define REG_DP_SOFTWARE_MVID (0x00000010)
|
||||
#define REG_DP_SOFTWARE_NVID (0x00000018)
|
||||
#define REG_DP_TOTAL_HOR_VER (0x0000001C)
|
||||
#define REG_DP_START_HOR_VER_FROM_SYNC (0x00000020)
|
||||
#define REG_DP_HSYNC_VSYNC_WIDTH_POLARITY (0x00000024)
|
||||
#define REG_DP_ACTIVE_HOR_VER (0x00000028)
|
||||
|
||||
#define REG_DP_MISC1_MISC0 (0x0000002C)
|
||||
#define DP_MISC0_SYNCHRONOUS_CLK (0x00000001)
|
||||
#define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
|
||||
#define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
|
||||
|
||||
#define REG_DP_VALID_BOUNDARY (0x00000030)
|
||||
#define REG_DP_VALID_BOUNDARY_2 (0x00000034)
|
||||
|
||||
#define REG_DP_LOGICAL2PHYSICAL_LANE_MAPPING (0x00000038)
|
||||
#define LANE0_MAPPING_SHIFT (0x00000000)
|
||||
#define LANE1_MAPPING_SHIFT (0x00000002)
|
||||
#define LANE2_MAPPING_SHIFT (0x00000004)
|
||||
#define LANE3_MAPPING_SHIFT (0x00000006)
|
||||
|
||||
#define REG_DP_MAINLINK_READY (0x00000040)
|
||||
#define DP_MAINLINK_READY_FOR_VIDEO (0x00000001)
|
||||
#define DP_MAINLINK_READY_LINK_TRAINING_SHIFT (0x00000003)
|
||||
|
||||
#define REG_DP_MAINLINK_LEVELS (0x00000044)
|
||||
#define DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2 (0x00000002)
|
||||
|
||||
|
||||
#define REG_DP_TU (0x0000004C)
|
||||
|
||||
#define REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET (0x00000054)
|
||||
#define DP_HBR2_ERM_PATTERN (0x00010000)
|
||||
|
||||
#define REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG0 (0x000000C0)
|
||||
#define REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG1 (0x000000C4)
|
||||
#define REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG2 (0x000000C8)
|
||||
|
||||
#define MMSS_DP_MISC1_MISC0 (0x0000002C)
|
||||
#define MMSS_DP_AUDIO_TIMING_GEN (0x00000080)
|
||||
#define MMSS_DP_AUDIO_TIMING_RBR_32 (0x00000084)
|
||||
#define MMSS_DP_AUDIO_TIMING_HBR_32 (0x00000088)
|
||||
#define MMSS_DP_AUDIO_TIMING_RBR_44 (0x0000008C)
|
||||
#define MMSS_DP_AUDIO_TIMING_HBR_44 (0x00000090)
|
||||
#define MMSS_DP_AUDIO_TIMING_RBR_48 (0x00000094)
|
||||
#define MMSS_DP_AUDIO_TIMING_HBR_48 (0x00000098)
|
||||
|
||||
#define MMSS_DP_PSR_CRC_RG (0x00000154)
|
||||
#define MMSS_DP_PSR_CRC_B (0x00000158)
|
||||
|
||||
#define REG_DP_COMPRESSION_MODE_CTRL (0x00000180)
|
||||
|
||||
#define MMSS_DP_AUDIO_CFG (0x00000200)
|
||||
#define MMSS_DP_AUDIO_STATUS (0x00000204)
|
||||
#define MMSS_DP_AUDIO_PKT_CTRL (0x00000208)
|
||||
#define MMSS_DP_AUDIO_PKT_CTRL2 (0x0000020C)
|
||||
#define MMSS_DP_AUDIO_ACR_CTRL (0x00000210)
|
||||
#define MMSS_DP_AUDIO_CTRL_RESET (0x00000214)
|
||||
|
||||
#define MMSS_DP_SDP_CFG (0x00000228)
|
||||
#define MMSS_DP_SDP_CFG2 (0x0000022C)
|
||||
#define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
|
||||
#define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
|
||||
|
||||
#define MMSS_DP_AUDIO_STREAM_0 (0x00000240)
|
||||
#define MMSS_DP_AUDIO_STREAM_1 (0x00000244)
|
||||
|
||||
#define MMSS_DP_EXTENSION_0 (0x00000250)
|
||||
#define MMSS_DP_EXTENSION_1 (0x00000254)
|
||||
#define MMSS_DP_EXTENSION_2 (0x00000258)
|
||||
#define MMSS_DP_EXTENSION_3 (0x0000025C)
|
||||
#define MMSS_DP_EXTENSION_4 (0x00000260)
|
||||
#define MMSS_DP_EXTENSION_5 (0x00000264)
|
||||
#define MMSS_DP_EXTENSION_6 (0x00000268)
|
||||
#define MMSS_DP_EXTENSION_7 (0x0000026C)
|
||||
#define MMSS_DP_EXTENSION_8 (0x00000270)
|
||||
#define MMSS_DP_EXTENSION_9 (0x00000274)
|
||||
#define MMSS_DP_AUDIO_COPYMANAGEMENT_0 (0x00000278)
|
||||
#define MMSS_DP_AUDIO_COPYMANAGEMENT_1 (0x0000027C)
|
||||
#define MMSS_DP_AUDIO_COPYMANAGEMENT_2 (0x00000280)
|
||||
#define MMSS_DP_AUDIO_COPYMANAGEMENT_3 (0x00000284)
|
||||
#define MMSS_DP_AUDIO_COPYMANAGEMENT_4 (0x00000288)
|
||||
#define MMSS_DP_AUDIO_COPYMANAGEMENT_5 (0x0000028C)
|
||||
#define MMSS_DP_AUDIO_ISRC_0 (0x00000290)
|
||||
#define MMSS_DP_AUDIO_ISRC_1 (0x00000294)
|
||||
#define MMSS_DP_AUDIO_ISRC_2 (0x00000298)
|
||||
#define MMSS_DP_AUDIO_ISRC_3 (0x0000029C)
|
||||
#define MMSS_DP_AUDIO_ISRC_4 (0x000002A0)
|
||||
#define MMSS_DP_AUDIO_ISRC_5 (0x000002A4)
|
||||
#define MMSS_DP_AUDIO_INFOFRAME_0 (0x000002A8)
|
||||
#define MMSS_DP_AUDIO_INFOFRAME_1 (0x000002AC)
|
||||
#define MMSS_DP_AUDIO_INFOFRAME_2 (0x000002B0)
|
||||
|
||||
#define MMSS_DP_GENERIC0_0 (0x00000300)
|
||||
#define MMSS_DP_GENERIC0_1 (0x00000304)
|
||||
#define MMSS_DP_GENERIC0_2 (0x00000308)
|
||||
#define MMSS_DP_GENERIC0_3 (0x0000030C)
|
||||
#define MMSS_DP_GENERIC0_4 (0x00000310)
|
||||
#define MMSS_DP_GENERIC0_5 (0x00000314)
|
||||
#define MMSS_DP_GENERIC0_6 (0x00000318)
|
||||
#define MMSS_DP_GENERIC0_7 (0x0000031C)
|
||||
#define MMSS_DP_GENERIC0_8 (0x00000320)
|
||||
#define MMSS_DP_GENERIC0_9 (0x00000324)
|
||||
#define MMSS_DP_GENERIC1_0 (0x00000328)
|
||||
#define MMSS_DP_GENERIC1_1 (0x0000032C)
|
||||
#define MMSS_DP_GENERIC1_2 (0x00000330)
|
||||
#define MMSS_DP_GENERIC1_3 (0x00000334)
|
||||
#define MMSS_DP_GENERIC1_4 (0x00000338)
|
||||
#define MMSS_DP_GENERIC1_5 (0x0000033C)
|
||||
#define MMSS_DP_GENERIC1_6 (0x00000340)
|
||||
#define MMSS_DP_GENERIC1_7 (0x00000344)
|
||||
#define MMSS_DP_GENERIC1_8 (0x00000348)
|
||||
#define MMSS_DP_GENERIC1_9 (0x0000034C)
|
||||
|
||||
#define MMSS_DP_VSCEXT_0 (0x000002D0)
|
||||
#define MMSS_DP_VSCEXT_1 (0x000002D4)
|
||||
#define MMSS_DP_VSCEXT_2 (0x000002D8)
|
||||
#define MMSS_DP_VSCEXT_3 (0x000002DC)
|
||||
#define MMSS_DP_VSCEXT_4 (0x000002E0)
|
||||
#define MMSS_DP_VSCEXT_5 (0x000002E4)
|
||||
#define MMSS_DP_VSCEXT_6 (0x000002E8)
|
||||
#define MMSS_DP_VSCEXT_7 (0x000002EC)
|
||||
#define MMSS_DP_VSCEXT_8 (0x000002F0)
|
||||
#define MMSS_DP_VSCEXT_9 (0x000002F4)
|
||||
|
||||
#define MMSS_DP_BIST_ENABLE (0x00000000)
|
||||
#define DP_BIST_ENABLE_DPBIST_EN (0x00000001)
|
||||
|
||||
#define MMSS_DP_TIMING_ENGINE_EN (0x00000010)
|
||||
#define DP_TIMING_ENGINE_EN_EN (0x00000001)
|
||||
|
||||
#define MMSS_DP_INTF_CONFIG (0x00000014)
|
||||
#define MMSS_DP_INTF_HSYNC_CTL (0x00000018)
|
||||
#define MMSS_DP_INTF_VSYNC_PERIOD_F0 (0x0000001C)
|
||||
#define MMSS_DP_INTF_VSYNC_PERIOD_F1 (0x00000020)
|
||||
#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0 (0x00000024)
|
||||
#define MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1 (0x00000028)
|
||||
#define MMSS_INTF_DISPLAY_V_START_F0 (0x0000002C)
|
||||
#define MMSS_INTF_DISPLAY_V_START_F1 (0x00000030)
|
||||
#define MMSS_DP_INTF_DISPLAY_V_END_F0 (0x00000034)
|
||||
#define MMSS_DP_INTF_DISPLAY_V_END_F1 (0x00000038)
|
||||
#define MMSS_DP_INTF_ACTIVE_V_START_F0 (0x0000003C)
|
||||
#define MMSS_DP_INTF_ACTIVE_V_START_F1 (0x00000040)
|
||||
#define MMSS_DP_INTF_ACTIVE_V_END_F0 (0x00000044)
|
||||
#define MMSS_DP_INTF_ACTIVE_V_END_F1 (0x00000048)
|
||||
#define MMSS_DP_INTF_DISPLAY_HCTL (0x0000004C)
|
||||
#define MMSS_DP_INTF_ACTIVE_HCTL (0x00000050)
|
||||
#define MMSS_DP_INTF_POLARITY_CTL (0x00000058)
|
||||
|
||||
#define MMSS_DP_TPG_MAIN_CONTROL (0x00000060)
|
||||
#define MMSS_DP_DSC_DTO (0x0000007C)
|
||||
#define DP_TPG_CHECKERED_RECT_PATTERN (0x00000100)
|
||||
|
||||
#define MMSS_DP_TPG_VIDEO_CONFIG (0x00000064)
|
||||
#define DP_TPG_VIDEO_CONFIG_BPP_8BIT (0x00000001)
|
||||
#define DP_TPG_VIDEO_CONFIG_RGB (0x00000004)
|
||||
|
||||
#define MMSS_DP_ASYNC_FIFO_CONFIG (0x00000088)
|
||||
|
||||
/*DP PHY Register offsets */
|
||||
#define REG_DP_PHY_REVISION_ID0 (0x00000000)
|
||||
#define REG_DP_PHY_REVISION_ID1 (0x00000004)
|
||||
#define REG_DP_PHY_REVISION_ID2 (0x00000008)
|
||||
#define REG_DP_PHY_REVISION_ID3 (0x0000000C)
|
||||
|
||||
#define REG_DP_PHY_CFG (0x00000010)
|
||||
|
||||
#define REG_DP_PHY_PD_CTL (0x00000018)
|
||||
#define DP_PHY_PD_CTL_PWRDN (0x00000001)
|
||||
#define DP_PHY_PD_CTL_PSR_PWRDN (0x00000002)
|
||||
#define DP_PHY_PD_CTL_AUX_PWRDN (0x00000004)
|
||||
#define DP_PHY_PD_CTL_LANE_0_1_PWRDN (0x00000008)
|
||||
#define DP_PHY_PD_CTL_LANE_2_3_PWRDN (0x00000010)
|
||||
#define DP_PHY_PD_CTL_PLL_PWRDN (0x00000020)
|
||||
#define DP_PHY_PD_CTL_DP_CLAMP_EN (0x00000040)
|
||||
|
||||
#define REG_DP_PHY_MODE (0x0000001C)
|
||||
|
||||
#define REG_DP_PHY_AUX_CFG0 (0x00000020)
|
||||
#define REG_DP_PHY_AUX_CFG1 (0x00000024)
|
||||
#define REG_DP_PHY_AUX_CFG2 (0x00000028)
|
||||
#define REG_DP_PHY_AUX_CFG3 (0x0000002C)
|
||||
#define REG_DP_PHY_AUX_CFG4 (0x00000030)
|
||||
#define REG_DP_PHY_AUX_CFG5 (0x00000034)
|
||||
#define REG_DP_PHY_AUX_CFG6 (0x00000038)
|
||||
#define REG_DP_PHY_AUX_CFG7 (0x0000003C)
|
||||
#define REG_DP_PHY_AUX_CFG8 (0x00000040)
|
||||
#define REG_DP_PHY_AUX_CFG9 (0x00000044)
|
||||
|
||||
#define REG_DP_PHY_AUX_INTERRUPT_MASK (0x00000048)
|
||||
#define PHY_AUX_STOP_ERR_MASK (0x00000001)
|
||||
#define PHY_AUX_DEC_ERR_MASK (0x00000002)
|
||||
#define PHY_AUX_SYNC_ERR_MASK (0x00000004)
|
||||
#define PHY_AUX_ALIGN_ERR_MASK (0x00000008)
|
||||
#define PHY_AUX_REQ_ERR_MASK (0x00000010)
|
||||
|
||||
|
||||
#define REG_DP_PHY_AUX_INTERRUPT_CLEAR (0x0000004C)
|
||||
#define REG_DP_PHY_AUX_BIST_CFG (0x00000050)
|
||||
#define REG_DP_PHY_AUX_INTERRUPT_STATUS (0x000000BC)
|
||||
|
||||
#define REG_DP_PHY_VCO_DIV 0x0064
|
||||
#define REG_DP_PHY_TX0_TX1_LANE_CTL 0x006C
|
||||
#define REG_DP_PHY_TX2_TX3_LANE_CTL 0x0088
|
||||
|
||||
#define REG_DP_PHY_SPARE0 (0x00AC)
|
||||
#define DP_PHY_SPARE0_MASK (0x000F)
|
||||
#define DP_PHY_SPARE0_ORIENTATION_INFO_SHIFT (0x0004)
|
||||
|
||||
#define REG_DP_PHY_STATUS (0x00C0)
|
||||
|
||||
/* Tx registers */
|
||||
#define REG_DP_PHY_TXn_BIST_MODE_LANENO 0x0000
|
||||
#define REG_DP_PHY_TXn_CLKBUF_ENABLE 0x0008
|
||||
|
||||
#define REG_DP_PHY_TXn_TX_EMP_POST1_LVL 0x000C
|
||||
#define DP_PHY_TXn_TX_EMP_POST1_LVL_MASK 0x001F
|
||||
#define DP_PHY_TXn_TX_EMP_POST1_LVL_MUX_EN 0x0020
|
||||
|
||||
#define REG_DP_PHY_TXn_TX_DRV_LVL 0x001C
|
||||
#define DP_PHY_TXn_TX_DRV_LVL_MASK 0x001F
|
||||
#define DP_PHY_TXn_TX_DRV_LVL_MUX_EN 0x0020
|
||||
|
||||
#define REG_DP_PHY_TXn_RESET_TSYNC_EN 0x0024
|
||||
#define REG_DP_PHY_TXn_PRE_STALL_LDO_BOOST_EN 0x0028
|
||||
#define REG_DP_PHY_TXn_TX_BAND 0x002C
|
||||
#define REG_DP_PHY_TXn_SLEW_CNTL 0x0030
|
||||
#define REG_DP_PHY_TXn_INTERFACE_SELECT 0x0034
|
||||
|
||||
#define REG_DP_PHY_TXn_RES_CODE_LANE_TX 0x003C
|
||||
#define REG_DP_PHY_TXn_RES_CODE_LANE_RX 0x0040
|
||||
#define REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_TX 0x0044
|
||||
#define REG_DP_PHY_TXn_RES_CODE_LANE_OFFSET_RX 0x0048
|
||||
|
||||
#define REG_DP_PHY_TXn_DEBUG_BUS_SEL 0x0058
|
||||
#define REG_DP_PHY_TXn_TRANSCEIVER_BIAS_EN 0x005C
|
||||
#define REG_DP_PHY_TXn_HIGHZ_DRVR_EN 0x0060
|
||||
#define REG_DP_PHY_TXn_TX_POL_INV 0x0064
|
||||
#define REG_DP_PHY_TXn_PARRATE_REC_DETECT_IDLE_EN 0x0068
|
||||
|
||||
#define REG_DP_PHY_TXn_LANE_MODE_1 0x008C
|
||||
|
||||
#define REG_DP_PHY_TXn_TRAN_DRVR_EMP_EN 0x00C0
|
||||
#define REG_DP_PHY_TXn_TX_INTERFACE_MODE 0x00C4
|
||||
|
||||
#define REG_DP_PHY_TXn_VMODE_CTRL1 0x00F0
|
||||
|
||||
/* PLL register offset */
|
||||
#define QSERDES_COM_ATB_SEL1 0x0000
|
||||
#define QSERDES_COM_ATB_SEL2 0x0004
|
||||
#define QSERDES_COM_FREQ_UPDATE 0x0008
|
||||
#define QSERDES_COM_BG_TIMER 0x000C
|
||||
#define QSERDES_COM_SSC_EN_CENTER 0x0010
|
||||
#define QSERDES_COM_SSC_ADJ_PER1 0x0014
|
||||
#define QSERDES_COM_SSC_ADJ_PER2 0x0018
|
||||
#define QSERDES_COM_SSC_PER1 0x001C
|
||||
#define QSERDES_COM_SSC_PER2 0x0020
|
||||
#define QSERDES_COM_SSC_STEP_SIZE1 0x0024
|
||||
#define QSERDES_COM_SSC_STEP_SIZE2 0x0028
|
||||
#define QSERDES_COM_POST_DIV 0x002C
|
||||
#define QSERDES_COM_POST_DIV_MUX 0x0030
|
||||
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN 0x0034
|
||||
#define QSERDES_COM_BIAS_EN 0x0001
|
||||
#define QSERDES_COM_BIAS_EN_MUX 0x0002
|
||||
#define QSERDES_COM_CLKBUF_R_EN 0x0004
|
||||
#define QSERDES_COM_CLKBUF_L_EN 0x0008
|
||||
#define QSERDES_COM_EN_SYSCLK_TX_SEL 0x0010
|
||||
#define QSERDES_COM_CLKBUF_RX_DRIVE_L 0x0020
|
||||
#define QSERDES_COM_CLKBUF_RX_DRIVE_R 0x0040
|
||||
|
||||
#define QSERDES_COM_CLK_ENABLE1 0x0038
|
||||
#define QSERDES_COM_SYS_CLK_CTRL 0x003C
|
||||
#define QSERDES_COM_SYSCLK_BUF_ENABLE 0x0040
|
||||
#define QSERDES_COM_PLL_EN 0x0044
|
||||
#define QSERDES_COM_PLL_IVCO 0x0048
|
||||
#define QSERDES_COM_CMN_IETRIM 0x004C
|
||||
#define QSERDES_COM_CMN_IPTRIM 0x0050
|
||||
|
||||
#define QSERDES_COM_CP_CTRL_MODE0 0x0060
|
||||
#define QSERDES_COM_CP_CTRL_MODE1 0x0064
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE0 0x0068
|
||||
#define QSERDES_COM_PLL_RCTRL_MODE1 0x006C
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE0 0x0070
|
||||
#define QSERDES_COM_PLL_CCTRL_MODE1 0x0074
|
||||
#define QSERDES_COM_PLL_CNTRL 0x0078
|
||||
#define QSERDES_COM_BIAS_EN_CTRL_BY_PSM 0x007C
|
||||
#define QSERDES_COM_SYSCLK_EN_SEL 0x0080
|
||||
#define QSERDES_COM_CML_SYSCLK_SEL 0x0084
|
||||
#define QSERDES_COM_RESETSM_CNTRL 0x0088
|
||||
#define QSERDES_COM_RESETSM_CNTRL2 0x008C
|
||||
#define QSERDES_COM_LOCK_CMP_EN 0x0090
|
||||
#define QSERDES_COM_LOCK_CMP_CFG 0x0094
|
||||
#define QSERDES_COM_LOCK_CMP1_MODE0 0x0098
|
||||
#define QSERDES_COM_LOCK_CMP2_MODE0 0x009C
|
||||
#define QSERDES_COM_LOCK_CMP3_MODE0 0x00A0
|
||||
|
||||
#define QSERDES_COM_DEC_START_MODE0 0x00B0
|
||||
#define QSERDES_COM_DEC_START_MODE1 0x00B4
|
||||
#define QSERDES_COM_DIV_FRAC_START1_MODE0 0x00B8
|
||||
#define QSERDES_COM_DIV_FRAC_START2_MODE0 0x00BC
|
||||
#define QSERDES_COM_DIV_FRAC_START3_MODE0 0x00C0
|
||||
#define QSERDES_COM_DIV_FRAC_START1_MODE1 0x00C4
|
||||
#define QSERDES_COM_DIV_FRAC_START2_MODE1 0x00C8
|
||||
#define QSERDES_COM_DIV_FRAC_START3_MODE1 0x00CC
|
||||
#define QSERDES_COM_INTEGLOOP_INITVAL 0x00D0
|
||||
#define QSERDES_COM_INTEGLOOP_EN 0x00D4
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0 0x00D8
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0 0x00DC
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1 0x00E0
|
||||
#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1 0x00E4
|
||||
#define QSERDES_COM_VCOCAL_DEADMAN_CTRL 0x00E8
|
||||
#define QSERDES_COM_VCO_TUNE_CTRL 0x00EC
|
||||
#define QSERDES_COM_VCO_TUNE_MAP 0x00F0
|
||||
|
||||
#define QSERDES_COM_CMN_STATUS 0x0124
|
||||
#define QSERDES_COM_RESET_SM_STATUS 0x0128
|
||||
|
||||
#define QSERDES_COM_CLK_SEL 0x0138
|
||||
#define QSERDES_COM_HSCLK_SEL 0x013C
|
||||
|
||||
#define QSERDES_COM_CORECLK_DIV_MODE0 0x0148
|
||||
|
||||
#define QSERDES_COM_SW_RESET 0x0150
|
||||
#define QSERDES_COM_CORE_CLK_EN 0x0154
|
||||
#define QSERDES_COM_C_READY_STATUS 0x0158
|
||||
#define QSERDES_COM_CMN_CONFIG 0x015C
|
||||
|
||||
#define QSERDES_COM_SVS_MODE_CLK_SEL 0x0164
|
||||
|
||||
/* DP MMSS_CC registers */
|
||||
#define MMSS_DP_LINK_CMD_RCGR (0x0138)
|
||||
#define MMSS_DP_LINK_CFG_RCGR (0x013C)
|
||||
#define MMSS_DP_PIXEL_M (0x01B4)
|
||||
#define MMSS_DP_PIXEL_N (0x01B8)
|
||||
|
||||
/* DP HDCP 1.3 registers */
|
||||
#define DP_HDCP_CTRL (0x0A0)
|
||||
#define DP_HDCP_STATUS (0x0A4)
|
||||
#define DP_HDCP_SW_UPPER_AKSV (0x098)
|
||||
#define DP_HDCP_SW_LOWER_AKSV (0x09C)
|
||||
#define DP_HDCP_ENTROPY_CTRL0 (0x350)
|
||||
#define DP_HDCP_ENTROPY_CTRL1 (0x35C)
|
||||
#define DP_HDCP_SHA_STATUS (0x0C8)
|
||||
#define DP_HDCP_RCVPORT_DATA2_0 (0x0B0)
|
||||
#define DP_HDCP_RCVPORT_DATA3 (0x0A4)
|
||||
#define DP_HDCP_RCVPORT_DATA4 (0x0A8)
|
||||
#define DP_HDCP_RCVPORT_DATA5 (0x0C0)
|
||||
#define DP_HDCP_RCVPORT_DATA6 (0x0C4)
|
||||
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_CTRL (0x024)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_SHA_DATA (0x028)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA0 (0x004)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA1 (0x008)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA7 (0x00C)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA8 (0x010)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA9 (0x014)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA10 (0x018)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA11 (0x01C)
|
||||
#define HDCP_SEC_DP_TZ_HV_HLOS_HDCP_RCVPORT_DATA12 (0x020)
|
||||
|
||||
/* USB3 DP COM registers */
|
||||
#define REG_USB3_DP_COM_RESET_OVRD_CTRL (0x1C)
|
||||
#define USB3_DP_COM_OVRD_CTRL_SW_DPPHY_RESET (0x01)
|
||||
#define USB3_DP_COM_OVRD_CTRL_SW_DPPHY_RESET_MUX (0x02)
|
||||
#define USB3_DP_COM_OVRD_CTRL_SW_USB3PHY_RESET (0x04)
|
||||
#define USB3_DP_COM_OVRD_CTRL_SW_USB3PHY_RESET_MUX (0x08)
|
||||
|
||||
#define REG_USB3_DP_COM_PHY_MODE_CTRL (0x00)
|
||||
#define USB3_DP_COM_PHY_MODE_DP (0x03)
|
||||
|
||||
#define REG_USB3_DP_COM_SW_RESET (0x04)
|
||||
#define USB3_DP_COM_SW_RESET_SET (0x01)
|
||||
|
||||
#define REG_USB3_DP_COM_TYPEC_CTRL (0x10)
|
||||
#define USB3_DP_COM_TYPEC_CTRL_PORTSEL (0x01)
|
||||
#define USB3_DP_COM_TYPEC_CTRL_PORTSEL_MUX (0x02)
|
||||
|
||||
#define REG_USB3_DP_COM_SWI_CTRL (0x0c)
|
||||
|
||||
#define REG_USB3_DP_COM_POWER_DOWN_CTRL (0x08)
|
||||
#define USB3_DP_COM_POWER_DOWN_CTRL_SW_PWRDN (0x01)
|
||||
|
||||
|
||||
|
||||
#endif /* _DP_REG_H_ */
|
@ -1352,6 +1352,7 @@ static int __init msm_drm_register(void)
|
||||
msm_dsi_register();
|
||||
msm_edp_register();
|
||||
msm_hdmi_register();
|
||||
msm_dp_register();
|
||||
adreno_register();
|
||||
return platform_driver_register(&msm_platform_driver);
|
||||
}
|
||||
@ -1360,6 +1361,7 @@ static void __exit msm_drm_unregister(void)
|
||||
{
|
||||
DBG("fini");
|
||||
platform_driver_unregister(&msm_platform_driver);
|
||||
msm_dp_unregister();
|
||||
msm_hdmi_unregister();
|
||||
adreno_unregister();
|
||||
msm_edp_unregister();
|
||||
|
@ -160,6 +160,8 @@ struct msm_drm_private {
|
||||
/* DSI is shared by mdp4 and mdp5 */
|
||||
struct msm_dsi *dsi[2];
|
||||
|
||||
struct msm_dp *dp;
|
||||
|
||||
/* when we have more than one 'msm_gpu' these need to be an array: */
|
||||
struct msm_gpu *gpu;
|
||||
struct msm_file_private *lastctx;
|
||||
@ -383,6 +385,49 @@ static inline int msm_dsi_modeset_init(struct msm_dsi *msm_dsi,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DRM_MSM_DP
|
||||
int __init msm_dp_register(void);
|
||||
void __exit msm_dp_unregister(void);
|
||||
int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,
|
||||
struct drm_encoder *encoder);
|
||||
int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder);
|
||||
int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder);
|
||||
void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
|
||||
#else
|
||||
static inline int __init msm_dp_register(void)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void __exit msm_dp_unregister(void)
|
||||
{
|
||||
}
|
||||
static inline int msm_dp_modeset_init(struct msm_dp *dp_display,
|
||||
struct drm_device *dev,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int msm_dp_display_enable(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline int msm_dp_display_disable(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
static inline void msm_dp_display_mode_set(struct msm_dp *dp,
|
||||
struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void __init msm_mdp_register(void);
|
||||
void __exit msm_mdp_unregister(void);
|
||||
void __init msm_dpu_register(void);
|
||||
@ -403,8 +448,9 @@ void msm_perf_debugfs_cleanup(struct msm_drm_private *priv);
|
||||
#else
|
||||
static inline int msm_debugfs_late_init(struct drm_device *dev) { return 0; }
|
||||
__printf(3, 4)
|
||||
static inline void msm_rd_dump_submit(struct msm_rd_state *rd, struct msm_gem_submit *submit,
|
||||
const char *fmt, ...) {}
|
||||
static inline void msm_rd_dump_submit(struct msm_rd_state *rd,
|
||||
struct msm_gem_submit *submit,
|
||||
const char *fmt, ...) {}
|
||||
static inline void msm_rd_debugfs_cleanup(struct msm_drm_private *priv) {}
|
||||
static inline void msm_perf_debugfs_cleanup(struct msm_drm_private *priv) {}
|
||||
#endif
|
||||
@ -424,7 +470,8 @@ struct msm_gpu_submitqueue;
|
||||
int msm_submitqueue_init(struct drm_device *drm, struct msm_file_private *ctx);
|
||||
struct msm_gpu_submitqueue *msm_submitqueue_get(struct msm_file_private *ctx,
|
||||
u32 id);
|
||||
int msm_submitqueue_create(struct drm_device *drm, struct msm_file_private *ctx,
|
||||
int msm_submitqueue_create(struct drm_device *drm,
|
||||
struct msm_file_private *ctx,
|
||||
u32 prio, u32 flags, u32 *id);
|
||||
int msm_submitqueue_query(struct drm_device *drm, struct msm_file_private *ctx,
|
||||
struct drm_msm_submitqueue_query *args);
|
||||
|
Loading…
Reference in New Issue
Block a user