summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Mayo <jmayo@nvidia.com>2011-01-06 17:17:01 -0800
committerYu-Huan Hsu <yhsu@nvidia.com>2011-01-10 12:02:35 -0800
commiteb4be8e8a319acfa0418bbc5f7084279e20f6f9c (patch)
tree86f8b3327b0dcb1e284b9f3b811384b27f835745
parent633a7967cad4454327572f85fbdf5cd88eb68b78 (diff)
[ARM] tegra: hdmi repeater, renegotiate, fixes
check for repeaters and store repeater info. userspace application queries this status to authenticate the connection. when authentication fails, auto-renegotiate every 1.75 seconds. fixes: * i2c infrastructure reimplemented in a clean way. removed hooks from hdmi.c and edid.c * added a few msleeps to conform to specifications. * removed read file operation, nobody was using it. * ioctl() bails out if hardware is disabled Bug 727054 Change-Id: I5e738323dd85911259bb4695659ffea19a5c1e49 Reviewed-on: http://git-master/r/15195 Reviewed-by: Jonathan Mayo <jmayo@nvidia.com> Tested-by: Jonathan Mayo <jmayo@nvidia.com> Reviewed-by: Peter Zu <pzu@nvidia.com> Reviewed-by: Yu-Huan Hsu <yhsu@nvidia.com>
-rw-r--r--drivers/video/tegra/dc/edid.c5
-rw-r--r--drivers/video/tegra/dc/edid.h1
-rw-r--r--drivers/video/tegra/dc/hdmi.c9
-rw-r--r--drivers/video/tegra/dc/hdmi_reg.h4
-rw-r--r--drivers/video/tegra/dc/nvhdcp.c507
-rw-r--r--drivers/video/tegra/dc/nvhdcp.h3
6 files changed, 326 insertions, 203 deletions
diff --git a/drivers/video/tegra/dc/edid.c b/drivers/video/tegra/dc/edid.c
index 46c3072e0b2e..0833ea6e4575 100644
--- a/drivers/video/tegra/dc/edid.c
+++ b/drivers/video/tegra/dc/edid.c
@@ -81,11 +81,6 @@ void tegra_edid_debug_add(struct tegra_edid *edid)
}
#endif
-int tegra_edid_i2c(struct tegra_edid *edid, struct i2c_msg *msg, int msg_len)
-{
- return i2c_transfer(edid->client->adapter, msg, msg_len);
-}
-
#ifdef DEBUG
static char tegra_edid_dump_buff[16 * 1024];
diff --git a/drivers/video/tegra/dc/edid.h b/drivers/video/tegra/dc/edid.h
index 4d417f2ef98f..821da90a8b4f 100644
--- a/drivers/video/tegra/dc/edid.h
+++ b/drivers/video/tegra/dc/edid.h
@@ -26,7 +26,6 @@ struct tegra_edid;
struct tegra_edid *tegra_edid_create(int bus);
void tegra_edid_destroy(struct tegra_edid *edid);
-int tegra_edid_i2c(struct tegra_edid *edid, struct i2c_msg *msg, int msg_len);
int tegra_edid_get_monspecs(struct tegra_edid *edid, struct fb_monspecs *specs);
#endif
diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c
index 323c93e553c2..cf9a57bb08c4 100644
--- a/drivers/video/tegra/dc/hdmi.c
+++ b/drivers/video/tegra/dc/hdmi.c
@@ -628,7 +628,8 @@ static int tegra_dc_hdmi_init(struct tegra_dc *dc)
goto err_free_irq;
}
- hdmi->nvhdcp = tegra_nvhdcp_create(hdmi, dc->ndev->id);
+ hdmi->nvhdcp = tegra_nvhdcp_create(hdmi, dc->ndev->id,
+ dc->out->dcc_bus);
if (IS_ERR_OR_NULL(hdmi->nvhdcp)) {
dev_err(&dc->ndev->dev, "hdmi: can't create nvhdcp\n");
err = PTR_ERR(hdmi->nvhdcp);
@@ -1213,9 +1214,3 @@ void tegra_hdmi_writel(struct tegra_dc_hdmi_data *hdmi,
{
_tegra_hdmi_writel(hdmi, val, reg);
}
-
-int tegra_hdmi_i2c(struct tegra_dc_hdmi_data *hdmi,
- struct i2c_msg *msg, int msg_len)
-{
- return tegra_edid_i2c(hdmi->edid, msg, msg_len);
-}
diff --git a/drivers/video/tegra/dc/hdmi_reg.h b/drivers/video/tegra/dc/hdmi_reg.h
index 9080fd79ecaa..db9c1b76e09f 100644
--- a/drivers/video/tegra/dc/hdmi_reg.h
+++ b/drivers/video/tegra/dc/hdmi_reg.h
@@ -430,7 +430,9 @@
#define PE_CURRENT3(x) (((x) & 0xf) << 24)
#define HDMI_NV_PDISP_KEY_CTRL 0x9a
-#define LOCAL_KEYS (1 << 1)
+#define LOCAL_KEYS (1 << 0)
+#define AUTOINC (1 << 1)
+#define WRITE16 (1 << 4)
#define PKEY_REQUEST_RELOAD_TRIGGER (1 << 5)
#define PKEY_LOADED (1 << 6)
#define HDMI_NV_PDISP_KEY_DEBUG0 0x9b
diff --git a/drivers/video/tegra/dc/nvhdcp.c b/drivers/video/tegra/dc/nvhdcp.c
index f6ca0b2238c7..2bec36a53219 100644
--- a/drivers/video/tegra/dc/nvhdcp.c
+++ b/drivers/video/tegra/dc/nvhdcp.c
@@ -36,14 +36,33 @@
#include "hdmi_reg.h"
#include "hdmi.h"
+/* for 0x40 Bcaps */
#define BCAPS_REPEATER (1 << 6)
#define BCAPS_READY (1 << 5)
#define BCAPS_11 (1 << 1) /* used for both Bcaps and Ainfo */
-// TODO remove this
-#define nvhdcp_debug(...) pr_err("nvhdcp: " __VA_ARGS__)
-#define nvhdcp_err(...) pr_err("nvhdcp: Error: " __VA_ARGS__)
-#define nvhdcp_info(...) pr_info("nvhdcp: " __VA_ARGS__)
+/* for 0x41 Bstatus */
+#define BSTATUS_MAX_DEVS_EXCEEDED (1 << 7)
+#define BSTATUS_MAX_CASCADE_EXCEEDED (1 << 11)
+
+#ifdef VERBOSE_DEBUG
+#define nvhdcp_vdbg(...) \
+ printk("nvhdcp: " __VA_ARGS__)
+#else
+#define nvhdcp_vdbg(...) \
+({ \
+ if(0) \
+ printk("nvhdcp: " __VA_ARGS__); \
+ 0; \
+})
+#endif
+#define nvhdcp_debug(...) \
+ pr_debug("nvhdcp: " __VA_ARGS__)
+#define nvhdcp_err(...) \
+ pr_err("nvhdcp: Error: " __VA_ARGS__)
+#define nvhdcp_info(...) \
+ pr_info("nvhdcp: " __VA_ARGS__)
+
/* for nvhdcp.state */
enum {
@@ -54,32 +73,38 @@ enum {
};
struct tegra_nvhdcp {
- struct work_struct work;
- struct tegra_dc_hdmi_data *hdmi;
- struct workqueue_struct *downstream_wq;
- struct mutex lock;
- struct miscdevice miscdev;
- char name[12];
- unsigned id;
- atomic_t plugged; /* true if hotplug detected */
- atomic_t policy; /* set policy */
- atomic_t state; /* STATE_xxx */
-
- u64 a_n;
- u64 c_n;
- u64 a_ksv;
- u64 b_ksv;
- u64 c_ksv;
- u64 d_ksv;
- u64 m_prime;
- u32 b_status;
+ struct work_struct work;
+ struct tegra_dc_hdmi_data *hdmi;
+ struct workqueue_struct *downstream_wq;
+ struct mutex lock;
+ struct miscdevice miscdev;
+ char name[12];
+ unsigned id;
+ atomic_t plugged; /* true if hotplug detected */
+ atomic_t policy; /* set policy */
+ atomic_t state; /* STATE_xxx */
+ struct i2c_client *client;
+ struct i2c_board_info info;
+ int bus;
+ u32 b_status;
+ u64 a_n;
+ u64 c_n;
+ u64 a_ksv;
+ u64 b_ksv;
+ u64 c_ksv;
+ u64 d_ksv;
+ u8 v_prime[20];
+ u64 m_prime;
+ u32 num_bksv_list;
+ u64 bksv_list[TEGRA_NVHDCP_MAX_DEVS];
};
#define TEGRA_NVHDCP_NUM_AP 1
static struct tegra_nvhdcp *nvhdcp_dev[TEGRA_NVHDCP_NUM_AP];
static int nvhdcp_ap;
-static int nvhdcp_i2c_read(struct tegra_dc_hdmi_data *hdmi, u8 reg, size_t len, u8 *data)
+static int nvhdcp_i2c_read(struct tegra_nvhdcp *nvhdcp, u8 reg,
+ size_t len, void *data)
{
int status;
struct i2c_msg msg[] = {
@@ -97,7 +122,7 @@ static int nvhdcp_i2c_read(struct tegra_dc_hdmi_data *hdmi, u8 reg, size_t len,
},
};
- status = tegra_hdmi_i2c(hdmi, msg, ARRAY_SIZE(msg));
+ status = i2c_transfer(nvhdcp->client->adapter, msg, ARRAY_SIZE(msg));
if (status < 0) {
nvhdcp_err("i2c xfer error %d\n", status);
@@ -107,7 +132,8 @@ static int nvhdcp_i2c_read(struct tegra_dc_hdmi_data *hdmi, u8 reg, size_t len,
return 0;
}
-static int nvhdcp_i2c_write(struct tegra_dc_hdmi_data *hdmi, u8 reg, size_t len, const u8 *data)
+static int nvhdcp_i2c_write(struct tegra_nvhdcp *nvhdcp, u8 reg,
+ size_t len, const void *data)
{
int status;
u8 buf[len + 1];
@@ -123,7 +149,7 @@ static int nvhdcp_i2c_write(struct tegra_dc_hdmi_data *hdmi, u8 reg, size_t len,
buf[0] = reg;
memcpy(buf + 1, data, len);
- status = tegra_hdmi_i2c(hdmi, msg, ARRAY_SIZE(msg));
+ status = i2c_transfer(nvhdcp->client->adapter, msg, ARRAY_SIZE(msg));
if (status < 0) {
nvhdcp_err("i2c xfer error %d\n", status);
@@ -133,22 +159,23 @@ static int nvhdcp_i2c_write(struct tegra_dc_hdmi_data *hdmi, u8 reg, size_t len,
return 0;
}
-static int nvhdcp_i2c_read8(struct tegra_dc_hdmi_data *hdmi, u8 reg, u8 *val)
+static inline int nvhdcp_i2c_read8(struct tegra_nvhdcp *nvhdcp, u8 reg, u8 *val)
{
- return nvhdcp_i2c_read(hdmi, reg, 1, val);
+ return nvhdcp_i2c_read(nvhdcp, reg, 1, val);
}
-static int nvhdcp_i2c_write8(struct tegra_dc_hdmi_data *hdmi, u8 reg, u8 val)
+static inline int nvhdcp_i2c_write8(struct tegra_nvhdcp *nvhdcp, u8 reg, u8 val)
{
- return nvhdcp_i2c_write(hdmi, reg, 1, &val);
+ return nvhdcp_i2c_write(nvhdcp, reg, 1, &val);
}
-static int nvhdcp_i2c_read16(struct tegra_dc_hdmi_data *hdmi, u8 reg, u16 *val)
+static inline int nvhdcp_i2c_read16(struct tegra_nvhdcp *nvhdcp,
+ u8 reg, u16 *val)
{
u8 buf[2];
int e;
- e = nvhdcp_i2c_read(hdmi, reg, sizeof buf, buf);
+ e = nvhdcp_i2c_read(nvhdcp, reg, sizeof buf, buf);
if (e)
return e;
@@ -158,13 +185,13 @@ static int nvhdcp_i2c_read16(struct tegra_dc_hdmi_data *hdmi, u8 reg, u16 *val)
return 0;
}
-static int nvhdcp_i2c_read40(struct tegra_dc_hdmi_data *hdmi, u8 reg, u64 *val)
+static int nvhdcp_i2c_read40(struct tegra_nvhdcp *nvhdcp, u8 reg, u64 *val)
{
u8 buf[5];
int e, i;
u64 n;
- e = nvhdcp_i2c_read(hdmi, reg, sizeof buf, buf);
+ e = nvhdcp_i2c_read(nvhdcp, reg, sizeof buf, buf);
if (e)
return e;
@@ -179,7 +206,7 @@ static int nvhdcp_i2c_read40(struct tegra_dc_hdmi_data *hdmi, u8 reg, u64 *val)
return 0;
}
-static int nvhdcp_i2c_write40(struct tegra_dc_hdmi_data *hdmi, u8 reg, u64 val)
+static int nvhdcp_i2c_write40(struct tegra_nvhdcp *nvhdcp, u8 reg, u64 val)
{
char buf[5];
int i;
@@ -187,10 +214,10 @@ static int nvhdcp_i2c_write40(struct tegra_dc_hdmi_data *hdmi, u8 reg, u64 val)
buf[i] = val;
val >>= 8;
}
- return nvhdcp_i2c_write(hdmi, reg, sizeof buf, buf);
+ return nvhdcp_i2c_write(nvhdcp, reg, sizeof buf, buf);
}
-static int nvhdcp_i2c_write64(struct tegra_dc_hdmi_data *hdmi, u8 reg, u64 val)
+static int nvhdcp_i2c_write64(struct tegra_nvhdcp *nvhdcp, u8 reg, u64 val)
{
char buf[8];
int i;
@@ -198,12 +225,12 @@ static int nvhdcp_i2c_write64(struct tegra_dc_hdmi_data *hdmi, u8 reg, u64 val)
buf[i] = val;
val >>= 8;
}
- return nvhdcp_i2c_write(hdmi, reg, sizeof buf, buf);
+ return nvhdcp_i2c_write(nvhdcp, reg, sizeof buf, buf);
}
/* 64-bit link encryption session random number */
-static u64 get_an(struct tegra_dc_hdmi_data *hdmi)
+static inline u64 get_an(struct tegra_dc_hdmi_data *hdmi)
{
u64 r;
r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_AN_MSB) << 32;
@@ -212,7 +239,7 @@ static u64 get_an(struct tegra_dc_hdmi_data *hdmi)
}
/* 64-bit upstream exchange random number */
-static void set_cn(struct tegra_dc_hdmi_data *hdmi, u64 c_n)
+static inline void set_cn(struct tegra_dc_hdmi_data *hdmi, u64 c_n)
{
tegra_hdmi_writel(hdmi, (u32)c_n, HDMI_NV_PDISP_RG_HDCP_CN_LSB);
tegra_hdmi_writel(hdmi, c_n >> 32, HDMI_NV_PDISP_RG_HDCP_CN_MSB);
@@ -229,16 +256,17 @@ static inline u64 get_aksv(struct tegra_dc_hdmi_data *hdmi)
}
/* 40-bit receiver's key selection vector */
-static inline u64 get_bksv(struct tegra_dc_hdmi_data *hdmi)
+static inline void set_bksv(struct tegra_dc_hdmi_data *hdmi, u64 b_ksv, bool repeater)
{
- u64 r;
- r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_BKSV_MSB) << 32;
- r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_BKSV_LSB);
- return r;
+ if (repeater)
+ b_ksv |= (u64)REPEATER << 32;
+ tegra_hdmi_writel(hdmi, (u32)b_ksv, HDMI_NV_PDISP_RG_HDCP_BKSV_LSB);
+ tegra_hdmi_writel(hdmi, b_ksv >> 32, HDMI_NV_PDISP_RG_HDCP_BKSV_MSB);
}
+
/* 40-bit software's key selection vector */
-static void set_cksv(struct tegra_dc_hdmi_data *hdmi, u64 c_ksv)
+static inline void set_cksv(struct tegra_dc_hdmi_data *hdmi, u64 c_ksv)
{
tegra_hdmi_writel(hdmi, (u32)c_ksv, HDMI_NV_PDISP_RG_HDCP_CKSV_LSB);
tegra_hdmi_writel(hdmi, c_ksv >> 32, HDMI_NV_PDISP_RG_HDCP_CKSV_MSB);
@@ -271,37 +299,21 @@ static inline u64 get_mprime(struct tegra_dc_hdmi_data *hdmi)
return r;
}
-/* 56-bit S' value + some status bits in the upper 8-bits
- * STATUS_CMODE_IDX 31:28
- * STATUS_UNPROTECTED 27
- * STATUS_EXTPNL 26
- * STATUS_RPTR 25
- * STATUS_ENCRYPTING 24
- */
-static inline u64 get_sprime(struct tegra_dc_hdmi_data *hdmi)
-{
- u64 r;
- r = (u64)tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB2) << 32;
- r |= tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_SPRIME_LSB1);
- return r;
-}
-
static inline u16 get_transmitter_ri(struct tegra_dc_hdmi_data *hdmi)
{
return tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_RI);
}
-static inline int get_receiver_ri(struct tegra_dc_hdmi_data *hdmi, u16 *r)
+static inline int get_receiver_ri(struct tegra_nvhdcp *nvhdcp, u16 *r)
{
- // TODO: implement short Ri read format
- return nvhdcp_i2c_read16(hdmi, 0x8, r); /* long read */
+ return nvhdcp_i2c_read16(nvhdcp, 0x8, r); /* long read */
}
-static int get_bcaps(struct tegra_dc_hdmi_data *hdmi, u8 *b_caps)
+static int get_bcaps(struct tegra_nvhdcp *nvhdcp, u8 *b_caps)
{
int e, retries = 3;
do {
- e = nvhdcp_i2c_read8(hdmi, 0x40, b_caps);
+ e = nvhdcp_i2c_read8(nvhdcp, 0x40, b_caps);
if (!e)
return 0;
msleep(100);
@@ -310,16 +322,64 @@ static int get_bcaps(struct tegra_dc_hdmi_data *hdmi, u8 *b_caps)
return -EIO;
}
+static int get_ksvfifo(struct tegra_nvhdcp *nvhdcp,
+ unsigned num_bksv_list, u64 *ksv_list)
+{
+ u8 *buf, *p;
+ int e;
+ unsigned i;
+ size_t buf_len = num_bksv_list * 5;
+
+ if (!ksv_list || num_bksv_list > TEGRA_NVHDCP_MAX_DEVS)
+ return -EINVAL;
+
+ buf = kmalloc(buf_len, GFP_KERNEL);
+ if (IS_ERR_OR_NULL(buf))
+ return -ENOMEM;
+
+ e = nvhdcp_i2c_read(nvhdcp, 0x43, buf_len, buf);
+ if (e) {
+ kfree(buf);
+ return e;
+ }
+
+ /* load 40-bit keys from repeater into array of u64 */
+ p = buf;
+ for (i = 0; i < num_bksv_list; i++) {
+ ksv_list[i] = p[0] | ((u64)p[1] << 8) | ((u64)p[2] << 16)
+ | ((u64)p[3] << 24) | ((u64)p[4] << 32);
+ p += 5;
+ }
+
+ kfree(buf);
+ return 0;
+}
+
+/* get V' 160-bit SHA-1 hash from repeater */
+static int get_vprime(struct tegra_nvhdcp *nvhdcp, u8 *v_prime)
+{
+ int e, i;
+
+ for (i = 0; i < 20; i += 4) {
+ e = nvhdcp_i2c_read(nvhdcp, 0x20 + i, 4, v_prime + i);
+ if (e)
+ return e;
+ }
+ return 0;
+}
+
+
/* set or clear RUN_YES */
static void hdcp_ctrl_run(struct tegra_dc_hdmi_data *hdmi, bool v)
{
u32 ctrl;
- ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CTRL);
- if (v)
+ if (v) {
+ ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_CTRL);
ctrl |= HDCP_RUN_YES;
- else
+ } else {
ctrl = 0;
+ }
tegra_hdmi_writel(hdmi, ctrl, HDMI_NV_PDISP_RG_HDCP_CTRL);
}
@@ -389,6 +449,14 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet *
u32 sp_msb, sp_lsb1, sp_lsb2;
int e;
+ /* if connection isn't authenticated ... */
+ if (atomic_read(&nvhdcp->state) != STATE_LINK_VERIFY) {
+ memset(pkt, 0, sizeof *pkt);
+ pkt->packet_results = TEGRA_NVHDCP_RESULT_LINK_FAILED;
+ return 0;
+ }
+
+
pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL;
/* we will be taking c_n, c_ksv as input */
@@ -404,7 +472,7 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet *
pkt->a_n = nvhdcp->a_n;
pkt->value_flags = TEGRA_NVHDCP_FLAG_AKSV | TEGRA_NVHDCP_FLAG_AN;
- nvhdcp_debug("%s():cn %llx cksv %llx\n", __func__, pkt->c_n, pkt->c_ksv);
+ nvhdcp_vdbg("%s():cn %llx cksv %llx\n", __func__, pkt->c_n, pkt->c_ksv);
set_cn(hdmi, pkt->c_n);
@@ -420,7 +488,7 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet *
return -EIO;
}
- msleep(100);
+ msleep(50);
/* read 56-bit Sprime plus 16 status bits */
sp_msb = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_RG_HDCP_SPRIME_MSB);
@@ -467,7 +535,7 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p
pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL;
/* if connection isn't authenticated ... */
- if (atomic_read(&nvhdcp->state) == STATE_UNAUTHENTICATED) {
+ if (atomic_read(&nvhdcp->state) != STATE_LINK_VERIFY) {
memset(pkt, 0, sizeof *pkt);
pkt->packet_results = TEGRA_NVHDCP_RESULT_LINK_FAILED;
return 0;
@@ -479,7 +547,6 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p
set_cn(hdmi, pkt->c_n);
- // TODO: original code used TMDS0_LINK1, weird
tegra_hdmi_writel(hdmi, TMDS0_LINK0 | READ_M,
HDMI_NV_PDISP_RG_HDCP_CMODE);
@@ -491,14 +558,29 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p
nvhdcp_err("Mprime read timeout\n");
return -EIO;
}
+ msleep(50);
/* load Mprime */
- pkt->m_prime = (u64)tegra_hdmi_readl(hdmi,
- HDMI_NV_PDISP_RG_HDCP_MPRIME_MSB) << 32;
- pkt->m_prime |= tegra_hdmi_readl(hdmi,
- HDMI_NV_PDISP_RG_HDCP_MPRIME_LSB);
+ pkt->m_prime = get_mprime(hdmi);
pkt->value_flags |= TEGRA_NVHDCP_FLAG_MP;
+ pkt->b_status = nvhdcp->b_status;
+ pkt->value_flags |= TEGRA_NVHDCP_FLAG_BSTATUS;
+
+ /* copy most recent KSVFIFO, if it is non-zero */
+ pkt->num_bksv_list = nvhdcp->num_bksv_list;
+ if( nvhdcp->num_bksv_list ) {
+ BUILD_BUG_ON(sizeof(pkt->bksv_list) != sizeof(nvhdcp->bksv_list));
+ memcpy(pkt->bksv_list, nvhdcp->bksv_list,
+ nvhdcp->num_bksv_list * sizeof(*pkt->bksv_list));
+ pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSVLIST;
+ }
+
+ /* copy v_prime */
+ BUILD_BUG_ON(sizeof(pkt->v_prime) != sizeof(nvhdcp->v_prime));
+ memcpy(pkt->v_prime, nvhdcp->v_prime, sizeof(nvhdcp->v_prime));
+ pkt->value_flags |= TEGRA_NVHDCP_FLAG_V;
+
/* load Dksv */
pkt->d_ksv = get_dksv(hdmi);
if (verify_ksv(pkt->d_ksv)) {
@@ -507,11 +589,15 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p
}
pkt->value_flags |= TEGRA_NVHDCP_FLAG_DKSV;
+ /* copy current Bksv */
+ pkt->b_ksv = nvhdcp->b_ksv;
+ pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSV;
+
pkt->packet_results = TEGRA_NVHDCP_RESULT_SUCCESS;
return 0;
}
-static int load_kfuse(struct tegra_dc_hdmi_data *hdmi, bool repeater)
+static int load_kfuse(struct tegra_dc_hdmi_data *hdmi)
{
unsigned buf[KFUSE_DATA_SZ / 4];
int e, i;
@@ -527,11 +613,6 @@ static int load_kfuse(struct tegra_dc_hdmi_data *hdmi, bool repeater)
}
/* write the kfuse to HDMI SRAM */
- if (repeater) {
- tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_RG_HDCP_BKSV_LSB);
- tegra_hdmi_writel(hdmi, REPEATER,
- HDMI_NV_PDISP_RG_HDCP_BKSV_MSB);
- }
tegra_hdmi_writel(hdmi, 1, HDMI_NV_PDISP_KEY_CTRL); /* LOAD_KEYS */
@@ -571,9 +652,9 @@ static int load_kfuse(struct tegra_dc_hdmi_data *hdmi, bool repeater)
/* trigger LOAD_HDCP_KEY */
tegra_hdmi_writel(hdmi, 0x100, HDMI_NV_PDISP_KEY_HDCP_KEY_TRIG);
- tmp = 0x11; /* LOCAL_KEYS | WRITE16 */
+ tmp = LOCAL_KEYS | WRITE16;
if (i)
- tmp |= 0x2; /* AUTOINC */
+ tmp |= AUTOINC;
tegra_hdmi_writel(hdmi, tmp, HDMI_NV_PDISP_KEY_CTRL);
/* wait for WRITE16 to complete */
@@ -602,20 +683,22 @@ static int verify_link(struct tegra_nvhdcp *nvhdcp, bool wait_ri)
if (wait_ri)
old = get_transmitter_ri(hdmi);
- e = get_receiver_ri(hdmi, &rx);
- if (e)
- continue;
-
- if (!rx) {
- nvhdcp_err("Ri is 0!\n");
- return -EINVAL;
+ e = get_receiver_ri(nvhdcp, &rx);
+ if (!e) {
+ if (!rx) {
+ nvhdcp_err("Ri is 0!\n");
+ return -EINVAL;
+ }
+
+ tx = get_transmitter_ri(hdmi);
+ } else {
+ rx = ~tx;
+ msleep(50);
}
- tx = get_transmitter_ri(hdmi);
-
} while (wait_ri && --retries && old != tx);
- nvhdcp_debug("%s():rx(0x%04x) == tx(0x%04x)\n", __func__, rx, tx);
+ nvhdcp_debug("R0 Ri poll:rx=0x%04x tx=0x%04x\n", rx, tx);
if (!atomic_read(&nvhdcp->plugged)) {
nvhdcp_err("aborting verify links - lost hdmi connection\n");
@@ -628,6 +711,72 @@ static int verify_link(struct tegra_nvhdcp *nvhdcp, bool wait_ri)
return 0;
}
+static int get_repeater_info(struct tegra_nvhdcp *nvhdcp)
+{
+ int e, retries;
+ u8 b_caps;
+ u16 b_status;
+
+ nvhdcp_vdbg("repeater found:fetching repeater info\n");
+
+ /* wait up to 5 seconds for READY on repeater */
+ retries = 50;
+ do {
+ if (!atomic_read(&nvhdcp->plugged)) {
+ nvhdcp_err("disconnect while waiting for repeater\n");
+ return -EIO;
+ }
+
+ e = get_bcaps(nvhdcp, &b_caps);
+ if (!e && (b_caps & BCAPS_READY)) {
+ nvhdcp_debug("Bcaps READY from repeater\n");
+ break;
+ }
+ msleep(100);
+ } while (--retries);
+ if (!retries) {
+ nvhdcp_err("repeater Bcaps read timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ memset(nvhdcp->v_prime, 0, sizeof nvhdcp->v_prime);
+ e = get_vprime(nvhdcp, nvhdcp->v_prime);
+ if (e) {
+ nvhdcp_err("repeater Vprime read failure!\n");
+ return e;
+ }
+
+ e = nvhdcp_i2c_read16(nvhdcp, 0x41, &b_status);
+ if (e) {
+ nvhdcp_err("Bstatus read failure!\n");
+ return e;
+ }
+
+ if (b_status & BSTATUS_MAX_DEVS_EXCEEDED) {
+ nvhdcp_err("repeater:max devices (0x%04x)\n", b_status);
+ return -EINVAL;
+ }
+
+ if (b_status & BSTATUS_MAX_CASCADE_EXCEEDED) {
+ nvhdcp_err("repeater:max cascade (0x%04x)\n", b_status);
+ return -EINVAL;
+ }
+
+ nvhdcp->b_status = b_status;
+ nvhdcp->num_bksv_list = b_status & 0x7f;
+ nvhdcp_vdbg("Bstatus 0x%x (devices: %d)\n",
+ b_status, nvhdcp->num_bksv_list);
+
+ memset(nvhdcp->bksv_list, 0, sizeof nvhdcp->bksv_list);
+ e = get_ksvfifo(nvhdcp, nvhdcp->num_bksv_list, nvhdcp->bksv_list);
+ if (e) {
+ nvhdcp_err("repeater:could not read KSVFIFO (err %d)\n", e);
+ return e;
+ }
+
+ return 0;
+}
+
static void nvhdcp_downstream_worker(struct work_struct *work)
{
struct tegra_nvhdcp *nvhdcp =
@@ -639,7 +788,7 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
u32 res;
int st;
- nvhdcp_debug("%s():started thread %s\n", __func__, nvhdcp->name);
+ nvhdcp_vdbg("%s():started thread %s\n", __func__, nvhdcp->name);
st = atomic_read(&nvhdcp->state);
if (st == STATE_OFF) {
@@ -659,19 +808,22 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
nvhdcp->b_ksv = 0;
nvhdcp->a_n = 0;
- nvhdcp_debug("%s():hpd=%d\n", __func__, atomic_read(&nvhdcp->plugged));
+ nvhdcp_vdbg("%s():hpd=%d\n", __func__, atomic_read(&nvhdcp->plugged));
- e = get_bcaps(hdmi, &b_caps);
+ e = get_bcaps(nvhdcp, &b_caps);
if (e) {
nvhdcp_err("Bcaps read failure\n");
goto failure;
}
- nvhdcp_debug("read Bcaps = 0x%02x\n", b_caps);
+ nvhdcp_vdbg("read Bcaps = 0x%02x\n", b_caps);
+
+ nvhdcp_vdbg("kfuse loading ...\n");
- nvhdcp_debug("kfuse loading ...\n");
+ /* repeater flag in Bskv must be configured before loading fuses */
+ set_bksv(hdmi, 0, (b_caps & BCAPS_REPEATER));
- e = load_kfuse(hdmi, (b_caps & BCAPS_REPEATER) != 0);
+ e = load_kfuse(hdmi);
if (e) {
nvhdcp_err("kfuse could not be loaded\n");
goto failure;
@@ -679,7 +831,7 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
hdcp_ctrl_run(hdmi, 1);
- nvhdcp_debug("wait AN_VALID ...\n");
+ nvhdcp_vdbg("wait AN_VALID ...\n");
/* wait for hardware to generate HDCP values */
e = wait_hdcp_ctrl(hdmi, AN_VALID | SROM_ERR, &res);
@@ -692,66 +844,63 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
goto failure;
}
+ msleep(25);
+
nvhdcp->a_ksv = get_aksv(hdmi);
nvhdcp->a_n = get_an(hdmi);
- nvhdcp_debug("Aksv is 0x%016llx\n", nvhdcp->a_ksv);
- nvhdcp_debug("An is 0x%016llx\n", nvhdcp->a_n);
+ nvhdcp_vdbg("Aksv is 0x%016llx\n", nvhdcp->a_ksv);
+ nvhdcp_vdbg("An is 0x%016llx\n", nvhdcp->a_n);
if (verify_ksv(nvhdcp->a_ksv)) {
- nvhdcp_err("Aksv verify failure!\n");
+ nvhdcp_err("Aksv verify failure! (0x%016llx)\n", nvhdcp->a_ksv);
goto failure;
}
/* write Ainfo to receiver - set 1.1 only if b_caps supports it */
- e = nvhdcp_i2c_write8(hdmi, 0x15, b_caps & BCAPS_11);
+ e = nvhdcp_i2c_write8(nvhdcp, 0x15, b_caps & BCAPS_11);
if (e) {
nvhdcp_err("Ainfo write failure\n");
goto failure;
}
/* write An to receiver */
- e = nvhdcp_i2c_write64(hdmi, 0x18, nvhdcp->a_n);
+ e = nvhdcp_i2c_write64(nvhdcp, 0x18, nvhdcp->a_n);
if (e) {
nvhdcp_err("An write failure\n");
goto failure;
}
- nvhdcp_debug("wrote An = 0x%016llx\n", nvhdcp->a_n);
+ nvhdcp_vdbg("wrote An = 0x%016llx\n", nvhdcp->a_n);
/* write Aksv to receiver - triggers auth sequence */
- e = nvhdcp_i2c_write40(hdmi, 0x10, nvhdcp->a_ksv);
+ e = nvhdcp_i2c_write40(nvhdcp, 0x10, nvhdcp->a_ksv);
if (e) {
nvhdcp_err("Aksv write failure\n");
goto failure;
}
- nvhdcp_debug("wrote Aksv = 0x%010llx\n", nvhdcp->a_ksv);
+ nvhdcp_vdbg("wrote Aksv = 0x%010llx\n", nvhdcp->a_ksv);
/* bail out if unplugged in the middle of negotiation */
if (!atomic_read(&nvhdcp->plugged))
goto lost_hdmi;
- /* get Bksv from reciever */
- e = nvhdcp_i2c_read40(hdmi, 0x00, &nvhdcp->b_ksv);
+ /* get Bksv from receiver */
+ e = nvhdcp_i2c_read40(nvhdcp, 0x00, &nvhdcp->b_ksv);
if (e) {
nvhdcp_err("Bksv read failure\n");
goto failure;
}
- nvhdcp_debug("Bksv is 0x%016llx\n", nvhdcp->b_ksv);
+ nvhdcp_vdbg("Bksv is 0x%016llx\n", nvhdcp->b_ksv);
if (verify_ksv(nvhdcp->b_ksv)) {
nvhdcp_err("Bksv verify failure!\n");
goto failure;
}
- nvhdcp_debug("read Bksv = 0x%010llx from device\n", nvhdcp->b_ksv);
+ nvhdcp_vdbg("read Bksv = 0x%010llx from device\n", nvhdcp->b_ksv);
- /* LSB must be written first */
- tmp = (u32)nvhdcp->b_ksv;
- tegra_hdmi_writel(hdmi, tmp, HDMI_NV_PDISP_RG_HDCP_BKSV_LSB);
- // TODO: mix in repeater flags
- tmp = (u32)(nvhdcp->b_ksv >> 32);
- tegra_hdmi_writel(hdmi, tmp, HDMI_NV_PDISP_RG_HDCP_BKSV_MSB);
+ set_bksv(hdmi, nvhdcp->b_ksv, (b_caps & BCAPS_REPEATER));
- nvhdcp_debug("load Bksv into controller\n");
+ nvhdcp_vdbg("loaded Bksv into controller\n");
e = wait_hdcp_ctrl(hdmi, R0_VALID, NULL);
if (e) {
@@ -759,12 +908,11 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
goto failure;
}
- nvhdcp_debug("R0 valid\n");
+ nvhdcp_vdbg("R0 valid\n");
- /* can't read R0' within 100ms of writing Aksv */
- msleep(100);
+ msleep(100); /* can't read R0' within 100ms of writing Aksv */
- nvhdcp_debug("verifying links ...\n");
+ nvhdcp_vdbg("verifying links ...\n");
e = verify_link(nvhdcp, false);
if (e) {
@@ -778,36 +926,15 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
tmp |= ONEONE_ENABLED;
tegra_hdmi_writel(hdmi, tmp, HDMI_NV_PDISP_RG_HDCP_CTRL);
- nvhdcp_debug("CRYPT enabled\n");
+ nvhdcp_vdbg("CRYPT enabled\n");
/* if repeater then get repeater info */
if (b_caps & BCAPS_REPEATER) {
- u64 v_prime;
- u16 b_status;
-
- nvhdcp_debug("repeater stuff ... \n"); // TODO: remove comments
-
- /* Vprime */
- e = nvhdcp_i2c_read40(hdmi, 0x20, &v_prime);
- if (e) {
- nvhdcp_err("V' read failure!\n");
- goto failure;
- }
- // TODO: do somthing with Vprime
-
- // TODO: get Bstatus
- e = nvhdcp_i2c_read16(hdmi, 0x41, &b_status);
+ e = get_repeater_info(nvhdcp);
if (e) {
- nvhdcp_err("Bstatus read failure!\n");
+ nvhdcp_err("get repeater info failed\n");
goto failure;
}
-
- nvhdcp_debug("Bstatus: device_count=%d\n", b_status & 0x7f);
-
- // TODO: read KSV fifo(0x43) for DEVICE_COUNT devices
-
- nvhdcp_err("not implemented - repeater info\n");
- goto failure;
}
atomic_set(&nvhdcp->state, STATE_LINK_VERIFY);
@@ -824,15 +951,20 @@ static void nvhdcp_downstream_worker(struct work_struct *work)
msleep(1500);
}
+failure:
+ /* TODO: check policy to see if we should start again
+ * or wait for read_m/read_s to come in (on-demand)
+ */
+ nvhdcp_err("nvhdcp failure - renegotiating in 1.75 seconds\n");
+ atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED);
+ hdcp_ctrl_run(hdmi, 0);
+ msleep(1750);
+ queue_work(nvhdcp->downstream_wq, &nvhdcp->work);
return;
lost_hdmi:
nvhdcp_err("lost hdmi connection\n");
-failure:
- nvhdcp_err("nvhdcp failure - giving up\n");
atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED);
- /* TODO: disable hdcp
- hdcp_ctrl_run(nvhdcp->hdmi, 0);
- */
+ hdcp_ctrl_run(hdmi, 0);
return;
}
@@ -868,7 +1000,8 @@ static int tegra_nvhdcp_off(struct tegra_nvhdcp *nvhdcp)
int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol)
{
pol = !!pol;
- nvhdcp_info("using \"%s\" policy.\n", pol ? "always on" : "on demand");
+ nvhdcp_info("using \"%s\" policy.\n",
+ pol ? "always on" : "on demand");
if (atomic_xchg(&nvhdcp->policy, pol) == TEGRA_NVHDCP_POLICY_ON_DEMAND
&& pol == TEGRA_NVHDCP_POLICY_ALWAYS_ON) {
/* policy was off but now it is on, start working */
@@ -891,30 +1024,6 @@ void tegra_nvhdcp_suspend(struct tegra_nvhdcp *nvhdcp)
}
-static ssize_t
-nvhdcp_dev_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos)
-{
- struct tegra_nvhdcp *nvhdcp = filp->private_data;
- u16 data[2];
- int e;
- size_t len;
-
- if (!count)
- return 0;
-
- /* read Ri pairs as fast as possible into buffer */
- for (len = 0; (len + sizeof(data)) < count; len += sizeof(data)) {
- data[0] = get_transmitter_ri(nvhdcp->hdmi);
- data[1] = 0;
- e = get_receiver_ri(nvhdcp->hdmi, &data[1]);
- if (e)
- return e;
-
- memcpy(buf + len, data, 2);
- }
- return len;
-}
-
static long nvhdcp_dev_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
@@ -992,25 +1101,23 @@ static int nvhdcp_dev_open(struct inode *inode, struct file *filp)
static int nvhdcp_dev_release(struct inode *inode, struct file *filp)
{
filp->private_data = NULL;
-
--nvhdcp_ap;
-
- // TODO: deactivate something maybe?
return 0;
}
static const struct file_operations nvhdcp_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .read = nvhdcp_dev_read,
.unlocked_ioctl = nvhdcp_dev_ioctl,
.open = nvhdcp_dev_open,
.release = nvhdcp_dev_release,
};
-struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, int id)
+struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi,
+ int id, int bus)
{
struct tegra_nvhdcp *nvhdcp;
+ struct i2c_adapter *adapter;
int e;
nvhdcp = kzalloc(sizeof(*nvhdcp), GFP_KERNEL);
@@ -1022,6 +1129,27 @@ struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, int id
nvhdcp->hdmi = hdmi;
mutex_init(&nvhdcp->lock);
+ strlcpy(nvhdcp->info.type, nvhdcp->name, sizeof(nvhdcp->info.type));
+ nvhdcp->bus = bus;
+ nvhdcp->info.addr = 0x74 >> 1;
+ nvhdcp->info.platform_data = nvhdcp;
+
+ adapter = i2c_get_adapter(bus);
+ if (!adapter) {
+ nvhdcp_err("can't get adapter for bus %d\n", bus);
+ e = -EBUSY;
+ goto free_nvhdcp;
+ }
+
+ nvhdcp->client = i2c_new_device(adapter, &nvhdcp->info);
+ i2c_put_adapter(adapter);
+
+ if (!nvhdcp->client) {
+ nvhdcp_err("can't create new device\n");
+ e = -EBUSY;
+ goto free_nvhdcp;
+ }
+
atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED);
nvhdcp->downstream_wq = create_singlethread_workqueue(nvhdcp->name);
@@ -1033,15 +1161,17 @@ struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, int id
e = misc_register(&nvhdcp->miscdev);
if (e)
- goto out1;
+ goto free_workqueue;
- nvhdcp_debug("%s(): created misc device %s\n", __func__, nvhdcp->name);
+ nvhdcp_vdbg("%s(): created misc device %s\n", __func__, nvhdcp->name);
nvhdcp_dev[0] = nvhdcp; /* we only support on AP right now */
return nvhdcp;
-out1:
+free_workqueue:
destroy_workqueue(nvhdcp->downstream_wq);
+ i2c_release_client(nvhdcp->client);
+free_nvhdcp:
kfree(nvhdcp);
nvhdcp_err("unable to create device.\n");
return ERR_PTR(e);
@@ -1053,5 +1183,6 @@ void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp)
atomic_set(&nvhdcp->plugged, 0); /* force early termination */
flush_workqueue(nvhdcp->downstream_wq);
destroy_workqueue(nvhdcp->downstream_wq);
+ i2c_release_client(nvhdcp->client);
kfree(nvhdcp);
}
diff --git a/drivers/video/tegra/dc/nvhdcp.h b/drivers/video/tegra/dc/nvhdcp.h
index 9c8fc00250b1..9b898252d534 100644
--- a/drivers/video/tegra/dc/nvhdcp.h
+++ b/drivers/video/tegra/dc/nvhdcp.h
@@ -22,7 +22,8 @@ struct tegra_nvhdcp;
void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd);
int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol);
void tegra_nvhdcp_suspend(struct tegra_nvhdcp *nvhdcp);
-struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, int id);
+struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi,
+ int id, int bus);
void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp);
#endif