summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorAbhinav Kumar <abhinavk@codeaurora.org>2017-05-09 23:51:48 -0700
committerAbhinav Kumar <abhinavk@codeaurora.org>2017-06-15 00:18:29 -0700
commite4db249be7e29a69e6f2edb97af8665956f26fb5 (patch)
tree42d87d195856cab82b5ce8a484f5f8c80b8b504e /drivers/gpu
parent110b6748f93b0f567a84d71c2971cbb1b34c8a4a (diff)
drm/msm: add HDCP helper functions to DRM HDMI driver
Add useful helper functions to the DRM HDMI driver to be used later when HDCP functionality gets added. Also add and initialize necessary members to the SDE HDMI controller to facilitate easier integration of DRM HDCP module. Change-Id: I699f6685327e674a871a404fe5cf3adebe823d46 Signed-off-by: Abhinav Kumar <abhinavk@codeaurora.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c129
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h36
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c34
-rw-r--r--drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c113
-rw-r--r--drivers/gpu/drm/msm/sde_hdcp.h76
5 files changed, 353 insertions, 35 deletions
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
index 5a7a5fa6b16c..f43011d75cd7 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c
@@ -424,6 +424,102 @@ static u64 _sde_hdmi_clip_valid_pclk(struct drm_display_mode *mode, u64 pclk_in)
return pclk_clip;
}
+static void sde_hdmi_tx_hdcp_cb(void *ptr, enum sde_hdcp_states status)
+{
+ struct sde_hdmi *hdmi_ctrl = (struct sde_hdmi *)ptr;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ DEV_ERR("%s: invalid input\n", __func__);
+ return;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+ hdmi_ctrl->hdcp_status = status;
+ queue_delayed_work(hdmi->workq, &hdmi_ctrl->hdcp_cb_work, HZ/4);
+}
+
+static void sde_hdmi_tx_hdcp_cb_work(struct work_struct *work)
+{
+ struct sde_hdmi *hdmi_ctrl = NULL;
+ struct delayed_work *dw = to_delayed_work(work);
+ int rc = 0;
+ struct hdmi *hdmi;
+
+ hdmi_ctrl = container_of(dw, struct sde_hdmi, hdcp_cb_work);
+ if (!hdmi_ctrl) {
+ DEV_DBG("%s: invalid input\n", __func__);
+ return;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ switch (hdmi_ctrl->hdcp_status) {
+ case HDCP_STATE_AUTHENTICATED:
+ hdmi_ctrl->auth_state = true;
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) &&
+ sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) {
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ }
+
+ if (hdmi_ctrl->hdcp1_use_sw_keys &&
+ hdmi_ctrl->hdcp14_present) {
+ if (!hdmi_ctrl->hdcp22_present)
+ hdcp1_set_enc(true);
+ }
+ break;
+ case HDCP_STATE_AUTH_FAIL:
+ if (hdmi_ctrl->hdcp1_use_sw_keys && hdmi_ctrl->hdcp14_present) {
+ if (hdmi_ctrl->auth_state && !hdmi_ctrl->hdcp22_present)
+ hdcp1_set_enc(false);
+ }
+
+ hdmi_ctrl->auth_state = false;
+
+ if (sde_hdmi_tx_is_encryption_set(hdmi_ctrl) ||
+ !sde_hdmi_tx_is_stream_shareable(hdmi_ctrl))
+ rc = sde_hdmi_config_avmute(hdmi, true);
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl)) {
+ pr_debug("%s: Reauthenticating\n", __func__);
+ if (hdmi_ctrl->hdcp_ops && hdmi_ctrl->hdcp_data) {
+ rc = hdmi_ctrl->hdcp_ops->reauthenticate(
+ hdmi_ctrl->hdcp_data);
+ if (rc)
+ pr_err("%s: HDCP reauth failed. rc=%d\n",
+ __func__, rc);
+ } else
+ pr_err("%s: NULL HDCP Ops and Data\n",
+ __func__);
+ } else {
+ pr_debug("%s: Not reauthenticating. Cable not conn\n",
+ __func__);
+ }
+
+ break;
+ case HDCP_STATE_AUTH_ENC_NONE:
+ hdmi_ctrl->enc_lvl = HDCP_STATE_AUTH_ENC_NONE;
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl))
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ break;
+ case HDCP_STATE_AUTH_ENC_1X:
+ case HDCP_STATE_AUTH_ENC_2P2:
+ hdmi_ctrl->enc_lvl = hdmi_ctrl->hdcp_status;
+
+ if (sde_hdmi_tx_is_panel_on(hdmi_ctrl) &&
+ sde_hdmi_tx_is_stream_shareable(hdmi_ctrl)) {
+ rc = sde_hdmi_config_avmute(hdmi, false);
+ } else {
+ rc = sde_hdmi_config_avmute(hdmi, true);
+ }
+ break;
+ default:
+ break;
+ /* do nothing */
+ }
+}
+
/**
* _sde_hdmi_update_pll_delta() - Update the HDMI pixel clock as per input ppm
*
@@ -1561,6 +1657,34 @@ static int sde_hdmi_tx_check_capability(struct sde_hdmi *sde_hdmi)
return ret;
} /* hdmi_tx_check_capability */
+static int _sde_hdmi_init_hdcp(struct sde_hdmi *hdmi_ctrl)
+{
+ struct sde_hdcp_init_data hdcp_init_data;
+ int rc = 0;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("sde_hdmi is NULL\n");
+ return -EINVAL;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+ hdcp_init_data.phy_addr = hdmi->mmio_phy_addr;
+ hdcp_init_data.core_io = &hdmi_ctrl->io[HDMI_TX_CORE_IO];
+ hdcp_init_data.qfprom_io = &hdmi_ctrl->io[HDMI_TX_QFPROM_IO];
+ hdcp_init_data.hdcp_io = &hdmi_ctrl->io[HDMI_TX_HDCP_IO];
+ hdcp_init_data.mutex = &hdmi_ctrl->hdcp_mutex;
+ hdcp_init_data.workq = hdmi->workq;
+ hdcp_init_data.notify_status = sde_hdmi_tx_hdcp_cb;
+ hdcp_init_data.cb_data = (void *)hdmi_ctrl;
+ hdcp_init_data.hdmi_tx_ver = hdmi_ctrl->hdmi_tx_major_version;
+ hdcp_init_data.sec_access = true;
+ hdcp_init_data.client_id = HDCP_CLIENT_HDMI;
+ hdcp_init_data.ddc_ctrl = &hdmi_ctrl->ddc_ctrl;
+
+ return rc;
+}
+
int sde_hdmi_connector_post_init(struct drm_connector *connector,
void *info,
void *display)
@@ -1796,6 +1920,11 @@ static int sde_hdmi_bind(struct device *dev, struct device *master, void *data)
_sde_hdmi_map_regs(display, priv->hdmi);
_sde_hdmi_init_ddc(display, priv->hdmi);
+ INIT_DELAYED_WORK(&display->hdcp_cb_work,
+ sde_hdmi_tx_hdcp_cb_work);
+ mutex_init(&display->hdcp_mutex);
+ _sde_hdmi_init_hdcp(display);
+
mutex_unlock(&display->display_lock);
return rc;
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
index 671c440d51d3..a4f7c4063787 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h
@@ -20,6 +20,7 @@
#include <linux/debugfs.h>
#include <linux/of_device.h>
#include <linux/msm_ext_display.h>
+#include <linux/hdcp_qseecom.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
@@ -30,6 +31,7 @@
#include "msm_drv.h"
#include "sde_edid_parser.h"
#include "sde_hdmi_util.h"
+#include "sde_hdcp.h"
#ifdef HDMI_DEBUG_ENABLE
#define SDE_HDMI_DEBUG(fmt, args...) SDE_ERROR(fmt, ##args)
@@ -113,7 +115,7 @@ struct sde_hdmi {
const char *display_type;
struct list_head list;
struct mutex display_lock;
-
+ struct mutex hdcp_mutex;
struct sde_hdmi_ctrl ctrl;
struct platform_device *ext_pdev;
@@ -131,6 +133,12 @@ struct sde_hdmi {
u32 max_pclk_khz;
bool hdcp1_use_sw_keys;
u32 hdcp14_present;
+ u32 hdcp22_present;
+ u8 hdcp_status;
+ u32 enc_lvl;
+ bool auth_state;
+ void *hdcp_data;
+ struct sde_hdcp_ops *hdcp_ops;
struct sde_hdmi_tx_ddc_ctrl ddc_ctrl;
struct work_struct hpd_work;
bool codec_ready;
@@ -139,6 +147,7 @@ struct sde_hdmi {
struct irq_domain *irq_domain;
struct cec_notifier *notifier;
+ struct delayed_work hdcp_cb_work;
struct dss_io_data io[HDMI_TX_MAX_IO];
/* DEBUG FS */
struct dentry *root;
@@ -405,6 +414,11 @@ void sde_hdmi_notify_clients(struct sde_hdmi *display, bool connected);
void sde_hdmi_ack_state(struct drm_connector *connector,
enum drm_connector_status status);
+bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl);
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl);
+
#else /*#ifdef CONFIG_DRM_SDE_HDMI*/
static inline u32 sde_hdmi_get_num_of_displays(void)
@@ -463,6 +477,26 @@ static inline int sde_hdmi_dev_deinit(struct sde_hdmi *display)
return 0;
}
+bool hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl)
+{
+ return false;
+}
+
static inline int sde_hdmi_drm_init(struct sde_hdmi *display,
struct drm_encoder *enc)
{
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
index 48a3a9316a41..d6213dc0a4aa 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c
@@ -355,37 +355,3 @@ void sde_hdmi_audio_off(struct hdmi *hdmi)
SDE_DEBUG("HDMI Audio: Disabled\n");
}
-int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
-{
- u32 av_mute_status;
- bool av_pkt_en = false;
-
- if (!hdmi) {
- SDE_ERROR("invalid HDMI Ctrl\n");
- return -ENODEV;
- }
-
- av_mute_status = hdmi_read(hdmi, HDMI_GC);
-
- if (set) {
- if (!(av_mute_status & BIT(0))) {
- hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0));
- av_pkt_en = true;
- }
- } else {
- if (av_mute_status & BIT(0)) {
- hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0));
- av_pkt_en = true;
- }
- }
-
- /* Enable AV Mute tranmission here */
- if (av_pkt_en)
- hdmi_write(hdmi, HDMI_VBI_PKT_CTRL,
- hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
-
- SDE_DEBUG("AVMUTE %s\n", set ? "set" : "cleared");
-
- return 0;
-}
-
diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
index 09d376dbc602..0ae275cbb3a5 100644
--- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
+++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c
@@ -198,3 +198,116 @@ int sde_hdmi_ddc_write(void *cb_data)
error:
return status;
} /* hdmi_ddc_write */
+
+bool sde_hdmi_tx_is_hdcp_enabled(struct sde_hdmi *hdmi_ctrl)
+{
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ return (hdmi_ctrl->hdcp14_present || hdmi_ctrl->hdcp22_present) &&
+ hdmi_ctrl->hdcp_ops;
+}
+
+bool sde_hdmi_tx_is_encryption_set(struct sde_hdmi *hdmi_ctrl)
+{
+ bool enc_en = true;
+ u32 reg_val;
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ goto end;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ reg_val = hdmi_read(hdmi, HDMI_HDCP_CTRL2);
+ if ((reg_val & BIT(0)) && (reg_val & BIT(1)))
+ goto end;
+
+ if (hdmi_read(hdmi, HDMI_CTRL) & BIT(2))
+ goto end;
+
+ return false;
+
+end:
+ return enc_en;
+} /* sde_hdmi_tx_is_encryption_set */
+
+bool sde_hdmi_tx_is_stream_shareable(struct sde_hdmi *hdmi_ctrl)
+{
+ bool ret;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ switch (hdmi_ctrl->enc_lvl) {
+ case HDCP_STATE_AUTH_ENC_NONE:
+ ret = true;
+ break;
+ case HDCP_STATE_AUTH_ENC_1X:
+ ret = sde_hdmi_tx_is_hdcp_enabled(hdmi_ctrl) &&
+ hdmi_ctrl->auth_state;
+ break;
+ case HDCP_STATE_AUTH_ENC_2P2:
+ ret = hdmi_ctrl->hdcp22_present &&
+ hdmi_ctrl->auth_state;
+ break;
+ default:
+ ret = false;
+ }
+
+ return ret;
+}
+
+bool sde_hdmi_tx_is_panel_on(struct sde_hdmi *hdmi_ctrl)
+{
+ struct hdmi *hdmi;
+
+ if (!hdmi_ctrl) {
+ SDE_ERROR("%s: invalid input\n", __func__);
+ return false;
+ }
+
+ hdmi = hdmi_ctrl->ctrl.ctrl;
+
+ return hdmi_ctrl->connected && hdmi->power_on;
+}
+
+int sde_hdmi_config_avmute(struct hdmi *hdmi, bool set)
+{
+ u32 av_mute_status;
+ bool av_pkt_en = false;
+
+ if (!hdmi) {
+ SDE_ERROR("invalid HDMI Ctrl\n");
+ return -ENODEV;
+ }
+
+ av_mute_status = hdmi_read(hdmi, HDMI_GC);
+
+ if (set) {
+ if (!(av_mute_status & BIT(0))) {
+ hdmi_write(hdmi, HDMI_GC, av_mute_status | BIT(0));
+ av_pkt_en = true;
+ }
+ } else {
+ if (av_mute_status & BIT(0)) {
+ hdmi_write(hdmi, HDMI_GC, av_mute_status & ~BIT(0));
+ av_pkt_en = true;
+ }
+ }
+
+ /* Enable AV Mute tranmission here */
+ if (av_pkt_en)
+ hdmi_write(hdmi, HDMI_VBI_PKT_CTRL,
+ hdmi_read(hdmi, HDMI_VBI_PKT_CTRL) | (BIT(4) & BIT(5)));
+
+ SDE_DEBUG("AVMUTE %s\n", set ? "set" : "cleared");
+
+ return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde_hdcp.h b/drivers/gpu/drm/msm/sde_hdcp.h
new file mode 100644
index 000000000000..2a6f2c621cac
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde_hdcp.h
@@ -0,0 +1,76 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef __SDE_HDCP_H__
+#define __SDE_HDCP_H__
+
+#include <soc/qcom/scm.h>
+
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/of_device.h>
+#include <linux/i2c.h>
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include "hdmi.h"
+
+enum sde_hdcp_client_id {
+ HDCP_CLIENT_HDMI,
+ HDCP_CLIENT_DP,
+};
+
+enum sde_hdcp_states {
+ HDCP_STATE_INACTIVE,
+ HDCP_STATE_AUTHENTICATING,
+ HDCP_STATE_AUTHENTICATED,
+ HDCP_STATE_AUTH_FAIL,
+ HDCP_STATE_AUTH_ENC_NONE,
+ HDCP_STATE_AUTH_ENC_1X,
+ HDCP_STATE_AUTH_ENC_2P2
+};
+
+struct sde_hdcp_init_data {
+ struct dss_io_data *core_io;
+ struct dss_io_data *qfprom_io;
+ struct dss_io_data *hdcp_io;
+ struct mutex *mutex;
+ struct workqueue_struct *workq;
+ void *cb_data;
+ void (*notify_status)(void *cb_data, enum sde_hdcp_states status);
+ struct sde_hdmi_tx_ddc_ctrl *ddc_ctrl;
+ u8 sink_rx_status;
+ u16 *version;
+ u32 phy_addr;
+ u32 hdmi_tx_ver;
+ bool sec_access;
+ enum sde_hdcp_client_id client_id;
+};
+
+struct sde_hdcp_ops {
+ int (*isr)(void *ptr);
+ int (*cp_irq)(void *ptr);
+ int (*reauthenticate)(void *input);
+ int (*authenticate)(void *hdcp_ctrl);
+ bool (*feature_supported)(void *input);
+ void (*off)(void *hdcp_ctrl);
+};
+
+void *sde_hdcp_1x_init(struct sde_hdcp_init_data *init_data);
+void sde_hdcp_1x_deinit(void *input);
+
+struct hdcp_ops *sde_hdcp_1x_start(void *input);
+
+const char *sde_hdcp_state_name(enum sde_hdcp_states hdcp_state);
+
+#endif /* __SDE_HDCP_H__ */