drm/bridge: dw-hdmi-qp: Add hdcp driver
Signed-off-by: Algea Cao <algea.cao@rock-chips.com> Change-Id: Ic71fc6c8345c9e2987e9f2507d7ba4e0eaf90a1e
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_DRM_DW_HDMI) += dw-hdmi.o dw-hdmi-hdcp.o \
|
||||
dw-hdmi-qp.o
|
||||
dw-hdmi-qp.o dw-hdmi-qp-hdcp.o
|
||||
obj-$(CONFIG_DRM_DW_HDMI_AHB_AUDIO) += dw-hdmi-ahb-audio.o
|
||||
obj-$(CONFIG_DRM_DW_HDMI_I2S_AUDIO) += dw-hdmi-i2s-audio.o dw-hdmi-qp-i2s-audio.o
|
||||
obj-$(CONFIG_DRM_DW_HDMI_CEC) += dw-hdmi-cec.o dw-hdmi-qp-cec.o
|
||||
|
||||
@@ -0,0 +1,650 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) Rockchip Electronics Co.Ltd
|
||||
* Author:
|
||||
* Algea Cao <algea.cao@rock-chips.com>
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hdmi.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/soc/rockchip/rk_vendor_storage.h>
|
||||
#include <crypto/sha.h>
|
||||
#include <drm/bridge/dw_hdmi.h>
|
||||
|
||||
#include "dw-hdmi-qp.h"
|
||||
#include "dw-hdmi-qp-hdcp.h"
|
||||
|
||||
#define HDCP_KEY_SIZE 308
|
||||
#define HDCP_KEY_SEED_SIZE 2
|
||||
|
||||
#define KSV_LEN 5
|
||||
#define HEADER 10
|
||||
#define SHAMAX 20
|
||||
|
||||
#define MAX_DOWNSTREAM_DEVICE_NUM 5
|
||||
#define DPK_WR_OK_TIMEOUT_US 30000
|
||||
#define HDMI_HDCP1X_ID 5
|
||||
|
||||
/* HDCP Registers */
|
||||
#define HDMI_HDCPREG_RMCTL 0x780e
|
||||
#define HDMI_HDCPREG_RMSTS 0x780f
|
||||
#define HDMI_HDCPREG_SEED0 0x7810
|
||||
#define HDMI_HDCPREG_SEED1 0x7811
|
||||
#define HDMI_HDCPREG_DPK0 0x7812
|
||||
#define HDMI_HDCPREG_DPK1 0x7813
|
||||
#define HDMI_HDCPREG_DPK2 0x7814
|
||||
#define HDMI_HDCPREG_DPK3 0x7815
|
||||
#define HDMI_HDCPREG_DPK4 0x7816
|
||||
#define HDMI_HDCPREG_DPK5 0x7817
|
||||
#define HDMI_HDCPREG_DPK6 0x7818
|
||||
#define HDMI_HDCP2REG_CTRL 0x7904
|
||||
#define HDMI_HDCP2REG_MASK 0x790c
|
||||
#define HDMI_HDCP2REG_MUTE 0x790e
|
||||
|
||||
enum dw_hdmi_hdcp_state {
|
||||
DW_HDCP_DISABLED,
|
||||
DW_HDCP_AUTH_START,
|
||||
DW_HDCP_AUTH_SUCCESS,
|
||||
DW_HDCP_AUTH_FAIL,
|
||||
};
|
||||
|
||||
enum {
|
||||
DW_HDMI_HDCP_KSV_LEN = 8,
|
||||
DW_HDMI_HDCP_SHA_LEN = 20,
|
||||
DW_HDMI_HDCP_DPK_LEN = 280,
|
||||
DW_HDMI_HDCP_KEY_LEN = 308,
|
||||
DW_HDMI_HDCP_SEED_LEN = 2,
|
||||
};
|
||||
|
||||
enum {
|
||||
HDCP14_R0_TIMER_OVR_EN_MASK = 0x01,
|
||||
HDCP14_R0_TIMER_OVR_EN = 0x01,
|
||||
HDCP14_R0_TIMER_OVR_DISABLE = 0x00,
|
||||
|
||||
HDCP14_RI_TIMER_OVR_EN_MASK = 0x80,
|
||||
HDCP14_RI_TIMER_OVR_EN = 0x80,
|
||||
HDCP14_RI_TIMER_OVR_DISABLE = 0x00,
|
||||
|
||||
HDCP14_R0_TIMER_OVR_VALUE_MASK = 0x1e,
|
||||
HDCP14_RI_TIMER_OVR_VALUE_MASK = 0xff00,
|
||||
|
||||
HDCP14_KEY_WR_OK = 0x100,
|
||||
|
||||
HDCP14_HPD_MASK = 0x01,
|
||||
HDCP14_HPD_EN = 0x01,
|
||||
HDCP14_HPD_DISABLE = 0x00,
|
||||
|
||||
HDCP14_ENCRYPTION_ENABLE_MASK = 0x04,
|
||||
HDCP14_ENCRYPTION_ENABLE = 0x04,
|
||||
HDCP14_ENCRYPTION_DISABLE = 0x04,
|
||||
|
||||
HDCP14_KEY_DECRYPT_EN_MASK = 0x400,
|
||||
HDCP14_KEY_DECRYPT_EN = 0x400,
|
||||
HDCP14_KEY_DECRYPT_DISABLE = 0x00,
|
||||
|
||||
HDMI_A_SRMCTRL_SHA1_FAIL_MASK = 0X08,
|
||||
HDMI_A_SRMCTRL_SHA1_FAIL_DISABLE = 0X00,
|
||||
HDMI_A_SRMCTRL_SHA1_FAIL_ENABLE = 0X08,
|
||||
|
||||
HDMI_A_SRMCTRL_KSV_UPDATE_MASK = 0X04,
|
||||
HDMI_A_SRMCTRL_KSV_UPDATE_DISABLE = 0X00,
|
||||
HDMI_A_SRMCTRL_KSV_UPDATE_ENABLE = 0X04,
|
||||
|
||||
HDMI_A_SRMCTRL_KSV_MEM_REQ_MASK = 0X01,
|
||||
HDMI_A_SRMCTRL_KSV_MEM_REQ_DISABLE = 0X00,
|
||||
HDMI_A_SRMCTRL_KSV_MEM_REQ_ENABLE = 0X01,
|
||||
|
||||
HDMI_A_SRMCTRL_KSV_MEM_ACCESS_MASK = 0X02,
|
||||
HDMI_A_SRMCTRL_KSV_MEM_ACCESS_DISABLE = 0X00,
|
||||
HDMI_A_SRMCTRL_KSV_MEM_ACCESS_ENABLE = 0X02,
|
||||
|
||||
HDMI_A_SRM_BASE_MAX_DEVS_EXCEEDED = 0x80,
|
||||
HDMI_A_SRM_BASE_DEVICE_COUNT = 0x7f,
|
||||
|
||||
HDMI_A_SRM_BASE_MAX_CASCADE_EXCEEDED = 0x08,
|
||||
|
||||
HDMI_A_APIINTSTAT_KSVSHA1_CALC_INT = 0x02,
|
||||
|
||||
/* HDCPREG_RMSTS field values */
|
||||
DPK_WR_OK_STS = 0x40,
|
||||
|
||||
HDMI_A_HDCP22_MASK = 0x40,
|
||||
|
||||
HDMI_HDCP2_OVR_EN_MASK = 0x02,
|
||||
HDMI_HDCP2_OVR_ENABLE = 0x02,
|
||||
HDMI_HDCP2_OVR_DISABLE = 0x00,
|
||||
|
||||
HDMI_HDCP2_FORCE_MASK = 0x04,
|
||||
HDMI_HDCP2_FORCE_ENABLE = 0x04,
|
||||
HDMI_HDCP2_FORCE_DISABLE = 0x00,
|
||||
};
|
||||
|
||||
struct sha_t {
|
||||
u8 mlength[8];
|
||||
u8 mblock[64];
|
||||
int mindex;
|
||||
int mcomputed;
|
||||
int mcorrupted;
|
||||
unsigned int mdigest[5];
|
||||
};
|
||||
|
||||
static inline unsigned int shacircularshift(unsigned int bits,
|
||||
unsigned int word)
|
||||
{
|
||||
return (((word << bits) & 0xFFFFFFFF) | (word >> (32 - bits)));
|
||||
}
|
||||
|
||||
static void hdcp_modb(struct dw_qp_hdcp *hdcp, u32 data, u32 mask, u32 reg)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = hdcp->hdmi;
|
||||
u32 val = hdcp->read(hdmi, reg) & ~mask;
|
||||
|
||||
val |= data & mask;
|
||||
hdcp->write(hdmi, val, reg);
|
||||
}
|
||||
|
||||
static int hdcp_load_keys_cb(struct dw_qp_hdcp *hdcp)
|
||||
{
|
||||
u32 size;
|
||||
u8 hdcp_vendor_data[320];
|
||||
|
||||
hdcp->keys = kmalloc(HDCP_KEY_SIZE, GFP_KERNEL);
|
||||
if (!hdcp->keys)
|
||||
return -ENOMEM;
|
||||
|
||||
hdcp->seeds = kmalloc(HDCP_KEY_SEED_SIZE, GFP_KERNEL);
|
||||
if (!hdcp->seeds) {
|
||||
kfree(hdcp->keys);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
size = rk_vendor_read(HDMI_HDCP1X_ID, hdcp_vendor_data, 314);
|
||||
if (size < (HDCP_KEY_SIZE + HDCP_KEY_SEED_SIZE)) {
|
||||
dev_err(hdcp->dev, "HDCP: read size %d\n", size);
|
||||
memset(hdcp->keys, 0, HDCP_KEY_SIZE);
|
||||
memset(hdcp->seeds, 0, HDCP_KEY_SEED_SIZE);
|
||||
} else {
|
||||
memcpy(hdcp->keys, hdcp_vendor_data, HDCP_KEY_SIZE);
|
||||
memcpy(hdcp->seeds, hdcp_vendor_data + HDCP_KEY_SIZE,
|
||||
HDCP_KEY_SEED_SIZE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdcp_qp_hdcp_load_key(struct dw_qp_hdcp *hdcp)
|
||||
{
|
||||
int i, j;
|
||||
int ret, val;
|
||||
void __iomem *reg_rmsts_addr;
|
||||
struct dw_hdmi_qp_hdcp_keys *hdcp_keys;
|
||||
struct dw_hdmi_qp *hdmi = hdcp->hdmi;
|
||||
u32 ksv, dkl, dkh;
|
||||
|
||||
if (!hdcp->keys) {
|
||||
ret = hdcp_load_keys_cb(hdcp);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
hdcp_keys = hdcp->keys;
|
||||
|
||||
reg_rmsts_addr = hdcp->regs + HDCP14_KEY_STATUS;
|
||||
|
||||
/* hdcp key has been written */
|
||||
if (hdcp->read(hdmi, HDCP14_KEY_STATUS) & 0x3f) {
|
||||
dev_info(hdcp->dev, "hdcp key has been written\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ksv = hdcp_keys->KSV[0] | hdcp_keys->KSV[1] << 8 |
|
||||
hdcp_keys->KSV[2] << 16 | hdcp_keys->KSV[3] << 24;
|
||||
hdcp->write(hdmi, ksv, HDCP14_AKSV_L);
|
||||
|
||||
ksv = hdcp_keys->KSV[4];
|
||||
hdcp->write(hdmi, ksv, HDCP14_AKSV_H);
|
||||
|
||||
if (hdcp->seeds) {
|
||||
hdcp_modb(hdcp, HDCP14_KEY_DECRYPT_EN,
|
||||
HDCP14_KEY_DECRYPT_EN_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
hdcp->write(hdmi, (hdcp->seeds[0] << 8) | hdcp->seeds[1],
|
||||
HDCP14_KEY_SEED);
|
||||
} else {
|
||||
hdcp_modb(hdcp, HDCP14_KEY_DECRYPT_DISABLE,
|
||||
HDCP14_KEY_DECRYPT_EN_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
}
|
||||
|
||||
for (i = 0; i < DW_HDMI_HDCP_DPK_LEN - 6; i += 7) {
|
||||
dkl = 0;
|
||||
dkh = 0;
|
||||
for (j = 0; j < 4; j++)
|
||||
dkl |= hdcp_keys->devicekey[i + j] << (j * 8);
|
||||
for (j = 4; j < 7; j++)
|
||||
dkh |= hdcp_keys->devicekey[i + j] << ((j - 4) * 8);
|
||||
|
||||
hdcp->write(hdmi, dkh, HDCP14_KEY_H);
|
||||
hdcp->write(hdmi, dkl, HDCP14_KEY_L);
|
||||
|
||||
ret = readx_poll_timeout(readl, reg_rmsts_addr, val,
|
||||
val & HDCP14_KEY_WR_OK, 1000,
|
||||
DPK_WR_OK_TIMEOUT_US);
|
||||
if (ret) {
|
||||
dev_err(hdcp->dev, "hdcp key write err\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void dw_hdcp_qp_hdcp_restart(struct dw_qp_hdcp *hdcp)
|
||||
{
|
||||
mutex_lock(&hdcp->mutex);
|
||||
|
||||
if (!hdcp->remaining_times) {
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
hdcp_modb(hdcp, 0, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
|
||||
hdcp->write(hdcp->hdmi, 1, HDCP14_CONFIG1);
|
||||
mdelay(50);
|
||||
hdcp->write(hdcp->hdmi, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N,
|
||||
AVP_1_INT_CLEAR);
|
||||
hdcp_modb(hdcp, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N,
|
||||
HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_MASK_N);
|
||||
|
||||
hdcp_modb(hdcp, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
|
||||
HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
|
||||
hdcp->remaining_times--;
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
}
|
||||
|
||||
static int dw_hdcp_qp_hdcp_start(struct dw_qp_hdcp *hdcp)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = hdcp->hdmi;
|
||||
|
||||
dw_hdcp_qp_hdcp_load_key(hdcp);
|
||||
|
||||
mutex_lock(&hdcp->mutex);
|
||||
hdcp->remaining_times = hdcp->retry_times;
|
||||
|
||||
hdcp->write(hdmi, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_CLEAR);
|
||||
hdcp_modb(hdcp, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N,
|
||||
HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_MASK_N);
|
||||
|
||||
mdelay(50);
|
||||
|
||||
hdcp_modb(hdcp, HDCP14_ENCRYPTION_ENABLE | HDCP14_HPD_EN,
|
||||
HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
|
||||
hdcp->status = DW_HDCP_AUTH_START;
|
||||
dev_info(hdcp->dev, "start hdcp\n");
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
|
||||
queue_work(hdcp->workqueue, &hdcp->work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdcp_qp_hdcp_stop(struct dw_qp_hdcp *hdcp)
|
||||
{
|
||||
mutex_lock(&hdcp->mutex);
|
||||
hdcp_modb(hdcp, 0, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
|
||||
hdcp_modb(hdcp, 0, HDCP14_AUTH_CHG_MASK_N | HDCP14_KSV_LIST_DONE_MASK_N, AVP_1_INT_MASK_N);
|
||||
hdcp->write(hdcp->hdmi, 0, HDCP14_CONFIG1);
|
||||
hdcp->status = DW_HDCP_DISABLED;
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sha_reset(struct sha_t *sha)
|
||||
{
|
||||
u32 i = 0;
|
||||
|
||||
sha->mindex = 0;
|
||||
sha->mcomputed = false;
|
||||
sha->mcorrupted = false;
|
||||
for (i = 0; i < sizeof(sha->mlength); i++)
|
||||
sha->mlength[i] = 0;
|
||||
|
||||
sha1_init(sha->mdigest);
|
||||
}
|
||||
|
||||
static void sha_processblock(struct sha_t *sha)
|
||||
{
|
||||
u32 array[SHA1_WORKSPACE_WORDS];
|
||||
|
||||
sha1_transform(sha->mdigest, sha->mblock, array);
|
||||
sha->mindex = 0;
|
||||
}
|
||||
|
||||
static void sha_padmessage(struct sha_t *sha)
|
||||
{
|
||||
/*
|
||||
* Check to see if the current message block is too small to hold
|
||||
* the initial padding bits and length. If so, we will pad the
|
||||
* block, process it, and then continue padding into a second
|
||||
* block.
|
||||
*/
|
||||
if (sha->mindex > 55) {
|
||||
sha->mblock[sha->mindex++] = 0x80;
|
||||
while (sha->mindex < 64)
|
||||
sha->mblock[sha->mindex++] = 0;
|
||||
|
||||
sha_processblock(sha);
|
||||
while (sha->mindex < 56)
|
||||
sha->mblock[sha->mindex++] = 0;
|
||||
} else {
|
||||
sha->mblock[sha->mindex++] = 0x80;
|
||||
while (sha->mindex < 56)
|
||||
sha->mblock[sha->mindex++] = 0;
|
||||
}
|
||||
|
||||
/* Store the message length as the last 8 octets */
|
||||
sha->mblock[56] = sha->mlength[7];
|
||||
sha->mblock[57] = sha->mlength[6];
|
||||
sha->mblock[58] = sha->mlength[5];
|
||||
sha->mblock[59] = sha->mlength[4];
|
||||
sha->mblock[60] = sha->mlength[3];
|
||||
sha->mblock[61] = sha->mlength[2];
|
||||
sha->mblock[62] = sha->mlength[1];
|
||||
sha->mblock[63] = sha->mlength[0];
|
||||
|
||||
sha_processblock(sha);
|
||||
}
|
||||
|
||||
static int sha_result(struct sha_t *sha)
|
||||
{
|
||||
if (sha->mcorrupted)
|
||||
return false;
|
||||
|
||||
if (sha->mcomputed == 0) {
|
||||
sha_padmessage(sha);
|
||||
sha->mcomputed = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void sha_input(struct sha_t *sha, const u8 *data, u32 size)
|
||||
{
|
||||
int i = 0;
|
||||
unsigned int j = 0;
|
||||
int rc = true;
|
||||
|
||||
if (data == 0 || size == 0)
|
||||
return;
|
||||
|
||||
if (sha->mcomputed || sha->mcorrupted) {
|
||||
sha->mcorrupted = true;
|
||||
return;
|
||||
}
|
||||
while (size-- && !sha->mcorrupted) {
|
||||
sha->mblock[sha->mindex++] = *data;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
rc = true;
|
||||
for (j = 0; j < sizeof(sha->mlength); j++) {
|
||||
sha->mlength[j]++;
|
||||
if (sha->mlength[j] != 0) {
|
||||
rc = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
sha->mcorrupted = (sha->mcorrupted ||
|
||||
rc) ? true : false;
|
||||
}
|
||||
/* if corrupted then message is too long */
|
||||
if (sha->mindex == 64)
|
||||
sha_processblock(sha);
|
||||
data++;
|
||||
}
|
||||
}
|
||||
|
||||
static int hdcp_verify_ksv(const u8 *data, u32 size)
|
||||
{
|
||||
u32 i = 0;
|
||||
struct sha_t sha;
|
||||
|
||||
if ((!data) || (size < (HEADER + SHAMAX)))
|
||||
return false;
|
||||
|
||||
sha_reset(&sha);
|
||||
sha_input(&sha, data, size - SHAMAX);
|
||||
if (sha_result(&sha) == false)
|
||||
return false;
|
||||
|
||||
for (i = 0; i < SHAMAX; i++) {
|
||||
if (data[size - SHAMAX + i] != (u8)(sha.mdigest[i / 4] >> ((i % 4) * 8)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dw_hdcp_qp_hdcp_2nd_auth(struct dw_qp_hdcp *hdcp)
|
||||
{
|
||||
u8 *data;
|
||||
u32 len;
|
||||
|
||||
len = (hdcp->read(hdcp->hdmi, HDCP14_STATUS0) & HDCP14_RPT_DEVICE_COUNT) >> 9;
|
||||
len = len * KSV_LEN + BSTATUS_LEN + M0_LEN + SHAMAX;
|
||||
|
||||
data = kmalloc(len, GFP_KERNEL);
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
hdcp->get_mem(hdcp->hdmi, data, len);
|
||||
|
||||
if (hdcp_verify_ksv(data, len))
|
||||
hdcp->write(hdcp->hdmi, HDCP14_SHA1_MSG_CORRECT_P, HDCP14_CONFIG1);
|
||||
else
|
||||
dw_hdcp_qp_hdcp_restart(hdcp);
|
||||
}
|
||||
|
||||
static void dw_hdcp_qp_hdcp_auth(struct dw_qp_hdcp *hdcp, u32 hdcp_status)
|
||||
{
|
||||
if (!(hdcp_status & BIT(2))) {
|
||||
mutex_lock(&hdcp->mutex);
|
||||
if (hdcp->status == DW_HDCP_DISABLED) {
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
return;
|
||||
}
|
||||
dev_err(hdcp->dev, "hdcp auth failed\n");
|
||||
hdcp_modb(hdcp, 0, HDCP14_ENCRYPTION_ENABLE_MASK | HDCP14_HPD_MASK,
|
||||
HDCP14_CONFIG0);
|
||||
hdcp->status = DW_HDCP_AUTH_FAIL;
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
|
||||
dw_hdcp_qp_hdcp_restart(hdcp);
|
||||
} else {
|
||||
mutex_lock(&hdcp->mutex);
|
||||
dev_info(hdcp->dev, "hdcp auth success\n");
|
||||
hdcp->status = DW_HDCP_AUTH_SUCCESS;
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_hdcp_qp_hdcp_isr(struct dw_qp_hdcp *hdcp, u32 avp_int, u32 hdcp_status)
|
||||
{
|
||||
if (hdcp->status == DW_HDCP_DISABLED)
|
||||
return;
|
||||
|
||||
dev_info(hdcp->dev, "hdcp_int is 0x%02x\n", hdcp_status);
|
||||
|
||||
if (avp_int & HDCP14_KSV_LIST_DONE_MASK_N)
|
||||
dw_hdcp_qp_hdcp_2nd_auth(hdcp);
|
||||
|
||||
if (avp_int & HDCP14_AUTH_CHG_MASK_N)
|
||||
dw_hdcp_qp_hdcp_auth(hdcp, hdcp_status);
|
||||
}
|
||||
|
||||
static ssize_t trytimes_show(struct device *device,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int trytimes = 0;
|
||||
struct dw_qp_hdcp *hdcp = dev_get_drvdata(device);
|
||||
|
||||
if (hdcp)
|
||||
trytimes = hdcp->retry_times;
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d\n", trytimes);
|
||||
}
|
||||
|
||||
static ssize_t trytimes_store(struct device *device,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int trytimes;
|
||||
struct dw_qp_hdcp *hdcp = dev_get_drvdata(device);
|
||||
|
||||
if (!hdcp)
|
||||
return -EINVAL;
|
||||
|
||||
if (kstrtoint(buf, 0, &trytimes))
|
||||
return -EINVAL;
|
||||
|
||||
if (hdcp->retry_times != trytimes) {
|
||||
hdcp->retry_times = trytimes;
|
||||
hdcp->remaining_times = hdcp->retry_times;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RW(trytimes);
|
||||
|
||||
static ssize_t status_show(struct device *device,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
int status = DW_HDCP_DISABLED;
|
||||
struct dw_qp_hdcp *hdcp = dev_get_drvdata(device);
|
||||
|
||||
if (hdcp)
|
||||
status = hdcp->status;
|
||||
|
||||
if (status == DW_HDCP_DISABLED)
|
||||
return snprintf(buf, PAGE_SIZE, "hdcp disable\n");
|
||||
else if (status == DW_HDCP_AUTH_START)
|
||||
return snprintf(buf, PAGE_SIZE, "hdcp_auth_start\n");
|
||||
else if (status == DW_HDCP_AUTH_SUCCESS)
|
||||
return snprintf(buf, PAGE_SIZE, "hdcp_auth_success\n");
|
||||
else if (status == DW_HDCP_AUTH_FAIL)
|
||||
return snprintf(buf, PAGE_SIZE, "hdcp_auth_fail\n");
|
||||
else
|
||||
return snprintf(buf, PAGE_SIZE, "unknown status\n");
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(status);
|
||||
|
||||
static struct attribute *dw_hdmi_qp_hdcp_attrs[] = {
|
||||
&dev_attr_trytimes.attr,
|
||||
&dev_attr_status.attr,
|
||||
NULL
|
||||
};
|
||||
ATTRIBUTE_GROUPS(dw_hdmi_qp_hdcp);
|
||||
|
||||
/* If sink is a repeater, we need to wait ksv list ready */
|
||||
static void dw_hdmi_qp_hdcp(struct work_struct *p_work)
|
||||
{
|
||||
struct dw_qp_hdcp *hdcp = container_of(p_work, struct dw_qp_hdcp, work);
|
||||
u32 val;
|
||||
int i = 500;
|
||||
|
||||
while (i--) {
|
||||
usleep_range(7000, 8000);
|
||||
|
||||
mutex_lock(&hdcp->mutex);
|
||||
if (hdcp->status == DW_HDCP_DISABLED) {
|
||||
dev_dbg(hdcp->dev, "hdcp is disabled, don't wait repeater ready\n");
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
val = hdcp->read(hdcp->hdmi, HDCP14_STATUS1);
|
||||
|
||||
/* sink isn't repeater or ksv fifo ready, stop waiting */
|
||||
if (!(val & HDCP14_RCV_REPEATER) || (val & HDCP14_RCV_KSV_FIFO_READY)) {
|
||||
dev_dbg(hdcp->dev, "wait ksv fifo finished\n");
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_unlock(&hdcp->mutex);
|
||||
}
|
||||
|
||||
if (i < 0) {
|
||||
dev_err(hdcp->dev, "wait repeater ready time out\n");
|
||||
dw_hdcp_qp_hdcp_restart(hdcp);
|
||||
}
|
||||
}
|
||||
|
||||
static int dw_hdcp_qp_hdcp_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct dw_qp_hdcp *hdcp = pdev->dev.platform_data;
|
||||
|
||||
/* retry time if hdcp auth fail. unlimited time if set 0 */
|
||||
hdcp->dev = &pdev->dev;
|
||||
hdcp->hdcp_start = dw_hdcp_qp_hdcp_start;
|
||||
hdcp->hdcp_stop = dw_hdcp_qp_hdcp_stop;
|
||||
hdcp->hdcp_isr = dw_hdcp_qp_hdcp_isr;
|
||||
|
||||
ret = device_add_groups(hdcp->dev, dw_hdmi_qp_hdcp_groups);
|
||||
if (ret) {
|
||||
dev_err(hdcp->dev, "Failed to add sysfs files group\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, hdcp);
|
||||
|
||||
hdcp->workqueue = create_workqueue("hdcp_queue");
|
||||
INIT_WORK(&hdcp->work, dw_hdmi_qp_hdcp);
|
||||
|
||||
hdcp->retry_times = 3;
|
||||
mutex_init(&hdcp->mutex);
|
||||
|
||||
dev_info(hdcp->dev, "%s success\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dw_hdcp_qp_hdcp_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct dw_qp_hdcp *hdcp = pdev->dev.platform_data;
|
||||
|
||||
cancel_work_sync(&hdcp->work);
|
||||
flush_workqueue(hdcp->workqueue);
|
||||
destroy_workqueue(hdcp->workqueue);
|
||||
|
||||
device_remove_groups(hdcp->dev, dw_hdmi_qp_hdcp_groups);
|
||||
kfree(hdcp->keys);
|
||||
kfree(hdcp->seeds);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver dw_hdcp_qp_hdcp_driver = {
|
||||
.probe = dw_hdcp_qp_hdcp_probe,
|
||||
.remove = dw_hdcp_qp_hdcp_remove,
|
||||
.driver = {
|
||||
.name = DW_HDCP_QP_DRIVER_NAME,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(dw_hdcp_qp_hdcp_driver);
|
||||
MODULE_DESCRIPTION("DW HDMI QP transmitter HDCP driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,55 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright (C) Rockchip Electronics Co.Ltd
|
||||
* Author:
|
||||
* Algea Cao <algea.cao@rock-chips.com>
|
||||
*/
|
||||
#ifndef DW_HDMI_QP_HDCP_H
|
||||
#define DW_HDMI_QP_HDCP_H
|
||||
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#define DW_HDCP_QP_DRIVER_NAME "dw-hdmi-qp-hdcp"
|
||||
#define PRIVATE_KEY_SIZE 280
|
||||
#define KEY_SHA_SIZE 20
|
||||
|
||||
#define KSV_LEN 5
|
||||
#define BSTATUS_LEN 2
|
||||
#define M0_LEN 8
|
||||
#define SHAMAX 20
|
||||
|
||||
struct dw_hdmi_qp_hdcp_keys {
|
||||
u8 KSV[8];
|
||||
u8 devicekey[PRIVATE_KEY_SIZE];
|
||||
u8 sha1[KEY_SHA_SIZE];
|
||||
};
|
||||
|
||||
struct dw_qp_hdcp {
|
||||
int retry_times;
|
||||
int remaining_times;
|
||||
char *seeds;
|
||||
int invalidkey;
|
||||
char *invalidkeys;
|
||||
int hdcp2_enable;
|
||||
int status;
|
||||
u32 reg_io_width;
|
||||
|
||||
struct dw_hdmi_qp_hdcp_keys *keys;
|
||||
struct device *dev;
|
||||
struct dw_hdmi_qp *hdmi;
|
||||
void __iomem *regs;
|
||||
|
||||
struct mutex mutex;
|
||||
|
||||
struct work_struct work;
|
||||
struct workqueue_struct *workqueue;
|
||||
|
||||
void (*write)(struct dw_hdmi_qp *hdmi, u32 val, int offset);
|
||||
u32 (*read)(struct dw_hdmi_qp *hdmi, int offset);
|
||||
void (*get_mem)(struct dw_hdmi_qp *hdmi, u8 *data, u32 len);
|
||||
int (*hdcp_start)(struct dw_qp_hdcp *hdcp);
|
||||
int (*hdcp_stop)(struct dw_qp_hdcp *hdcp);
|
||||
void (*hdcp_isr)(struct dw_qp_hdcp *hdcp, u32 avp_int, u32 hdcp_status);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -4,6 +4,7 @@
|
||||
* Author:
|
||||
* Algea Cao <algea.cao@rock-chips.com>
|
||||
*/
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
@@ -25,6 +26,7 @@
|
||||
#include <drm/drm_dsc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_encoder_slave.h>
|
||||
#include <drm/drm_hdcp.h>
|
||||
#include <drm/drm_of.h>
|
||||
#include <drm/drm_panel.h>
|
||||
#include <drm/drm_print.h>
|
||||
@@ -38,6 +40,7 @@
|
||||
#include "dw-hdmi-qp-audio.h"
|
||||
#include "dw-hdmi-qp.h"
|
||||
#include "dw-hdmi-qp-cec.h"
|
||||
#include "dw-hdmi-qp-hdcp.h"
|
||||
|
||||
#include <media/cec-notifier.h>
|
||||
|
||||
@@ -52,6 +55,19 @@
|
||||
#define HDMI14_MAX_TMDSCLK 340000000
|
||||
#define HDMI20_MAX_TMDSCLK_KHZ 600000
|
||||
|
||||
#define HDMI_VH0 0x20
|
||||
#define HDMI_HDCP_ADDR 0x3a
|
||||
#define HDMI_BCAPS 0x40
|
||||
#define HDMI_HDCP14_SUPPORT BIT(7)
|
||||
#define HDMI_HDCP2_VERSION 0x50
|
||||
#define HDMI_HDCP2_SUPPORT BIT(2)
|
||||
|
||||
#define SINK_CAP_HDCP14 BIT(0)
|
||||
#define SINK_CAP_HDCP2 BIT(1)
|
||||
|
||||
#define HDMI_HDCP2_AUTH BIT(1)
|
||||
#define HDMI_HDCP14_AUTH BIT(0)
|
||||
|
||||
static const unsigned int dw_hdmi_cable[] = {
|
||||
EXTCON_DISP_HDMI,
|
||||
EXTCON_NONE,
|
||||
@@ -231,7 +247,7 @@ struct dw_hdmi_qp {
|
||||
|
||||
struct hdmi_qp_data_info hdmi_data;
|
||||
const struct dw_hdmi_plat_data *plat_data;
|
||||
|
||||
struct dw_qp_hdcp *hdcp;
|
||||
int vic;
|
||||
int main_irq;
|
||||
int avp_irq;
|
||||
@@ -250,6 +266,7 @@ struct dw_hdmi_qp {
|
||||
|
||||
struct i2c_adapter *ddc;
|
||||
void __iomem *regs;
|
||||
void __iomem *hdcp14_mem;
|
||||
bool sink_is_hdmi;
|
||||
bool sink_has_audio;
|
||||
bool dclk_en;
|
||||
@@ -270,6 +287,8 @@ struct dw_hdmi_qp {
|
||||
bool rxsense; /* rxsense state */
|
||||
u8 phy_mask; /* desired phy int mask settings */
|
||||
u8 mc_clkdis; /* clock disable register */
|
||||
u8 hdcp_caps;
|
||||
u8 hdcp_status;
|
||||
|
||||
bool update;
|
||||
bool hdr2sdr;
|
||||
@@ -947,8 +966,6 @@ static void dw_hdmi_i2c_init(struct dw_hdmi_qp *hdmi)
|
||||
/* Software reset */
|
||||
hdmi_writel(hdmi, 0x01, I2CM_CONTROL0);
|
||||
|
||||
hdmi_writel(hdmi, 0x085c085c, I2CM_FM_SCL_CONFIG0);
|
||||
|
||||
hdmi_modb(hdmi, 0, I2CM_FM_EN, I2CM_INTERFACE_CONTROL0);
|
||||
|
||||
/* Clear DONE and ERROR interrupts */
|
||||
@@ -1879,6 +1896,58 @@ hdmi_get_tmdsclock(struct dw_hdmi_qp *hdmi, unsigned long mpixelclock)
|
||||
return tmdsclock;
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_hdcp_enable(struct dw_hdmi_qp *hdmi,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
int ret, val;
|
||||
const struct drm_connector_state *conn_state = connector->state;
|
||||
void *data = hdmi->plat_data->phy_data;
|
||||
|
||||
if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_DESIRED)
|
||||
return;
|
||||
|
||||
/* sink support hdcp2.x */
|
||||
if (hdmi->hdcp_caps & SINK_CAP_HDCP2) {
|
||||
hdmi_writel(hdmi, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ, AVP_3_INT_CLEAR);
|
||||
hdmi_modb(hdmi, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ,
|
||||
HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ, AVP_3_INT_MASK_N);
|
||||
|
||||
hdmi_writel(hdmi, 0x35, HDCP2LOGIC_ESM_GPIO_IN);
|
||||
hdmi_modb(hdmi, 0, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
||||
if (hdmi->plat_data->set_hdcp2_enable)
|
||||
hdmi->plat_data->set_hdcp2_enable(data, true);
|
||||
|
||||
/* wait hdcp2.X auth success */
|
||||
ret = regmap_read_poll_timeout(hdmi->regm, HDCP2LOGIC_ESM_GPIO_OUT, val,
|
||||
FIELD_GET(HDCP2_AUTHENTICATION_SUCCESS, val),
|
||||
10000, 2000000);
|
||||
if (ret) {
|
||||
hdmi->hdcp_status &= ~HDMI_HDCP2_AUTH;
|
||||
dev_info(hdmi->dev, "hdcp2 auth failed,start hdcp1.4\n");
|
||||
|
||||
hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN);
|
||||
hdmi_modb(hdmi, HDCP2_BYPASS, HDCP2_BYPASS, HDCP2LOGIC_CONFIG0);
|
||||
|
||||
if (hdmi->plat_data->set_hdcp2_enable)
|
||||
hdmi->plat_data->set_hdcp2_enable(data, false);
|
||||
|
||||
if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
|
||||
hdmi->hdcp->hdcp_start(hdmi->hdcp);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
hdmi->hdcp_status |= HDMI_HDCP2_AUTH;
|
||||
drm_hdcp_update_content_protection(connector, DRM_MODE_CONTENT_PROTECTION_ENABLED);
|
||||
dev_info(hdmi->dev, "HDCP2 authentication succeed\n");
|
||||
} else {
|
||||
if (hdmi->hdcp && hdmi->hdcp->hdcp_start)
|
||||
hdmi->hdcp->hdcp_start(hdmi->hdcp);
|
||||
}
|
||||
exit:
|
||||
if (hdmi->plat_data->set_hdcp_status)
|
||||
hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi,
|
||||
const struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
@@ -2040,6 +2109,7 @@ static int dw_hdmi_qp_setup(struct dw_hdmi_qp *hdmi,
|
||||
dev_info(hdmi->dev, "%s DVI mode\n", __func__);
|
||||
}
|
||||
|
||||
dw_hdmi_qp_hdcp_enable(hdmi, hdmi->curr_conn);
|
||||
hdmi->frl_switch = false;
|
||||
return 0;
|
||||
}
|
||||
@@ -2129,6 +2199,58 @@ static bool dw_hdmi_qp_check_output_type_changed(struct dw_hdmi_qp *hdmi)
|
||||
return false;
|
||||
}
|
||||
|
||||
static ssize_t hdcp_ddc_read(struct i2c_adapter *adapter, u8 address,
|
||||
u8 offset, void *buffer)
|
||||
{
|
||||
int ret;
|
||||
struct i2c_msg msgs[2] = {
|
||||
{
|
||||
.addr = address,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &offset,
|
||||
}, {
|
||||
.addr = address,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = buffer,
|
||||
}
|
||||
};
|
||||
|
||||
ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret != ARRAY_SIZE(msgs))
|
||||
return -EPROTO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 dw_hdmi_qp_hdcp_capable(struct dw_hdmi_qp *hdmi)
|
||||
{
|
||||
u8 version = 0;
|
||||
u8 bcaps;
|
||||
int ret;
|
||||
|
||||
ret = hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_BCAPS, &bcaps);
|
||||
if (ret < 0) {
|
||||
dev_err(hdmi->dev, "get hdcp1.4 capable failed:%d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
if (bcaps & HDMI_HDCP14_SUPPORT)
|
||||
version |= SINK_CAP_HDCP14;
|
||||
|
||||
ret = hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_HDCP2_VERSION, &bcaps);
|
||||
if (ret < 0) {
|
||||
dev_err(hdmi->dev, "get hdcp2.x capable failed:%d\n", ret);
|
||||
return 0;
|
||||
}
|
||||
if (bcaps & HDMI_HDCP2_SUPPORT)
|
||||
version |= SINK_CAP_HDCP2;
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi =
|
||||
@@ -2178,6 +2300,7 @@ static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
|
||||
if (hdmi->plat_data->get_yuv422_format)
|
||||
hdmi->plat_data->get_yuv422_format(connector, edid);
|
||||
dw_hdmi_update_hdr_property(connector);
|
||||
hdmi->hdcp_caps = dw_hdmi_qp_hdcp_capable(hdmi);
|
||||
if (ret > 0 && hdmi->plat_data->split_mode) {
|
||||
struct dw_hdmi_qp *secondary = NULL;
|
||||
void *secondary_data;
|
||||
@@ -2460,6 +2583,37 @@ static bool check_hdr_color_change(struct drm_connector_state *old_state,
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool check_dw_hdcp_state_changed(struct drm_connector *conn,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
struct drm_connector_state *old_state, *new_state;
|
||||
u64 old_cp, new_cp;
|
||||
|
||||
old_state = drm_atomic_get_old_connector_state(state, conn);
|
||||
new_state = drm_atomic_get_new_connector_state(state, conn);
|
||||
old_cp = old_state->content_protection;
|
||||
new_cp = new_state->content_protection;
|
||||
|
||||
if (old_state->hdcp_content_type != new_state->hdcp_content_type &&
|
||||
new_cp != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
|
||||
new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!new_state->crtc) {
|
||||
if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
|
||||
new_state->content_protection = DRM_MODE_CONTENT_PROTECTION_DESIRED;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (old_cp == new_cp ||
|
||||
(old_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
|
||||
new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
|
||||
struct drm_atomic_state *state)
|
||||
{
|
||||
@@ -2605,6 +2759,9 @@ static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
|
||||
}
|
||||
}
|
||||
|
||||
if (check_dw_hdcp_state_changed(connector, state))
|
||||
crtc_state->mode_changed = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2791,6 +2948,7 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = bridge->driver_private;
|
||||
void *data = hdmi->plat_data->phy_data;
|
||||
const struct drm_connector_state *conn_state = hdmi->curr_conn->state;
|
||||
|
||||
if (hdmi->panel)
|
||||
drm_panel_disable(hdmi->panel);
|
||||
@@ -2799,6 +2957,19 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
hdmi_writel(hdmi, 1, PKTSCHED_PKT_CONTROL0);
|
||||
mdelay(50);
|
||||
|
||||
hdmi_modb(hdmi, 0, HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ,
|
||||
AVP_3_INT_MASK_N);
|
||||
if (hdmi->hdcp && hdmi->hdcp->hdcp_stop)
|
||||
hdmi->hdcp->hdcp_stop(hdmi->hdcp);
|
||||
|
||||
hdmi_writel(hdmi, 0, HDCP2LOGIC_ESM_GPIO_IN);
|
||||
if (conn_state->content_protection != DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
|
||||
drm_hdcp_update_content_protection(hdmi->curr_conn,
|
||||
DRM_MODE_CONTENT_PROTECTION_DESIRED);
|
||||
|
||||
if (hdmi->plat_data->set_hdcp_status)
|
||||
hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
|
||||
|
||||
extcon_set_state_sync(hdmi->extcon, EXTCON_DISP_HDMI, false);
|
||||
handle_plugged_change(hdmi, false);
|
||||
mutex_lock(&hdmi->mutex);
|
||||
@@ -2815,7 +2986,7 @@ static void dw_hdmi_qp_bridge_atomic_disable(struct drm_bridge *bridge,
|
||||
hdmi_writel(hdmi, 0, FLT_CONFIG0);
|
||||
hdmi_writel(hdmi, 0, SCRAMB_CONFIG0);
|
||||
/* set sink frl mode disable */
|
||||
if (hdmi->curr_conn && dw_hdmi_support_scdc(hdmi, &hdmi->curr_conn->display_info))
|
||||
if (dw_hdmi_support_scdc(hdmi, &hdmi->curr_conn->display_info))
|
||||
drm_scdc_writeb(hdmi->ddc, 0x31, 0);
|
||||
|
||||
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
|
||||
@@ -2947,17 +3118,77 @@ static irqreturn_t dw_hdmi_qp_main_hardirq(int irq, void *dev_id)
|
||||
static irqreturn_t dw_hdmi_qp_avp_hardirq(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
u32 stat;
|
||||
u32 stat1, stat3;
|
||||
|
||||
stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
|
||||
if (stat) {
|
||||
dev_dbg(hdmi->dev, "HDCP irq %#x\n", stat);
|
||||
stat &= ~stat;
|
||||
hdmi_writel(hdmi, stat, AVP_1_INT_MASK_N);
|
||||
return IRQ_WAKE_THREAD;
|
||||
stat1 = hdmi_readl(hdmi, AVP_1_INT_STATUS);
|
||||
stat3 = hdmi_readl(hdmi, AVP_3_INT_STATUS);
|
||||
|
||||
if (!stat1 && !stat3)
|
||||
return IRQ_NONE;
|
||||
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
struct drm_connector_state *conn_state;
|
||||
void *data = hdmi->plat_data->phy_data;
|
||||
u32 stat1, stat3, val;
|
||||
|
||||
stat1 = hdmi_readl(hdmi, AVP_1_INT_STATUS);
|
||||
stat3 = hdmi_readl(hdmi, AVP_3_INT_STATUS);
|
||||
|
||||
hdmi_writel(hdmi, stat1, AVP_1_INT_CLEAR);
|
||||
hdmi_writel(hdmi, stat3, AVP_3_INT_CLEAR);
|
||||
|
||||
if (!hdmi->curr_conn || !hdmi->curr_conn->state)
|
||||
return IRQ_HANDLED;
|
||||
|
||||
conn_state = hdmi->curr_conn->state;
|
||||
val = conn_state->content_protection;
|
||||
|
||||
if (hdmi->hdcp && hdmi->hdcp->hdcp_isr) {
|
||||
u32 hdcp_status = hdmi_readl(hdmi, HDCP14_STATUS0);
|
||||
|
||||
if (stat1 & HDCP14_AUTH_CHG_MASK_N) {
|
||||
/* hdcp14 auth success */
|
||||
if (hdcp_status & BIT(2)) {
|
||||
hdmi->hdcp_status |= HDMI_HDCP14_AUTH;
|
||||
if (conn_state->content_protection !=
|
||||
DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
|
||||
val = DRM_MODE_CONTENT_PROTECTION_ENABLED;
|
||||
} else if (!(hdcp_status & BIT(2))) {
|
||||
hdmi->hdcp_status &= ~HDMI_HDCP14_AUTH;
|
||||
if (conn_state->content_protection !=
|
||||
DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
|
||||
val = DRM_MODE_CONTENT_PROTECTION_DESIRED;
|
||||
}
|
||||
conn_state->content_protection = val;
|
||||
}
|
||||
hdmi->hdcp->hdcp_isr(hdmi->hdcp, stat1, hdcp_status);
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
if (stat3 & HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ) {
|
||||
stat3 = hdmi_readl(hdmi, HDCP2LOGIC_ESM_GPIO_OUT);
|
||||
if (stat3 & HDCP2_AUTHENTICATION_SUCCESS) {
|
||||
hdmi->hdcp_status |= HDMI_HDCP2_AUTH;
|
||||
if (conn_state->content_protection !=
|
||||
DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
|
||||
val = DRM_MODE_CONTENT_PROTECTION_ENABLED;
|
||||
} else if (!(stat3 & HDCP2_AUTHENTICATION_SUCCESS)) {
|
||||
hdmi->hdcp_status &= ~HDMI_HDCP2_AUTH;
|
||||
if (conn_state->content_protection !=
|
||||
DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
|
||||
val = DRM_MODE_CONTENT_PROTECTION_DESIRED;
|
||||
}
|
||||
conn_state->content_protection = val;
|
||||
}
|
||||
|
||||
if (hdmi->plat_data->set_hdcp_status)
|
||||
hdmi->plat_data->set_hdcp_status(data, hdmi->hdcp_status);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id)
|
||||
@@ -2976,21 +3207,6 @@ static irqreturn_t dw_hdmi_qp_earc_hardirq(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_avp_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
u32 stat;
|
||||
|
||||
stat = hdmi_readl(hdmi, AVP_1_INT_STATUS);
|
||||
|
||||
if (!stat)
|
||||
return IRQ_NONE;
|
||||
|
||||
hdmi_writel(hdmi, stat, AVP_1_INT_CLEAR);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t dw_hdmi_qp_earc_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct dw_hdmi_qp *hdmi = dev_id;
|
||||
@@ -3383,6 +3599,70 @@ static void dw_hdmi_register_debugfs(struct device *dev, struct dw_hdmi_qp *hdmi
|
||||
hdmi, &dw_hdmi_ctrl_fops);
|
||||
}
|
||||
|
||||
static void dw_hdmi_qp_hdcp14_get_mem(struct dw_hdmi_qp *hdmi, u8 *data, u32 len)
|
||||
{
|
||||
u32 ksv_len, i, val;
|
||||
void *hdmi_data = hdmi->plat_data->phy_data;
|
||||
|
||||
if (hdmi->plat_data->set_hdcp14_mem)
|
||||
hdmi->plat_data->set_hdcp14_mem(hdmi_data, true);
|
||||
|
||||
ksv_len = len - BSTATUS_LEN - M0_LEN - SHAMAX;
|
||||
for (i = 0; i < len; i++) {
|
||||
/* read ksv list */
|
||||
if (i < ksv_len)
|
||||
val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_KSV0 + i * 4);
|
||||
/* read bstatus */
|
||||
else if (i < len - SHAMAX - M0_LEN)
|
||||
val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_BSTATUS0 +
|
||||
(i - ksv_len) * 4);
|
||||
/* read M0 */
|
||||
else if (i < len - SHAMAX)
|
||||
val = readl(hdmi->hdcp14_mem + HDMI_HDCP14_MEM_M0_1 +
|
||||
(i - ksv_len - BSTATUS_LEN) * 4);
|
||||
else
|
||||
/* VH0 save in external memory is error, we need to read VH0 via ddc */
|
||||
hdcp_ddc_read(hdmi->ddc, HDMI_HDCP_ADDR, HDMI_VH0 + i - (len - SHAMAX),
|
||||
&val);
|
||||
|
||||
data[i] = val;
|
||||
}
|
||||
|
||||
if (hdmi->plat_data->set_hdcp14_mem)
|
||||
hdmi->plat_data->set_hdcp14_mem(hdmi_data, false);
|
||||
}
|
||||
|
||||
static int dw_hdmi_qp_register_hdcp(struct device *dev,
|
||||
struct dw_hdmi_qp *hdmi)
|
||||
{
|
||||
struct dw_qp_hdcp hdmi_hdcp = {
|
||||
.hdmi = hdmi,
|
||||
.write = hdmi_writel,
|
||||
.read = hdmi_readl,
|
||||
.regs = hdmi->regs,
|
||||
.get_mem = dw_hdmi_qp_hdcp14_get_mem,
|
||||
};
|
||||
struct platform_device_info hdcp_device_info = {
|
||||
.parent = dev,
|
||||
.id = PLATFORM_DEVID_AUTO,
|
||||
.res = NULL,
|
||||
.num_res = 0,
|
||||
.name = DW_HDCP_QP_DRIVER_NAME,
|
||||
.data = &hdmi_hdcp,
|
||||
.size_data = sizeof(hdmi_hdcp),
|
||||
.dma_mask = DMA_BIT_MASK(32),
|
||||
};
|
||||
hdmi->hdcp_dev = platform_device_register_full(&hdcp_device_info);
|
||||
if (IS_ERR(hdmi->hdcp_dev)) {
|
||||
dev_err(dev, "failed to register hdcp!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
hdmi->hdcp = hdmi->hdcp_dev->dev.platform_data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct dw_hdmi_qp *
|
||||
__dw_hdmi_probe(struct platform_device *pdev,
|
||||
const struct dw_hdmi_plat_data *plat_data)
|
||||
@@ -3558,7 +3838,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
||||
hdmi->avp_irq = irq;
|
||||
ret = devm_request_threaded_irq(dev, hdmi->avp_irq,
|
||||
dw_hdmi_qp_avp_hardirq,
|
||||
dw_hdmi_qp_avp_irq, IRQF_SHARED,
|
||||
dw_hdmi_qp_avp_irq, IRQF_ONESHOT,
|
||||
dev_name(dev), hdmi);
|
||||
if (ret)
|
||||
goto err_aud;
|
||||
@@ -3611,6 +3891,20 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
||||
|
||||
dw_hdmi_register_debugfs(dev, hdmi);
|
||||
|
||||
if (hdmi_readl(hdmi, CONFIG_REG) & CONFIG_HDCP14) {
|
||||
iores = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
hdmi->hdcp14_mem = devm_ioremap_resource(dev, iores);
|
||||
|
||||
if (IS_ERR(hdmi->hdcp14_mem)) {
|
||||
ret = PTR_ERR(hdmi->hdcp14_mem);
|
||||
goto err_cec;
|
||||
}
|
||||
|
||||
ret = dw_hdmi_qp_register_hdcp(dev, hdmi);
|
||||
if (ret)
|
||||
goto err_cec;
|
||||
}
|
||||
|
||||
return hdmi;
|
||||
|
||||
err_cec:
|
||||
@@ -3663,6 +3957,8 @@ static void __dw_hdmi_remove(struct dw_hdmi_qp *hdmi)
|
||||
hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder);
|
||||
if (!IS_ERR(hdmi->cec))
|
||||
platform_device_unregister(hdmi->cec);
|
||||
if (!IS_ERR(hdmi->hdcp_dev))
|
||||
platform_device_unregister(hdmi->hdcp_dev);
|
||||
if (hdmi->i2c)
|
||||
i2c_del_adapter(&hdmi->i2c->adap);
|
||||
else
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define CONFIG_REG 0xc
|
||||
#define CONFIG_CEC BIT(28)
|
||||
#define CONFIG_AUD_UD BIT(23)
|
||||
#define CONFIG_HDCP14 BIT(8)
|
||||
#define CORE_TIMESTAMP_HHMM 0x14
|
||||
#define CORE_TIMESTAMP_MMDD 0x18
|
||||
#define CORE_TIMESTAMP_YYYY 0x1c
|
||||
@@ -139,7 +140,7 @@
|
||||
#define FRAME_COMPOSER_CONFIG8 0x860
|
||||
#define FRAME_COMPOSER_CONFIG9 0x864
|
||||
#define KEEPOUT_REKEY_CFG GENMASK(9, 8)
|
||||
#define KEEPOUT_REKEY_ALWAYS 0x2
|
||||
#define KEEPOUT_REKEY_ALWAYS (0x2 << 8)
|
||||
#define FRAME_COMPOSER_CONTROL0 0x86c
|
||||
/* Video Monitor Registers */
|
||||
#define VIDEO_MONITOR_CONFIG0 0x880
|
||||
@@ -155,9 +156,13 @@
|
||||
#define HDCP2_BYPASS BIT(0)
|
||||
#define HDCP2LOGIC_ESM_GPIO_IN 0x8e4
|
||||
#define HDCP2LOGIC_ESM_GPIO_OUT 0x8e8
|
||||
#define HDCP2_AUTHENTICATION_SUCCESS BIT(6)
|
||||
/* HDCP14 Registers */
|
||||
#define HDCP14_CONFIG0 0x900
|
||||
#define HDCP14_OESS_ESSS_OVR_VALUE BIT(14)
|
||||
#define HDCP14_OESS_ESSS_OVR_EN BIT(13)
|
||||
#define HDCP14_CONFIG1 0x904
|
||||
#define HDCP14_SHA1_MSG_CORRECT_P BIT(3)
|
||||
#define HDCP14_CONFIG2 0x908
|
||||
#define HDCP14_CONFIG3 0x90c
|
||||
#define HDCP14_KEY_SEED 0x914
|
||||
@@ -169,7 +174,10 @@
|
||||
#define HDCP14_AN_H 0x92c
|
||||
#define HDCP14_AN_L 0x930
|
||||
#define HDCP14_STATUS0 0x934
|
||||
#define HDCP14_RPT_DEVICE_COUNT 0xFE00
|
||||
#define HDCP14_STATUS1 0x938
|
||||
#define HDCP14_RCV_REPEATER BIT(6)
|
||||
#define HDCP14_RCV_KSV_FIFO_READY BIT(5)
|
||||
/* Scrambler Registers */
|
||||
#define SCRAMB_CONFIG0 0x960
|
||||
/* Video Configuration Registers */
|
||||
@@ -792,6 +800,7 @@
|
||||
#define AVP_1_INT_STATUS 0x3820
|
||||
#define AVP_1_INT_MASK_N 0x3824
|
||||
#define HDCP14_AUTH_CHG_MASK_N BIT(6)
|
||||
#define HDCP14_KSV_LIST_DONE_MASK_N BIT(1)
|
||||
#define AVP_1_INT_CLEAR 0x3828
|
||||
#define AVP_1_INT_FORCE 0x382c
|
||||
#define AVP_2_INT_STATUS 0x3830
|
||||
@@ -802,6 +811,7 @@
|
||||
#define AVP_3_INT_MASK_N 0x3844
|
||||
#define AVP_3_INT_CLEAR 0x3848
|
||||
#define AVP_3_INT_FORCE 0x384c
|
||||
#define HDCP2_ESM_P0_GPIO_OUT_2_CHG_IRQ BIT(17)
|
||||
#define AVP_4_INT_STATUS 0x3850
|
||||
#define AVP_4_INT_MASK_N 0x3854
|
||||
#define AVP_4_INT_CLEAR 0x3858
|
||||
@@ -832,4 +842,9 @@
|
||||
#define EARCRX_1_INT_CLEAR 0x4828
|
||||
#define EARCRX_1_INT_FORCE 0x482c
|
||||
|
||||
#define HDMI_HDCP14_MEM_KSV0 0x4f08
|
||||
#define HDMI_HDCP14_MEM_BSTATUS0 0x5958
|
||||
#define HDMI_HDCP14_MEM_M0_1 0x5960
|
||||
#define HDMI_HDCP14_MEM_M0_7 0x597c
|
||||
|
||||
#endif /* __DW_HDMI_QP_H__ */
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_dsc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_hdcp.h>
|
||||
#include <drm/bridge/dw_hdmi.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include <drm/drm_of.h>
|
||||
@@ -105,6 +106,8 @@
|
||||
#define RK3588_HDMI1_LEVEL_INT BIT(24)
|
||||
#define RK3588_HDMI1_INTR_CHANGE_CNT (0x7 << 21)
|
||||
|
||||
#define RK3588_GRF_VO1_CON1 0x0004
|
||||
#define HDCP1_P1_GPIO_IN BIT(9)
|
||||
#define RK3588_GRF_VO1_CON3 0x000c
|
||||
#define RK3588_COLOR_FORMAT_MASK 0xf
|
||||
#define RK3588_RGB 0
|
||||
@@ -129,6 +132,8 @@
|
||||
#define RK3588_HDMI0_GRANT_SW BIT(11)
|
||||
#define RK3588_HDMI1_GRANT_SEL BIT(12)
|
||||
#define RK3588_HDMI1_GRANT_SW BIT(13)
|
||||
#define RK3588_GRF_VO1_CON4 0x0010
|
||||
#define RK3588_HDMI_HDCP14_MEM_EN BIT(15)
|
||||
#define RK3588_GRF_VO1_CON6 0x0018
|
||||
#define RK3588_GRF_VO1_CON7 0x001c
|
||||
|
||||
@@ -216,6 +221,7 @@ struct rockchip_hdmi {
|
||||
struct drm_property *output_type_capacity;
|
||||
struct drm_property *allm_capacity;
|
||||
struct drm_property *allm_enable;
|
||||
struct drm_property *hdcp_state_property;
|
||||
|
||||
struct drm_property_blob *hdr_panel_blob_ptr;
|
||||
struct drm_property_blob *next_hdr_data_ptr;
|
||||
@@ -232,6 +238,7 @@ struct rockchip_hdmi {
|
||||
u8 max_lanes;
|
||||
u8 add_func;
|
||||
u8 edid_colorimetry;
|
||||
u8 hdcp_status;
|
||||
struct rockchip_drm_dsc_cap dsc_cap;
|
||||
struct next_hdr_sink_data next_hdr_data;
|
||||
struct dw_hdmi_link_config link_cfg;
|
||||
@@ -1889,6 +1896,26 @@ static void rk3588_set_color_format(struct rockchip_hdmi *hdmi, u64 bus_format,
|
||||
regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON6, val);
|
||||
}
|
||||
|
||||
static void rk3588_set_hdcp_status(void *data, u8 status)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
|
||||
hdmi->hdcp_status = status;
|
||||
}
|
||||
|
||||
static void rk3588_set_hdcp2_enable(void *data, bool enable)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
u32 val;
|
||||
|
||||
if (enable)
|
||||
val = HIWORD_UPDATE(HDCP1_P1_GPIO_IN, HDCP1_P1_GPIO_IN);
|
||||
else
|
||||
val = HIWORD_UPDATE(0, HDCP1_P1_GPIO_IN);
|
||||
|
||||
regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON1, val);
|
||||
}
|
||||
|
||||
static void rk3588_set_grf_cfg(void *data)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
@@ -2562,6 +2589,18 @@ static void dw_hdmi_rockchip_set_ddc_io(void *data, bool enable)
|
||||
}
|
||||
}
|
||||
|
||||
static void dw_hdmi_rockchip_set_hdcp14_mem(void *data, bool enable)
|
||||
{
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
u32 val;
|
||||
|
||||
val = HIWORD_UPDATE(enable << 15, RK3588_HDMI_HDCP14_MEM_EN);
|
||||
if (!hdmi->id)
|
||||
regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON4, val);
|
||||
else
|
||||
regmap_write(hdmi->vo1_regmap, RK3588_GRF_VO1_CON7, val);
|
||||
}
|
||||
|
||||
static const struct drm_prop_enum_list color_depth_enum_list[] = {
|
||||
{ 0, "Automatic" }, /* Prefer highest color depth */
|
||||
{ 8, "24bit" },
|
||||
@@ -2608,6 +2647,7 @@ dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
|
||||
struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data;
|
||||
struct drm_property *prop;
|
||||
struct rockchip_drm_private *private = connector->dev->dev_private;
|
||||
int ret;
|
||||
|
||||
switch (color) {
|
||||
case MEDIA_BUS_FMT_RGB101010_1X30:
|
||||
@@ -2776,6 +2816,21 @@ dw_hdmi_rockchip_attach_properties(struct drm_connector *connector,
|
||||
drm_object_attach_property(&connector->base,
|
||||
connector->colorspace_property, 0);
|
||||
drm_object_attach_property(&connector->base, private->connector_id_prop, hdmi->id);
|
||||
|
||||
ret = drm_connector_attach_content_protection_property(connector, true);
|
||||
if (ret) {
|
||||
dev_err(hdmi->dev, "failed to attach content protection: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
prop = drm_property_create_range(connector->dev, 0, RK_IF_PROP_ENCRYPTED,
|
||||
RK_IF_HDCP_ENCRYPTED_NONE, RK_IF_HDCP_ENCRYPTED_LEVEL2);
|
||||
if (!prop) {
|
||||
dev_err(hdmi->dev, "create hdcp encrypted prop for hdmi%d failed\n", hdmi->id);
|
||||
return;
|
||||
}
|
||||
hdmi->hdcp_state_property = prop;
|
||||
drm_object_attach_property(&connector->base, prop, RK_IF_HDCP_ENCRYPTED_NONE);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2909,6 +2964,7 @@ dw_hdmi_rockchip_set_property(struct drm_connector *connector,
|
||||
hdmi->enable_allm = val;
|
||||
if (allm_enable != hdmi->enable_allm)
|
||||
dw_hdmi_qp_set_allm_enable(hdmi->hdmi_qp, hdmi->enable_allm);
|
||||
} else if (property == hdmi->hdcp_state_property) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2985,6 +3041,14 @@ dw_hdmi_rockchip_get_property(struct drm_connector *connector,
|
||||
} else if (property == hdmi->allm_enable) {
|
||||
*val = hdmi->enable_allm;
|
||||
return 0;
|
||||
} else if (property == hdmi->hdcp_state_property) {
|
||||
if (hdmi->hdcp_status & BIT(1))
|
||||
*val = RK_IF_HDCP_ENCRYPTED_LEVEL2;
|
||||
else if (hdmi->hdcp_status & BIT(0))
|
||||
*val = RK_IF_HDCP_ENCRYPTED_LEVEL1;
|
||||
else
|
||||
*val = RK_IF_HDCP_ENCRYPTED_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
DRM_ERROR("Unknown property [PROP:%d:%s]\n",
|
||||
@@ -3458,6 +3522,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
plat_data->get_colorimetry =
|
||||
dw_hdmi_rockchip_get_colorimetry;
|
||||
plat_data->get_link_cfg = dw_hdmi_rockchip_get_link_cfg;
|
||||
plat_data->set_hdcp2_enable = rk3588_set_hdcp2_enable;
|
||||
plat_data->set_hdcp_status = rk3588_set_hdcp_status;
|
||||
plat_data->set_grf_cfg = rk3588_set_grf_cfg;
|
||||
plat_data->get_grf_color_fmt = rk3588_get_grf_color_fmt;
|
||||
plat_data->convert_to_split_mode = drm_mode_convert_to_split_mode;
|
||||
@@ -3473,6 +3539,8 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master,
|
||||
dw_hdmi_rockchip_set_prev_bus_format;
|
||||
plat_data->set_ddc_io =
|
||||
dw_hdmi_rockchip_set_ddc_io;
|
||||
plat_data->set_hdcp14_mem =
|
||||
dw_hdmi_rockchip_set_hdcp14_mem;
|
||||
plat_data->property_ops = &dw_hdmi_rockchip_property_ops;
|
||||
|
||||
secondary = rockchip_hdmi_find_by_id(dev->driver, !hdmi->id);
|
||||
|
||||
@@ -244,6 +244,8 @@ struct dw_hdmi_plat_data {
|
||||
int (*get_next_hdr_data)(void *data, struct edid *edid,
|
||||
struct drm_connector *connector);
|
||||
struct dw_hdmi_link_config *(*get_link_cfg)(void *data);
|
||||
void (*set_hdcp_status)(void *data, u8 status);
|
||||
void (*set_hdcp2_enable)(void *data, bool enable);
|
||||
void (*set_grf_cfg)(void *data);
|
||||
u64 (*get_grf_color_fmt)(void *data);
|
||||
void (*convert_to_split_mode)(struct drm_display_mode *mode);
|
||||
@@ -256,6 +258,7 @@ struct dw_hdmi_plat_data {
|
||||
void (*set_prev_bus_format)(void *data, unsigned long bus_format);
|
||||
int (*get_colorimetry)(void *data, struct edid *edid);
|
||||
void (*set_ddc_io)(void *data, bool enable);
|
||||
void (*set_hdcp14_mem)(void *data, bool enable);
|
||||
|
||||
/* Vendor Property support */
|
||||
const struct dw_hdmi_property_ops *property_ops;
|
||||
|
||||
Reference in New Issue
Block a user