Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/xe/hdcp: Enable HDCP for XE

Enable HDCP for Xe by defining functions which take care of
interaction of HDCP as a client with the GSC CS interface.
Add intel_hdcp_gsc_message to Makefile and add corresponding
changes to xe_hdcp_gsc.c to make it build.

--v2
-add kfree at appropriate place [Daniele]
-remove useless define [Daniele]
-move host session logic to xe_gsc_submit.c [Daniele]
-call xe_gsc_check_and_update_pending directly in an if condition
[Daniele]
-use xe_device instead of drm_i915_private [Daniele]

--v3
-use xe prefix for newly exposed function [Daniele]
-remove client specific defines from intel_gsc_mtl_header [Daniele]
-add missing kfree() [Daniele]
-have NULL check for hdcp_message in finish function [Daniele]
-dont have too many variable declarations in the same line [Daniele]

--v4
-don't point the hdcp_message structure in xe_device to anything
until it properly gets initialized [Daniele]

--v5
-Squash commits for buildability

--v6
-Order includes alphabetically [Lucas]

Signed-off-by: Suraj Kandpal <suraj.kandpal@intel.com>
Reviewed-by: Arun R Murthy <arun.r.murthy@intel.com>
Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240306024247.1857881-6-suraj.kandpal@intel.com

authored by

Suraj Kandpal and committed by
Mika Kahola
152f2df9 4af50beb

