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>
This commit is contained in:
Kris Huang 2016-08-25 16:57:14 +08:00 committed by Greg Kroah-Hartman
parent 0e352343c2
commit cc43368a3c

View 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)