123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Cadence MHDP8546 DP bridge driver.
- *
- * Copyright (C) 2020 Cadence Design Systems, Inc.
- *
- */
- #include <linux/io.h>
- #include <linux/iopoll.h>
- #include <asm/unaligned.h>
- #include <drm/display/drm_hdcp_helper.h>
- #include "cdns-mhdp8546-hdcp.h"
- static int cdns_mhdp_secure_mailbox_read(struct cdns_mhdp_device *mhdp)
- {
- int ret, empty;
- WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex));
- ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_EMPTY,
- empty, !empty, MAILBOX_RETRY_US,
- MAILBOX_TIMEOUT_US);
- if (ret < 0)
- return ret;
- return readl(mhdp->sapb_regs + CDNS_MAILBOX_RX_DATA) & 0xff;
- }
- static int cdns_mhdp_secure_mailbox_write(struct cdns_mhdp_device *mhdp,
- u8 val)
- {
- int ret, full;
- WARN_ON(!mutex_is_locked(&mhdp->mbox_mutex));
- ret = readx_poll_timeout(readl, mhdp->sapb_regs + CDNS_MAILBOX_FULL,
- full, !full, MAILBOX_RETRY_US,
- MAILBOX_TIMEOUT_US);
- if (ret < 0)
- return ret;
- writel(val, mhdp->sapb_regs + CDNS_MAILBOX_TX_DATA);
- return 0;
- }
- static int cdns_mhdp_secure_mailbox_recv_header(struct cdns_mhdp_device *mhdp,
- u8 module_id,
- u8 opcode,
- u16 req_size)
- {
- u32 mbox_size, i;
- u8 header[4];
- int ret;
- /* read the header of the message */
- for (i = 0; i < sizeof(header); i++) {
- ret = cdns_mhdp_secure_mailbox_read(mhdp);
- if (ret < 0)
- return ret;
- header[i] = ret;
- }
- mbox_size = get_unaligned_be16(header + 2);
- if (opcode != header[0] || module_id != header[1] ||
- (opcode != HDCP_TRAN_IS_REC_ID_VALID && req_size != mbox_size)) {
- for (i = 0; i < mbox_size; i++)
- if (cdns_mhdp_secure_mailbox_read(mhdp) < 0)
- break;
- return -EINVAL;
- }
- return 0;
- }
- static int cdns_mhdp_secure_mailbox_recv_data(struct cdns_mhdp_device *mhdp,
- u8 *buff, u16 buff_size)
- {
- int ret;
- u32 i;
- for (i = 0; i < buff_size; i++) {
- ret = cdns_mhdp_secure_mailbox_read(mhdp);
- if (ret < 0)
- return ret;
- buff[i] = ret;
- }
- return 0;
- }
- static int cdns_mhdp_secure_mailbox_send(struct cdns_mhdp_device *mhdp,
- u8 module_id,
- u8 opcode,
- u16 size,
- u8 *message)
- {
- u8 header[4];
- int ret;
- u32 i;
- header[0] = opcode;
- header[1] = module_id;
- put_unaligned_be16(size, header + 2);
- for (i = 0; i < sizeof(header); i++) {
- ret = cdns_mhdp_secure_mailbox_write(mhdp, header[i]);
- if (ret)
- return ret;
- }
- for (i = 0; i < size; i++) {
- ret = cdns_mhdp_secure_mailbox_write(mhdp, message[i]);
- if (ret)
- return ret;
- }
- return 0;
- }
- static int cdns_mhdp_hdcp_get_status(struct cdns_mhdp_device *mhdp,
- u16 *hdcp_port_status)
- {
- u8 hdcp_status[HDCP_STATUS_SIZE];
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP_TRAN_STATUS_CHANGE, 0, NULL);
- if (ret)
- goto err_get_hdcp_status;
- ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP_TRAN_STATUS_CHANGE,
- sizeof(hdcp_status));
- if (ret)
- goto err_get_hdcp_status;
- ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_status,
- sizeof(hdcp_status));
- if (ret)
- goto err_get_hdcp_status;
- *hdcp_port_status = ((u16)(hdcp_status[0] << 8) | hdcp_status[1]);
- err_get_hdcp_status:
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- static u8 cdns_mhdp_hdcp_handle_status(struct cdns_mhdp_device *mhdp,
- u16 status)
- {
- u8 err = GET_HDCP_PORT_STS_LAST_ERR(status);
- if (err)
- dev_dbg(mhdp->dev, "HDCP Error = %d", err);
- return err;
- }
- static int cdns_mhdp_hdcp_rx_id_valid_response(struct cdns_mhdp_device *mhdp,
- u8 valid)
- {
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP_TRAN_RESPOND_RECEIVER_ID_VALID,
- 1, &valid);
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- static int cdns_mhdp_hdcp_rx_id_valid(struct cdns_mhdp_device *mhdp,
- u8 *recv_num, u8 *hdcp_rx_id)
- {
- u8 rec_id_hdr[2];
- u8 status;
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP_TRAN_IS_REC_ID_VALID, 0, NULL);
- if (ret)
- goto err_rx_id_valid;
- ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP_TRAN_IS_REC_ID_VALID,
- sizeof(status));
- if (ret)
- goto err_rx_id_valid;
- ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, rec_id_hdr, 2);
- if (ret)
- goto err_rx_id_valid;
- *recv_num = rec_id_hdr[0];
- ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, hdcp_rx_id, 5 * *recv_num);
- err_rx_id_valid:
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- static int cdns_mhdp_hdcp_km_stored_resp(struct cdns_mhdp_device *mhdp,
- u32 size, u8 *km)
- {
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP2X_TX_RESPOND_KM, size, km);
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- static int cdns_mhdp_hdcp_tx_is_km_stored(struct cdns_mhdp_device *mhdp,
- u8 *resp, u32 size)
- {
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP2X_TX_IS_KM_STORED, 0, NULL);
- if (ret)
- goto err_is_km_stored;
- ret = cdns_mhdp_secure_mailbox_recv_header(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP2X_TX_IS_KM_STORED,
- size);
- if (ret)
- goto err_is_km_stored;
- ret = cdns_mhdp_secure_mailbox_recv_data(mhdp, resp, size);
- err_is_km_stored:
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- static int cdns_mhdp_hdcp_tx_config(struct cdns_mhdp_device *mhdp,
- u8 hdcp_cfg)
- {
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP_TRAN_CONFIGURATION, 1, &hdcp_cfg);
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- static int cdns_mhdp_hdcp_set_config(struct cdns_mhdp_device *mhdp,
- u8 hdcp_config, bool enable)
- {
- u16 hdcp_port_status;
- u32 ret_event;
- u8 hdcp_cfg;
- int ret;
- hdcp_cfg = hdcp_config | (enable ? 0x04 : 0) |
- (HDCP_CONTENT_TYPE_0 << 3);
- cdns_mhdp_hdcp_tx_config(mhdp, hdcp_cfg);
- ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS);
- if (!ret_event)
- return -1;
- ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);
- if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status))
- return -1;
- return 0;
- }
- static int cdns_mhdp_hdcp_auth_check(struct cdns_mhdp_device *mhdp)
- {
- u16 hdcp_port_status;
- u32 ret_event;
- int ret;
- ret_event = cdns_mhdp_wait_for_sw_event(mhdp, CDNS_HDCP_TX_STATUS);
- if (!ret_event)
- return -1;
- ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);
- if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status))
- return -1;
- if (hdcp_port_status & 1) {
- dev_dbg(mhdp->dev, "Authentication completed successfully!\n");
- return 0;
- }
- dev_dbg(mhdp->dev, "Authentication failed\n");
- return -1;
- }
- static int cdns_mhdp_hdcp_check_receviers(struct cdns_mhdp_device *mhdp)
- {
- u8 hdcp_rec_id[HDCP_MAX_RECEIVERS][HDCP_RECEIVER_ID_SIZE_BYTES];
- u8 hdcp_num_rec;
- u32 ret_event;
- ret_event = cdns_mhdp_wait_for_sw_event(mhdp,
- CDNS_HDCP_TX_IS_RCVR_ID_VALID);
- if (!ret_event)
- return -1;
- hdcp_num_rec = 0;
- memset(&hdcp_rec_id, 0, sizeof(hdcp_rec_id));
- cdns_mhdp_hdcp_rx_id_valid(mhdp, &hdcp_num_rec, (u8 *)hdcp_rec_id);
- cdns_mhdp_hdcp_rx_id_valid_response(mhdp, 1);
- return 0;
- }
- static int cdns_mhdp_hdcp_auth_22(struct cdns_mhdp_device *mhdp)
- {
- u8 resp[HDCP_STATUS_SIZE];
- u16 hdcp_port_status;
- u32 ret_event;
- int ret;
- dev_dbg(mhdp->dev, "HDCP: Start 2.2 Authentication\n");
- ret_event = cdns_mhdp_wait_for_sw_event(mhdp,
- CDNS_HDCP2_TX_IS_KM_STORED);
- if (!ret_event)
- return -1;
- if (ret_event & CDNS_HDCP_TX_STATUS) {
- mhdp->sw_events &= ~CDNS_HDCP_TX_STATUS;
- ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);
- if (ret || cdns_mhdp_hdcp_handle_status(mhdp, hdcp_port_status))
- return -1;
- }
- cdns_mhdp_hdcp_tx_is_km_stored(mhdp, resp, sizeof(resp));
- cdns_mhdp_hdcp_km_stored_resp(mhdp, 0, NULL);
- if (cdns_mhdp_hdcp_check_receviers(mhdp))
- return -1;
- return 0;
- }
- static inline int cdns_mhdp_hdcp_auth_14(struct cdns_mhdp_device *mhdp)
- {
- dev_dbg(mhdp->dev, "HDCP: Starting 1.4 Authentication\n");
- return cdns_mhdp_hdcp_check_receviers(mhdp);
- }
- static int cdns_mhdp_hdcp_auth(struct cdns_mhdp_device *mhdp,
- u8 hdcp_config)
- {
- int ret;
- ret = cdns_mhdp_hdcp_set_config(mhdp, hdcp_config, true);
- if (ret)
- goto auth_failed;
- if (hdcp_config == HDCP_TX_1)
- ret = cdns_mhdp_hdcp_auth_14(mhdp);
- else
- ret = cdns_mhdp_hdcp_auth_22(mhdp);
- if (ret)
- goto auth_failed;
- ret = cdns_mhdp_hdcp_auth_check(mhdp);
- if (ret)
- ret = cdns_mhdp_hdcp_auth_check(mhdp);
- auth_failed:
- return ret;
- }
- static int _cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp)
- {
- int ret;
- dev_dbg(mhdp->dev, "[%s:%d] HDCP is being disabled...\n",
- mhdp->connector.name, mhdp->connector.base.id);
- ret = cdns_mhdp_hdcp_set_config(mhdp, 0, false);
- return ret;
- }
- static int _cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type)
- {
- int ret, tries = 3;
- u32 i;
- for (i = 0; i < tries; i++) {
- if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0 ||
- content_type == DRM_MODE_HDCP_CONTENT_TYPE1) {
- ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_2);
- if (!ret)
- return 0;
- _cdns_mhdp_hdcp_disable(mhdp);
- }
- if (content_type == DRM_MODE_HDCP_CONTENT_TYPE0) {
- ret = cdns_mhdp_hdcp_auth(mhdp, HDCP_TX_1);
- if (!ret)
- return 0;
- _cdns_mhdp_hdcp_disable(mhdp);
- }
- }
- dev_err(mhdp->dev, "HDCP authentication failed (%d tries/%d)\n",
- tries, ret);
- return ret;
- }
- static int cdns_mhdp_hdcp_check_link(struct cdns_mhdp_device *mhdp)
- {
- u16 hdcp_port_status;
- int ret = 0;
- mutex_lock(&mhdp->hdcp.mutex);
- if (mhdp->hdcp.value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
- goto out;
- ret = cdns_mhdp_hdcp_get_status(mhdp, &hdcp_port_status);
- if (!ret && hdcp_port_status & HDCP_PORT_STS_AUTH)
- goto out;
- dev_err(mhdp->dev,
- "[%s:%d] HDCP link failed, retrying authentication\n",
- mhdp->connector.name, mhdp->connector.base.id);
- ret = _cdns_mhdp_hdcp_disable(mhdp);
- if (ret) {
- mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&mhdp->hdcp.prop_work);
- goto out;
- }
- ret = _cdns_mhdp_hdcp_enable(mhdp, mhdp->hdcp.hdcp_content_type);
- if (ret) {
- mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_DESIRED;
- schedule_work(&mhdp->hdcp.prop_work);
- }
- out:
- mutex_unlock(&mhdp->hdcp.mutex);
- return ret;
- }
- static void cdns_mhdp_hdcp_check_work(struct work_struct *work)
- {
- struct delayed_work *d_work = to_delayed_work(work);
- struct cdns_mhdp_hdcp *hdcp = container_of(d_work,
- struct cdns_mhdp_hdcp,
- check_work);
- struct cdns_mhdp_device *mhdp = container_of(hdcp,
- struct cdns_mhdp_device,
- hdcp);
- if (!cdns_mhdp_hdcp_check_link(mhdp))
- schedule_delayed_work(&hdcp->check_work,
- DRM_HDCP_CHECK_PERIOD_MS);
- }
- static void cdns_mhdp_hdcp_prop_work(struct work_struct *work)
- {
- struct cdns_mhdp_hdcp *hdcp = container_of(work,
- struct cdns_mhdp_hdcp,
- prop_work);
- struct cdns_mhdp_device *mhdp = container_of(hdcp,
- struct cdns_mhdp_device,
- hdcp);
- struct drm_device *dev = mhdp->connector.dev;
- struct drm_connector_state *state;
- drm_modeset_lock(&dev->mode_config.connection_mutex, NULL);
- mutex_lock(&mhdp->hdcp.mutex);
- if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- state = mhdp->connector.state;
- state->content_protection = mhdp->hdcp.value;
- }
- mutex_unlock(&mhdp->hdcp.mutex);
- drm_modeset_unlock(&dev->mode_config.connection_mutex);
- }
- int cdns_mhdp_hdcp_set_lc(struct cdns_mhdp_device *mhdp, u8 *val)
- {
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_GENERAL,
- HDCP_GENERAL_SET_LC_128,
- 16, val);
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- int
- cdns_mhdp_hdcp_set_public_key_param(struct cdns_mhdp_device *mhdp,
- struct cdns_hdcp_tx_public_key_param *val)
- {
- int ret;
- mutex_lock(&mhdp->mbox_mutex);
- ret = cdns_mhdp_secure_mailbox_send(mhdp, MB_MODULE_ID_HDCP_TX,
- HDCP2X_TX_SET_PUBLIC_KEY_PARAMS,
- sizeof(*val), (u8 *)val);
- mutex_unlock(&mhdp->mbox_mutex);
- return ret;
- }
- int cdns_mhdp_hdcp_enable(struct cdns_mhdp_device *mhdp, u8 content_type)
- {
- int ret;
- mutex_lock(&mhdp->hdcp.mutex);
- ret = _cdns_mhdp_hdcp_enable(mhdp, content_type);
- if (ret)
- goto out;
- mhdp->hdcp.hdcp_content_type = content_type;
- mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_ENABLED;
- schedule_work(&mhdp->hdcp.prop_work);
- schedule_delayed_work(&mhdp->hdcp.check_work,
- DRM_HDCP_CHECK_PERIOD_MS);
- out:
- mutex_unlock(&mhdp->hdcp.mutex);
- return ret;
- }
- int cdns_mhdp_hdcp_disable(struct cdns_mhdp_device *mhdp)
- {
- int ret = 0;
- mutex_lock(&mhdp->hdcp.mutex);
- if (mhdp->hdcp.value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- mhdp->hdcp.value = DRM_MODE_CONTENT_PROTECTION_UNDESIRED;
- schedule_work(&mhdp->hdcp.prop_work);
- ret = _cdns_mhdp_hdcp_disable(mhdp);
- }
- mutex_unlock(&mhdp->hdcp.mutex);
- cancel_delayed_work_sync(&mhdp->hdcp.check_work);
- return ret;
- }
- void cdns_mhdp_hdcp_init(struct cdns_mhdp_device *mhdp)
- {
- INIT_DELAYED_WORK(&mhdp->hdcp.check_work, cdns_mhdp_hdcp_check_work);
- INIT_WORK(&mhdp->hdcp.prop_work, cdns_mhdp_hdcp_prop_work);
- mutex_init(&mhdp->hdcp.mutex);
- }
|