xref: /linux/drivers/gpu/drm/display/drm_hdcp_helper.c (revision a940daa52167e9db8ecce82213813b735a9d9f23)
16a99099fSThomas Zimmermann // SPDX-License-Identifier: GPL-2.0
26a99099fSThomas Zimmermann /*
36a99099fSThomas Zimmermann  * Copyright (C) 2019 Intel Corporation.
46a99099fSThomas Zimmermann  *
56a99099fSThomas Zimmermann  * Authors:
66a99099fSThomas Zimmermann  * Ramalingam C <ramalingam.c@intel.com>
76a99099fSThomas Zimmermann  */
86a99099fSThomas Zimmermann 
96a99099fSThomas Zimmermann #include <linux/device.h>
106a99099fSThomas Zimmermann #include <linux/err.h>
116a99099fSThomas Zimmermann #include <linux/gfp.h>
126a99099fSThomas Zimmermann #include <linux/export.h>
136a99099fSThomas Zimmermann #include <linux/slab.h>
146a99099fSThomas Zimmermann #include <linux/firmware.h>
156a99099fSThomas Zimmermann 
166a99099fSThomas Zimmermann #include <drm/display/drm_hdcp_helper.h>
176a99099fSThomas Zimmermann #include <drm/drm_sysfs.h>
186a99099fSThomas Zimmermann #include <drm/drm_print.h>
196a99099fSThomas Zimmermann #include <drm/drm_device.h>
206a99099fSThomas Zimmermann #include <drm/drm_property.h>
216a99099fSThomas Zimmermann #include <drm/drm_mode_object.h>
226a99099fSThomas Zimmermann #include <drm/drm_connector.h>
236a99099fSThomas Zimmermann 
drm_hdcp_print_ksv(const u8 * ksv)246a99099fSThomas Zimmermann static inline void drm_hdcp_print_ksv(const u8 *ksv)
256a99099fSThomas Zimmermann {
266a99099fSThomas Zimmermann 	DRM_DEBUG("\t%#02x, %#02x, %#02x, %#02x, %#02x\n",
276a99099fSThomas Zimmermann 		  ksv[0], ksv[1], ksv[2], ksv[3], ksv[4]);
286a99099fSThomas Zimmermann }
296a99099fSThomas Zimmermann 
drm_hdcp_get_revoked_ksv_count(const u8 * buf,u32 vrls_length)306a99099fSThomas Zimmermann static u32 drm_hdcp_get_revoked_ksv_count(const u8 *buf, u32 vrls_length)
316a99099fSThomas Zimmermann {
326a99099fSThomas Zimmermann 	u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz;
336a99099fSThomas Zimmermann 
346a99099fSThomas Zimmermann 	while (parsed_bytes < vrls_length) {
356a99099fSThomas Zimmermann 		vrl_ksv_cnt = *buf;
366a99099fSThomas Zimmermann 		ksv_count += vrl_ksv_cnt;
376a99099fSThomas Zimmermann 
386a99099fSThomas Zimmermann 		vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1;
396a99099fSThomas Zimmermann 		buf += vrl_sz;
406a99099fSThomas Zimmermann 		parsed_bytes += vrl_sz;
416a99099fSThomas Zimmermann 	}
426a99099fSThomas Zimmermann 
436a99099fSThomas Zimmermann 	/*
446a99099fSThomas Zimmermann 	 * When vrls are not valid, ksvs are not considered.
456a99099fSThomas Zimmermann 	 * Hence SRM will be discarded.
466a99099fSThomas Zimmermann 	 */
476a99099fSThomas Zimmermann 	if (parsed_bytes != vrls_length)
486a99099fSThomas Zimmermann 		ksv_count = 0;
496a99099fSThomas Zimmermann 
506a99099fSThomas Zimmermann 	return ksv_count;
516a99099fSThomas Zimmermann }
526a99099fSThomas Zimmermann 
drm_hdcp_get_revoked_ksvs(const u8 * buf,u8 ** revoked_ksv_list,u32 vrls_length)536a99099fSThomas Zimmermann static u32 drm_hdcp_get_revoked_ksvs(const u8 *buf, u8 **revoked_ksv_list,
546a99099fSThomas Zimmermann 				     u32 vrls_length)
556a99099fSThomas Zimmermann {
566a99099fSThomas Zimmermann 	u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0;
576a99099fSThomas Zimmermann 	u32 parsed_bytes = 0, ksv_count = 0;
586a99099fSThomas Zimmermann 
596a99099fSThomas Zimmermann 	do {
606a99099fSThomas Zimmermann 		vrl_ksv_cnt = *buf;
616a99099fSThomas Zimmermann 		vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN;
626a99099fSThomas Zimmermann 
636a99099fSThomas Zimmermann 		buf++;
646a99099fSThomas Zimmermann 
656a99099fSThomas Zimmermann 		DRM_DEBUG("vrl: %d, Revoked KSVs: %d\n", vrl_idx++,
666a99099fSThomas Zimmermann 			  vrl_ksv_cnt);
676a99099fSThomas Zimmermann 		memcpy((*revoked_ksv_list) + (ksv_count * DRM_HDCP_KSV_LEN),
686a99099fSThomas Zimmermann 		       buf, vrl_ksv_sz);
696a99099fSThomas Zimmermann 
706a99099fSThomas Zimmermann 		ksv_count += vrl_ksv_cnt;
716a99099fSThomas Zimmermann 		buf += vrl_ksv_sz;
726a99099fSThomas Zimmermann 
736a99099fSThomas Zimmermann 		parsed_bytes += (vrl_ksv_sz + 1);
746a99099fSThomas Zimmermann 	} while (parsed_bytes < vrls_length);
756a99099fSThomas Zimmermann 
766a99099fSThomas Zimmermann 	return ksv_count;
776a99099fSThomas Zimmermann }
786a99099fSThomas Zimmermann 
get_vrl_length(const u8 * buf)796a99099fSThomas Zimmermann static inline u32 get_vrl_length(const u8 *buf)
806a99099fSThomas Zimmermann {
816a99099fSThomas Zimmermann 	return drm_hdcp_be24_to_cpu(buf);
826a99099fSThomas Zimmermann }
836a99099fSThomas Zimmermann 
drm_hdcp_parse_hdcp1_srm(const u8 * buf,size_t count,u8 ** revoked_ksv_list,u32 * revoked_ksv_cnt)846a99099fSThomas Zimmermann static int drm_hdcp_parse_hdcp1_srm(const u8 *buf, size_t count,
856a99099fSThomas Zimmermann 				    u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
866a99099fSThomas Zimmermann {
876a99099fSThomas Zimmermann 	struct hdcp_srm_header *header;
886a99099fSThomas Zimmermann 	u32 vrl_length, ksv_count;
896a99099fSThomas Zimmermann 
906a99099fSThomas Zimmermann 	if (count < (sizeof(struct hdcp_srm_header) +
916a99099fSThomas Zimmermann 	    DRM_HDCP_1_4_VRL_LENGTH_SIZE + DRM_HDCP_1_4_DCP_SIG_SIZE)) {
926a99099fSThomas Zimmermann 		DRM_ERROR("Invalid blob length\n");
936a99099fSThomas Zimmermann 		return -EINVAL;
946a99099fSThomas Zimmermann 	}
956a99099fSThomas Zimmermann 
966a99099fSThomas Zimmermann 	header = (struct hdcp_srm_header *)buf;
976a99099fSThomas Zimmermann 	DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
986a99099fSThomas Zimmermann 		  header->srm_id,
996a99099fSThomas Zimmermann 		  be16_to_cpu(header->srm_version), header->srm_gen_no);
1006a99099fSThomas Zimmermann 
1016a99099fSThomas Zimmermann 	WARN_ON(header->reserved);
1026a99099fSThomas Zimmermann 
1036a99099fSThomas Zimmermann 	buf = buf + sizeof(*header);
1046a99099fSThomas Zimmermann 	vrl_length = get_vrl_length(buf);
1056a99099fSThomas Zimmermann 	if (count < (sizeof(struct hdcp_srm_header) + vrl_length) ||
1066a99099fSThomas Zimmermann 	    vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
1076a99099fSThomas Zimmermann 			  DRM_HDCP_1_4_DCP_SIG_SIZE)) {
1086a99099fSThomas Zimmermann 		DRM_ERROR("Invalid blob length or vrl length\n");
1096a99099fSThomas Zimmermann 		return -EINVAL;
1106a99099fSThomas Zimmermann 	}
1116a99099fSThomas Zimmermann 
1126a99099fSThomas Zimmermann 	/* Length of the all vrls combined */
1136a99099fSThomas Zimmermann 	vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
1146a99099fSThomas Zimmermann 		       DRM_HDCP_1_4_DCP_SIG_SIZE);
1156a99099fSThomas Zimmermann 
1166a99099fSThomas Zimmermann 	if (!vrl_length) {
1176a99099fSThomas Zimmermann 		DRM_ERROR("No vrl found\n");
1186a99099fSThomas Zimmermann 		return -EINVAL;
1196a99099fSThomas Zimmermann 	}
1206a99099fSThomas Zimmermann 
1216a99099fSThomas Zimmermann 	buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE;
1226a99099fSThomas Zimmermann 	ksv_count = drm_hdcp_get_revoked_ksv_count(buf, vrl_length);
1236a99099fSThomas Zimmermann 	if (!ksv_count) {
1246a99099fSThomas Zimmermann 		DRM_DEBUG("Revoked KSV count is 0\n");
1256a99099fSThomas Zimmermann 		return 0;
1266a99099fSThomas Zimmermann 	}
1276a99099fSThomas Zimmermann 
1286a99099fSThomas Zimmermann 	*revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, GFP_KERNEL);
1296a99099fSThomas Zimmermann 	if (!*revoked_ksv_list) {
1306a99099fSThomas Zimmermann 		DRM_ERROR("Out of Memory\n");
1316a99099fSThomas Zimmermann 		return -ENOMEM;
1326a99099fSThomas Zimmermann 	}
1336a99099fSThomas Zimmermann 
1346a99099fSThomas Zimmermann 	if (drm_hdcp_get_revoked_ksvs(buf, revoked_ksv_list,
1356a99099fSThomas Zimmermann 				      vrl_length) != ksv_count) {
1366a99099fSThomas Zimmermann 		*revoked_ksv_cnt = 0;
1376a99099fSThomas Zimmermann 		kfree(*revoked_ksv_list);
1386a99099fSThomas Zimmermann 		return -EINVAL;
1396a99099fSThomas Zimmermann 	}
1406a99099fSThomas Zimmermann 
1416a99099fSThomas Zimmermann 	*revoked_ksv_cnt = ksv_count;
1426a99099fSThomas Zimmermann 	return 0;
1436a99099fSThomas Zimmermann }
1446a99099fSThomas Zimmermann 
drm_hdcp_parse_hdcp2_srm(const u8 * buf,size_t count,u8 ** revoked_ksv_list,u32 * revoked_ksv_cnt)1456a99099fSThomas Zimmermann static int drm_hdcp_parse_hdcp2_srm(const u8 *buf, size_t count,
1466a99099fSThomas Zimmermann 				    u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
1476a99099fSThomas Zimmermann {
1486a99099fSThomas Zimmermann 	struct hdcp_srm_header *header;
1496a99099fSThomas Zimmermann 	u32 vrl_length, ksv_count, ksv_sz;
1506a99099fSThomas Zimmermann 
1516a99099fSThomas Zimmermann 	if (count < (sizeof(struct hdcp_srm_header) +
1526a99099fSThomas Zimmermann 	    DRM_HDCP_2_VRL_LENGTH_SIZE + DRM_HDCP_2_DCP_SIG_SIZE)) {
1536a99099fSThomas Zimmermann 		DRM_ERROR("Invalid blob length\n");
1546a99099fSThomas Zimmermann 		return -EINVAL;
1556a99099fSThomas Zimmermann 	}
1566a99099fSThomas Zimmermann 
1576a99099fSThomas Zimmermann 	header = (struct hdcp_srm_header *)buf;
1586a99099fSThomas Zimmermann 	DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
1596a99099fSThomas Zimmermann 		  header->srm_id & DRM_HDCP_SRM_ID_MASK,
1606a99099fSThomas Zimmermann 		  be16_to_cpu(header->srm_version), header->srm_gen_no);
1616a99099fSThomas Zimmermann 
1626a99099fSThomas Zimmermann 	if (header->reserved)
1636a99099fSThomas Zimmermann 		return -EINVAL;
1646a99099fSThomas Zimmermann 
1656a99099fSThomas Zimmermann 	buf = buf + sizeof(*header);
1666a99099fSThomas Zimmermann 	vrl_length = get_vrl_length(buf);
1676a99099fSThomas Zimmermann 
1686a99099fSThomas Zimmermann 	if (count < (sizeof(struct hdcp_srm_header) + vrl_length) ||
1696a99099fSThomas Zimmermann 	    vrl_length < (DRM_HDCP_2_VRL_LENGTH_SIZE +
1706a99099fSThomas Zimmermann 	    DRM_HDCP_2_DCP_SIG_SIZE)) {
1716a99099fSThomas Zimmermann 		DRM_ERROR("Invalid blob length or vrl length\n");
1726a99099fSThomas Zimmermann 		return -EINVAL;
1736a99099fSThomas Zimmermann 	}
1746a99099fSThomas Zimmermann 
1756a99099fSThomas Zimmermann 	/* Length of the all vrls combined */
1766a99099fSThomas Zimmermann 	vrl_length -= (DRM_HDCP_2_VRL_LENGTH_SIZE +
1776a99099fSThomas Zimmermann 		       DRM_HDCP_2_DCP_SIG_SIZE);
1786a99099fSThomas Zimmermann 
1796a99099fSThomas Zimmermann 	if (!vrl_length) {
1806a99099fSThomas Zimmermann 		DRM_ERROR("No vrl found\n");
1816a99099fSThomas Zimmermann 		return -EINVAL;
1826a99099fSThomas Zimmermann 	}
1836a99099fSThomas Zimmermann 
1846a99099fSThomas Zimmermann 	buf += DRM_HDCP_2_VRL_LENGTH_SIZE;
1856a99099fSThomas Zimmermann 	ksv_count = (*buf << 2) | DRM_HDCP_2_KSV_COUNT_2_LSBITS(*(buf + 1));
1866a99099fSThomas Zimmermann 	if (!ksv_count) {
1876a99099fSThomas Zimmermann 		DRM_DEBUG("Revoked KSV count is 0\n");
1886a99099fSThomas Zimmermann 		return 0;
1896a99099fSThomas Zimmermann 	}
1906a99099fSThomas Zimmermann 
1916a99099fSThomas Zimmermann 	*revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, GFP_KERNEL);
1926a99099fSThomas Zimmermann 	if (!*revoked_ksv_list) {
1936a99099fSThomas Zimmermann 		DRM_ERROR("Out of Memory\n");
1946a99099fSThomas Zimmermann 		return -ENOMEM;
1956a99099fSThomas Zimmermann 	}
1966a99099fSThomas Zimmermann 
1976a99099fSThomas Zimmermann 	ksv_sz = ksv_count * DRM_HDCP_KSV_LEN;
1986a99099fSThomas Zimmermann 	buf += DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ;
1996a99099fSThomas Zimmermann 
2006a99099fSThomas Zimmermann 	DRM_DEBUG("Revoked KSVs: %d\n", ksv_count);
2016a99099fSThomas Zimmermann 	memcpy(*revoked_ksv_list, buf, ksv_sz);
2026a99099fSThomas Zimmermann 
2036a99099fSThomas Zimmermann 	*revoked_ksv_cnt = ksv_count;
2046a99099fSThomas Zimmermann 	return 0;
2056a99099fSThomas Zimmermann }
2066a99099fSThomas Zimmermann 
is_srm_version_hdcp1(const u8 * buf)2076a99099fSThomas Zimmermann static inline bool is_srm_version_hdcp1(const u8 *buf)
2086a99099fSThomas Zimmermann {
2096a99099fSThomas Zimmermann 	return *buf == (u8)(DRM_HDCP_1_4_SRM_ID << 4);
2106a99099fSThomas Zimmermann }
2116a99099fSThomas Zimmermann 
is_srm_version_hdcp2(const u8 * buf)2126a99099fSThomas Zimmermann static inline bool is_srm_version_hdcp2(const u8 *buf)
2136a99099fSThomas Zimmermann {
2146a99099fSThomas Zimmermann 	return *buf == (u8)(DRM_HDCP_2_SRM_ID << 4 | DRM_HDCP_2_INDICATOR);
2156a99099fSThomas Zimmermann }
2166a99099fSThomas Zimmermann 
drm_hdcp_srm_update(const u8 * buf,size_t count,u8 ** revoked_ksv_list,u32 * revoked_ksv_cnt)2176a99099fSThomas Zimmermann static int drm_hdcp_srm_update(const u8 *buf, size_t count,
2186a99099fSThomas Zimmermann 			       u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
2196a99099fSThomas Zimmermann {
2206a99099fSThomas Zimmermann 	if (count < sizeof(struct hdcp_srm_header))
2216a99099fSThomas Zimmermann 		return -EINVAL;
2226a99099fSThomas Zimmermann 
2236a99099fSThomas Zimmermann 	if (is_srm_version_hdcp1(buf))
2246a99099fSThomas Zimmermann 		return drm_hdcp_parse_hdcp1_srm(buf, count, revoked_ksv_list,
2256a99099fSThomas Zimmermann 						revoked_ksv_cnt);
2266a99099fSThomas Zimmermann 	else if (is_srm_version_hdcp2(buf))
2276a99099fSThomas Zimmermann 		return drm_hdcp_parse_hdcp2_srm(buf, count, revoked_ksv_list,
2286a99099fSThomas Zimmermann 						revoked_ksv_cnt);
2296a99099fSThomas Zimmermann 	else
2306a99099fSThomas Zimmermann 		return -EINVAL;
2316a99099fSThomas Zimmermann }
2326a99099fSThomas Zimmermann 
drm_hdcp_request_srm(struct drm_device * drm_dev,u8 ** revoked_ksv_list,u32 * revoked_ksv_cnt)2336a99099fSThomas Zimmermann static int drm_hdcp_request_srm(struct drm_device *drm_dev,
2346a99099fSThomas Zimmermann 				u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
2356a99099fSThomas Zimmermann {
2366a99099fSThomas Zimmermann 	char fw_name[36] = "display_hdcp_srm.bin";
2376a99099fSThomas Zimmermann 	const struct firmware *fw;
2386a99099fSThomas Zimmermann 	int ret;
2396a99099fSThomas Zimmermann 
2406a99099fSThomas Zimmermann 	ret = request_firmware_direct(&fw, (const char *)fw_name,
2416a99099fSThomas Zimmermann 				      drm_dev->dev);
2426a99099fSThomas Zimmermann 	if (ret < 0) {
2436a99099fSThomas Zimmermann 		*revoked_ksv_cnt = 0;
2446a99099fSThomas Zimmermann 		*revoked_ksv_list = NULL;
2456a99099fSThomas Zimmermann 		ret = 0;
2466a99099fSThomas Zimmermann 		goto exit;
2476a99099fSThomas Zimmermann 	}
2486a99099fSThomas Zimmermann 
2496a99099fSThomas Zimmermann 	if (fw->size && fw->data)
2506a99099fSThomas Zimmermann 		ret = drm_hdcp_srm_update(fw->data, fw->size, revoked_ksv_list,
2516a99099fSThomas Zimmermann 					  revoked_ksv_cnt);
2526a99099fSThomas Zimmermann 
2536a99099fSThomas Zimmermann exit:
2546a99099fSThomas Zimmermann 	release_firmware(fw);
2556a99099fSThomas Zimmermann 	return ret;
2566a99099fSThomas Zimmermann }
2576a99099fSThomas Zimmermann 
2586a99099fSThomas Zimmermann /**
2596a99099fSThomas Zimmermann  * drm_hdcp_check_ksvs_revoked - Check the revoked status of the IDs
2606a99099fSThomas Zimmermann  *
2616a99099fSThomas Zimmermann  * @drm_dev: drm_device for which HDCP revocation check is requested
2626a99099fSThomas Zimmermann  * @ksvs: List of KSVs (HDCP receiver IDs)
2636a99099fSThomas Zimmermann  * @ksv_count: KSV count passed in through @ksvs
2646a99099fSThomas Zimmermann  *
2656a99099fSThomas Zimmermann  * This function reads the HDCP System renewability Message(SRM Table)
2666a99099fSThomas Zimmermann  * from userspace as a firmware and parses it for the revoked HDCP
2676a99099fSThomas Zimmermann  * KSVs(Receiver IDs) detected by DCP LLC. Once the revoked KSVs are known,
2686a99099fSThomas Zimmermann  * revoked state of the KSVs in the list passed in by display drivers are
2696a99099fSThomas Zimmermann  * decided and response is sent.
2706a99099fSThomas Zimmermann  *
2716a99099fSThomas Zimmermann  * SRM should be presented in the name of "display_hdcp_srm.bin".
2726a99099fSThomas Zimmermann  *
2736a99099fSThomas Zimmermann  * Format of the SRM table, that userspace needs to write into the binary file,
2746a99099fSThomas Zimmermann  * is defined at:
2756a99099fSThomas Zimmermann  * 1. Renewability chapter on 55th page of HDCP 1.4 specification
2766a99099fSThomas Zimmermann  * https://www.digital-cp.com/sites/default/files/specifications/HDCP%20Specification%20Rev1_4_Secure.pdf
2776a99099fSThomas Zimmermann  * 2. Renewability chapter on 63rd page of HDCP 2.2 specification
2786a99099fSThomas Zimmermann  * https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf
2796a99099fSThomas Zimmermann  *
2806a99099fSThomas Zimmermann  * Returns:
2816a99099fSThomas Zimmermann  * Count of the revoked KSVs or -ve error number in case of the failure.
2826a99099fSThomas Zimmermann  */
drm_hdcp_check_ksvs_revoked(struct drm_device * drm_dev,u8 * ksvs,u32 ksv_count)2836a99099fSThomas Zimmermann int drm_hdcp_check_ksvs_revoked(struct drm_device *drm_dev, u8 *ksvs,
2846a99099fSThomas Zimmermann 				u32 ksv_count)
2856a99099fSThomas Zimmermann {
2866a99099fSThomas Zimmermann 	u32 revoked_ksv_cnt = 0, i, j;
2876a99099fSThomas Zimmermann 	u8 *revoked_ksv_list = NULL;
2886a99099fSThomas Zimmermann 	int ret = 0;
2896a99099fSThomas Zimmermann 
2906a99099fSThomas Zimmermann 	ret = drm_hdcp_request_srm(drm_dev, &revoked_ksv_list,
2916a99099fSThomas Zimmermann 				   &revoked_ksv_cnt);
2926a99099fSThomas Zimmermann 	if (ret)
2936a99099fSThomas Zimmermann 		return ret;
2946a99099fSThomas Zimmermann 
2956a99099fSThomas Zimmermann 	/* revoked_ksv_cnt will be zero when above function failed */
2966a99099fSThomas Zimmermann 	for (i = 0; i < revoked_ksv_cnt; i++)
2976a99099fSThomas Zimmermann 		for  (j = 0; j < ksv_count; j++)
2986a99099fSThomas Zimmermann 			if (!memcmp(&ksvs[j * DRM_HDCP_KSV_LEN],
2996a99099fSThomas Zimmermann 				    &revoked_ksv_list[i * DRM_HDCP_KSV_LEN],
3006a99099fSThomas Zimmermann 				    DRM_HDCP_KSV_LEN)) {
3016a99099fSThomas Zimmermann 				DRM_DEBUG("Revoked KSV is ");
3026a99099fSThomas Zimmermann 				drm_hdcp_print_ksv(&ksvs[j * DRM_HDCP_KSV_LEN]);
3036a99099fSThomas Zimmermann 				ret++;
3046a99099fSThomas Zimmermann 			}
3056a99099fSThomas Zimmermann 
3066a99099fSThomas Zimmermann 	kfree(revoked_ksv_list);
3076a99099fSThomas Zimmermann 	return ret;
3086a99099fSThomas Zimmermann }
3096a99099fSThomas Zimmermann EXPORT_SYMBOL_GPL(drm_hdcp_check_ksvs_revoked);
3106a99099fSThomas Zimmermann 
3116a99099fSThomas Zimmermann static struct drm_prop_enum_list drm_cp_enum_list[] = {
3126a99099fSThomas Zimmermann 	{ DRM_MODE_CONTENT_PROTECTION_UNDESIRED, "Undesired" },
3136a99099fSThomas Zimmermann 	{ DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" },
3146a99099fSThomas Zimmermann 	{ DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" },
3156a99099fSThomas Zimmermann };
3166a99099fSThomas Zimmermann DRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
3176a99099fSThomas Zimmermann 
3186a99099fSThomas Zimmermann static struct drm_prop_enum_list drm_hdcp_content_type_enum_list[] = {
3196a99099fSThomas Zimmermann 	{ DRM_MODE_HDCP_CONTENT_TYPE0, "HDCP Type0" },
3206a99099fSThomas Zimmermann 	{ DRM_MODE_HDCP_CONTENT_TYPE1, "HDCP Type1" },
3216a99099fSThomas Zimmermann };
DRM_ENUM_NAME_FN(drm_get_hdcp_content_type_name,drm_hdcp_content_type_enum_list)3226a99099fSThomas Zimmermann DRM_ENUM_NAME_FN(drm_get_hdcp_content_type_name,
3236a99099fSThomas Zimmermann 		 drm_hdcp_content_type_enum_list)
3246a99099fSThomas Zimmermann 
3256a99099fSThomas Zimmermann /**
3266a99099fSThomas Zimmermann  * drm_connector_attach_content_protection_property - attach content protection
3276a99099fSThomas Zimmermann  * property
3286a99099fSThomas Zimmermann  *
3296a99099fSThomas Zimmermann  * @connector: connector to attach CP property on.
3306a99099fSThomas Zimmermann  * @hdcp_content_type: is HDCP Content Type property needed for connector
3316a99099fSThomas Zimmermann  *
3326a99099fSThomas Zimmermann  * This is used to add support for content protection on select connectors.
3336a99099fSThomas Zimmermann  * Content Protection is intentionally vague to allow for different underlying
3346a99099fSThomas Zimmermann  * technologies, however it is most implemented by HDCP.
3356a99099fSThomas Zimmermann  *
3366a99099fSThomas Zimmermann  * When hdcp_content_type is true enum property called HDCP Content Type is
3376a99099fSThomas Zimmermann  * created (if it is not already) and attached to the connector.
3386a99099fSThomas Zimmermann  *
3396a99099fSThomas Zimmermann  * This property is used for sending the protected content's stream type
3406a99099fSThomas Zimmermann  * from userspace to kernel on selected connectors. Protected content provider
3416a99099fSThomas Zimmermann  * will decide their type of their content and declare the same to kernel.
3426a99099fSThomas Zimmermann  *
3436a99099fSThomas Zimmermann  * Content type will be used during the HDCP 2.2 authentication.
3446a99099fSThomas Zimmermann  * Content type will be set to &drm_connector_state.hdcp_content_type.
3456a99099fSThomas Zimmermann  *
3466a99099fSThomas Zimmermann  * The content protection will be set to &drm_connector_state.content_protection
3476a99099fSThomas Zimmermann  *
3486a99099fSThomas Zimmermann  * When kernel triggered content protection state change like DESIRED->ENABLED
3496a99099fSThomas Zimmermann  * and ENABLED->DESIRED, will use drm_hdcp_update_content_protection() to update
3506a99099fSThomas Zimmermann  * the content protection state of a connector.
3516a99099fSThomas Zimmermann  *
3526a99099fSThomas Zimmermann  * Returns:
3536a99099fSThomas Zimmermann  * Zero on success, negative errno on failure.
3546a99099fSThomas Zimmermann  */
3556a99099fSThomas Zimmermann int drm_connector_attach_content_protection_property(
3566a99099fSThomas Zimmermann 		struct drm_connector *connector, bool hdcp_content_type)
3576a99099fSThomas Zimmermann {
3586a99099fSThomas Zimmermann 	struct drm_device *dev = connector->dev;
3596a99099fSThomas Zimmermann 	struct drm_property *prop =
3606a99099fSThomas Zimmermann 			dev->mode_config.content_protection_property;
3616a99099fSThomas Zimmermann 
3626a99099fSThomas Zimmermann 	if (!prop)
3636a99099fSThomas Zimmermann 		prop = drm_property_create_enum(dev, 0, "Content Protection",
3646a99099fSThomas Zimmermann 						drm_cp_enum_list,
3656a99099fSThomas Zimmermann 						ARRAY_SIZE(drm_cp_enum_list));
3666a99099fSThomas Zimmermann 	if (!prop)
3676a99099fSThomas Zimmermann 		return -ENOMEM;
3686a99099fSThomas Zimmermann 
3696a99099fSThomas Zimmermann 	drm_object_attach_property(&connector->base, prop,
3706a99099fSThomas Zimmermann 				   DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
3716a99099fSThomas Zimmermann 	dev->mode_config.content_protection_property = prop;
3726a99099fSThomas Zimmermann 
3736a99099fSThomas Zimmermann 	if (!hdcp_content_type)
3746a99099fSThomas Zimmermann 		return 0;
3756a99099fSThomas Zimmermann 
3766a99099fSThomas Zimmermann 	prop = dev->mode_config.hdcp_content_type_property;
3776a99099fSThomas Zimmermann 	if (!prop)
3786a99099fSThomas Zimmermann 		prop = drm_property_create_enum(dev, 0, "HDCP Content Type",
3796a99099fSThomas Zimmermann 					drm_hdcp_content_type_enum_list,
3806a99099fSThomas Zimmermann 					ARRAY_SIZE(
3816a99099fSThomas Zimmermann 					drm_hdcp_content_type_enum_list));
3826a99099fSThomas Zimmermann 	if (!prop)
3836a99099fSThomas Zimmermann 		return -ENOMEM;
3846a99099fSThomas Zimmermann 
3856a99099fSThomas Zimmermann 	drm_object_attach_property(&connector->base, prop,
3866a99099fSThomas Zimmermann 				   DRM_MODE_HDCP_CONTENT_TYPE0);
3876a99099fSThomas Zimmermann 	dev->mode_config.hdcp_content_type_property = prop;
3886a99099fSThomas Zimmermann 
3896a99099fSThomas Zimmermann 	return 0;
3906a99099fSThomas Zimmermann }
3916a99099fSThomas Zimmermann EXPORT_SYMBOL(drm_connector_attach_content_protection_property);
3926a99099fSThomas Zimmermann 
3936a99099fSThomas Zimmermann /**
3946a99099fSThomas Zimmermann  * drm_hdcp_update_content_protection - Updates the content protection state
3956a99099fSThomas Zimmermann  * of a connector
3966a99099fSThomas Zimmermann  *
3976a99099fSThomas Zimmermann  * @connector: drm_connector on which content protection state needs an update
3986a99099fSThomas Zimmermann  * @val: New state of the content protection property
3996a99099fSThomas Zimmermann  *
4006a99099fSThomas Zimmermann  * This function can be used by display drivers, to update the kernel triggered
4016a99099fSThomas Zimmermann  * content protection state changes of a drm_connector such as DESIRED->ENABLED
4026a99099fSThomas Zimmermann  * and ENABLED->DESIRED. No uevent for DESIRED->UNDESIRED or ENABLED->UNDESIRED,
4036a99099fSThomas Zimmermann  * as userspace is triggering such state change and kernel performs it without
4046a99099fSThomas Zimmermann  * fail.This function update the new state of the property into the connector's
4056a99099fSThomas Zimmermann  * state and generate an uevent to notify the userspace.
4066a99099fSThomas Zimmermann  */
drm_hdcp_update_content_protection(struct drm_connector * connector,u64 val)4076a99099fSThomas Zimmermann void drm_hdcp_update_content_protection(struct drm_connector *connector,
4086a99099fSThomas Zimmermann 					u64 val)
4096a99099fSThomas Zimmermann {
4106a99099fSThomas Zimmermann 	struct drm_device *dev = connector->dev;
4116a99099fSThomas Zimmermann 	struct drm_connector_state *state = connector->state;
4126a99099fSThomas Zimmermann 
4136a99099fSThomas Zimmermann 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
4146a99099fSThomas Zimmermann 	if (state->content_protection == val)
4156a99099fSThomas Zimmermann 		return;
4166a99099fSThomas Zimmermann 
4176a99099fSThomas Zimmermann 	state->content_protection = val;
418*0cf8d292SSimon Ser 	drm_sysfs_connector_property_event(connector,
4196a99099fSThomas Zimmermann 					   dev->mode_config.content_protection_property);
4206a99099fSThomas Zimmermann }
4216a99099fSThomas Zimmermann EXPORT_SYMBOL(drm_hdcp_update_content_protection);
422