/* * Copyright 2019 Advanced Micro Devices, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: AMD * */ #include "amdgpu_dm_hdcp.h" #include "amdgpu.h" #include "amdgpu_dm.h" #include "dm_helpers.h" #include <drm/display/drm_hdcp_helper.h> #include "hdcp_psp.h" /* * If the SRM version being loaded is less than or equal to the * currently loaded SRM, psp will return 0xFFFF as the version */ #define PSP_SRM_VERSION_MAX … static bool lp_write_i2c(void *handle, uint32_t address, const uint8_t *data, uint32_t size) { … } static bool lp_read_i2c(void *handle, uint32_t address, uint8_t offset, uint8_t *data, uint32_t size) { … } static bool lp_write_dpcd(void *handle, uint32_t address, const uint8_t *data, uint32_t size) { … } static bool lp_read_dpcd(void *handle, uint32_t address, uint8_t *data, uint32_t size) { … } static uint8_t *psp_get_srm(struct psp_context *psp, uint32_t *srm_version, uint32_t *srm_size) { … } static int psp_set_srm(struct psp_context *psp, u8 *srm, uint32_t srm_size, uint32_t *srm_version) { … } static void process_output(struct hdcp_workqueue *hdcp_work) { … } static void link_lock(struct hdcp_workqueue *work, bool lock) { … } void hdcp_update_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index, struct amdgpu_dm_connector *aconnector, u8 content_type, bool enable_encryption) { … } static void hdcp_remove_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index, struct amdgpu_dm_connector *aconnector) { … } void hdcp_reset_display(struct hdcp_workqueue *hdcp_work, unsigned int link_index) { … } void hdcp_handle_cpirq(struct hdcp_workqueue *hdcp_work, unsigned int link_index) { … } static void event_callback(struct work_struct *work) { … } static void event_property_update(struct work_struct *work) { … } static void event_property_validate(struct work_struct *work) { … } static void event_watchdog_timer(struct work_struct *work) { … } static void event_cpirq(struct work_struct *work) { … } void hdcp_destroy(struct kobject *kobj, struct hdcp_workqueue *hdcp_work) { … } static bool enable_assr(void *handle, struct dc_link *link) { … } static void update_config(void *handle, struct cp_psp_stream_config *config) { … } /** * DOC: Add sysfs interface for set/get srm * * NOTE: From the usermodes prospective you only need to call write *ONCE*, the kernel * will automatically call once or twice depending on the size * * call: "cat file > /sys/class/drm/card0/device/hdcp_srm" from usermode no matter what the size is * * The kernel can only send PAGE_SIZE at once and since MAX_SRM_FILE(5120) > PAGE_SIZE(4096), * srm_data_write can be called multiple times. * * sysfs interface doesn't tell us the size we will get so we are sending partial SRMs to psp and on * the last call we will send the full SRM. PSP will fail on every call before the last. * * This means we don't know if the SRM is good until the last call. And because of this * limitation we cannot throw errors early as it will stop the kernel from writing to sysfs * * Example 1: * Good SRM size = 5096 * first call to write 4096 -> PSP fails * Second call to write 1000 -> PSP Pass -> SRM is set * * Example 2: * Bad SRM size = 4096 * first call to write 4096 -> PSP fails (This is the same as above, but we don't know if this * is the last call) * * Solution?: * 1: Parse the SRM? -> It is signed so we don't know the EOF * 2: We can have another sysfs that passes the size before calling set. -> simpler solution * below * * Easy Solution: * Always call get after Set to verify if set was successful. * +----------------------+ * | Why it works: | * +----------------------+ * PSP will only update its srm if its older than the one we are trying to load. * Always do set first than get. * -if we try to "1. SET" a older version PSP will reject it and we can "2. GET" the newer * version and save it * * -if we try to "1. SET" a newer version PSP will accept it and we can "2. GET" the * same(newer) version back and save it * * -if we try to "1. SET" a newer version and PSP rejects it. That means the format is * incorrect/corrupted and we should correct our SRM by getting it from PSP */ static ssize_t srm_data_write(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { … } static ssize_t srm_data_read(struct file *filp, struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t pos, size_t count) { … } /* From the hdcp spec (5.Renewability) SRM needs to be stored in a non-volatile memory. * * For example, * if Application "A" sets the SRM (ver 2) and we reboot/suspend and later when Application "B" * needs to use HDCP, the version in PSP should be SRM(ver 2). So SRM should be persistent * across boot/reboots/suspend/resume/shutdown * * Currently when the system goes down (suspend/shutdown) the SRM is cleared from PSP. For HDCP * we need to make the SRM persistent. * * -PSP owns the checking of SRM but doesn't have the ability to store it in a non-volatile memory. * -The kernel cannot write to the file systems. * -So we need usermode to do this for us, which is why an interface for usermode is needed * * * * Usermode can read/write to/from PSP using the sysfs interface * For example: * to save SRM from PSP to storage : cat /sys/class/drm/card0/device/hdcp_srm > srmfile * to load from storage to PSP: cat srmfile > /sys/class/drm/card0/device/hdcp_srm */ static const struct bin_attribute data_attr = …; struct hdcp_workqueue *hdcp_create_workqueue(struct amdgpu_device *adev, struct cp_psp *cp_psp, struct dc *dc) { … }