brintos

brintos / linux-shallow public Read only

0
0
Text · 7.9 KiB · 16afeb8 Raw
286 lines · c
1// SPDX-License-Identifier: MIT2/*3 * Copyright 2023, Intel Corporation.4 */5 6#include <drm/intel/i915_hdcp_interface.h>7 8#include "gem/i915_gem_region.h"9#include "gt/intel_gt.h"10#include "gt/uc/intel_gsc_uc_heci_cmd_submit.h"11#include "i915_drv.h"12#include "i915_utils.h"13#include "intel_hdcp_gsc.h"14#include "intel_hdcp_gsc_message.h"15 16struct intel_hdcp_gsc_message {17	struct i915_vma *vma;18	void *hdcp_cmd_in;19	void *hdcp_cmd_out;20};21 22bool intel_hdcp_gsc_cs_required(struct drm_i915_private *i915)23{24	return DISPLAY_VER(i915) >= 14;25}26 27bool intel_hdcp_gsc_check_status(struct drm_i915_private *i915)28{29	struct intel_gt *gt = i915->media_gt;30	struct intel_gsc_uc *gsc = gt ? &gt->uc.gsc : NULL;31 32	if (!gsc || !intel_uc_fw_is_running(&gsc->fw)) {33		drm_dbg_kms(&i915->drm,34			    "GSC components required for HDCP2.2 are not ready\n");35		return false;36	}37 38	return true;39}40 41/*This function helps allocate memory for the command that we will send to gsc cs */42static int intel_hdcp_gsc_initialize_message(struct drm_i915_private *i915,43					     struct intel_hdcp_gsc_message *hdcp_message)44{45	struct intel_gt *gt = i915->media_gt;46	struct drm_i915_gem_object *obj = NULL;47	struct i915_vma *vma = NULL;48	void *cmd_in, *cmd_out;49	int err;50 51	/* allocate object of two page for HDCP command memory and store it */52	obj = i915_gem_object_create_shmem(i915, 2 * PAGE_SIZE);53 54	if (IS_ERR(obj)) {55		drm_err(&i915->drm, "Failed to allocate HDCP streaming command!\n");56		return PTR_ERR(obj);57	}58 59	cmd_in = i915_gem_object_pin_map_unlocked(obj, intel_gt_coherent_map_type(gt, obj, true));60	if (IS_ERR(cmd_in)) {61		drm_err(&i915->drm, "Failed to map gsc message page!\n");62		err = PTR_ERR(cmd_in);63		goto out_unpin;64	}65 66	cmd_out = cmd_in + PAGE_SIZE;67 68	vma = i915_vma_instance(obj, &gt->ggtt->vm, NULL);69	if (IS_ERR(vma)) {70		err = PTR_ERR(vma);71		goto out_unmap;72	}73 74	err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);75	if (err)76		goto out_unmap;77 78	memset(cmd_in, 0, obj->base.size);79 80	hdcp_message->hdcp_cmd_in = cmd_in;81	hdcp_message->hdcp_cmd_out = cmd_out;82	hdcp_message->vma = vma;83 84	return 0;85 86out_unmap:87	i915_gem_object_unpin_map(obj);88out_unpin:89	i915_gem_object_put(obj);90	return err;91}92 93static const struct i915_hdcp_ops gsc_hdcp_ops = {94	.initiate_hdcp2_session = intel_hdcp_gsc_initiate_session,95	.verify_receiver_cert_prepare_km =96				intel_hdcp_gsc_verify_receiver_cert_prepare_km,97	.verify_hprime = intel_hdcp_gsc_verify_hprime,98	.store_pairing_info = intel_hdcp_gsc_store_pairing_info,99	.initiate_locality_check = intel_hdcp_gsc_initiate_locality_check,100	.verify_lprime = intel_hdcp_gsc_verify_lprime,101	.get_session_key = intel_hdcp_gsc_get_session_key,102	.repeater_check_flow_prepare_ack =103				intel_hdcp_gsc_repeater_check_flow_prepare_ack,104	.verify_mprime = intel_hdcp_gsc_verify_mprime,105	.enable_hdcp_authentication = intel_hdcp_gsc_enable_authentication,106	.close_hdcp_session = intel_hdcp_gsc_close_session,107};108 109static int intel_hdcp_gsc_hdcp2_init(struct drm_i915_private *i915)110{111	struct intel_hdcp_gsc_message *hdcp_message;112	int ret;113 114	hdcp_message = kzalloc(sizeof(*hdcp_message), GFP_KERNEL);115 116	if (!hdcp_message)117		return -ENOMEM;118 119	/*120	 * NOTE: No need to lock the comp mutex here as it is already121	 * going to be taken before this function called122	 */123	i915->display.hdcp.hdcp_message = hdcp_message;124	ret = intel_hdcp_gsc_initialize_message(i915, hdcp_message);125 126	if (ret)127		drm_err(&i915->drm, "Could not initialize hdcp_message\n");128 129	return ret;130}131 132static void intel_hdcp_gsc_free_message(struct drm_i915_private *i915)133{134	struct intel_hdcp_gsc_message *hdcp_message =135					i915->display.hdcp.hdcp_message;136 137	hdcp_message->hdcp_cmd_in = NULL;138	hdcp_message->hdcp_cmd_out = NULL;139	i915_vma_unpin_and_release(&hdcp_message->vma, I915_VMA_RELEASE_MAP);140	kfree(hdcp_message);141}142 143int intel_hdcp_gsc_init(struct drm_i915_private *i915)144{145	struct i915_hdcp_arbiter *data;146	int ret;147 148	data = kzalloc(sizeof(struct i915_hdcp_arbiter), GFP_KERNEL);149	if (!data)150		return -ENOMEM;151 152	mutex_lock(&i915->display.hdcp.hdcp_mutex);153	i915->display.hdcp.arbiter = data;154	i915->display.hdcp.arbiter->hdcp_dev = i915->drm.dev;155	i915->display.hdcp.arbiter->ops = &gsc_hdcp_ops;156	ret = intel_hdcp_gsc_hdcp2_init(i915);157	mutex_unlock(&i915->display.hdcp.hdcp_mutex);158 159	return ret;160}161 162void intel_hdcp_gsc_fini(struct drm_i915_private *i915)163{164	intel_hdcp_gsc_free_message(i915);165	kfree(i915->display.hdcp.arbiter);166}167 168static int intel_gsc_send_sync(struct drm_i915_private *i915,169			       struct intel_gsc_mtl_header *header_in,170			       struct intel_gsc_mtl_header *header_out,171			       u64 addr_in, u64 addr_out,172			       size_t msg_out_len)173{174	struct intel_gt *gt = i915->media_gt;175	int ret;176 177	ret = intel_gsc_uc_heci_cmd_submit_packet(&gt->uc.gsc, addr_in,178						  header_in->message_size,179						  addr_out,180						  msg_out_len + sizeof(*header_out));181	if (ret) {182		drm_err(&i915->drm, "failed to send gsc HDCP msg (%d)\n", ret);183		return ret;184	}185 186	/*187	 * Checking validity marker and header status to see if some error has188	 * blocked us from sending message to gsc cs189	 */190	if (header_out->validity_marker != GSC_HECI_VALIDITY_MARKER) {191		drm_err(&i915->drm, "invalid validity marker\n");192		return -EINVAL;193	}194 195	if (header_out->status != 0) {196		drm_err(&i915->drm, "header status indicates error %d\n",197			header_out->status);198		return -EINVAL;199	}200 201	if (header_out->flags & GSC_OUTFLAG_MSG_PENDING) {202		header_in->gsc_message_handle = header_out->gsc_message_handle;203		return -EAGAIN;204	}205 206	return 0;207}208 209/*210 * This function can now be used for sending requests and will also handle211 * receipt of reply messages hence no different function of message retrieval212 * is required. We will initialize intel_hdcp_gsc_message structure then add213 * gsc cs memory header as stated in specs after which the normal HDCP payload214 * will follow215 */216ssize_t intel_hdcp_gsc_msg_send(struct drm_i915_private *i915, u8 *msg_in,217				size_t msg_in_len, u8 *msg_out,218				size_t msg_out_len)219{220	struct intel_gt *gt = i915->media_gt;221	struct intel_gsc_mtl_header *header_in, *header_out;222	const size_t max_msg_size = PAGE_SIZE - sizeof(*header_in);223	struct intel_hdcp_gsc_message *hdcp_message;224	u64 addr_in, addr_out, host_session_id;225	u32 reply_size, msg_size_in, msg_size_out;226	int ret, tries = 0;227 228	if (!intel_uc_uses_gsc_uc(&gt->uc))229		return -ENODEV;230 231	if (msg_in_len > max_msg_size || msg_out_len > max_msg_size)232		return -ENOSPC;233 234	msg_size_in = msg_in_len + sizeof(*header_in);235	msg_size_out = msg_out_len + sizeof(*header_out);236	hdcp_message = i915->display.hdcp.hdcp_message;237	header_in = hdcp_message->hdcp_cmd_in;238	header_out = hdcp_message->hdcp_cmd_out;239	addr_in = i915_ggtt_offset(hdcp_message->vma);240	addr_out = addr_in + PAGE_SIZE;241 242	memset(header_in, 0, msg_size_in);243	memset(header_out, 0, msg_size_out);244	get_random_bytes(&host_session_id, sizeof(u64));245	intel_gsc_uc_heci_cmd_emit_mtl_header(header_in, HECI_MEADDRESS_HDCP,246					      msg_size_in, host_session_id);247	memcpy(hdcp_message->hdcp_cmd_in + sizeof(*header_in), msg_in, msg_in_len);248 249	/*250	 * Keep sending request in case the pending bit is set no need to add251	 * message handle as we are using same address hence loc. of header is252	 * same and it will contain the message handle. we will send the message253	 * 20 times each message 50 ms apart254	 */255	do {256		ret = intel_gsc_send_sync(i915, header_in, header_out, addr_in,257					  addr_out, msg_out_len);258 259		/* Only try again if gsc says so */260		if (ret != -EAGAIN)261			break;262 263		msleep(50);264 265	} while (++tries < 20);266 267	if (ret)268		goto err;269 270	/* we use the same mem for the reply, so header is in the same loc */271	reply_size = header_out->message_size - sizeof(*header_out);272	if (reply_size > msg_out_len) {273		drm_warn(&i915->drm, "caller with insufficient HDCP reply size %u (%d)\n",274			 reply_size, (u32)msg_out_len);275		reply_size = msg_out_len;276	} else if (reply_size != msg_out_len) {277		drm_dbg_kms(&i915->drm, "caller unexpected HCDP reply size %u (%d)\n",278			    reply_size, (u32)msg_out_len);279	}280 281	memcpy(msg_out, hdcp_message->hdcp_cmd_out + sizeof(*header_out), msg_out_len);282 283err:284	return ret;285}286