+214 -5
+1
drivers/gpu/drm/xe/Makefile
··· 257 257 i915-display/intel_global_state.o \ 258 258 i915-display/intel_gmbus.o \ 259 259 i915-display/intel_hdcp.o \ 260 + i915-display/intel_hdcp_gsc_message.o \ 260 261 i915-display/intel_hdmi.o \ 261 262 i915-display/intel_hotplug.o \ 262 263 i915-display/intel_hotplug_irq.o \
+197 -5
drivers/gpu/drm/xe/display/xe_hdcp_gsc.c
··· 4 4 */ 5 5 6 6 #include <drm/drm_print.h> 7 + #include <drm/i915_hdcp_interface.h> 8 + #include <linux/delay.h> 7 9 10 + #include "abi/gsc_command_header_abi.h" 8 11 #include "intel_hdcp_gsc.h" 9 - #include "xe_device_types.h" 12 + #include "intel_hdcp_gsc_message.h" 13 + #include "xe_bo.h" 10 14 #include "xe_device.h" 11 - #include "xe_gt.h" 15 + #include "xe_device_types.h" 12 16 #include "xe_gsc_proxy.h" 17 + #include "xe_gsc_submit.h" 18 + #include "xe_gt.h" 19 + #include "xe_map.h" 13 20 #include "xe_pm.h" 14 21 #include "xe_uc_fw.h" 22 + 23 + #define HECI_MEADDRESS_HDCP 18 24 + 25 + struct intel_hdcp_gsc_message { 26 + struct xe_bo *hdcp_bo; 27 + u64 hdcp_cmd_in; 28 + u64 hdcp_cmd_out; 29 + }; 30 + 31 + #define HDCP_GSC_HEADER_SIZE sizeof(struct intel_gsc_mtl_header) 15 32 16 33 bool intel_hdcp_gsc_cs_required(struct xe_device *xe) 17 34 { ··· 61 44 return ret; 62 45 } 63 46 47 + /*This function helps allocate memory for the command that we will send to gsc cs */ 48 + static int intel_hdcp_gsc_initialize_message(struct xe_device *xe, 49 + struct intel_hdcp_gsc_message *hdcp_message) 50 + { 51 + struct xe_bo *bo = NULL; 52 + u64 cmd_in, cmd_out; 53 + int ret = 0; 54 + 55 + /* allocate object of two page for HDCP command memory and store it */ 56 + xe_device_mem_access_get(xe); 57 + bo = xe_bo_create_pin_map(xe, xe_device_get_root_tile(xe), NULL, PAGE_SIZE * 2, 58 + ttm_bo_type_kernel, 59 + XE_BO_CREATE_SYSTEM_BIT | 60 + XE_BO_CREATE_GGTT_BIT); 61 + 62 + if (IS_ERR(bo)) { 63 + drm_err(&xe->drm, "Failed to allocate bo for HDCP streaming command!\n"); 64 + ret = PTR_ERR(bo); 65 + goto out; 66 + } 67 + 68 + cmd_in = xe_bo_ggtt_addr(bo); 69 + cmd_out = cmd_in + PAGE_SIZE; 70 + xe_map_memset(xe, &bo->vmap, 0, 0, bo->size); 71 + 72 + hdcp_message->hdcp_bo = bo; 73 + hdcp_message->hdcp_cmd_in = cmd_in; 74 + hdcp_message->hdcp_cmd_out = cmd_out; 75 + out: 76 + xe_device_mem_access_put(xe); 77 + return ret; 78 + } 79 + 80 + static int intel_hdcp_gsc_hdcp2_init(struct xe_device *xe) 81 + { 82 + struct intel_hdcp_gsc_message *hdcp_message; 83 + int ret; 84 + 85 + hdcp_message = kzalloc(sizeof(*hdcp_message), GFP_KERNEL); 86 + 87 + if (!hdcp_message) 88 + return -ENOMEM; 89 + 90 + /* 91 + * NOTE: No need to lock the comp mutex here as it is already 92 + * going to be taken before this function called 93 + */ 94 + ret = intel_hdcp_gsc_initialize_message(xe, hdcp_message); 95 + if (ret) { 96 + drm_err(&xe->drm, "Could not initialize hdcp_message\n"); 97 + kfree(hdcp_message); 98 + return ret; 99 + } 100 + 101 + xe->display.hdcp.hdcp_message = hdcp_message; 102 + return ret; 103 + } 104 + 105 + static const struct i915_hdcp_ops gsc_hdcp_ops = { 106 + .initiate_hdcp2_session = intel_hdcp_gsc_initiate_session, 107 + .verify_receiver_cert_prepare_km = 108 + intel_hdcp_gsc_verify_receiver_cert_prepare_km, 109 + .verify_hprime = intel_hdcp_gsc_verify_hprime, 110 + .store_pairing_info = intel_hdcp_gsc_store_pairing_info, 111 + .initiate_locality_check = intel_hdcp_gsc_initiate_locality_check, 112 + .verify_lprime = intel_hdcp_gsc_verify_lprime, 113 + .get_session_key = intel_hdcp_gsc_get_session_key, 114 + .repeater_check_flow_prepare_ack = 115 + intel_hdcp_gsc_repeater_check_flow_prepare_ack, 116 + .verify_mprime = intel_hdcp_gsc_verify_mprime, 117 + .enable_hdcp_authentication = intel_hdcp_gsc_enable_authentication, 118 + .close_hdcp_session = intel_hdcp_gsc_close_session, 119 + }; 120 + 64 121 int intel_hdcp_gsc_init(struct xe_device *xe) 65 122 { 66 - drm_dbg_kms(&xe->drm, "HDCP support not yet implemented\n"); 67 - return -ENODEV; 123 + struct i915_hdcp_arbiter *data; 124 + int ret; 125 + 126 + data = kzalloc(sizeof(*data), GFP_KERNEL); 127 + if (!data) 128 + return -ENOMEM; 129 + 130 + mutex_lock(&xe->display.hdcp.hdcp_mutex); 131 + xe->display.hdcp.arbiter = data; 132 + xe->display.hdcp.arbiter->hdcp_dev = xe->drm.dev; 133 + xe->display.hdcp.arbiter->ops = &gsc_hdcp_ops; 134 + ret = intel_hdcp_gsc_hdcp2_init(xe); 135 + if (ret) 136 + kfree(data); 137 + 138 + mutex_unlock(&xe->display.hdcp.hdcp_mutex); 139 + 140 + return ret; 68 141 } 69 142 70 143 void intel_hdcp_gsc_fini(struct xe_device *xe) 71 144 { 145 + struct intel_hdcp_gsc_message *hdcp_message = 146 + xe->display.hdcp.hdcp_message; 147 + 148 + if (!hdcp_message) 149 + return; 150 + 151 + xe_bo_unpin_map_no_vm(hdcp_message->hdcp_bo); 152 + kfree(hdcp_message); 153 + } 154 + 155 + static int xe_gsc_send_sync(struct xe_device *xe, 156 + struct intel_hdcp_gsc_message *hdcp_message, 157 + u32 msg_size_in, u32 msg_size_out, 158 + u32 addr_out_off) 159 + { 160 + struct xe_gt *gt = hdcp_message->hdcp_bo->tile->media_gt; 161 + struct iosys_map *map = &hdcp_message->hdcp_bo->vmap; 162 + struct xe_gsc *gsc = &gt->uc.gsc; 163 + int ret; 164 + 165 + ret = xe_gsc_pkt_submit_kernel(gsc, hdcp_message->hdcp_cmd_in, msg_size_in, 166 + hdcp_message->hdcp_cmd_out, msg_size_out); 167 + if (ret) { 168 + drm_err(&xe->drm, "failed to send gsc HDCP msg (%d)\n", ret); 169 + return ret; 170 + } 171 + 172 + if (xe_gsc_check_and_update_pending(xe, map, 0, map, addr_out_off)) 173 + return -EAGAIN; 174 + 175 + ret = xe_gsc_read_out_header(xe, map, addr_out_off, 176 + sizeof(struct hdcp_cmd_header), NULL); 177 + 178 + return ret; 72 179 } 73 180 74 181 ssize_t intel_hdcp_gsc_msg_send(struct xe_device *xe, u8 *msg_in, 75 182 size_t msg_in_len, u8 *msg_out, 76 183 size_t msg_out_len) 77 184 { 78 - return -ENODEV; 185 + const size_t max_msg_size = PAGE_SIZE - HDCP_GSC_HEADER_SIZE; 186 + struct intel_hdcp_gsc_message *hdcp_message; 187 + u64 host_session_id; 188 + u32 msg_size_in, msg_size_out; 189 + u32 addr_out_off, addr_in_wr_off = 0; 190 + int ret, tries = 0; 191 + 192 + if (msg_in_len > max_msg_size || msg_out_len > max_msg_size) { 193 + ret = -ENOSPC; 194 + goto out; 195 + } 196 + 197 + msg_size_in = msg_in_len + HDCP_GSC_HEADER_SIZE; 198 + msg_size_out = msg_out_len + HDCP_GSC_HEADER_SIZE; 199 + hdcp_message = xe->display.hdcp.hdcp_message; 200 + addr_out_off = PAGE_SIZE; 201 + 202 + host_session_id = xe_gsc_create_host_session_id(); 203 + xe_device_mem_access_get(xe); 204 + addr_in_wr_off = xe_gsc_emit_header(xe, &hdcp_message->hdcp_bo->vmap, 205 + addr_in_wr_off, HECI_MEADDRESS_HDCP, 206 + host_session_id, msg_in_len); 207 + xe_map_memcpy_to(xe, &hdcp_message->hdcp_bo->vmap, addr_in_wr_off, 208 + msg_in, msg_in_len); 209 + /* 210 + * Keep sending request in case the pending bit is set no need to add 211 + * message handle as we are using same address hence loc. of header is 212 + * same and it will contain the message handle. we will send the message 213 + * 20 times each message 50 ms apart 214 + */ 215 + do { 216 + ret = xe_gsc_send_sync(xe, hdcp_message, msg_size_in, msg_size_out, 217 + addr_out_off); 218 + 219 + /* Only try again if gsc says so */ 220 + if (ret != -EAGAIN) 221 + break; 222 + 223 + msleep(50); 224 + 225 + } while (++tries < 20); 226 + 227 + if (ret) 228 + goto out; 229 + 230 + xe_map_memcpy_from(xe, msg_out, &hdcp_message->hdcp_bo->vmap, 231 + addr_out_off + HDCP_GSC_HEADER_SIZE, 232 + msg_out_len); 233 + 234 + out: 235 + xe_device_mem_access_put(xe); 236 + return ret; 79 237 }
+15
drivers/gpu/drm/xe/xe_gsc_submit.c
··· 41 41 } 42 42 43 43 /** 44 + * xe_gsc_get_host_session_id - Creates a random 64 bit host_session id with 45 + * bits 56-63 masked. 46 + * 47 + * Returns: random host_session_id which can be used to send messages to gsc cs 48 + */ 49 + u64 xe_gsc_create_host_session_id(void) 50 + { 51 + u64 host_session_id; 52 + 53 + get_random_bytes(&host_session_id, sizeof(u64)); 54 + host_session_id &= ~HOST_SESSION_CLIENT_MASK; 55 + return host_session_id; 56 + } 57 + 58 + /** 44 59 * xe_gsc_emit_header - write the MTL GSC header in memory 45 60 * @xe: the Xe device 46 61 * @map: the iosys map to write to
+1
drivers/gpu/drm/xe/xe_gsc_submit.h
··· 28 28 int xe_gsc_pkt_submit_kernel(struct xe_gsc *gsc, u64 addr_in, u32 size_in, 29 29 u64 addr_out, u32 size_out); 30 30 31 + u64 xe_gsc_create_host_session_id(void); 31 32 #endif