summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus/light.c
diff options
context:
space:
mode:
authorKris Huang <huang_kris@projectara.com>2016-08-25 16:57:14 +0800
committerGreg Kroah-Hartman <gregkh@google.com>2016-08-26 13:16:11 +0200
commitcc43368a3cde151739ad20cbf71139530bd53f1a (patch)
tree3d585a5fcdcea9148211bb1b40cfd27a21c43705 /drivers/staging/greybus/light.c
parent0e352343c2e371ad618d8850f542e9ca230e3aa6 (diff)
greybus: lights: Control runtime pm suspend/resume on AP side
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>
Diffstat (limited to 'drivers/staging/greybus/light.c')
-rw-r--r--drivers/staging/greybus/light.c59
1 files changed, 57 insertions, 2 deletions
diff --git a/drivers/staging/greybus/light.c b/drivers/staging/greybus/light.c
index 85bf5559f6fb..71db077765f7 100644
--- a/drivers/staging/greybus/light.c
+++ b/drivers/staging/greybus/light.c
@@ -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)