Commit 6a3608ea authored by Parshuram Thombare's avatar Parshuram Thombare Committed by Robert Foss

drm: bridge: cdns-mhdp8546: Enable HDCP

This patch enable HDCP in MHDP driver.
Signed-off-by: default avatarParshuram Thombare <pthombar@cadence.com>
Reviewed-by: default avatarRobert Foss <robert.foss@linaro.org>
Signed-off-by: default avatarRobert Foss <robert.foss@linaro.org>
Link: https://patchwork.freedesktop.org/patch/msgid/1618078542-30679-1-git-send-email-pthombar@cadence.com
parent 7169d082
# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_DRM_CDNS_MHDP8546) += cdns-mhdp8546.o
cdns-mhdp8546-y := cdns-mhdp8546-core.o
cdns-mhdp8546-y := cdns-mhdp8546-core.o cdns-mhdp8546-hdcp.o
cdns-mhdp8546-$(CONFIG_DRM_CDNS_MHDP8546_J721E) += cdns-mhdp8546-j721e.o
......@@ -42,6 +42,7 @@
#include <drm/drm_connector.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_hdcp.h>
#include <drm/drm_modeset_helper_vtables.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
......@@ -49,7 +50,7 @@
#include <asm/unaligned.h>
#include "cdns-mhdp8546-core.h"
#include "cdns-mhdp8546-hdcp.h"
#include "cdns-mhdp8546-j721e.h"
static int cdns_mhdp_mailbox_read(struct cdns_mhdp_device *mhdp)
......@@ -1614,10 +1615,51 @@ enum drm_mode_status cdns_mhdp_mode_valid(struct drm_connector *conn,
return MODE_OK;
}
static int cdns_mhdp_connector_atomic_check(struct drm_connector *conn,
struct drm_atomic_state *state)
{
struct cdns_mhdp_device *mhdp = connector_to_mhdp(conn);
struct drm_connector_state *old_state, *new_state;
struct drm_crtc_state *crtc_state;
u64 old_cp, new_cp;
if (!mhdp->hdcp_supported)
return 0;
old_state = drm_atomic_get_old_connector_state(state, conn);
new_state = drm_atomic_get_new_connector_state(state, conn);
old_cp = old_state->content_protection;
new_cp = new_state->content_protection;
if (old_state->hdcp_content_type != new_state->hdcp_content_type &&
new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
goto mode_changed;
}
if (!new_state->crtc) {
if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
return 0;
}
if (old_cp == new_cp ||
(old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED))
return 0;
mode_changed:
crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc);
crtc_state->mode_changed = true;
return 0;
}
static const struct drm_connector_helper_funcs cdns_mhdp_conn_helper_funcs = {
.detect_ctx = cdns_mhdp_connector_detect,
.get_modes = cdns_mhdp_get_modes,
.mode_valid = cdns_mhdp_mode_valid,
.atomic_check = cdns_mhdp_connector_atomic_check,
};
static const struct drm_connector_funcs cdns_mhdp_conn_funcs = {
......@@ -1662,7 +1704,10 @@ static int cdns_mhdp_connector_init(struct cdns_mhdp_device *mhdp)
return ret;
}
return 0;
if (mhdp->hdcp_supported)
ret = drm_connector_attach_content_protection_property(conn, true);
return ret;
}
static int cdns_mhdp_attach(struct drm_bridge *bridge,
......@@ -1957,6 +2002,15 @@ static void cdns_mhdp_atomic_enable(struct drm_bridge *bridge,
if (WARN_ON(!conn_state))
goto out;
if (mhdp->hdcp_supported &&
mhdp->hw_state == MHDP_HW_READY &&
conn_state->content_protection ==
DRM_MODE_CONTENT_PROTECTION_DESIRED) {
mutex_unlock(&mhdp->link_mutex);
cdns_mhdp_hdcp_enable(mhdp, conn_state->hdcp_content_type);
mutex_lock(&mhdp->link_mutex);
}
crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
if (WARN_ON(!crtc_state))
goto out;
......@@ -2000,6 +2054,9 @@ static void cdns_mhdp_atomic_disable(struct drm_bridge *bridge,
mutex_lock(&mhdp->link_mutex);
if (mhdp->hdcp_supported)
cdns_mhdp_hdcp_disable(mhdp);
mhdp->bridge_enabled = false;
cdns_mhdp_reg_read(mhdp, CDNS_DP_FRAMER_GLOBAL_CONFIG, &resp);
resp &= ~CDNS_DP_FRAMER_EN;
......@@ -2288,7 +2345,6 @@ static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data)
struct cdns_mhdp_device *mhdp = data;
u32 apb_stat, sw_ev0;
bool bridge_attached;
int ret;
apb_stat = readl(mhdp->regs + CDNS_APB_INT_STATUS);
if (!(apb_stat & CDNS_APB_INT_MASK_SW_EVENT_INT))
......@@ -2307,20 +2363,54 @@ static irqreturn_t cdns_mhdp_irq_handler(int irq, void *data)
spin_unlock(&mhdp->start_lock);
if (bridge_attached && (sw_ev0 & CDNS_DPTX_HPD)) {
ret = cdns_mhdp_update_link_status(mhdp);
if (mhdp->connector.dev) {
if (ret < 0)
schedule_work(&mhdp->modeset_retry_work);
else
drm_kms_helper_hotplug_event(mhdp->bridge.dev);
} else {
drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp));
}
schedule_work(&mhdp->hpd_work);
}
if (sw_ev0 & ~CDNS_DPTX_HPD) {
mhdp->sw_events |= (sw_ev0 & ~CDNS_DPTX_HPD);
wake_up(&mhdp->sw_events_wq);
}
return IRQ_HANDLED;
}
u32 cdns_mhdp_wait_for_sw_event(struct cdns_mhdp_device *mhdp, u32 event)
{
u32 ret;
ret = wait_event_timeout(mhdp->sw_events_wq,
mhdp->sw_events & event,
msecs_to_jiffies(500));
if (!ret) {
dev_dbg(mhdp->dev, "SW event 0x%x timeout\n", event);
goto sw_event_out;
}
ret = mhdp->sw_events;
mhdp->sw_events &= ~event;
sw_event_out:
return ret;
}
static void cdns_mhdp_hpd_work(struct work_struct *work)
{
struct cdns_mhdp_device *mhdp = container_of(work,
struct cdns_mhdp_device,
hpd_work);
int ret;
ret = cdns_mhdp_update_link_status(mhdp);
if (mhdp->connector.dev) {
if (ret < 0)
schedule_work(&mhdp->modeset_retry_work);
else
drm_kms_helper_hotplug_event(mhdp->bridge.dev);
} else {
drm_bridge_hpd_notify(&mhdp->bridge, cdns_mhdp_detect(mhdp));
}
}
static int cdns_mhdp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
......@@ -2356,6 +2446,15 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
return PTR_ERR(mhdp->regs);
}
mhdp->sapb_regs = devm_platform_ioremap_resource_byname(pdev, "mhdptx-sapb");
if (IS_ERR(mhdp->sapb_regs)) {
mhdp->hdcp_supported = false;
dev_warn(dev,
"Failed to get SAPB memory resource, HDCP not supported\n");
} else {
mhdp->hdcp_supported = true;
}
mhdp->phy = devm_of_phy_get_by_index(dev, pdev->dev.of_node, 0);
if (IS_ERR(mhdp->phy)) {
dev_err(dev, "no PHY configured\n");
......@@ -2430,13 +2529,18 @@ static int cdns_mhdp_probe(struct platform_device *pdev)
/* Initialize the work for modeset in case of link train failure */
INIT_WORK(&mhdp->modeset_retry_work, cdns_mhdp_modeset_retry_fn);
INIT_WORK(&mhdp->hpd_work, cdns_mhdp_hpd_work);
init_waitqueue_head(&mhdp->fw_load_wq);
init_waitqueue_head(&mhdp->sw_events_wq);
ret = cdns_mhdp_load_firmware(mhdp);
if (ret)
goto phy_exit;
if (mhdp->hdcp_supported)
cdns_mhdp_hdcp_init(mhdp);
drm_bridge_add(&mhdp->bridge);
return 0;
......
......@@ -47,6 +47,10 @@ struct phy;
#define CDNS_SW_EVENT0 0x00044
#define CDNS_DPTX_HPD BIT(0)
#define CDNS_HDCP_TX_STATUS BIT(4)
#define CDNS_HDCP2_TX_IS_KM_STORED BIT(5)
#define CDNS_HDCP2_TX_STORE_KM BIT(6)
#define CDNS_HDCP_TX_IS_RCVR_ID_VALID BIT(7)
#define CDNS_SW_EVENT1 0x00048
#define CDNS_SW_EVENT2 0x0004c
......@@ -339,8 +343,17 @@ struct cdns_mhdp_platform_info {
#define to_cdns_mhdp_bridge_state(s) \
container_of(s, struct cdns_mhdp_bridge_state, base)
struct cdns_mhdp_hdcp {
struct delayed_work check_work;
struct work_struct prop_work;
struct mutex mutex; /* mutex to protect hdcp.value */
u32 value;
u8 hdcp_content_type;
};
struct cdns_mhdp_device {
void __iomem *regs;
void __iomem *sapb_regs;
void __iomem *j721e_regs;
struct device *dev;
......@@ -392,9 +405,18 @@ struct cdns_mhdp_device {
/* Work struct to schedule a uevent on link train failure */
struct work_struct modeset_retry_work;
struct work_struct hpd_work;
wait_queue_head_t sw_events_wq;
u32 sw_events;
struct cdns_mhdp_hdcp hdcp;
bool hdcp_supported;
};
#define connector_to_mhdp(x) container_of(x, struct cdns_mhdp_device, connector)
#define bridge_to_mhdp(x) container_of(x, struct cdns_mhdp_device, bridge)
u32 cdns_mhdp_wait_for_sw_event(struct cdns_mhdp_device *mhdp, uint32_t event);
#endif
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cadence MHDP8546 DP bridge driver.
*
* Copyright (C) 2020 Cadence Design Systems, Inc.
*
*/
#ifndef CDNS_MHDP8546_HDCP_H
#define CDNS_MHDP8546_HDCP_H
#include "cdns-mhdp8546-core.h"
#define HDCP_MAX_RECEIVERS 32
#define HDCP_RECEIVER_ID_SIZE_BYTES 5
#define HDCP_STATUS_SIZE 0x5
#define HDCP_PORT_STS_AUTH 0x1
#define HDCP_PORT_STS_LAST_ERR_SHIFT 0x5
#define HDCP_PORT_STS_LAST_ERR_MASK (0x0F << 5)
#define GET_HDCP_PORT_STS_LAST_ERR(__sts__) \
(((__sts__) & HDCP_PORT_STS_LAST_ERR_MASK) >> \
HDCP_PORT_STS_LAST_ERR_SHIFT)
#define HDCP_CONFIG_1_4 BIT(0) /* use HDCP 1.4 only */
#define HDCP_CONFIG_2_2 BIT(1) /* use HDCP 2.2 only */
/* use All HDCP versions */
#define HDCP_CONFIG_ALL (BIT(0) | BIT(1))
#define HDCP_CONFIG_NONE 0
enum {
HDCP_GENERAL_SET_LC_128,
HDCP_SET_SEED,
};
enum {
HDCP_TRAN_CONFIGURATION,
HDCP2X_TX_SET_PUBLIC_KEY_PARAMS,
HDCP2X_TX_SET_DEBUG_RANDOM_NUMBERS,
HDCP2X_TX_RESPOND_KM,
HDCP1_TX_SEND_KEYS,
HDCP1_TX_SEND_RANDOM_AN,
HDCP_TRAN_STATUS_CHANGE,
HDCP2X_TX_IS_KM_STORED,
HDCP2X_TX_STORE_KM,
HDCP_TRAN_IS_REC_ID_VALID,
HDCP_TRAN_RESPOND_RECEIVER_ID_VALID,
HDCP_TRAN_TEST_KEYS,
HDCP2X_TX_SET_KM_KEY_PARAMS,
HDCP_NUM_OF_SUPPORTED_MESSAGES
};
enum {
HDCP_CONTENT_TYPE_0,
HDCP_CONTENT_TYPE_1,
};
#define DRM_HDCP_CHECK_PERIOD_MS (128 * 16)
#define HDCP_PAIRING_R_ID 5
#define HDCP_PAIRING_M_LEN 16
#define HDCP_KM_LEN 16
#define HDCP_PAIRING_M_EKH 16
struct cdns_hdcp_pairing_data {
u8 receiver_id[HDCP_PAIRING_R_ID];
u8 m[HDCP_PAIRING_M_LEN];
u8 km[HDCP_KM_LEN];
u8 ekh[HDCP_PAIRING_M_EKH];
};
enum {
HDCP_TX_2,
HDCP_TX_1,
HDCP_TX_BOTH,
};
#define DLP_MODULUS_N 384
#define DLP_E 3
struct cdns_hdcp_tx_public_key_param {
u8 N[DLP_MODULUS_N];
u8 E[DLP_E];
};
int cdns_mhdp_hdcp_set_public_key_param(struct cdns_mhdp_device *mhdp,
struct cdns_hdcp_tx_public_key_param *val);
int cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device *mhdp, u8 *val);
int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type);
int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp);
void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment