diff options
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c | 129 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h | 36 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_audio.c | 34 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_util.c | 113 | ||||
| -rw-r--r-- | drivers/gpu/drm/msm/sde_hdcp.h | 76 |
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__ */ |
