From eb4be8e8a319acfa0418bbc5f7084279e20f6f9c Mon Sep 17 00:00:00 2001 From: Jon Mayo Date: Thu, 6 Jan 2011 17:17:01 -0800 Subject: [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 Tested-by: Jonathan Mayo Reviewed-by: Peter Zu Reviewed-by: Yu-Huan Hsu --- drivers/video/tegra/dc/edid.c | 5 - drivers/video/tegra/dc/edid.h | 1 - drivers/video/tegra/dc/hdmi.c | 9 +- drivers/video/tegra/dc/hdmi_reg.h | 4 +- drivers/video/tegra/dc/nvhdcp.c | 507 ++++++++++++++++++++++++-------------- drivers/video/tegra/dc/nvhdcp.h | 3 +- 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 -- cgit v1.2.3