Skip to content

Commit 2320175

Browse files
committed
drm/i915: Implement HDCP for HDMI
This patch adds HDCP support for HDMI connectors by implementing the intel_hdcp_shim. Nothing too special, just a bunch of DDC reads/writes. Changes in v2: - Rebased on drm-intel-next Changes in v3: - Initialize new worker Changes in v4: - Remove SKL_ prefix from most register names (Daniel) - Wrap sanity checks in WARN_ON (Daniel) - Consolidate the enable/disable functions into one toggle fn - Use intel_hdcp_init (Daniel) Changes in v5: - checkpatch whitespace nits Changes in v6: - None Cc: Daniel Vetter <[email protected]> Reviewed-by: Ramalingam C <[email protected]> Signed-off-by: Sean Paul <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 07e17a7 commit 2320175

File tree

4 files changed

+282
-0
lines changed

4 files changed

+282
-0
lines changed

drivers/gpu/drm/i915/i915_reg.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8460,6 +8460,7 @@ enum skl_power_gate {
84608460
#define TRANS_DDI_EDP_INPUT_A_ONOFF (4<<12)
84618461
#define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12)
84628462
#define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12)
8463+
#define TRANS_DDI_HDCP_SIGNALLING (1<<9)
84638464
#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1<<8)
84648465
#define TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7)
84658466
#define TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)

drivers/gpu/drm/i915/intel_ddi.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,35 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
16151615
I915_WRITE(reg, val);
16161616
}
16171617

1618+
int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
1619+
bool enable)
1620+
{
1621+
struct drm_device *dev = intel_encoder->base.dev;
1622+
struct drm_i915_private *dev_priv = to_i915(dev);
1623+
enum pipe pipe = 0;
1624+
int ret = 0;
1625+
uint32_t tmp;
1626+
1627+
if (WARN_ON(!intel_display_power_get_if_enabled(dev_priv,
1628+
intel_encoder->power_domain)))
1629+
return -ENXIO;
1630+
1631+
if (WARN_ON(!intel_encoder->get_hw_state(intel_encoder, &pipe))) {
1632+
ret = -EIO;
1633+
goto out;
1634+
}
1635+
1636+
tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
1637+
if (enable)
1638+
tmp |= TRANS_DDI_HDCP_SIGNALLING;
1639+
else
1640+
tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
1641+
I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
1642+
out:
1643+
intel_display_power_put(dev_priv, intel_encoder->power_domain);
1644+
return ret;
1645+
}
1646+
16181647
bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
16191648
{
16201649
struct drm_device *dev = intel_connector->base.dev;

drivers/gpu/drm/i915/intel_drv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,6 +1377,8 @@ void intel_ddi_compute_min_voltage_level(struct drm_i915_private *dev_priv,
13771377
u32 bxt_signal_levels(struct intel_dp *intel_dp);
13781378
uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
13791379
u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
1380+
int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
1381+
bool enable);
13801382

13811383
unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
13821384
int plane, unsigned int height);

drivers/gpu/drm/i915/intel_hdmi.c

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include <drm/drm_atomic_helper.h>
3535
#include <drm/drm_crtc.h>
3636
#include <drm/drm_edid.h>
37+
#include <drm/drm_hdcp.h>
3738
#include <drm/drm_scdc_helper.h>
3839
#include "intel_drv.h"
3940
#include <drm/i915_drm.h>
@@ -876,6 +877,248 @@ void intel_dp_dual_mode_set_tmds_output(struct intel_hdmi *hdmi, bool enable)
876877
adapter, enable);
877878
}
878879

