Skip to content

Commit

Permalink
drm: hdlcd: Revamp runtime power management
Browse files Browse the repository at this point in the history
Because the HDLCD driver acts as a component master it can end
up enabling the runtime PM functionality before the encoders
are initialised. This can cause crashes if the component slave
never probes (missing module) or if the PM operations kick in
before the probe finishes.

Move the enabling of the runtime PM after the component master
has finished collecting the slave components and use the DRM
atomic helpers to suspend and resume the device.

Tested-by: Robin Murphy <Robin.Murphy@arm.com>
Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
  • Loading branch information
Liviu Dudau authored and Liviu Dudau committed Jun 2, 2016
1 parent 1a695a9 commit a95acec
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 35 deletions.
23 changes: 11 additions & 12 deletions drivers/gpu/drm/arm/hdlcd_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,17 @@
*
*/

static void hdlcd_crtc_cleanup(struct drm_crtc *crtc)
{
struct hdlcd_drm_private *hdlcd = crtc_to_hdlcd_priv(crtc);

/* stop the controller on cleanup */
hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0);
drm_crtc_cleanup(crtc);
}

static const struct drm_crtc_funcs hdlcd_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.destroy = hdlcd_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
.page_flip = drm_atomic_helper_page_flip,
.reset = drm_atomic_helper_crtc_reset,
Expand Down Expand Up @@ -155,8 +164,8 @@ static void hdlcd_crtc_disable(struct drm_crtc *crtc)
if (!crtc->primary->fb)
return;

clk_disable_unprepare(hdlcd->clk);
hdlcd_write(hdlcd, HDLCD_REG_COMMAND, 0);
clk_disable_unprepare(hdlcd->clk);
drm_crtc_vblank_off(crtc);
}

Expand Down Expand Up @@ -294,16 +303,6 @@ static struct drm_plane *hdlcd_plane_init(struct drm_device *drm)
return plane;
}

void hdlcd_crtc_suspend(struct drm_crtc *crtc)
{
hdlcd_crtc_disable(crtc);
}

void hdlcd_crtc_resume(struct drm_crtc *crtc)
{
hdlcd_crtc_enable(crtc);
}

int hdlcd_setup_crtc(struct drm_device *drm)
{
struct hdlcd_drm_private *hdlcd = drm->dev_private;
Expand Down
48 changes: 27 additions & 21 deletions drivers/gpu/drm/arm/hdlcd_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,7 @@ static int hdlcd_load(struct drm_device *drm, unsigned long flags)
goto setup_fail;
}

pm_runtime_enable(drm->dev);

pm_runtime_get_sync(drm->dev);
ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
pm_runtime_put_sync(drm->dev);
if (ret < 0) {
DRM_ERROR("failed to install IRQ handler\n");
goto irq_fail;
Expand Down Expand Up @@ -357,6 +353,8 @@ static int hdlcd_drm_bind(struct device *dev)
return -ENOMEM;

drm->dev_private = hdlcd;
dev_set_drvdata(dev, drm);

hdlcd_setup_mode_config(drm);
ret = hdlcd_load(drm, 0);
if (ret)
Expand All @@ -366,14 +364,18 @@ static int hdlcd_drm_bind(struct device *dev)
if (ret)
goto err_unload;

dev_set_drvdata(dev, drm);

ret = component_bind_all(dev, drm);
if (ret) {
DRM_ERROR("Failed to bind all components\n");
goto err_unregister;
}

ret = pm_runtime_set_active(dev);
if (ret)
goto err_pm_active;

pm_runtime_enable(dev);

ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (ret < 0) {
DRM_ERROR("failed to initialise vblank\n");
Expand All @@ -399,16 +401,16 @@ static int hdlcd_drm_bind(struct device *dev)
drm_mode_config_cleanup(drm);
drm_vblank_cleanup(drm);
err_vblank:
pm_runtime_disable(drm->dev);
err_pm_active:
component_unbind_all(dev, drm);
err_unregister:
drm_dev_unregister(drm);
err_unload:
pm_runtime_get_sync(drm->dev);
drm_irq_uninstall(drm);
pm_runtime_put_sync(drm->dev);
pm_runtime_disable(drm->dev);
of_reserved_mem_device_release(drm->dev);
err_free:
dev_set_drvdata(dev, NULL);
drm_dev_unref(drm);

return ret;
Expand Down Expand Up @@ -495,30 +497,34 @@ MODULE_DEVICE_TABLE(of, hdlcd_of_match);
static int __maybe_unused hdlcd_pm_suspend(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct drm_crtc *crtc;
struct hdlcd_drm_private *hdlcd = drm ? drm->dev_private : NULL;

if (pm_runtime_suspended(dev))
if (!hdlcd)
return 0;

drm_modeset_lock_all(drm);
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
hdlcd_crtc_suspend(crtc);
drm_modeset_unlock_all(drm);
drm_kms_helper_poll_disable(drm);

hdlcd->state = drm_atomic_helper_suspend(drm);
if (IS_ERR(hdlcd->state)) {
drm_kms_helper_poll_enable(drm);
return PTR_ERR(hdlcd->state);
}

return 0;
}

static int __maybe_unused hdlcd_pm_resume(struct device *dev)
{
struct drm_device *drm = dev_get_drvdata(dev);
struct drm_crtc *crtc;
struct hdlcd_drm_private *hdlcd = drm ? drm->dev_private : NULL;

if (!pm_runtime_suspended(dev))
if (!hdlcd)
return 0;

drm_modeset_lock_all(drm);
list_for_each_entry(crtc, &drm->mode_config.crtc_list, head)
hdlcd_crtc_resume(crtc);
drm_modeset_unlock_all(drm);
drm_atomic_helper_resume(drm, hdlcd->state);
drm_kms_helper_poll_enable(drm);
pm_runtime_set_active(dev);

return 0;
}

Expand Down
3 changes: 1 addition & 2 deletions drivers/gpu/drm/arm/hdlcd_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct hdlcd_drm_private {
struct list_head event_list;
struct drm_crtc crtc;
struct drm_plane *plane;
struct drm_atomic_state *state;
#ifdef CONFIG_DEBUG_FS
atomic_t buffer_underrun_count;
atomic_t bus_error_count;
Expand All @@ -36,7 +37,5 @@ static inline u32 hdlcd_read(struct hdlcd_drm_private *hdlcd, unsigned int reg)

int hdlcd_setup_crtc(struct drm_device *dev);
void hdlcd_set_scanout(struct hdlcd_drm_private *hdlcd);
void hdlcd_crtc_suspend(struct drm_crtc *crtc);
void hdlcd_crtc_resume(struct drm_crtc *crtc);

#endif /* __HDLCD_DRV_H__ */

0 comments on commit a95acec

Please sign in to comment.