Merge "drm/msm/dp: add encoder and connector reservation dp-mst"
This commit is contained in:
commit
1a40281936
24 changed files with 1314 additions and 276 deletions
|
@ -100,11 +100,14 @@ Optional properties:
|
|||
- compatible: Must be "qcom,msm-ext-disp"
|
||||
- qcom,dp-low-power-hw-hpd: Low power hardware HPD feature enable control node
|
||||
- qcom,phy-version: Phy version
|
||||
- qcom,pn-swap-lane-map: P/N swap configuration of each lane
|
||||
- pinctrl-names: List of names to assign mdss pin states defined in pinctrl device node
|
||||
Refer to pinctrl-bindings.txt
|
||||
- pinctrl-<0..n>: Lists phandles each pointing to the pin configuration node within a pin
|
||||
controller. These pin configurations are installed in the pinctrl
|
||||
device node. Refer to pinctrl-bindings.txt
|
||||
- qcom,max-lclk-frequency-khz: An integer specifying the max. link clock in KHz supported by Display Port.
|
||||
- qcom,mst-fixed-topology-ports: u32 values of which MST output port to reserve, start from one
|
||||
|
||||
[Optional child nodes]: These nodes are for devices which are
|
||||
dependent on msm_ext_disp. If msm_ext_disp is disabled then
|
||||
|
|
|
@ -51,10 +51,6 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
|
|||
int id,
|
||||
struct drm_dp_payload *payload);
|
||||
|
||||
static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port,
|
||||
int offset, int size, u8 *bytes);
|
||||
|
||||
static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_branch *mstb);
|
||||
static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
|
||||
|
@ -439,6 +435,7 @@ static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx
|
|||
if (idx > raw->curlen)
|
||||
goto fail_len;
|
||||
repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
|
||||
idx++;
|
||||
if (idx > raw->curlen)
|
||||
goto fail_len;
|
||||
|
||||
|
@ -1402,7 +1399,6 @@ static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
|
|||
return false;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
|
||||
{
|
||||
struct drm_dp_sideband_msg_req_body req;
|
||||
|
@ -1415,7 +1411,6 @@ static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
|
||||
bool up, u8 *msg, int len)
|
||||
|
@ -1981,30 +1976,65 @@ int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_dp_update_payload_part2);
|
||||
|
||||
#if 0 /* unused as of yet */
|
||||
static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
|
||||
int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port,
|
||||
int offset, int size)
|
||||
int offset, int size, u8 *bytes)
|
||||
{
|
||||
int len;
|
||||
int ret;
|
||||
struct drm_dp_sideband_msg_tx *txmsg;
|
||||
struct drm_dp_mst_branch *mstb;
|
||||
|
||||
memset(bytes, 0, size);
|
||||
|
||||
mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
|
||||
if (!mstb)
|
||||
return -EINVAL;
|
||||
|
||||
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
|
||||
if (!txmsg)
|
||||
return -ENOMEM;
|
||||
if (!txmsg) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_put;
|
||||
}
|
||||
|
||||
len = build_dpcd_read(txmsg, port->port_num, 0, 8);
|
||||
txmsg->dst = port->parent;
|
||||
len = build_dpcd_read(txmsg, port->port_num, offset, size);
|
||||
txmsg->dst = mstb;
|
||||
|
||||
drm_dp_queue_down_tx(mgr, txmsg);
|
||||
ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
|
||||
if (ret <= 0) {
|
||||
DRM_ERROR("dpcd read failed\n");
|
||||
goto fail_free_msg;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (txmsg->reply.reply_type == 1) {
|
||||
DRM_ERROR("dpcd read nack received\n");
|
||||
ret = -EINVAL;
|
||||
goto fail_free_msg;
|
||||
}
|
||||
|
||||
if (port->port_num != txmsg->reply.u.remote_dpcd_read_ack.port_number) {
|
||||
DRM_ERROR("got incorrect port in response\n");
|
||||
ret = -EINVAL;
|
||||
goto fail_free_msg;
|
||||
}
|
||||
|
||||
if (size > txmsg->reply.u.remote_dpcd_read_ack.num_bytes)
|
||||
size = txmsg->reply.u.remote_dpcd_read_ack.num_bytes;
|
||||
|
||||
memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, size);
|
||||
|
||||
fail_free_msg:
|
||||
kfree(txmsg);
|
||||
fail_put:
|
||||
drm_dp_put_mst_branch_device(mstb);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
EXPORT_SYMBOL(drm_dp_send_dpcd_read);
|
||||
|
||||
static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port,
|
||||
int offset, int size, u8 *bytes)
|
||||
int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port,
|
||||
int offset, int size, u8 *bytes)
|
||||
{
|
||||
int len;
|
||||
int ret;
|
||||
|
@ -2038,6 +2068,7 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
|
|||
drm_dp_put_mst_branch_device(mstb);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_dp_send_dpcd_write);
|
||||
|
||||
static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
|
||||
{
|
||||
|
|
|
@ -882,6 +882,30 @@ static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl,
|
|||
0xe4);
|
||||
}
|
||||
|
||||
static void dp_catalog_ctrl_lane_pnswap(struct dp_catalog_ctrl *ctrl,
|
||||
u8 ln_pnswap)
|
||||
{
|
||||
struct dp_catalog_private *catalog;
|
||||
struct dp_io_data *io_data;
|
||||
u32 cfg0, cfg1;
|
||||
|
||||
catalog = dp_catalog_get_priv(ctrl);
|
||||
|
||||
cfg0 = 0x0a;
|
||||
cfg1 = 0x0a;
|
||||
|
||||
cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
|
||||
cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2;
|
||||
cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
|
||||
cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2;
|
||||
|
||||
io_data = catalog->io.dp_ln_tx0;
|
||||
dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV, cfg0);
|
||||
|
||||
io_data = catalog->io.dp_ln_tx1;
|
||||
dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV, cfg1);
|
||||
}
|
||||
|
||||
static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
|
||||
bool enable)
|
||||
{
|
||||
|
@ -2509,6 +2533,7 @@ struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
|
|||
.state_ctrl = dp_catalog_ctrl_state_ctrl,
|
||||
.config_ctrl = dp_catalog_ctrl_config_ctrl,
|
||||
.lane_mapping = dp_catalog_ctrl_lane_mapping,
|
||||
.lane_pnswap = dp_catalog_ctrl_lane_pnswap,
|
||||
.mainlink_ctrl = dp_catalog_ctrl_mainlink_ctrl,
|
||||
.set_pattern = dp_catalog_ctrl_set_pattern,
|
||||
.reset = dp_catalog_ctrl_reset,
|
||||
|
|
|
@ -1,6 +1,19 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
|
||||
<<<<<<< HEAD
|
||||
=======
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
>>>>>>> aacf58a... drm/msm/dp: Add P/N swap support for dp phy
|
||||
*/
|
||||
|
||||
#ifndef _DP_CATALOG_H_
|
||||
|
@ -93,6 +106,7 @@ struct dp_catalog_ctrl {
|
|||
void (*config_ctrl)(struct dp_catalog_ctrl *ctrl, u8 ln_cnt);
|
||||
void (*lane_mapping)(struct dp_catalog_ctrl *ctrl, bool flipped,
|
||||
char *lane_map);
|
||||
void (*lane_pnswap)(struct dp_catalog_ctrl *ctrl, u8 ln_pnswap);
|
||||
void (*mainlink_ctrl)(struct dp_catalog_ctrl *ctrl, bool enable);
|
||||
void (*set_pattern)(struct dp_catalog_ctrl *ctrl, u32 pattern);
|
||||
void (*reset)(struct dp_catalog_ctrl *ctrl);
|
||||
|
|
|
@ -266,6 +266,30 @@ static void dp_catalog_ctrl_update_vx_px_v420(struct dp_catalog_ctrl *ctrl,
|
|||
}
|
||||
}
|
||||
|
||||
static void dp_catalog_ctrl_lane_pnswap_v420(struct dp_catalog_ctrl *ctrl,
|
||||
u8 ln_pnswap)
|
||||
{
|
||||
struct dp_catalog_private_v420 *catalog;
|
||||
struct dp_io_data *io_data;
|
||||
u32 cfg0, cfg1;
|
||||
|
||||
catalog = dp_catalog_get_priv_v420(ctrl);
|
||||
|
||||
cfg0 = 0x0a;
|
||||
cfg1 = 0x0a;
|
||||
|
||||
cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
|
||||
cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2;
|
||||
cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
|
||||
cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2;
|
||||
|
||||
io_data = catalog->io->dp_ln_tx0;
|
||||
dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV_V420, cfg0);
|
||||
|
||||
io_data = catalog->io->dp_ln_tx1;
|
||||
dp_write(catalog->exe_mode, io_data, TXn_TX_POL_INV_V420, cfg1);
|
||||
}
|
||||
|
||||
static void dp_catalog_put_v420(struct dp_catalog *catalog)
|
||||
{
|
||||
struct dp_catalog_private_v420 *catalog_priv;
|
||||
|
@ -316,6 +340,7 @@ int dp_catalog_get_v420(struct device *dev, struct dp_catalog *catalog,
|
|||
catalog->panel.config_msa = dp_catalog_panel_config_msa_v420;
|
||||
catalog->ctrl.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg_v420;
|
||||
catalog->ctrl.update_vx_px = dp_catalog_ctrl_update_vx_px_v420;
|
||||
catalog->ctrl.lane_pnswap = dp_catalog_ctrl_lane_pnswap_v420;
|
||||
|
||||
/* Set the default execution mode to hardware mode */
|
||||
dp_catalog_set_exe_mode_v420(catalog, "hw");
|
||||
|
|
|
@ -161,6 +161,8 @@ static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl,
|
|||
if (enable) {
|
||||
ctrl->catalog->lane_mapping(ctrl->catalog, ctrl->orientation,
|
||||
ctrl->parser->l_map);
|
||||
ctrl->catalog->lane_pnswap(ctrl->catalog,
|
||||
ctrl->parser->l_pnswap);
|
||||
ctrl->catalog->mst_config(ctrl->catalog, ctrl->mst_mode);
|
||||
ctrl->catalog->config_ctrl(ctrl->catalog,
|
||||
ctrl->link->link_params.lane_count);
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "dp_power.h"
|
||||
#include "dp_catalog.h"
|
||||
#include "dp_aux.h"
|
||||
#include "dp_ctrl.h"
|
||||
#include "dp_debug.h"
|
||||
#include "drm_connector.h"
|
||||
#include "sde_connector.h"
|
||||
|
@ -41,6 +40,8 @@ struct dp_debug_private {
|
|||
struct device *dev;
|
||||
struct dp_debug dp_debug;
|
||||
struct dp_parser *parser;
|
||||
struct dp_ctrl *ctrl;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
static int dp_debug_get_edid_buf(struct dp_debug_private *debug)
|
||||
|
@ -90,6 +91,8 @@ static ssize_t dp_debug_write_edid(struct file *file,
|
|||
if (!debug)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&debug->lock);
|
||||
|
||||
if (*ppos)
|
||||
goto bail;
|
||||
|
||||
|
@ -161,6 +164,7 @@ static ssize_t dp_debug_write_edid(struct file *file,
|
|||
*/
|
||||
pr_info("[%s]\n", edid ? "SET" : "CLEAR");
|
||||
|
||||
mutex_unlock(&debug->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -180,6 +184,8 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
|
|||
if (!debug)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&debug->lock);
|
||||
|
||||
if (*ppos)
|
||||
goto bail;
|
||||
|
||||
|
@ -260,6 +266,7 @@ static ssize_t dp_debug_write_dpcd(struct file *file,
|
|||
} else
|
||||
debug->aux->dpcd_updated(debug->aux);
|
||||
|
||||
mutex_unlock(&debug->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -747,7 +754,7 @@ static ssize_t dp_debug_write_exe_mode(struct file *file,
|
|||
const char __user *user_buff, size_t count, loff_t *ppos)
|
||||
{
|
||||
struct dp_debug_private *debug = file->private_data;
|
||||
char *buf;
|
||||
char buf[SZ_32];
|
||||
size_t len = 0;
|
||||
|
||||
if (!debug)
|
||||
|
@ -757,7 +764,9 @@ static ssize_t dp_debug_write_exe_mode(struct file *file,
|
|||
return 0;
|
||||
|
||||
len = min_t(size_t, count, SZ_32 - 1);
|
||||
buf = memdup_user(user_buff, len);
|
||||
if (copy_from_user(buf, user_buff, len))
|
||||
goto end;
|
||||
|
||||
buf[len] = '\0';
|
||||
|
||||
if (sscanf(buf, "%3s", debug->exe_mode) != 1)
|
||||
|
@ -1437,6 +1446,7 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
|
|||
|
||||
if (dp_debug_get_dpcd_buf(debug)) {
|
||||
devm_kfree(debug->dev, debug->edid);
|
||||
debug->edid = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1444,6 +1454,9 @@ static void dp_debug_set_sim_mode(struct dp_debug_private *debug, bool sim)
|
|||
debug->aux->set_sim_mode(debug->aux, true,
|
||||
debug->edid, debug->dpcd);
|
||||
} else {
|
||||
debug->aux->abort(debug->aux);
|
||||
debug->ctrl->abort(debug->ctrl);
|
||||
|
||||
debug->aux->set_sim_mode(debug->aux, false, NULL, NULL);
|
||||
debug->dp_debug.sim_mode = false;
|
||||
|
||||
|
@ -1482,6 +1495,8 @@ static ssize_t dp_debug_write_sim(struct file *file,
|
|||
if (*ppos)
|
||||
return 0;
|
||||
|
||||
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))
|
||||
|
@ -1494,6 +1509,7 @@ static ssize_t dp_debug_write_sim(struct file *file,
|
|||
|
||||
dp_debug_set_sim_mode(debug, sim);
|
||||
end:
|
||||
mutex_unlock(&debug->lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
|
@ -1941,6 +1957,14 @@ static int dp_debug_init(struct dp_debug *dp_debug)
|
|||
DEBUG_NAME, rc);
|
||||
}
|
||||
|
||||
file = debugfs_create_u32("max_lclk_khz", 0644, dir,
|
||||
&debug->parser->max_lclk_khz);
|
||||
if (IS_ERR_OR_NULL(file)) {
|
||||
rc = PTR_ERR(file);
|
||||
pr_err("[%s] debugfs max_lclk_khz failed, rc=%d\n",
|
||||
DEBUG_NAME, rc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error_remove_dir:
|
||||
|
@ -1972,7 +1996,9 @@ static void dp_debug_abort(struct dp_debug *dp_debug)
|
|||
|
||||
debug = container_of(dp_debug, struct dp_debug_private, dp_debug);
|
||||
|
||||
mutex_lock(&debug->lock);
|
||||
dp_debug_set_sim_mode(debug, false);
|
||||
mutex_unlock(&debug->lock);
|
||||
}
|
||||
|
||||
struct dp_debug *dp_debug_get(struct dp_debug_in *in)
|
||||
|
@ -1981,7 +2007,8 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
|
|||
struct dp_debug_private *debug;
|
||||
struct dp_debug *dp_debug;
|
||||
|
||||
if (!in->dev || !in->panel || !in->hpd || !in->link || !in->catalog) {
|
||||
if (!in->dev || !in->panel || !in->hpd || !in->link ||
|
||||
!in->catalog || !in->ctrl) {
|
||||
pr_err("invalid input\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
|
@ -2002,12 +2029,15 @@ struct dp_debug *dp_debug_get(struct dp_debug_in *in)
|
|||
debug->connector = in->connector;
|
||||
debug->catalog = in->catalog;
|
||||
debug->parser = in->parser;
|
||||
debug->ctrl = in->ctrl;
|
||||
|
||||
dp_debug = &debug->dp_debug;
|
||||
dp_debug->vdisplay = 0;
|
||||
dp_debug->hdisplay = 0;
|
||||
dp_debug->vrefresh = 0;
|
||||
|
||||
mutex_init(&debug->lock);
|
||||
|
||||
rc = dp_debug_init(dp_debug);
|
||||
if (rc) {
|
||||
devm_kfree(in->dev, debug);
|
||||
|
@ -2059,6 +2089,8 @@ void dp_debug_put(struct dp_debug *dp_debug)
|
|||
|
||||
dp_debug_deinit(dp_debug);
|
||||
|
||||
mutex_destroy(&debug->lock);
|
||||
|
||||
if (debug->edid)
|
||||
devm_kfree(debug->dev, debug->edid);
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define _DP_DEBUG_H_
|
||||
|
||||
#include "dp_panel.h"
|
||||
#include "dp_ctrl.h"
|
||||
#include "dp_link.h"
|
||||
#include "dp_usbpd.h"
|
||||
#include "dp_aux.h"
|
||||
|
@ -63,6 +64,7 @@ struct dp_debug_in {
|
|||
struct drm_connector **connector;
|
||||
struct dp_catalog *catalog;
|
||||
struct dp_parser *parser;
|
||||
struct dp_ctrl *ctrl;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -103,6 +103,8 @@ struct dp_display_private {
|
|||
|
||||
u32 tot_dsc_blks_in_use;
|
||||
|
||||
bool process_hpd_connect;
|
||||
|
||||
struct notifier_block usb_nb;
|
||||
};
|
||||
|
||||
|
@ -111,11 +113,6 @@ static const struct of_device_id dp_dt_match[] = {
|
|||
{}
|
||||
};
|
||||
|
||||
static bool dp_display_framework_ready(struct dp_display_private *dp)
|
||||
{
|
||||
return dp->dp_display.post_open ? false : true;
|
||||
}
|
||||
|
||||
static inline bool dp_display_is_hdcp_enabled(struct dp_display_private *dp)
|
||||
{
|
||||
return dp->link->hdcp_status.hdcp_version && dp->hdcp.ops;
|
||||
|
@ -236,16 +233,64 @@ static void dp_display_check_source_hdcp_caps(struct dp_display_private *dp)
|
|||
struct sde_hdcp_ops *ops = dev->ops;
|
||||
void *fd = dev->fd;
|
||||
|
||||
if (!fd || !ops || (dp->hdcp.source_cap & dev->ver))
|
||||
if (!fd || !ops)
|
||||
continue;
|
||||
|
||||
if (ops->feature_supported(fd))
|
||||
if (ops->set_mode && ops->set_mode(fd, dp->mst.mst_active))
|
||||
continue;
|
||||
|
||||
if (!(dp->hdcp.source_cap & dev->ver) &&
|
||||
ops->feature_supported &&
|
||||
ops->feature_supported(fd))
|
||||
dp->hdcp.source_cap |= dev->ver;
|
||||
}
|
||||
|
||||
dp_display_update_hdcp_status(dp, false);
|
||||
}
|
||||
|
||||
static void dp_display_hdcp_register_streams(struct dp_display_private *dp)
|
||||
{
|
||||
int rc;
|
||||
size_t i;
|
||||
struct sde_hdcp_ops *ops = dp->hdcp.ops;
|
||||
void *data = dp->hdcp.data;
|
||||
|
||||
if (dp_display_is_ready(dp) && dp->mst.mst_active && ops &&
|
||||
ops->register_streams){
|
||||
struct stream_info streams[DP_STREAM_MAX];
|
||||
int index = 0;
|
||||
|
||||
pr_debug("Registering all active panel streams with HDCP\n");
|
||||
for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
|
||||
if (!dp->active_panels[i])
|
||||
continue;
|
||||
streams[index].stream_id = i;
|
||||
streams[index].virtual_channel =
|
||||
dp->active_panels[i]->vcpi;
|
||||
index++;
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
rc = ops->register_streams(data, index, streams);
|
||||
if (rc)
|
||||
pr_err("failed to register streams. rc = %d\n",
|
||||
rc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dp_display_hdcp_deregister_stream(struct dp_display_private *dp,
|
||||
enum dp_stream_id stream_id)
|
||||
{
|
||||
if (dp->hdcp.ops->deregister_streams) {
|
||||
struct stream_info stream = {stream_id,
|
||||
dp->active_panels[stream_id]->vcpi};
|
||||
|
||||
pr_debug("Deregistering stream within HDCP library\n");
|
||||
dp->hdcp.ops->deregister_streams(dp->hdcp.data, 1, &stream);
|
||||
}
|
||||
}
|
||||
|
||||
static void dp_display_hdcp_cb_work(struct work_struct *work)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
|
@ -255,12 +300,21 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
|
|||
void *data;
|
||||
int rc = 0;
|
||||
u32 hdcp_auth_state;
|
||||
u8 sink_status = 0;
|
||||
|
||||
dp = container_of(dw, struct dp_display_private, hdcp_cb_work);
|
||||
|
||||
if (!dp->power_on || !dp->is_connected || atomic_read(&dp->aborted))
|
||||
return;
|
||||
|
||||
drm_dp_dpcd_readb(dp->aux->drm_aux, DP_SINK_STATUS, &sink_status);
|
||||
sink_status &= (DP_RECEIVE_PORT_0_STATUS | DP_RECEIVE_PORT_1_STATUS);
|
||||
if (sink_status < 1) {
|
||||
pr_debug("Sink not synchronized. Queuing again then exiting\n");
|
||||
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
|
||||
return;
|
||||
}
|
||||
|
||||
status = &dp->link->hdcp_status;
|
||||
|
||||
if (status->hdcp_state == HDCP_STATE_INACTIVE) {
|
||||
|
@ -268,6 +322,11 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
|
|||
dp_display_update_hdcp_info(dp);
|
||||
|
||||
if (dp_display_is_hdcp_enabled(dp)) {
|
||||
if (dp->hdcp.ops && dp->hdcp.ops->on &&
|
||||
dp->hdcp.ops->on(dp->hdcp.data)) {
|
||||
dp_display_update_hdcp_status(dp, true);
|
||||
return;
|
||||
}
|
||||
status->hdcp_state = HDCP_STATE_AUTHENTICATING;
|
||||
} else {
|
||||
dp_display_update_hdcp_status(dp, true);
|
||||
|
@ -294,11 +353,18 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
|
|||
|
||||
switch (status->hdcp_state) {
|
||||
case HDCP_STATE_AUTHENTICATING:
|
||||
dp_display_hdcp_register_streams(dp);
|
||||
if (dp->hdcp.ops && dp->hdcp.ops->authenticate)
|
||||
rc = dp->hdcp.ops->authenticate(data);
|
||||
break;
|
||||
case HDCP_STATE_AUTH_FAIL:
|
||||
if (dp_display_is_ready(dp) && dp->power_on) {
|
||||
if (ops && ops->on && ops->on(data)) {
|
||||
dp_display_update_hdcp_status(dp, true);
|
||||
return;
|
||||
}
|
||||
dp_display_hdcp_register_streams(dp);
|
||||
status->hdcp_state = HDCP_STATE_AUTHENTICATING;
|
||||
if (ops && ops->reauthenticate) {
|
||||
rc = ops->reauthenticate(data);
|
||||
if (rc)
|
||||
|
@ -309,6 +375,7 @@ static void dp_display_hdcp_cb_work(struct work_struct *work)
|
|||
}
|
||||
break;
|
||||
default:
|
||||
dp_display_hdcp_register_streams(dp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -502,36 +569,6 @@ static void dp_display_send_hpd_event(struct dp_display_private *dp)
|
|||
envp);
|
||||
}
|
||||
|
||||
static void dp_display_post_open(struct dp_display *dp_display)
|
||||
{
|
||||
struct drm_connector *connector;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dp_display) {
|
||||
pr_err("invalid input\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
if (IS_ERR_OR_NULL(dp)) {
|
||||
pr_err("invalid params\n");
|
||||
return;
|
||||
}
|
||||
|
||||
connector = dp->dp_display.base_connector;
|
||||
|
||||
if (!connector) {
|
||||
pr_err("base connector not set\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* if cable is already connected, send notification */
|
||||
if (dp->hpd->hpd_high)
|
||||
queue_work(dp->wq, &dp->connect_work);
|
||||
else
|
||||
dp_display->post_open = NULL;
|
||||
}
|
||||
|
||||
static int dp_display_send_hpd_notification(struct dp_display_private *dp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -541,6 +578,8 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)
|
|||
|
||||
if (!dp->mst.mst_active)
|
||||
dp->dp_display.is_sst_connected = hpd;
|
||||
else
|
||||
dp->dp_display.is_sst_connected = false;
|
||||
|
||||
reinit_completion(&dp->notification_comp);
|
||||
dp_display_send_hpd_event(dp);
|
||||
|
@ -551,9 +590,6 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)
|
|||
if (!dp->mst.mst_active && (dp->power_on == hpd))
|
||||
goto skip_wait;
|
||||
|
||||
if (!dp_display_framework_ready(dp))
|
||||
goto skip_wait;
|
||||
|
||||
if (!wait_for_completion_timeout(&dp->notification_comp,
|
||||
HZ * 5)) {
|
||||
pr_warn("%s timeout\n", hpd ? "connect" : "disconnect");
|
||||
|
@ -571,30 +607,47 @@ static void dp_display_update_mst_state(struct dp_display_private *dp,
|
|||
dp->panel->mst_state = state;
|
||||
}
|
||||
|
||||
static void dp_display_process_mst_hpd_high(struct dp_display_private *dp)
|
||||
static void dp_display_process_mst_hpd_high(struct dp_display_private *dp,
|
||||
bool mst_probe)
|
||||
{
|
||||
bool is_mst_receiver;
|
||||
struct dp_mst_hpd_info info;
|
||||
int ret;
|
||||
|
||||
if (dp->parser->has_mst && dp->mst.drm_registered) {
|
||||
DP_MST_DEBUG("mst_hpd_high work\n");
|
||||
if (!dp->parser->has_mst || !dp->mst.drm_registered) {
|
||||
DP_MST_DEBUG("mst not enabled. has_mst:%d, registered:%d\n",
|
||||
dp->parser->has_mst, dp->mst.drm_registered);
|
||||
return;
|
||||
}
|
||||
|
||||
DP_MST_DEBUG("mst_hpd_high work. mst_probe:%d\n", mst_probe);
|
||||
|
||||
if (!dp->mst.mst_active) {
|
||||
is_mst_receiver = dp->panel->read_mst_cap(dp->panel);
|
||||
|
||||
if (is_mst_receiver && !dp->mst.mst_active) {
|
||||
|
||||
/* clear sink mst state */
|
||||
drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL, 0);
|
||||
|
||||
dp_display_update_mst_state(dp, true);
|
||||
|
||||
info.mst_protocol = dp->parser->has_mst_sideband;
|
||||
info.mst_port_cnt = dp->debug->mst_port_cnt;
|
||||
info.edid = dp->debug->get_edid(dp->debug);
|
||||
|
||||
if (dp->mst.cbs.hpd)
|
||||
dp->mst.cbs.hpd(&dp->dp_display, true, &info);
|
||||
if (!is_mst_receiver) {
|
||||
DP_MST_DEBUG("sink doesn't support mst\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* clear sink mst state */
|
||||
drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL, 0);
|
||||
|
||||
ret = drm_dp_dpcd_writeb(dp->aux->drm_aux, DP_MSTM_CTRL,
|
||||
DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
|
||||
if (ret < 0) {
|
||||
pr_err("sink mst enablement failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dp_display_update_mst_state(dp, true);
|
||||
} else if (dp->mst.mst_active && mst_probe) {
|
||||
info.mst_protocol = dp->parser->has_mst_sideband;
|
||||
info.mst_port_cnt = dp->debug->mst_port_cnt;
|
||||
info.edid = dp->debug->get_edid(dp->debug);
|
||||
|
||||
if (dp->mst.cbs.hpd)
|
||||
dp->mst.cbs.hpd(&dp->dp_display, true, &info);
|
||||
}
|
||||
|
||||
DP_MST_DEBUG("mst_hpd_high. mst_active:%d\n", dp->mst.mst_active);
|
||||
|
@ -648,7 +701,16 @@ static void dp_display_host_deinit(struct dp_display_private *dp)
|
|||
|
||||
static int dp_display_process_hpd_high(struct dp_display_private *dp)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc = -EINVAL;
|
||||
|
||||
mutex_lock(&dp->session_lock);
|
||||
|
||||
if (dp->is_connected) {
|
||||
pr_debug("dp already connected, skipping hpd high\n");
|
||||
mutex_unlock(&dp->session_lock);
|
||||
rc = -EISCONN;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->is_connected = true;
|
||||
|
||||
|
@ -671,25 +733,32 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
|
|||
* ETIMEDOUT --> cable may have been removed
|
||||
* ENOTCONN --> no downstream device connected
|
||||
*/
|
||||
if (rc == -ETIMEDOUT || rc == -ENOTCONN)
|
||||
if (rc == -ETIMEDOUT || rc == -ENOTCONN) {
|
||||
dp->is_connected = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->link->process_request(dp->link);
|
||||
dp->panel->handle_sink_request(dp->panel);
|
||||
|
||||
dp_display_process_mst_hpd_high(dp);
|
||||
dp_display_process_mst_hpd_high(dp, false);
|
||||
|
||||
mutex_lock(&dp->session_lock);
|
||||
rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active,
|
||||
dp->panel->fec_en, false);
|
||||
if (rc) {
|
||||
mutex_unlock(&dp->session_lock);
|
||||
dp->is_connected = false;
|
||||
goto end;
|
||||
}
|
||||
|
||||
dp->process_hpd_connect = false;
|
||||
|
||||
dp_display_process_mst_hpd_high(dp, true);
|
||||
end:
|
||||
mutex_unlock(&dp->session_lock);
|
||||
|
||||
dp_display_send_hpd_notification(dp);
|
||||
end:
|
||||
if (!rc)
|
||||
dp_display_send_hpd_notification(dp);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -715,6 +784,7 @@ static int dp_display_process_hpd_low(struct dp_display_private *dp)
|
|||
int rc = 0;
|
||||
|
||||
dp->is_connected = false;
|
||||
dp->process_hpd_connect = false;
|
||||
|
||||
dp_display_process_mst_hpd_low(dp);
|
||||
|
||||
|
@ -755,11 +825,15 @@ static int dp_display_usbpd_configure_cb(struct device *dev)
|
|||
goto end;
|
||||
}
|
||||
|
||||
mutex_lock(&dp->session_lock);
|
||||
dp_display_host_init(dp);
|
||||
|
||||
/* check for hpd high */
|
||||
if (dp->hpd->hpd_high)
|
||||
queue_work(dp->wq, &dp->connect_work);
|
||||
else
|
||||
dp->process_hpd_connect = true;
|
||||
mutex_unlock(&dp->session_lock);
|
||||
end:
|
||||
return rc;
|
||||
}
|
||||
|
@ -793,8 +867,10 @@ static void dp_display_clean(struct dp_display_private *dp)
|
|||
{
|
||||
int idx;
|
||||
struct dp_panel *dp_panel;
|
||||
struct dp_link_hdcp_status *status = &dp->link->hdcp_status;
|
||||
|
||||
if (dp_display_is_hdcp_enabled(dp)) {
|
||||
if (dp_display_is_hdcp_enabled(dp) &&
|
||||
status->hdcp_state != HDCP_STATE_INACTIVE) {
|
||||
cancel_delayed_work_sync(&dp->hdcp_cb_work);
|
||||
if (dp->hdcp.ops->off)
|
||||
dp->hdcp.ops->off(dp->hdcp.data);
|
||||
|
@ -878,18 +954,12 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/*
|
||||
* In case cable/dongle is disconnected during adb shell stop,
|
||||
* reset psm_enabled flag to false since it is no more needed
|
||||
*/
|
||||
if (dp->dp_display.post_open)
|
||||
dp->debug->psm_enabled = false;
|
||||
|
||||
if (dp->debug->psm_enabled)
|
||||
mutex_lock(&dp->session_lock);
|
||||
if (dp->debug->psm_enabled && dp->core_initialized)
|
||||
dp->link->psm_config(dp->link, &dp->panel->link_info, true);
|
||||
mutex_unlock(&dp->session_lock);
|
||||
|
||||
dp_display_disconnect_sync(dp);
|
||||
dp->dp_display.post_open = NULL;
|
||||
|
||||
if (!dp->debug->sim_mode && !dp->parser->no_aux_switch
|
||||
&& !dp->parser->gpio_aux_switch)
|
||||
|
@ -936,11 +1006,19 @@ static void dp_display_attention_work(struct work_struct *work)
|
|||
struct dp_display_private *dp = container_of(work,
|
||||
struct dp_display_private, attention_work);
|
||||
|
||||
if (dp->debug->mst_hpd_sim)
|
||||
goto mst_attention;
|
||||
mutex_lock(&dp->session_lock);
|
||||
|
||||
if (dp->link->process_request(dp->link))
|
||||
if (dp->debug->mst_hpd_sim || !dp->core_initialized) {
|
||||
mutex_unlock(&dp->session_lock);
|
||||
goto mst_attention;
|
||||
}
|
||||
|
||||
if (dp->link->process_request(dp->link)) {
|
||||
mutex_unlock(&dp->session_lock);
|
||||
goto cp_irq;
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->session_lock);
|
||||
|
||||
if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
|
||||
if (dp_display_is_sink_count_zero(dp)) {
|
||||
|
@ -997,16 +1075,16 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_debug("hpd_irq:%d, hpd_high:%d, power_on:%d\n",
|
||||
pr_debug("hpd_irq:%d, hpd_high:%d, power_on:%d, is_connected:%d\n",
|
||||
dp->hpd->hpd_irq, dp->hpd->hpd_high,
|
||||
dp->power_on);
|
||||
dp->power_on, dp->is_connected);
|
||||
|
||||
if (!dp->hpd->hpd_high)
|
||||
dp_display_disconnect_sync(dp);
|
||||
else if ((dp->hpd->hpd_irq && dp->core_initialized) ||
|
||||
dp->debug->mst_hpd_sim)
|
||||
queue_work(dp->wq, &dp->attention_work);
|
||||
else if (!dp->power_on)
|
||||
else if (dp->process_hpd_connect || !dp->is_connected)
|
||||
queue_work(dp->wq, &dp->connect_work);
|
||||
else
|
||||
pr_debug("ignored\n");
|
||||
|
@ -1228,6 +1306,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)
|
|||
debug_in.connector = &dp->dp_display.base_connector;
|
||||
debug_in.catalog = dp->catalog;
|
||||
debug_in.parser = dp->parser;
|
||||
debug_in.ctrl = dp->ctrl;
|
||||
|
||||
dp->debug = dp_debug_get(&debug_in);
|
||||
if (IS_ERR(dp->debug)) {
|
||||
|
@ -1399,7 +1478,7 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)
|
|||
|
||||
static int dp_display_set_stream_info(struct dp_display *dp_display,
|
||||
void *panel, u32 strm_id, u32 start_slot,
|
||||
u32 num_slots, u32 pbn)
|
||||
u32 num_slots, u32 pbn, int vcpi)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_panel *dp_panel;
|
||||
|
@ -1432,7 +1511,7 @@ static int dp_display_set_stream_info(struct dp_display *dp_display,
|
|||
if (panel) {
|
||||
dp_panel = panel;
|
||||
dp_panel->set_stream_info(dp_panel, strm_id, start_slot,
|
||||
num_slots, pbn);
|
||||
num_slots, pbn, vcpi);
|
||||
}
|
||||
|
||||
mutex_unlock(&dp->session_lock);
|
||||
|
@ -1539,8 +1618,6 @@ static int dp_display_post_enable(struct dp_display *dp_display, void *panel)
|
|||
cancel_delayed_work_sync(&dp->hdcp_cb_work);
|
||||
queue_delayed_work(dp->wq, &dp->hdcp_cb_work, HZ);
|
||||
end:
|
||||
/* clear framework event notifier */
|
||||
dp_display->post_open = NULL;
|
||||
dp->aux->state |= DP_STATE_CTRL_POWERED_ON;
|
||||
|
||||
complete_all(&dp->notification_comp);
|
||||
|
@ -1552,7 +1629,9 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)
|
|||
{
|
||||
struct dp_display_private *dp;
|
||||
struct dp_panel *dp_panel = panel;
|
||||
struct dp_link_hdcp_status *status;
|
||||
int rc = 0;
|
||||
size_t i;
|
||||
|
||||
if (!dp_display || !panel) {
|
||||
pr_err("invalid input\n");
|
||||
|
@ -1563,19 +1642,35 @@ static int dp_display_pre_disable(struct dp_display *dp_display, void *panel)
|
|||
|
||||
mutex_lock(&dp->session_lock);
|
||||
|
||||
status = &dp->link->hdcp_status;
|
||||
|
||||
if (!dp->power_on) {
|
||||
pr_debug("stream already powered off, return\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (dp_display_is_hdcp_enabled(dp)) {
|
||||
cancel_delayed_work_sync(&dp->hdcp_cb_work);
|
||||
if (dp_display_is_hdcp_enabled(dp) &&
|
||||
status->hdcp_state != HDCP_STATE_INACTIVE) {
|
||||
flush_delayed_work(&dp->hdcp_cb_work);
|
||||
if (dp->mst.mst_active) {
|
||||
dp_display_hdcp_deregister_stream(dp,
|
||||
dp_panel->stream_id);
|
||||
for (i = DP_STREAM_0; i < DP_STREAM_MAX; i++) {
|
||||
if (i != dp_panel->stream_id &&
|
||||
dp->active_panels[i]) {
|
||||
pr_debug("Streams are still active. Skip disabling HDCP\n");
|
||||
goto stream;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dp->hdcp.ops->off)
|
||||
dp->hdcp.ops->off(dp->hdcp.data);
|
||||
|
||||
dp_display_update_hdcp_status(dp, true);
|
||||
}
|
||||
|
||||
stream:
|
||||
if (dp_panel->audio_supported)
|
||||
dp_panel->audio->off(dp_panel->audio);
|
||||
|
||||
|
@ -1689,14 +1784,6 @@ static int dp_display_unprepare(struct dp_display *dp_display, void *panel)
|
|||
dp->link->psm_config(dp->link, &dp->panel->link_info, true);
|
||||
dp->debug->psm_enabled = true;
|
||||
|
||||
/*
|
||||
* In case of framework reboot, the DP off sequence is executed
|
||||
* without any notification from driver. Initialize post_open
|
||||
* callback to notify DP connection once framework restarts.
|
||||
*/
|
||||
dp_display->post_open = dp_display_post_open;
|
||||
dp->dp_display.is_sst_connected = false;
|
||||
|
||||
dp->ctrl->off(dp->ctrl);
|
||||
dp_display_host_deinit(dp);
|
||||
}
|
||||
|
@ -2259,6 +2346,77 @@ static int dp_display_update_pps(struct dp_display *dp_display,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_mst_connector_update_link_info(
|
||||
struct dp_display *dp_display,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
int rc = 0;
|
||||
struct sde_connector *sde_conn;
|
||||
struct dp_panel *dp_panel;
|
||||
struct dp_display_private *dp;
|
||||
|
||||
if (!dp_display || !connector) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
if (!dp->mst.drm_registered) {
|
||||
pr_debug("drm mst not registered\n");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
sde_conn = to_sde_connector(connector);
|
||||
if (!sde_conn->drv_panel) {
|
||||
pr_err("invalid panel for connector:%d\n", connector->base.id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp_panel = sde_conn->drv_panel;
|
||||
|
||||
memcpy(dp_panel->dpcd, dp->panel->dpcd,
|
||||
DP_RECEIVER_CAP_SIZE + 1);
|
||||
memcpy(dp_panel->dsc_dpcd, dp->panel->dsc_dpcd,
|
||||
DP_RECEIVER_DSC_CAP_SIZE + 1);
|
||||
memcpy(&dp_panel->link_info, &dp->panel->link_info,
|
||||
sizeof(dp_panel->link_info));
|
||||
|
||||
DP_MST_DEBUG("dp mst connector:%d link info updated\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_display_mst_get_fixed_topology_port(
|
||||
struct dp_display *dp_display,
|
||||
u32 strm_id, u32 *port_num)
|
||||
{
|
||||
struct dp_display_private *dp;
|
||||
u32 port;
|
||||
|
||||
if (!dp_display) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strm_id >= DP_STREAM_MAX) {
|
||||
pr_err("invalid stream id:%d\n", strm_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp = container_of(dp_display, struct dp_display_private, dp_display);
|
||||
|
||||
port = dp->parser->mst_fixed_port[strm_id];
|
||||
|
||||
if (!port || port > 255)
|
||||
return -ENOENT;
|
||||
|
||||
if (port_num)
|
||||
*port_num = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dp_display_get_mst_caps(struct dp_display *dp_display,
|
||||
struct dp_mst_caps *mst_caps)
|
||||
{
|
||||
|
@ -2332,7 +2490,7 @@ static int dp_display_probe(struct platform_device *pdev)
|
|||
g_dp_display->unprepare = dp_display_unprepare;
|
||||
g_dp_display->request_irq = dp_request_irq;
|
||||
g_dp_display->get_debug = dp_get_debug;
|
||||
g_dp_display->post_open = dp_display_post_open;
|
||||
g_dp_display->post_open = NULL;
|
||||
g_dp_display->post_init = dp_display_post_init;
|
||||
g_dp_display->config_hdr = dp_display_config_hdr;
|
||||
g_dp_display->mst_install = dp_display_mst_install;
|
||||
|
@ -2342,12 +2500,16 @@ static int dp_display_probe(struct platform_device *pdev)
|
|||
dp_display_mst_connector_uninstall;
|
||||
g_dp_display->mst_connector_update_edid =
|
||||
dp_display_mst_connector_update_edid;
|
||||
g_dp_display->mst_connector_update_link_info =
|
||||
dp_display_mst_connector_update_link_info;
|
||||
g_dp_display->get_mst_caps = dp_display_get_mst_caps;
|
||||
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;
|
||||
|
||||
rc = component_add(&pdev->dev, &dp_display_comp_ops);
|
||||
if (rc) {
|
||||
|
|
|
@ -100,13 +100,18 @@ struct dp_display {
|
|||
int (*mst_connector_update_edid)(struct dp_display *dp_display,
|
||||
struct drm_connector *connector,
|
||||
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,
|
||||
struct dp_mst_caps *mst_caps);
|
||||
int (*set_stream_info)(struct dp_display *dp_display, void *panel,
|
||||
u32 strm_id, u32 start_slot, u32 num_slots, u32 pbn);
|
||||
u32 strm_id, u32 start_slot, u32 num_slots, u32 pbn,
|
||||
int vcpi);
|
||||
void (*convert_to_dp_mode)(struct dp_display *dp_display, void *panel,
|
||||
const struct drm_display_mode *drm_mode,
|
||||
struct dp_display_mode *dp_mode);
|
||||
|
|
|
@ -114,7 +114,7 @@ static void dp_bridge_pre_enable(struct drm_bridge *drm_bridge)
|
|||
}
|
||||
|
||||
/* for SST force stream id, start slot and total slots to 0 */
|
||||
dp->set_stream_info(dp, bridge->dp_panel, 0, 0, 0, 0);
|
||||
dp->set_stream_info(dp, bridge->dp_panel, 0, 0, 0, 0, 0);
|
||||
|
||||
rc = dp->enable(dp, bridge->dp_panel);
|
||||
if (rc) {
|
||||
|
|
|
@ -51,7 +51,6 @@ struct dp_hdcp2p2_ctrl {
|
|||
u8 rx_status;
|
||||
char abort_mask;
|
||||
|
||||
bool cp_irq_done;
|
||||
bool polling;
|
||||
};
|
||||
|
||||
|
@ -66,6 +65,25 @@ struct dp_hdcp2p2_interrupts {
|
|||
struct dp_hdcp2p2_int_set *int_set;
|
||||
};
|
||||
|
||||
static inline int dp_hdcp2p2_valid_handle(struct dp_hdcp2p2_ctrl *ctrl)
|
||||
{
|
||||
if (!ctrl) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ctrl->lib_ctx) {
|
||||
pr_err("HDCP library needs to be acquired\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ctrl->lib) {
|
||||
pr_err("invalid lib ops data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool dp_hdcp2p2_is_valid_state(struct dp_hdcp2p2_ctrl *ctrl)
|
||||
{
|
||||
enum hdcp_transport_wakeup_cmd cmd;
|
||||
|
@ -174,6 +192,7 @@ static int dp_hdcp2p2_wakeup(struct hdcp_transport_wakeup_data *data)
|
|||
if (dp_hdcp2p2_copy_buf(ctrl, data))
|
||||
goto exit;
|
||||
|
||||
ctrl->polling = false;
|
||||
switch (data->cmd) {
|
||||
case HDCP_TRANSPORT_CMD_STATUS_SUCCESS:
|
||||
atomic_set(&ctrl->auth_state, HDCP_STATE_AUTHENTICATED);
|
||||
|
@ -216,38 +235,77 @@ static void dp_hdcp2p2_reset(struct dp_hdcp2p2_ctrl *ctrl)
|
|||
atomic_set(&ctrl->auth_state, HDCP_STATE_INACTIVE);
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_register(void *input, bool mst_enabled)
|
||||
{
|
||||
int rc;
|
||||
enum sde_hdcp_2x_device_type device_type;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
|
||||
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (mst_enabled)
|
||||
device_type = HDCP_TXMTR_DP_MST;
|
||||
else
|
||||
device_type = HDCP_TXMTR_DP;
|
||||
|
||||
return sde_hdcp_2x_enable(ctrl->lib_ctx, device_type);
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_on(void *input)
|
||||
{
|
||||
int rc = 0;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = input;
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
|
||||
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cdata.cmd = HDCP_2X_CMD_START;
|
||||
cdata.context = ctrl->lib_ctx;
|
||||
rc = ctrl->lib->wakeup(&cdata);
|
||||
if (rc)
|
||||
pr_err("Unable to start the HDCP 2.2 library (%d)\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void dp_hdcp2p2_off(void *input)
|
||||
{
|
||||
int rc;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
|
||||
struct hdcp_transport_wakeup_data cdata = {
|
||||
HDCP_TRANSPORT_CMD_AUTHENTICATE};
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
|
||||
|
||||
if (!ctrl) {
|
||||
pr_err("invalid input\n");
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
|
||||
pr_err("hdcp is off\n");
|
||||
return;
|
||||
if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) {
|
||||
cdata.cmd = HDCP_2X_CMD_STOP;
|
||||
cdata.context = ctrl->lib_ctx;
|
||||
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
|
||||
}
|
||||
|
||||
dp_hdcp2p2_set_interrupts(ctrl, false);
|
||||
|
||||
dp_hdcp2p2_reset(ctrl);
|
||||
|
||||
cdata.context = input;
|
||||
dp_hdcp2p2_wakeup(&cdata);
|
||||
|
||||
kthread_park(ctrl->thread);
|
||||
|
||||
sde_hdcp_2x_disable(ctrl->lib_ctx);
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_authenticate(void *input)
|
||||
{
|
||||
int rc;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = input;
|
||||
struct hdcp_transport_wakeup_data cdata = {
|
||||
HDCP_TRANSPORT_CMD_AUTHENTICATE};
|
||||
int rc = 0;
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dp_hdcp2p2_set_interrupts(ctrl, true);
|
||||
|
||||
|
@ -370,44 +428,34 @@ static int dp_hdcp2p2_aux_write_message(struct dp_hdcp2p2_ctrl *ctrl,
|
|||
|
||||
static bool dp_hdcp2p2_feature_supported(void *input)
|
||||
{
|
||||
int rc;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = input;
|
||||
struct sde_hdcp_2x_ops *lib = NULL;
|
||||
bool supported = false;
|
||||
|
||||
if (!ctrl) {
|
||||
pr_err("invalid input\n");
|
||||
goto end;
|
||||
}
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return supported;
|
||||
|
||||
lib = ctrl->lib;
|
||||
if (!lib) {
|
||||
pr_err("invalid lib ops data\n");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (lib->feature_supported)
|
||||
supported = lib->feature_supported(
|
||||
ctrl->lib_ctx);
|
||||
end:
|
||||
|
||||
return supported;
|
||||
}
|
||||
|
||||
static void dp_hdcp2p2_force_encryption(void *data, bool enable)
|
||||
{
|
||||
int rc;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = data;
|
||||
struct sde_hdcp_2x_ops *lib = NULL;
|
||||
|
||||
if (!ctrl) {
|
||||
pr_err("invalid input\n");
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return;
|
||||
}
|
||||
|
||||
lib = ctrl->lib;
|
||||
if (!lib) {
|
||||
pr_err("invalid lib ops data\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (lib->force_encryption)
|
||||
lib->force_encryption(ctrl->lib_ctx, enable);
|
||||
}
|
||||
|
@ -493,26 +541,12 @@ static void dp_hdcp2p2_recv_msg(struct dp_hdcp2p2_ctrl *ctrl)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ctrl->rx_status) {
|
||||
if (!ctrl->cp_irq_done) {
|
||||
pr_debug("waiting for CP_IRQ\n");
|
||||
ctrl->polling = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctrl->rx_status & ctrl->sink_rx_status) {
|
||||
ctrl->cp_irq_done = false;
|
||||
ctrl->sink_rx_status = 0;
|
||||
ctrl->rx_status = 0;
|
||||
}
|
||||
}
|
||||
|
||||
dp_hdcp2p2_get_msg_from_sink(ctrl);
|
||||
}
|
||||
|
||||
static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc = 0, retries = 10;
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
|
||||
|
||||
if (!ctrl) {
|
||||
|
@ -545,6 +579,11 @@ static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
/* wait for polling to start till spec allowed timeout */
|
||||
while (!ctrl->polling && retries--)
|
||||
msleep(20);
|
||||
|
||||
/* check if sink has made a message available */
|
||||
if (ctrl->polling && (ctrl->sink_rx_status & ctrl->rx_status)) {
|
||||
ctrl->sink_rx_status = 0;
|
||||
ctrl->rx_status = 0;
|
||||
|
@ -552,26 +591,19 @@ static void dp_hdcp2p2_link_check(struct dp_hdcp2p2_ctrl *ctrl)
|
|||
dp_hdcp2p2_get_msg_from_sink(ctrl);
|
||||
|
||||
ctrl->polling = false;
|
||||
} else {
|
||||
ctrl->cp_irq_done = true;
|
||||
}
|
||||
exit:
|
||||
if (rc)
|
||||
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
|
||||
}
|
||||
|
||||
static void dp_hdcp2p2_manage_session(struct dp_hdcp2p2_ctrl *ctrl)
|
||||
static void dp_hdcp2p2_start_auth(struct dp_hdcp2p2_ctrl *ctrl)
|
||||
{
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_INVALID};
|
||||
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_START_AUTH};
|
||||
cdata.context = ctrl->lib_ctx;
|
||||
|
||||
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTHENTICATING)
|
||||
cdata.cmd = HDCP_2X_CMD_START;
|
||||
else
|
||||
cdata.cmd = HDCP_2X_CMD_STOP;
|
||||
|
||||
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
|
||||
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
|
||||
|
@ -617,34 +649,31 @@ static int dp_hdcp2p2_read_rx_status(struct dp_hdcp2p2_ctrl *ctrl,
|
|||
|
||||
static int dp_hdcp2p2_cp_irq(void *input)
|
||||
{
|
||||
int rc = 0;
|
||||
int rc;
|
||||
struct dp_hdcp2p2_ctrl *ctrl = input;
|
||||
|
||||
if (!ctrl) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = dp_hdcp2p2_valid_handle(ctrl);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (atomic_read(&ctrl->auth_state) == HDCP_STATE_AUTH_FAIL ||
|
||||
atomic_read(&ctrl->auth_state) == HDCP_STATE_INACTIVE) {
|
||||
pr_err("invalid hdcp state\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ctrl->sink_rx_status = 0;
|
||||
rc = dp_hdcp2p2_read_rx_status(ctrl, &ctrl->sink_rx_status);
|
||||
if (rc) {
|
||||
pr_err("failed to read rx status\n");
|
||||
goto error;
|
||||
return rc;
|
||||
}
|
||||
|
||||
pr_debug("sink_rx_status=0x%x\n", ctrl->sink_rx_status);
|
||||
|
||||
if (!ctrl->sink_rx_status) {
|
||||
pr_debug("not a hdcp 2.2 irq\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -652,8 +681,6 @@ static int dp_hdcp2p2_cp_irq(void *input)
|
|||
wake_up(&ctrl->wait_q);
|
||||
|
||||
return 0;
|
||||
error:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_isr(void *input)
|
||||
|
@ -721,6 +748,51 @@ static bool dp_hdcp2p2_supported(void *input)
|
|||
return false;
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_change_streams(struct dp_hdcp2p2_ctrl *ctrl,
|
||||
struct sde_hdcp_2x_wakeup_data *cdata)
|
||||
{
|
||||
if (!ctrl || cdata->num_streams == 0 || !cdata->streams) {
|
||||
pr_err("invalid input\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ctrl->lib_ctx) {
|
||||
pr_err("HDCP library needs to be acquired\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!ctrl->lib) {
|
||||
pr_err("invalid lib ops data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cdata->context = ctrl->lib_ctx;
|
||||
return ctrl->lib->wakeup(cdata);
|
||||
}
|
||||
|
||||
|
||||
static int dp_hdcp2p2_register_streams(void *input, u8 num_streams,
|
||||
struct stream_info *streams)
|
||||
{
|
||||
struct dp_hdcp2p2_ctrl *ctrl = input;
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_OPEN_STREAMS};
|
||||
|
||||
cdata.streams = streams;
|
||||
cdata.num_streams = num_streams;
|
||||
return dp_hdcp2p2_change_streams(ctrl, &cdata);
|
||||
}
|
||||
|
||||
static int dp_hdcp2p2_deregister_streams(void *input, u8 num_streams,
|
||||
struct stream_info *streams)
|
||||
{
|
||||
struct dp_hdcp2p2_ctrl *ctrl = input;
|
||||
struct sde_hdcp_2x_wakeup_data cdata = {HDCP_2X_CMD_CLOSE_STREAMS};
|
||||
|
||||
cdata.streams = streams;
|
||||
cdata.num_streams = num_streams;
|
||||
return dp_hdcp2p2_change_streams(ctrl, &cdata);
|
||||
}
|
||||
|
||||
void sde_dp_hdcp2p2_deinit(void *input)
|
||||
{
|
||||
struct dp_hdcp2p2_ctrl *ctrl = (struct dp_hdcp2p2_ctrl *)input;
|
||||
|
@ -731,9 +803,13 @@ void sde_dp_hdcp2p2_deinit(void *input)
|
|||
return;
|
||||
}
|
||||
|
||||
cdata.cmd = HDCP_2X_CMD_STOP;
|
||||
cdata.context = ctrl->lib_ctx;
|
||||
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
|
||||
if (atomic_read(&ctrl->auth_state) != HDCP_STATE_AUTH_FAIL) {
|
||||
cdata.cmd = HDCP_2X_CMD_STOP;
|
||||
cdata.context = ctrl->lib_ctx;
|
||||
dp_hdcp2p2_wakeup_lib(ctrl, &cdata);
|
||||
}
|
||||
|
||||
sde_hdcp_2x_deregister(ctrl->lib_ctx);
|
||||
|
||||
kthread_stop(ctrl->thread);
|
||||
|
||||
|
@ -769,7 +845,10 @@ static int dp_hdcp2p2_main(void *data)
|
|||
dp_hdcp2p2_send_msg(ctrl);
|
||||
break;
|
||||
case HDCP_TRANSPORT_CMD_RECV_MESSAGE:
|
||||
dp_hdcp2p2_recv_msg(ctrl);
|
||||
if (ctrl->rx_status)
|
||||
ctrl->polling = true;
|
||||
else
|
||||
dp_hdcp2p2_recv_msg(ctrl);
|
||||
break;
|
||||
case HDCP_TRANSPORT_CMD_STATUS_SUCCESS:
|
||||
dp_hdcp2p2_send_auth_status(ctrl);
|
||||
|
@ -779,16 +858,13 @@ static int dp_hdcp2p2_main(void *data)
|
|||
dp_hdcp2p2_send_auth_status(ctrl);
|
||||
break;
|
||||
case HDCP_TRANSPORT_CMD_LINK_POLL:
|
||||
if (ctrl->cp_irq_done)
|
||||
dp_hdcp2p2_recv_msg(ctrl);
|
||||
else
|
||||
ctrl->polling = true;
|
||||
ctrl->polling = true;
|
||||
break;
|
||||
case HDCP_TRANSPORT_CMD_LINK_CHECK:
|
||||
dp_hdcp2p2_link_check(ctrl);
|
||||
break;
|
||||
case HDCP_TRANSPORT_CMD_AUTHENTICATE:
|
||||
dp_hdcp2p2_manage_session(ctrl);
|
||||
dp_hdcp2p2_start_auth(ctrl);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
@ -809,8 +885,12 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
|
|||
.feature_supported = dp_hdcp2p2_feature_supported,
|
||||
.force_encryption = dp_hdcp2p2_force_encryption,
|
||||
.sink_support = dp_hdcp2p2_supported,
|
||||
.set_mode = dp_hdcp2p2_register,
|
||||
.on = dp_hdcp2p2_on,
|
||||
.off = dp_hdcp2p2_off,
|
||||
.cp_irq = dp_hdcp2p2_cp_irq,
|
||||
.register_streams = dp_hdcp2p2_register_streams,
|
||||
.deregister_streams = dp_hdcp2p2_deregister_streams,
|
||||
};
|
||||
|
||||
static struct hdcp_transport_ops client_ops = {
|
||||
|
@ -865,7 +945,6 @@ void *sde_dp_hdcp2p2_init(struct sde_hdcp_init_data *init_data)
|
|||
register_data.hdcp_data = &ctrl->lib_ctx;
|
||||
register_data.client_ops = &client_ops;
|
||||
register_data.ops = &hdcp2x_ops;
|
||||
register_data.device_type = HDCP_TXMTR_DP;
|
||||
register_data.client_data = ctrl;
|
||||
|
||||
rc = sde_hdcp_2x_register(®ister_data);
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
#include "dp_drm.h"
|
||||
|
||||
#define DP_MST_DEBUG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)
|
||||
#define DP_MST_INFO_LOG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)
|
||||
|
||||
#define MAX_DP_MST_STREAMS 2
|
||||
#define MAX_DP_MST_DRM_ENCODERS 2
|
||||
#define MAX_DP_MST_DRM_BRIDGES 2
|
||||
#define HPD_STRING_SIZE 30
|
||||
|
@ -93,12 +93,18 @@ struct dp_mst_bridge {
|
|||
struct drm_display_mode drm_mode;
|
||||
struct dp_display_mode dp_mode;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector *old_connector;
|
||||
void *dp_panel;
|
||||
void *old_dp_panel;
|
||||
|
||||
int vcpi;
|
||||
int pbn;
|
||||
int num_slots;
|
||||
int start_slot;
|
||||
|
||||
u32 fixed_port_num;
|
||||
bool fixed_port_added;
|
||||
struct drm_connector *fixed_connector;
|
||||
};
|
||||
|
||||
struct dp_mst_private {
|
||||
|
@ -111,6 +117,7 @@ struct dp_mst_private {
|
|||
struct dp_mst_sim_mode simulator;
|
||||
struct mutex mst_lock;
|
||||
enum dp_drv_state state;
|
||||
bool mst_session_state;
|
||||
};
|
||||
|
||||
struct dp_mst_encoder_info_cache {
|
||||
|
@ -167,10 +174,13 @@ static void dp_mst_sim_add_port(struct dp_mst_private *mst,
|
|||
mutex_lock(&mstb->mgr->lock);
|
||||
list_del(&port->next);
|
||||
mutex_unlock(&mstb->mgr->lock);
|
||||
return;
|
||||
goto put_port;
|
||||
}
|
||||
(*mstb->mgr->cbs->register_connector)(port->connector);
|
||||
}
|
||||
|
||||
put_port:
|
||||
kref_put(&port->kref, NULL);
|
||||
}
|
||||
|
||||
static void dp_mst_sim_link_probe_work(struct work_struct *work)
|
||||
|
@ -525,7 +535,8 @@ static void _dp_mst_update_timeslots(struct dp_mst_private *mst,
|
|||
|
||||
mst->dp_display->set_stream_info(mst->dp_display,
|
||||
dp_bridge->dp_panel,
|
||||
dp_bridge->id, start_slot, num_slots, pbn);
|
||||
dp_bridge->id, start_slot, num_slots, pbn,
|
||||
dp_bridge->vcpi);
|
||||
|
||||
pr_info("bridge:%d vcpi:%d start_slot:%d num_slots:%d, pbn:%d\n",
|
||||
dp_bridge->id, dp_bridge->vcpi,
|
||||
|
@ -550,7 +561,8 @@ static void _dp_mst_update_single_timeslot(struct dp_mst_private *mst,
|
|||
|
||||
mst->dp_display->set_stream_info(mst->dp_display,
|
||||
mst_bridge->dp_panel,
|
||||
mst_bridge->id, start_slot, num_slots, pbn);
|
||||
mst_bridge->id, start_slot, num_slots, pbn,
|
||||
mst_bridge->vcpi);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -672,8 +684,6 @@ static void dp_mst_bridge_pre_enable(struct drm_bridge *drm_bridge)
|
|||
struct dp_display *dp;
|
||||
struct dp_mst_private *mst;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
if (!drm_bridge) {
|
||||
pr_err("Invalid params\n");
|
||||
return;
|
||||
|
@ -682,6 +692,9 @@ static void dp_mst_bridge_pre_enable(struct drm_bridge *drm_bridge)
|
|||
bridge = to_dp_mst_bridge(drm_bridge);
|
||||
dp = bridge->display;
|
||||
|
||||
bridge->old_connector = NULL;
|
||||
bridge->old_dp_panel = NULL;
|
||||
|
||||
if (!bridge->connector) {
|
||||
pr_err("Invalid connector\n");
|
||||
return;
|
||||
|
@ -718,7 +731,14 @@ static void dp_mst_bridge_pre_enable(struct drm_bridge *drm_bridge)
|
|||
_dp_mst_bridge_pre_enable_part2(bridge);
|
||||
}
|
||||
|
||||
DP_MST_DEBUG("mst bridge [%d] pre enable complete\n", bridge->id);
|
||||
DP_MST_INFO_LOG("mode: id(%d) mode(%s), refresh(%d)\n",
|
||||
bridge->id, bridge->drm_mode.name,
|
||||
bridge->drm_mode.vrefresh);
|
||||
DP_MST_INFO_LOG("dsc: id(%d) dsc(%d)\n", bridge->id,
|
||||
bridge->dp_mode.timing.comp_info.comp_ratio);
|
||||
DP_MST_INFO_LOG("channel: id(%d) vcpi(%d) start(%d) tot(%d)\n",
|
||||
bridge->id, bridge->vcpi, bridge->start_slot,
|
||||
bridge->num_slots);
|
||||
end:
|
||||
mutex_unlock(&mst->mst_lock);
|
||||
}
|
||||
|
@ -729,8 +749,6 @@ static void dp_mst_bridge_enable(struct drm_bridge *drm_bridge)
|
|||
struct dp_mst_bridge *bridge;
|
||||
struct dp_display *dp;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
if (!drm_bridge) {
|
||||
pr_err("Invalid params\n");
|
||||
return;
|
||||
|
@ -751,7 +769,8 @@ static void dp_mst_bridge_enable(struct drm_bridge *drm_bridge)
|
|||
return;
|
||||
}
|
||||
|
||||
DP_MST_DEBUG("mst bridge [%d] post enable complete\n", bridge->id);
|
||||
DP_MST_INFO_LOG("mst bridge [%d] post enable complete\n",
|
||||
bridge->id);
|
||||
}
|
||||
|
||||
static void dp_mst_bridge_disable(struct drm_bridge *drm_bridge)
|
||||
|
@ -761,8 +780,6 @@ static void dp_mst_bridge_disable(struct drm_bridge *drm_bridge)
|
|||
struct dp_display *dp;
|
||||
struct dp_mst_private *mst;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
if (!drm_bridge) {
|
||||
pr_err("Invalid params\n");
|
||||
return;
|
||||
|
@ -791,7 +808,7 @@ static void dp_mst_bridge_disable(struct drm_bridge *drm_bridge)
|
|||
|
||||
_dp_mst_bridge_pre_disable_part2(bridge);
|
||||
|
||||
DP_MST_DEBUG("mst bridge [%d] disable complete\n", bridge->id);
|
||||
DP_MST_INFO_LOG("mst bridge [%d] disable complete\n", bridge->id);
|
||||
|
||||
mutex_unlock(&mst->mst_lock);
|
||||
}
|
||||
|
@ -803,8 +820,6 @@ static void dp_mst_bridge_post_disable(struct drm_bridge *drm_bridge)
|
|||
struct dp_display *dp;
|
||||
struct dp_mst_private *mst;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
if (!drm_bridge) {
|
||||
pr_err("Invalid params\n");
|
||||
return;
|
||||
|
@ -832,12 +847,17 @@ static void dp_mst_bridge_post_disable(struct drm_bridge *drm_bridge)
|
|||
/* maintain the connector to encoder link during suspend/resume */
|
||||
if (mst->state != PM_SUSPEND) {
|
||||
/* Disconnect the connector and panel info from bridge */
|
||||
mst->mst_bridge[bridge->id].old_connector =
|
||||
mst->mst_bridge[bridge->id].connector;
|
||||
mst->mst_bridge[bridge->id].old_dp_panel =
|
||||
mst->mst_bridge[bridge->id].dp_panel;
|
||||
mst->mst_bridge[bridge->id].connector = NULL;
|
||||
mst->mst_bridge[bridge->id].dp_panel = NULL;
|
||||
mst->mst_bridge[bridge->id].encoder_active_sts = false;
|
||||
}
|
||||
|
||||
DP_MST_DEBUG("mst bridge [%d] post disable complete\n", bridge->id);
|
||||
DP_MST_INFO_LOG("mst bridge [%d] post disable complete\n",
|
||||
bridge->id);
|
||||
}
|
||||
|
||||
static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,
|
||||
|
@ -856,13 +876,21 @@ static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,
|
|||
|
||||
bridge = to_dp_mst_bridge(drm_bridge);
|
||||
if (!bridge->connector) {
|
||||
pr_err("Invalid connector\n");
|
||||
return;
|
||||
if (!bridge->old_connector) {
|
||||
pr_err("Invalid connector\n");
|
||||
return;
|
||||
}
|
||||
bridge->connector = bridge->old_connector;
|
||||
bridge->old_connector = NULL;
|
||||
}
|
||||
|
||||
if (!bridge->dp_panel) {
|
||||
pr_err("Invalid dp_panel\n");
|
||||
return;
|
||||
if (!bridge->old_dp_panel) {
|
||||
pr_err("Invalid dp_panel\n");
|
||||
return;
|
||||
}
|
||||
bridge->dp_panel = bridge->old_dp_panel;
|
||||
bridge->old_dp_panel = NULL;
|
||||
}
|
||||
|
||||
dp = bridge->display;
|
||||
|
@ -877,6 +905,10 @@ static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,
|
|||
|
||||
/* DP MST Bridge APIs */
|
||||
|
||||
static struct drm_connector *
|
||||
dp_mst_drm_fixed_connector_init(struct dp_display *dp_display,
|
||||
struct drm_encoder *encoder);
|
||||
|
||||
static const struct drm_bridge_funcs dp_mst_bridge_ops = {
|
||||
.attach = dp_mst_bridge_attach,
|
||||
.mode_fixup = dp_mst_bridge_mode_fixup,
|
||||
|
@ -944,6 +976,23 @@ int dp_mst_drm_bridge_init(void *data, struct drm_encoder *encoder)
|
|||
|
||||
DP_MST_DEBUG("mst drm bridge init. bridge id:%d\n", i);
|
||||
|
||||
/*
|
||||
* If fixed topology port is defined, connector will be created
|
||||
* immediately.
|
||||
*/
|
||||
rc = display->mst_get_fixed_topology_port(display, bridge->id,
|
||||
&bridge->fixed_port_num);
|
||||
if (!rc) {
|
||||
bridge->fixed_connector =
|
||||
dp_mst_drm_fixed_connector_init(display,
|
||||
bridge->encoder);
|
||||
if (bridge->fixed_connector == NULL) {
|
||||
pr_err("failed to create fixed connector\n");
|
||||
rc = -ENOMEM;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
end:
|
||||
|
@ -1136,7 +1185,8 @@ dp_mst_atomic_best_encoder(struct drm_connector *connector,
|
|||
}
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (!mst->mst_bridge[i].encoder_active_sts) {
|
||||
if (!mst->mst_bridge[i].encoder_active_sts &&
|
||||
!mst->mst_bridge[i].fixed_connector) {
|
||||
mst->mst_bridge[i].encoder_active_sts = true;
|
||||
mst->mst_bridge[i].connector = connector;
|
||||
mst->mst_bridge[i].dp_panel = conn->drv_panel;
|
||||
|
@ -1343,6 +1393,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|||
|
||||
if (!connector) {
|
||||
pr_err("mst sde_connector_init failed\n");
|
||||
drm_modeset_unlock_all(dev);
|
||||
return connector;
|
||||
}
|
||||
|
||||
|
@ -1350,6 +1401,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|||
if (rc) {
|
||||
pr_err("mst connector install failed\n");
|
||||
sde_connector_destroy(connector);
|
||||
drm_modeset_unlock_all(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1372,7 +1424,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|||
/* unlock connector and make it accessible */
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
DP_MST_DEBUG("add mst connector:%d\n", connector->base.id);
|
||||
DP_MST_INFO_LOG("add mst connector id:%d\n", connector->base.id);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
@ -1383,7 +1435,8 @@ static void dp_mst_register_connector(struct drm_connector *connector)
|
|||
|
||||
connector->status = connector->funcs->detect(connector, false);
|
||||
|
||||
DP_MST_DEBUG("register mst connector:%d\n", connector->base.id);
|
||||
DP_MST_INFO_LOG("register mst connector id:%d\n",
|
||||
connector->base.id);
|
||||
drm_connector_register(connector);
|
||||
}
|
||||
|
||||
|
@ -1392,12 +1445,297 @@ static void dp_mst_destroy_connector(struct drm_dp_mst_topology_mgr *mgr,
|
|||
{
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
DP_MST_DEBUG("destroy mst connector:%d\n", connector->base.id);
|
||||
DP_MST_INFO_LOG("destroy mst connector id:%d\n", connector->base.id);
|
||||
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_put(connector);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
dp_mst_fixed_connector_detect(struct drm_connector *connector, bool force,
|
||||
void *display)
|
||||
{
|
||||
struct dp_display *dp_display = display;
|
||||
struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (mst->mst_bridge[i].fixed_connector != connector)
|
||||
continue;
|
||||
|
||||
if (!mst->mst_bridge[i].fixed_port_added)
|
||||
break;
|
||||
|
||||
return dp_mst_connector_detect(connector, force, display);
|
||||
}
|
||||
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static struct drm_encoder *
|
||||
dp_mst_fixed_atomic_best_encoder(struct drm_connector *connector,
|
||||
void *display, struct drm_connector_state *state)
|
||||
{
|
||||
struct dp_display *dp_display = display;
|
||||
struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
|
||||
struct sde_connector *conn = to_sde_connector(connector);
|
||||
struct drm_encoder *enc = NULL;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (mst->mst_bridge[i].connector == connector) {
|
||||
enc = mst->mst_bridge[i].encoder;
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (mst->mst_bridge[i].fixed_connector == connector) {
|
||||
mst->mst_bridge[i].encoder_active_sts = true;
|
||||
mst->mst_bridge[i].connector = connector;
|
||||
mst->mst_bridge[i].dp_panel = conn->drv_panel;
|
||||
enc = mst->mst_bridge[i].encoder;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (enc)
|
||||
DP_MST_DEBUG("mst connector:%d atomic best encoder:%d\n",
|
||||
connector->base.id, i);
|
||||
else
|
||||
DP_MST_DEBUG("mst connector:%d atomic best encoder failed\n",
|
||||
connector->base.id);
|
||||
|
||||
return enc;
|
||||
}
|
||||
|
||||
static u32 dp_mst_find_fixed_port_num(struct drm_dp_mst_branch *mstb,
|
||||
struct drm_dp_mst_port *target)
|
||||
{
|
||||
struct drm_dp_mst_port *port;
|
||||
u32 port_num = 0;
|
||||
|
||||
/*
|
||||
* search through reversed order of adding sequence, so the port number
|
||||
* will be unique once topology is fixed
|
||||
*/
|
||||
list_for_each_entry_reverse(port, &mstb->ports, next) {
|
||||
if (port->mstb)
|
||||
port_num += dp_mst_find_fixed_port_num(port->mstb,
|
||||
target);
|
||||
else if (!port->input) {
|
||||
++port_num;
|
||||
if (port == target)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return port_num;
|
||||
}
|
||||
|
||||
static struct drm_connector *
|
||||
dp_mst_find_fixed_connector(struct dp_mst_private *dp_mst,
|
||||
struct drm_dp_mst_port *port)
|
||||
{
|
||||
struct dp_display *dp_display = dp_mst->dp_display;
|
||||
struct drm_connector *connector = NULL;
|
||||
struct sde_connector *c_conn;
|
||||
u32 port_num;
|
||||
int i;
|
||||
|
||||
mutex_lock(&port->mgr->lock);
|
||||
port_num = dp_mst_find_fixed_port_num(port->mgr->mst_primary, port);
|
||||
mutex_unlock(&port->mgr->lock);
|
||||
|
||||
if (!port_num)
|
||||
return NULL;
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (dp_mst->mst_bridge[i].fixed_port_num == port_num) {
|
||||
connector = dp_mst->mst_bridge[i].fixed_connector;
|
||||
c_conn = to_sde_connector(connector);
|
||||
c_conn->mst_port = port;
|
||||
dp_display->mst_connector_update_link_info(dp_display,
|
||||
connector);
|
||||
dp_mst->mst_bridge[i].fixed_port_added = true;
|
||||
DP_MST_DEBUG("found fixed connector %d\n",
|
||||
DRMID(connector));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static int
|
||||
dp_mst_find_first_available_encoder_idx(struct dp_mst_private *dp_mst)
|
||||
{
|
||||
int enc_idx = MAX_DP_MST_DRM_BRIDGES;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (!dp_mst->mst_bridge[i].fixed_connector) {
|
||||
enc_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return enc_idx;
|
||||
}
|
||||
|
||||
static struct drm_connector *
|
||||
dp_mst_add_fixed_connector(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port, const char *pathprop)
|
||||
{
|
||||
struct dp_mst_private *dp_mst;
|
||||
struct drm_device *dev;
|
||||
struct dp_display *dp_display;
|
||||
struct drm_connector *connector;
|
||||
int i, enc_idx;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
dp_mst = container_of(mgr, struct dp_mst_private, mst_mgr);
|
||||
|
||||
dp_display = dp_mst->dp_display;
|
||||
dev = dp_display->drm_dev;
|
||||
|
||||
if (port->input || port->mstb)
|
||||
enc_idx = MAX_DP_MST_DRM_BRIDGES;
|
||||
else {
|
||||
/* if port is already reserved, return immediately */
|
||||
connector = dp_mst_find_fixed_connector(dp_mst, port);
|
||||
if (connector != NULL)
|
||||
return connector;
|
||||
|
||||
/* first available bridge index for non-reserved port */
|
||||
enc_idx = dp_mst_find_first_available_encoder_idx(dp_mst);
|
||||
}
|
||||
|
||||
/* add normal connector */
|
||||
connector = dp_mst_add_connector(mgr, port, pathprop);
|
||||
if (!connector) {
|
||||
DP_MST_DEBUG("failed to add connector\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
|
||||
/* clear encoder list */
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
|
||||
connector->encoder_ids[i] = 0;
|
||||
|
||||
/* re-attach encoders from first available encoders */
|
||||
for (i = enc_idx; i < MAX_DP_MST_DRM_BRIDGES; i++)
|
||||
drm_connector_attach_encoder(connector,
|
||||
dp_mst->mst_bridge[i].encoder);
|
||||
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
DP_MST_DEBUG("add mst connector:%d\n", connector->base.id);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static void dp_mst_register_fixed_connector(struct drm_connector *connector)
|
||||
{
|
||||
struct sde_connector *c_conn = to_sde_connector(connector);
|
||||
struct dp_display *dp_display = c_conn->display;
|
||||
struct dp_mst_private *dp_mst = dp_display->dp_mst_prv_info;
|
||||
int i;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
/* skip connector registered for fixed topology ports */
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (dp_mst->mst_bridge[i].fixed_connector == connector) {
|
||||
DP_MST_DEBUG("found fixed connector %d\n",
|
||||
DRMID(connector));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dp_mst_register_connector(connector);
|
||||
}
|
||||
|
||||
static void dp_mst_destroy_fixed_connector(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct dp_mst_private *dp_mst;
|
||||
int i;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
dp_mst = container_of(mgr, struct dp_mst_private, mst_mgr);
|
||||
|
||||
/* skip connector destroy for fixed topology ports */
|
||||
for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
|
||||
if (dp_mst->mst_bridge[i].fixed_connector == connector) {
|
||||
dp_mst->mst_bridge[i].fixed_port_added = false;
|
||||
DP_MST_DEBUG("destroy fixed connector %d\n",
|
||||
DRMID(connector));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
dp_mst_destroy_connector(mgr, connector);
|
||||
}
|
||||
|
||||
static struct drm_connector *
|
||||
dp_mst_drm_fixed_connector_init(struct dp_display *dp_display,
|
||||
struct drm_encoder *encoder)
|
||||
{
|
||||
static const struct sde_connector_ops dp_mst_connector_ops = {
|
||||
.post_init = NULL,
|
||||
.detect = dp_mst_fixed_connector_detect,
|
||||
.get_modes = dp_mst_connector_get_modes,
|
||||
.mode_valid = dp_mst_connector_mode_valid,
|
||||
.get_info = dp_mst_connector_get_info,
|
||||
.get_mode_info = dp_mst_connector_get_mode_info,
|
||||
.atomic_best_encoder = dp_mst_fixed_atomic_best_encoder,
|
||||
.atomic_check = dp_mst_connector_atomic_check,
|
||||
.config_hdr = dp_mst_connector_config_hdr,
|
||||
.pre_destroy = dp_mst_connector_pre_destroy,
|
||||
};
|
||||
struct drm_device *dev;
|
||||
struct drm_connector *connector;
|
||||
int rc;
|
||||
|
||||
DP_MST_DEBUG("enter\n");
|
||||
|
||||
dev = dp_display->drm_dev;
|
||||
|
||||
connector = sde_connector_init(dev,
|
||||
encoder,
|
||||
NULL,
|
||||
dp_display,
|
||||
&dp_mst_connector_ops,
|
||||
DRM_CONNECTOR_POLL_HPD,
|
||||
DRM_MODE_CONNECTOR_DisplayPort);
|
||||
|
||||
if (!connector) {
|
||||
pr_err("mst sde_connector_init failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rc = dp_display->mst_connector_install(dp_display, connector);
|
||||
if (rc) {
|
||||
pr_err("mst connector install failed\n");
|
||||
sde_connector_destroy(connector);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
drm_object_attach_property(&connector->base,
|
||||
dev->mode_config.path_property, 0);
|
||||
drm_object_attach_property(&connector->base,
|
||||
dev->mode_config.tile_property, 0);
|
||||
|
||||
DP_MST_DEBUG("add mst fixed connector:%d\n", connector->base.id);
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
struct dp_mst_private *mst = container_of(mgr, struct dp_mst_private,
|
||||
|
@ -1411,7 +1749,7 @@ static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
|
|||
|
||||
kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
DP_MST_DEBUG("mst hot plug event\n");
|
||||
DP_MST_INFO_LOG("mst hot plug event\n");
|
||||
}
|
||||
|
||||
static void dp_mst_hpd_event_notify(struct dp_mst_private *mst, bool hpd_status)
|
||||
|
@ -1432,7 +1770,7 @@ static void dp_mst_hpd_event_notify(struct dp_mst_private *mst, bool hpd_status)
|
|||
|
||||
kobject_uevent_env(&dev->primary->kdev->kobj, KOBJ_CHANGE, envp);
|
||||
|
||||
DP_MST_DEBUG("%s finished\n", __func__);
|
||||
DP_MST_INFO_LOG("%s finished\n", __func__);
|
||||
}
|
||||
|
||||
/* DP Driver Callback OPs */
|
||||
|
@ -1444,7 +1782,9 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status,
|
|||
struct dp_display *dp = dp_display;
|
||||
struct dp_mst_private *mst = dp->dp_mst_prv_info;
|
||||
|
||||
DP_MST_DEBUG("enter:\n");
|
||||
mutex_lock(&mst->mst_lock);
|
||||
mst->mst_session_state = hpd_status;
|
||||
mutex_unlock(&mst->mst_lock);
|
||||
|
||||
if (!hpd_status)
|
||||
rc = mst->mst_fw_cbs->topology_mgr_set_mst(&mst->mst_mgr,
|
||||
|
@ -1466,9 +1806,7 @@ static void dp_mst_display_hpd(void *dp_display, bool hpd_status,
|
|||
|
||||
dp_mst_hpd_event_notify(mst, hpd_status);
|
||||
|
||||
DP_MST_DEBUG("mst display hpd:%d, rc:%d\n", hpd_status, rc);
|
||||
|
||||
DP_MST_DEBUG("exit:\n");
|
||||
DP_MST_INFO_LOG("mst display hpd:%d, rc:%d\n", hpd_status, rc);
|
||||
}
|
||||
|
||||
static void dp_mst_display_hpd_irq(void *dp_display,
|
||||
|
@ -1477,26 +1815,29 @@ static void dp_mst_display_hpd_irq(void *dp_display,
|
|||
int rc;
|
||||
struct dp_display *dp = dp_display;
|
||||
struct dp_mst_private *mst = dp->dp_mst_prv_info;
|
||||
u8 esi[14], idx;
|
||||
u8 esi[14];
|
||||
unsigned int esi_res = DP_SINK_COUNT_ESI + 1;
|
||||
bool handled;
|
||||
|
||||
DP_MST_DEBUG("enter:\n");
|
||||
|
||||
if (info->mst_hpd_sim) {
|
||||
dp_mst_hotplug(&mst->mst_mgr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mst->mst_session_state) {
|
||||
pr_err("mst_hpd_irq received before mst session start\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rc = drm_dp_dpcd_read(mst->caps.drm_aux, DP_SINK_COUNT_ESI,
|
||||
esi, 14);
|
||||
if (rc != 14) {
|
||||
pr_err("dpcd sync status read failed, rlen=%d\n", rc);
|
||||
goto end;
|
||||
pr_err("dpcd sink status read failed, rlen=%d\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
for (idx = 0; idx < 14; idx++)
|
||||
DP_MST_DEBUG("mst irq: esi[%d]: 0x%x\n", idx, esi[idx]);
|
||||
DP_MST_DEBUG("mst irq: esi1[0x%x] esi2[0x%x] esi3[%x]\n",
|
||||
esi[1], esi[2], esi[3]);
|
||||
|
||||
rc = drm_dp_mst_hpd_irq(&mst->mst_mgr, esi, &handled);
|
||||
|
||||
|
@ -1509,9 +1850,6 @@ static void dp_mst_display_hpd_irq(void *dp_display,
|
|||
}
|
||||
|
||||
DP_MST_DEBUG("mst display hpd_irq handled:%d rc:%d\n", handled, rc);
|
||||
|
||||
end:
|
||||
DP_MST_DEBUG("exit:\n");
|
||||
}
|
||||
|
||||
static void dp_mst_set_state(void *dp_display, enum dp_drv_state mst_state)
|
||||
|
@ -1525,6 +1863,7 @@ static void dp_mst_set_state(void *dp_display, enum dp_drv_state mst_state)
|
|||
}
|
||||
|
||||
mst->state = mst_state;
|
||||
DP_MST_INFO_LOG("mst power state:%d\n", mst_state);
|
||||
}
|
||||
|
||||
/* DP MST APIs */
|
||||
|
@ -1542,6 +1881,13 @@ static const struct drm_dp_mst_topology_cbs dp_mst_drm_cbs = {
|
|||
.hotplug = dp_mst_hotplug,
|
||||
};
|
||||
|
||||
static const struct drm_dp_mst_topology_cbs dp_mst_fixed_drm_cbs = {
|
||||
.add_connector = dp_mst_add_fixed_connector,
|
||||
.register_connector = dp_mst_register_fixed_connector,
|
||||
.destroy_connector = dp_mst_destroy_fixed_connector,
|
||||
.hotplug = dp_mst_hotplug,
|
||||
};
|
||||
|
||||
static void dp_mst_sim_init(struct dp_mst_private *mst)
|
||||
{
|
||||
INIT_WORK(&mst->simulator.probe_work, dp_mst_sim_link_probe_work);
|
||||
|
@ -1606,7 +1952,11 @@ int dp_mst_init(struct dp_display *dp_display)
|
|||
}
|
||||
memset(&dp_mst_enc_cache, 0, sizeof(dp_mst_enc_cache));
|
||||
|
||||
DP_MST_DEBUG("dp drm mst topology manager init completed\n");
|
||||
/* choose fixed callback function if fixed topology is found */
|
||||
if (!dp_display->mst_get_fixed_topology_port(dp_display, 0, NULL))
|
||||
dp_mst.mst_mgr.cbs = &dp_mst_fixed_drm_cbs;
|
||||
|
||||
DP_MST_INFO_LOG("dp drm mst topology manager init completed\n");
|
||||
|
||||
return ret;
|
||||
|
||||
|
@ -1637,6 +1987,6 @@ void dp_mst_deinit(struct dp_display *dp_display)
|
|||
|
||||
mutex_destroy(&mst->mst_lock);
|
||||
|
||||
DP_MST_DEBUG("dp drm mst topology manager deinit completed\n");
|
||||
DP_MST_INFO_LOG("dp drm mst topology manager deinit completed\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -1530,12 +1530,14 @@ static int dp_panel_dsc_prepare_basic_params(
|
|||
struct dp_dsc_slices_per_line *rec;
|
||||
int slice_width;
|
||||
u32 ppr = dp_mode->timing.pixel_clk_khz/1000;
|
||||
int max_slice_width;
|
||||
|
||||
comp_info->dsc_info.slice_per_pkt = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(slice_per_line_tbl); i++) {
|
||||
rec = &slice_per_line_tbl[i];
|
||||
if ((ppr > rec->min_ppr) && (ppr <= rec->max_ppr)) {
|
||||
comp_info->dsc_info.slice_per_pkt = rec->num_slices;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1543,9 +1545,21 @@ static int dp_panel_dsc_prepare_basic_params(
|
|||
if (comp_info->dsc_info.slice_per_pkt == 0)
|
||||
return -EINVAL;
|
||||
|
||||
max_slice_width = dp_panel->dsc_dpcd[12] * 320;
|
||||
slice_width = (dp_mode->timing.h_active /
|
||||
comp_info->dsc_info.slice_per_pkt);
|
||||
|
||||
while (slice_width >= max_slice_width) {
|
||||
if (i == ARRAY_SIZE(slice_per_line_tbl))
|
||||
return -EINVAL;
|
||||
|
||||
rec = &slice_per_line_tbl[i];
|
||||
comp_info->dsc_info.slice_per_pkt = rec->num_slices;
|
||||
slice_width = (dp_mode->timing.h_active /
|
||||
comp_info->dsc_info.slice_per_pkt);
|
||||
i++;
|
||||
}
|
||||
|
||||
comp_info->dsc_info.block_pred_enable =
|
||||
dp_panel->sink_dsc_caps.block_pred_en;
|
||||
comp_info->dsc_info.vbr_enable = 0;
|
||||
|
@ -1657,8 +1671,8 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel, bool multi_func)
|
|||
panel->minor = link_info->revision & 0x0f;
|
||||
pr_debug("version: %d.%d\n", panel->major, panel->minor);
|
||||
|
||||
link_info->rate =
|
||||
drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE]);
|
||||
link_info->rate = min_t(unsigned long, panel->parser->max_lclk_khz,
|
||||
drm_dp_bw_code_to_link_rate(dp_panel->dpcd[DP_MAX_LINK_RATE]));
|
||||
pr_debug("link_rate=%d\n", link_info->rate);
|
||||
|
||||
link_info->num_lanes = dp_panel->dpcd[DP_MAX_LANE_COUNT] &
|
||||
|
@ -2305,13 +2319,14 @@ static void dp_panel_edid_deregister(struct dp_panel_private *panel)
|
|||
|
||||
static int dp_panel_set_stream_info(struct dp_panel *dp_panel,
|
||||
enum dp_stream_id stream_id, u32 ch_start_slot,
|
||||
u32 ch_tot_slots, u32 pbn)
|
||||
u32 ch_tot_slots, u32 pbn, int vcpi)
|
||||
{
|
||||
if (!dp_panel || stream_id > DP_STREAM_MAX) {
|
||||
pr_err("invalid input. stream_id: %d\n", stream_id);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dp_panel->vcpi = vcpi;
|
||||
dp_panel->stream_id = stream_id;
|
||||
dp_panel->channel_start_slot = ch_start_slot;
|
||||
dp_panel->channel_total_slots = ch_tot_slots;
|
||||
|
@ -2376,7 +2391,7 @@ static int dp_panel_deinit_panel_info(struct dp_panel *dp_panel, u32 flags)
|
|||
if (!panel->custom_edid && 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);
|
||||
dp_panel_set_stream_info(dp_panel, DP_STREAM_MAX, 0, 0, 0, 0);
|
||||
memset(&dp_panel->pinfo, 0, sizeof(dp_panel->pinfo));
|
||||
memset(&hdr->hdr_meta, 0, sizeof(hdr->hdr_meta));
|
||||
panel->panel_on = false;
|
||||
|
@ -2931,6 +2946,8 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in)
|
|||
if (in->base_panel) {
|
||||
memcpy(dp_panel->dpcd, in->base_panel->dpcd,
|
||||
DP_RECEIVER_CAP_SIZE + 1);
|
||||
memcpy(dp_panel->dsc_dpcd, in->base_panel->dsc_dpcd,
|
||||
DP_RECEIVER_DSC_CAP_SIZE + 1);
|
||||
memcpy(&dp_panel->link_info, &in->base_panel->link_info,
|
||||
sizeof(dp_panel->link_info));
|
||||
dp_panel->mst_state = in->base_panel->mst_state;
|
||||
|
|
|
@ -110,6 +110,7 @@ struct dp_panel {
|
|||
* Client sets the stream id value using set_stream_id interface.
|
||||
*/
|
||||
enum dp_stream_id stream_id;
|
||||
int vcpi;
|
||||
|
||||
u32 channel_start_slot;
|
||||
u32 channel_total_slots;
|
||||
|
@ -154,7 +155,7 @@ struct dp_panel {
|
|||
|
||||
int (*set_stream_info)(struct dp_panel *dp_panel,
|
||||
enum dp_stream_id stream_id, u32 ch_start_slot,
|
||||
u32 ch_tot_slots, u32 pbn);
|
||||
u32 ch_tot_slots, u32 pbn, int vcpi);
|
||||
|
||||
int (*read_sink_status)(struct dp_panel *dp_panel, u8 *sts, u32 size);
|
||||
int (*update_edid)(struct dp_panel *dp_panel, struct edid *edid);
|
||||
|
|
|
@ -151,11 +151,22 @@ static int dp_parser_misc(struct dp_parser *parser)
|
|||
parser->l_map[i] = data[i];
|
||||
}
|
||||
|
||||
data = of_get_property(of_node, "qcom,pn-swap-lane-map", &len);
|
||||
if (data && (len == DP_MAX_PHY_LN)) {
|
||||
for (i = 0; i < len; i++)
|
||||
parser->l_pnswap |= (data[i] & 0x01) << i;
|
||||
}
|
||||
|
||||
rc = of_property_read_u32(of_node,
|
||||
"qcom,max-pclk-frequency-khz", &parser->max_pclk_khz);
|
||||
if (rc)
|
||||
parser->max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;
|
||||
|
||||
rc = of_property_read_u32(of_node,
|
||||
"qcom,max-lclk-frequency-khz", &parser->max_lclk_khz);
|
||||
if (rc)
|
||||
parser->max_lclk_khz = DP_MAX_LINK_CLK_KHZ;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -692,6 +703,7 @@ static int dp_parser_catalog(struct dp_parser *parser)
|
|||
static int dp_parser_mst(struct dp_parser *parser)
|
||||
{
|
||||
struct device *dev = &parser->pdev->dev;
|
||||
int i;
|
||||
|
||||
parser->has_mst = of_property_read_bool(dev->of_node,
|
||||
"qcom,mst-enable");
|
||||
|
@ -699,6 +711,12 @@ static int dp_parser_mst(struct dp_parser *parser)
|
|||
|
||||
pr_debug("mst parsing successful. mst:%d\n", parser->has_mst);
|
||||
|
||||
for (i = 0; i < MAX_DP_MST_STREAMS; i++) {
|
||||
of_property_read_u32_index(dev->of_node,
|
||||
"qcom,mst-fixed-topology-ports", i,
|
||||
&parser->mst_fixed_port[i]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#define DP_LABEL "MDSS DP DISPLAY"
|
||||
#define AUX_CFG_LEN 10
|
||||
#define DP_MAX_PIXEL_CLK_KHZ 675000
|
||||
#define DP_MAX_LINK_CLK_KHZ 810000
|
||||
#define MAX_DP_MST_STREAMS 2
|
||||
|
||||
enum dp_pm_type {
|
||||
DP_CORE_PM,
|
||||
|
@ -181,6 +183,9 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
|
|||
* @mp: gpio, regulator and clock related data
|
||||
* @pinctrl: pin-control related data
|
||||
* @disp_data: controller's display related data
|
||||
* @l_pnswap: P/N swap status on each lane
|
||||
* @max_pclk_khz: maximum pixel clock supported for the platform
|
||||
* @max_lclk_khz: maximum link clock supported for the platform
|
||||
* @hw_cfg: DP HW specific settings
|
||||
* @has_mst: MST feature enable status
|
||||
* @has_mst_sideband: MST sideband feature enable status
|
||||
|
@ -191,6 +196,7 @@ static inline char *dp_phy_aux_config_type_to_string(u32 cfg_type)
|
|||
* @max_dp_dsc_blks: maximum DSC blks for DP interface
|
||||
* @max_dp_dsc_input_width_pixs: Maximum input width for DSC block
|
||||
* @has_widebus: widebus (2PPC) feature eanble status
|
||||
*@mst_fixed_port: mst port_num reserved for fixed topology
|
||||
* @parse: function to be called by client to parse device tree.
|
||||
* @get_io: function to be called by client to get io data.
|
||||
* @get_io_buf: function to be called by client to get io buffers.
|
||||
|
@ -205,8 +211,10 @@ struct dp_parser {
|
|||
struct dp_display_data disp_data;
|
||||
|
||||
u8 l_map[4];
|
||||
u8 l_pnswap;
|
||||
struct dp_aux_cfg aux_cfg[AUX_CFG_LEN];
|
||||
u32 max_pclk_khz;
|
||||
u32 max_lclk_khz;
|
||||
struct dp_hw_cfg hw_cfg;
|
||||
bool has_mst;
|
||||
bool has_mst_sideband;
|
||||
|
@ -218,6 +226,7 @@ struct dp_parser {
|
|||
u32 max_dp_dsc_blks;
|
||||
u32 max_dp_dsc_input_width_pixs;
|
||||
bool lphw_hpd;
|
||||
u32 mst_fixed_port[MAX_DP_MST_STREAMS];
|
||||
|
||||
int (*parse)(struct dp_parser *parser);
|
||||
struct dp_io_data *(*get_io)(struct dp_parser *parser, char *name);
|
||||
|
|
|
@ -351,12 +351,14 @@
|
|||
|
||||
#define TXn_TX_EMP_POST1_LVL (0x000C)
|
||||
#define TXn_TX_DRV_LVL (0x001C)
|
||||
#define TXn_TX_POL_INV (0x0064)
|
||||
|
||||
#define DP_PHY_AUX_INTERRUPT_MASK_V420 (0x0054)
|
||||
#define DP_PHY_AUX_INTERRUPT_CLEAR_V420 (0x0058)
|
||||
#define DP_PHY_AUX_INTERRUPT_STATUS_V420 (0x00D8)
|
||||
#define DP_PHY_SPARE0_V420 (0x00C8)
|
||||
#define TXn_TX_DRV_LVL_V420 (0x0014)
|
||||
#define TXn_TX_POL_INV_V420 (0x005C)
|
||||
|
||||
#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN (0x004)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2012, 2014-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2012, 2014-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __SDE_HDCP_H__
|
||||
|
@ -13,12 +13,15 @@
|
|||
#include <linux/debugfs.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/list.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <linux/hdcp_qseecom.h>
|
||||
#include "sde_kms.h"
|
||||
|
||||
#define MAX_STREAM_COUNT 2
|
||||
|
||||
enum sde_hdcp_client_id {
|
||||
HDCP_CLIENT_HDMI,
|
||||
HDCP_CLIENT_DP,
|
||||
|
@ -38,6 +41,18 @@ enum sde_hdcp_version {
|
|||
HDCP_VERSION_MAX = BIT(2),
|
||||
};
|
||||
|
||||
struct stream_info {
|
||||
u8 stream_id;
|
||||
u8 virtual_channel;
|
||||
};
|
||||
|
||||
struct sde_hdcp_stream {
|
||||
struct list_head list;
|
||||
u8 stream_id;
|
||||
u8 virtual_channel;
|
||||
u32 stream_handle;
|
||||
};
|
||||
|
||||
struct sde_hdcp_init_data {
|
||||
struct device *msm_hdcp_dev;
|
||||
struct dss_io_data *core_io;
|
||||
|
@ -67,7 +82,13 @@ struct sde_hdcp_ops {
|
|||
bool (*feature_supported)(void *input);
|
||||
void (*force_encryption)(void *input, bool enable);
|
||||
bool (*sink_support)(void *input);
|
||||
int (*set_mode)(void *input, bool mst_enabled);
|
||||
int (*on)(void *input);
|
||||
void (*off)(void *hdcp_ctrl);
|
||||
int (*register_streams)(void *input, u8 num_streams,
|
||||
struct stream_info *streams);
|
||||
int (*deregister_streams)(void *input, u8 num_streams,
|
||||
struct stream_info *streams);
|
||||
};
|
||||
|
||||
static inline const char *sde_hdcp_state_name(enum sde_hdcp_state hdcp_state)
|
||||
|
|
|
@ -64,6 +64,10 @@ struct sde_hdcp_2x_ctrl {
|
|||
atomic_t hdcp_off;
|
||||
enum sde_hdcp_2x_device_type device_type;
|
||||
u8 min_enc_level;
|
||||
struct list_head stream_handles;
|
||||
u8 stream_count;
|
||||
struct stream_info *streams;
|
||||
u8 num_streams;
|
||||
|
||||
struct task_struct *thread;
|
||||
struct completion response_completion;
|
||||
|
@ -315,6 +319,8 @@ static void sde_hdcp_2x_force_encryption(void *data, bool enable)
|
|||
|
||||
static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
|
||||
{
|
||||
struct list_head *element;
|
||||
struct sde_hdcp_stream *stream_entry;
|
||||
struct hdcp_transport_wakeup_data cdata = {HDCP_TRANSPORT_CMD_INVALID};
|
||||
|
||||
hdcp->authenticated = false;
|
||||
|
@ -322,10 +328,20 @@ static void sde_hdcp_2x_clean(struct sde_hdcp_2x_ctrl *hdcp)
|
|||
cdata.context = hdcp->client_data;
|
||||
cdata.cmd = HDCP_TRANSPORT_CMD_STATUS_FAILED;
|
||||
|
||||
if (!atomic_read(&hdcp->hdcp_off))
|
||||
sde_hdcp_2x_wakeup_client(hdcp, &cdata);
|
||||
while (!list_empty(&hdcp->stream_handles)) {
|
||||
element = hdcp->stream_handles.next;
|
||||
list_del(element);
|
||||
|
||||
atomic_set(&hdcp->hdcp_off, 1);
|
||||
stream_entry = list_entry(element, struct sde_hdcp_stream,
|
||||
list);
|
||||
hdcp2_close_stream(hdcp->hdcp2_ctx,
|
||||
stream_entry->stream_handle);
|
||||
kzfree(stream_entry);
|
||||
hdcp->stream_count--;
|
||||
}
|
||||
|
||||
if (!atomic_xchg(&hdcp->hdcp_off, 1))
|
||||
sde_hdcp_2x_wakeup_client(hdcp, &cdata);
|
||||
|
||||
hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_STOP, &hdcp->app_data);
|
||||
}
|
||||
|
@ -477,19 +493,26 @@ static void sde_hdcp_2x_msg_sent(struct sde_hdcp_2x_ctrl *hdcp)
|
|||
static void sde_hdcp_2x_init(struct sde_hdcp_2x_ctrl *hdcp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START, &hdcp->app_data);
|
||||
if (rc)
|
||||
goto exit;
|
||||
sde_hdcp_2x_clean(hdcp);
|
||||
}
|
||||
|
||||
pr_debug("[tz]: %s\n", sde_hdcp_2x_message_name(
|
||||
hdcp->app_data.response.data[0]));
|
||||
static void sde_hdcp_2x_start_auth(struct sde_hdcp_2x_ctrl *hdcp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = hdcp2_app_comm(hdcp->hdcp2_ctx, HDCP2_CMD_START_AUTH,
|
||||
&hdcp->app_data);
|
||||
if (rc) {
|
||||
sde_hdcp_2x_clean(hdcp);
|
||||
return;
|
||||
}
|
||||
|
||||
pr_debug("message received from TZ: %s\n",
|
||||
sde_hdcp_2x_message_name(hdcp->app_data.response.data[0]));
|
||||
|
||||
sde_hdcp_2x_send_message(hdcp);
|
||||
|
||||
return;
|
||||
exit:
|
||||
sde_hdcp_2x_clean(hdcp);
|
||||
}
|
||||
|
||||
static void sde_hdcp_2x_timeout(struct sde_hdcp_2x_ctrl *hdcp)
|
||||
|
@ -539,7 +562,8 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
|
|||
goto exit;
|
||||
}
|
||||
|
||||
if (hdcp->device_type == HDCP_TXMTR_DP) {
|
||||
if (hdcp->device_type == HDCP_TXMTR_DP ||
|
||||
hdcp->device_type == HDCP_TXMTR_DP_MST) {
|
||||
msg[0] = hdcp->last_msg;
|
||||
message_id_bytes = 1;
|
||||
}
|
||||
|
@ -625,6 +649,147 @@ static void sde_hdcp_2x_msg_recvd(struct sde_hdcp_2x_ctrl *hdcp)
|
|||
sde_hdcp_2x_clean(hdcp);
|
||||
}
|
||||
|
||||
static struct list_head *sde_hdcp_2x_stream_present(
|
||||
struct sde_hdcp_2x_ctrl *hdcp, u8 stream_id, u8 virtual_channel)
|
||||
{
|
||||
struct sde_hdcp_stream *stream_entry;
|
||||
struct list_head *entry;
|
||||
bool present = false;
|
||||
|
||||
list_for_each(entry, &hdcp->stream_handles) {
|
||||
stream_entry = list_entry(entry,
|
||||
struct sde_hdcp_stream, list);
|
||||
if (stream_entry->virtual_channel == virtual_channel &&
|
||||
stream_entry->stream_id == stream_id) {
|
||||
present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!present)
|
||||
entry = NULL;
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void sde_hdcp_2x_open_stream(struct sde_hdcp_2x_ctrl *hdcp)
|
||||
{
|
||||
int rc;
|
||||
size_t iterations, i;
|
||||
u8 stream_id;
|
||||
u8 virtual_channel;
|
||||
u32 stream_handle = 0;
|
||||
bool query_streams = false;
|
||||
|
||||
if (!hdcp->streams) {
|
||||
pr_err("Array of streams to register is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT));
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
if (hdcp->stream_count == MAX_STREAM_COUNT) {
|
||||
pr_debug("Registered the maximum amount of streams\n");
|
||||
break;
|
||||
}
|
||||
|
||||
stream_id = hdcp->streams[i].stream_id;
|
||||
virtual_channel = hdcp->streams[i].virtual_channel;
|
||||
|
||||
pr_debug("Opening stream %d, virtual channel %d\n",
|
||||
stream_id, virtual_channel);
|
||||
|
||||
if (sde_hdcp_2x_stream_present(hdcp, stream_id,
|
||||
virtual_channel)) {
|
||||
pr_debug("Stream %d, virtual channel %d already open\n",
|
||||
stream_id, virtual_channel);
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = hdcp2_open_stream(hdcp->hdcp2_ctx, virtual_channel,
|
||||
stream_id, &stream_handle);
|
||||
if (rc) {
|
||||
pr_err("Unable to open stream %d, virtual channel %d\n",
|
||||
stream_id, virtual_channel);
|
||||
} else {
|
||||
struct sde_hdcp_stream *stream =
|
||||
kzalloc(sizeof(struct sde_hdcp_stream),
|
||||
GFP_KERNEL);
|
||||
if (!stream)
|
||||
break;
|
||||
|
||||
INIT_LIST_HEAD(&stream->list);
|
||||
stream->stream_handle = stream_handle;
|
||||
stream->stream_id = stream_id;
|
||||
stream->virtual_channel = virtual_channel;
|
||||
|
||||
list_add(&stream->list, &hdcp->stream_handles);
|
||||
hdcp->stream_count++;
|
||||
|
||||
query_streams = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (query_streams && hdcp->authenticated)
|
||||
sde_hdcp_2x_query_stream(hdcp);
|
||||
}
|
||||
|
||||
static void sde_hdcp_2x_close_stream(struct sde_hdcp_2x_ctrl *hdcp)
|
||||
{
|
||||
int rc;
|
||||
size_t iterations, i;
|
||||
u8 stream_id;
|
||||
u8 virtual_channel;
|
||||
struct list_head *entry;
|
||||
struct sde_hdcp_stream *stream_entry;
|
||||
bool query_streams = false;
|
||||
|
||||
if (!hdcp->streams) {
|
||||
pr_err("Array of streams to register is NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
iterations = min(hdcp->num_streams, (u8)(MAX_STREAM_COUNT));
|
||||
|
||||
for (i = 0; i < iterations; i++) {
|
||||
if (hdcp->stream_count == 0) {
|
||||
pr_debug("No streams are currently registered\n");
|
||||
return;
|
||||
}
|
||||
|
||||
stream_id = hdcp->streams[i].stream_id;
|
||||
virtual_channel = hdcp->streams[i].virtual_channel;
|
||||
|
||||
pr_debug("Closing stream %d, virtual channel %d\n",
|
||||
stream_id, virtual_channel);
|
||||
|
||||
entry = sde_hdcp_2x_stream_present(hdcp, stream_id,
|
||||
virtual_channel);
|
||||
|
||||
if (!entry) {
|
||||
pr_err("Unable to find stream %d, virtual channel %d\n"
|
||||
, stream_id, virtual_channel);
|
||||
continue;
|
||||
}
|
||||
|
||||
stream_entry = list_entry(entry, struct sde_hdcp_stream,
|
||||
list);
|
||||
|
||||
rc = hdcp2_close_stream(hdcp->hdcp2_ctx,
|
||||
stream_entry->stream_handle);
|
||||
if (rc)
|
||||
pr_err("Unable to close stream %d, virtual channel %d\n"
|
||||
, stream_id, virtual_channel);
|
||||
hdcp->stream_count--;
|
||||
list_del(entry);
|
||||
kzfree(stream_entry);
|
||||
query_streams = true;
|
||||
}
|
||||
|
||||
if (query_streams && hdcp->authenticated)
|
||||
sde_hdcp_2x_query_stream(hdcp);
|
||||
}
|
||||
|
||||
/** sde_hdcp_2x_wakeup() - wakeup the module to execute a requested command
|
||||
* @data: data required for executing corresponding command.
|
||||
*
|
||||
|
@ -648,6 +813,8 @@ static int sde_hdcp_2x_wakeup(struct sde_hdcp_2x_wakeup_data *data)
|
|||
hdcp->timeout_left = data->timeout;
|
||||
hdcp->total_message_length = data->total_message_length;
|
||||
hdcp->min_enc_level = data->min_enc_level;
|
||||
hdcp->streams = data->streams;
|
||||
hdcp->num_streams = data->num_streams;
|
||||
|
||||
if (!completion_done(&hdcp->response_completion))
|
||||
complete_all(&hdcp->response_completion);
|
||||
|
@ -709,6 +876,9 @@ static int sde_hdcp_2x_main(void *data)
|
|||
case HDCP_2X_CMD_STOP:
|
||||
sde_hdcp_2x_clean(hdcp);
|
||||
break;
|
||||
case HDCP_2X_CMD_START_AUTH:
|
||||
sde_hdcp_2x_start_auth(hdcp);
|
||||
break;
|
||||
case HDCP_2X_CMD_MSG_SEND_SUCCESS:
|
||||
sde_hdcp_2x_msg_sent(hdcp);
|
||||
break;
|
||||
|
@ -733,6 +903,12 @@ static int sde_hdcp_2x_main(void *data)
|
|||
}
|
||||
sde_hdcp_2x_query_stream(hdcp);
|
||||
break;
|
||||
case HDCP_2X_CMD_OPEN_STREAMS:
|
||||
sde_hdcp_2x_open_stream(hdcp);
|
||||
break;
|
||||
case HDCP_2X_CMD_CLOSE_STREAMS:
|
||||
sde_hdcp_2x_close_stream(hdcp);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -777,16 +953,14 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
|
|||
goto unlock;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&hdcp->stream_handles);
|
||||
hdcp->client_data = data->client_data;
|
||||
hdcp->client_ops = data->client_ops;
|
||||
hdcp->device_type = data->device_type;
|
||||
|
||||
hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type);
|
||||
|
||||
INIT_KFIFO(hdcp->cmd_q);
|
||||
|
||||
init_waitqueue_head(&hdcp->wait_q);
|
||||
atomic_set(&hdcp->hdcp_off, 0);
|
||||
atomic_set(&hdcp->hdcp_off, 1);
|
||||
|
||||
init_completion(&hdcp->response_completion);
|
||||
|
||||
|
@ -811,6 +985,40 @@ int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data)
|
|||
return rc;
|
||||
}
|
||||
|
||||
int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type)
|
||||
{
|
||||
int rc = 0;
|
||||
struct sde_hdcp_2x_ctrl *hdcp = data;
|
||||
|
||||
if (!hdcp)
|
||||
return -EINVAL;
|
||||
|
||||
if (hdcp->hdcp2_ctx) {
|
||||
pr_debug("HDCP library context already acquired\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
hdcp->device_type = device_type;
|
||||
hdcp->hdcp2_ctx = hdcp2_init(hdcp->device_type);
|
||||
if (!hdcp->hdcp2_ctx) {
|
||||
pr_err("Unable to acquire HDCP library handle\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void sde_hdcp_2x_disable(void *data)
|
||||
{
|
||||
struct sde_hdcp_2x_ctrl *hdcp = data;
|
||||
|
||||
if (!hdcp->hdcp2_ctx)
|
||||
return;
|
||||
|
||||
hdcp2_deinit(hdcp->hdcp2_ctx);
|
||||
hdcp->hdcp2_ctx = NULL;
|
||||
}
|
||||
|
||||
void sde_hdcp_2x_deregister(void *data)
|
||||
{
|
||||
struct sde_hdcp_2x_ctrl *hdcp = data;
|
||||
|
@ -818,7 +1026,7 @@ void sde_hdcp_2x_deregister(void *data)
|
|||
if (!hdcp)
|
||||
return;
|
||||
|
||||
sde_hdcp_2x_disable(data);
|
||||
kthread_stop(hdcp->thread);
|
||||
hdcp2_deinit(hdcp->hdcp2_ctx);
|
||||
kzfree(hdcp);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,9 @@
|
|||
/**
|
||||
* enum sde_hdcp_2x_wakeup_cmd - commands for interacting with HDCP driver
|
||||
* @HDCP_2X_CMD_INVALID: initialization value
|
||||
* @HDCP_2X_CMD_START: start authentication
|
||||
* @HDCP_2X_CMD_STOP: stop authentication
|
||||
* @HDCP_2X_CMD_START: start HDCP driver
|
||||
* @HDCP_2X_CMD_START_AUTH: start authentication
|
||||
* @HDCP_2X_CMD_STOP: stop HDCP driver
|
||||
* @HDCP_2X_CMD_MSG_SEND_SUCCESS: sending message to sink succeeded
|
||||
* @HDCP_2X_CMD_MSG_SEND_FAILED: sending message to sink failed
|
||||
* @HDCP_2X_CMD_MSG_SEND_TIMEOUT: sending message to sink timed out
|
||||
|
@ -26,10 +27,13 @@
|
|||
* @HDCP_2X_CMD_QUERY_STREAM_TYPE: start content stream processing
|
||||
* @HDCP_2X_CMD_LINK_FAILED: link failure notification
|
||||
* @HDCP_2X_CMD_MIN_ENC_LEVEL: trigger minimum encryption level change
|
||||
* @HDCP_2X_CMD_OPEN_STREAMS: open a virtual channel
|
||||
* @HDCP_2X_CMD_CLOSE_STREAMS: close a virtual channel
|
||||
*/
|
||||
enum sde_hdcp_2x_wakeup_cmd {
|
||||
HDCP_2X_CMD_INVALID,
|
||||
HDCP_2X_CMD_START,
|
||||
HDCP_2X_CMD_START_AUTH,
|
||||
HDCP_2X_CMD_STOP,
|
||||
HDCP_2X_CMD_MSG_SEND_SUCCESS,
|
||||
HDCP_2X_CMD_MSG_SEND_FAILED,
|
||||
|
@ -40,6 +44,8 @@ enum sde_hdcp_2x_wakeup_cmd {
|
|||
HDCP_2X_CMD_QUERY_STREAM_TYPE,
|
||||
HDCP_2X_CMD_LINK_FAILED,
|
||||
HDCP_2X_CMD_MIN_ENC_LEVEL,
|
||||
HDCP_2X_CMD_OPEN_STREAMS,
|
||||
HDCP_2X_CMD_CLOSE_STREAMS,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -66,16 +72,19 @@ enum hdcp_transport_wakeup_cmd {
|
|||
|
||||
enum sde_hdcp_2x_device_type {
|
||||
HDCP_TXMTR_HDMI = 0x8001,
|
||||
HDCP_TXMTR_DP = 0x8002
|
||||
HDCP_TXMTR_DP = 0x8002,
|
||||
HDCP_TXMTR_DP_MST = 0x8003
|
||||
};
|
||||
|
||||
/**
|
||||
* struct sde_hdcp_2x_lib_wakeup_data - command and data send to HDCP driver
|
||||
* @cmd: command type
|
||||
* @context: void pointer to the HDCP driver instance
|
||||
* @buf: message received from the sink
|
||||
* @buf_len: length of message received from the sink
|
||||
* @timeout: time out value for timed transactions
|
||||
* @cmd: command type
|
||||
* @context: void pointer to the HDCP driver instance
|
||||
* @buf: message received from the sink
|
||||
* @buf_len: length of message received from the sink
|
||||
* @timeout: time out value for timed transactions
|
||||
* @streams: list indicating which streams need adjustment
|
||||
* @num_streams: number of entries in streams
|
||||
*/
|
||||
struct sde_hdcp_2x_wakeup_data {
|
||||
enum sde_hdcp_2x_wakeup_cmd cmd;
|
||||
|
@ -83,6 +92,8 @@ struct sde_hdcp_2x_wakeup_data {
|
|||
uint32_t total_message_length;
|
||||
uint32_t timeout;
|
||||
u8 min_enc_level;
|
||||
struct stream_info *streams;
|
||||
u8 num_streams;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -151,6 +162,10 @@ static inline const char *sde_hdcp_2x_cmd_to_str(
|
|||
return TO_STR(HDCP_2X_CMD_MSG_RECV_TIMEOUT);
|
||||
case HDCP_2X_CMD_QUERY_STREAM_TYPE:
|
||||
return TO_STR(HDCP_2X_CMD_QUERY_STREAM_TYPE);
|
||||
case HDCP_2X_CMD_OPEN_STREAMS:
|
||||
return TO_STR(HDCP_2X_CMD_OPEN_STREAMS);
|
||||
case HDCP_2X_CMD_CLOSE_STREAMS:
|
||||
return TO_STR(HDCP_2X_CMD_CLOSE_STREAMS);
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
@ -190,12 +205,13 @@ struct hdcp_transport_ops {
|
|||
struct sde_hdcp_2x_register_data {
|
||||
struct hdcp_transport_ops *client_ops;
|
||||
struct sde_hdcp_2x_ops *ops;
|
||||
enum sde_hdcp_2x_device_type device_type;
|
||||
void *client_data;
|
||||
void **hdcp_data;
|
||||
};
|
||||
|
||||
/* functions for the HDCP 2.2 state machine module */
|
||||
int sde_hdcp_2x_register(struct sde_hdcp_2x_register_data *data);
|
||||
int sde_hdcp_2x_enable(void *data, enum sde_hdcp_2x_device_type device_type);
|
||||
void sde_hdcp_2x_disable(void *data);
|
||||
void sde_hdcp_2x_deregister(void *data);
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "[hdcp-qseecom] %s: " fmt, __func__
|
||||
|
@ -1044,11 +1044,7 @@ static int hdcp2_app_start(struct hdcp2_handle *handle)
|
|||
}
|
||||
|
||||
rc = handle->tx_init(handle);
|
||||
if (rc)
|
||||
goto error;
|
||||
|
||||
if (!handle->legacy_app)
|
||||
rc = hdcp2_app_start_auth(handle);
|
||||
error:
|
||||
return rc;
|
||||
}
|
||||
|
@ -1188,6 +1184,7 @@ int hdcp2_force_encryption(void *ctx, uint32_t enable)
|
|||
pr_err("failed, rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(hdcp2_force_encryption);
|
||||
|
||||
static int hdcp2_app_query_stream(struct hdcp2_handle *handle)
|
||||
{
|
||||
|
@ -1236,6 +1233,9 @@ int hdcp2_app_comm(void *ctx, enum hdcp2_app_cmd cmd,
|
|||
case HDCP2_CMD_START:
|
||||
rc = hdcp2_app_start(handle);
|
||||
break;
|
||||
case HDCP2_CMD_START_AUTH:
|
||||
rc = hdcp2_app_start_auth(handle);
|
||||
break;
|
||||
case HDCP2_CMD_PROCESS_MSG:
|
||||
rc = hdcp2_app_process_msg(handle);
|
||||
break;
|
||||
|
@ -1268,6 +1268,7 @@ int hdcp2_app_comm(void *ctx, enum hdcp2_app_cmd cmd,
|
|||
error:
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(hdcp2_app_comm);
|
||||
|
||||
static int hdcp2_open_stream_helper(struct hdcp2_handle *handle,
|
||||
uint8_t vc_payload_id,
|
||||
|
@ -1322,6 +1323,7 @@ int hdcp2_open_stream(void *ctx, uint8_t vc_payload_id, uint8_t stream_number,
|
|||
return hdcp2_open_stream_helper(handle, vc_payload_id, stream_number,
|
||||
stream_id);
|
||||
}
|
||||
EXPORT_SYMBOL(hdcp2_open_stream);
|
||||
|
||||
static int hdcp2_close_stream_helper(struct hdcp2_handle *handle,
|
||||
uint32_t stream_id)
|
||||
|
@ -1368,6 +1370,7 @@ int hdcp2_close_stream(void *ctx, uint32_t stream_id)
|
|||
|
||||
return hdcp2_close_stream_helper(handle, stream_id);
|
||||
}
|
||||
EXPORT_SYMBOL(hdcp2_close_stream);
|
||||
|
||||
void *hdcp2_init(u32 device_type)
|
||||
{
|
||||
|
@ -1382,11 +1385,13 @@ void *hdcp2_init(u32 device_type)
|
|||
error:
|
||||
return handle;
|
||||
}
|
||||
EXPORT_SYMBOL(hdcp2_init);
|
||||
|
||||
void hdcp2_deinit(void *ctx)
|
||||
{
|
||||
kzfree(ctx);
|
||||
}
|
||||
EXPORT_SYMBOL(hdcp2_deinit);
|
||||
|
||||
void *hdcp1_init(void)
|
||||
{
|
||||
|
|
|
@ -634,4 +634,12 @@ int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
|
|||
int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port, bool power_up);
|
||||
|
||||
int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port,
|
||||
int offset, int size, u8 *bytes);
|
||||
|
||||
int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_dp_mst_port *port,
|
||||
int offset, int size, u8 *bytes);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2015-2019, The Linux Foundation. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef __HDCP_QSEECOM_H
|
||||
|
@ -11,6 +11,7 @@
|
|||
|
||||
enum hdcp2_app_cmd {
|
||||
HDCP2_CMD_START,
|
||||
HDCP2_CMD_START_AUTH,
|
||||
HDCP2_CMD_STOP,
|
||||
HDCP2_CMD_PROCESS_MSG,
|
||||
HDCP2_CMD_TIMEOUT,
|
||||
|
@ -35,6 +36,8 @@ static inline const char *hdcp2_app_cmd_str(enum hdcp2_app_cmd cmd)
|
|||
switch (cmd) {
|
||||
case HDCP2_CMD_START:
|
||||
return HDCP_QSEECOM_ENUM_STR(HDCP2_CMD_START);
|
||||
case HDCP2_CMD_START_AUTH:
|
||||
return HDCP_QSEECOM_ENUM_STR(HDCP2_CMD_START_AUTH);
|
||||
case HDCP2_CMD_STOP:
|
||||
return HDCP_QSEECOM_ENUM_STR(HDCP2_CMD_STOP);
|
||||
case HDCP2_CMD_PROCESS_MSG:
|
||||
|
|
Loading…
Reference in a new issue