]> git.karo-electronics.de Git - linux-beck.git/commitdiff
greybus: lights: Control runtime pm suspend/resume on AP side
authorKris Huang <huang_kris@projectara.com>
Thu, 25 Aug 2016 08:57:14 +0000 (16:57 +0800)
committerGreg Kroah-Hartman <gregkh@google.com>
Fri, 26 Aug 2016 11:16:11 +0000 (13:16 +0200)
According to runtime pm architecture, the kernel side driver should be
as smart as needed to know when the module is idle or active, so that it can
issue the suspend/resume operations to the firmware side at the right time.
To add logics prevents AP from issuing the suspend request to the firmware
when a channel turning to active state, and put it to suspend if the state
is going to inactive with still holding a reference.

Testing Done: Compiled and verified on EVT2 and gpbridge-test module
              with device class daughter board.

Signed-off-by: Kris Huang <huang_kris@projectara.com>
Reviewed-by: Rui Miguel Silva <rui.silva@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/light.c

index 85bf5559f6fb89cdda97a7ac54ee129c02f99d0a..71db077765f705f4c714510c3b2a347a38b32229 100644 (file)
@@ -45,6 +45,8 @@ struct gb_channel {
        bool                            is_registered;
        bool                            releasing;
        bool                            strobe_state;
+       bool                            active;
+       struct mutex                    lock;
 };
 
 struct gb_light {
@@ -384,11 +386,15 @@ static int __gb_lights_led_brightness_set(struct gb_channel *channel)
        struct gb_lights_set_brightness_request req;
        struct gb_connection *connection = get_conn_from_channel(channel);
        struct gb_bundle *bundle = connection->bundle;
+       bool old_active;
        int ret;
 
+       mutex_lock(&channel->lock);
        ret = gb_pm_runtime_get_sync(bundle);
        if (ret < 0)
-               return ret;
+               goto out_unlock;
+
+       old_active = channel->active;
 
        req.light_id = channel->light->id;
        req.channel_id = channel->id;
@@ -396,8 +402,29 @@ static int __gb_lights_led_brightness_set(struct gb_channel *channel)
 
        ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BRIGHTNESS,
                                &req, sizeof(req), NULL, 0);
+       if (ret < 0)
+               goto out_pm_put;
+
+       if (channel->led->brightness)
+               channel->active = true;
+       else
+               channel->active = false;
 
+       /* we need to keep module alive when turning to active state */
+       if (!old_active && channel->active)
+               goto out_unlock;
+
+       /*
+        * on the other hand if going to inactive we still hold a reference and
+        * need to put it, so we could go to suspend.
+        */
+       if (old_active && !channel->active)
+               gb_pm_runtime_put_autosuspend(bundle);
+
+out_pm_put:
        gb_pm_runtime_put_autosuspend(bundle);
+out_unlock:
+       mutex_unlock(&channel->lock);
 
        return ret;
 }
@@ -476,14 +503,18 @@ static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on,
        struct gb_connection *connection = get_conn_from_channel(channel);
        struct gb_bundle *bundle = connection->bundle;
        struct gb_lights_blink_request req;
+       bool old_active;
        int ret;
 
        if (channel->releasing)
                return -ESHUTDOWN;
 
+       mutex_lock(&channel->lock);
        ret = gb_pm_runtime_get_sync(bundle);
        if (ret < 0)
-               return ret;
+               goto out_unlock;
+
+       old_active = channel->active;
 
        req.light_id = channel->light->id;
        req.channel_id = channel->id;
@@ -492,8 +523,29 @@ static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on,
 
        ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BLINK, &req,
                                sizeof(req), NULL, 0);
+       if (ret < 0)
+               goto out_pm_put;
+
+       if (delay_on)
+               channel->active = true;
+       else
+               channel->active = false;
+
+       /* we need to keep module alive when turning to active state */
+       if (!old_active && channel->active)
+               goto out_unlock;
 
+       /*
+        * on the other hand if going to inactive we still hold a reference and
+        * need to put it, so we could go to suspend.
+        */
+       if (old_active && !channel->active)
+               gb_pm_runtime_put_autosuspend(bundle);
+
+out_pm_put:
        gb_pm_runtime_put_autosuspend(bundle);
+out_unlock:
+       mutex_unlock(&channel->lock);
 
        return ret;
 }
@@ -1061,6 +1113,8 @@ static int gb_lights_light_register(struct gb_light *light)
                ret = gb_lights_channel_register(&light->channels[i]);
                if (ret < 0)
                        return ret;
+
+               mutex_init(&light->channels[i].lock);
        }
 
        light->ready = true;
@@ -1086,6 +1140,7 @@ static void gb_lights_channel_free(struct gb_channel *channel)
        kfree(channel->attr_groups);
        kfree(channel->color_name);
        kfree(channel->mode_name);
+       mutex_destroy(&channel->lock);
 }
 
 static void gb_lights_channel_release(struct gb_channel *channel)