880+
static int intel_hdmi_hdcp_read(struct intel_digital_port *intel_dig_port,
881+
unsigned int offset, void *buffer, size_t size)
882+
{
883+
struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
884+
struct drm_i915_private *dev_priv =
885+
intel_dig_port->base.base.dev->dev_private;
886+
struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
887+
hdmi->ddc_bus);
888+
int ret;
889+
u8 start = offset & 0xff;
890+
struct i2c_msg msgs[] = {
891+
{
892+
.addr = DRM_HDCP_DDC_ADDR,
893+
.flags = 0,
894+
.len = 1,
895+
.buf = &start,
896+
},
897+
{
898+
.addr = DRM_HDCP_DDC_ADDR,
899+
.flags = I2C_M_RD,
900+
.len = size,
901+
.buf = buffer
902+
}
903+
};
904+
ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
905+
if (ret == ARRAY_SIZE(msgs))
906+
return 0;
907+
return ret >= 0 ? -EIO : ret;
908+
}
909+
910+
static int intel_hdmi_hdcp_write(struct intel_digital_port *intel_dig_port,
911+
unsigned int offset, void *buffer, size_t size)
912+
{
913+
struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
914+
struct drm_i915_private *dev_priv =
915+
intel_dig_port->base.base.dev->dev_private;
916+
struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
917+
hdmi->ddc_bus);
918+
int ret;
919+
u8 *write_buf;
920+
struct i2c_msg msg;
921+
922+
write_buf = kzalloc(size + 1, GFP_KERNEL);
923+
if (!write_buf)
924+
return -ENOMEM;
925+
926+
write_buf[0] = offset & 0xff;
927+
memcpy(&write_buf[1], buffer, size);
928+
929+
msg.addr = DRM_HDCP_DDC_ADDR;
930+
msg.flags = 0,
931+
msg.len = size + 1,
932+
msg.buf = write_buf;
933+
934+
ret = i2c_transfer(adapter, &msg, 1);
935+
if (ret == 1)
936+
return 0;
937+
return ret >= 0 ? -EIO : ret;
938+
}
939+
940+
static
941+
int intel_hdmi_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
942+
u8 *an)
943+
{
944+
struct intel_hdmi *hdmi = &intel_dig_port->hdmi;
945+
struct drm_i915_private *dev_priv =
946+
intel_dig_port->base.base.dev->dev_private;
947+
struct i2c_adapter *adapter = intel_gmbus_get_adapter(dev_priv,
948+
hdmi->ddc_bus);
949+
int ret;
950+
951+
ret = intel_hdmi_hdcp_write(intel_dig_port, DRM_HDCP_DDC_AN, an,
952+
DRM_HDCP_AN_LEN);
953+
if (ret) {
954+
DRM_ERROR("Write An over DDC failed (%d)\n", ret);
955+
return ret;
956+
}
957+
958+
ret = intel_gmbus_output_aksv(adapter);
959+
if (ret < 0) {
960+
DRM_ERROR("Failed to output aksv (%d)\n", ret);
961+
return ret;
962+
}
963+
return 0;
964+
}
965+
966+
static int intel_hdmi_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
967+
u8 *bksv)
968+
{
969+
int ret;
970+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BKSV, bksv,
971+
DRM_HDCP_KSV_LEN);
972+
if (ret)
973+
DRM_ERROR("Read Bksv over DDC failed (%d)\n", ret);
974+
return ret;
975+
}
976+
977+
static
978+
int intel_hdmi_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
979+
u8 *bstatus)
980+
{
981+
int ret;
982+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BSTATUS,
983+
bstatus, DRM_HDCP_BSTATUS_LEN);
984+
if (ret)
985+
DRM_ERROR("Read bstatus over DDC failed (%d)\n", ret);
986+
return ret;
987+
}
988+
989+
static
990+
int intel_hdmi_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
991+
bool *repeater_present)
992+
{
993+
int ret;
994+
u8 val;
995+
996+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
997+
if (ret) {
998+
DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
999+
return ret;
1000+
}
1001+
*repeater_present = val & DRM_HDCP_DDC_BCAPS_REPEATER_PRESENT;
1002+
return 0;
1003+
}
1004+
1005+
static
1006+
int intel_hdmi_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
1007+
u8 *ri_prime)
1008+
{
1009+
int ret;
1010+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_RI_PRIME,
1011+
ri_prime, DRM_HDCP_RI_LEN);
1012+
if (ret)
1013+
DRM_ERROR("Read Ri' over DDC failed (%d)\n", ret);
1014+
return ret;
1015+
}
1016+
1017+
static
1018+
int intel_hdmi_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
1019+
bool *ksv_ready)
1020+
{
1021+
int ret;
1022+
u8 val;
1023+
1024+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_BCAPS, &val, 1);
1025+
if (ret) {
1026+
DRM_ERROR("Read bcaps over DDC failed (%d)\n", ret);
1027+
return ret;
1028+
}
1029+
*ksv_ready = val & DRM_HDCP_DDC_BCAPS_KSV_FIFO_READY;
1030+
return 0;
1031+
}
1032+
1033+
static
1034+
int intel_hdmi_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
1035+
int num_downstream, u8 *ksv_fifo)
1036+
{
1037+
int ret;
1038+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_KSV_FIFO,
1039+
ksv_fifo, num_downstream * DRM_HDCP_KSV_LEN);
1040+
if (ret) {
1041+
DRM_ERROR("Read ksv fifo over DDC failed (%d)\n", ret);
1042+
return ret;
1043+
}
1044+
return 0;
1045+
}
1046+
1047+
static
1048+
int intel_hdmi_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
1049+
int i, u32 *part)
1050+
{
1051+
int ret;
1052+
1053+
if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
1054+
return -EINVAL;
1055+
1056+
ret = intel_hdmi_hdcp_read(intel_dig_port, DRM_HDCP_DDC_V_PRIME(i),
1057+
part, DRM_HDCP_V_PRIME_PART_LEN);
1058+
if (ret)
1059+
DRM_ERROR("Read V'[%d] over DDC failed (%d)\n", i, ret);
1060+
return ret;
1061+
}
1062+
1063+
static
1064+
int intel_hdmi_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
1065+
bool enable)
1066+
{
1067+
int ret;
1068+
1069+
if (!enable)
1070+
usleep_range(6, 60); /* Bspec says >= 6us */
1071+
1072+
ret = intel_ddi_toggle_hdcp_signalling(&intel_dig_port->base, enable);
1073+
if (ret) {
1074+
DRM_ERROR("%s HDCP signalling failed (%d)\n",
1075+
enable ? "Enable" : "Disable", ret);
1076+
return ret;
1077+
}
1078+
return 0;
1079+
}
1080+
1081+
static
1082+
bool intel_hdmi_hdcp_check_link(struct intel_digital_port *intel_dig_port)
1083+
{
1084+
struct drm_i915_private *dev_priv =
1085+
intel_dig_port->base.base.dev->dev_private;
1086+
enum port port = intel_dig_port->base.port;
1087+
int ret;
1088+
union {
1089+
u32 reg;
1090+
u8 shim[DRM_HDCP_RI_LEN];
1091+
} ri;
1092+
1093+
ret = intel_hdmi_hdcp_read_ri_prime(intel_dig_port, ri.shim);
1094+
if (ret)
1095+
return false;
1096+
1097+
I915_WRITE(PORT_HDCP_RPRIME(port), ri.reg);
1098+
1099+
/* Wait for Ri prime match */
1100+
if (wait_for(I915_READ(PORT_HDCP_STATUS(port)) &
1101+
(HDCP_STATUS_RI_MATCH | HDCP_STATUS_ENC), 1)) {
1102+
DRM_ERROR("Ri' mismatch detected, link check failed (%x)\n",
1103+
I915_READ(PORT_HDCP_STATUS(port)));
1104+
return false;
1105+
}
1106+
return true;
1107+
}
1108+
1109+
static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = {
1110+
.write_an_aksv = intel_hdmi_hdcp_write_an_aksv,
1111+
.read_bksv = intel_hdmi_hdcp_read_bksv,
1112+
.read_bstatus = intel_hdmi_hdcp_read_bstatus,
1113+
.repeater_present = intel_hdmi_hdcp_repeater_present,
1114+
.read_ri_prime = intel_hdmi_hdcp_read_ri_prime,
1115+
.read_ksv_ready = intel_hdmi_hdcp_read_ksv_ready,
1116+
.read_ksv_fifo = intel_hdmi_hdcp_read_ksv_fifo,
1117+
.read_v_prime_part = intel_hdmi_hdcp_read_v_prime_part,
1118+
.toggle_signalling = intel_hdmi_hdcp_toggle_signalling,
1119+
.check_link = intel_hdmi_hdcp_check_link,
1120+
};
1121+
8791122
static void intel_hdmi_prepare(struct intel_encoder *encoder,
8801123
const struct intel_crtc_state *crtc_state)
8811124
{
@@ -2053,6 +2296,13 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
20532296

20542297
intel_hdmi_add_properties(intel_hdmi, connector);
20552298

2299+
if (INTEL_GEN(dev_priv) >= 9) {
2300+
int ret = intel_hdcp_init(intel_connector,
2301+
&intel_hdmi_hdcp_shim);
2302+
if (ret)
2303+
DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
2304+
}
2305+
20562306
intel_connector_attach_encoder(intel_connector, intel_encoder);
20572307
intel_hdmi->attached_connector = intel_connector;
20582308

0 commit comments

Comments
 (0)