Loading drivers/gpu/drm/msm/dp/dp_debug.c +340 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ #include <linux/debugfs.h> #include <linux/slab.h> #include "dp_power.h" #include "dp_catalog.h" Loading @@ -35,6 +36,8 @@ struct dp_debug_private { u8 *dpcd; u32 dpcd_size; u32 mst_con_id; char exe_mode[SZ_32]; char reg_dump[SZ_32]; Loading Loading @@ -344,6 +347,112 @@ static ssize_t dp_debug_write_edid_modes(struct file *file, return len; } 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; char buf[SZ_32]; 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; if (*ppos) goto end; len = min_t(size_t, count, SZ_32 - 1); if (copy_from_user(buf, user_buff, len)) goto end; 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; } } if (!in_list) pr_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: return len; } 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; char buf[SZ_32]; size_t len = 0; int con_id = 0; bool in_list = false; if (!debug) return -ENODEV; if (*ppos) goto end; /* Leave room for termination char */ len = min_t(size_t, count, SZ_32 - 1); if (copy_from_user(buf, user_buff, len)) goto clear; buf[len] = '\0'; if (kstrtoint(buf, 10, &con_id) != 0) goto clear; 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; break; } } mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock); if (!in_list) pr_err("invalid connector id %u\n", con_id); goto end; clear: pr_debug("clearing mst_con_id\n"); debug->mst_con_id = 0; end: return len; } static ssize_t dp_debug_bw_code_write(struct file *file, const char __user *user_buff, size_t count, loff_t *ppos) { Loading Loading @@ -713,6 +822,183 @@ static ssize_t dp_debug_read_edid_modes(struct file *file, return rc; } 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) { pr_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) { pr_err("connector %u not in mst list\n", debug->mst_con_id); rc = -EINVAL; goto error; } if (!connector) { pr_err("connector is NULL\n"); rc = -EINVAL; goto error; } if (*ppos) goto error; buf = kzalloc(SZ_4K, GFP_KERNEL); if (!buf) { rc = -ENOMEM; goto error; } mutex_lock(&connector->dev->mode_config.mutex); list_for_each_entry(mode, &connector->modes, head) { ret = snprintf(buf + len, max_size, "%s %d %d %d %d %d 0x%x\n", mode->name, mode->vrefresh, mode->picture_aspect_ratio, mode->htotal, mode->vtotal, mode->clock, mode->flags); if (dp_debug_check_buffer_overflow(ret, &max_size, &len)) break; } mutex_unlock(&connector->dev->mode_config.mutex); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; goto error; } *ppos += len; kfree(buf); return len; error: return rc; } static ssize_t dp_debug_read_mst_con_id(struct file *file, char __user *user_buff, size_t count, loff_t *ppos) { struct dp_debug_private *debug = file->private_data; char *buf; u32 len = 0, ret = 0, max_size = SZ_4K; int rc = 0; if (!debug) { pr_err("invalid data\n"); rc = -ENODEV; goto error; } if (*ppos) goto error; buf = kzalloc(SZ_4K, GFP_KERNEL); if (!buf) { rc = -ENOMEM; goto error; } ret = snprintf(buf, max_size, "%u\n", debug->mst_con_id); len += ret; if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; goto error; } *ppos += len; kfree(buf); return len; error: return rc; } 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; char *buf; u32 len = 0, ret = 0, max_size = SZ_4K; int rc = 0; struct drm_connector *connector; if (!debug) { pr_err("invalid data\n"); rc = -ENODEV; goto error; } if (*ppos) goto error; buf = kzalloc(SZ_4K, GFP_KERNEL); if (!buf) { rc = -ENOMEM; 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) continue; connector = mst_connector->conn; if (!connector) { pr_err("connector for id %d is NULL\n", mst_connector->con_id); continue; } ret = snprintf(buf + len, max_size, "conn name:%s, conn id:%d\n", connector->name, connector->base.id); if (dp_debug_check_buffer_overflow(ret, &max_size, &len)) break; } mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; goto error; } *ppos += len; kfree(buf); return len; error: return rc; } static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff, size_t count, loff_t *ppos) { Loading Loading @@ -1209,6 +1495,23 @@ static const struct file_operations edid_modes_fops = { .write = dp_debug_write_edid_modes, }; static const struct file_operations edid_modes_mst_fops = { .open = simple_open, .read = dp_debug_read_edid_modes_mst, .write = dp_debug_write_edid_modes_mst, }; static const struct file_operations mst_conn_info_fops = { .open = simple_open, .read = dp_debug_read_mst_conn_info, }; static const struct file_operations mst_con_id_fops = { .open = simple_open, .read = dp_debug_read_mst_con_id, .write = dp_debug_write_mst_con_id, }; static const struct file_operations hpd_fops = { .open = simple_open, .write = dp_debug_write_hpd, Loading Loading @@ -1328,6 +1631,33 @@ static int dp_debug_init(struct dp_debug *dp_debug) goto error_remove_dir; } file = debugfs_create_file("edid_modes_mst", 0644, dir, debug, &edid_modes_mst_fops); if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); pr_err("[%s] debugfs create edid_modes_mst failed, rc=%d\n", DEBUG_NAME, rc); goto error_remove_dir; } file = debugfs_create_file("mst_con_id", 0644, dir, debug, &mst_con_id_fops); if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); pr_err("[%s] debugfs create mst_con_id failed, rc=%d\n", DEBUG_NAME, rc); goto error_remove_dir; } file = debugfs_create_file("mst_con_info", 0644, dir, debug, &mst_conn_info_fops); if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); pr_err("[%s] debugfs create mst_conn_info failed, rc=%d\n", DEBUG_NAME, rc); goto error_remove_dir; } file = debugfs_create_file("hpd", 0644, dir, debug, &hpd_fops); if (IS_ERR_OR_NULL(file)) { Loading Loading @@ -1537,6 +1867,16 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in) dp_debug->get_edid = dp_debug_get_edid; 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; return dp_debug; error: return ERR_PTR(rc); Loading drivers/gpu/drm/msm/dp/dp_debug.h +2 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include "dp_link.h" #include "dp_usbpd.h" #include "dp_aux.h" #include "dp_display.h" /** * struct dp_debug Loading @@ -43,6 +44,7 @@ struct dp_debug { u32 max_pclk_khz; bool force_encryption; char hdcp_status[SZ_128]; struct dp_mst_connector dp_mst_connector_list; u8 *(*get_edid)(struct dp_debug *dp_debug); }; Loading drivers/gpu/drm/msm/dp/dp_display.c +94 −0 Original line number Diff line number Diff line Loading @@ -1654,6 +1654,9 @@ static enum drm_mode_status dp_display_validate_mode( struct dp_panel *dp_panel; struct dp_debug *debug; enum drm_mode_status mode_status = MODE_BAD; bool in_list = false; struct dp_mst_connector *mst_connector; int hdis, vdis, vref, ar, _hdis, _vdis, _vref, _ar; if (!dp_display || !mode || !panel) { pr_err("invalid params\n"); Loading Loading @@ -1697,6 +1700,58 @@ static enum drm_mode_status dp_display_validate_mode( goto end; } /* * 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); goto verify_default; } list_for_each_entry(mst_connector, &debug->dp_mst_connector_list.list, list) { if (mst_connector->con_id == dp_panel->connector->base.id) { in_list = true; if (!mst_connector->debug_en) { mode_status = MODE_OK; mutex_unlock( &debug->dp_mst_connector_list.lock); goto end; } hdis = mst_connector->hdisplay; vdis = mst_connector->vdisplay; vref = mst_connector->vrefresh; ar = mst_connector->aspect_ratio; _hdis = mode->hdisplay; _vdis = mode->vdisplay; _vref = mode->vrefresh; _ar = mode->picture_aspect_ratio; if (hdis == _hdis && vdis == _vdis && vref == _vref && ar == _ar) { mode_status = MODE_OK; mutex_unlock( &debug->dp_mst_connector_list.lock); goto end; } break; } } mutex_unlock(&debug->dp_mst_connector_list.lock); if (in_list) goto end; verify_default: if (debug->debug_en && (mode->hdisplay != debug->hdisplay || mode->vdisplay != debug->vdisplay || mode->vrefresh != debug->vrefresh || Loading Loading @@ -1894,6 +1949,7 @@ 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; if (!dp_display || !connector) { pr_err("invalid input\n"); Loading @@ -1902,8 +1958,11 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display, dp = container_of(dp_display, struct dp_display_private, dp_display); mutex_lock(&dp->session_lock); if (!dp->mst.drm_registered) { pr_debug("drm mst not registered\n"); mutex_unlock(&dp->session_lock); return -EPERM; } Loading @@ -1918,6 +1977,7 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display, if (IS_ERR(dp_panel)) { rc = PTR_ERR(dp_panel); pr_err("failed to initialize panel, rc = %d\n", rc); mutex_unlock(&dp->session_lock); return rc; } Loading @@ -1926,12 +1986,28 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display, rc = PTR_ERR(dp_panel->audio); pr_err("[mst] failed to initialize audio, rc = %d\n", rc); dp_panel->audio = NULL; mutex_unlock(&dp->session_lock); return rc; } 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); mst_connector->debug_en = false; mst_connector->conn = connector; mst_connector->con_id = connector->base.id; 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); mutex_unlock(&dp->session_lock); return 0; } Loading @@ -1942,6 +2018,7 @@ 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) { pr_err("invalid input\n"); Loading @@ -1950,14 +2027,18 @@ static int dp_display_mst_connector_uninstall(struct dp_display *dp_display, dp = container_of(dp_display, struct dp_display_private, dp_display); mutex_lock(&dp->session_lock); if (!dp->mst.drm_registered) { pr_debug("drm mst not registered\n"); mutex_unlock(&dp->session_lock); return -EPERM; } sde_conn = to_sde_connector(connector); if (!sde_conn->drv_panel) { pr_err("invalid panel for connector:%d\n", connector->base.id); mutex_unlock(&dp->session_lock); return -EINVAL; } Loading @@ -1968,6 +2049,19 @@ 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->con_id == connector->base.id) { list_del(&con_to_remove->list); kfree(con_to_remove); } } mutex_unlock(&dp->debug->dp_mst_connector_list.lock); mutex_unlock(&dp->session_lock); return rc; } Loading drivers/gpu/drm/msm/dp/dp_display.h +13 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #ifndef _DP_DISPLAY_H_ #define _DP_DISPLAY_H_ #include <linux/list.h> #include <drm/drmP.h> #include <drm/msm_drm.h> Loading Loading @@ -43,6 +44,18 @@ 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; }; struct dp_display { struct drm_device *drm_dev; struct dp_bridge *bridge; Loading Loading
drivers/gpu/drm/msm/dp/dp_debug.c +340 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ #include <linux/debugfs.h> #include <linux/slab.h> #include "dp_power.h" #include "dp_catalog.h" Loading @@ -35,6 +36,8 @@ struct dp_debug_private { u8 *dpcd; u32 dpcd_size; u32 mst_con_id; char exe_mode[SZ_32]; char reg_dump[SZ_32]; Loading Loading @@ -344,6 +347,112 @@ static ssize_t dp_debug_write_edid_modes(struct file *file, return len; } 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; char buf[SZ_32]; 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; if (*ppos) goto end; len = min_t(size_t, count, SZ_32 - 1); if (copy_from_user(buf, user_buff, len)) goto end; 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; } } if (!in_list) pr_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: return len; } 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; char buf[SZ_32]; size_t len = 0; int con_id = 0; bool in_list = false; if (!debug) return -ENODEV; if (*ppos) goto end; /* Leave room for termination char */ len = min_t(size_t, count, SZ_32 - 1); if (copy_from_user(buf, user_buff, len)) goto clear; buf[len] = '\0'; if (kstrtoint(buf, 10, &con_id) != 0) goto clear; 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; break; } } mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock); if (!in_list) pr_err("invalid connector id %u\n", con_id); goto end; clear: pr_debug("clearing mst_con_id\n"); debug->mst_con_id = 0; end: return len; } static ssize_t dp_debug_bw_code_write(struct file *file, const char __user *user_buff, size_t count, loff_t *ppos) { Loading Loading @@ -713,6 +822,183 @@ static ssize_t dp_debug_read_edid_modes(struct file *file, return rc; } 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) { pr_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) { pr_err("connector %u not in mst list\n", debug->mst_con_id); rc = -EINVAL; goto error; } if (!connector) { pr_err("connector is NULL\n"); rc = -EINVAL; goto error; } if (*ppos) goto error; buf = kzalloc(SZ_4K, GFP_KERNEL); if (!buf) { rc = -ENOMEM; goto error; } mutex_lock(&connector->dev->mode_config.mutex); list_for_each_entry(mode, &connector->modes, head) { ret = snprintf(buf + len, max_size, "%s %d %d %d %d %d 0x%x\n", mode->name, mode->vrefresh, mode->picture_aspect_ratio, mode->htotal, mode->vtotal, mode->clock, mode->flags); if (dp_debug_check_buffer_overflow(ret, &max_size, &len)) break; } mutex_unlock(&connector->dev->mode_config.mutex); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; goto error; } *ppos += len; kfree(buf); return len; error: return rc; } static ssize_t dp_debug_read_mst_con_id(struct file *file, char __user *user_buff, size_t count, loff_t *ppos) { struct dp_debug_private *debug = file->private_data; char *buf; u32 len = 0, ret = 0, max_size = SZ_4K; int rc = 0; if (!debug) { pr_err("invalid data\n"); rc = -ENODEV; goto error; } if (*ppos) goto error; buf = kzalloc(SZ_4K, GFP_KERNEL); if (!buf) { rc = -ENOMEM; goto error; } ret = snprintf(buf, max_size, "%u\n", debug->mst_con_id); len += ret; if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; goto error; } *ppos += len; kfree(buf); return len; error: return rc; } 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; char *buf; u32 len = 0, ret = 0, max_size = SZ_4K; int rc = 0; struct drm_connector *connector; if (!debug) { pr_err("invalid data\n"); rc = -ENODEV; goto error; } if (*ppos) goto error; buf = kzalloc(SZ_4K, GFP_KERNEL); if (!buf) { rc = -ENOMEM; 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) continue; connector = mst_connector->conn; if (!connector) { pr_err("connector for id %d is NULL\n", mst_connector->con_id); continue; } ret = snprintf(buf + len, max_size, "conn name:%s, conn id:%d\n", connector->name, connector->base.id); if (dp_debug_check_buffer_overflow(ret, &max_size, &len)) break; } mutex_unlock(&debug->dp_debug.dp_mst_connector_list.lock); if (copy_to_user(user_buff, buf, len)) { kfree(buf); rc = -EFAULT; goto error; } *ppos += len; kfree(buf); return len; error: return rc; } static ssize_t dp_debug_read_info(struct file *file, char __user *user_buff, size_t count, loff_t *ppos) { Loading Loading @@ -1209,6 +1495,23 @@ static const struct file_operations edid_modes_fops = { .write = dp_debug_write_edid_modes, }; static const struct file_operations edid_modes_mst_fops = { .open = simple_open, .read = dp_debug_read_edid_modes_mst, .write = dp_debug_write_edid_modes_mst, }; static const struct file_operations mst_conn_info_fops = { .open = simple_open, .read = dp_debug_read_mst_conn_info, }; static const struct file_operations mst_con_id_fops = { .open = simple_open, .read = dp_debug_read_mst_con_id, .write = dp_debug_write_mst_con_id, }; static const struct file_operations hpd_fops = { .open = simple_open, .write = dp_debug_write_hpd, Loading Loading @@ -1328,6 +1631,33 @@ static int dp_debug_init(struct dp_debug *dp_debug) goto error_remove_dir; } file = debugfs_create_file("edid_modes_mst", 0644, dir, debug, &edid_modes_mst_fops); if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); pr_err("[%s] debugfs create edid_modes_mst failed, rc=%d\n", DEBUG_NAME, rc); goto error_remove_dir; } file = debugfs_create_file("mst_con_id", 0644, dir, debug, &mst_con_id_fops); if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); pr_err("[%s] debugfs create mst_con_id failed, rc=%d\n", DEBUG_NAME, rc); goto error_remove_dir; } file = debugfs_create_file("mst_con_info", 0644, dir, debug, &mst_conn_info_fops); if (IS_ERR_OR_NULL(file)) { rc = PTR_ERR(file); pr_err("[%s] debugfs create mst_conn_info failed, rc=%d\n", DEBUG_NAME, rc); goto error_remove_dir; } file = debugfs_create_file("hpd", 0644, dir, debug, &hpd_fops); if (IS_ERR_OR_NULL(file)) { Loading Loading @@ -1537,6 +1867,16 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in) dp_debug->get_edid = dp_debug_get_edid; 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; return dp_debug; error: return ERR_PTR(rc); Loading
drivers/gpu/drm/msm/dp/dp_debug.h +2 −0 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ #include "dp_link.h" #include "dp_usbpd.h" #include "dp_aux.h" #include "dp_display.h" /** * struct dp_debug Loading @@ -43,6 +44,7 @@ struct dp_debug { u32 max_pclk_khz; bool force_encryption; char hdcp_status[SZ_128]; struct dp_mst_connector dp_mst_connector_list; u8 *(*get_edid)(struct dp_debug *dp_debug); }; Loading
drivers/gpu/drm/msm/dp/dp_display.c +94 −0 Original line number Diff line number Diff line Loading @@ -1654,6 +1654,9 @@ static enum drm_mode_status dp_display_validate_mode( struct dp_panel *dp_panel; struct dp_debug *debug; enum drm_mode_status mode_status = MODE_BAD; bool in_list = false; struct dp_mst_connector *mst_connector; int hdis, vdis, vref, ar, _hdis, _vdis, _vref, _ar; if (!dp_display || !mode || !panel) { pr_err("invalid params\n"); Loading Loading @@ -1697,6 +1700,58 @@ static enum drm_mode_status dp_display_validate_mode( goto end; } /* * 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); goto verify_default; } list_for_each_entry(mst_connector, &debug->dp_mst_connector_list.list, list) { if (mst_connector->con_id == dp_panel->connector->base.id) { in_list = true; if (!mst_connector->debug_en) { mode_status = MODE_OK; mutex_unlock( &debug->dp_mst_connector_list.lock); goto end; } hdis = mst_connector->hdisplay; vdis = mst_connector->vdisplay; vref = mst_connector->vrefresh; ar = mst_connector->aspect_ratio; _hdis = mode->hdisplay; _vdis = mode->vdisplay; _vref = mode->vrefresh; _ar = mode->picture_aspect_ratio; if (hdis == _hdis && vdis == _vdis && vref == _vref && ar == _ar) { mode_status = MODE_OK; mutex_unlock( &debug->dp_mst_connector_list.lock); goto end; } break; } } mutex_unlock(&debug->dp_mst_connector_list.lock); if (in_list) goto end; verify_default: if (debug->debug_en && (mode->hdisplay != debug->hdisplay || mode->vdisplay != debug->vdisplay || mode->vrefresh != debug->vrefresh || Loading Loading @@ -1894,6 +1949,7 @@ 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; if (!dp_display || !connector) { pr_err("invalid input\n"); Loading @@ -1902,8 +1958,11 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display, dp = container_of(dp_display, struct dp_display_private, dp_display); mutex_lock(&dp->session_lock); if (!dp->mst.drm_registered) { pr_debug("drm mst not registered\n"); mutex_unlock(&dp->session_lock); return -EPERM; } Loading @@ -1918,6 +1977,7 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display, if (IS_ERR(dp_panel)) { rc = PTR_ERR(dp_panel); pr_err("failed to initialize panel, rc = %d\n", rc); mutex_unlock(&dp->session_lock); return rc; } Loading @@ -1926,12 +1986,28 @@ static int dp_display_mst_connector_install(struct dp_display *dp_display, rc = PTR_ERR(dp_panel->audio); pr_err("[mst] failed to initialize audio, rc = %d\n", rc); dp_panel->audio = NULL; mutex_unlock(&dp->session_lock); return rc; } 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); mst_connector->debug_en = false; mst_connector->conn = connector; mst_connector->con_id = connector->base.id; 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); mutex_unlock(&dp->session_lock); return 0; } Loading @@ -1942,6 +2018,7 @@ 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) { pr_err("invalid input\n"); Loading @@ -1950,14 +2027,18 @@ static int dp_display_mst_connector_uninstall(struct dp_display *dp_display, dp = container_of(dp_display, struct dp_display_private, dp_display); mutex_lock(&dp->session_lock); if (!dp->mst.drm_registered) { pr_debug("drm mst not registered\n"); mutex_unlock(&dp->session_lock); return -EPERM; } sde_conn = to_sde_connector(connector); if (!sde_conn->drv_panel) { pr_err("invalid panel for connector:%d\n", connector->base.id); mutex_unlock(&dp->session_lock); return -EINVAL; } Loading @@ -1968,6 +2049,19 @@ 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->con_id == connector->base.id) { list_del(&con_to_remove->list); kfree(con_to_remove); } } mutex_unlock(&dp->debug->dp_mst_connector_list.lock); mutex_unlock(&dp->session_lock); return rc; } Loading
drivers/gpu/drm/msm/dp/dp_display.h +13 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ #ifndef _DP_DISPLAY_H_ #define _DP_DISPLAY_H_ #include <linux/list.h> #include <drm/drmP.h> #include <drm/msm_drm.h> Loading Loading @@ -43,6 +44,18 @@ 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; }; struct dp_display { struct drm_device *drm_dev; struct dp_bridge *bridge; Loading