disp: msm: dp: use dp_msm_sim for dp-mst simulation
Revert changes in dp mst topology manager and use dp_msm_sim bridge at aux layer to implement dp-mst simulation. Change-Id: I863649f901ac918f65c9078e6a2f1b6931d19e3a 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:
@@ -27,6 +27,7 @@ struct dp_aux_private {
|
||||
struct drm_dp_aux drm_aux;
|
||||
|
||||
struct dp_aux_bridge *aux_bridge;
|
||||
struct dp_aux_bridge *sim_bridge;
|
||||
bool bridge_in_transfer;
|
||||
|
||||
bool cmd_busy;
|
||||
@@ -470,6 +471,12 @@ error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool dp_aux_is_sideband_msg(u32 address, size_t size)
|
||||
{
|
||||
return (address >= 0x1000 && address + size < 0x1800) ||
|
||||
(address >= 0x2000 && address + size < 0x2200);
|
||||
}
|
||||
|
||||
static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
|
||||
struct drm_dp_aux_msg *msg)
|
||||
{
|
||||
@@ -491,7 +498,8 @@ static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
|
||||
goto end;
|
||||
}
|
||||
|
||||
if ((msg->address + msg->size) > SZ_4K) {
|
||||
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;
|
||||
@@ -521,9 +529,20 @@ static ssize_t dp_aux_transfer_debug(struct drm_dp_aux *drm_aux,
|
||||
}
|
||||
|
||||
mutex_lock(aux->dp_aux.access_lock);
|
||||
if (aux->read)
|
||||
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;
|
||||
@@ -756,7 +775,7 @@ static void dp_aux_dpcd_updated(struct dp_aux *dp_aux)
|
||||
}
|
||||
|
||||
static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
|
||||
u8 *edid, u8 *dpcd)
|
||||
u8 *edid, u8 *dpcd, struct dp_aux_bridge *sim_bridge)
|
||||
{
|
||||
struct dp_aux_private *aux;
|
||||
|
||||
@@ -771,6 +790,7 @@ static void dp_aux_set_sim_mode(struct dp_aux *dp_aux, bool en,
|
||||
|
||||
aux->edid = edid;
|
||||
aux->dpcd = dpcd;
|
||||
aux->sim_bridge = sim_bridge;
|
||||
|
||||
if (en) {
|
||||
atomic_set(&aux->aborted, 0);
|
||||
|
@@ -53,7 +53,8 @@ struct dp_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, bool en, u8 *edid, u8 *dpcd,
|
||||
struct dp_aux_bridge *sim_bridge);
|
||||
int (*aux_switch)(struct dp_aux *aux, bool enable, int orientation);
|
||||
};
|
||||
|
||||
|
@@ -15,6 +15,7 @@
|
||||
#include "dp_display.h"
|
||||
#include "dp_pll.h"
|
||||
#include "dp_hpd.h"
|
||||
#include "dp_mst_sim_helper.h"
|
||||
|
||||
#define DEBUG_NAME "drm_dp"
|
||||
|
||||
@@ -45,8 +46,73 @@ struct dp_debug_private {
|
||||
struct dp_pll *pll;
|
||||
struct dp_display *display;
|
||||
struct mutex lock;
|
||||
struct dp_aux_bridge *sim_bridge;
|
||||
};
|
||||
|
||||
static int dp_debug_sim_hpd_cb(void *arg, bool hpd, bool hpd_irq)
|
||||
{
|
||||
struct dp_debug_private *debug = arg;
|
||||
|
||||
if (hpd_irq)
|
||||
return debug->hpd->simulate_attention(debug->hpd, 0);
|
||||
else
|
||||
return debug->hpd->simulate_connect(debug->hpd, hpd);
|
||||
}
|
||||
|
||||
static int dp_debug_configure_mst_bridge(struct dp_debug_private *debug)
|
||||
{
|
||||
struct device_node *bridge_node;
|
||||
struct dp_mst_sim_port *ports;
|
||||
int i, 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)
|
||||
return 0;
|
||||
|
||||
ports = kcalloc(debug->dp_debug.mst_port_cnt,
|
||||
sizeof(*ports), GFP_KERNEL);
|
||||
if (!ports)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
ret = dp_mst_sim_update(debug->sim_bridge->mst_ctx,
|
||||
debug->dp_debug.mst_port_cnt, ports);
|
||||
|
||||
kfree(ports);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int dp_debug_get_edid_buf(struct dp_debug_private *debug)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -133,7 +199,8 @@ static ssize_t dp_debug_write_edid(struct file *file,
|
||||
|
||||
debug->aux->set_sim_mode(debug->aux,
|
||||
debug->dp_debug.sim_mode,
|
||||
debug->edid, debug->dpcd);
|
||||
debug->edid, debug->dpcd,
|
||||
debug->sim_bridge);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,6 +234,9 @@ bail:
|
||||
*/
|
||||
DP_INFO("[%s]\n", edid ? "SET" : "CLEAR");
|
||||
|
||||
if (dp_debug_configure_mst_bridge(debug))
|
||||
DP_ERR("failed to config mst bridge\n");
|
||||
|
||||
mutex_unlock(&debug->lock);
|
||||
return rc;
|
||||
}
|
||||
@@ -830,10 +900,13 @@ static ssize_t dp_debug_mst_sideband_mode_write(struct file *file,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
debug->parser->has_mst_sideband = mst_sideband_mode ? true : false;
|
||||
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");
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -1636,9 +1709,13 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
|
||||
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->edid, debug->dpcd, debug->sim_bridge);
|
||||
debug->ctrl->set_sim_mode(debug->ctrl, true);
|
||||
} else {
|
||||
if (debug->hotplug) {
|
||||
@@ -1649,9 +1726,10 @@ 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);
|
||||
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) {
|
||||
|
@@ -992,19 +992,11 @@ static void dp_display_mst_init(struct dp_display_private *dp)
|
||||
static void dp_display_set_mst_mgr_state(struct dp_display_private *dp,
|
||||
bool state)
|
||||
{
|
||||
struct dp_mst_hpd_info info = {0};
|
||||
|
||||
if (!dp->mst.mst_active)
|
||||
return;
|
||||
|
||||
info.mst_protocol = dp->parser->has_mst_sideband;
|
||||
if (state) {
|
||||
info.mst_port_cnt = dp->debug->mst_port_cnt;
|
||||
info.edid = dp->debug->get_edid(dp->debug);
|
||||
}
|
||||
|
||||
if (dp->mst.cbs.set_mgr_state)
|
||||
dp->mst.cbs.set_mgr_state(&dp->dp_display, state, &info);
|
||||
dp->mst.cbs.set_mgr_state(&dp->dp_display, state);
|
||||
|
||||
DP_MST_DEBUG("mst_mgr_state: %d\n", state);
|
||||
}
|
||||
@@ -1585,19 +1577,8 @@ static int dp_display_stream_enable(struct dp_display_private *dp,
|
||||
|
||||
static void dp_display_mst_attention(struct dp_display_private *dp)
|
||||
{
|
||||
struct dp_mst_hpd_info hpd_irq = {0};
|
||||
|
||||
if (dp->mst.mst_active && dp->mst.cbs.hpd_irq) {
|
||||
hpd_irq.mst_hpd_sim = dp->debug->mst_hpd_sim;
|
||||
hpd_irq.mst_sim_add_con = dp->debug->mst_sim_add_con;
|
||||
hpd_irq.mst_sim_remove_con = dp->debug->mst_sim_remove_con;
|
||||
hpd_irq.mst_sim_remove_con_id = dp->debug->mst_sim_remove_con_id;
|
||||
hpd_irq.edid = dp->debug->get_edid(dp->debug);
|
||||
dp->mst.cbs.hpd_irq(&dp->dp_display, &hpd_irq);
|
||||
dp->debug->mst_hpd_sim = false;
|
||||
dp->debug->mst_sim_add_con = false;
|
||||
dp->debug->mst_sim_remove_con = false;
|
||||
}
|
||||
if (dp->mst.mst_active && dp->mst.cbs.hpd_irq)
|
||||
dp->mst.cbs.hpd_irq(&dp->dp_display);
|
||||
|
||||
DP_MST_DEBUG("mst_attention_work. mst_active:%d\n", dp->mst.mst_active);
|
||||
}
|
||||
|
@@ -1,6 +1,6 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DP_DISPLAY_H_
|
||||
@@ -18,23 +18,12 @@ enum dp_drv_state {
|
||||
PM_SUSPEND,
|
||||
};
|
||||
|
||||
struct dp_mst_hpd_info {
|
||||
bool mst_protocol;
|
||||
bool mst_hpd_sim;
|
||||
u32 mst_port_cnt;
|
||||
u8 *edid;
|
||||
bool mst_sim_add_con;
|
||||
bool mst_sim_remove_con;
|
||||
int mst_sim_remove_con_id;
|
||||
};
|
||||
|
||||
struct dp_mst_drm_cbs {
|
||||
void (*hpd)(void *display, bool hpd_status);
|
||||
void (*hpd_irq)(void *display, struct dp_mst_hpd_info *info);
|
||||
void (*hpd_irq)(void *display);
|
||||
void (*set_drv_state)(void *dp_display,
|
||||
enum dp_drv_state mst_state);
|
||||
int (*set_mgr_state)(void *dp_display, bool state,
|
||||
struct dp_mst_hpd_info *info);
|
||||
int (*set_mgr_state)(void *dp_display, bool state);
|
||||
};
|
||||
|
||||
struct dp_mst_drm_install_info {
|
||||
|
@@ -86,34 +86,12 @@ struct dp_drm_mst_fw_helper_ops {
|
||||
struct drm_dp_mst_port *port);
|
||||
};
|
||||
|
||||
struct dp_mst_sim_port_data {
|
||||
bool input_port;
|
||||
u8 peer_device_type;
|
||||
u8 port_number;
|
||||
bool mcs;
|
||||
bool ddps;
|
||||
bool legacy_device_plug_status;
|
||||
u8 dpcd_revision;
|
||||
u8 peer_guid[16];
|
||||
u8 num_sdp_streams;
|
||||
u8 num_sdp_stream_sinks;
|
||||
};
|
||||
|
||||
struct dp_mst_sim_port_edid {
|
||||
u8 port_number;
|
||||
u8 edid[SZ_256];
|
||||
bool valid;
|
||||
};
|
||||
|
||||
struct dp_mst_sim_mode {
|
||||
bool mst_state;
|
||||
struct edid *edid;
|
||||
struct dp_mst_sim_port_edid port_edids[DP_MST_SIM_MAX_PORTS];
|
||||
struct work_struct probe_work;
|
||||
const struct drm_dp_mst_topology_cbs *cbs;
|
||||
u32 port_cnt;
|
||||
};
|
||||
|
||||
struct dp_mst_bridge {
|
||||
struct drm_bridge base;
|
||||
struct drm_private_obj obj;
|
||||
@@ -153,7 +131,6 @@ struct dp_mst_private {
|
||||
struct dp_mst_bridge mst_bridge[MAX_DP_MST_DRM_BRIDGES];
|
||||
struct dp_display *dp_display;
|
||||
const struct dp_drm_mst_fw_helper_ops *mst_fw_cbs;
|
||||
struct dp_mst_sim_mode simulator;
|
||||
struct mutex mst_lock;
|
||||
struct mutex edid_lock;
|
||||
enum dp_drv_state state;
|
||||
@@ -176,22 +153,6 @@ struct dp_mst_encoder_info_cache {
|
||||
struct dp_mst_private dp_mst;
|
||||
struct dp_mst_encoder_info_cache dp_mst_enc_cache;
|
||||
|
||||
static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
struct dp_mst_private *mst = container_of(mgr, struct dp_mst_private,
|
||||
mst_mgr);
|
||||
struct drm_device *dev = mst->dp_display->drm_dev;
|
||||
char event_string[] = "HOTPLUG=1";
|
||||
char *envp[2];
|
||||
|
||||
envp[0] = event_string;
|
||||
envp[1] = NULL;
|
||||
|
||||
kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
DP_MST_INFO("mst hot plug event\n");
|
||||
}
|
||||
|
||||
static struct drm_private_state *dp_mst_duplicate_bridge_state(
|
||||
struct drm_private_obj *obj)
|
||||
{
|
||||
@@ -231,408 +192,6 @@ static struct dp_mst_bridge_state *dp_mst_get_bridge_atomic_state(
|
||||
drm_atomic_get_private_obj_state(state, &bridge->obj));
|
||||
}
|
||||
|
||||
static void dp_mst_sim_destroy_port(struct kref *ref)
|
||||
{
|
||||
struct drm_dp_mst_port *port = container_of(ref,
|
||||
struct drm_dp_mst_port, topology_kref);
|
||||
struct drm_dp_mst_topology_mgr *mgr = port->mgr;
|
||||
|
||||
if (port->cached_edid)
|
||||
kfree(port->cached_edid);
|
||||
|
||||
if (port->connector) {
|
||||
mutex_lock(&mgr->delayed_destroy_lock);
|
||||
list_add(&port->next, &mgr->destroy_port_list);
|
||||
mutex_unlock(&mgr->delayed_destroy_lock);
|
||||
schedule_work(&mgr->delayed_destroy_work);
|
||||
return;
|
||||
}
|
||||
|
||||
drm_dp_mst_put_port_malloc(port);
|
||||
}
|
||||
|
||||
static void dp_mst_sim_topology_get_port(struct drm_dp_mst_port *port)
|
||||
{
|
||||
WARN_ON(kref_read(&port->topology_kref) == 0);
|
||||
kref_get(&port->topology_kref);
|
||||
DP_MST_DEBUG("get port %p topology(%d)\n", port,
|
||||
kref_read(&port->topology_kref));
|
||||
}
|
||||
|
||||
static void dp_mst_sim_topology_put_port(struct drm_dp_mst_port *port)
|
||||
{
|
||||
DP_MST_DEBUG("put port %p topology(%d)\n", port,
|
||||
kref_read(&port->topology_kref) - 1);
|
||||
kref_put(&port->topology_kref, dp_mst_sim_destroy_port);
|
||||
}
|
||||
|
||||
/*
|
||||
* DRM DP MST Framework simulator OPs
|
||||
*
|
||||
* If simulation mode is enabled, on HPD connect, mst mode is enabled on the
|
||||
* topology manager and 2 simulated ports are created using
|
||||
* dp_mst_sim_add_port. These 2 ports are added to the MST branch device.
|
||||
* Each node in the DP MST topology has 2 reference objects one for memory
|
||||
* allocation and the other for its use on the topology tree. When the last
|
||||
* topology reference is released on a port, the port gets destroyed and when
|
||||
* the last malloc reference is released, then the port memory is freed.
|
||||
* Each port also gets a reference on its parent branch object to make sure
|
||||
* that the branch is not destroyed prematurely.
|
||||
*/
|
||||
|
||||
static void dp_mst_sim_add_port(struct dp_mst_private *mst,
|
||||
struct dp_mst_sim_port_data *port_msg)
|
||||
{
|
||||
struct drm_dp_mst_branch *mstb;
|
||||
struct drm_dp_mst_port *port;
|
||||
|
||||
mstb = mst->mst_mgr.mst_primary;
|
||||
if (!mstb) {
|
||||
DP_ERR("Unable to add port. mst branch device was destroyed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return;
|
||||
kref_init(&port->topology_kref);
|
||||
kref_init(&port->malloc_kref);
|
||||
port->parent = mstb;
|
||||
port->port_num = port_msg->port_number;
|
||||
port->mgr = mstb->mgr;
|
||||
port->aux.name = dp_mst.caps.drm_aux->name;
|
||||
port->aux.dev = mst->dp_display->drm_dev->dev;
|
||||
|
||||
/*
|
||||
* Make sure the memory allocation for our parent branch stays
|
||||
* around until our own memory allocation is released
|
||||
*/
|
||||
kref_get(&mstb->malloc_kref);
|
||||
|
||||
port->pdt = port_msg->peer_device_type;
|
||||
port->input = port_msg->input_port;
|
||||
port->mcs = port_msg->mcs;
|
||||
port->ddps = port_msg->ddps;
|
||||
port->ldps = port_msg->legacy_device_plug_status;
|
||||
port->dpcd_rev = port_msg->dpcd_revision;
|
||||
port->num_sdp_streams = port_msg->num_sdp_streams;
|
||||
port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
|
||||
|
||||
mutex_lock(&mstb->mgr->lock);
|
||||
dp_mst_sim_topology_get_port(port);
|
||||
list_add(&port->next, &mstb->ports);
|
||||
mutex_unlock(&mstb->mgr->lock);
|
||||
|
||||
/* use fixed pbn for simulator ports */
|
||||
port->full_pbn = 2520;
|
||||
|
||||
if (!port->input) {
|
||||
port->connector = (*mstb->mgr->cbs->add_connector)
|
||||
(mstb->mgr, port, NULL);
|
||||
if (!port->connector) {
|
||||
/* remove it from the port list */
|
||||
mutex_lock(&mstb->mgr->lock);
|
||||
list_del(&port->next);
|
||||
mutex_unlock(&mstb->mgr->lock);
|
||||
dp_mst_sim_topology_put_port(port);
|
||||
goto put_port;
|
||||
}
|
||||
|
||||
drm_connector_register(port->connector);
|
||||
}
|
||||
|
||||
put_port:
|
||||
dp_mst_sim_topology_put_port(port);
|
||||
}
|
||||
|
||||
static void dp_mst_sim_remove_port(struct dp_mst_private *mst,
|
||||
struct drm_dp_mst_port *port)
|
||||
{
|
||||
struct drm_dp_mst_branch *mstb;
|
||||
int i;
|
||||
|
||||
mstb = mst->mst_mgr.mst_primary;
|
||||
if (!mstb) {
|
||||
DP_ERR("Unable to remove port. mst branch device is null\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&mstb->mgr->lock);
|
||||
list_del(&port->next);
|
||||
mutex_unlock(&mstb->mgr->lock);
|
||||
dp_mst_sim_topology_put_port(port);
|
||||
|
||||
for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
|
||||
if (mst->simulator.port_edids[i].port_number ==
|
||||
port->port_num) {
|
||||
mst->simulator.port_edids[i].valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dp_mst_sim_link_probe_work(struct work_struct *work)
|
||||
{
|
||||
struct dp_mst_sim_mode *sim;
|
||||
struct dp_mst_private *mst;
|
||||
struct dp_mst_sim_port_data port_data;
|
||||
u8 cnt, i;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY);
|
||||
|
||||
sim = container_of(work, struct dp_mst_sim_mode, probe_work);
|
||||
mst = container_of(sim, struct dp_mst_private, simulator);
|
||||
|
||||
port_data.input_port = false;
|
||||
port_data.peer_device_type = DP_PEER_DEVICE_SST_SINK;
|
||||
port_data.mcs = false;
|
||||
port_data.ddps = true;
|
||||
port_data.legacy_device_plug_status = false;
|
||||
port_data.dpcd_revision = 0;
|
||||
port_data.num_sdp_streams = 0;
|
||||
port_data.num_sdp_stream_sinks = 0;
|
||||
|
||||
for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++)
|
||||
sim->port_edids[i].valid = false;
|
||||
|
||||
for (cnt = 0; cnt < sim->port_cnt; cnt++) {
|
||||
port_data.port_number = cnt;
|
||||
|
||||
for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
|
||||
if (sim->port_edids[i].valid) continue;
|
||||
|
||||
sim->port_edids[i].port_number = port_data.port_number;
|
||||
memcpy(sim->port_edids[i].edid, sim->edid, SZ_256);
|
||||
sim->port_edids[i].valid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
dp_mst_sim_add_port(mst, &port_data);
|
||||
}
|
||||
|
||||
dp_mst_hotplug(&mst->mst_mgr);
|
||||
DP_MST_DEBUG("completed\n");
|
||||
}
|
||||
|
||||
static int dp_mst_sim_no_action(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_mst_sim_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
int i, j;
|
||||
int cur_slots = 1;
|
||||
struct drm_dp_payload req_payload;
|
||||
struct drm_dp_mst_port *port;
|
||||
|
||||
mutex_lock(&mgr->payload_lock);
|
||||
for (i = 0; i < mgr->max_payloads; i++) {
|
||||
req_payload.start_slot = cur_slots;
|
||||
if (mgr->proposed_vcpis[i]) {
|
||||
port = container_of(mgr->proposed_vcpis[i],
|
||||
struct drm_dp_mst_port, vcpi);
|
||||
req_payload.num_slots =
|
||||
mgr->proposed_vcpis[i]->num_slots;
|
||||
req_payload.vcpi = mgr->proposed_vcpis[i]->vcpi;
|
||||
} else {
|
||||
port = NULL;
|
||||
req_payload.num_slots = 0;
|
||||
}
|
||||
|
||||
if (mgr->payloads[i].start_slot != req_payload.start_slot)
|
||||
mgr->payloads[i].start_slot = req_payload.start_slot;
|
||||
|
||||
if (mgr->payloads[i].num_slots != req_payload.num_slots) {
|
||||
if (req_payload.num_slots) {
|
||||
req_payload.payload_state = DP_PAYLOAD_LOCAL;
|
||||
mgr->payloads[i].num_slots =
|
||||
req_payload.num_slots;
|
||||
mgr->payloads[i].vcpi = req_payload.vcpi;
|
||||
} else if (mgr->payloads[i].num_slots) {
|
||||
mgr->payloads[i].num_slots = 0;
|
||||
mgr->payloads[i].payload_state =
|
||||
DP_PAYLOAD_DELETE_LOCAL;
|
||||
req_payload.payload_state =
|
||||
mgr->payloads[i].payload_state;
|
||||
mgr->payloads[i].start_slot = 0;
|
||||
} else
|
||||
req_payload.payload_state =
|
||||
mgr->payloads[i].payload_state;
|
||||
|
||||
mgr->payloads[i].payload_state =
|
||||
req_payload.payload_state;
|
||||
}
|
||||
cur_slots += req_payload.num_slots;
|
||||
}
|
||||
|
||||
for (i = 0; i < mgr->max_payloads; i++) {
|
||||
if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
|
||||
DP_DEBUG("removing payload %d\n", i);
|
||||
for (j = i; j < mgr->max_payloads - 1; j++) {
|
||||
memcpy(&mgr->payloads[j],
|
||||
&mgr->payloads[j + 1],
|
||||
sizeof(struct drm_dp_payload));
|
||||
mgr->proposed_vcpis[j] =
|
||||
mgr->proposed_vcpis[j + 1];
|
||||
if (mgr->proposed_vcpis[j] &&
|
||||
mgr->proposed_vcpis[j]->num_slots) {
|
||||
set_bit(j + 1, &mgr->payload_mask);
|
||||
} else {
|
||||
clear_bit(j + 1, &mgr->payload_mask);
|
||||
}
|
||||
}
|
||||
memset(&mgr->payloads[mgr->max_payloads - 1], 0,
|
||||
sizeof(struct drm_dp_payload));
|
||||
mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL;
|
||||
clear_bit(mgr->max_payloads, &mgr->payload_mask);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&mgr->payload_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_mst_sim_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
struct drm_dp_mst_port *port;
|
||||
int i;
|
||||
|
||||
mutex_lock(&mgr->payload_lock);
|
||||
for (i = 0; i < mgr->max_payloads; i++) {
|
||||
|
||||
if (!mgr->proposed_vcpis[i])
|
||||
continue;
|
||||
|
||||
port = container_of(mgr->proposed_vcpis[i],
|
||||
struct drm_dp_mst_port, vcpi);
|
||||
|
||||
DP_DEBUG("payload %d %d\n", i, mgr->payloads[i].payload_state);
|
||||
if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL)
|
||||
mgr->payloads[i].payload_state = DP_PAYLOAD_REMOTE;
|
||||
else if (mgr->payloads[i].payload_state ==
|
||||
DP_PAYLOAD_DELETE_LOCAL)
|
||||
mgr->payloads[i].payload_state = 0;
|
||||
}
|
||||
mutex_unlock(&mgr->payload_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct edid *dp_mst_sim_get_edid(struct drm_connector *connector,
|
||||
struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port)
|
||||
{
|
||||
struct dp_mst_private *mst = container_of(mgr,
|
||||
struct dp_mst_private, mst_mgr);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
|
||||
if (mst->simulator.port_edids[i].valid &&
|
||||
mst->simulator.port_edids[i].port_number ==
|
||||
port->port_num) {
|
||||
return drm_edid_duplicate((struct edid *)
|
||||
(mst->simulator.port_edids[i].edid));
|
||||
}
|
||||
}
|
||||
|
||||
DRM_ERROR("edid not found for connector %d\n", connector->base.id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int dp_mst_sim_topology_mgr_set_mst(
|
||||
struct drm_dp_mst_topology_mgr *mgr,
|
||||
bool mst_state)
|
||||
{
|
||||
int rc;
|
||||
struct dp_mst_private *mst = container_of(mgr,
|
||||
struct dp_mst_private, mst_mgr);
|
||||
|
||||
DP_MST_DEBUG("enter: mst_state %d\n", mst_state);
|
||||
SDE_EVT32_EXTERNAL(SDE_EVTLOG_FUNC_ENTRY, mst_state);
|
||||
|
||||
rc = drm_dp_mst_topology_mgr_set_mst(mgr, mst_state);
|
||||
if (rc < 0) {
|
||||
DRM_ERROR("unable to set mst topology mgr, rc: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (mst_state)
|
||||
queue_work(system_long_wq, &mst->simulator.probe_work);
|
||||
|
||||
mst->simulator.mst_state = mst_state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dp_mst_sim_handle_hpd_irq(void *dp_display,
|
||||
struct dp_mst_hpd_info *info)
|
||||
{
|
||||
struct dp_display *dp;
|
||||
struct dp_mst_private *mst;
|
||||
struct drm_dp_mst_port *port;
|
||||
struct dp_mst_sim_port_data port_data;
|
||||
struct drm_dp_mst_branch *mstb;
|
||||
int i;
|
||||
bool in_list, port_available;
|
||||
|
||||
dp = dp_display;
|
||||
mst = dp->dp_mst_prv_info;
|
||||
|
||||
if (info->mst_sim_add_con) {
|
||||
port_available = false;
|
||||
for (i = 0; i < DP_MST_SIM_MAX_PORTS; i++) {
|
||||
if (mst->simulator.port_edids[i].valid) continue;
|
||||
|
||||
port_data.port_number = i;
|
||||
mst->simulator.port_edids[i].port_number = i;
|
||||
memcpy(mst->simulator.port_edids[i].edid, info->edid,
|
||||
SZ_256);
|
||||
mst->simulator.port_edids[i].valid = true;
|
||||
port_available = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!port_available) {
|
||||
DRM_ERROR("add port failed, limit (%d) reached\n",
|
||||
DP_MST_SIM_MAX_PORTS);
|
||||
return;
|
||||
}
|
||||
|
||||
port_data.input_port = false;
|
||||
port_data.peer_device_type = DP_PEER_DEVICE_SST_SINK;
|
||||
port_data.mcs = false;
|
||||
port_data.ddps = true;
|
||||
port_data.legacy_device_plug_status = false;
|
||||
port_data.dpcd_revision = 0;
|
||||
port_data.num_sdp_streams = 0;
|
||||
port_data.num_sdp_stream_sinks = 0;
|
||||
|
||||
dp_mst_sim_add_port(mst, &port_data);
|
||||
} else if (info->mst_sim_remove_con) {
|
||||
mstb = mst->mst_mgr.mst_primary;
|
||||
in_list = false;
|
||||
|
||||
mutex_lock(&mst->mst_mgr.lock);
|
||||
list_for_each_entry(port,
|
||||
&mstb->ports, next) {
|
||||
if (port->connector && port->connector->base.id ==
|
||||
info->mst_sim_remove_con_id) {
|
||||
in_list = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&mst->mst_mgr.lock);
|
||||
|
||||
if (!in_list) {
|
||||
DRM_ERROR("invalid connector id %d\n",
|
||||
info->mst_sim_remove_con_id);
|
||||
return;
|
||||
} else {
|
||||
dp_mst_sim_remove_port(mst, port);
|
||||
}
|
||||
|
||||
dp_mst_sim_topology_put_port(port);
|
||||
}
|
||||
}
|
||||
|
||||
static int dp_mst_detect_port(
|
||||
struct drm_connector *connector,
|
||||
struct drm_modeset_acquire_ctx *ctx,
|
||||
@@ -719,23 +278,6 @@ static const struct dp_drm_mst_fw_helper_ops drm_dp_mst_fw_helper_ops = {
|
||||
.deallocate_vcpi = drm_dp_mst_deallocate_vcpi,
|
||||
};
|
||||
|
||||
static const struct dp_drm_mst_fw_helper_ops drm_dp_sim_mst_fw_helper_ops = {
|
||||
.calc_pbn_mode = dp_mst_calc_pbn_mode,
|
||||
.find_vcpi_slots = drm_dp_find_vcpi_slots,
|
||||
.atomic_find_vcpi_slots = drm_dp_atomic_find_vcpi_slots,
|
||||
.allocate_vcpi = drm_dp_mst_allocate_vcpi,
|
||||
.update_payload_part1 = dp_mst_sim_update_payload_part1,
|
||||
.check_act_status = dp_mst_sim_no_action,
|
||||
.update_payload_part2 = dp_mst_sim_update_payload_part2,
|
||||
.detect_port_ctx = dp_mst_detect_port,
|
||||
.get_edid = dp_mst_sim_get_edid,
|
||||
.topology_mgr_set_mst = dp_mst_sim_topology_mgr_set_mst,
|
||||
.get_vcpi_info = _dp_mst_get_vcpi_info,
|
||||
.atomic_release_vcpi_slots = drm_dp_atomic_release_vcpi_slots,
|
||||
.reset_vcpi_slots = drm_dp_mst_reset_vcpi_slots,
|
||||
.deallocate_vcpi = drm_dp_mst_deallocate_vcpi,
|
||||
};
|
||||
|
||||
/* DP MST Bridge OPs */
|
||||
|
||||
static int dp_mst_bridge_attach(struct drm_bridge *dp_bridge,
|
||||
@@ -1008,9 +550,6 @@ static void _dp_mst_bridge_pre_disable_part2(struct dp_mst_bridge *dp_bridge)
|
||||
port->vcpi.vcpi = dp_bridge->vcpi;
|
||||
mst->mst_fw_cbs->deallocate_vcpi(&mst->mst_mgr, port);
|
||||
|
||||
if (mst->simulator.mst_state)
|
||||
dp_mst_sim_remove_port(mst, port);
|
||||
|
||||
dp_bridge->vcpi = 0;
|
||||
dp_bridge->pbn = 0;
|
||||
|
||||
@@ -2159,8 +1698,7 @@ static void dp_mst_hpd_event_notify(struct dp_mst_private *mst, bool hpd_status)
|
||||
|
||||
/* DP Driver Callback OPs */
|
||||
|
||||
static int dp_mst_display_set_mgr_state(void *dp_display, bool state,
|
||||
struct dp_mst_hpd_info *info)
|
||||
static int dp_mst_display_set_mgr_state(void *dp_display, bool state)
|
||||
{
|
||||
int rc;
|
||||
struct dp_display *dp = dp_display;
|
||||
@@ -2176,15 +1714,7 @@ static int dp_mst_display_set_mgr_state(void *dp_display, bool state,
|
||||
if (state)
|
||||
mst->mst_session_state = state;
|
||||
|
||||
if (info && !info->mst_protocol) {
|
||||
if (state) {
|
||||
mst->simulator.edid = (struct edid *)info->edid;
|
||||
mst->simulator.port_cnt = info->mst_port_cnt;
|
||||
}
|
||||
mst->mst_fw_cbs = &drm_dp_sim_mst_fw_helper_ops;
|
||||
} else {
|
||||
mst->mst_fw_cbs = &drm_dp_mst_fw_helper_ops;
|
||||
}
|
||||
mst->mst_fw_cbs = &drm_dp_mst_fw_helper_ops;
|
||||
|
||||
rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr, state);
|
||||
if (rc < 0) {
|
||||
@@ -2215,8 +1745,7 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status)
|
||||
dp_mst_hpd_event_notify(mst, hpd_status);
|
||||
}
|
||||
|
||||
static void dp_mst_display_hpd_irq(void *dp_display,
|
||||
struct dp_mst_hpd_info *info)
|
||||
static void dp_mst_display_hpd_irq(void *dp_display)
|
||||
{
|
||||
int rc;
|
||||
struct dp_display *dp = dp_display;
|
||||
@@ -2228,26 +1757,6 @@ static void dp_mst_display_hpd_irq(void *dp_display,
|
||||
struct drm_connector *conn;
|
||||
struct sde_connector *c_conn;
|
||||
|
||||
if (info->mst_hpd_sim) {
|
||||
if (mst->simulator.mst_state && (info->mst_sim_add_con ||
|
||||
info->mst_sim_remove_con)) {
|
||||
dp_mst_sim_handle_hpd_irq(dp_display, info);
|
||||
|
||||
/*
|
||||
* When removing a connector, hpd_irq -> sim_destroy ->
|
||||
* destroy_connector_work will be executed in a thread.
|
||||
* This thread will perform the dp_mst_hotplug at the
|
||||
* appropriate time. Do not perform hotplug here
|
||||
* because it may be too early.
|
||||
*/
|
||||
if (info->mst_sim_remove_con)
|
||||
return;
|
||||
}
|
||||
|
||||
dp_mst_hotplug(&mst->mst_mgr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mst->mst_session_state) {
|
||||
DP_ERR("mst_hpd_irq received before mst session start\n");
|
||||
return;
|
||||
@@ -2324,12 +1833,6 @@ static const struct drm_dp_mst_topology_cbs dp_mst_fixed_drm_cbs = {
|
||||
.add_connector = dp_mst_add_fixed_connector,
|
||||
};
|
||||
|
||||
static void dp_mst_sim_init(struct dp_mst_private *mst)
|
||||
{
|
||||
INIT_WORK(&mst->simulator.probe_work, dp_mst_sim_link_probe_work);
|
||||
mst->simulator.cbs = &dp_mst_drm_cbs;
|
||||
}
|
||||
|
||||
int dp_mst_init(struct dp_display *dp_display)
|
||||
{
|
||||
struct drm_device *dev;
|
||||
@@ -2378,8 +1881,6 @@ int dp_mst_init(struct dp_display *dp_display)
|
||||
goto error;
|
||||
}
|
||||
|
||||
dp_mst_sim_init(&dp_mst);
|
||||
|
||||
dp_mst.mst_initialized = true;
|
||||
|
||||
/* create drm_bridges for cached mst encoders and clear cache */
|
||||
|
Reference in New Issue
Block a user