summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorNitin Kumbhar <nkumbhar@nvidia.com>2011-03-01 12:32:50 +0530
committerNitin Kumbhar <nkumbhar@nvidia.com>2011-03-01 12:32:50 +0530
commit3c1bf41f41587ba952ba15755d0f0592ca424ee1 (patch)
treed7ea17065d88cba1da864f28426cc5c06f7dcd60 /drivers
parent489747ad5e710815778d251fb91249f9595d44b1 (diff)
parent0fcb985c6a9b535c2501eaa041d8ad9289e4ae92 (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
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/gadget/android.c5
-rw-r--r--drivers/video/tegra/dc/hdmi.c5
-rw-r--r--drivers/video/tegra/dc/nvhdcp.c206
3 files changed, 131 insertions, 85 deletions
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);