disp: msm: dp: optimize sim function handling in dp_debug

Remove edid/dpcd simulation function from dp_debug and calls to
sim bridge instead to simplify dp_debug module. Also add mst edid
support and mst hpd simulation from aux level.

Move selected mode from dp_debug module to dp_panel module to
simplify mst handling and decouple dp_debug from main dp driver.

Remove custom edid/dpcd mode from dp_panel and dp_aux module.
Remove mst connector list handling from dp_display module.

Change-Id: Ife1d2deb0e353f0a9695b7b90e5bf3459e1c81f7
Signed-off-by: Xiaowen Wu <wxiaowen@codeaurora.org>
Signed-off-by: Karim Henain <khenain@codeaurora.org>
Signed-off-by: Sudarsan Ramesh <sudarame@codeaurora.org>
This commit is contained in:
Xiaowen Wu
2020-07-16 10:47:50 -04:00
committed by Sudarsan Ramesh
parent 24d245556e
commit 67ce55687b
10 changed files with 328 additions and 821 deletions

View File

@@ -29,6 +29,7 @@ struct dp_aux_private {
struct dp_aux_bridge *aux_bridge;
struct dp_aux_bridge *sim_bridge;
bool bridge_in_transfer;
bool sim_in_transfer;
bool cmd_busy;
bool native;
@@ -43,9 +44,6 @@ struct dp_aux_private {
u32 retry_cnt;
atomic_t aborted;
u8 *dpcd;
u8 *edid;
};
#ifdef CONFIG_DYNAMIC_DEBUG
@@ -477,114 +475,6 @@ static inline bool dp_aux_is_sideband_msg(u32 address, size_t size)
(address >= 0x2000 && address + size < 0x2200);
}
static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
struct drm_dp_aux_msg *msg)
{
u32 timeout;
ssize_t ret;
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
mutex_lock(&aux->mutex);
ret = dp_aux_transfer_ready(aux, msg, false);
if (ret)
goto end;
aux->aux_error_num = DP_AUX_ERR_NONE;
if (!aux->dpcd || !aux->edid) {
DP_ERR("invalid aux/dpcd structure\n");
goto end;
}
if ((msg->address + msg->size) > SZ_4K &&
!dp_aux_is_sideband_msg(msg->address, msg->size)) {
DP_DEBUG("invalid dpcd access: addr=0x%x, size=0x%lx\n",
msg->address, msg->size);
goto address_error;
}
if (aux->native) {
mutex_lock(aux->dp_aux.access_lock);
aux->dp_aux.reg = msg->address;
aux->dp_aux.read = aux->read;
aux->dp_aux.size = msg->size;
if (!aux->read)
memcpy(aux->dpcd + msg->address,
msg->buffer, msg->size);
reinit_completion(&aux->comp);
mutex_unlock(aux->dp_aux.access_lock);
timeout = wait_for_completion_timeout(&aux->comp, HZ * 2);
if (!timeout) {
DP_ERR("%s timeout: 0x%x\n",
aux->read ? "read" : "write",
msg->address);
atomic_set(&aux->aborted, 1);
ret = -ETIMEDOUT;
goto end;
}
mutex_lock(aux->dp_aux.access_lock);
if (dp_aux_is_sideband_msg(msg->address, msg->size)) {
if (!aux->sim_bridge || !aux->sim_bridge->transfer) {
DP_ERR("no mst bridge available\n");
atomic_set(&aux->aborted, 1);
ret = -ETIMEDOUT;
goto end;
}
ret = aux->sim_bridge->transfer(aux->sim_bridge,
drm_aux, msg);
} else if (aux->read) {
memcpy(msg->buffer, aux->dpcd + msg->address,
msg->size);
}
mutex_unlock(aux->dp_aux.access_lock);
aux->aux_error_num = DP_AUX_ERR_NONE;
} else {
if (aux->read && msg->address == 0x50) {
memcpy(msg->buffer,
aux->edid + aux->offset - 16,
msg->size);
}
}
if (aux->aux_error_num == DP_AUX_ERR_NONE) {
dp_aux_hex_dump(drm_aux, msg);
if (!aux->read)
memset(msg->buffer, 0, msg->size);
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;
}
ret = msg->size;
goto end;
address_error:
memset(msg->buffer, 0, msg->size);
ret = msg->size;
end:
if (ret == -ETIMEDOUT)
aux->dp_aux.state |= DP_STATE_AUX_TIMEOUT;
aux->dp_aux.reg = 0xFFFF;
aux->dp_aux.read = true;
aux->dp_aux.size = 0;
mutex_unlock(&aux->mutex);
return ret;
}
/*
* This function does the real job to process an AUX transaction.
* It will call aux_reset() function to reset the AUX channel,
@@ -664,6 +554,28 @@ static ssize_t dp_aux_bridge_transfer(struct drm_dp_aux *drm_aux,
return size;
}
static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
struct drm_dp_aux_msg *msg)
{
struct dp_aux_private *aux = container_of(drm_aux,
struct dp_aux_private, drm_aux);
ssize_t size;
if (aux->sim_in_transfer) {
if (aux->aux_bridge && aux->aux_bridge->transfer)
size = dp_aux_bridge_transfer(drm_aux, msg);
else
size = dp_aux_transfer(drm_aux, msg);
} else {
aux->sim_in_transfer = true;
size = aux->sim_bridge->transfer(aux->sim_bridge,
drm_aux, msg);
aux->sim_in_transfer = false;
}
return size;
}
static void dp_aux_reset_phy_config_indices(struct dp_aux_cfg *aux_cfg)
{
int i = 0;
@@ -758,24 +670,8 @@ static void dp_aux_deregister(struct dp_aux *dp_aux)
drm_dp_aux_unregister(&aux->drm_aux);
}
static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
{
struct dp_aux_private *aux;
if (!dp_aux) {
DP_ERR("invalid input\n");
return;
}
aux = container_of(dp_aux, struct dp_aux_private, dp_aux);
/* make sure wait has started */
usleep_range(20, 30);
complete(&aux->comp);
}
static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
u8 *edid, u8 *dpcd, struct dp_aux_bridge *sim_bridge)
static void dp_aux_set_sim_mode(struct dp_aux *dp_aux,
struct dp_aux_bridge *sim_bridge)
{
struct dp_aux_private *aux;
@@ -788,11 +684,9 @@ static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
mutex_lock(&aux->mutex);
aux->edid = edid;
aux->dpcd = dpcd;
aux->sim_bridge = sim_bridge;
if (en) {
if (sim_bridge) {
atomic_set(&aux->aborted, 0);
aux->drm_aux.transfer = dp_aux_transfer_debug;
} else if (aux->aux_bridge && aux->aux_bridge->transfer) {
@@ -884,7 +778,6 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
aux->aux_bridge = aux_bridge;
dp_aux = &aux->dp_aux;
aux->retry_cnt = 0;
aux->dp_aux.reg = 0xFFFF;
dp_aux->isr = dp_aux_isr;
dp_aux->init = dp_aux_init;
@@ -893,7 +786,6 @@ struct dp_aux *dp_aux_get(struct device *dev, struct dp_catalog_aux *catalog,
dp_aux->drm_aux_deregister = dp_aux_deregister;
dp_aux->reconfig = dp_aux_reconfig;
dp_aux->abort = dp_aux_abort_transaction;
dp_aux->dpcd_updated = dp_aux_dpcd_updated;
dp_aux->set_sim_mode = dp_aux_set_sim_mode;
dp_aux->aux_switch = dp_aux_configure_aux_switch;

View File

@@ -36,8 +36,6 @@ enum dp_aux_error {
};
struct dp_aux {
u32 reg;
u32 size;
u32 state;
bool read;
@@ -52,8 +50,7 @@ struct dp_aux {
void (*deinit)(struct dp_aux *aux);
void (*reconfig)(struct dp_aux *aux);
void (*abort)(struct dp_aux *aux, bool abort);
void (*dpcd_updated)(struct dp_aux *aux);
void (*set_sim_mode)(struct dp_aux *aux, bool en, u8 *edid, u8 *dpcd,
void (*set_sim_mode)(struct dp_aux *aux,
struct dp_aux_bridge *sim_bridge);
int (*aux_switch)(struct dp_aux *aux, bool enable, int orientation);
};

View File

@@ -5,6 +5,8 @@
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <drm/drm_dp_mst_helper.h>
#include <drm/drm_probe_helper.h>
#include "dp_power.h"
#include "dp_catalog.h"
@@ -15,20 +17,20 @@
#include "dp_display.h"
#include "dp_pll.h"
#include "dp_hpd.h"
#include "dp_mst_sim_helper.h"
#include "dp_mst_sim.h"
#define DEBUG_NAME "drm_dp"
struct dp_debug_private {
struct dentry *root;
u8 *edid;
u32 edid_size;
u8 *dpcd;
u32 dpcd_offset;
u32 dpcd_size;
u32 mst_con_id;
u32 mst_edid_idx;
bool hotplug;
u32 sim_mode;
char exe_mode[SZ_32];
char reg_dump[SZ_32];
@@ -59,92 +61,60 @@ static int dp_debug_sim_hpd_cb(void *arg, bool hpd, bool hpd_irq)
return debug->hpd->simulate_connect(debug->hpd, hpd);
}
static int dp_debug_configure_mst_bridge(struct dp_debug_private *debug)
static int dp_debug_attach_sim_bridge(struct dp_debug_private *debug)
{
struct device_node *bridge_node;
struct dp_mst_sim_port *ports;
int i, ret;
int ret;
static const struct dp_mst_sim_port output_port = {
false, false, true, 3, false, 0x12,
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
0, 0, 2520, 2520, NULL, 0
};
if (!debug->sim_bridge) {
bridge_node = of_parse_phandle(debug->dev->of_node,
"qcom,dp-aux-bridge-sim", 0);
if (!bridge_node)
return 0;
debug->sim_bridge = of_dp_aux_find_bridge(bridge_node);
}
if (!debug->sim_bridge)
return -EINVAL;
if (debug->sim_bridge->register_hpd) {
ret = debug->sim_bridge->register_hpd(debug->sim_bridge,
dp_debug_sim_hpd_cb, debug);
if (ret)
return ret;
}
if (!debug->dp_debug.mst_port_cnt || !debug->sim_bridge->mst_ctx)
if (debug->sim_bridge)
return 0;
ports = kcalloc(debug->dp_debug.mst_port_cnt,
sizeof(*ports), GFP_KERNEL);
if (!ports)
return -ENOMEM;
ret = dp_sim_create_bridge(debug->dev, &debug->sim_bridge);
if (ret)
return ret;
for (i = 0; i < debug->dp_debug.mst_port_cnt; i++) {
memcpy(&ports[i], &output_port, sizeof(*ports));
ports[i].peer_guid[0] = i;
ports[i].edid = debug->edid;
ports[i].edid_size = debug->edid_size;
}
dp_sim_update_port_num(debug->sim_bridge, 1);
ret = dp_mst_sim_update(debug->sim_bridge->mst_ctx,
debug->dp_debug.mst_port_cnt, ports);
if (debug->sim_bridge->register_hpd)
debug->sim_bridge->register_hpd(debug->sim_bridge,
dp_debug_sim_hpd_cb, debug);
kfree(ports);
return ret;
return 0;
}
static int dp_debug_get_edid_buf(struct dp_debug_private *debug)
static void dp_debug_enable_sim_mode(struct dp_debug_private *debug,
u32 mode_mask)
{
int rc = 0;
/* return if mode is already enabled */
if ((debug->sim_mode & mode_mask) == mode_mask)
return;
if (!debug->edid) {
debug->edid = devm_kzalloc(debug->dev, SZ_256, GFP_KERNEL);
if (!debug->edid) {
rc = -ENOMEM;
goto end;
}
/* create bridge if not yet */
if (dp_debug_attach_sim_bridge(debug))
return;
debug->edid_size = SZ_256;
}
end:
return rc;
/* switch to bridge mode */
if (!debug->sim_mode)
debug->aux->set_sim_mode(debug->aux, debug->sim_bridge);
/* update sim mode */
debug->sim_mode |= mode_mask;
dp_sim_set_sim_mode(debug->sim_bridge, debug->sim_mode);
}
static int dp_debug_get_dpcd_buf(struct dp_debug_private *debug)
static void dp_debug_disable_sim_mode(struct dp_debug_private *debug,
u32 mode_mask)
{
int rc = 0;
/* return if mode is already disabled */
if (!(debug->sim_mode & mode_mask))
return;
if (!debug->dpcd) {
debug->dpcd = devm_kzalloc(debug->dev, SZ_4K, GFP_KERNEL);
if (!debug->dpcd) {
rc = -ENOMEM;
goto end;
}
/* update sim mode */
debug->sim_mode &= ~mode_mask;
dp_sim_set_sim_mode(debug->sim_bridge, debug->sim_mode);
debug->dpcd_size = SZ_4K;
}
end:
return rc;
/* switch to normal mode */
if (!debug->sim_mode)
debug->aux->set_sim_mode(debug->aux, NULL);
}
static ssize_t dp_debug_write_edid(struct file *file,
@@ -178,33 +148,13 @@ static ssize_t dp_debug_write_edid(struct file *file,
edid_size = size / char_to_nib;
buf_t = buf;
size = edid_size;
if (dp_debug_get_edid_buf(debug))
edid = kzalloc(size, GFP_KERNEL);
if (!edid)
goto bail;
if (edid_size != debug->edid_size) {
DP_DEBUG("realloc debug edid\n");
if (debug->edid) {
devm_kfree(debug->dev, debug->edid);
debug->edid = devm_kzalloc(debug->dev,
edid_size, GFP_KERNEL);
if (!debug->edid) {
rc = -ENOMEM;
goto bail;
}
debug->edid_size = edid_size;
debug->aux->set_sim_mode(debug->aux,
debug->dp_debug.sim_mode,
debug->edid, debug->dpcd,
debug->sim_bridge);
}
}
while (edid_size--) {
while (size--) {
char t[3];
int d;
@@ -216,26 +166,16 @@ static ssize_t dp_debug_write_edid(struct file *file,
goto bail;
}
if (debug->edid && (edid_buf_index < debug->edid_size))
debug->edid[edid_buf_index++] = d;
edid[edid_buf_index++] = d;
buf_t += char_to_nib;
}
edid = debug->edid;
dp_debug_enable_sim_mode(debug, DP_SIM_MODE_EDID);
dp_sim_update_port_edid(debug->sim_bridge, debug->mst_edid_idx,
edid, edid_size);
bail:
kfree(buf);
debug->panel->set_edid(debug->panel, edid, debug->edid_size);
/*
* print edid status as this code is executed
* only while running in debug mode which is manually
* triggered by a tester or a script.
*/
DP_INFO("[%s]\n", edid ? "SET" : "CLEAR");
if (dp_debug_configure_mst_bridge(debug))
DP_ERR("failed to config mst bridge\n");
kfree(edid);
mutex_unlock(&debug->lock);
return rc;
@@ -252,7 +192,6 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
ssize_t rc = count;
char offset_ch[5];
u32 offset, data_len;
const u32 dp_receiver_cap_size = 16;
if (!debug)
return -ENODEV;
@@ -284,15 +223,6 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
goto bail;
}
if (dp_debug_get_dpcd_buf(debug))
goto bail;
if (offset == 0xFFFF) {
DP_ERR("clearing dpcd\n");
memset(debug->dpcd, 0, debug->dpcd_size);
goto bail;
}
size -= 4;
if (size == 0)
goto bail;
@@ -301,7 +231,11 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
data_len = dpcd_size;
buf_t = buf + 4;
dpcd_buf_index = offset;
dpcd = kzalloc(dpcd_size, GFP_KERNEL);
if (ZERO_OR_NULL_PTR(dpcd)) {
rc = -ENOMEM;
goto bail;
}
while (dpcd_size--) {
char t[3];
@@ -315,33 +249,33 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
goto bail;
}
if (dpcd_buf_index < debug->dpcd_size)
debug->dpcd[dpcd_buf_index++] = d;
dpcd[dpcd_buf_index++] = d;
buf_t += char_to_nib;
}
dpcd = debug->dpcd;
/*
* if link training status registers are reprogramed,
* read link training status from simulator, otherwise
* read link training status from real aux channel.
*/
if (offset <= DP_LANE0_1_STATUS &&
offset + dpcd_buf_index > DP_LANE0_1_STATUS)
dp_debug_enable_sim_mode(debug,
DP_SIM_MODE_DPCD_READ | DP_SIM_MODE_LINK_TRAIN);
else
dp_debug_enable_sim_mode(debug, DP_SIM_MODE_DPCD_READ);
dp_sim_write_dpcd_reg(debug->sim_bridge,
dpcd, dpcd_buf_index, offset);
debug->dpcd_offset = offset;
debug->dpcd_size = dpcd_buf_index;
bail:
kfree(buf);
if (!dpcd || (size / char_to_nib) >= dp_receiver_cap_size ||
offset == 0xffff) {
debug->panel->set_dpcd(debug->panel, dpcd);
/*
* print dpcd status as this code is executed
* only while running in debug mode which is manually
* triggered by a tester or a script.
*/
if (!dpcd || (offset == 0xffff))
DP_INFO("[%s]\n", "CLEAR");
else
DP_INFO("[%s]\n", "SET");
}
kfree(dpcd);
mutex_unlock(&debug->lock);
debug->aux->dpcd_updated(debug->aux);
return rc;
}
@@ -353,44 +287,42 @@ static ssize_t dp_debug_read_dpcd(struct file *file,
int const buf_size = SZ_4K;
u32 offset = 0;
u32 len = 0;
bool notify = false;
u8 *dpcd;
if (!debug || !debug->aux || !debug->dpcd)
if (!debug || !debug->aux)
return -ENODEV;
mutex_lock(&debug->lock);
if (*ppos)
goto end;
return 0;
buf = kzalloc(buf_size, GFP_KERNEL);
if (!buf)
goto end;
return -ENOMEM;
len += snprintf(buf, buf_size, "0x%x", debug->aux->reg);
mutex_lock(&debug->lock);
dpcd = kzalloc(buf_size, GFP_KERNEL);
if (!dpcd)
goto bail;
if (!debug->aux->read) {
while (1) {
if (debug->aux->reg + offset >= buf_size ||
offset >= debug->aux->size)
break;
dp_sim_read_dpcd_reg(debug->sim_bridge, dpcd,
debug->dpcd_size, debug->dpcd_offset);
len += snprintf(buf + len, buf_size - len, "0x%x",
debug->dpcd[debug->aux->reg + offset++]);
}
len += snprintf(buf, buf_size, "0x%x", debug->dpcd_offset);
notify = true;
while (offset < debug->dpcd_size) {
len += snprintf(buf + len, buf_size - len, "0x%x",
dpcd[debug->dpcd_offset + offset++]);
}
kfree(dpcd);
len = min_t(size_t, count, len);
if (!copy_to_user(user_buff, buf, len))
*ppos += len;
kfree(buf);
end:
bail:
mutex_unlock(&debug->lock);
if (notify)
debug->aux->dpcd_updated(debug->aux);
kfree(buf);
return len;
}
@@ -441,6 +373,7 @@ static ssize_t dp_debug_write_edid_modes(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
struct dp_panel *panel;
char buf[SZ_32];
size_t len = 0;
int hdisplay = 0, vdisplay = 0, vrefresh = 0, aspect_ratio;
@@ -451,6 +384,8 @@ static ssize_t dp_debug_write_edid_modes(struct file *file,
if (*ppos)
goto end;
panel = debug->panel;
/* Leave room for termination char */
len = min_t(size_t, count, SZ_32 - 1);
if (copy_from_user(buf, user_buff, len))
@@ -465,15 +400,15 @@ static ssize_t dp_debug_write_edid_modes(struct file *file,
if (!hdisplay || !vdisplay || !vrefresh)
goto clear;
debug->dp_debug.debug_en = true;
debug->dp_debug.hdisplay = hdisplay;
debug->dp_debug.vdisplay = vdisplay;
debug->dp_debug.vrefresh = vrefresh;
debug->dp_debug.aspect_ratio = aspect_ratio;
panel->mode_override = true;
panel->hdisplay = hdisplay;
panel->vdisplay = vdisplay;
panel->vrefresh = vrefresh;
panel->aspect_ratio = aspect_ratio;
goto end;
clear:
DP_DEBUG("clearing debug modes\n");
debug->dp_debug.debug_en = false;
panel->mode_override = false;
end:
return len;
}
@@ -482,18 +417,21 @@ static ssize_t dp_debug_write_edid_modes_mst(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
struct dp_mst_connector *mst_connector;
struct drm_connector *connector;
struct sde_connector *sde_conn;
struct dp_panel *panel = NULL;
char buf[SZ_512];
char *read_buf;
size_t len = 0;
int hdisplay = 0, vdisplay = 0, vrefresh = 0, aspect_ratio = 0;
int con_id = 0, offset = 0, debug_en = 0;
bool in_list = false;
if (!debug)
return -ENODEV;
mutex_lock(&debug->lock);
if (*ppos)
goto end;
@@ -504,33 +442,32 @@ static ssize_t dp_debug_write_edid_modes_mst(struct file *file,
buf[len] = '\0';
read_buf = buf;
mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
while (sscanf(read_buf, "%d %d %d %d %d %d%n", &debug_en, &con_id,
&hdisplay, &vdisplay, &vrefresh, &aspect_ratio,
&offset) == 6) {
list_for_each_entry(mst_connector,
&debug->dp_debug.dp_mst_connector_list.list,
list) {
if (mst_connector->con_id == con_id) {
in_list = true;
mst_connector->debug_en = (bool) debug_en;
mst_connector->hdisplay = hdisplay;
mst_connector->vdisplay = vdisplay;
mst_connector->vrefresh = vrefresh;
mst_connector->aspect_ratio = aspect_ratio;
DP_INFO("Setting %dx%dp%d on conn %d\n",
hdisplay, vdisplay, vrefresh, con_id);
connector = drm_connector_lookup((*debug->connector)->dev,
NULL, con_id);
if (connector) {
sde_conn = to_sde_connector(connector);
panel = sde_conn->drv_panel;
if (panel && sde_conn->mst_port) {
panel->mode_override = debug_en;
panel->hdisplay = hdisplay;
panel->vdisplay = vdisplay;
panel->vrefresh = vrefresh;
panel->aspect_ratio = aspect_ratio;
} else {
DP_ERR("connector id %d is not mst\n", con_id);
}
drm_connector_put(connector);
} else {
DP_ERR("invalid connector id %d\n", con_id);
}
if (!in_list)
DP_DEBUG("dp connector id %d is invalid\n", con_id);
in_list = false;
read_buf += offset;
}
mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);
end:
mutex_unlock(&debug->lock);
return len;
}
@@ -538,17 +475,19 @@ static ssize_t dp_debug_write_mst_con_id(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
struct dp_mst_connector *mst_connector;
struct drm_connector *connector;
struct sde_connector *sde_conn;
struct drm_dp_mst_port *mst_port;
struct dp_panel *dp_panel;
char buf[SZ_32];
size_t len = 0;
int con_id = 0, status;
bool in_list = false;
const int dp_en = BIT(3), hpd_high = BIT(7), hpd_irq = BIT(8);
int vdo = dp_en | hpd_high | hpd_irq;
if (!debug)
return -ENODEV;
mutex_lock(&debug->lock);
if (*ppos)
goto end;
@@ -559,50 +498,49 @@ static ssize_t dp_debug_write_mst_con_id(struct file *file,
buf[len] = '\0';
if (sscanf(buf, "%d %d", &con_id, &status) != 2) {
len = 0;
if (sscanf(buf, "%d %d", &con_id, &status) != 2)
goto end;
}
if (!con_id)
goto clear;
/* Verify that the connector id is for a valid mst connector. */
mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
list_for_each_entry(mst_connector,
&debug->dp_debug.dp_mst_connector_list.list, list) {
if (mst_connector->con_id == con_id) {
in_list = true;
debug->mst_con_id = con_id;
mst_connector->state = status;
break;
}
}
mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);
if (!in_list && status != connector_status_connected) {
connector = drm_connector_lookup((*debug->connector)->dev,
NULL, con_id);
if (!connector) {
DP_ERR("invalid connector id %u\n", con_id);
goto end;
}
if (status == connector_status_unknown)
goto end;
sde_conn = to_sde_connector(connector);
debug->dp_debug.mst_hpd_sim = true;
if (status == connector_status_connected) {
DP_INFO("plug mst connector\n", con_id, status);
debug->dp_debug.mst_sim_add_con = true;
} else {
DP_INFO("unplug mst connector %d\n", con_id, status);
if (!sde_conn->drv_panel || !sde_conn->mst_port) {
DP_ERR("invalid connector state %d\n", con_id);
goto out;
}
debug->hpd->simulate_attention(debug->hpd, vdo);
debug->mst_con_id = con_id;
if (status == connector_status_unknown)
goto out;
mst_port = sde_conn->mst_port;
dp_panel = sde_conn->drv_panel;
if (debug->dp_debug.sim_mode) {
dp_sim_update_port_status(debug->sim_bridge,
mst_port->port_num, status);
} else {
dp_panel->mst_hide = (status == connector_status_disconnected);
drm_kms_helper_hotplug_event(connector->dev);
}
out:
drm_connector_put(connector);
goto end;
clear:
DP_DEBUG("clearing mst_con_id\n");
debug->mst_con_id = 0;
end:
mutex_unlock(&debug->lock);
return len;
}
@@ -626,7 +564,6 @@ static ssize_t dp_debug_write_mst_con_add(struct file *file,
if (copy_from_user(buf, user_buff, len))
goto end;
debug->dp_debug.mst_hpd_sim = true;
debug->dp_debug.mst_sim_add_con = true;
debug->hpd->simulate_attention(debug->hpd, vdo);
end:
@@ -637,7 +574,8 @@ static ssize_t dp_debug_write_mst_con_remove(struct file *file,
const char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
struct dp_mst_connector *mst_connector;
struct drm_connector_list_iter conn_iter;
struct drm_connector *connector;
char buf[SZ_32];
size_t len = 0;
int con_id = 0;
@@ -666,23 +604,20 @@ static ssize_t dp_debug_write_mst_con_remove(struct file *file,
if (!con_id)
goto end;
/* Verify that the connector id is for a valid mst connector. */
mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
list_for_each_entry(mst_connector,
&debug->dp_debug.dp_mst_connector_list.list, list) {
if (mst_connector->con_id == con_id) {
drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
if (connector->base.id == con_id) {
in_list = true;
break;
}
}
mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);
drm_connector_list_iter_end(&conn_iter);
if (!in_list) {
DRM_ERROR("invalid connector id %u\n", con_id);
goto end;
}
debug->dp_debug.mst_hpd_sim = true;
debug->dp_debug.mst_sim_remove_con = true;
debug->dp_debug.mst_sim_remove_con_id = con_id;
debug->hpd->simulate_attention(debug->hpd, vdo);
@@ -882,6 +817,8 @@ static ssize_t dp_debug_mst_sideband_mode_write(struct file *file,
if (!debug)
return -ENODEV;
mutex_lock(&debug->lock);
/* Leave room for termination char */
len = min_t(size_t, count, SZ_8 - 1);
if (copy_from_user(buf, user_buff, len))
@@ -891,22 +828,29 @@ static ssize_t dp_debug_mst_sideband_mode_write(struct file *file,
if (sscanf(buf, "%d %u", &mst_sideband_mode, &mst_port_cnt) != 2) {
DP_ERR("invalid input\n");
return -EINVAL;
goto bail;
}
if (mst_port_cnt > DP_MST_SIM_MAX_PORTS) {
DP_ERR("port cnt:%d exceeding max:%d\n", mst_port_cnt,
DP_MST_SIM_MAX_PORTS);
return -EINVAL;
}
if (!mst_port_cnt)
mst_port_cnt = 1;
debug->mst_edid_idx = 0;
if (mst_sideband_mode)
dp_debug_disable_sim_mode(debug, DP_SIM_MODE_MST);
else
dp_debug_enable_sim_mode(debug, DP_SIM_MODE_MST);
dp_sim_update_port_num(debug->sim_bridge, mst_port_cnt);
buf[0] = !mst_sideband_mode;
dp_sim_write_dpcd_reg(debug->sim_bridge, buf, 1, DP_MSTM_CAP);
debug->dp_debug.mst_port_cnt = mst_port_cnt;
DP_DEBUG("mst_sideband_mode: %d port_cnt:%d\n",
mst_sideband_mode, mst_port_cnt);
if (dp_debug_configure_mst_bridge(debug))
DP_ERR("failed to config mst bridge\n");
bail:
mutex_unlock(&debug->lock);
return count;
}
@@ -1129,50 +1073,29 @@ static ssize_t dp_debug_read_edid_modes_mst(struct file *file,
char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
struct dp_mst_connector *mst_connector;
char *buf;
u32 len = 0, ret = 0, max_size = SZ_4K;
int rc = 0;
struct drm_connector *connector;
struct drm_display_mode *mode;
bool in_list = false;
if (!debug) {
DP_ERR("invalid data\n");
rc = -ENODEV;
goto error;
}
mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
list_for_each_entry(mst_connector,
&debug->dp_debug.dp_mst_connector_list.list, list) {
if (mst_connector->con_id == debug->mst_con_id) {
connector = mst_connector->conn;
in_list = true;
}
}
mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);
if (!in_list) {
DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
rc = -EINVAL;
goto error;
}
if (!connector) {
DP_ERR("connector is NULL\n");
rc = -EINVAL;
goto error;
return -ENODEV;
}
if (*ppos)
goto error;
return 0;
connector = drm_connector_lookup((*debug->connector)->dev,
NULL, debug->mst_con_id);
if (!connector) {
DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
return 0;
}
buf = kzalloc(SZ_4K, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto error;
}
if (!buf)
goto clean;
mutex_lock(&connector->dev->mode_config.mutex);
list_for_each_entry(mode, &connector->modes, head) {
@@ -1188,17 +1111,15 @@ static ssize_t dp_debug_read_edid_modes_mst(struct file *file,
len = min_t(size_t, count, len);
if (copy_to_user(user_buff, buf, len)) {
kfree(buf);
rc = -EFAULT;
goto error;
len = -EFAULT;
goto clean;
}
*ppos += len;
clean:
kfree(buf);
drm_connector_put(connector);
return len;
error:
return rc;
}
static ssize_t dp_debug_read_mst_con_id(struct file *file,
@@ -1246,11 +1167,13 @@ static ssize_t dp_debug_read_mst_conn_info(struct file *file,
char __user *user_buff, size_t count, loff_t *ppos)
{
struct dp_debug_private *debug = file->private_data;
struct dp_mst_connector *mst_connector;
struct drm_connector_list_iter conn_iter;
struct drm_connector *connector;
struct sde_connector *sde_conn;
struct dp_display *display;
char *buf;
u32 len = 0, ret = 0, max_size = SZ_4K;
int rc = 0;
struct drm_connector *connector;
if (!debug) {
DP_ERR("invalid data\n");
@@ -1267,21 +1190,13 @@ static ssize_t dp_debug_read_mst_conn_info(struct file *file,
goto error;
}
mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
list_for_each_entry(mst_connector,
&debug->dp_debug.dp_mst_connector_list.list, list) {
/* Do not print info for head node */
if (mst_connector->con_id == -1)
drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
sde_conn = to_sde_connector(connector);
display = sde_conn->display;
if (!sde_conn->mst_port ||
display->base_connector != (*debug->connector))
continue;
connector = mst_connector->conn;
if (!connector) {
DP_ERR("connector for id %d is NULL\n",
mst_connector->con_id);
continue;
}
ret = scnprintf(buf + len, max_size,
"conn name:%s, conn id:%d state:%d\n",
connector->name, connector->base.id,
@@ -1289,7 +1204,7 @@ static ssize_t dp_debug_read_mst_conn_info(struct file *file,
if (dp_debug_check_buffer_overflow(ret, &max_size, &len))
break;
}
mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);
drm_connector_list_iter_end(&conn_iter);
len = min_t(size_t, count, len);
if (copy_to_user(user_buff, buf, len)) {
@@ -1643,7 +1558,7 @@ static ssize_t dp_debug_read_hdr_mst(struct file *file,
struct dp_debug_private *debug = file->private_data;
char *buf = NULL;
u32 len = 0, max_size = SZ_4K;
struct dp_mst_connector *mst_connector;
struct drm_connector_list_iter conn_iter;
struct drm_connector *connector;
bool in_list = false;
@@ -1652,15 +1567,14 @@ static ssize_t dp_debug_read_hdr_mst(struct file *file,
return -ENODEV;
}
mutex_lock(&debug->dp_debug.dp_mst_connector_list.lock);
list_for_each_entry(mst_connector,
&debug->dp_debug.dp_mst_connector_list.list, list) {
if (mst_connector->con_id == debug->mst_con_id) {
connector = mst_connector->conn;
drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
if (connector->base.id == debug->mst_con_id) {
in_list = true;
break;
}
}
mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock);
drm_connector_list_iter_end(&conn_iter);
if (!in_list) {
DP_ERR("connector %u not in mst list\n", debug->mst_con_id);
@@ -1699,24 +1613,15 @@ static ssize_t dp_debug_read_hdr_mst(struct file *file,
static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
{
struct drm_connector_list_iter conn_iter;
struct drm_connector *connector;
struct sde_connector *sde_conn;
struct dp_display *display;
struct dp_panel *panel;
if (sim) {
if (dp_debug_get_edid_buf(debug))
return;
if (dp_debug_get_dpcd_buf(debug)) {
devm_kfree(debug->dev, debug->edid);
debug->edid = NULL;
return;
}
if (dp_debug_configure_mst_bridge(debug))
DP_ERR("failed to config mst bridge\n");
debug->dp_debug.mst_hpd_sim = true;
debug->dp_debug.sim_mode = true;
debug->aux->set_sim_mode(debug->aux, true,
debug->edid, debug->dpcd, debug->sim_bridge);
debug->ctrl->set_sim_mode(debug->ctrl, true);
dp_debug_enable_sim_mode(debug, DP_SIM_MODE_ALL);
} else {
if (debug->hotplug) {
DP_WARN("sim mode off before hotplug disconnect\n");
@@ -1726,23 +1631,24 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
debug->aux->abort(debug->aux, true);
debug->ctrl->abort(debug->ctrl, true);
debug->aux->set_sim_mode(debug->aux, false, NULL, NULL, NULL);
debug->ctrl->set_sim_mode(debug->ctrl, false);
debug->dp_debug.sim_mode = false;
debug->dp_debug.mst_hpd_sim = false;
debug->panel->set_edid(debug->panel, 0, 0);
if (debug->edid) {
devm_kfree(debug->dev, debug->edid);
debug->edid = NULL;
}
debug->mst_edid_idx = 0;
dp_debug_disable_sim_mode(debug, DP_SIM_MODE_ALL);
}
debug->panel->set_dpcd(debug->panel, 0);
if (debug->dpcd) {
devm_kfree(debug->dev, debug->dpcd);
debug->dpcd = NULL;
/* clear override settings in panel */
drm_connector_list_iter_begin((*debug->connector)->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
sde_conn = to_sde_connector(connector);
display = sde_conn->display;
if (display->base_connector == (*debug->connector)) {
panel = sde_conn->drv_panel;
panel->mode_override = false;
panel->mst_hide = false;
}
}
drm_connector_list_iter_end(&conn_iter);
/*
* print simulation status as this code is executed
@@ -2067,6 +1973,8 @@ static int dp_debug_init_mst(struct dp_debug_private *debug, struct dentry *dir)
return rc;
}
debugfs_create_u32("mst_edid_idx", 0644, dir, &debug->mst_edid_idx);
return rc;
}
@@ -2485,18 +2393,6 @@ error:
return rc;
}
u8 *dp_debug_get_edid(struct dp_debug *dp_debug)
{
struct dp_debug_private *debug;
if (!dp_debug)
return NULL;
debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
return debug->edid;
}
static void dp_debug_abort(struct dp_debug *dp_debug)
{
struct dp_debug_private *debug;
@@ -2544,7 +2440,6 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
goto error;
}
debug->dp_debug.debug_en = false;
debug->hpd = in->hpd;
debug->link = in->link;
debug->panel = in->panel;
@@ -2558,9 +2453,6 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
debug->display = in->display;
dp_debug = &debug->dp_debug;
dp_debug->vdisplay = 0;
dp_debug->hdisplay = 0;
dp_debug->vrefresh = 0;
mutex_init(&debug->lock);
@@ -2571,21 +2463,9 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
}
debug->aux->access_lock = &debug->lock;
dp_debug->get_edid = dp_debug_get_edid;
dp_debug->abort = dp_debug_abort;
dp_debug->set_mst_con = dp_debug_set_mst_con;
INIT_LIST_HEAD(&dp_debug->dp_mst_connector_list.list);
/*
* Do not associate the head of the list with any connector in order to
* maintain backwards compatibility with the SST use case.
*/
dp_debug->dp_mst_connector_list.con_id = -1;
dp_debug->dp_mst_connector_list.conn = NULL;
dp_debug->dp_mst_connector_list.debug_en = false;
mutex_init(&dp_debug->dp_mst_connector_list.lock);
dp_debug->max_pclk_khz = debug->parser->max_pclk_khz;
return dp_debug;
@@ -2604,6 +2484,9 @@ static int dp_debug_deinit(struct dp_debug *dp_debug)
debugfs_remove_recursive(debug->root);
if (debug->sim_bridge)
dp_sim_destroy_bridge(debug->sim_bridge);
return 0;
}
@@ -2618,14 +2501,7 @@ void dp_debug_put(struct dp_debug *dp_debug)
dp_debug_deinit(dp_debug);
mutex_destroy(&dp_debug->dp_mst_connector_list.lock);
mutex_destroy(&debug->lock);
if (debug->edid)
devm_kfree(debug->dev, debug->edid);
if (debug->dpcd)
devm_kfree(debug->dev, debug->dpcd);
devm_kfree(debug->dev, debug);
}

View File

@@ -48,57 +48,39 @@
/**
* struct dp_debug
* @debug_en: specifies whether debug mode enabled
* @sim_mode: specifies whether sim mode enabled
* @psm_enabled: specifies whether psm enabled
* @hdcp_disabled: specifies if hdcp is disabled
* @hdcp_wait_sink_sync: used to wait for sink synchronization before HDCP auth
* @aspect_ratio: used to filter out aspect_ratio value
* @vdisplay: used to filter out vdisplay value
* @hdisplay: used to filter out hdisplay value
* @vrefresh: used to filter out vrefresh value
* @tpg_state: specifies whether tpg feature is enabled
* @max_pclk_khz: max pclk supported
* @force_encryption: enable/disable forced encryption for HDCP 2.2
* @skip_uevent: skip hotplug uevent to the user space
* @hdcp_status: string holding hdcp status information
* @dp_mst_connector_list: list containing all dp mst connectors
* @mst_hpd_sim: specifies whether simulated hpd enabled
* @mst_sim_add_con: specifies whether new sim connector is to be added
* @mst_sim_remove_con: specifies whether sim connector is to be removed
* @mst_sim_remove_con_id: specifies id of sim connector to be removed
* @mst_port_cnt: number of mst ports to be added during hpd
* @connect_notification_delay_ms: time (in ms) to wait for any attention
* messages before sending the connect notification uevent
* @disconnect_delay_ms: time (in ms) to wait before turning off the mainlink
* in response to HPD low of cable disconnect event
*/
struct dp_debug {
bool debug_en;
bool sim_mode;
bool psm_enabled;
bool hdcp_disabled;
bool hdcp_wait_sink_sync;
int aspect_ratio;
int vdisplay;
int hdisplay;
int vrefresh;
bool tpg_state;
u32 max_pclk_khz;
bool force_encryption;
bool skip_uevent;
char hdcp_status[SZ_128];
struct dp_mst_connector dp_mst_connector_list;
bool mst_hpd_sim;
bool mst_sim_add_con;
bool mst_sim_remove_con;
int mst_sim_remove_con_id;
u32 mst_port_cnt;
unsigned long connect_notification_delay_ms;
u32 disconnect_delay_ms;
struct dp_mst_connector mst_connector_cache;
u8 *(*get_edid)(struct dp_debug *dp_debug);
void (*abort)(struct dp_debug *dp_debug);
void (*set_mst_con)(struct dp_debug *dp_debug, int con_id);
};

View File

@@ -1599,7 +1599,7 @@ static void dp_display_attention_work(struct work_struct *work)
goto exit;
}
if (dp->debug->mst_hpd_sim || !dp_display_state_is(DP_STATE_READY)) {
if (!dp_display_state_is(DP_STATE_READY)) {
mutex_unlock(&dp->session_lock);
goto mst_attention;
}
@@ -1740,8 +1740,7 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
return 0;
}
if ((dp->hpd->hpd_irq && dp_display_state_is(DP_STATE_READY)) ||
dp->debug->mst_hpd_sim) {
if (dp->hpd->hpd_irq && dp_display_state_is(DP_STATE_READY)) {
queue_work(dp->wq, &dp->attention_work);
complete_all(&dp->attention_comp);
} else if (dp->process_hpd_connect ||
@@ -2796,72 +2795,6 @@ static int dp_display_validate_topology(struct dp_display_private *dp,
return 0;
}
static void dp_display_validate_mst_connectors(struct dp_debug *debug,
struct dp_panel *dp_panel, struct drm_display_mode *mode,
enum drm_mode_status *mode_status, bool *use_default)
{
struct dp_mst_connector *mst_connector;
int hdis, vdis, vref, ar, _hdis, _vdis, _vref, _ar;
bool in_list = false;
/*
* If the connector exists in the mst connector list and if debug is
* enabled for that connector, use the mst connector settings from the
* list for validation. Otherwise, use non-mst default settings.
*/
mutex_lock(&debug->dp_mst_connector_list.lock);
if (list_empty(&debug->dp_mst_connector_list.list)) {
mutex_unlock(&debug->dp_mst_connector_list.lock);
*use_default = true;
return;
}
list_for_each_entry(mst_connector, &debug->dp_mst_connector_list.list,
list) {
if (mst_connector->con_id != dp_panel->connector->base.id)
continue;
in_list = true;
if (!mst_connector->debug_en) {
mutex_unlock(&debug->dp_mst_connector_list.lock);
*use_default = false;
*mode_status = MODE_OK;
return;
}
hdis = mst_connector->hdisplay;
vdis = mst_connector->vdisplay;
vref = mst_connector->vrefresh;
ar = mst_connector->aspect_ratio;
_hdis = mode->hdisplay;
_vdis = mode->vdisplay;
_vref = drm_mode_vrefresh(mode);
_ar = mode->picture_aspect_ratio;
if (hdis == _hdis && vdis == _vdis && vref == _vref &&
ar == _ar) {
mutex_unlock(&debug->dp_mst_connector_list.lock);
*use_default = false;
*mode_status = MODE_OK;
return;
}
break;
}
mutex_unlock(&debug->dp_mst_connector_list.lock);
if (in_list) {
*use_default = false;
return;
}
*use_default = true;
}
static enum drm_mode_status dp_display_validate_mode(
struct dp_display *dp_display,
void *panel, struct drm_display_mode *mode,
@@ -2873,7 +2806,6 @@ static enum drm_mode_status dp_display_validate_mode(
enum drm_mode_status mode_status = MODE_BAD;
struct dp_display_mode dp_mode;
int rc = 0;
bool use_default = true;
if (!dp_display || !mode || !panel ||
!avail_res || !avail_res->max_mixer_width) {
@@ -2919,17 +2851,6 @@ static enum drm_mode_status dp_display_validate_mode(
if (rc)
goto end;
dp_display_validate_mst_connectors(debug, dp_panel, mode, &mode_status,
&use_default);
if (!use_default)
goto end;
if (debug->debug_en && (mode->hdisplay != debug->hdisplay ||
mode->vdisplay != debug->vdisplay ||
drm_mode_vrefresh(mode) != debug->vrefresh ||
mode->picture_aspect_ratio != debug->aspect_ratio))
goto end;
mode_status = MODE_OK;
end:
mutex_unlock(&dp->session_lock);
@@ -3281,8 +3202,6 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display,
struct dp_panel_in panel_in;
struct dp_panel *dp_panel;
struct dp_display_private *dp;
struct dp_mst_connector *mst_connector;
struct dp_mst_connector *cached_connector;
if (!dp_display || !connector) {
DP_ERR("invalid input\n");
@@ -3326,38 +3245,6 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display,
DP_MST_DEBUG("dp mst connector installed. conn:%d\n",
connector->base.id);
mutex_lock(&dp->debug->dp_mst_connector_list.lock);
mst_connector = kmalloc(sizeof(struct dp_mst_connector),
GFP_KERNEL);
if (!mst_connector) {
mutex_unlock(&dp->debug->dp_mst_connector_list.lock);
rc = -ENOMEM;
goto end;
}
cached_connector = &dp->debug->mst_connector_cache;
if (cached_connector->debug_en) {
mst_connector->debug_en = true;
mst_connector->hdisplay = cached_connector->hdisplay;
mst_connector->vdisplay = cached_connector->vdisplay;
mst_connector->vrefresh = cached_connector->vrefresh;
mst_connector->aspect_ratio = cached_connector->aspect_ratio;
memset(cached_connector, 0, sizeof(*cached_connector));
dp->debug->set_mst_con(dp->debug, connector->base.id);
} else {
mst_connector->debug_en = false;
}
mst_connector->conn = connector;
mst_connector->con_id = connector->base.id;
mst_connector->state = connector_status_unknown;
INIT_LIST_HEAD(&mst_connector->list);
list_add(&mst_connector->list,
&dp->debug->dp_mst_connector_list.list);
mutex_unlock(&dp->debug->dp_mst_connector_list.lock);
end:
mutex_unlock(&dp->session_lock);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state, rc);
@@ -3372,7 +3259,6 @@ static int dp_display_mst_connector_uninstall(struct dp_display *dp_display,
struct sde_connector *sde_conn;
struct dp_panel *dp_panel;
struct dp_display_private *dp;
struct dp_mst_connector *con_to_remove, *temp_con;
if (!dp_display || !connector) {
DP_ERR("invalid input\n");
@@ -3404,64 +3290,12 @@ static int dp_display_mst_connector_uninstall(struct dp_display *dp_display,
DP_MST_DEBUG("dp mst connector uninstalled. conn:%d\n",
connector->base.id);
mutex_lock(&dp->debug->dp_mst_connector_list.lock);
list_for_each_entry_safe(con_to_remove, temp_con,
&dp->debug->dp_mst_connector_list.list, list) {
if (con_to_remove->conn == connector) {
/*
* cache any debug info if enabled that can be applied
* on new connectors.
*/
if (con_to_remove->debug_en)
memcpy(&dp->debug->mst_connector_cache,
con_to_remove,
sizeof(*con_to_remove));
list_del(&con_to_remove->list);
kfree(con_to_remove);
}
}
mutex_unlock(&dp->debug->dp_mst_connector_list.lock);
mutex_unlock(&dp->session_lock);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, dp->state);
return rc;
}
static int dp_display_mst_get_connector_info(struct dp_display *dp_display,
struct drm_connector *connector,
struct dp_mst_connector *mst_conn)
{
struct dp_display_private *dp;
struct dp_mst_connector *conn, *temp_conn;
if (!connector || !mst_conn) {
DP_ERR("invalid input\n");
return -EINVAL;
}
dp = container_of(dp_display, struct dp_display_private, dp_display);
mutex_lock(&dp->session_lock);
if (!dp->mst.drm_registered) {
DP_DEBUG("drm mst not registered\n");
mutex_unlock(&dp->session_lock);
return -EPERM;
}
mutex_lock(&dp->debug->dp_mst_connector_list.lock);
list_for_each_entry_safe(conn, temp_conn,
&dp->debug->dp_mst_connector_list.list, list) {
if (conn->con_id == connector->base.id)
memcpy(mst_conn, conn, sizeof(*mst_conn));
}
mutex_unlock(&dp->debug->dp_mst_connector_list.lock);
mutex_unlock(&dp->session_lock);
return 0;
}
static int dp_display_mst_connector_update_edid(struct dp_display *dp_display,
struct drm_connector *connector,
struct edid *edid)
@@ -3710,8 +3544,6 @@ static int dp_display_probe(struct platform_device *pdev)
g_dp_display->set_stream_info = dp_display_set_stream_info;
g_dp_display->update_pps = dp_display_update_pps;
g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
g_dp_display->mst_get_connector_info =
dp_display_mst_get_connector_info;
g_dp_display->mst_get_fixed_topology_port =
dp_display_mst_get_fixed_topology_port;
g_dp_display->wakeup_phy_layer =

View File

@@ -11,7 +11,6 @@
#include "dp_panel.h"
#define DP_MST_SIM_MAX_PORTS 8
enum dp_drv_state {
PM_DEFAULT,
@@ -38,19 +37,6 @@ struct dp_mst_caps {
struct drm_dp_aux *drm_aux;
};
struct dp_mst_connector {
bool debug_en;
int con_id;
int hdisplay;
int vdisplay;
int vrefresh;
int aspect_ratio;
struct drm_connector *conn;
struct mutex lock;
struct list_head list;
enum drm_connector_status state;
};
struct dp_display {
struct drm_device *drm_dev;
struct dp_bridge *bridge;
@@ -100,9 +86,6 @@ struct dp_display {
struct edid *edid);
int (*mst_connector_update_link_info)(struct dp_display *dp_display,
struct drm_connector *connector);
int (*mst_get_connector_info)(struct dp_display *dp_display,
struct drm_connector *connector,
struct dp_mst_connector *mst_conn);
int (*mst_get_fixed_topology_port)(struct dp_display *dp_display,
u32 strm_id, u32 *port_num);
int (*get_mst_caps)(struct dp_display *dp_display,

View File

@@ -654,10 +654,11 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode, void *display,
const struct msm_resource_caps_info *avail_res)
{
int rc = 0;
int rc = 0, vrefresh;
struct dp_display *dp_disp;
struct sde_connector *sde_conn;
struct msm_resource_caps_info avail_dp_res;
struct dp_panel *dp_panel;
if (!mode || !display || !connector) {
DP_ERR("invalid params\n");
@@ -671,6 +672,9 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
}
dp_disp = display;
dp_panel = sde_conn->drv_panel;
vrefresh = drm_mode_vrefresh(mode);
rc = dp_disp->get_available_dp_resources(dp_disp, avail_res,
&avail_dp_res);
@@ -679,6 +683,12 @@ enum drm_mode_status dp_connector_mode_valid(struct drm_connector *connector,
return MODE_ERROR;
}
if (dp_panel->mode_override && (mode->hdisplay != dp_panel->hdisplay ||
mode->vdisplay != dp_panel->vdisplay ||
vrefresh != dp_panel->vrefresh ||
mode->picture_aspect_ratio != dp_panel->aspect_ratio))
return MODE_BAD;
return dp_disp->validate_mode(dp_disp, sde_conn->drv_panel,
mode, &avail_dp_res);
}

View File

@@ -896,25 +896,27 @@ dp_mst_connector_detect(struct drm_connector *connector, bool force,
struct sde_connector *c_conn = to_sde_connector(connector);
struct dp_display *dp_display = c_conn->display;
struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
enum drm_connector_status status;
struct dp_mst_connector mst_conn;
struct dp_panel *dp_panel;
struct drm_modeset_acquire_ctx ctx;
enum drm_connector_status status;
DP_MST_DEBUG("enter:\n");
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
if (!c_conn->drv_panel || !c_conn->mst_port) {
DP_DEBUG("conn %d is invalid\n");
return connector_status_disconnected;
}
dp_panel = c_conn->drv_panel;
if (dp_panel->mst_hide)
return connector_status_disconnected;
drm_modeset_acquire_init(&ctx, 0);
status = mst->mst_fw_cbs->detect_port_ctx(connector,
&ctx, &mst->mst_mgr,
c_conn->mst_port);
memset(&mst_conn, 0, sizeof(mst_conn));
dp_display->mst_get_connector_info(dp_display, connector, &mst_conn);
if (mst_conn.conn == connector &&
mst_conn.state != connector_status_unknown) {
status = mst_conn.state;
}
&ctx, &mst->mst_mgr, c_conn->mst_port);
DP_MST_INFO("conn:%d status:%d\n", connector->base.id, status);
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_EXIT, connector->base.id, status);
@@ -989,10 +991,11 @@ enum drm_mode_status dp_mst_connector_mode_valid(
struct sde_connector *c_conn;
struct drm_dp_mst_port *mst_port;
struct dp_display_mode dp_mode;
struct dp_panel *dp_panel;
uint16_t full_pbn, required_pbn;
int available_slots, required_slots;
struct dp_mst_bridge_state *dp_bridge_state;
int i, slots_in_use = 0, active_enc_cnt = 0;
int i, vrefresh, slots_in_use = 0, active_enc_cnt = 0;
const u32 tot_slots = 63;
if (!connector || !mode || !display) {
@@ -1003,6 +1006,18 @@ enum drm_mode_status dp_mst_connector_mode_valid(
mst = dp_display->dp_mst_prv_info;
c_conn = to_sde_connector(connector);
mst_port = c_conn->mst_port;
dp_panel = c_conn->drv_panel;
if (!dp_panel || !mst_port)
return MODE_ERROR;
vrefresh = drm_mode_vrefresh(mode);
if (dp_panel->mode_override && (mode->hdisplay != dp_panel->hdisplay ||
mode->vdisplay != dp_panel->vdisplay ||
vrefresh != dp_panel->vrefresh ||
mode->picture_aspect_ratio != dp_panel->aspect_ratio))
return MODE_BAD;
/* dp bridge state is protected by drm_mode_config.connection_mutex */
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
@@ -1036,7 +1051,7 @@ enum drm_mode_status dp_mst_connector_mode_valid(
return MODE_BAD;
}
return dp_connector_mode_valid(connector, mode, display, avail_res);
return dp_display->validate_mode(dp_display, dp_panel, mode, avail_res);
}
int dp_mst_connector_get_info(struct drm_connector *connector,

View File

@@ -69,8 +69,6 @@ struct dp_panel_private {
struct dp_link *link;
struct dp_parser *parser;
struct dp_catalog_panel *catalog;
bool custom_edid;
bool custom_dpcd;
bool panel_on;
bool vsc_supported;
bool vscext_supported;
@@ -1489,11 +1487,6 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
panel->vscext_supported = false;
panel->vscext_chaining_supported = false;
if (panel->custom_dpcd) {
DP_DEBUG("skip dpcd read in debug mode\n");
goto skip_dpcd_read;
}
rlen = drm_dp_dpcd_read(drm_aux, DP_TRAINING_AUX_RD_INTERVAL, &temp, 1);
if (rlen != 1) {
DP_ERR("error reading DP_TRAINING_AUX_RD_INTERVAL\n");
@@ -1527,26 +1520,22 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
if (rlen != 1) {
DP_DEBUG("failed to read DPRX_FEATURE_ENUMERATION_LIST\n");
rx_feature = 0;
} else {
panel->vsc_supported = !!(rx_feature &
VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED);
panel->vscext_supported = !!(rx_feature &
VSC_EXT_VESA_SDP_SUPPORTED);
panel->vscext_chaining_supported = !!(rx_feature &
VSC_EXT_VESA_SDP_CHAINING_SUPPORTED);
DP_DEBUG("vsc=%d, vscext=%d, vscext_chaining=%d\n",
panel->vsc_supported, panel->vscext_supported,
panel->vscext_chaining_supported);
}
skip_dpcd_read:
if (panel->custom_dpcd)
rx_feature = dp_panel->dpcd[DP_RECEIVER_CAP_SIZE + 1];
panel->vsc_supported = !!(rx_feature &
VSC_SDP_EXTENSION_FOR_COLORIMETRY_SUPPORTED);
panel->vscext_supported = !!(rx_feature & VSC_EXT_VESA_SDP_SUPPORTED);
panel->vscext_chaining_supported = !!(rx_feature &
VSC_EXT_VESA_SDP_CHAINING_SUPPORTED);
DP_DEBUG("vsc=%d, vscext=%d, vscext_chaining=%d\n",
panel->vsc_supported, panel->vscext_supported,
panel->vscext_chaining_supported);
link_info->revision = dpcd[DP_DPCD_REV];
panel->major = (link_info->revision >> 4) & 0x0f;
panel->minor = link_info->revision & 0x0f;
/* override link params updated in dp_panel_init_panel_info */
link_info->rate = min_t(unsigned long, panel->parser->max_lclk_khz,
drm_dp_bw_code_to_link_rate(dpcd[DP_MAX_LINK_RATE]));
@@ -1617,74 +1606,6 @@ static int dp_panel_set_default_link_params(struct dp_panel *dp_panel)
return 0;
}
static bool dp_panel_validate_edid(struct edid *edid, size_t edid_size)
{
if (!edid || (edid_size < EDID_LENGTH))
return false;
if (EDID_LENGTH * (edid->extensions + 1) > edid_size) {
DP_ERR("edid size does not match allocated.\n");
return false;
}
if (!drm_edid_is_valid(edid)) {
DP_ERR("invalid edid.\n");
return false;
}
return true;
}
static int dp_panel_set_edid(struct dp_panel *dp_panel, u8 *edid,
size_t edid_size)
{
struct dp_panel_private *panel;
if (!dp_panel) {
DP_ERR("invalid input\n");
return -EINVAL;
}
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
if (edid && dp_panel_validate_edid((struct edid *)edid, edid_size)) {
dp_panel->edid_ctrl->edid = (struct edid *)edid;
panel->custom_edid = true;
} else {
panel->custom_edid = false;
dp_panel->edid_ctrl->edid = NULL;
}
DP_DEBUG("%d\n", panel->custom_edid);
return 0;
}
static int dp_panel_set_dpcd(struct dp_panel *dp_panel, u8 *dpcd)
{
struct dp_panel_private *panel;
u8 *dp_dpcd;
if (!dp_panel) {
DP_ERR("invalid input\n");
return -EINVAL;
}
dp_dpcd = dp_panel->dpcd;
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
if (dpcd) {
memcpy(dp_dpcd, dpcd, DP_RECEIVER_CAP_SIZE +
DP_RECEIVER_EXT_CAP_SIZE + 1);
panel->custom_dpcd = true;
} else {
panel->custom_dpcd = false;
}
DP_DEBUG("%d\n", panel->custom_dpcd);
return 0;
}
static int dp_panel_read_edid(struct dp_panel *dp_panel,
struct drm_connector *connector)
{
@@ -1699,11 +1620,6 @@ static int dp_panel_read_edid(struct dp_panel *dp_panel,
panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
if (panel->custom_edid) {
DP_DEBUG("skip edid read in debug mode\n");
goto end;
}
sde_get_edid(connector, &panel->aux->drm_aux->ddc,
(void **)&dp_panel->edid_ctrl);
if (!dp_panel->edid_ctrl->edid) {
@@ -2347,7 +2263,7 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
shdr_if_sdp = &panel->catalog->shdr_if_sdp;
vsc_colorimetry = &panel->catalog->vsc_colorimetry;
if (!panel->custom_edid && dp_panel->edid_ctrl->edid)
if (dp_panel->edid_ctrl->edid)
sde_free_edid((void **)&dp_panel->edid_ctrl);
dp_panel_set_stream_info(dp_panel, DP_STREAM_MAX, 0, 0, 0, 0);
@@ -3083,8 +2999,6 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
dp_panel->get_mode_bpp = dp_panel_get_mode_bpp;
dp_panel->get_modes = dp_panel_get_modes;
dp_panel->handle_sink_request = dp_panel_handle_sink_request;
dp_panel->set_edid = dp_panel_set_edid;
dp_panel->set_dpcd = dp_panel_set_dpcd;
dp_panel->tpg_config = dp_panel_tpg_config;
dp_panel->spd_config = dp_panel_spd_config;
dp_panel->setup_hdr = dp_panel_setup_hdr;

View File

@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2012-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _DP_PANEL_H_
@@ -151,6 +151,14 @@ struct dp_panel {
bool dsc_continuous_pps;
bool mst_state;
/* override debug option */
bool mst_hide;
bool mode_override;
int hdisplay;
int vdisplay;
int vrefresh;
int aspect_ratio;
s64 fec_overhead_fp;
int (*init)(struct dp_panel *dp_panel);
@@ -163,8 +171,6 @@ struct dp_panel {
int (*get_modes)(struct dp_panel *dp_panel,
struct drm_connector *connector, struct dp_display_mode *mode);
void (*handle_sink_request)(struct dp_panel *dp_panel);
int (*set_edid)(struct dp_panel *dp_panel, u8 *edid, size_t edid_size);
int (*set_dpcd)(struct dp_panel *dp_panel, u8 *dpcd);
int (*setup_hdr)(struct dp_panel *dp_panel,
struct drm_msm_ext_hdr_metadata *hdr_meta,
bool dhdr_update, u64 core_clk_rate, bool flush);