162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2019 Intel Corporation.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors:
662306a36Sopenharmony_ci * Ramalingam C <ramalingam.c@intel.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/err.h>
1162306a36Sopenharmony_ci#include <linux/gfp.h>
1262306a36Sopenharmony_ci#include <linux/export.h>
1362306a36Sopenharmony_ci#include <linux/slab.h>
1462306a36Sopenharmony_ci#include <linux/firmware.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <drm/display/drm_hdcp_helper.h>
1762306a36Sopenharmony_ci#include <drm/drm_sysfs.h>
1862306a36Sopenharmony_ci#include <drm/drm_print.h>
1962306a36Sopenharmony_ci#include <drm/drm_device.h>
2062306a36Sopenharmony_ci#include <drm/drm_property.h>
2162306a36Sopenharmony_ci#include <drm/drm_mode_object.h>
2262306a36Sopenharmony_ci#include <drm/drm_connector.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic inline void drm_hdcp_print_ksv(const u8 *ksv)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	DRM_DEBUG("\t%#02x, %#02x, %#02x, %#02x, %#02x\n",
2762306a36Sopenharmony_ci		  ksv[0], ksv[1], ksv[2], ksv[3], ksv[4]);
2862306a36Sopenharmony_ci}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic u32 drm_hdcp_get_revoked_ksv_count(const u8 *buf, u32 vrls_length)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	u32 parsed_bytes = 0, ksv_count = 0, vrl_ksv_cnt, vrl_sz;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	while (parsed_bytes < vrls_length) {
3562306a36Sopenharmony_ci		vrl_ksv_cnt = *buf;
3662306a36Sopenharmony_ci		ksv_count += vrl_ksv_cnt;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci		vrl_sz = (vrl_ksv_cnt * DRM_HDCP_KSV_LEN) + 1;
3962306a36Sopenharmony_ci		buf += vrl_sz;
4062306a36Sopenharmony_ci		parsed_bytes += vrl_sz;
4162306a36Sopenharmony_ci	}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/*
4462306a36Sopenharmony_ci	 * When vrls are not valid, ksvs are not considered.
4562306a36Sopenharmony_ci	 * Hence SRM will be discarded.
4662306a36Sopenharmony_ci	 */
4762306a36Sopenharmony_ci	if (parsed_bytes != vrls_length)
4862306a36Sopenharmony_ci		ksv_count = 0;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return ksv_count;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_cistatic u32 drm_hdcp_get_revoked_ksvs(const u8 *buf, u8 **revoked_ksv_list,
5462306a36Sopenharmony_ci				     u32 vrls_length)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	u32 vrl_ksv_cnt, vrl_ksv_sz, vrl_idx = 0;
5762306a36Sopenharmony_ci	u32 parsed_bytes = 0, ksv_count = 0;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	do {
6062306a36Sopenharmony_ci		vrl_ksv_cnt = *buf;
6162306a36Sopenharmony_ci		vrl_ksv_sz = vrl_ksv_cnt * DRM_HDCP_KSV_LEN;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		buf++;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci		DRM_DEBUG("vrl: %d, Revoked KSVs: %d\n", vrl_idx++,
6662306a36Sopenharmony_ci			  vrl_ksv_cnt);
6762306a36Sopenharmony_ci		memcpy((*revoked_ksv_list) + (ksv_count * DRM_HDCP_KSV_LEN),
6862306a36Sopenharmony_ci		       buf, vrl_ksv_sz);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci		ksv_count += vrl_ksv_cnt;
7162306a36Sopenharmony_ci		buf += vrl_ksv_sz;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		parsed_bytes += (vrl_ksv_sz + 1);
7462306a36Sopenharmony_ci	} while (parsed_bytes < vrls_length);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	return ksv_count;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic inline u32 get_vrl_length(const u8 *buf)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	return drm_hdcp_be24_to_cpu(buf);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int drm_hdcp_parse_hdcp1_srm(const u8 *buf, size_t count,
8562306a36Sopenharmony_ci				    u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	struct hdcp_srm_header *header;
8862306a36Sopenharmony_ci	u32 vrl_length, ksv_count;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	if (count < (sizeof(struct hdcp_srm_header) +
9162306a36Sopenharmony_ci	    DRM_HDCP_1_4_VRL_LENGTH_SIZE + DRM_HDCP_1_4_DCP_SIG_SIZE)) {
9262306a36Sopenharmony_ci		DRM_ERROR("Invalid blob length\n");
9362306a36Sopenharmony_ci		return -EINVAL;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	header = (struct hdcp_srm_header *)buf;
9762306a36Sopenharmony_ci	DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
9862306a36Sopenharmony_ci		  header->srm_id,
9962306a36Sopenharmony_ci		  be16_to_cpu(header->srm_version), header->srm_gen_no);
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	WARN_ON(header->reserved);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	buf = buf + sizeof(*header);
10462306a36Sopenharmony_ci	vrl_length = get_vrl_length(buf);
10562306a36Sopenharmony_ci	if (count < (sizeof(struct hdcp_srm_header) + vrl_length) ||
10662306a36Sopenharmony_ci	    vrl_length < (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
10762306a36Sopenharmony_ci			  DRM_HDCP_1_4_DCP_SIG_SIZE)) {
10862306a36Sopenharmony_ci		DRM_ERROR("Invalid blob length or vrl length\n");
10962306a36Sopenharmony_ci		return -EINVAL;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Length of the all vrls combined */
11362306a36Sopenharmony_ci	vrl_length -= (DRM_HDCP_1_4_VRL_LENGTH_SIZE +
11462306a36Sopenharmony_ci		       DRM_HDCP_1_4_DCP_SIG_SIZE);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	if (!vrl_length) {
11762306a36Sopenharmony_ci		DRM_ERROR("No vrl found\n");
11862306a36Sopenharmony_ci		return -EINVAL;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	buf += DRM_HDCP_1_4_VRL_LENGTH_SIZE;
12262306a36Sopenharmony_ci	ksv_count = drm_hdcp_get_revoked_ksv_count(buf, vrl_length);
12362306a36Sopenharmony_ci	if (!ksv_count) {
12462306a36Sopenharmony_ci		DRM_DEBUG("Revoked KSV count is 0\n");
12562306a36Sopenharmony_ci		return 0;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	*revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, GFP_KERNEL);
12962306a36Sopenharmony_ci	if (!*revoked_ksv_list) {
13062306a36Sopenharmony_ci		DRM_ERROR("Out of Memory\n");
13162306a36Sopenharmony_ci		return -ENOMEM;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (drm_hdcp_get_revoked_ksvs(buf, revoked_ksv_list,
13562306a36Sopenharmony_ci				      vrl_length) != ksv_count) {
13662306a36Sopenharmony_ci		*revoked_ksv_cnt = 0;
13762306a36Sopenharmony_ci		kfree(*revoked_ksv_list);
13862306a36Sopenharmony_ci		return -EINVAL;
13962306a36Sopenharmony_ci	}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	*revoked_ksv_cnt = ksv_count;
14262306a36Sopenharmony_ci	return 0;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int drm_hdcp_parse_hdcp2_srm(const u8 *buf, size_t count,
14662306a36Sopenharmony_ci				    u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	struct hdcp_srm_header *header;
14962306a36Sopenharmony_ci	u32 vrl_length, ksv_count, ksv_sz;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	if (count < (sizeof(struct hdcp_srm_header) +
15262306a36Sopenharmony_ci	    DRM_HDCP_2_VRL_LENGTH_SIZE + DRM_HDCP_2_DCP_SIG_SIZE)) {
15362306a36Sopenharmony_ci		DRM_ERROR("Invalid blob length\n");
15462306a36Sopenharmony_ci		return -EINVAL;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	header = (struct hdcp_srm_header *)buf;
15862306a36Sopenharmony_ci	DRM_DEBUG("SRM ID: 0x%x, SRM Ver: 0x%x, SRM Gen No: 0x%x\n",
15962306a36Sopenharmony_ci		  header->srm_id & DRM_HDCP_SRM_ID_MASK,
16062306a36Sopenharmony_ci		  be16_to_cpu(header->srm_version), header->srm_gen_no);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	if (header->reserved)
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	buf = buf + sizeof(*header);
16662306a36Sopenharmony_ci	vrl_length = get_vrl_length(buf);
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (count < (sizeof(struct hdcp_srm_header) + vrl_length) ||
16962306a36Sopenharmony_ci	    vrl_length < (DRM_HDCP_2_VRL_LENGTH_SIZE +
17062306a36Sopenharmony_ci	    DRM_HDCP_2_DCP_SIG_SIZE)) {
17162306a36Sopenharmony_ci		DRM_ERROR("Invalid blob length or vrl length\n");
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* Length of the all vrls combined */
17662306a36Sopenharmony_ci	vrl_length -= (DRM_HDCP_2_VRL_LENGTH_SIZE +
17762306a36Sopenharmony_ci		       DRM_HDCP_2_DCP_SIG_SIZE);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (!vrl_length) {
18062306a36Sopenharmony_ci		DRM_ERROR("No vrl found\n");
18162306a36Sopenharmony_ci		return -EINVAL;
18262306a36Sopenharmony_ci	}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	buf += DRM_HDCP_2_VRL_LENGTH_SIZE;
18562306a36Sopenharmony_ci	ksv_count = (*buf << 2) | DRM_HDCP_2_KSV_COUNT_2_LSBITS(*(buf + 1));
18662306a36Sopenharmony_ci	if (!ksv_count) {
18762306a36Sopenharmony_ci		DRM_DEBUG("Revoked KSV count is 0\n");
18862306a36Sopenharmony_ci		return 0;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	*revoked_ksv_list = kcalloc(ksv_count, DRM_HDCP_KSV_LEN, GFP_KERNEL);
19262306a36Sopenharmony_ci	if (!*revoked_ksv_list) {
19362306a36Sopenharmony_ci		DRM_ERROR("Out of Memory\n");
19462306a36Sopenharmony_ci		return -ENOMEM;
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ksv_sz = ksv_count * DRM_HDCP_KSV_LEN;
19862306a36Sopenharmony_ci	buf += DRM_HDCP_2_NO_OF_DEV_PLUS_RESERVED_SZ;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	DRM_DEBUG("Revoked KSVs: %d\n", ksv_count);
20162306a36Sopenharmony_ci	memcpy(*revoked_ksv_list, buf, ksv_sz);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	*revoked_ksv_cnt = ksv_count;
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic inline bool is_srm_version_hdcp1(const u8 *buf)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	return *buf == (u8)(DRM_HDCP_1_4_SRM_ID << 4);
21062306a36Sopenharmony_ci}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_cistatic inline bool is_srm_version_hdcp2(const u8 *buf)
21362306a36Sopenharmony_ci{
21462306a36Sopenharmony_ci	return *buf == (u8)(DRM_HDCP_2_SRM_ID << 4 | DRM_HDCP_2_INDICATOR);
21562306a36Sopenharmony_ci}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_cistatic int drm_hdcp_srm_update(const u8 *buf, size_t count,
21862306a36Sopenharmony_ci			       u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	if (count < sizeof(struct hdcp_srm_header))
22162306a36Sopenharmony_ci		return -EINVAL;
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	if (is_srm_version_hdcp1(buf))
22462306a36Sopenharmony_ci		return drm_hdcp_parse_hdcp1_srm(buf, count, revoked_ksv_list,
22562306a36Sopenharmony_ci						revoked_ksv_cnt);
22662306a36Sopenharmony_ci	else if (is_srm_version_hdcp2(buf))
22762306a36Sopenharmony_ci		return drm_hdcp_parse_hdcp2_srm(buf, count, revoked_ksv_list,
22862306a36Sopenharmony_ci						revoked_ksv_cnt);
22962306a36Sopenharmony_ci	else
23062306a36Sopenharmony_ci		return -EINVAL;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic int drm_hdcp_request_srm(struct drm_device *drm_dev,
23462306a36Sopenharmony_ci				u8 **revoked_ksv_list, u32 *revoked_ksv_cnt)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	char fw_name[36] = "display_hdcp_srm.bin";
23762306a36Sopenharmony_ci	const struct firmware *fw;
23862306a36Sopenharmony_ci	int ret;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ret = request_firmware_direct(&fw, (const char *)fw_name,
24162306a36Sopenharmony_ci				      drm_dev->dev);
24262306a36Sopenharmony_ci	if (ret < 0) {
24362306a36Sopenharmony_ci		*revoked_ksv_cnt = 0;
24462306a36Sopenharmony_ci		*revoked_ksv_list = NULL;
24562306a36Sopenharmony_ci		ret = 0;
24662306a36Sopenharmony_ci		goto exit;
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if (fw->size && fw->data)
25062306a36Sopenharmony_ci		ret = drm_hdcp_srm_update(fw->data, fw->size, revoked_ksv_list,
25162306a36Sopenharmony_ci					  revoked_ksv_cnt);
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ciexit:
25462306a36Sopenharmony_ci	release_firmware(fw);
25562306a36Sopenharmony_ci	return ret;
25662306a36Sopenharmony_ci}
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci/**
25962306a36Sopenharmony_ci * drm_hdcp_check_ksvs_revoked - Check the revoked status of the IDs
26062306a36Sopenharmony_ci *
26162306a36Sopenharmony_ci * @drm_dev: drm_device for which HDCP revocation check is requested
26262306a36Sopenharmony_ci * @ksvs: List of KSVs (HDCP receiver IDs)
26362306a36Sopenharmony_ci * @ksv_count: KSV count passed in through @ksvs
26462306a36Sopenharmony_ci *
26562306a36Sopenharmony_ci * This function reads the HDCP System renewability Message(SRM Table)
26662306a36Sopenharmony_ci * from userspace as a firmware and parses it for the revoked HDCP
26762306a36Sopenharmony_ci * KSVs(Receiver IDs) detected by DCP LLC. Once the revoked KSVs are known,
26862306a36Sopenharmony_ci * revoked state of the KSVs in the list passed in by display drivers are
26962306a36Sopenharmony_ci * decided and response is sent.
27062306a36Sopenharmony_ci *
27162306a36Sopenharmony_ci * SRM should be presented in the name of "display_hdcp_srm.bin".
27262306a36Sopenharmony_ci *
27362306a36Sopenharmony_ci * Format of the SRM table, that userspace needs to write into the binary file,
27462306a36Sopenharmony_ci * is defined at:
27562306a36Sopenharmony_ci * 1. Renewability chapter on 55th page of HDCP 1.4 specification
27662306a36Sopenharmony_ci * https://www.digital-cp.com/sites/default/files/specifications/HDCP%20Specification%20Rev1_4_Secure.pdf
27762306a36Sopenharmony_ci * 2. Renewability chapter on 63rd page of HDCP 2.2 specification
27862306a36Sopenharmony_ci * https://www.digital-cp.com/sites/default/files/specifications/HDCP%20on%20HDMI%20Specification%20Rev2_2_Final1.pdf
27962306a36Sopenharmony_ci *
28062306a36Sopenharmony_ci * Returns:
28162306a36Sopenharmony_ci * Count of the revoked KSVs or -ve error number in case of the failure.
28262306a36Sopenharmony_ci */
28362306a36Sopenharmony_ciint drm_hdcp_check_ksvs_revoked(struct drm_device *drm_dev, u8 *ksvs,
28462306a36Sopenharmony_ci				u32 ksv_count)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	u32 revoked_ksv_cnt = 0, i, j;
28762306a36Sopenharmony_ci	u8 *revoked_ksv_list = NULL;
28862306a36Sopenharmony_ci	int ret = 0;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	ret = drm_hdcp_request_srm(drm_dev, &revoked_ksv_list,
29162306a36Sopenharmony_ci				   &revoked_ksv_cnt);
29262306a36Sopenharmony_ci	if (ret)
29362306a36Sopenharmony_ci		return ret;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* revoked_ksv_cnt will be zero when above function failed */
29662306a36Sopenharmony_ci	for (i = 0; i < revoked_ksv_cnt; i++)
29762306a36Sopenharmony_ci		for  (j = 0; j < ksv_count; j++)
29862306a36Sopenharmony_ci			if (!memcmp(&ksvs[j * DRM_HDCP_KSV_LEN],
29962306a36Sopenharmony_ci				    &revoked_ksv_list[i * DRM_HDCP_KSV_LEN],
30062306a36Sopenharmony_ci				    DRM_HDCP_KSV_LEN)) {
30162306a36Sopenharmony_ci				DRM_DEBUG("Revoked KSV is ");
30262306a36Sopenharmony_ci				drm_hdcp_print_ksv(&ksvs[j * DRM_HDCP_KSV_LEN]);
30362306a36Sopenharmony_ci				ret++;
30462306a36Sopenharmony_ci			}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	kfree(revoked_ksv_list);
30762306a36Sopenharmony_ci	return ret;
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(drm_hdcp_check_ksvs_revoked);
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_cistatic struct drm_prop_enum_list drm_cp_enum_list[] = {
31262306a36Sopenharmony_ci	{ DRM_MODE_CONTENT_PROTECTION_UNDESIRED, "Undesired" },
31362306a36Sopenharmony_ci	{ DRM_MODE_CONTENT_PROTECTION_DESIRED, "Desired" },
31462306a36Sopenharmony_ci	{ DRM_MODE_CONTENT_PROTECTION_ENABLED, "Enabled" },
31562306a36Sopenharmony_ci};
31662306a36Sopenharmony_ciDRM_ENUM_NAME_FN(drm_get_content_protection_name, drm_cp_enum_list)
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_cistatic struct drm_prop_enum_list drm_hdcp_content_type_enum_list[] = {
31962306a36Sopenharmony_ci	{ DRM_MODE_HDCP_CONTENT_TYPE0, "HDCP Type0" },
32062306a36Sopenharmony_ci	{ DRM_MODE_HDCP_CONTENT_TYPE1, "HDCP Type1" },
32162306a36Sopenharmony_ci};
32262306a36Sopenharmony_ciDRM_ENUM_NAME_FN(drm_get_hdcp_content_type_name,
32362306a36Sopenharmony_ci		 drm_hdcp_content_type_enum_list)
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/**
32662306a36Sopenharmony_ci * drm_connector_attach_content_protection_property - attach content protection
32762306a36Sopenharmony_ci * property
32862306a36Sopenharmony_ci *
32962306a36Sopenharmony_ci * @connector: connector to attach CP property on.
33062306a36Sopenharmony_ci * @hdcp_content_type: is HDCP Content Type property needed for connector
33162306a36Sopenharmony_ci *
33262306a36Sopenharmony_ci * This is used to add support for content protection on select connectors.
33362306a36Sopenharmony_ci * Content Protection is intentionally vague to allow for different underlying
33462306a36Sopenharmony_ci * technologies, however it is most implemented by HDCP.
33562306a36Sopenharmony_ci *
33662306a36Sopenharmony_ci * When hdcp_content_type is true enum property called HDCP Content Type is
33762306a36Sopenharmony_ci * created (if it is not already) and attached to the connector.
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * This property is used for sending the protected content's stream type
34062306a36Sopenharmony_ci * from userspace to kernel on selected connectors. Protected content provider
34162306a36Sopenharmony_ci * will decide their type of their content and declare the same to kernel.
34262306a36Sopenharmony_ci *
34362306a36Sopenharmony_ci * Content type will be used during the HDCP 2.2 authentication.
34462306a36Sopenharmony_ci * Content type will be set to &drm_connector_state.hdcp_content_type.
34562306a36Sopenharmony_ci *
34662306a36Sopenharmony_ci * The content protection will be set to &drm_connector_state.content_protection
34762306a36Sopenharmony_ci *
34862306a36Sopenharmony_ci * When kernel triggered content protection state change like DESIRED->ENABLED
34962306a36Sopenharmony_ci * and ENABLED->DESIRED, will use drm_hdcp_update_content_protection() to update
35062306a36Sopenharmony_ci * the content protection state of a connector.
35162306a36Sopenharmony_ci *
35262306a36Sopenharmony_ci * Returns:
35362306a36Sopenharmony_ci * Zero on success, negative errno on failure.
35462306a36Sopenharmony_ci */
35562306a36Sopenharmony_ciint drm_connector_attach_content_protection_property(
35662306a36Sopenharmony_ci		struct drm_connector *connector, bool hdcp_content_type)
35762306a36Sopenharmony_ci{
35862306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
35962306a36Sopenharmony_ci	struct drm_property *prop =
36062306a36Sopenharmony_ci			dev->mode_config.content_protection_property;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (!prop)
36362306a36Sopenharmony_ci		prop = drm_property_create_enum(dev, 0, "Content Protection",
36462306a36Sopenharmony_ci						drm_cp_enum_list,
36562306a36Sopenharmony_ci						ARRAY_SIZE(drm_cp_enum_list));
36662306a36Sopenharmony_ci	if (!prop)
36762306a36Sopenharmony_ci		return -ENOMEM;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	drm_object_attach_property(&connector->base, prop,
37062306a36Sopenharmony_ci				   DRM_MODE_CONTENT_PROTECTION_UNDESIRED);
37162306a36Sopenharmony_ci	dev->mode_config.content_protection_property = prop;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	if (!hdcp_content_type)
37462306a36Sopenharmony_ci		return 0;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	prop = dev->mode_config.hdcp_content_type_property;
37762306a36Sopenharmony_ci	if (!prop)
37862306a36Sopenharmony_ci		prop = drm_property_create_enum(dev, 0, "HDCP Content Type",
37962306a36Sopenharmony_ci					drm_hdcp_content_type_enum_list,
38062306a36Sopenharmony_ci					ARRAY_SIZE(
38162306a36Sopenharmony_ci					drm_hdcp_content_type_enum_list));
38262306a36Sopenharmony_ci	if (!prop)
38362306a36Sopenharmony_ci		return -ENOMEM;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	drm_object_attach_property(&connector->base, prop,
38662306a36Sopenharmony_ci				   DRM_MODE_HDCP_CONTENT_TYPE0);
38762306a36Sopenharmony_ci	dev->mode_config.hdcp_content_type_property = prop;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	return 0;
39062306a36Sopenharmony_ci}
39162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_connector_attach_content_protection_property);
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci/**
39462306a36Sopenharmony_ci * drm_hdcp_update_content_protection - Updates the content protection state
39562306a36Sopenharmony_ci * of a connector
39662306a36Sopenharmony_ci *
39762306a36Sopenharmony_ci * @connector: drm_connector on which content protection state needs an update
39862306a36Sopenharmony_ci * @val: New state of the content protection property
39962306a36Sopenharmony_ci *
40062306a36Sopenharmony_ci * This function can be used by display drivers, to update the kernel triggered
40162306a36Sopenharmony_ci * content protection state changes of a drm_connector such as DESIRED->ENABLED
40262306a36Sopenharmony_ci * and ENABLED->DESIRED. No uevent for DESIRED->UNDESIRED or ENABLED->UNDESIRED,
40362306a36Sopenharmony_ci * as userspace is triggering such state change and kernel performs it without
40462306a36Sopenharmony_ci * fail.This function update the new state of the property into the connector's
40562306a36Sopenharmony_ci * state and generate an uevent to notify the userspace.
40662306a36Sopenharmony_ci */
40762306a36Sopenharmony_civoid drm_hdcp_update_content_protection(struct drm_connector *connector,
40862306a36Sopenharmony_ci					u64 val)
40962306a36Sopenharmony_ci{
41062306a36Sopenharmony_ci	struct drm_device *dev = connector->dev;
41162306a36Sopenharmony_ci	struct drm_connector_state *state = connector->state;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
41462306a36Sopenharmony_ci	if (state->content_protection == val)
41562306a36Sopenharmony_ci		return;
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	state->content_protection = val;
41862306a36Sopenharmony_ci	drm_sysfs_connector_property_event(connector,
41962306a36Sopenharmony_ci					   dev->mode_config.content_protection_property);
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_hdcp_update_content_protection);
422