From 04dd17822334871b23ea2862f7798fb0e0007777 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 11 May 2024 08:53:19 +0000
Subject: [PATCH] change otg to host mode
---
kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 files changed, 407 insertions(+), 47 deletions(-)
diff --git a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
index 7ea4a54..906f0f6 100644
--- a/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
+++ b/kernel/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c
@@ -4,6 +4,7 @@
* Author:
* Algea Cao <algea.cao@rock-chips.com>
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
@@ -25,6 +26,7 @@
#include <drm/drm_dsc.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder_slave.h>
+#include <drm/drm_hdcp.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
@@ -38,6 +40,7 @@
#include "dw-hdmi-qp-audio.h"
#include "dw-hdmi-qp.h"
#include "dw-hdmi-qp-cec.h"
+#include "dw-hdmi-qp-hdcp.h"
#include <media/cec-notifier.h>
@@ -51,6 +54,19 @@
#define HDMI14_MAX_TMDSCLK 340000000
#define HDMI20_MAX_TMDSCLK_KHZ 600000
+
+#define HDMI_VH0 0x20
+#define HDMI_HDCP_ADDR 0x3a
+#define HDMI_BCAPS 0x40
+#define HDMI_HDCP14_SUPPORT BIT(7)
+#define HDMI_HDCP2_VERSION 0x50
+#define HDMI_HDCP2_SUPPORT BIT(2)
+
+#define SINK_CAP_HDCP14 BIT(0)
+#define SINK_CAP_HDCP2 BIT(1)
+
+#define HDMI_HDCP2_AUTH BIT(1)
+#define HDMI_HDCP14_AUTH BIT(0)
static const unsigned int dw_hdmi_cable[] = {
EXTCON_DISP_HDMI,
@@ -221,6 +237,7 @@
struct dw_hdmi_qp {
struct drm_connector connector;
struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
struct drm_panel *panel;
struct platform_device *hdcp_dev;
struct platform_device *audio;
@@ -230,7 +247,7 @@
struct hdmi_qp_data_info hdmi_data;
const struct dw_hdmi_plat_data *plat_data;
-
+ struct dw_qp_hdcp *hdcp;
int vic;
int main_irq;
int avp_irq;
@@ -249,6 +266,7 @@
struct i2c_adapter *ddc;
void __iomem *regs;
+ void __iomem *hdcp14_mem;
bool sink_is_hdmi;
bool sink_has_audio;
bool dclk_en;
@@ -256,6 +274,7 @@
bool cec_enable;
bool allm_enable;
bool support_hdmi;
+ bool skip_connector;
int force_output;
int vp_id;
int old_vp_id;
@@ -268,6 +287,8 @@
bool rxsense; /* rxsense state */
u8 phy_mask; /* desired phy int mask settings */
u8 mc_clkdis; /* clock disable register */
+ u8 hdcp_caps;
+ u8 hdcp_status;
bool update;
bool hdr2sdr;
@@ -944,8 +965,6 @@
{
/* Software reset */
hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
-
- hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
@@ -1877,6 +1896,58 @@
return tmdsclock;
}
+static void dw_hdmi_qp_hdcp_enable(struct dw_hdmi_qp *hdmi,
+ struct drm_connector *connector)
+{
+ int ret, val;
+ const struct drm_connector_state *conn_state = connector->state;
+ void *data = hdmi->plat_data->phy_data;
+
+ if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_DESIRED)
+ return;
+
+ /* sink support hdcp2.x */
+ if (hdmi->hdcp_caps & SINK_CAP_HDCP2) {
+ hdmi_writel(hdmi, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ, AVP_3_INT_CLEAR);
+ hdmi_modb(hdmi, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ,
+ HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ, AVP_3_INT_MASK_N);
+
+ hdmi_writel(hdmi, 0x35, HDCP2LOGIC_ESM_GPIO_IN);
+ hdmi_modb(hdmi, 0, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
+ if (hdmi->plat_data->set_hdcp2_enable)
+ hdmi->plat_data->set_hdcp2_enable(data, true);
+
+ /* wait hdcp2.X auth success */
+ ret = regmap_read_poll_timeout(hdmi->regm, HDCP2LOGIC_ESM_GPIO_OUT, val,
+ FIELD_GET(HDCP2_AUTHENTICATION_SUCCESS, val),
+ 10000, 2000000);
+ if (ret) {
+ hdmi->hdcp_status &= ~HDMI_HDCP2_AUTH;
+ dev_info(hdmi->dev, "hdcp2 auth failed,start hdcp1.4\n");
+
+ hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN);
+ hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
+
+ if (hdmi->plat_data->set_hdcp2_enable)
+ hdmi->plat_data->set_hdcp2_enable(data, false);
+
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
+ hdmi->hdcp->hdcp_start(hdmi->hdcp);
+ goto exit;
+ }
+
+ hdmi->hdcp_status |= HDMI_HDCP2_AUTH;
+ drm_hdcp_update_content_protection(connector, DRM_MODE_CONTENT_PROTECTION_ENABLED);
+ dev_info(hdmi->dev, "HDCP2 authentication succeed\n");
+ } else {
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
+ hdmi->hdcp->hdcp_start(hdmi->hdcp);
+ }
+exit:
+ if (hdmi->plat_data->set_hdcp_status)
+ hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
+}
+
static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi,
const struct drm_connector *connector,
struct drm_display_mode *mode)
@@ -2038,6 +2109,7 @@
dev_info(hdmi->dev, "%s DVI mode\n", __func__);
}
+ dw_hdmi_qp_hdcp_enable(hdmi, hdmi->curr_conn);
hdmi->frl_switch = false;
return 0;
}
@@ -2056,6 +2128,9 @@
if (hdmi->panel)
return connector_status_connected;
+
+ if (hdmi->next_bridge && hdmi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+ return drm_bridge_detect(hdmi->next_bridge);
if (hdmi->plat_data->left)
secondary = hdmi->plat_data->left;
@@ -2124,6 +2199,58 @@
return false;
}
+static ssize_t hdcp_ddc_read(struct i2c_adapter *adapter, u8 address,
+ u8 offset, void *buffer)
+{
+ int ret;
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = address,
+ .flags = 0,
+ .len = 1,
+ .buf = &offset,
+ }, {
+ .addr = address,
+ .flags = I2C_M_RD,
+ .len = 1,
+ .buf = buffer,
+ }
+ };
+
+ ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(msgs))
+ return -EPROTO;
+
+ return 0;
+}
+
+static u8 dw_hdmi_qp_hdcp_capable(struct dw_hdmi_qp *hdmi)
+{
+ u8 version = 0;
+ u8 bcaps;
+ int ret;
+
+ ret = hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_BCAPS, &bcaps);
+ if (ret < 0) {
+ dev_err(hdmi->dev, "get hdcp1.4 capable failed:%d\n", ret);
+ return 0;
+ }
+ if (bcaps & HDMI_HDCP14_SUPPORT)
+ version |= SINK_CAP_HDCP14;
+
+ ret = hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_HDCP2_VERSION, &bcaps);
+ if (ret < 0) {
+ dev_err(hdmi->dev, "get hdcp2.x capable failed:%d\n", ret);
+ return 0;
+ }
+ if (bcaps & HDMI_HDCP2_SUPPORT)
+ version |= SINK_CAP_HDCP2;
+
+ return version;
+}
+
static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
{
struct dw_hdmi_qp *hdmi =
@@ -2134,16 +2261,39 @@
struct drm_display_mode *mode;
struct drm_display_info *info = &connector->display_info;
void *data = hdmi->plat_data->phy_data;
+ struct drm_property_blob *edid_blob_ptr = connector->edid_blob_ptr;
int i, ret = 0;
+
+ if (hdmi->plat_data->right && hdmi->plat_data->right->next_bridge) {
+ struct drm_bridge *bridge = hdmi->plat_data->right->next_bridge;
+
+ if (bridge->ops & DRM_BRIDGE_OP_MODES) {
+ if (!drm_bridge_get_modes(bridge, connector))
+ return 0;
+ }
+ }
if (hdmi->panel)
return drm_panel_get_modes(hdmi->panel, connector);
+
+ if (hdmi->next_bridge && hdmi->next_bridge->ops & DRM_BRIDGE_OP_MODES)
+ return drm_bridge_get_modes(hdmi->next_bridge, connector);
if (!hdmi->ddc)
return 0;
memset(metedata, 0, sizeof(*metedata));
- edid = drm_get_edid(connector, hdmi->ddc);
+
+ if (edid_blob_ptr && edid_blob_ptr->length) {
+ edid = kmalloc(edid_blob_ptr->length, GFP_KERNEL);
+ if (!edid)
+ return -ENOMEM;
+ memcpy(edid, edid_blob_ptr->data, edid_blob_ptr->length);
+ } else {
+ edid = drm_get_edid(connector, hdmi->ddc);
+ hdmi->hdcp_caps = dw_hdmi_qp_hdcp_capable(hdmi);
+ }
+
if (edid) {
dev_dbg(hdmi->dev, "got edid: width[%d] x height[%d]\n",
edid->width_cm, edid->height_cm);
@@ -2161,6 +2311,7 @@
if (hdmi->plat_data->get_yuv422_format)
hdmi->plat_data->get_yuv422_format(connector, edid);
dw_hdmi_update_hdr_property(connector);
+ hdmi->hdcp_caps = dw_hdmi_qp_hdcp_capable(hdmi);
if (ret > 0 && hdmi->plat_data->split_mode) {
struct dw_hdmi_qp *secondary = NULL;
void *secondary_data;
@@ -2170,8 +2321,10 @@
else if (hdmi->plat_data->right)
secondary = hdmi->plat_data->right;
- if (!secondary)
+ if (!secondary) {
+ kfree(edid);
return -ENOMEM;
+ }
secondary_data = secondary->plat_data->phy_data;
list_for_each_entry(mode, &connector->probed_modes, head)
@@ -2224,7 +2377,6 @@
dev_info(hdmi->dev, "failed to get edid\n");
}
- dw_hdmi_qp_check_output_type_changed(hdmi);
return ret;
}
@@ -2444,6 +2596,37 @@
return false;
}
+static bool check_dw_hdcp_state_changed(struct drm_connector *conn,
+ struct drm_atomic_state *state)
+{
+ struct drm_connector_state *old_state, *new_state;
+ u64 old_cp, new_cp;
+
+ 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;
+ return true;
+ }
+
+ if (!new_state->crtc) {
+ if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
+ new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ return false;
+ }
+
+ if (old_cp == new_cp ||
+ (old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
+ new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED))
+ return false;
+
+ return true;
+}
+
static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *state)
{
@@ -2546,7 +2729,8 @@
}
if (check_hdr_color_change(old_state, new_state, hdmi) || hdmi->logo_plug_out ||
- dw_hdmi_color_changed(connector, state)) {
+ dw_hdmi_color_changed(connector, state) ||
+ dw_hdmi_qp_check_output_type_changed(hdmi)) {
u32 mtmdsclk;
crtc_state = drm_atomic_get_crtc_state(state, crtc);
@@ -2580,13 +2764,16 @@
hdmi_modb(hdmi, PKTSCHED_GCP_TX_EN, PKTSCHED_GCP_TX_EN, PKTSCHED_PKT_EN);
mdelay(50);
} else if (!hdmi->disabled) {
- if (mode.clock > 600000)
+ if (hdmi->previous_mode.clock > 600000 && mode.clock > 600000)
hdmi->frl_switch = true;
hdmi->update = false;
crtc_state->mode_changed = true;
hdmi->logo_plug_out = false;
}
}
+
+ if (check_dw_hdcp_state_changed(connector, state))
+ crtc_state->mode_changed = true;
return 0;
}
@@ -2608,17 +2795,6 @@
void dw_hdmi_qp_set_output_type(struct dw_hdmi_qp *hdmi, u64 val)
{
hdmi->force_output = val;
-
- if (!dw_hdmi_qp_check_output_type_changed(hdmi))
- return;
-
- if (hdmi->disabled)
- return;
-
- if (!hdmi->sink_is_hdmi)
- hdmi_modb(hdmi, OPMODE_DVI, OPMODE_DVI, LINK_CONFIG0);
- else
- hdmi_modb(hdmi, 0, OPMODE_DVI, LINK_CONFIG0);
}
EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_output_type);
@@ -2682,13 +2858,31 @@
struct drm_connector *connector = &hdmi->connector;
struct cec_connector_info conn_info;
struct cec_notifier *notifier;
+ bool skip_connector = false;
- if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
+ if (hdmi->next_bridge) {
+ struct drm_bridge *next_bridge = hdmi->next_bridge;
+ int ret;
+
+ ret = drm_bridge_attach(bridge->encoder, next_bridge, bridge,
+ next_bridge->ops & DRM_BRIDGE_OP_MODES ?
+ DRM_BRIDGE_ATTACH_NO_CONNECTOR : 0);
+ if (ret) {
+ DRM_ERROR("failed to attach next bridge: %d\n", ret);
+ return ret;
+ }
+
+ skip_connector = !(next_bridge->ops & DRM_BRIDGE_OP_MODES);
+ }
+
+ hdmi->skip_connector = skip_connector;
+ if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR || skip_connector)
return 0;
connector->interlace_allowed = 1;
connector->polled = DRM_CONNECTOR_POLL_HPD;
-
+ if (hdmi->next_bridge && hdmi->next_bridge->ops & DRM_BRIDGE_OP_DETECT)
+ connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs);
drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs,
@@ -2728,8 +2922,18 @@
const struct drm_display_info *info,
const struct drm_display_mode *mode)
{
+ struct dw_hdmi_qp *hdmi = bridge->driver_private;
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+
if (mode->clock <= 25000)
return MODE_CLOCK_RANGE;
+
+ if (!hdmi->sink_is_hdmi && mode->clock > 340000)
+ return MODE_BAD;
+
+ if (pdata->mode_valid)
+ return pdata->mode_valid(NULL, pdata->priv_data, info,
+ mode);
return MODE_OK;
}
@@ -2757,6 +2961,7 @@
{
struct dw_hdmi_qp *hdmi = bridge->driver_private;
void *data = hdmi->plat_data->phy_data;
+ const struct drm_connector_state *conn_state = hdmi->curr_conn->state;
if (hdmi->panel)
drm_panel_disable(hdmi->panel);
@@ -2764,6 +2969,19 @@
/* set avmute */
hdmi_writel(hdmi, 1, PKTSCHED_PKT_CONTROL0);
mdelay(50);
+
+ hdmi_modb(hdmi, 0, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ,
+ AVP_3_INT_MASK_N);
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
+ hdmi->hdcp->hdcp_stop(hdmi->hdcp);
+
+ hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN);
+ if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ drm_hdcp_update_content_protection(hdmi->curr_conn,
+ DRM_MODE_CONTENT_PROTECTION_DESIRED);
+
+ if (hdmi->plat_data->set_hdcp_status)
+ hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
handle_plugged_change(hdmi, false);
@@ -2781,7 +2999,7 @@
hdmi_writel(hdmi, 0, FLT_CONFIG0);
hdmi_writel(hdmi, 0, SCRAMB_CONFIG0);
/* set sink frl mode disable */
- if (hdmi->curr_conn && dw_hdmi_support_scdc(hdmi, &hdmi->curr_conn->display_info))
+ if (dw_hdmi_support_scdc(hdmi, &hdmi->curr_conn->display_info))
drm_scdc_writeb(hdmi->ddc, 0x31, 0);
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
@@ -2913,17 +3131,77 @@
static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id)
{
struct dw_hdmi_qp *hdmi = dev_id;
- u32 stat;
+ u32 stat1, stat3;
- stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
- if (stat) {
- dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat);
- stat &= ~stat;
- hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N);
- return IRQ_WAKE_THREAD;
+ stat1 = hdmi_readl(hdmi, AVP_1_INT_STATUS);
+ stat3 = hdmi_readl(hdmi, AVP_3_INT_STATUS);
+
+ if (!stat1 && !stat3)
+ return IRQ_NONE;
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
+{
+ struct dw_hdmi_qp *hdmi = dev_id;
+ struct drm_connector_state *conn_state;
+ void *data = hdmi->plat_data->phy_data;
+ u32 stat1, stat3, val;
+
+ stat1 = hdmi_readl(hdmi, AVP_1_INT_STATUS);
+ stat3 = hdmi_readl(hdmi, AVP_3_INT_STATUS);
+
+ hdmi_writel(hdmi, stat1, AVP_1_INT_CLEAR);
+ hdmi_writel(hdmi, stat3, AVP_3_INT_CLEAR);
+
+ if (!hdmi->curr_conn || !hdmi->curr_conn->state)
+ return IRQ_HANDLED;
+
+ conn_state = hdmi->curr_conn->state;
+ val = conn_state->content_protection;
+
+ if (hdmi->hdcp && hdmi->hdcp->hdcp_isr) {
+ u32 hdcp_status = hdmi_readl(hdmi, HDCP14_STATUS0);
+
+ if (stat1 & HDCP14_AUTH_CHG_MASK_N) {
+ /* hdcp14 auth success */
+ if (hdcp_status & BIT(2)) {
+ hdmi->hdcp_status |= HDMI_HDCP14_AUTH;
+ if (conn_state->content_protection !=
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ val = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ } else if (!(hdcp_status & BIT(2))) {
+ hdmi->hdcp_status &= ~HDMI_HDCP14_AUTH;
+ if (conn_state->content_protection !=
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ val = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ }
+ conn_state->content_protection = val;
+ }
+ hdmi->hdcp->hdcp_isr(hdmi->hdcp, stat1, hdcp_status);
}
- return IRQ_NONE;
+ if (stat3 & HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ) {
+ stat3 = hdmi_readl(hdmi, HDCP2LOGIC_ESM_GPIO_OUT);
+ if (stat3 & HDCP2_AUTHENTICATION_SUCCESS) {
+ hdmi->hdcp_status |= HDMI_HDCP2_AUTH;
+ if (conn_state->content_protection !=
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ val = DRM_MODE_CONTENT_PROTECTION_ENABLED;
+ } else if (!(stat3 & HDCP2_AUTHENTICATION_SUCCESS)) {
+ hdmi->hdcp_status &= ~HDMI_HDCP2_AUTH;
+ if (conn_state->content_protection !=
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ val = DRM_MODE_CONTENT_PROTECTION_DESIRED;
+ }
+ conn_state->content_protection = val;
+ }
+
+ if (hdmi->plat_data->set_hdcp_status)
+ hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
+
+ return IRQ_HANDLED;
}
static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id)
@@ -2940,21 +3218,6 @@
}
return IRQ_NONE;
-}
-
-static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
-{
- struct dw_hdmi_qp *hdmi = dev_id;
- u32 stat;
-
- stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
-
- if (!stat)
- return IRQ_NONE;
-
- hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR);
-
- return IRQ_HANDLED;
}
static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id)
@@ -3124,6 +3387,11 @@
struct dw_hdmi_qp *hdmi = s->private;
u32 i = 0, j = 0, val = 0;
+ if (hdmi->disabled) {
+ dev_err(hdmi->dev, "hdmi is disabled\n");
+ return -EACCES;
+ }
+
seq_puts(s, "\n---------------------------------------------------");
for (i = 0; i < ARRAY_SIZE(hdmi_reg_table); i++) {
@@ -3154,6 +3422,11 @@
((struct seq_file *)file->private_data)->private;
u32 reg, val;
char kbuf[25];
+
+ if (hdmi->disabled) {
+ dev_err(hdmi->dev, "hdmi is disabled\n");
+ return -EACCES;
+ }
if (count > 24) {
dev_err(hdmi->dev, "out of buf range\n");
@@ -3339,6 +3612,70 @@
hdmi, &dw_hdmi_ctrl_fops);
}
+static void dw_hdmi_qp_hdcp14_get_mem(struct dw_hdmi_qp *hdmi, u8 *data, u32 len)
+{
+ u32 ksv_len, i, val;
+ void *hdmi_data = hdmi->plat_data->phy_data;
+
+ if (hdmi->plat_data->set_hdcp14_mem)
+ hdmi->plat_data->set_hdcp14_mem(hdmi_data, true);
+
+ ksv_len = len - BSTATUS_LEN - M0_LEN - SHAMAX;
+ for (i = 0; i < len; i++) {
+ /* read ksv list */
+ if (i < ksv_len)
+ val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_KSV0 + i * 4);
+ /* read bstatus */
+ else if (i < len - SHAMAX - M0_LEN)
+ val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_BSTATUS0 +
+ (i - ksv_len) * 4);
+ /* read M0 */
+ else if (i < len - SHAMAX)
+ val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_M0_1 +
+ (i - ksv_len - BSTATUS_LEN) * 4);
+ else
+ /* VH0 save in external memory is error, we need to read VH0 via ddc */
+ hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_VH0 + i - (len - SHAMAX),
+ &val);
+
+ data[i] = val;
+ }
+
+ if (hdmi->plat_data->set_hdcp14_mem)
+ hdmi->plat_data->set_hdcp14_mem(hdmi_data, false);
+}
+
+static int dw_hdmi_qp_register_hdcp(struct device *dev,
+ struct dw_hdmi_qp *hdmi)
+{
+ struct dw_qp_hdcp hdmi_hdcp = {
+ .hdmi = hdmi,
+ .write = hdmi_writel,
+ .read = hdmi_readl,
+ .regs = hdmi->regs,
+ .get_mem = dw_hdmi_qp_hdcp14_get_mem,
+ };
+ struct platform_device_info hdcp_device_info = {
+ .parent = dev,
+ .id = PLATFORM_DEVID_AUTO,
+ .res = NULL,
+ .num_res = 0,
+ .name = DW_HDCP_QP_DRIVER_NAME,
+ .data = &hdmi_hdcp,
+ .size_data = sizeof(hdmi_hdcp),
+ .dma_mask = DMA_BIT_MASK(32),
+ };
+ hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
+ if (IS_ERR(hdmi->hdcp_dev)) {
+ dev_err(dev, "failed to register hdcp!\n");
+ return -ENOMEM;
+ }
+
+ hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
+
+ return 0;
+}
+
static struct dw_hdmi_qp *
__dw_hdmi_probe(struct platform_device *pdev,
const struct dw_hdmi_plat_data *plat_data)
@@ -3352,10 +3689,11 @@
struct dw_hdmi_qp_cec_data cec;
struct resource *iores = NULL;
struct drm_panel *panel = NULL;
+ struct drm_bridge *bridge = NULL;
int irq;
int ret;
- ret = drm_of_find_panel_or_bridge(np, 1, -1, &panel, NULL);
+ ret = drm_of_find_panel_or_bridge(np, 1, -1, &panel, &bridge);
if (ret < 0 && ret != -ENODEV)
return ERR_PTR(ret);
@@ -3364,6 +3702,7 @@
return ERR_PTR(-ENOMEM);
hdmi->panel = panel;
+ hdmi->next_bridge = bridge;
hdmi->connector.stereo_allowed = 1;
hdmi->plat_data = plat_data;
hdmi->dev = dev;
@@ -3512,7 +3851,7 @@
hdmi->avp_irq = irq;
ret = devm_request_threaded_irq(dev, hdmi->avp_irq,
dw_hdmi_qp_avp_hardirq,
- dw_hdmi_qp_avp_irq, IRQF_SHARED,
+ dw_hdmi_qp_avp_irq, IRQF_ONESHOT,
dev_name(dev), hdmi);
if (ret)
goto err_aud;
@@ -3564,6 +3903,20 @@
goto err_cec;
dw_hdmi_register_debugfs(dev, hdmi);
+
+ if (hdmi_readl(hdmi, CONFIG_REG) & CONFIG_HDCP14) {
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ hdmi->hdcp14_mem = devm_ioremap_resource(dev, iores);
+
+ if (IS_ERR(hdmi->hdcp14_mem)) {
+ ret = PTR_ERR(hdmi->hdcp14_mem);
+ goto err_cec;
+ }
+
+ ret = dw_hdmi_qp_register_hdcp(dev, hdmi);
+ if (ret)
+ goto err_cec;
+ }
return hdmi;
@@ -3617,6 +3970,8 @@
hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
if (!IS_ERR(hdmi->cec))
platform_device_unregister(hdmi->cec);
+ if (!IS_ERR(hdmi->hdcp_dev))
+ platform_device_unregister(hdmi->hdcp_dev);
if (hdmi->i2c)
i2c_del_adapter(&hdmi->i2c->adap);
else
@@ -3646,6 +4001,10 @@
}
plat_data->connector = &hdmi->connector;
+ if (hdmi->skip_connector && hdmi->next_bridge)
+ plat_data->bridge = hdmi->next_bridge;
+ else
+ plat_data->bridge = NULL;
}
if (plat_data->split_mode && !hdmi->plat_data->first_screen) {
@@ -3703,6 +4062,7 @@
disable_irq(hdmi->earc_irq);
pinctrl_pm_select_sleep_state(dev);
+ drm_connector_update_edid_property(&hdmi->connector, NULL);
}
EXPORT_SYMBOL_GPL(dw_hdmi_qp_suspend);
--
Gitblit v1.6.2