linux/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_hdcp.c

/*
 * 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)
{}