#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <video/display_timing.h>
bool enabled;
bool no_hpd;
+ bool prepared;
+
ktime_t prepared_time;
ktime_t unprepared_time;
return 0;
}
+static int panel_simple_suspend(struct device *dev)
+{
+ struct panel_simple *p = dev_get_drvdata(dev);
+
+ gpiod_set_value_cansleep(p->enable_gpio, 0);
+ regulator_disable(p->supply);
+ p->unprepared_time = ktime_get();
+
+ return 0;
+}
+
static int panel_simple_unprepare(struct drm_panel *panel)
{
struct panel_simple *p = to_panel_simple(panel);
+ int ret;
- if (p->prepared_time == 0)
+ /* Unpreparing when already unprepared is a no-op */
+ if (!p->prepared)
return 0;
- gpiod_set_value_cansleep(p->enable_gpio, 0);
-
- regulator_disable(p->supply);
-
- p->prepared_time = 0;
- p->unprepared_time = ktime_get();
+ pm_runtime_mark_last_busy(panel->dev);
+ ret = pm_runtime_put_autosuspend(panel->dev);
+ if (ret < 0)
+ return ret;
+ p->prepared = false;
return 0;
}
return 0;
}
-static int panel_simple_prepare_once(struct drm_panel *panel)
+static int panel_simple_prepare_once(struct panel_simple *p)
{
- struct panel_simple *p = to_panel_simple(panel);
+ struct device *dev = p->base.dev;
unsigned int delay;
int err;
int hpd_asserted;
unsigned long hpd_wait_us;
- if (p->prepared_time != 0)
- return 0;
-
panel_simple_wait(p->unprepared_time, p->desc->delay.unprepare);
err = regulator_enable(p->supply);
if (err < 0) {
- dev_err(panel->dev, "failed to enable supply: %d\n", err);
+ dev_err(dev, "failed to enable supply: %d\n", err);
return err;
}
if (p->hpd_gpio) {
if (IS_ERR(p->hpd_gpio)) {
- err = panel_simple_get_hpd_gpio(panel->dev, p, false);
+ err = panel_simple_get_hpd_gpio(dev, p, false);
if (err)
goto error;
}
if (err) {
if (err != -ETIMEDOUT)
- dev_err(panel->dev,
+ dev_err(dev,
"error waiting for hpd GPIO: %d\n", err);
goto error;
}
*/
#define MAX_PANEL_PREPARE_TRIES 5
-static int panel_simple_prepare(struct drm_panel *panel)
+static int panel_simple_resume(struct device *dev)
{
+ struct panel_simple *p = dev_get_drvdata(dev);
int ret;
int try;
for (try = 0; try < MAX_PANEL_PREPARE_TRIES; try++) {
- ret = panel_simple_prepare_once(panel);
+ ret = panel_simple_prepare_once(p);
if (ret != -ETIMEDOUT)
break;
}
if (ret == -ETIMEDOUT)
- dev_err(panel->dev, "Prepare timeout after %d tries\n", try);
+ dev_err(dev, "Prepare timeout after %d tries\n", try);
else if (try)
- dev_warn(panel->dev, "Prepare needed %d retries\n", try);
+ dev_warn(dev, "Prepare needed %d retries\n", try);
return ret;
}
+static int panel_simple_prepare(struct drm_panel *panel)
+{
+ struct panel_simple *p = to_panel_simple(panel);
+ int ret;
+
+ /* Preparing when already prepared is a no-op */
+ if (p->prepared)
+ return 0;
+
+ ret = pm_runtime_get_sync(panel->dev);
+ if (ret < 0) {
+ pm_runtime_put_autosuspend(panel->dev);
+ return ret;
+ }
+
+ p->prepared = true;
+
+ return 0;
+}
+
static int panel_simple_enable(struct drm_panel *panel)
{
struct panel_simple *p = to_panel_simple(panel);
break;
}
+ dev_set_drvdata(dev, panel);
+
+ /*
+ * We use runtime PM for prepare / unprepare since those power the panel
+ * on and off and those can be very slow operations. This is important
+ * to optimize powering the panel on briefly to read the EDID before
+ * fully enabling the panel.
+ */
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+
drm_panel_init(&panel->base, dev, &panel_simple_funcs, connector_type);
err = drm_panel_of_backlight(&panel->base);
drm_panel_add(&panel->base);
- dev_set_drvdata(dev, panel);
-
return 0;
free_ddc:
panel_simple_shutdown(&pdev->dev);
}
+static const struct dev_pm_ops panel_simple_pm_ops = {
+ SET_RUNTIME_PM_OPS(panel_simple_suspend, panel_simple_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+};
+
static struct platform_driver panel_simple_platform_driver = {
.driver = {
.name = "panel-simple",
.of_match_table = platform_of_match,
+ .pm = &panel_simple_pm_ops,
},
.probe = panel_simple_platform_probe,
.remove = panel_simple_platform_remove,
.driver = {
.name = "panel-simple-dsi",
.of_match_table = dsi_of_match,
+ .pm = &panel_simple_pm_ops,
},
.probe = panel_simple_dsi_probe,
.remove = panel_simple_dsi_remove,