diff options
author | Nitin Kumbhar <nkumbhar@nvidia.com> | 2011-03-01 12:32:50 +0530 |
---|---|---|
committer | Nitin Kumbhar <nkumbhar@nvidia.com> | 2011-03-01 12:32:50 +0530 |
commit | 3c1bf41f41587ba952ba15755d0f0592ca424ee1 (patch) | |
tree | d7ea17065d88cba1da864f28426cc5c06f7dcd60 | |
parent | 489747ad5e710815778d251fb91249f9595d44b1 (diff) | |
parent | 0fcb985c6a9b535c2501eaa041d8ad9289e4ae92 (diff) |
merging android-tegra-2.6.36 into git-master/linux-2.6/android-tegra-2.6.36
Conflicts:
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/fuse.c
arch/arm/mach-tegra/fuse.h
arch/arm/mach-tegra/kfuse.c
arch/arm/mach-tegra/tegra2_clocks.c
drivers/video/tegra/dc/Makefile
drivers/video/tegra/dc/hdmi.c
drivers/video/tegra/dc/hdmi.h
drivers/video/tegra/dc/nvhdcp.c
Change-Id: I60a025d9e23e0699afcfaf9e3e42a98263cd7de8
-rw-r--r-- | arch/arm/Kconfig | 10 | ||||
-rw-r--r-- | arch/arm/include/asm/hardware/cache-l2x0.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/common.c | 1 | ||||
-rw-r--r-- | arch/arm/mach-tegra/cortex-a9.S | 2 | ||||
-rw-r--r-- | arch/arm/mach-tegra/kfuse.c | 11 | ||||
-rw-r--r-- | arch/arm/mm/proc-v7.S | 10 | ||||
-rw-r--r-- | drivers/usb/gadget/android.c | 5 | ||||
-rw-r--r-- | drivers/video/tegra/dc/hdmi.c | 5 | ||||
-rw-r--r-- | drivers/video/tegra/dc/nvhdcp.c | 206 |
9 files changed, 163 insertions, 88 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index cd2c427f0e2b..0a293ee4a313 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1129,6 +1129,16 @@ config ARM_ERRATA_743622 visible impact on the overall performance or power consumption of the processor. +config ARM_ERRATA_720791 + bool "ARM errata: Dynamic high-level clock gating corrupts the Jazelle instruction stream" + depends on CPU_V7 + help + This option enables the workaround for the 720791 Cortex-A9 + (r1p0..r1p2) erratum. The Jazelle instruction stream may be + corrupted when dynamic high-level clock gating is enabled. + This workaround disables gating the Core clock when the Instruction + side is waiting for a Page Table Walk answer or linefill completion. + endmenu source "arch/arm/common/Kconfig" diff --git a/arch/arm/include/asm/hardware/cache-l2x0.h b/arch/arm/include/asm/hardware/cache-l2x0.h index 787c06ada555..d62847df3df5 100644 --- a/arch/arm/include/asm/hardware/cache-l2x0.h +++ b/arch/arm/include/asm/hardware/cache-l2x0.h @@ -54,6 +54,7 @@ #define L2X0_LINE_TAG 0xF30 #define L2X0_DEBUG_CTRL 0xF40 #define L2X0_PREFETCH_OFFSET 0xF60 +#define L2X0_PWR_CTRL 0xF80 #ifndef __ASSEMBLY__ extern void __init l2x0_init(void __iomem *base, __u32 aux_val, __u32 aux_mask); diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index d09ff3b13b54..15f09825df2b 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -100,6 +100,7 @@ void __init tegra_init_cache(void) writel(0x331, p + L2X0_TAG_LATENCY_CTRL); writel(0x441, p + L2X0_DATA_LATENCY_CTRL); writel(7, p + L2X0_PREFETCH_OFFSET); + writel(2, p + L2X0_PWR_CTRL); l2x0_init(p, 0x7C480001, 0x8200c3fe); #endif diff --git a/arch/arm/mach-tegra/cortex-a9.S b/arch/arm/mach-tegra/cortex-a9.S index 91d787f2adcb..1ca815d0fab8 100644 --- a/arch/arm/mach-tegra/cortex-a9.S +++ b/arch/arm/mach-tegra/cortex-a9.S @@ -536,6 +536,8 @@ ENTRY(__cortex_a9_l2x0_restart) str r6, [r9, #L2X0_DATA_LATENCY_CTRL] str r7, [r9, #L2X0_PREFETCH_OFFSET] str r4, [r9, #L2X0_AUX_CTRL] + mov r4, #0x2 @ L2X0_DYNAMIC_CLK_GATING_EN + str r4, [r9, #L2X0_PWR_CTRL] cmp r0, #0 beq __reenable_l2x0 diff --git a/arch/arm/mach-tegra/kfuse.c b/arch/arm/mach-tegra/kfuse.c index b2439f5a4ae2..f5de828bb508 100644 --- a/arch/arm/mach-tegra/kfuse.c +++ b/arch/arm/mach-tegra/kfuse.c @@ -22,6 +22,7 @@ #include <linux/io.h> #include <linux/err.h> #include <linux/string.h> +#include <linux/delay.h> #include <mach/iomap.h> #include <mach/kfuse.h> @@ -46,13 +47,17 @@ static inline void tegra_kfuse_writel(u32 value, unsigned long offset) tegra_apb_writel(value, TEGRA_KFUSE_BASE + offset); } -static void wait_for_done(void) +static int wait_for_done(void) { u32 reg; - + int retries = 50; do { reg = tegra_kfuse_readl(KFUSE_STATE); - } while ((reg & KFUSE_STATE_DONE) == 0); + if (reg & KFUSE_STATE_DONE); + return 0; + msleep(10); + } while(--retries); + return -ETIMEDOUT; } /* read up to KFUSE_DATA_SZ bytes into dest. diff --git a/arch/arm/mm/proc-v7.S b/arch/arm/mm/proc-v7.S index 8cdf3bf26719..de77d5b4271a 100644 --- a/arch/arm/mm/proc-v7.S +++ b/arch/arm/mm/proc-v7.S @@ -238,6 +238,16 @@ __v7_setup: 2: ldr r10, =0x00000c09 @ Cortex-A9 primary part number teq r0, r10 bne 3f + cmp r6, #0x10 @ power ctrl reg added r1p0 + mrcge p15, 0, r10, c15, c0, 0 @ read power control register + orrge r10, r10, #1 @ enable dynamic clock gating + mcrge p15, 0, r10, c15, c0, 0 @ write power control register +#ifdef CONFIG_ARM_ERRATA_720791 + teq r5, #0x00100000 @ only present in r1p* + mrceq p15, 0, r10, c15, c0, 2 @ read "chicken power ctrl" reg + orreq r10, r10, #0x30 @ disable core clk gate on + mcreq p15, 0, r10, c15, c0, 2 @ instr-side waits +#endif #ifdef CONFIG_ARM_ERRATA_742230 cmp r6, #0x22 @ only present up to r2p2 mrcle p15, 0, r10, c15, c0, 1 @ read diagnostic register diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c index 8e6cafc851cb..7017e23a80ad 100644 --- a/drivers/usb/gadget/android.c +++ b/drivers/usb/gadget/android.c @@ -344,6 +344,7 @@ static int android_bind(struct usb_composite_dev *cdev) dev->cdev = cdev; device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); + cdev->desc.idVendor = device_desc.idVendor; cdev->desc.idProduct = device_desc.idProduct; return 0; @@ -436,8 +437,10 @@ void android_enable_function(struct usb_function *f, int enable) device_desc.idVendor = __constant_cpu_to_le16(get_vendor_id(dev)); device_desc.idProduct = __constant_cpu_to_le16(get_product_id(dev)); - if (dev->cdev) + if (dev->cdev) { + dev->cdev->desc.idVendor = device_desc.idVendor; dev->cdev->desc.idProduct = device_desc.idProduct; + } usb_composite_force_reset(dev->cdev); } } diff --git a/drivers/video/tegra/dc/hdmi.c b/drivers/video/tegra/dc/hdmi.c index 44d15b8f5686..71e892ac57d0 100644 --- a/drivers/video/tegra/dc/hdmi.c +++ b/drivers/video/tegra/dc/hdmi.c @@ -551,6 +551,7 @@ static void tegra_dc_hdmi_suspend(struct tegra_dc *dc) tegra_nvhdcp_suspend(hdmi->nvhdcp); spin_lock_irqsave(&hdmi->suspend_lock, flags); + tegra_nvhdcp_suspend(hdmi->nvhdcp); hdmi->suspended = true; spin_unlock_irqrestore(&hdmi->suspend_lock, flags); } @@ -1203,8 +1204,8 @@ static void tegra_dc_hdmi_enable(struct tegra_dc *dc) tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL); tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL); - tegra_nvhdcp_set_plug(hdmi->nvhdcp, 1); - + if (!hdmi->dvi) + tegra_nvhdcp_set_plug(hdmi->nvhdcp, 1); } static void tegra_dc_hdmi_disable(struct tegra_dc *dc) diff --git a/drivers/video/tegra/dc/nvhdcp.c b/drivers/video/tegra/dc/nvhdcp.c index 2bec36a53219..7bbe6741be45 100644 --- a/drivers/video/tegra/dc/nvhdcp.c +++ b/drivers/video/tegra/dc/nvhdcp.c @@ -65,7 +65,7 @@ /* for nvhdcp.state */ -enum { +enum tegra_nvhdcp_state { STATE_OFF, STATE_UNAUTHENTICATED, STATE_LINK_VERIFY, @@ -80,9 +80,9 @@ struct tegra_nvhdcp { struct miscdevice miscdev; char name[12]; unsigned id; - atomic_t plugged; /* true if hotplug detected */ + bool plugged; /* true if hotplug detected */ atomic_t policy; /* set policy */ - atomic_t state; /* STATE_xxx */ + enum tegra_nvhdcp_state state; /* STATE_xxx */ struct i2c_client *client; struct i2c_board_info info; int bus; @@ -97,11 +97,20 @@ struct tegra_nvhdcp { u64 m_prime; u32 num_bksv_list; u64 bksv_list[TEGRA_NVHDCP_MAX_DEVS]; + int fail_count; }; -#define TEGRA_NVHDCP_NUM_AP 1 -static struct tegra_nvhdcp *nvhdcp_dev[TEGRA_NVHDCP_NUM_AP]; -static int nvhdcp_ap; +static inline bool nvhdcp_is_plugged(struct tegra_nvhdcp *nvhdcp) +{ + rmb(); + return nvhdcp->plugged; +} + +static inline bool nvhdcp_set_plugged(struct tegra_nvhdcp *nvhdcp, bool plugged) +{ + return nvhdcp->plugged = plugged; + wmb(); +} static int nvhdcp_i2c_read(struct tegra_nvhdcp *nvhdcp, u8 reg, size_t len, void *data) @@ -311,12 +320,13 @@ static inline int get_receiver_ri(struct tegra_nvhdcp *nvhdcp, u16 *r) static int get_bcaps(struct tegra_nvhdcp *nvhdcp, u8 *b_caps) { - int e, retries = 3; + int e, retries = 4; do { e = nvhdcp_i2c_read8(nvhdcp, 0x40, b_caps); if (!e) return 0; - msleep(100); + if (retries > 1) + msleep(100); } while (--retries); return -EIO; @@ -388,7 +398,7 @@ static void hdcp_ctrl_run(struct tegra_dc_hdmi_data *hdmi, bool v) * sleeps up to 120mS */ static int wait_hdcp_ctrl(struct tegra_dc_hdmi_data *hdmi, u32 mask, u32 *v) { - int retries = 12; + int retries = 13; u32 ctrl; do { @@ -398,7 +408,8 @@ static int wait_hdcp_ctrl(struct tegra_dc_hdmi_data *hdmi, u32 mask, u32 *v) *v = ctrl; break; } - msleep(10); + if (retries > 1) + msleep(10); } while (--retries); if (!retries) { nvhdcp_err("ctrl read timeout (mask=0x%x)\n", mask); @@ -411,14 +422,15 @@ static int wait_hdcp_ctrl(struct tegra_dc_hdmi_data *hdmi, u32 mask, u32 *v) * waits up to 100mS */ static int wait_key_ctrl(struct tegra_dc_hdmi_data *hdmi, u32 mask) { - int retries = 100; + int retries = 101; u32 ctrl; do { ctrl = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_KEY_CTRL); if ((ctrl | (mask))) break; - msleep(1); + if (retries > 1) + msleep(1); } while (--retries); if (!retries) { nvhdcp_err("key ctrl read timeout (mask=0x%x)\n", mask); @@ -450,20 +462,22 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet * int e; /* if connection isn't authenticated ... */ - if (atomic_read(&nvhdcp->state) != STATE_LINK_VERIFY) { + mutex_lock(&nvhdcp->lock); + if (nvhdcp->state != STATE_LINK_VERIFY) { memset(pkt, 0, sizeof *pkt); pkt->packet_results = TEGRA_NVHDCP_RESULT_LINK_FAILED; - return 0; + e = 0; + goto err; } - pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; /* we will be taking c_n, c_ksv as input */ if (!(pkt->value_flags & TEGRA_NVHDCP_FLAG_CN) || !(pkt->value_flags & TEGRA_NVHDCP_FLAG_CKSV)) { nvhdcp_err("missing value_flags (0x%x)\n", pkt->value_flags); - return -EINVAL; + e = -EINVAL; + goto err; } pkt->value_flags = 0; @@ -485,7 +499,8 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet * if (e) { nvhdcp_err("Sprime read timeout\n"); pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; - return -EIO; + e = -EIO; + goto err; } msleep(50); @@ -514,7 +529,7 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet * if (verify_ksv(pkt->d_ksv)) { nvhdcp_err("Dksv invalid!\n"); pkt->packet_results = TEGRA_NVHDCP_RESULT_UNSUCCESSFUL; - return -EIO; /* treat bad Dksv as I/O error */ + e = -EIO; /* treat bad Dksv as I/O error */ } pkt->value_flags |= TEGRA_NVHDCP_FLAG_DKSV; @@ -523,7 +538,12 @@ static int get_s_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_packet * pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSV; pkt->packet_results = TEGRA_NVHDCP_RESULT_SUCCESS; + mutex_unlock(&nvhdcp->lock); return 0; + +err: + mutex_unlock(&nvhdcp->lock); + return e; } /* get M prime - READ_M on TMDS0_LINK0 only */ @@ -535,10 +555,12 @@ 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_LINK_VERIFY) { + mutex_lock(&nvhdcp->lock); + if (nvhdcp->state != STATE_LINK_VERIFY) { memset(pkt, 0, sizeof *pkt); pkt->packet_results = TEGRA_NVHDCP_RESULT_LINK_FAILED; - return 0; + e = 0; + goto err; } pkt->a_ksv = nvhdcp->a_ksv; @@ -556,7 +578,8 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p e = wait_hdcp_ctrl(hdmi, MPRIME_VALID, NULL); if (e) { nvhdcp_err("Mprime read timeout\n"); - return -EIO; + e = -EIO; + goto err; } msleep(50); @@ -585,7 +608,8 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p pkt->d_ksv = get_dksv(hdmi); if (verify_ksv(pkt->d_ksv)) { nvhdcp_err("Dksv invalid!\n"); - return -EIO; /* treat bad Dksv as I/O error */ + e = -EIO; + goto err; } pkt->value_flags |= TEGRA_NVHDCP_FLAG_DKSV; @@ -594,7 +618,12 @@ static inline int get_m_prime(struct tegra_nvhdcp *nvhdcp, struct tegra_nvhdcp_p pkt->value_flags |= TEGRA_NVHDCP_FLAG_BKSV; pkt->packet_results = TEGRA_NVHDCP_RESULT_SUCCESS; + mutex_unlock(&nvhdcp->lock); return 0; + +err: + mutex_unlock(&nvhdcp->lock); + return e; } static int load_kfuse(struct tegra_dc_hdmi_data *hdmi) @@ -630,11 +659,12 @@ static int load_kfuse(struct tegra_dc_hdmi_data *hdmi) tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_KEY_SKEY_INDEX); /* wait for SRAM to be cleared */ - retries = 5; + retries = 6; do { tmp = tegra_hdmi_readl(hdmi, HDMI_NV_PDISP_KEY_DEBUG0); if ((tmp & 1) == 0) break; - mdelay(1); + if (retries > 1) + mdelay(1); } while (--retries); if (!retries) { nvhdcp_err("key SRAM clear timeout\n"); @@ -700,7 +730,7 @@ static int verify_link(struct tegra_nvhdcp *nvhdcp, bool wait_ri) nvhdcp_debug("R0 Ri poll:rx=0x%04x tx=0x%04x\n", rx, tx); - if (!atomic_read(&nvhdcp->plugged)) { + if (!nvhdcp_is_plugged(nvhdcp)) { nvhdcp_err("aborting verify links - lost hdmi connection\n"); return -EIO; } @@ -720,9 +750,9 @@ static int get_repeater_info(struct tegra_nvhdcp *nvhdcp) nvhdcp_vdbg("repeater found:fetching repeater info\n"); /* wait up to 5 seconds for READY on repeater */ - retries = 50; + retries = 51; do { - if (!atomic_read(&nvhdcp->plugged)) { + if (!nvhdcp_is_plugged(nvhdcp)) { nvhdcp_err("disconnect while waiting for repeater\n"); return -EIO; } @@ -732,7 +762,8 @@ static int get_repeater_info(struct tegra_nvhdcp *nvhdcp) nvhdcp_debug("Bcaps READY from repeater\n"); break; } - msleep(100); + if (retries > 1) + msleep(100); } while (--retries); if (!retries) { nvhdcp_err("repeater Bcaps read timeout\n"); @@ -786,30 +817,27 @@ static void nvhdcp_downstream_worker(struct work_struct *work) u8 b_caps; u32 tmp; u32 res; - int st; nvhdcp_vdbg("%s():started thread %s\n", __func__, nvhdcp->name); - st = atomic_read(&nvhdcp->state); - if (st == STATE_OFF) { + mutex_lock(&nvhdcp->lock); + if (nvhdcp->state == STATE_OFF) { nvhdcp_err("nvhdcp failure - giving up\n"); - return; + goto err; } - - atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED); + nvhdcp->state = STATE_UNAUTHENTICATED; /* check plug state to terminate early in case flush_workqueue() */ - if (!atomic_read(&nvhdcp->plugged)) { + if (!nvhdcp_is_plugged(nvhdcp)) { nvhdcp_err("worker started while unplugged!\n"); goto lost_hdmi; } + nvhdcp_vdbg("%s():hpd=%d\n", __func__, nvhdcp->plugged); nvhdcp->a_ksv = 0; nvhdcp->b_ksv = 0; nvhdcp->a_n = 0; - nvhdcp_vdbg("%s():hpd=%d\n", __func__, atomic_read(&nvhdcp->plugged)); - e = get_bcaps(nvhdcp, &b_caps); if (e) { nvhdcp_err("Bcaps read failure\n"); @@ -881,7 +909,7 @@ static void nvhdcp_downstream_worker(struct work_struct *work) nvhdcp_vdbg("wrote Aksv = 0x%010llx\n", nvhdcp->a_ksv); /* bail out if unplugged in the middle of negotiation */ - if (!atomic_read(&nvhdcp->plugged)) + if (!nvhdcp_is_plugged(nvhdcp)) goto lost_hdmi; /* get Bksv from receiver */ @@ -937,42 +965,51 @@ static void nvhdcp_downstream_worker(struct work_struct *work) } } - atomic_set(&nvhdcp->state, STATE_LINK_VERIFY); + nvhdcp->state = STATE_LINK_VERIFY; nvhdcp_info("link verified!\n"); - while (atomic_read(&nvhdcp->state) == STATE_LINK_VERIFY) { - if (!atomic_read(&nvhdcp->plugged)) + while (1) { + if (nvhdcp->state != STATE_LINK_VERIFY) + goto failure; + + if (!nvhdcp_is_plugged(nvhdcp)) goto lost_hdmi; + e = verify_link(nvhdcp, true); if (e) { nvhdcp_err("link verification failed err %d\n", e); goto failure; } + mutex_unlock(&nvhdcp->lock); msleep(1500); + mutex_lock(&nvhdcp->lock); + } 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; + nvhdcp->fail_count++; + if(nvhdcp->fail_count > 5) { + nvhdcp_err("nvhdcp failure - too many failures, giving up!\n"); + } else { + nvhdcp_err("nvhdcp failure - renegotiating in 1.75 seconds\n"); + msleep(1750); + queue_work(nvhdcp->downstream_wq, &nvhdcp->work); + } + lost_hdmi: - nvhdcp_err("lost hdmi connection\n"); - atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED); + nvhdcp->state = STATE_UNAUTHENTICATED; hdcp_ctrl_run(hdmi, 0); + +err: + mutex_unlock(&nvhdcp->lock); return; } void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd) { - nvhdcp_info("hdmi hotplug detected (hpd = %d)\n", hpd); + nvhdcp_debug("hdmi hotplug detected (hpd = %d)\n", hpd); - atomic_set(&nvhdcp->plugged, hpd); + nvhdcp_set_plugged(nvhdcp, hpd); if (hpd) { queue_work(nvhdcp->downstream_wq, &nvhdcp->work); @@ -983,36 +1020,45 @@ void tegra_nvhdcp_set_plug(struct tegra_nvhdcp *nvhdcp, bool hpd) static int tegra_nvhdcp_on(struct tegra_nvhdcp *nvhdcp) { - atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED); - if (atomic_read(&nvhdcp->plugged)) + nvhdcp->state = STATE_UNAUTHENTICATED; + if (nvhdcp_is_plugged(nvhdcp)) { + nvhdcp->fail_count = 0; queue_work(nvhdcp->downstream_wq, &nvhdcp->work); + } return 0; } static int tegra_nvhdcp_off(struct tegra_nvhdcp *nvhdcp) { - atomic_set(&nvhdcp->state, STATE_OFF); - atomic_set(&nvhdcp->plugged, 0); /* force early termination */ + mutex_lock(&nvhdcp->lock); + nvhdcp->state = STATE_OFF; + nvhdcp_set_plugged(nvhdcp, false); + mutex_unlock(&nvhdcp->lock); flush_workqueue(nvhdcp->downstream_wq); return 0; } int tegra_nvhdcp_set_policy(struct tegra_nvhdcp *nvhdcp, int pol) { - pol = !!pol; - 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 */ - tegra_nvhdcp_on(nvhdcp); + if (pol == TEGRA_NVHDCP_POLICY_ALWAYS_ON) { + nvhdcp_info("using \"always on\" policy.\n"); + if (atomic_xchg(&nvhdcp->policy, pol) != pol) { + /* policy changed, start working */ + tegra_nvhdcp_on(nvhdcp); + } + } else { + /* unsupported policy */ + return -EINVAL; } + return 0; } static int tegra_nvhdcp_renegotiate(struct tegra_nvhdcp *nvhdcp) { - atomic_set(&nvhdcp->state, STATE_RENEGOTIATE); + mutex_lock(&nvhdcp->lock); + nvhdcp->state = STATE_RENEGOTIATE; + mutex_unlock(&nvhdcp->lock); tegra_nvhdcp_on(nvhdcp); return 0; } @@ -1084,24 +1130,18 @@ kfree_pkt: return e; } -/* every open indexes a new AP link */ static int nvhdcp_dev_open(struct inode *inode, struct file *filp) { - if (nvhdcp_ap >= TEGRA_NVHDCP_NUM_AP) - return -EMFILE; - - if (!nvhdcp_dev[nvhdcp_ap]) - return -ENODEV; - - filp->private_data = nvhdcp_dev[nvhdcp_ap++]; - + struct miscdevice *miscdev = filp->private_data; + struct tegra_nvhdcp *nvhdcp = + container_of(miscdev, struct tegra_nvhdcp, miscdev); + filp->private_data = nvhdcp; return 0; } static int nvhdcp_dev_release(struct inode *inode, struct file *filp) { filp->private_data = NULL; - --nvhdcp_ap; return 0; } @@ -1113,13 +1153,17 @@ static const struct file_operations nvhdcp_fops = { .release = nvhdcp_dev_release, }; +/* we only support one AP right now, so should only call this once. */ struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, int id, int bus) { - struct tegra_nvhdcp *nvhdcp; + static struct tegra_nvhdcp *nvhdcp; /* prevent multiple calls */ struct i2c_adapter *adapter; int e; + if (nvhdcp) + return ERR_PTR(-EMFILE); + nvhdcp = kzalloc(sizeof(*nvhdcp), GFP_KERNEL); if (!nvhdcp) return ERR_PTR(-ENOMEM); @@ -1133,6 +1177,7 @@ struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, nvhdcp->bus = bus; nvhdcp->info.addr = 0x74 >> 1; nvhdcp->info.platform_data = nvhdcp; + nvhdcp->fail_count = 0; adapter = i2c_get_adapter(bus); if (!adapter) { @@ -1150,7 +1195,7 @@ struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, goto free_nvhdcp; } - atomic_set(&nvhdcp->state, STATE_UNAUTHENTICATED); + nvhdcp->state = STATE_UNAUTHENTICATED; nvhdcp->downstream_wq = create_singlethread_workqueue(nvhdcp->name); INIT_WORK(&nvhdcp->work, nvhdcp_downstream_worker); @@ -1165,8 +1210,6 @@ struct tegra_nvhdcp *tegra_nvhdcp_create(struct tegra_dc_hdmi_data *hdmi, nvhdcp_vdbg("%s(): created misc device %s\n", __func__, nvhdcp->name); - nvhdcp_dev[0] = nvhdcp; /* we only support on AP right now */ - return nvhdcp; free_workqueue: destroy_workqueue(nvhdcp->downstream_wq); @@ -1180,8 +1223,7 @@ free_nvhdcp: void tegra_nvhdcp_destroy(struct tegra_nvhdcp *nvhdcp) { misc_deregister(&nvhdcp->miscdev); - atomic_set(&nvhdcp->plugged, 0); /* force early termination */ - flush_workqueue(nvhdcp->downstream_wq); + tegra_nvhdcp_off(nvhdcp); destroy_workqueue(nvhdcp->downstream_wq); i2c_release_client(nvhdcp->client); kfree(nvhdcp); |