Merge "drm/msm/dp: add encoder and connector reservation dp-mst"

This commit is contained in:
qctecmdr Service 2019-03-26 21:03:58 -07:00 committed by Gerrit - the friendly Code Review server
commit 1a40281936
24 changed files with 1314 additions and 276 deletions

View file

@ -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

View file

@ -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)
{

View file

@ -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,

View file

@ -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);

View file

@ -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");

View file

@ -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);

View file

@ -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);

View file

@ -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;
};
/**

View file

@ -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) {

View file

@ -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);

View file

@ -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) {

View file

@ -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(&register_data);

View file

@ -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");
}

View file

@ -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;

View file

@ -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);

View file

@ -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;
}

View file

@ -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);

View file

@ -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)

View file

@ -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)

View file

@ -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);
}

View file

@ -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

View file

@ -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)
{

View file

@ -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

View file

@ -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: