diff options
Diffstat (limited to 'drivers/gpu/drm/stm')
-rw-r--r-- | drivers/gpu/drm/stm/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/drv.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/dw_mipi_dsi-stm.c | 105 | ||||
-rw-r--r-- | drivers/gpu/drm/stm/ltdc.c | 142 |
5 files changed, 231 insertions, 62 deletions
diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig index d15b10de1da6..b7d66915a2be 100644 --- a/drivers/gpu/drm/stm/Kconfig +++ b/drivers/gpu/drm/stm/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config DRM_STM tristate "DRM Support for STMicroelectronics SoC Series" depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM) diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile index d883adc365a2..4df5caf01f35 100644 --- a/drivers/gpu/drm/stm/Makefile +++ b/drivers/gpu/drm/stm/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only stm-drm-y := \ drv.o \ ltdc.o diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 5834ef56fbaa..9dee4e430de5 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -12,6 +12,7 @@ #include <linux/dma-mapping.h> #include <linux/module.h> #include <linux/of_platform.h> +#include <linux/pm_runtime.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> @@ -135,14 +136,14 @@ static __maybe_unused int drv_suspend(struct device *dev) struct ltdc_device *ldev = ddev->dev_private; struct drm_atomic_state *state; - drm_kms_helper_poll_disable(ddev); + WARN_ON(ldev->suspend_state); + state = drm_atomic_helper_suspend(ddev); - if (IS_ERR(state)) { - drm_kms_helper_poll_enable(ddev); + if (IS_ERR(state)) return PTR_ERR(state); - } + ldev->suspend_state = state; - ltdc_suspend(ddev); + pm_runtime_force_suspend(dev); return 0; } @@ -151,16 +152,43 @@ static __maybe_unused int drv_resume(struct device *dev) { struct drm_device *ddev = dev_get_drvdata(dev); struct ltdc_device *ldev = ddev->dev_private; + int ret; + + if (WARN_ON(!ldev->suspend_state)) + return -ENOENT; + + pm_runtime_force_resume(dev); + ret = drm_atomic_helper_resume(ddev, ldev->suspend_state); + if (ret) + pm_runtime_force_suspend(dev); + + ldev->suspend_state = NULL; + + return ret; +} - ltdc_resume(ddev); - drm_atomic_helper_resume(ddev, ldev->suspend_state); - drm_kms_helper_poll_enable(ddev); +static __maybe_unused int drv_runtime_suspend(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + + DRM_DEBUG_DRIVER("\n"); + ltdc_suspend(ddev); return 0; } +static __maybe_unused int drv_runtime_resume(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + + DRM_DEBUG_DRIVER("\n"); + return ltdc_resume(ddev); +} + static const struct dev_pm_ops drv_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(drv_suspend, drv_resume) + SET_RUNTIME_PM_OPS(drv_runtime_suspend, + drv_runtime_resume, NULL) }; static int stm_drm_platform_probe(struct platform_device *pdev) diff --git a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c index 1bef73e8c8fe..0ab32fee6c1b 100644 --- a/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c +++ b/drivers/gpu/drm/stm/dw_mipi_dsi-stm.c @@ -9,6 +9,7 @@ #include <linux/clk.h> #include <linux/iopoll.h> #include <linux/module.h> +#include <linux/regulator/consumer.h> #include <drm/drmP.h> #include <drm/drm_mipi_dsi.h> #include <drm/bridge/dw_mipi_dsi.h> @@ -76,6 +77,7 @@ struct dw_mipi_dsi_stm { u32 hw_version; int lane_min_kbps; int lane_max_kbps; + struct regulator *vdd_supply; }; static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val) @@ -208,10 +210,27 @@ static int dw_mipi_dsi_phy_init(void *priv_data) if (ret) DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n"); + return 0; +} + +static void dw_mipi_dsi_phy_power_on(void *priv_data) +{ + struct dw_mipi_dsi_stm *dsi = priv_data; + + DRM_DEBUG_DRIVER("\n"); + /* Enable the DSI wrapper */ dsi_set(dsi, DSI_WCR, WCR_DSIEN); +} - return 0; +static void dw_mipi_dsi_phy_power_off(void *priv_data) +{ + struct dw_mipi_dsi_stm *dsi = priv_data; + + DRM_DEBUG_DRIVER("\n"); + + /* Disable the DSI wrapper */ + dsi_clear(dsi, DSI_WCR, WCR_DSIEN); } static int @@ -225,7 +244,6 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, u32 val; /* Update lane capabilities according to hw version */ - dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; dsi->lane_min_kbps = LANE_MIN_KBPS; dsi->lane_max_kbps = LANE_MAX_KBPS; if (dsi->hw_version == HWVER_131) { @@ -286,6 +304,8 @@ dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = { .init = dw_mipi_dsi_phy_init, + .power_on = dw_mipi_dsi_phy_power_on, + .power_off = dw_mipi_dsi_phy_power_off, .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, }; @@ -304,6 +324,7 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dw_mipi_dsi_stm *dsi; + struct clk *pclk; struct resource *res; int ret; @@ -314,21 +335,58 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); dsi->base = devm_ioremap_resource(dev, res); if (IS_ERR(dsi->base)) { - DRM_ERROR("Unable to get dsi registers\n"); - return PTR_ERR(dsi->base); + ret = PTR_ERR(dsi->base); + DRM_ERROR("Unable to get dsi registers %d\n", ret); + return ret; + } + + dsi->vdd_supply = devm_regulator_get(dev, "phy-dsi"); + if (IS_ERR(dsi->vdd_supply)) { + ret = PTR_ERR(dsi->vdd_supply); + if (ret != -EPROBE_DEFER) + DRM_ERROR("Failed to request regulator: %d\n", ret); + return ret; + } + + ret = regulator_enable(dsi->vdd_supply); + if (ret) { + DRM_ERROR("Failed to enable regulator: %d\n", ret); + return ret; } dsi->pllref_clk = devm_clk_get(dev, "ref"); if (IS_ERR(dsi->pllref_clk)) { ret = PTR_ERR(dsi->pllref_clk); - dev_err(dev, "Unable to get pll reference clock: %d\n", ret); - return ret; + DRM_ERROR("Unable to get pll reference clock: %d\n", ret); + goto err_clk_get; } ret = clk_prepare_enable(dsi->pllref_clk); if (ret) { - dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__); - return ret; + DRM_ERROR("Failed to enable pllref_clk: %d\n", ret); + goto err_clk_get; + } + + pclk = devm_clk_get(dev, "pclk"); + if (IS_ERR(pclk)) { + ret = PTR_ERR(pclk); + DRM_ERROR("Unable to get peripheral clock: %d\n", ret); + goto err_dsi_probe; + } + + ret = clk_prepare_enable(pclk); + if (ret) { + DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__); + goto err_dsi_probe; + } + + dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION; + clk_disable_unprepare(pclk); + + if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) { + ret = -ENODEV; + DRM_ERROR("bad dsi hardware version\n"); + goto err_dsi_probe; } dw_mipi_dsi_stm_plat_data.base = dsi->base; @@ -338,20 +396,28 @@ static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); if (IS_ERR(dsi->dsi)) { - DRM_ERROR("Failed to initialize mipi dsi host\n"); - clk_disable_unprepare(dsi->pllref_clk); - return PTR_ERR(dsi->dsi); + ret = PTR_ERR(dsi->dsi); + DRM_ERROR("Failed to initialize mipi dsi host: %d\n", ret); + goto err_dsi_probe; } return 0; + +err_dsi_probe: + clk_disable_unprepare(dsi->pllref_clk); +err_clk_get: + regulator_disable(dsi->vdd_supply); + + return ret; } static int dw_mipi_dsi_stm_remove(struct platform_device *pdev) { struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev); - clk_disable_unprepare(dsi->pllref_clk); dw_mipi_dsi_remove(dsi->dsi); + clk_disable_unprepare(dsi->pllref_clk); + regulator_disable(dsi->vdd_supply); return 0; } @@ -363,6 +429,7 @@ static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) DRM_DEBUG_DRIVER("\n"); clk_disable_unprepare(dsi->pllref_clk); + regulator_disable(dsi->vdd_supply); return 0; } @@ -370,10 +437,22 @@ static int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev) static int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev) { struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; + int ret; DRM_DEBUG_DRIVER("\n"); - clk_prepare_enable(dsi->pllref_clk); + ret = regulator_enable(dsi->vdd_supply); + if (ret) { + DRM_ERROR("Failed to enable regulator: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(dsi->pllref_clk); + if (ret) { + regulator_disable(dsi->vdd_supply); + DRM_ERROR("Failed to enable pllref_clk: %d\n", ret); + return ret; + } return 0; } diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c index 32fd6a3b37fb..2fe6c4a8d915 100644 --- a/drivers/gpu/drm/stm/ltdc.c +++ b/drivers/gpu/drm/stm/ltdc.c @@ -16,6 +16,7 @@ #include <linux/of_address.h> #include <linux/of_graph.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/reset.h> #include <drm/drm_atomic.h> @@ -232,6 +233,11 @@ static const enum ltdc_pix_fmt ltdc_pix_fmt_a1[NB_PF] = { PF_ARGB4444 /* 0x07 */ }; +static const u64 ltdc_format_modifiers[] = { + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID +}; + static inline u32 reg_read(void __iomem *base, u32 reg) { return readl_relaxed(base + reg); @@ -426,8 +432,8 @@ static void ltdc_crtc_atomic_enable(struct drm_crtc *crtc, /* Enable IRQ */ reg_set(ldev->regs, LTDC_IER, IER_RRIE | IER_FUIE | IER_TERRIE); - /* Immediately commit the planes */ - reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); + /* Commit shadow registers = update planes at next vblank */ + reg_set(ldev->regs, LTDC_SRCR, SRCR_VBR); /* Enable LTDC */ reg_set(ldev->regs, LTDC_GCR, GCR_LTDCEN); @@ -439,6 +445,7 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); + struct drm_device *ddev = crtc->dev; DRM_DEBUG_DRIVER("\n"); @@ -452,6 +459,8 @@ static void ltdc_crtc_atomic_disable(struct drm_crtc *crtc, /* immediately commit disable of layers before switching off LTDC */ reg_set(ldev->regs, LTDC_SRCR, SRCR_IMR); + + pm_runtime_put_sync(ddev->dev); } #define CLK_TOLERANCE_HZ 50 @@ -500,33 +509,55 @@ static bool ltdc_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *adjusted_mode) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); + struct drm_device *ddev = crtc->dev; int rate = mode->clock * 1000; + bool runtime_active; + int ret; - /* - * TODO clk_round_rate() does not work yet. When ready, it can - * be used instead of clk_set_rate() then clk_get_rate(). - */ + runtime_active = pm_runtime_active(ddev->dev); + + if (runtime_active) + pm_runtime_put_sync(ddev->dev); - clk_disable(ldev->pixel_clk); if (clk_set_rate(ldev->pixel_clk, rate) < 0) { DRM_ERROR("Cannot set rate (%dHz) for pixel clk\n", rate); return false; } - clk_enable(ldev->pixel_clk); adjusted_mode->clock = clk_get_rate(ldev->pixel_clk) / 1000; + if (runtime_active) { + ret = pm_runtime_get_sync(ddev->dev); + if (ret) { + DRM_ERROR("Failed to fixup mode, cannot get sync\n"); + return false; + } + } + + DRM_DEBUG_DRIVER("requested clock %dkHz, adjusted clock %dkHz\n", + mode->clock, adjusted_mode->clock); + return true; } static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); + struct drm_device *ddev = crtc->dev; struct drm_display_mode *mode = &crtc->state->adjusted_mode; struct videomode vm; u32 hsync, vsync, accum_hbp, accum_vbp, accum_act_w, accum_act_h; u32 total_width, total_height; u32 val; + int ret; + + if (!pm_runtime_active(ddev->dev)) { + ret = pm_runtime_get_sync(ddev->dev); + if (ret) { + DRM_ERROR("Failed to set mode, cannot get sync\n"); + return; + } + } drm_display_mode_to_videomode(mode, &vm); @@ -555,7 +586,7 @@ static void ltdc_crtc_mode_set_nofb(struct drm_crtc *crtc) if (vm.flags & DISPLAY_FLAGS_VSYNC_HIGH) val |= GCR_VSPOL; - if (vm.flags & DISPLAY_FLAGS_DE_HIGH) + if (vm.flags & DISPLAY_FLAGS_DE_LOW) val |= GCR_DEPOL; if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) @@ -587,6 +618,7 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, struct drm_crtc_state *old_crtc_state) { struct ltdc_device *ldev = crtc_to_ltdc(crtc); + struct drm_device *ddev = crtc->dev; struct drm_pending_vblank_event *event = crtc->state->event; DRM_DEBUG_ATOMIC("\n"); @@ -599,12 +631,12 @@ static void ltdc_crtc_atomic_flush(struct drm_crtc *crtc, if (event) { crtc->state->event = NULL; - spin_lock_irq(&crtc->dev->event_lock); + spin_lock_irq(&ddev->event_lock); if (drm_crtc_vblank_get(crtc) == 0) drm_crtc_arm_vblank_event(crtc, event); else drm_crtc_send_vblank_event(crtc, event); - spin_unlock_irq(&crtc->dev->event_lock); + spin_unlock_irq(&ddev->event_lock); } } @@ -660,15 +692,19 @@ bool ltdc_crtc_scanoutpos(struct drm_device *ddev, unsigned int pipe, * Computation for the two first cases are identical so we can * simplify the code and only test if line > vactive_end */ - line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; - vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; - vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; - vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; - - if (line > vactive_end) - *vpos = line - vtotal - vactive_start; - else - *vpos = line - vactive_start; + if (pm_runtime_active(ddev->dev)) { + line = reg_read(ldev->regs, LTDC_CPSR) & CPSR_CYPOS; + vactive_start = reg_read(ldev->regs, LTDC_BPCR) & BPCR_AVBP; + vactive_end = reg_read(ldev->regs, LTDC_AWCR) & AWCR_AAH; + vtotal = reg_read(ldev->regs, LTDC_TWCR) & TWCR_TOTALH; + + if (line > vactive_end) + *vpos = line - vtotal - vactive_start; + else + *vpos = line - vactive_start; + } else { + *vpos = 0; + } *hpos = 0; @@ -779,7 +815,7 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, /* Configures the color frame buffer pitch in bytes & line length */ pitch_in_bytes = fb->pitches[0]; - line_length = drm_format_plane_cpp(fb->format->format, 0) * + line_length = fb->format->cpp[0] * (x1 - x0 + 1) + (ldev->caps.bus_width >> 3) - 1; val = ((pitch_in_bytes << 16) | line_length); reg_update_bits(ldev->regs, LTDC_L1CFBLR + lofs, @@ -822,11 +858,11 @@ static void ltdc_plane_atomic_update(struct drm_plane *plane, mutex_lock(&ldev->err_lock); if (ldev->error_status & ISR_FUIF) { - DRM_DEBUG_DRIVER("Fifo underrun\n"); + DRM_WARN("ltdc fifo underrun: please verify display mode\n"); ldev->error_status &= ~ISR_FUIF; } if (ldev->error_status & ISR_TERRIF) { - DRM_DEBUG_DRIVER("Transfer error\n"); + DRM_WARN("ltdc transfer error\n"); ldev->error_status &= ~ISR_TERRIF; } mutex_unlock(&ldev->err_lock); @@ -864,6 +900,16 @@ static void ltdc_plane_atomic_print_state(struct drm_printer *p, fpsi->counter = 0; } +static bool ltdc_plane_format_mod_supported(struct drm_plane *plane, + u32 format, + u64 modifier) +{ + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; + + return false; +} + static const struct drm_plane_funcs ltdc_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, @@ -872,6 +918,7 @@ static const struct drm_plane_funcs ltdc_plane_funcs = { .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, .atomic_print_state = ltdc_plane_atomic_print_state, + .format_mod_supported = ltdc_plane_format_mod_supported, }; static const struct drm_plane_helper_funcs ltdc_plane_helper_funcs = { @@ -890,6 +937,7 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, unsigned int i, nb_fmt = 0; u32 formats[NB_PF * 2]; u32 drm_fmt, drm_fmt_no_alpha; + const u64 *modifiers = ltdc_format_modifiers; int ret; /* Get supported pixel formats */ @@ -918,7 +966,7 @@ static struct drm_plane *ltdc_plane_create(struct drm_device *ddev, ret = drm_universal_plane_init(ddev, plane, possible_crtcs, <dc_plane_funcs, formats, nb_fmt, - NULL, type, NULL); + modifiers, type, NULL); if (ret < 0) return NULL; @@ -1021,10 +1069,13 @@ static int ltdc_get_caps(struct drm_device *ddev) struct ltdc_device *ldev = ddev->dev_private; u32 bus_width_log2, lcr, gc2r; - /* at least 1 layer must be managed */ + /* + * at least 1 layer must be managed & the number of layers + * must not exceed LTDC_MAX_LAYER + */ lcr = reg_read(ldev->regs, LTDC_LCR); - ldev->caps.nb_layers = max_t(int, lcr, 1); + ldev->caps.nb_layers = clamp((int)lcr, 1, LTDC_MAX_LAYER); /* set data bus width */ gc2r = reg_read(ldev->regs, LTDC_GC2R); @@ -1125,8 +1176,9 @@ int ltdc_load(struct drm_device *ddev) ldev->pixel_clk = devm_clk_get(dev, "lcd"); if (IS_ERR(ldev->pixel_clk)) { - DRM_ERROR("Unable to get lcd clock\n"); - return -ENODEV; + if (PTR_ERR(ldev->pixel_clk) != -EPROBE_DEFER) + DRM_ERROR("Unable to get lcd clock\n"); + return PTR_ERR(ldev->pixel_clk); } if (clk_prepare_enable(ldev->pixel_clk)) { @@ -1134,6 +1186,12 @@ int ltdc_load(struct drm_device *ddev) return -ENODEV; } + if (!IS_ERR(rstc)) { + reset_control_assert(rstc); + usleep_range(10, 20); + reset_control_deassert(rstc); + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ldev->regs = devm_ioremap_resource(dev, res); if (IS_ERR(ldev->regs)) { @@ -1142,8 +1200,15 @@ int ltdc_load(struct drm_device *ddev) goto err; } + /* Disable interrupts */ + reg_clear(ldev->regs, LTDC_IER, + IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE); + for (i = 0; i < MAX_IRQ; i++) { irq = platform_get_irq(pdev, i); + if (irq == -EPROBE_DEFER) + goto err; + if (irq < 0) continue; @@ -1156,15 +1221,6 @@ int ltdc_load(struct drm_device *ddev) } } - if (!IS_ERR(rstc)) { - reset_control_assert(rstc); - usleep_range(10, 20); - reset_control_deassert(rstc); - } - - /* Disable interrupts */ - reg_clear(ldev->regs, LTDC_IER, - IER_LIE | IER_RRIE | IER_FUIE | IER_TERRIE); ret = ltdc_get_caps(ddev); if (ret) { @@ -1173,7 +1229,7 @@ int ltdc_load(struct drm_device *ddev) goto err; } - DRM_INFO("ltdc hw version 0x%08x - ready\n", ldev->caps.hw_version); + DRM_DEBUG_DRIVER("ltdc hw version 0x%08x\n", ldev->caps.hw_version); /* Add endpoints panels or bridges if any */ for (i = 0; i < MAX_ENDPOINTS; i++) { @@ -1203,6 +1259,8 @@ int ltdc_load(struct drm_device *ddev) goto err; } + ddev->mode_config.allow_fb_modifiers = true; + ret = ltdc_crtc_init(ddev, crtc); if (ret) { DRM_ERROR("Failed to init crtc\n"); @@ -1218,8 +1276,11 @@ int ltdc_load(struct drm_device *ddev) /* Allow usage of vblank without having to call drm_irq_install */ ddev->irq_enabled = 1; - return 0; + clk_disable_unprepare(ldev->pixel_clk); + + pm_runtime_enable(ddev->dev); + return 0; err: for (i = 0; i < MAX_ENDPOINTS; i++) drm_panel_bridge_remove(bridge[i]); @@ -1231,7 +1292,6 @@ err: void ltdc_unload(struct drm_device *ddev) { - struct ltdc_device *ldev = ddev->dev_private; int i; DRM_DEBUG_DRIVER("\n"); @@ -1239,7 +1299,7 @@ void ltdc_unload(struct drm_device *ddev) for (i = 0; i < MAX_ENDPOINTS; i++) drm_of_panel_bridge_remove(ddev->dev->of_node, 0, i); - clk_disable_unprepare(ldev->pixel_clk); + pm_runtime_disable(ddev->dev); } MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); |