usb: gadget: Tegra: fix audio stutter
David Yu [Mon, 13 Jan 2014 01:24:50 +0000 (10:24 +0900)]
Boost CPU freq when cable is plugged in/out.
Use high priority single thread workqueue to run work function earlier.

Bug 1435328

Change-Id: Ib2192fa5c30546d0b4e47dec6479c715d58bf795
Signed-off-by: David Yu <davyu@nvidia.com>
Reviewed-on: http://git-master/r/354831
Reviewed-by: Automatic_Commit_Validation_User
GVS: Gerrit_Virtual_Submit
Reviewed-by: Harry Hong <hhong@nvidia.com>
Tested-by: Harry Hong <hhong@nvidia.com>

drivers/usb/gadget/tegra_udc.c
drivers/usb/gadget/tegra_udc.h

index bf9ca45..399053a 100644 (file)
@@ -40,7 +40,6 @@
 #include <linux/io.h>
 #include <linux/pm_qos.h>
 #include <linux/platform_data/tegra_usb.h>
-#include <linux/timer.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
@@ -104,8 +103,6 @@ static struct tegra_udc *the_udc;
 #ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
 static struct pm_qos_request boost_cpu_freq_req;
 static u32 ep_queue_request_count;
-static u8 boost_cpufreq_work_flag, set_cpufreq_normal_flag;
-static struct timer_list boost_timer;
 static bool boost_enable = true;
 static int boost_enable_set(const char *arg, const struct kernel_param *kp)
 {
@@ -981,7 +978,7 @@ tegra_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 #ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
        if (req->req.length >= BOOST_TRIGGER_SIZE) {
                ep_queue_request_count++;
-               schedule_work(&udc->boost_cpufreq_work);
+               queue_work(udc->boost_cpufreq_wq, &udc->boost_cpufreq_work);
        }
 #endif
 
@@ -1504,6 +1501,9 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active)
                udc->vbus_active ? "on" : "off", is_active ? "on" : "off");
 
        if (udc->vbus_active && !is_active) {
+#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
+               queue_work(udc->boost_cpufreq_wq, &udc->boost_cpufreq_work);
+#endif
                /* If cable disconnected, cancel any delayed work */
                cancel_delayed_work_sync(&udc->non_std_charger_work);
                spin_lock_irqsave(&udc->lock, flags);
@@ -1519,6 +1519,9 @@ static int tegra_vbus_session(struct usb_gadget *gadget, int is_active)
                tegra_usb_phy_power_off(udc->phy);
                tegra_usb_set_charging_current(udc);
        } else if (!udc->vbus_active && is_active) {
+#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
+               queue_work(udc->boost_cpufreq_wq, &udc->boost_cpufreq_work);
+#endif
                tegra_usb_phy_power_on(udc->phy);
                /* setup the controller in the device mode */
                dr_controller_setup(udc);
@@ -2363,36 +2366,16 @@ static void tegra_udc_set_current_limit_work(struct work_struct *work)
 }
 
 #ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
-void tegra_udc_set_cpu_freq_normal(unsigned long data)
-{
-       set_cpufreq_normal_flag = 1;
-       schedule_work(&the_udc->boost_cpufreq_work);
-}
-
 static void tegra_udc_boost_cpu_frequency_work(struct work_struct *work)
 {
-       if (set_cpufreq_normal_flag) {
-               pm_qos_update_request(&boost_cpu_freq_req,
-                                       PM_QOS_DEFAULT_VALUE);
-               boost_cpufreq_work_flag = 1;
-               set_cpufreq_normal_flag = 0;
-               DBG("%s(%d) set CPU frequency to normal\n", __func__,
-                                                       __LINE__);
-               return ;
-       }
-
        /* If CPU frequency is not boosted earlier boost it, and modify
         * timer expiry time to 2sec */
-       if (boost_cpufreq_work_flag) {
-               if (boost_enable)
-                       pm_qos_update_request(
-                               &boost_cpu_freq_req,
-                               (s32)(CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
-                                     * 1000));
-               boost_cpufreq_work_flag = 0;
-               DBG("%s(%d) boost CPU frequency\n", __func__, __LINE__);
-       }
-       mod_timer(&boost_timer, jiffies + msecs_to_jiffies(2000));
+       if (boost_enable)
+               pm_qos_update_request_timeout(
+                       &boost_cpu_freq_req,
+                       (s32)(CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ * 1000),
+                       2 * USEC_PER_SEC);
+       DBG("%s(%d) boost CPU frequency\n", __func__, __LINE__);
 }
 #endif
 
@@ -2917,13 +2900,15 @@ static int __init tegra_udc_probe(struct platform_device *pdev)
        if (err)
                goto err_del_udc;
 #ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
-       boost_cpufreq_work_flag = 1;
+       udc->boost_cpufreq_wq = alloc_workqueue(driver_name,
+                                   WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+       if (!udc->boost_cpufreq_wq)
+               return -ENOMEM;
        ep_queue_request_count = 0;
        INIT_WORK(&udc->boost_cpufreq_work,
                                        tegra_udc_boost_cpu_frequency_work);
        pm_qos_add_request(&boost_cpu_freq_req, PM_QOS_CPU_FREQ_MIN,
                                        PM_QOS_DEFAULT_VALUE);
-       setup_timer(&boost_timer, tegra_udc_set_cpu_freq_normal, 0);
 #endif
 
        /* Create work for controlling clocks to the phy if otg is disabled */
@@ -3035,7 +3020,7 @@ static int __exit tegra_udc_remove(struct platform_device *pdev)
 #ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ
        cancel_work_sync(&udc->boost_cpufreq_work);
        pm_qos_remove_request(&boost_cpu_freq_req);
-       del_timer(&boost_timer);
+       destroy_workqueue(udc->boost_cpufreq_wq);
 #endif
 
        if (udc->vbus_reg)
index f87aaf3..79f2cad 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2013, NVIDIA CORPORATION.  All rights reserved.
+ * Copyright (c) 2012-2014, NVIDIA CORPORATION.  All rights reserved.
  *
  * Description:
  * High-speed USB device controller driver.
@@ -438,6 +438,7 @@ struct tegra_udc {
        struct work_struct current_work;
        /* work for boosting cpu frequency */
        struct work_struct boost_cpufreq_work;
+       struct workqueue_struct *boost_cpufreq_wq;
        /* irq work for controlling the usb power */
        struct work_struct irq_work;
        enum tegra_connect_type connect_type;