139a00914SJani Nikula // SPDX-License-Identifier: MIT
239a00914SJani Nikula /*
339a00914SJani Nikula * Copyright 2023, Intel Corporation.
439a00914SJani Nikula */
539a00914SJani Nikula
639a00914SJani Nikula #include <drm/drm_print.h>
729fdc6e9SJani Nikula #include <drm/intel/display_parent_interface.h>
839a00914SJani Nikula #include <drm/intel/i915_hdcp_interface.h>
939a00914SJani Nikula
1039a00914SJani Nikula #include "gem/i915_gem_region.h"
1139a00914SJani Nikula #include "gt/intel_gt.h"
1239a00914SJani Nikula #include "gt/uc/intel_gsc_uc_heci_cmd_submit.h"
1339a00914SJani Nikula #include "i915_drv.h"
1429fdc6e9SJani Nikula #include "i915_hdcp_gsc.h"
1539a00914SJani Nikula
1639a00914SJani Nikula struct intel_hdcp_gsc_context {
1739a00914SJani Nikula struct drm_i915_private *i915;
1839a00914SJani Nikula struct i915_vma *vma;
1939a00914SJani Nikula void *hdcp_cmd_in;
2039a00914SJani Nikula void *hdcp_cmd_out;
2139a00914SJani Nikula };
2239a00914SJani Nikula
intel_hdcp_gsc_check_status(struct drm_device * drm)2329fdc6e9SJani Nikula static bool intel_hdcp_gsc_check_status(struct drm_device *drm)
2439a00914SJani Nikula {
2539a00914SJani Nikula struct drm_i915_private *i915 = to_i915(drm);
2639a00914SJani Nikula struct intel_gt *gt = i915->media_gt;
2739a00914SJani Nikula struct intel_gsc_uc *gsc = gt ? >->uc.gsc : NULL;
2839a00914SJani Nikula
2939a00914SJani Nikula if (!gsc || !intel_uc_fw_is_running(&gsc->fw)) {
3039a00914SJani Nikula drm_dbg_kms(&i915->drm,
3139a00914SJani Nikula "GSC components required for HDCP2.2 are not ready\n");
3239a00914SJani Nikula return false;
3339a00914SJani Nikula }
3439a00914SJani Nikula
3539a00914SJani Nikula return true;
3639a00914SJani Nikula }
3739a00914SJani Nikula
3839a00914SJani Nikula /*This function helps allocate memory for the command that we will send to gsc cs */
intel_hdcp_gsc_initialize_message(struct drm_i915_private * i915,struct intel_hdcp_gsc_context * gsc_context)3939a00914SJani Nikula static int intel_hdcp_gsc_initialize_message(struct drm_i915_private *i915,
4039a00914SJani Nikula struct intel_hdcp_gsc_context *gsc_context)
4139a00914SJani Nikula {
4239a00914SJani Nikula struct intel_gt *gt = i915->media_gt;
4339a00914SJani Nikula struct drm_i915_gem_object *obj = NULL;
4439a00914SJani Nikula struct i915_vma *vma = NULL;
4539a00914SJani Nikula void *cmd_in, *cmd_out;
4639a00914SJani Nikula int err;
4739a00914SJani Nikula
4839a00914SJani Nikula /* allocate object of two page for HDCP command memory and store it */
4939a00914SJani Nikula obj = i915_gem_object_create_shmem(i915, 2 * PAGE_SIZE);
5039a00914SJani Nikula
5139a00914SJani Nikula if (IS_ERR(obj)) {
5239a00914SJani Nikula drm_err(&i915->drm, "Failed to allocate HDCP streaming command!\n");
5339a00914SJani Nikula return PTR_ERR(obj);
5439a00914SJani Nikula }
5539a00914SJani Nikula
5639a00914SJani Nikula cmd_in = i915_gem_object_pin_map_unlocked(obj, intel_gt_coherent_map_type(gt, obj, true));
5739a00914SJani Nikula if (IS_ERR(cmd_in)) {
5839a00914SJani Nikula drm_err(&i915->drm, "Failed to map gsc message page!\n");
5939a00914SJani Nikula err = PTR_ERR(cmd_in);
6039a00914SJani Nikula goto out_unpin;
6139a00914SJani Nikula }
6239a00914SJani Nikula
6339a00914SJani Nikula cmd_out = cmd_in + PAGE_SIZE;
6439a00914SJani Nikula
6539a00914SJani Nikula vma = i915_vma_instance(obj, >->ggtt->vm, NULL);
6639a00914SJani Nikula if (IS_ERR(vma)) {
6739a00914SJani Nikula err = PTR_ERR(vma);
6839a00914SJani Nikula goto out_unmap;
6939a00914SJani Nikula }
7039a00914SJani Nikula
7139a00914SJani Nikula err = i915_vma_pin(vma, 0, 0, PIN_GLOBAL | PIN_HIGH);
7239a00914SJani Nikula if (err)
7339a00914SJani Nikula goto out_unmap;
7439a00914SJani Nikula
7539a00914SJani Nikula memset(cmd_in, 0, obj->base.size);
7639a00914SJani Nikula
7739a00914SJani Nikula gsc_context->hdcp_cmd_in = cmd_in;
7839a00914SJani Nikula gsc_context->hdcp_cmd_out = cmd_out;
7939a00914SJani Nikula gsc_context->vma = vma;
8039a00914SJani Nikula gsc_context->i915 = i915;
8139a00914SJani Nikula
8239a00914SJani Nikula return 0;
8339a00914SJani Nikula
8439a00914SJani Nikula out_unmap:
8539a00914SJani Nikula i915_gem_object_unpin_map(obj);
8639a00914SJani Nikula out_unpin:
8739a00914SJani Nikula i915_gem_object_put(obj);
8839a00914SJani Nikula return err;
8939a00914SJani Nikula }
9039a00914SJani Nikula
intel_hdcp_gsc_context_alloc(struct drm_device * drm)9129fdc6e9SJani Nikula static struct intel_hdcp_gsc_context *intel_hdcp_gsc_context_alloc(struct drm_device *drm)
9239a00914SJani Nikula {
9339a00914SJani Nikula struct drm_i915_private *i915 = to_i915(drm);
9439a00914SJani Nikula struct intel_hdcp_gsc_context *gsc_context;
9539a00914SJani Nikula int ret;
9639a00914SJani Nikula
97*bf4afc53SLinus Torvalds gsc_context = kzalloc_obj(*gsc_context);
9839a00914SJani Nikula if (!gsc_context)
9939a00914SJani Nikula return ERR_PTR(-ENOMEM);
10039a00914SJani Nikula
10139a00914SJani Nikula /*
10239a00914SJani Nikula * NOTE: No need to lock the comp mutex here as it is already
10339a00914SJani Nikula * going to be taken before this function called
10439a00914SJani Nikula */
10539a00914SJani Nikula ret = intel_hdcp_gsc_initialize_message(i915, gsc_context);
10639a00914SJani Nikula if (ret) {
10739a00914SJani Nikula drm_err(&i915->drm, "Could not initialize gsc_context\n");
10839a00914SJani Nikula kfree(gsc_context);
10939a00914SJani Nikula gsc_context = ERR_PTR(ret);
11039a00914SJani Nikula }
11139a00914SJani Nikula
11239a00914SJani Nikula return gsc_context;
11339a00914SJani Nikula }
11439a00914SJani Nikula
intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context * gsc_context)11529fdc6e9SJani Nikula static void intel_hdcp_gsc_context_free(struct intel_hdcp_gsc_context *gsc_context)
11639a00914SJani Nikula {
11739a00914SJani Nikula if (!gsc_context)
11839a00914SJani Nikula return;
11939a00914SJani Nikula
12039a00914SJani Nikula i915_vma_unpin_and_release(&gsc_context->vma, I915_VMA_RELEASE_MAP);
12139a00914SJani Nikula kfree(gsc_context);
12239a00914SJani Nikula }
12339a00914SJani Nikula
intel_gsc_send_sync(struct drm_i915_private * i915,struct intel_gsc_mtl_header * header_in,struct intel_gsc_mtl_header * header_out,u64 addr_in,u64 addr_out,size_t msg_out_len)12439a00914SJani Nikula static int intel_gsc_send_sync(struct drm_i915_private *i915,
12539a00914SJani Nikula struct intel_gsc_mtl_header *header_in,
12639a00914SJani Nikula struct intel_gsc_mtl_header *header_out,
12739a00914SJani Nikula u64 addr_in, u64 addr_out,
12839a00914SJani Nikula size_t msg_out_len)
12939a00914SJani Nikula {
13039a00914SJani Nikula struct intel_gt *gt = i915->media_gt;
13139a00914SJani Nikula int ret;
13239a00914SJani Nikula
13339a00914SJani Nikula ret = intel_gsc_uc_heci_cmd_submit_packet(>->uc.gsc, addr_in,
13439a00914SJani Nikula header_in->message_size,
13539a00914SJani Nikula addr_out,
13639a00914SJani Nikula msg_out_len + sizeof(*header_out));
13739a00914SJani Nikula if (ret) {
13839a00914SJani Nikula drm_err(&i915->drm, "failed to send gsc HDCP msg (%d)\n", ret);
13939a00914SJani Nikula return ret;
14039a00914SJani Nikula }
14139a00914SJani Nikula
14239a00914SJani Nikula /*
14339a00914SJani Nikula * Checking validity marker and header status to see if some error has
14439a00914SJani Nikula * blocked us from sending message to gsc cs
14539a00914SJani Nikula */
14639a00914SJani Nikula if (header_out->validity_marker != GSC_HECI_VALIDITY_MARKER) {
14739a00914SJani Nikula drm_err(&i915->drm, "invalid validity marker\n");
14839a00914SJani Nikula return -EINVAL;
14939a00914SJani Nikula }
15039a00914SJani Nikula
15139a00914SJani Nikula if (header_out->status != 0) {
15239a00914SJani Nikula drm_err(&i915->drm, "header status indicates error %d\n",
15339a00914SJani Nikula header_out->status);
15439a00914SJani Nikula return -EINVAL;
15539a00914SJani Nikula }
15639a00914SJani Nikula
15739a00914SJani Nikula if (header_out->flags & GSC_OUTFLAG_MSG_PENDING) {
15839a00914SJani Nikula header_in->gsc_message_handle = header_out->gsc_message_handle;
15939a00914SJani Nikula return -EAGAIN;
16039a00914SJani Nikula }
16139a00914SJani Nikula
16239a00914SJani Nikula return 0;
16339a00914SJani Nikula }
16439a00914SJani Nikula
16539a00914SJani Nikula /*
16639a00914SJani Nikula * This function can now be used for sending requests and will also handle
16739a00914SJani Nikula * receipt of reply messages hence no different function of message retrieval
16839a00914SJani Nikula * is required. We will initialize intel_hdcp_gsc_context structure then add
16939a00914SJani Nikula * gsc cs memory header as stated in specs after which the normal HDCP payload
17039a00914SJani Nikula * will follow
17139a00914SJani Nikula */
intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context * gsc_context,void * msg_in,size_t msg_in_len,void * msg_out,size_t msg_out_len)17229fdc6e9SJani Nikula static ssize_t intel_hdcp_gsc_msg_send(struct intel_hdcp_gsc_context *gsc_context,
17339a00914SJani Nikula void *msg_in, size_t msg_in_len,
17439a00914SJani Nikula void *msg_out, size_t msg_out_len)
17539a00914SJani Nikula {
17639a00914SJani Nikula struct drm_i915_private *i915 = gsc_context->i915;
17739a00914SJani Nikula struct intel_gt *gt = i915->media_gt;
17839a00914SJani Nikula struct intel_gsc_mtl_header *header_in, *header_out;
17939a00914SJani Nikula const size_t max_msg_size = PAGE_SIZE - sizeof(*header_in);
18039a00914SJani Nikula u64 addr_in, addr_out, host_session_id;
18139a00914SJani Nikula u32 reply_size, msg_size_in, msg_size_out;
18239a00914SJani Nikula int ret, tries = 0;
18339a00914SJani Nikula
18439a00914SJani Nikula if (!intel_uc_uses_gsc_uc(>->uc))
18539a00914SJani Nikula return -ENODEV;
18639a00914SJani Nikula
18739a00914SJani Nikula if (msg_in_len > max_msg_size || msg_out_len > max_msg_size)
18839a00914SJani Nikula return -ENOSPC;
18939a00914SJani Nikula
19039a00914SJani Nikula msg_size_in = msg_in_len + sizeof(*header_in);
19139a00914SJani Nikula msg_size_out = msg_out_len + sizeof(*header_out);
19239a00914SJani Nikula header_in = gsc_context->hdcp_cmd_in;
19339a00914SJani Nikula header_out = gsc_context->hdcp_cmd_out;
19439a00914SJani Nikula addr_in = i915_ggtt_offset(gsc_context->vma);
19539a00914SJani Nikula addr_out = addr_in + PAGE_SIZE;
19639a00914SJani Nikula
19739a00914SJani Nikula memset(header_in, 0, msg_size_in);
19839a00914SJani Nikula memset(header_out, 0, msg_size_out);
19939a00914SJani Nikula get_random_bytes(&host_session_id, sizeof(u64));
20039a00914SJani Nikula intel_gsc_uc_heci_cmd_emit_mtl_header(header_in, HECI_MEADDRESS_HDCP,
20139a00914SJani Nikula msg_size_in, host_session_id);
20239a00914SJani Nikula memcpy(gsc_context->hdcp_cmd_in + sizeof(*header_in), msg_in, msg_in_len);
20339a00914SJani Nikula
20439a00914SJani Nikula /*
20539a00914SJani Nikula * Keep sending request in case the pending bit is set no need to add
20639a00914SJani Nikula * message handle as we are using same address hence loc. of header is
20739a00914SJani Nikula * same and it will contain the message handle. we will send the message
20839a00914SJani Nikula * 20 times each message 50 ms apart
20939a00914SJani Nikula */
21039a00914SJani Nikula do {
21139a00914SJani Nikula ret = intel_gsc_send_sync(i915, header_in, header_out, addr_in,
21239a00914SJani Nikula addr_out, msg_out_len);
21339a00914SJani Nikula
21439a00914SJani Nikula /* Only try again if gsc says so */
21539a00914SJani Nikula if (ret != -EAGAIN)
21639a00914SJani Nikula break;
21739a00914SJani Nikula
21839a00914SJani Nikula msleep(50);
21939a00914SJani Nikula
22039a00914SJani Nikula } while (++tries < 20);
22139a00914SJani Nikula
22239a00914SJani Nikula if (ret)
22339a00914SJani Nikula goto err;
22439a00914SJani Nikula
22539a00914SJani Nikula /* we use the same mem for the reply, so header is in the same loc */
22639a00914SJani Nikula reply_size = header_out->message_size - sizeof(*header_out);
22739a00914SJani Nikula if (reply_size > msg_out_len) {
22839a00914SJani Nikula drm_warn(&i915->drm, "caller with insufficient HDCP reply size %u (%d)\n",
22939a00914SJani Nikula reply_size, (u32)msg_out_len);
23039a00914SJani Nikula reply_size = msg_out_len;
23139a00914SJani Nikula } else if (reply_size != msg_out_len) {
23239a00914SJani Nikula drm_dbg_kms(&i915->drm, "caller unexpected HCDP reply size %u (%d)\n",
23339a00914SJani Nikula reply_size, (u32)msg_out_len);
23439a00914SJani Nikula }
23539a00914SJani Nikula
23639a00914SJani Nikula memcpy(msg_out, gsc_context->hdcp_cmd_out + sizeof(*header_out), msg_out_len);
23739a00914SJani Nikula
23839a00914SJani Nikula err:
23939a00914SJani Nikula return ret;
24039a00914SJani Nikula }
24129fdc6e9SJani Nikula
24229fdc6e9SJani Nikula const struct intel_display_hdcp_interface i915_display_hdcp_interface = {
24329fdc6e9SJani Nikula .gsc_msg_send = intel_hdcp_gsc_msg_send,
24429fdc6e9SJani Nikula .gsc_check_status = intel_hdcp_gsc_check_status,
24529fdc6e9SJani Nikula .gsc_context_alloc = intel_hdcp_gsc_context_alloc,
24629fdc6e9SJani Nikula .gsc_context_free = intel_hdcp_gsc_context_free,
24729fdc6e9SJani Nikula };
248