Path: blob/master/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-hdcp.c
26516 views
// SPDX-License-Identifier: GPL-2.01/*2* Cadence MHDP8546 DP bridge driver.3*4* Copyright (C) 2020 Cadence Design Systems, Inc.5*6*/78#include <linux/io.h>9#include <linux/iopoll.h>1011#include <linux/unaligned.h>1213#include <drm/display/drm_hdcp_helper.h>1415#include "cdns-mhdp8546-hdcp.h"1617static int cdns_mhdp_secure_mailbox_read(struct cdns_mhdp_device *mhdp)18{19int ret, empty;2021WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex));2223ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_EMPTY,24empty, !empty, MAILBOX_RETRY_US,25MAILBOX_TIMEOUT_US);26if (ret < 0)27return ret;2829return readl(mhdp->sapb_regs + CDNS_MAILBOX_RX_DATA) & 0xff;30}3132static int cdns_mhdp_secure_mailbox_write(struct cdns_mhdp_device *mhdp,33u8 val)34{35int ret, full;3637WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex));3839ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_FULL,40full, !full, MAILBOX_RETRY_US,41MAILBOX_TIMEOUT_US);42if (ret < 0)43return ret;4445writel(val, mhdp->sapb_regs + CDNS_MAILBOX_TX_DATA);4647return 0;48}4950static int cdns_mhdp_secure_mailbox_recv_header(struct cdns_mhdp_device *mhdp,51u8 module_id,52u8 opcode,53u16 req_size)54{55u32 mbox_size, i;56u8 header[4];57int ret;5859/* read the header of the message */60for (i = 0; i < sizeof(header); i++) {61ret = cdns_mhdp_secure_mailbox_read(mhdp);62if (ret < 0)63return ret;6465header[i] = ret;66}6768mbox_size = get_unaligned_be16(header + 2);6970if (opcode != header[0] || module_id != header[1] ||71(opcode != HDCP_TRAN_IS_REC_ID_VALID && req_size != mbox_size)) {72for (i = 0; i < mbox_size; i++)73if (cdns_mhdp_secure_mailbox_read(mhdp) < 0)74break;75return -EINVAL;76}7778return 0;79}8081static int cdns_mhdp_secure_mailbox_recv_data(struct cdns_mhdp_device *mhdp,82u8 *buff, u16 buff_size)83{84int ret;85u32 i;8687for (i = 0; i < buff_size; i++) {88ret = cdns_mhdp_secure_mailbox_read(mhdp);89if (ret < 0)90return ret;9192buff[i] = ret;93}9495return 0;96}9798static int cdns_mhdp_secure_mailbox_send(struct cdns_mhdp_device *mhdp,99u8 module_id,100u8 opcode,101u16 size,102u8 *message)103{104u8 header[4];105int ret;106u32 i;107108header[0] = opcode;109header[1] = module_id;110put_unaligned_be16(size, header + 2);111112for (i = 0; i < sizeof(header); i++) {113ret = cdns_mhdp_secure_mailbox_write(mhdp, header[i]);114if (ret)115return ret;116}117118for (i = 0; i < size; i++) {119ret = cdns_mhdp_secure_mailbox_write(mhdp, message[i]);120if (ret)121return ret;122}123124return 0;125}126127static int cdns_mhdp_hdcp_get_status(struct cdns_mhdp_device *mhdp,128u16 *hdcp_port_status)129{130u8 hdcp_status[HDCP_STATUS_SIZE];131int ret;132133mutex_lock(&mhdp->mbox_mutex);134ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,135HDCP_TRAN_STATUS_CHANGE, 0, NULL);136if (ret)137goto err_get_hdcp_status;138139ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX,140HDCP_TRAN_STATUS_CHANGE,141sizeof(hdcp_status));142if (ret)143goto err_get_hdcp_status;144145ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_status,146sizeof(hdcp_status));147if (ret)148goto err_get_hdcp_status;149150*hdcp_port_status = ((u16)(hdcp_status[0] << 8) | hdcp_status[1]);151152err_get_hdcp_status:153mutex_unlock(&mhdp->mbox_mutex);154155return ret;156}157158static u8 cdns_mhdp_hdcp_handle_status(struct cdns_mhdp_device *mhdp,159u16 status)160{161u8 err = GET_HDCP_PORT_STS_LAST_ERR(status);162163if (err)164dev_dbg(mhdp->dev, "HDCP Error = %d", err);165166return err;167}168169static int cdns_mhdp_hdcp_rx_id_valid_response(struct cdns_mhdp_device *mhdp,170u8 valid)171{172int ret;173174mutex_lock(&mhdp->mbox_mutex);175ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,176HDCP_TRAN_RESPOND_RECEIVER_ID_VALID,1771, &valid);178mutex_unlock(&mhdp->mbox_mutex);179180return ret;181}182183static int cdns_mhdp_hdcp_rx_id_valid(struct cdns_mhdp_device *mhdp,184u8 *recv_num, u8 *hdcp_rx_id)185{186u8 rec_id_hdr[2];187u8 status;188int ret;189190mutex_lock(&mhdp->mbox_mutex);191ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,192HDCP_TRAN_IS_REC_ID_VALID, 0, NULL);193if (ret)194goto err_rx_id_valid;195196ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX,197HDCP_TRAN_IS_REC_ID_VALID,198sizeof(status));199if (ret)200goto err_rx_id_valid;201202ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, rec_id_hdr, 2);203if (ret)204goto err_rx_id_valid;205206*recv_num = rec_id_hdr[0];207208ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_rx_id, 5 * *recv_num);209210err_rx_id_valid:211mutex_unlock(&mhdp->mbox_mutex);212213return ret;214}215216static int cdns_mhdp_hdcp_km_stored_resp(struct cdns_mhdp_device *mhdp,217u32 size, u8 *km)218{219int ret;220221mutex_lock(&mhdp->mbox_mutex);222ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,223HDCP2X_TX_RESPOND_KM, size, km);224mutex_unlock(&mhdp->mbox_mutex);225226return ret;227}228229static int cdns_mhdp_hdcp_tx_is_km_stored(struct cdns_mhdp_device *mhdp,230u8 *resp, u32 size)231{232int ret;233234mutex_lock(&mhdp->mbox_mutex);235ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,236HDCP2X_TX_IS_KM_STORED, 0, NULL);237if (ret)238goto err_is_km_stored;239240ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX,241HDCP2X_TX_IS_KM_STORED,242size);243if (ret)244goto err_is_km_stored;245246ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, resp, size);247err_is_km_stored:248mutex_unlock(&mhdp->mbox_mutex);249250return ret;251}252253static int cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device *mhdp,254u8 hdcp_cfg)255{256int ret;257258mutex_lock(&mhdp->mbox_mutex);259ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,260HDCP_TRAN_CONFIGURATION, 1, &hdcp_cfg);261mutex_unlock(&mhdp->mbox_mutex);262263return ret;264}265266static int cdns_mhdp_hdcp_set_config(struct cdns_mhdp_device *mhdp,267u8 hdcp_config, bool enable)268{269u16 hdcp_port_status;270u32 ret_event;271u8 hdcp_cfg;272int ret;273274hdcp_cfg = hdcp_config | (enable ? 0x04 : 0) |275(HDCP_CONTENT_TYPE_0 << 3);276cdns_mhdp_hdcp_tx_config(mhdp, hdcp_cfg);277ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS);278if (!ret_event)279return -1;280281ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);282if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status))283return -1;284285return 0;286}287288static int cdns_mhdp_hdcp_auth_check(struct cdns_mhdp_device *mhdp)289{290u16 hdcp_port_status;291u32 ret_event;292int ret;293294ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS);295if (!ret_event)296return -1;297298ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);299if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status))300return -1;301302if (hdcp_port_status & 1) {303dev_dbg(mhdp->dev, "Authentication completed successfully!\n");304return 0;305}306307dev_dbg(mhdp->dev, "Authentication failed\n");308309return -1;310}311312static int cdns_mhdp_hdcp_check_receviers(struct cdns_mhdp_device *mhdp)313{314u8 hdcp_rec_id[HDCP_MAX_RECEIVERS][HDCP_RECEIVER_ID_SIZE_BYTES];315u8 hdcp_num_rec;316u32 ret_event;317318ret_event = cdns_mhdp_wait_for_sw_event(mhdp,319CDNS_HDCP_TX_IS_RCVR_ID_VALID);320if (!ret_event)321return -1;322323hdcp_num_rec = 0;324memset(&hdcp_rec_id, 0, sizeof(hdcp_rec_id));325cdns_mhdp_hdcp_rx_id_valid(mhdp, &hdcp_num_rec, (u8 *)hdcp_rec_id);326cdns_mhdp_hdcp_rx_id_valid_response(mhdp, 1);327328return 0;329}330331static int cdns_mhdp_hdcp_auth_22(struct cdns_mhdp_device *mhdp)332{333u8 resp[HDCP_STATUS_SIZE];334u16 hdcp_port_status;335u32 ret_event;336int ret;337338dev_dbg(mhdp->dev, "HDCP: Start 2.2 Authentication\n");339ret_event = cdns_mhdp_wait_for_sw_event(mhdp,340CDNS_HDCP2_TX_IS_KM_STORED);341if (!ret_event)342return -1;343344if (ret_event & CDNS_HDCP_TX_STATUS) {345mhdp->sw_events &= ~CDNS_HDCP_TX_STATUS;346ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);347if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status))348return -1;349}350351cdns_mhdp_hdcp_tx_is_km_stored(mhdp, resp, sizeof(resp));352cdns_mhdp_hdcp_km_stored_resp(mhdp, 0, NULL);353354if (cdns_mhdp_hdcp_check_receviers(mhdp))355return -1;356357return 0;358}359360static inline int cdns_mhdp_hdcp_auth_14(struct cdns_mhdp_device *mhdp)361{362dev_dbg(mhdp->dev, "HDCP: Starting 1.4 Authentication\n");363return cdns_mhdp_hdcp_check_receviers(mhdp);364}365366static int cdns_mhdp_hdcp_auth(struct cdns_mhdp_device *mhdp,367u8 hdcp_config)368{369int ret;370371ret = cdns_mhdp_hdcp_set_config(mhdp, hdcp_config, true);372if (ret)373goto auth_failed;374375if (hdcp_config == HDCP_TX_1)376ret = cdns_mhdp_hdcp_auth_14(mhdp);377else378ret = cdns_mhdp_hdcp_auth_22(mhdp);379380if (ret)381goto auth_failed;382383ret = cdns_mhdp_hdcp_auth_check(mhdp);384if (ret)385ret = cdns_mhdp_hdcp_auth_check(mhdp);386387auth_failed:388return ret;389}390391static int _cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp)392{393int ret;394395dev_dbg(mhdp->dev, "[%s:%d] HDCP is being disabled...\n",396mhdp->connector.name, mhdp->connector.base.id);397398ret = cdns_mhdp_hdcp_set_config(mhdp, 0, false);399400return ret;401}402403static int _cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type)404{405int ret = -EINVAL;406int tries = 3;407u32 i;408409for (i = 0; i < tries; i++) {410if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0 ||411content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {412ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_2);413if (!ret)414return 0;415_cdns_mhdp_hdcp_disable(mhdp);416}417418if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {419ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_1);420if (!ret)421return 0;422_cdns_mhdp_hdcp_disable(mhdp);423}424}425426dev_err(mhdp->dev, "HDCP authentication failed (%d tries/%d)\n",427tries, ret);428429return ret;430}431432static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp)433{434u16 hdcp_port_status;435int ret = 0;436437mutex_lock(&mhdp->hdcp.mutex);438if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)439goto out;440441ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);442if (!ret && hdcp_port_status & HDCP_PORT_STS_AUTH)443goto out;444445dev_err(mhdp->dev,446"[%s:%d] HDCP link failed, retrying authentication\n",447mhdp->connector.name, mhdp->connector.base.id);448449ret = _cdns_mhdp_hdcp_disable(mhdp);450if (ret) {451mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED;452schedule_work(&mhdp->hdcp.prop_work);453goto out;454}455456ret = _cdns_mhdp_hdcp_enable(mhdp, mhdp->hdcp.hdcp_content_type);457if (ret) {458mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED;459schedule_work(&mhdp->hdcp.prop_work);460}461out:462mutex_unlock(&mhdp->hdcp.mutex);463return ret;464}465466static void cdns_mhdp_hdcp_check_work(struct work_struct *work)467{468struct delayed_work *d_work = to_delayed_work(work);469struct cdns_mhdp_hdcp *hdcp = container_of(d_work,470struct cdns_mhdp_hdcp,471check_work);472struct cdns_mhdp_device *mhdp = container_of(hdcp,473struct cdns_mhdp_device,474hdcp);475476if (!cdns_mhdp_hdcp_check_link(mhdp))477schedule_delayed_work(&hdcp->check_work,478DRM_HDCP_CHECK_PERIOD_MS);479}480481static void cdns_mhdp_hdcp_prop_work(struct work_struct *work)482{483struct cdns_mhdp_hdcp *hdcp = container_of(work,484struct cdns_mhdp_hdcp,485prop_work);486struct cdns_mhdp_device *mhdp = container_of(hdcp,487struct cdns_mhdp_device,488hdcp);489struct drm_device *dev = mhdp->connector.dev;490struct drm_connector_state *state;491492drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);493mutex_lock(&mhdp->hdcp.mutex);494if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {495state = mhdp->connector.state;496state->content_protection = mhdp->hdcp.value;497}498mutex_unlock(&mhdp->hdcp.mutex);499drm_modeset_unlock(&dev->mode_config.connection_mutex);500}501502int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type)503{504int ret;505506mutex_lock(&mhdp->hdcp.mutex);507ret = _cdns_mhdp_hdcp_enable(mhdp, content_type);508if (ret)509goto out;510511mhdp->hdcp.hdcp_content_type = content_type;512mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_ENABLED;513schedule_work(&mhdp->hdcp.prop_work);514schedule_delayed_work(&mhdp->hdcp.check_work,515DRM_HDCP_CHECK_PERIOD_MS);516out:517mutex_unlock(&mhdp->hdcp.mutex);518return ret;519}520521int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp)522{523int ret = 0;524525mutex_lock(&mhdp->hdcp.mutex);526if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {527mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;528schedule_work(&mhdp->hdcp.prop_work);529ret = _cdns_mhdp_hdcp_disable(mhdp);530}531mutex_unlock(&mhdp->hdcp.mutex);532cancel_delayed_work_sync(&mhdp->hdcp.check_work);533534return ret;535}536537void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp)538{539INIT_DELAYED_WORK(&mhdp->hdcp.check_work, cdns_mhdp_hdcp_check_work);540INIT_WORK(&mhdp->hdcp.prop_work, cdns_mhdp_hdcp_prop_work);541mutex_init(&mhdp->hdcp.mutex);542}543544545