greybus: camera: add runtime pm support
authorDavid Lin <dtwlin@google.com>
Thu, 14 Jul 2016 20:13:00 +0000 (15:13 -0500)
committerAlex Elder <elder@linaro.org>
Thu, 14 Jul 2016 21:53:55 +0000 (16:53 -0500)
Add runtime pm support to camera protocol device class driver.

Testing Done:
 - Make sure white camera module is able to runtime suspend and resume
   when the camera is being used

Signed-off-by: David Lin <dtwlin@google.com>
Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@linaro.org>
Signed-off-by: Alex Elder <elder@linaro.org>
drivers/staging/greybus/camera.c

index ce0ff75..bdceb77 100644 (file)
@@ -333,6 +333,10 @@ static int gb_camera_capabilities(struct gb_camera *gcam,
        struct gb_operation *op = NULL;
        int ret;
 
+       ret = gb_pm_runtime_get_sync(gcam->bundle);
+       if (ret)
+               return ret;
+
        mutex_lock(&gcam->mutex);
 
        if (!gcam->connection) {
@@ -362,6 +366,9 @@ done:
        mutex_unlock(&gcam->mutex);
        if (op)
                gb_operation_put(op);
+
+       gb_pm_runtime_put_autosuspend(gcam->bundle);
+
        return ret;
 }
 
@@ -408,6 +415,10 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
 
        mutex_lock(&gcam->mutex);
 
+       ret = gb_pm_runtime_get_sync(gcam->bundle);
+       if (ret)
+               goto done_skip_pm_put;
+
        if (!gcam->connection) {
                ret = -EINVAL;
                goto done;
@@ -460,9 +471,24 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
        if (gcam->state == GB_CAMERA_STATE_CONFIGURED) {
                gb_camera_teardown_data_connection(gcam);
                gcam->state = GB_CAMERA_STATE_UNCONFIGURED;
+
+               /*
+                * When unconfiguring streams release the PM runtime reference
+                * that was acquired when streams were configured. The bundle
+                * won't be suspended until the PM runtime reference acquired at
+                * the beginning of this function gets released right before
+                * returning.
+                */
+               gb_pm_runtime_put_noidle(gcam->bundle);
        }
 
        if (resp->num_streams) {
+               /*
+                * Make sure the bundle won't be suspended until streams get
+                * unconfigured after the stream is configured successfully
+                */
+               gb_pm_runtime_get_noresume(gcam->bundle);
+
                ret = gb_camera_setup_data_connection(gcam, resp, csi_params);
                if (ret < 0) {
                        memset(req, 0, sizeof(*req));
@@ -472,6 +498,7 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
                                          resp, sizeof(*resp));
                        *flags = 0;
                        *num_streams = 0;
+                       gb_pm_runtime_put_noidle(gcam->bundle);
                        goto done;
                }
 
@@ -479,6 +506,9 @@ static int gb_camera_configure_streams(struct gb_camera *gcam,
        }
 
 done:
+       gb_pm_runtime_put_autosuspend(gcam->bundle);
+
+done_skip_pm_put:
        mutex_unlock(&gcam->mutex);
        kfree(req);
        kfree(resp);
@@ -1150,6 +1180,8 @@ static int gb_camera_probe(struct gb_bundle *bundle,
 
        greybus_set_drvdata(bundle, gcam);
 
+       gb_pm_runtime_put_autosuspend(gcam->bundle);
+
        return 0;
 
 error:
@@ -1161,6 +1193,11 @@ error:
 static void gb_camera_disconnect(struct gb_bundle *bundle)
 {
        struct gb_camera *gcam = greybus_get_drvdata(bundle);
+       int ret;
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               gb_pm_runtime_get_noresume(bundle);
 
        gb_camera_cleanup(gcam);
        gb_camera_unregister(&gcam->module);
@@ -1171,11 +1208,55 @@ static const struct greybus_bundle_id gb_camera_id_table[] = {
        { },
 };
 
+#ifdef CONFIG_PM_RUNTIME
+static int gb_camera_suspend(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       struct gb_camera *gcam = greybus_get_drvdata(bundle);
+
+       if (gcam->data_connection)
+               gb_connection_disable(gcam->data_connection);
+
+       gb_connection_disable(gcam->connection);
+
+       return 0;
+}
+
+static int gb_camera_resume(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       struct gb_camera *gcam = greybus_get_drvdata(bundle);
+       int ret;
+
+       ret = gb_connection_enable(gcam->connection);
+       if (ret) {
+               gcam_err(gcam, "failed to enable connection: %d\n", ret);
+               return ret;
+       }
+
+       if (gcam->data_connection) {
+               ret = gb_connection_enable(gcam->data_connection);
+               if (ret) {
+                       gcam_err(gcam,
+                                "failed to enable data connection: %d\n", ret);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops gb_camera_pm_ops = {
+       SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL)
+};
+
 static struct greybus_driver gb_camera_driver = {
        .name           = "camera",
        .probe          = gb_camera_probe,
        .disconnect     = gb_camera_disconnect,
        .id_table       = gb_camera_id_table,
+       .driver.pm      = &gb_camera_pm_ops,
 };
 
 module_greybus_driver(gb_camera_driver);