From 3d19306a8240a163f6b02bb46213c277d6d44e08 Mon Sep 17 00:00:00 2001 From: Darren Etheridge Date: Wed, 15 Jan 2014 15:52:36 -0600 Subject: [PATCH 01/22] drm/tilcdc: rewrite pixel clock calculation Updating the tilcdc DRM driver code to calculate the LCD controller pixel clock more accurately. Based on a suggested implementation by Tomi Valkeinen. The current code does not work correctly and produces wrong results with many requested clock rates. It also oddly uses two different clocks, a display pll clock and a divider clock (child of display pll), instead of just using the clock coming to the lcdc. This patch removes the use of the display pll clock, and rewrites the code to calculate the clock rates. The idea is simply to request a clock rate of pixelclock*2, as the LCD controller has an internal divider which we set to 2. Signed-off-by: Darren Etheridge [Rewrapped description] Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 16 ++++++++-------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 11 +---------- drivers/gpu/drm/tilcdc/tilcdc_drv.h | 1 - 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 4802da8e6d6f..aaf8989810b1 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -573,7 +573,8 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc) struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; int dpms = tilcdc_crtc->dpms; - unsigned int lcd_clk, div; + unsigned long lcd_clk; + const unsigned clkdiv = 2; /* using a fixed divider of 2 */ int ret; pm_runtime_get_sync(dev->dev); @@ -581,22 +582,21 @@ void tilcdc_crtc_update_clk(struct drm_crtc *crtc) if (dpms == DRM_MODE_DPMS_ON) tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); - /* in raster mode, minimum divisor is 2: */ - ret = clk_set_rate(priv->disp_clk, crtc->mode.clock * 1000 * 2); - if (ret) { + /* mode.clock is in KHz, set_rate wants parameter in Hz */ + ret = clk_set_rate(priv->clk, crtc->mode.clock * 1000 * clkdiv); + if (ret < 0) { dev_err(dev->dev, "failed to set display clock rate to: %d\n", crtc->mode.clock); goto out; } lcd_clk = clk_get_rate(priv->clk); - div = lcd_clk / (crtc->mode.clock * 1000); - DBG("lcd_clk=%u, mode clock=%d, div=%u", lcd_clk, crtc->mode.clock, div); - DBG("fck=%lu, dpll_disp_ck=%lu", clk_get_rate(priv->clk), clk_get_rate(priv->disp_clk)); + DBG("lcd_clk=%lu, mode clock=%d, div=%u", + lcd_clk, crtc->mode.clock, clkdiv); /* Configure the LCD clock divisor. */ - tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(div) | + tilcdc_write(dev, LCDC_CTRL_REG, LCDC_CLK_DIVISOR(clkdiv) | LCDC_RASTER_MODE); if (priv->rev == 2) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 8190ac3b1b32..b3dbbe9d652e 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -192,13 +192,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) goto fail_iounmap; } - priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck"); - if (IS_ERR(priv->clk)) { - dev_err(dev->dev, "failed to get display clock\n"); - ret = -ENODEV; - goto fail_put_clk; - } - #ifdef CONFIG_CPU_FREQ priv->lcd_fck_rate = clk_get_rate(priv->clk); priv->freq_transition.notifier_call = cpufreq_transition; @@ -206,7 +199,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) CPUFREQ_TRANSITION_NOTIFIER); if (ret) { dev_err(dev->dev, "failed to register cpufreq notifier\n"); - goto fail_put_disp_clk; + goto fail_put_clk; } #endif @@ -330,8 +323,6 @@ fail_cpufreq_unregister: #ifdef CONFIG_CPU_FREQ cpufreq_unregister_notifier(&priv->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); -fail_put_disp_clk: - clk_put(priv->disp_clk); #endif fail_put_clk: diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 66105d8dc620..62a1d688e6b3 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -49,7 +49,6 @@ struct tilcdc_drm_private { void __iomem *mmio; - struct clk *disp_clk; /* display dpll */ struct clk *clk; /* functional clock */ int rev; /* IP revision */ From 6f206e9d2a965771e99bca4c22dbadac1b58a0e8 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Fri, 7 Feb 2014 17:37:07 +0000 Subject: [PATCH 02/22] drm/tilcdc: verify fb pitch LCDC hardware does not support fb pitch that is different (i.e. larger) than the screen size. The driver currently does no checks for this, and the results of too big pitch are are flickering and lower fps. This issue easily happens when using libdrm's modetest tool with non-32 bpp modes. As modetest always allocated 4 bytes per pixel, it implies a bigger pitch for 16 or 24 bpp modes. This patch adds a check to reject pitches the hardware cannot support. Signed-off-by: Tomi Valkeinen Signed-off-by: Darren Etheridge Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 31 ++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index aaf8989810b1..6485e1c03fe3 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -151,6 +151,22 @@ static void tilcdc_crtc_destroy(struct drm_crtc *crtc) kfree(tilcdc_crtc); } +static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) +{ + struct drm_device *dev = crtc->dev; + unsigned int depth, bpp; + + drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); + + if (fb->pitches[0] != crtc->mode.hdisplay * bpp / 8) { + dev_err(dev->dev, + "Invalid pitch: fb and crtc widths must be the same"); + return -EINVAL; + } + + return 0; +} + static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, @@ -158,6 +174,11 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; + int r; + + r = tilcdc_verify_fb(crtc, fb); + if (r) + return r; if (tilcdc_crtc->event) { dev_err(dev->dev, "already pending page flip!\n"); @@ -272,6 +293,10 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, if (WARN_ON(!info)) return -EINVAL; + ret = tilcdc_verify_fb(crtc, crtc->primary->fb); + if (ret) + return ret; + pm_runtime_get_sync(dev->dev); /* Configure the Burst Size and fifo threshold of DMA: */ @@ -431,6 +456,12 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + int r; + + r = tilcdc_verify_fb(crtc, crtc->primary->fb); + if (r) + return r; + update_scanout(crtc); return 0; } From 416a07fbe7b1f1a6f7e0595b43b5a85a1c877e05 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Tue, 29 Jul 2014 06:27:58 +0000 Subject: [PATCH 03/22] drm/tilcdc: adopt pinctrl support Update tilcdc driver to set the state of the pins to: - "default on resume - "sleep" on suspend By optionally putting the pins into sleep state in the suspend callback we can accomplish two things. - minimize current leakage from pins and thus save power, - prevent the IP from driving pins output in an uncontrolled manner, which may happen if the power domain drops the domain regulator. Signed-off-by: Dave Gerlach Signed-off-by: Darren Etheridge Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index b3dbbe9d652e..420a237a99fc 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -18,6 +18,8 @@ /* LCDC DRM driver, based on da8xx-fb */ #include +#include +#include #include "tilcdc_drv.h" #include "tilcdc_regs.h" @@ -585,6 +587,9 @@ static int tilcdc_pm_suspend(struct device *dev) if (registers[i].save && (priv->rev >= registers[i].rev)) priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -594,6 +599,9 @@ static int tilcdc_pm_resume(struct device *dev) struct tilcdc_drm_private *priv = ddev->dev_private; unsigned i, n = 0; + /* Select default pin state */ + pinctrl_pm_select_default_state(dev); + /* Restore register state: */ for (i = 0; i < ARRAY_SIZE(registers); i++) if (registers[i].save && (priv->rev >= registers[i].rev)) From 85fd27f80b3641c9af9f04cde1b712c8c20916d8 Mon Sep 17 00:00:00 2001 From: Darren Etheridge Date: Fri, 19 Sep 2014 01:42:57 +0000 Subject: [PATCH 04/22] drm/tilcdc: fix kernel panic on suspend when no hdmi monitor connected On BeagleBone Black if no HDMI monitor is connected and suspend is requested a kernel panic will result: root@am335x-evm:~# echo mem > /sys/power/state [ 65.548710] PM: Syncing filesystems ... done. [ 65.631311] Freezing user space processes ... (elapsed 0.006 seconds) done. [ 65.648619] Freezing remaining freezable tasks ... (elapsed 0.005 seconds) done. [ 65.833500] Unhandled fault: external abort on non-linefetch (0x1028) at 0xfa30e004 [ 65.841692] Internal error: : 1028 [#1] SMP ARM [ 66.105287] [] (platform_pm_suspend) from [] (dpm_run_callback+0x34/0x70) [ 66.114370] [] (dpm_run_callback) from [] (__device_suspend+0x10c/0x2f4) [ 66.123357] [] (__device_suspend) from [] (dpm_suspend+0x58/0x218) [ 66.131796] [] (dpm_suspend) from [] (suspend_devices_and_enter+0x9c/0x3c0) [ 66.141055] [] (suspend_devices_and_enter) from [] (pm_suspend+0x210/0x24c) [ 66.150312] [] (pm_suspend) from [] (state_store+0x68/0xb8) [ 66.158103] [] (state_store) from [] (kobj_attr_store+0x14/0x20) [ 66.166355] [] (kobj_attr_store) from [] (sysfs_kf_write+0x4c/0x50) [ 66.174883] [] (sysfs_kf_write) from [] (kernfs_fop_write+0xb4/0x150) [ 66.183598] [] (kernfs_fop_write) from [] (vfs_write+0xa8/0x180) [ 66.191846] [] (vfs_write) from [] (SyS_write+0x40/0x8c) [ 66.199365] [] (SyS_write) from [] (ret_fast_syscall+0x0/0x48) [ 66.207426] Code: e595c210 e5932000 e59cc000 e08c2002 (e592c000) This is because the lcdc module is not enabled when no monitor is detected to save power. However the suspend handler just blindly tries to save the lcdc state by copying out the pertinent registers. However module is off so no good things happen when you try and access it. This patch only saves off the registers if the module is enabled, and then only restores the registers on resume if they were saved off during suspend. Signed-off-by: Darren Etheridge Tested-by: Dave Gerlach Acked-by: Felipe Balbi Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 23 +++++++++++++++++------ drivers/gpu/drm/tilcdc/tilcdc_drv.h | 1 + 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 420a237a99fc..67ed9f7fe197 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -582,13 +582,20 @@ static int tilcdc_pm_suspend(struct device *dev) drm_kms_helper_poll_disable(ddev); + /* Select sleep pin state */ + pinctrl_pm_select_sleep_state(dev); + + if (pm_runtime_suspended(dev)) { + priv->ctx_valid = false; + return 0; + } + /* Save register state: */ for (i = 0; i < ARRAY_SIZE(registers); i++) if (registers[i].save && (priv->rev >= registers[i].rev)) priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg); - /* Select sleep pin state */ - pinctrl_pm_select_sleep_state(dev); + priv->ctx_valid = true; return 0; } @@ -602,10 +609,14 @@ static int tilcdc_pm_resume(struct device *dev) /* Select default pin state */ pinctrl_pm_select_default_state(dev); - /* Restore register state: */ - for (i = 0; i < ARRAY_SIZE(registers); i++) - if (registers[i].save && (priv->rev >= registers[i].rev)) - tilcdc_write(ddev, registers[i].reg, priv->saved_register[n++]); + if (priv->ctx_valid == true) { + /* Restore register state: */ + for (i = 0; i < ARRAY_SIZE(registers); i++) + if (registers[i].save && + (priv->rev >= registers[i].rev)) + tilcdc_write(ddev, registers[i].reg, + priv->saved_register[n++]); + } drm_kms_helper_poll_enable(ddev); diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 62a1d688e6b3..55d17b3466c4 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -67,6 +67,7 @@ struct tilcdc_drm_private { /* register contents saved across suspend/resume: */ u32 saved_register[12]; + bool ctx_valid; #ifdef CONFIG_CPU_FREQ struct notifier_block freq_transition; From b62222fcaab994177f121d58acdab269f0f54897 Mon Sep 17 00:00:00 2001 From: Darren Etheridge Date: Thu, 25 Sep 2014 00:59:31 +0000 Subject: [PATCH 05/22] drm/tilcdc: make frame_done interrupt active at all times The frame_done interrupt was only being enabled when the vsync interrupts were being enabled by DRM. However the frame_done is used to determine if the LCD controller has successfully completed the raster_enable, raster_disable commands and the vsync interrupts are not always enabled during these operations. Signed-off-by: Darren Etheridge Tested-by: Dave Gerlach Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 67ed9f7fe197..7c39362c9455 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -369,7 +369,9 @@ static int tilcdc_irq_postinstall(struct drm_device *dev) if (priv->rev == 1) tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); else - tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA); + tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, + LCDC_V2_UNDERFLOW_INT_ENA | + LCDC_FRAME_DONE); return 0; } @@ -403,7 +405,7 @@ static void enable_vblank(struct drm_device *dev, bool enable) } else { reg = LCDC_INT_ENABLE_SET_REG; mask = LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE; + LCDC_V2_END_OF_FRAME1_INT_ENA; } if (enable) From 614b3cfeb8d22e2b0f49bcfeaf5b52900242a944 Mon Sep 17 00:00:00 2001 From: Darren Etheridge Date: Thu, 25 Sep 2014 00:59:32 +0000 Subject: [PATCH 06/22] drm/tilcdc: disable the lcd controller/dma engine when suspend invoked The LCD controller must be deactivated and all DMA transactions stopped when the suspend power state is entered otherwise the PRCM causes the L3 bus to get stuck in transition state. This commit forces the lcdc to be shut down and waits for all pending DMA transactions to complete as part of the suspend handler for this driver. Signed-off-by: Darren Etheridge Tested-by: Dave Gerlach Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 3 +-- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 3 +++ drivers/gpu/drm/tilcdc/tilcdc_drv.h | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 6485e1c03fe3..465fd047875a 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -138,7 +138,6 @@ static void stop(struct drm_crtc *crtc) tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); } -static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode); static void tilcdc_crtc_destroy(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); @@ -192,7 +191,7 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, return 0; } -static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) +void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 7c39362c9455..30d8ac61cc85 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -592,6 +592,9 @@ static int tilcdc_pm_suspend(struct device *dev) return 0; } + /* Disable the LCDC controller, to avoid locking up the PRCM */ + tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF); + /* Save register state: */ for (i = 0; i < ARRAY_SIZE(registers); i++) if (registers[i].save && (priv->rev >= registers[i].rev)) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 55d17b3466c4..95324f19d891 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -171,5 +171,6 @@ void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, bool simulate_vesa_sync); int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); int tilcdc_crtc_max_width(struct drm_crtc *crtc); +void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode); #endif /* __TILCDC_DRV_H__ */ From 9c15390506d6888978fa98094f7578142d2e2f01 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 23 Jun 2015 14:31:17 +0300 Subject: [PATCH 07/22] drm/tilcdc: Implement dma-buf support for tilcdc There is nothing special about tilcdc HW when the video memory is concerned. Just using the standard drm helpers for implementation is enough. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 30d8ac61cc85..c204359422a2 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -542,7 +542,8 @@ static const struct file_operations fops = { }; static struct drm_driver tilcdc_driver = { - .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET, + .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | + DRIVER_PRIME), .load = tilcdc_load, .unload = tilcdc_unload, .lastclose = tilcdc_lastclose, @@ -559,6 +560,16 @@ static struct drm_driver tilcdc_driver = { .dumb_create = drm_gem_cma_dumb_create, .dumb_map_offset = drm_gem_cma_dumb_map_offset, .dumb_destroy = drm_gem_dumb_destroy, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, #ifdef CONFIG_DEBUG_FS .debugfs_init = tilcdc_debugfs_init, .debugfs_cleanup = tilcdc_debugfs_cleanup, From 7974dff4957f953f0f6fd71c30e02a7c25aea7f0 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 25 Feb 2015 18:19:43 +0200 Subject: [PATCH 08/22] drm/tilcdc: fix build error when !CONFIG_CPU_FREQ Fix build error when !CONFIG_CPU_FREQ drivers/gpu/drm/tilcdc/tilcdc_drv.c: In function 'tilcdc_load': drivers/gpu/drm/tilcdc/tilcdc_drv.c:327:1: error: label 'fail_put_clk' defined but not used [-Werror=unused-label] fail_put_clk: ^ Signed-off-by: Grygorii Strashko Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index c204359422a2..977c843f663a 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -325,9 +325,9 @@ fail_cpufreq_unregister: #ifdef CONFIG_CPU_FREQ cpufreq_unregister_notifier(&priv->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); -#endif fail_put_clk: +#endif clk_put(priv->clk); fail_iounmap: From 29ddd6e171abae990a881b9e221359f13c546369 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Thu, 2 Jul 2015 16:26:12 +0300 Subject: [PATCH 09/22] drm/tilcdc: Allocate register storage based on the actual number registers Allocate suspend/resume register storage based on the actual number registers the driver is aware of. The static allocation for register storage had fallen behind badly. Reported-by: Michael Bode Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 21 ++++++++++++++++++++- drivers/gpu/drm/tilcdc/tilcdc_drv.h | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 977c843f663a..47096b1b8b2a 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -141,11 +141,14 @@ static int tilcdc_unload(struct drm_device *dev) pm_runtime_disable(dev->dev); + kfree(priv->saved_register); kfree(priv); return 0; } +static size_t tilcdc_num_regs(void); + static int tilcdc_load(struct drm_device *dev, unsigned long flags) { struct platform_device *pdev = dev->platformdev; @@ -157,7 +160,12 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) int ret; priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { + if (priv) + priv->saved_register = kcalloc(tilcdc_num_regs(), + sizeof(*priv->saved_register), + GFP_KERNEL); + if (!priv || !priv->saved_register) { + kfree(priv); dev_err(dev->dev, "failed to allocate private data\n"); return -ENOMEM; } @@ -339,6 +347,7 @@ fail_free_wq: fail_free_priv: dev->dev_private = NULL; + kfree(priv->saved_register); kfree(priv); return ret; } @@ -456,6 +465,16 @@ static const struct { REG(2, true, LCDC_INT_ENABLE_SET_REG), #undef REG }; + +static size_t tilcdc_num_regs(void) +{ + return ARRAY_SIZE(registers); +} +#else +static size_t tilcdc_num_regs(void) +{ + return 0; +} #endif #ifdef CONFIG_DEBUG_FS diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 95324f19d891..c1de18bae415 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h @@ -66,7 +66,7 @@ struct tilcdc_drm_private { uint32_t max_width; /* register contents saved across suspend/resume: */ - u32 saved_register[12]; + u32 *saved_register; bool ctx_valid; #ifdef CONFIG_CPU_FREQ From 65734a262350a746100dcfd85a81f7dc1b69dd10 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 19 Oct 2015 12:30:03 +0300 Subject: [PATCH 10/22] drm/tilcdc: cleanup runtime PM handling Cleanup runtime PM handling. Before the patch the usage of pm_runtime calls was inconsistent and hard to follow. After the update the pm_runtime calls are removed from set_scanout() and called around major operations that access the HW. After the patch the DPMS code does not have pm_runtime_forbid/allow calls any more and pm_runtime_irq_safe() is not set anymore. Signed-off-by: Tomi Valkeinen [Added description to the patch] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 19 +++++++++++-------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 1 - 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 465fd047875a..08b1e03b502e 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -71,7 +71,6 @@ static void set_scanout(struct drm_crtc *crtc, int n) struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; - pm_runtime_get_sync(dev->dev); tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); if (tilcdc_crtc->scanout[n]) { @@ -81,7 +80,6 @@ static void set_scanout(struct drm_crtc *crtc, int n) tilcdc_crtc->scanout[n] = crtc->primary->fb; drm_framebuffer_reference(tilcdc_crtc->scanout[n]); tilcdc_crtc->dirty &= ~stat[n]; - pm_runtime_put_sync(dev->dev); } static void update_scanout(struct drm_crtc *crtc) @@ -186,8 +184,13 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, crtc->primary->fb = fb; tilcdc_crtc->event = event; + + pm_runtime_get_sync(dev->dev); + update_scanout(crtc); + pm_runtime_put_sync(dev->dev); + return 0; } @@ -206,10 +209,8 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) tilcdc_crtc->dpms = mode; - pm_runtime_get_sync(dev->dev); - if (mode == DRM_MODE_DPMS_ON) { - pm_runtime_forbid(dev->dev); + pm_runtime_get_sync(dev->dev); start(crtc); } else { tilcdc_crtc->frame_done = false; @@ -227,10 +228,9 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) if (ret == 0) dev_err(dev->dev, "timeout waiting for framedone\n"); } - pm_runtime_allow(dev->dev); - } - pm_runtime_put_sync(dev->dev); + pm_runtime_put_sync(dev->dev); + } } static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, @@ -455,13 +455,16 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, struct drm_framebuffer *old_fb) { + struct drm_device *dev = crtc->dev; int r; r = tilcdc_verify_fb(crtc, crtc->primary->fb); if (r) return r; + pm_runtime_get_sync(dev->dev); update_scanout(crtc); + pm_runtime_put_sync(dev->dev); return 0; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 47096b1b8b2a..47f0e0282d5d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -230,7 +230,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock); pm_runtime_enable(dev->dev); - pm_runtime_irq_safe(dev->dev); /* Determine LCD IP Version */ pm_runtime_get_sync(dev->dev); From 1aea1e79dbfd65a99da11868829c3e147b85cc32 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Mon, 19 Oct 2015 14:15:26 +0300 Subject: [PATCH 11/22] drm/tilcdc: disable crtc on unload Disable crtc on unload. Call tilcdc_crtc_dpms() with DRM_MODE_DPMS_OFF in the beginning of unload function. Signed-off-by: Tomi Valkeinen [Added description to the patch] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 47f0e0282d5d..e1f697986289 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -112,6 +112,8 @@ static int tilcdc_unload(struct drm_device *dev) { struct tilcdc_drm_private *priv = dev->dev_private; + tilcdc_crtc_dpms(priv->crtc, DRM_MODE_DPMS_OFF); + tilcdc_remove_external_encoders(dev); drm_fbdev_cma_fini(priv->fbdev); From 2efec4f3064d084bac1b2c1e1513a7452e8e245d Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Oct 2015 09:37:27 +0300 Subject: [PATCH 12/22] drm/tilcdc: split reset to a separate function Split reset to a separate function and use usleep_range(250, 1000) instead of msleep(1) to to keep the reset bit on long enough. Signed-off-by: Tomi Valkeinen [Added description to the patch, changed mdelay(500) to usleep_range(250, 1000)] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 08b1e03b502e..e62a950b8b76 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -112,17 +112,24 @@ static void update_scanout(struct drm_crtc *crtc) } } -static void start(struct drm_crtc *crtc) +static void reset(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; - if (priv->rev == 2) { - tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); - msleep(1); - tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); - msleep(1); - } + if (priv->rev != 2) + return; + + tilcdc_set(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); + usleep_range(250, 1000); + tilcdc_clear(dev, LCDC_CLK_RESET_REG, LCDC_CLK_MAIN_RESET); +} + +static void start(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + + reset(crtc); tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); From 31ec5a2c7eed3a3e182a592591f4fb04304668a1 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Oct 2015 12:04:05 +0300 Subject: [PATCH 13/22] drm/tilcdc: remove broken error handling Remove broken error handling. The condition for handling the LCDC_SYNC_LOST and LCDC_FIFO_UNDERFLOW could never be satisfied as the LCDC_SYNC_LOST interrupt is not enabled. Also the requirement to have both LCDC_SYNC_LOST and LCDC_FIFO_UNDERFLOW fired at once before handling the error looks weird. Signed-off-by: Tomi Valkeinen [Added description to the patch] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index e62a950b8b76..61aead2ddccb 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -658,12 +658,7 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) struct tilcdc_drm_private *priv = dev->dev_private; uint32_t stat = tilcdc_read_irqstatus(dev); - if ((stat & LCDC_SYNC_LOST) && (stat & LCDC_FIFO_UNDERFLOW)) { - stop(crtc); - dev_err(dev->dev, "error: %08x\n", stat); - tilcdc_clear_irqstatus(dev, stat); - start(crtc); - } else if (stat & LCDC_PL_LOAD_DONE) { + if (stat & LCDC_PL_LOAD_DONE) { tilcdc_clear_irqstatus(dev, stat); } else { struct drm_pending_vblank_event *event; From 317aae738b6402cd66fb9b52434b783f17ff5dd4 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Oct 2015 12:08:03 +0300 Subject: [PATCH 14/22] drm/tilcdc: cleanup irq handling Cleanup irq handling. Clear the irq status unconditionally and restructure the status bit conditions. Signed-off-by: Tomi Valkeinen [Added description to the patch] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 61aead2ddccb..a6ef737c7d35 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -656,11 +656,12 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; struct tilcdc_drm_private *priv = dev->dev_private; - uint32_t stat = tilcdc_read_irqstatus(dev); + uint32_t stat; - if (stat & LCDC_PL_LOAD_DONE) { - tilcdc_clear_irqstatus(dev, stat); - } else { + stat = tilcdc_read_irqstatus(dev); + tilcdc_clear_irqstatus(dev, stat); + + if ((stat & LCDC_END_OF_FRAME0) || (stat & LCDC_END_OF_FRAME1)) { struct drm_pending_vblank_event *event; unsigned long flags; uint32_t dirty = tilcdc_crtc->dirty & stat; From 2b2080d7e9ae2463b15a003629d2ea7d733759a0 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 20 Oct 2015 09:37:27 +0300 Subject: [PATCH 15/22] drm/tilcdc: Get rid of complex ping-pong mechanism Get rid of complex ping-pong mechanism and replace it with simpler single buffer flipping code. The LCDC HW appears to be designed mainly static framebuffers in mind. There are two modes of operation, either static single buffer, or ping pong double buffering with two static buffers switching back and forth. Luckily the framebuffer start address is fetched only in the beginning of the frame and changing the address after that only takes effect after the next vertical blank. The page flipping code can simply write the address of the new framebuffer and the page is flipped automatically after the next vertical blank. Using the ping pong double buffering makes the flipping code way more complex and it does not provide any benefit, so it is better to switch to single buffer operation. There is still one problem in updating the framebuffer dma address on the fly. There are two registers defining the framebuffer dma area and things may break if the dma address is fetched in while the registers are are being updated. Signed-off-by: Tomi Valkeinen [Added description to the patch] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 121 +++++++++++---------------- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 27 +----- 2 files changed, 53 insertions(+), 95 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index a6ef737c7d35..3257228564d9 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -25,15 +25,12 @@ struct tilcdc_crtc { struct drm_crtc base; const struct tilcdc_panel_info *info; - uint32_t dirty; - dma_addr_t start, end; struct drm_pending_vblank_event *event; int dpms; wait_queue_head_t frame_done_wq; bool frame_done; - /* fb currently set to scanout 0/1: */ - struct drm_framebuffer *scanout[2]; + struct drm_framebuffer *curr_fb; /* for deferred fb unref's: */ struct drm_flip_work unref_work; @@ -54,62 +51,31 @@ static void unref_worker(struct drm_flip_work *work, void *val) mutex_unlock(&dev->mode_config.mutex); } -static void set_scanout(struct drm_crtc *crtc, int n) -{ - static const uint32_t base_reg[] = { - LCDC_DMA_FB_BASE_ADDR_0_REG, - LCDC_DMA_FB_BASE_ADDR_1_REG, - }; - static const uint32_t ceil_reg[] = { - LCDC_DMA_FB_CEILING_ADDR_0_REG, - LCDC_DMA_FB_CEILING_ADDR_1_REG, - }; - static const uint32_t stat[] = { - LCDC_END_OF_FRAME0, LCDC_END_OF_FRAME1, - }; - struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct tilcdc_drm_private *priv = dev->dev_private; - - tilcdc_write(dev, base_reg[n], tilcdc_crtc->start); - tilcdc_write(dev, ceil_reg[n], tilcdc_crtc->end); - if (tilcdc_crtc->scanout[n]) { - drm_flip_work_queue(&tilcdc_crtc->unref_work, tilcdc_crtc->scanout[n]); - drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - } - tilcdc_crtc->scanout[n] = crtc->primary->fb; - drm_framebuffer_reference(tilcdc_crtc->scanout[n]); - tilcdc_crtc->dirty &= ~stat[n]; -} - -static void update_scanout(struct drm_crtc *crtc) +static void set_scanout(struct drm_crtc *crtc, struct drm_framebuffer *fb) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; - struct drm_framebuffer *fb = crtc->primary->fb; struct drm_gem_cma_object *gem; unsigned int depth, bpp; + dma_addr_t start, end; drm_fb_get_bpp_depth(fb->pixel_format, &depth, &bpp); gem = drm_fb_cma_get_gem_obj(fb, 0); - tilcdc_crtc->start = gem->paddr + fb->offsets[0] + - (crtc->y * fb->pitches[0]) + (crtc->x * bpp/8); + start = gem->paddr + fb->offsets[0] + + crtc->y * fb->pitches[0] + + crtc->x * bpp / 8; - tilcdc_crtc->end = tilcdc_crtc->start + - (crtc->mode.vdisplay * fb->pitches[0]); + end = start + (crtc->mode.vdisplay * fb->pitches[0]); - if (tilcdc_crtc->dpms == DRM_MODE_DPMS_ON) { - /* already enabled, so just mark the frames that need - * updating and they will be updated on vblank: - */ - tilcdc_crtc->dirty |= LCDC_END_OF_FRAME0 | LCDC_END_OF_FRAME1; - drm_vblank_get(dev, 0); - } else { - /* not enabled yet, so update registers immediately: */ - set_scanout(crtc, 0); - set_scanout(crtc, 1); - } + tilcdc_write(dev, LCDC_DMA_FB_BASE_ADDR_0_REG, start); + tilcdc_write(dev, LCDC_DMA_FB_CEILING_ADDR_0_REG, end); + + if (tilcdc_crtc->curr_fb) + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->curr_fb); + + tilcdc_crtc->curr_fb = fb; } static void reset(struct drm_crtc *crtc) @@ -131,7 +97,7 @@ static void start(struct drm_crtc *crtc) reset(crtc); - tilcdc_set(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); + tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_DUAL_FRAME_BUFFER_ENABLE); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_PALETTE_LOAD_MODE(DATA_ONLY)); tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); } @@ -179,6 +145,7 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); struct drm_device *dev = crtc->dev; int r; + unsigned long flags; r = tilcdc_verify_fb(crtc, fb); if (r) @@ -189,12 +156,18 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, return -EBUSY; } + drm_framebuffer_reference(fb); + crtc->primary->fb = fb; - tilcdc_crtc->event = event; pm_runtime_get_sync(dev->dev); - update_scanout(crtc); + + set_scanout(crtc, fb); + + spin_lock_irqsave(&dev->event_lock, flags); + tilcdc_crtc->event = event; + spin_unlock_irqrestore(&dev->event_lock, flags); pm_runtime_put_sync(dev->dev); @@ -237,6 +210,14 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) } pm_runtime_put_sync(dev->dev); + + if (tilcdc_crtc->curr_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->curr_fb); + tilcdc_crtc->curr_fb = NULL; + } + + drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); } } @@ -450,8 +431,10 @@ static int tilcdc_crtc_mode_set(struct drm_crtc *crtc, else tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ORDER); + drm_framebuffer_reference(crtc->primary->fb); + + set_scanout(crtc, crtc->primary->fb); - update_scanout(crtc); tilcdc_crtc_update_clk(crtc); pm_runtime_put_sync(dev->dev); @@ -469,9 +452,14 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, if (r) return r; + drm_framebuffer_reference(crtc->primary->fb); + pm_runtime_get_sync(dev->dev); - update_scanout(crtc); + + set_scanout(crtc, crtc->primary->fb); + pm_runtime_put_sync(dev->dev); + return 0; } @@ -661,30 +649,21 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) stat = tilcdc_read_irqstatus(dev); tilcdc_clear_irqstatus(dev, stat); - if ((stat & LCDC_END_OF_FRAME0) || (stat & LCDC_END_OF_FRAME1)) { - struct drm_pending_vblank_event *event; + if (stat & LCDC_END_OF_FRAME0) { unsigned long flags; - uint32_t dirty = tilcdc_crtc->dirty & stat; - tilcdc_clear_irqstatus(dev, stat); - - if (dirty & LCDC_END_OF_FRAME0) - set_scanout(crtc, 0); - - if (dirty & LCDC_END_OF_FRAME1) - set_scanout(crtc, 1); + drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); drm_handle_vblank(dev, 0); spin_lock_irqsave(&dev->event_lock, flags); - event = tilcdc_crtc->event; - tilcdc_crtc->event = NULL; - if (event) - drm_send_vblank_event(dev, 0, event); - spin_unlock_irqrestore(&dev->event_lock, flags); - if (dirty && !tilcdc_crtc->dirty) - drm_vblank_put(dev, 0); + if (tilcdc_crtc->event) { + drm_send_vblank_event(dev, 0, tilcdc_crtc->event); + tilcdc_crtc->event = NULL; + } + + spin_unlock_irqrestore(&dev->event_lock, flags); } if (priv->rev == 2) { diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index e1f697986289..c5d9e3a2f812 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -381,6 +381,7 @@ static int tilcdc_irq_postinstall(struct drm_device *dev) else tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA | + LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_FRAME_DONE); return 0; @@ -398,41 +399,19 @@ static void tilcdc_irq_uninstall(struct drm_device *dev) } else { tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | - LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_V2_END_OF_FRAME1_INT_ENA | + LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_FRAME_DONE); } - -} - -static void enable_vblank(struct drm_device *dev, bool enable) -{ - struct tilcdc_drm_private *priv = dev->dev_private; - u32 reg, mask; - - if (priv->rev == 1) { - reg = LCDC_DMA_CTRL_REG; - mask = LCDC_V1_END_OF_FRAME_INT_ENA; - } else { - reg = LCDC_INT_ENABLE_SET_REG; - mask = LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_V2_END_OF_FRAME1_INT_ENA; - } - - if (enable) - tilcdc_set(dev, reg, mask); - else - tilcdc_clear(dev, reg, mask); } static int tilcdc_enable_vblank(struct drm_device *dev, unsigned int pipe) { - enable_vblank(dev, true); return 0; } static void tilcdc_disable_vblank(struct drm_device *dev, unsigned int pipe) { - enable_vblank(dev, false); + return; } #if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP) From 2b3a8cd71c2b830164df5de07e4ddebe0faa58f5 Mon Sep 17 00:00:00 2001 From: Tomi Valkeinen Date: Tue, 3 Nov 2015 12:00:51 +0200 Subject: [PATCH 16/22] drm/tilcdc: Do not update the next frame buffer close to vertical blank Do not update the next frame buffer close to vertical blank. This is to avoid situation when the frame changes between writing of LCDC_DMA_FB_BASE_ADDR_0_REG and LCDC_DMA_FB_CEILING_ADDR_0_REG. Signed-off-by: Tomi Valkeinen [Added description to the patch] Signed-off-by: Jyri Sarha --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 63 ++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 3257228564d9..b1df04625f50 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -21,6 +21,8 @@ #include "tilcdc_drv.h" #include "tilcdc_regs.h" +#define TILCDC_VBLANK_SAFETY_THRESHOLD_US 1000 + struct tilcdc_crtc { struct drm_crtc base; @@ -29,8 +31,12 @@ struct tilcdc_crtc { int dpms; wait_queue_head_t frame_done_wq; bool frame_done; + spinlock_t irq_lock; + + ktime_t last_vblank; struct drm_framebuffer *curr_fb; + struct drm_framebuffer *next_fb; /* for deferred fb unref's: */ struct drm_flip_work unref_work; @@ -146,6 +152,8 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, struct drm_device *dev = crtc->dev; int r; unsigned long flags; + s64 tdiff; + ktime_t next_vblank; r = tilcdc_verify_fb(crtc, fb); if (r) @@ -162,12 +170,21 @@ static int tilcdc_crtc_page_flip(struct drm_crtc *crtc, pm_runtime_get_sync(dev->dev); + spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); - set_scanout(crtc, fb); + next_vblank = ktime_add_us(tilcdc_crtc->last_vblank, + 1000000 / crtc->hwmode.vrefresh); + + tdiff = ktime_to_us(ktime_sub(next_vblank, ktime_get())); + + if (tdiff >= TILCDC_VBLANK_SAFETY_THRESHOLD_US) + set_scanout(crtc, fb); + else + tilcdc_crtc->next_fb = fb; - spin_lock_irqsave(&dev->event_lock, flags); tilcdc_crtc->event = event; - spin_unlock_irqrestore(&dev->event_lock, flags); + + spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); pm_runtime_put_sync(dev->dev); @@ -211,6 +228,12 @@ void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode) pm_runtime_put_sync(dev->dev); + if (tilcdc_crtc->next_fb) { + drm_flip_work_queue(&tilcdc_crtc->unref_work, + tilcdc_crtc->next_fb); + tilcdc_crtc->next_fb = NULL; + } + if (tilcdc_crtc->curr_fb) { drm_flip_work_queue(&tilcdc_crtc->unref_work, tilcdc_crtc->curr_fb); @@ -651,19 +674,39 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) if (stat & LCDC_END_OF_FRAME0) { unsigned long flags; + bool skip_event = false; + ktime_t now; + + now = ktime_get(); drm_flip_work_commit(&tilcdc_crtc->unref_work, priv->wq); - drm_handle_vblank(dev, 0); + spin_lock_irqsave(&tilcdc_crtc->irq_lock, flags); - spin_lock_irqsave(&dev->event_lock, flags); + tilcdc_crtc->last_vblank = now; - if (tilcdc_crtc->event) { - drm_send_vblank_event(dev, 0, tilcdc_crtc->event); - tilcdc_crtc->event = NULL; + if (tilcdc_crtc->next_fb) { + set_scanout(crtc, tilcdc_crtc->next_fb); + tilcdc_crtc->next_fb = NULL; + skip_event = true; } - spin_unlock_irqrestore(&dev->event_lock, flags); + spin_unlock_irqrestore(&tilcdc_crtc->irq_lock, flags); + + drm_handle_vblank(dev, 0); + + if (!skip_event) { + struct drm_pending_vblank_event *event; + + spin_lock_irqsave(&dev->event_lock, flags); + + event = tilcdc_crtc->event; + tilcdc_crtc->event = NULL; + if (event) + drm_send_vblank_event(dev, 0, event); + + spin_unlock_irqrestore(&dev->event_lock, flags); + } } if (priv->rev == 2) { @@ -697,6 +740,8 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) drm_flip_work_init(&tilcdc_crtc->unref_work, "unref", unref_worker); + spin_lock_init(&tilcdc_crtc->irq_lock); + ret = drm_crtc_init(dev, crtc, &tilcdc_crtc_funcs); if (ret < 0) goto fail; From 947df7e3f019bba902a55485635060e5970fb9a2 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 9 Dec 2015 12:16:11 +0200 Subject: [PATCH 17/22] drm/tilcdc: Fix interrupt enable/disable code for version 2 tilcdc Fix interrupt enable/disable code for version 2 tilcdc. In version 2 tilcdc there is a separate register for disabling interrupts. Writing 0 to enable registers bits does not have any effect. The interrupt clear register works the same way, writing 1 to specific bit disables the interrupt and writing 0 does not have any effect. The "bug" that is fixed here does not really do any harm since the interrupts are enabled only once in the power up and disabled before power down. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index c5d9e3a2f812..964e1925342f 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -376,13 +376,14 @@ static int tilcdc_irq_postinstall(struct drm_device *dev) struct tilcdc_drm_private *priv = dev->dev_private; /* enable FIFO underflow irq: */ - if (priv->rev == 1) + if (priv->rev == 1) { tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA); - else - tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, + } else { + tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_FRAME_DONE); + } return 0; } @@ -397,7 +398,7 @@ static void tilcdc_irq_uninstall(struct drm_device *dev) LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA); tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA); } else { - tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG, + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_FRAME_DONE); @@ -442,7 +443,7 @@ static const struct { REG(2, false, LCDC_INT_ENABLE_CLR_REG), REG(2, false, LCDC_END_OF_INT_IND_REG), REG(2, true, LCDC_CLK_ENABLE_REG), - REG(2, true, LCDC_INT_ENABLE_SET_REG), + REG(2, true, LCDC_INT_ENABLE_SET_REG), #undef REG }; From f3a99946a95b3482eabec63b9f662963d7d2e3c8 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 8 Jan 2016 12:17:50 +0200 Subject: [PATCH 18/22] drm/tilcdc: Remove the duplicate LCDC_INT_ENABLE_SET_REG in registers[] Removes the duplicate LCDC_INT_ENABLE_SET_REG-entry in registers array. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 964e1925342f..d96083d62e11 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -439,11 +439,10 @@ static const struct { /* new in revision 2: */ REG(2, false, LCDC_RAW_STAT_REG), REG(2, false, LCDC_MASKED_STAT_REG), - REG(2, false, LCDC_INT_ENABLE_SET_REG), + REG(2, true, LCDC_INT_ENABLE_SET_REG), REG(2, false, LCDC_INT_ENABLE_CLR_REG), REG(2, false, LCDC_END_OF_INT_IND_REG), REG(2, true, LCDC_CLK_ENABLE_REG), - REG(2, true, LCDC_INT_ENABLE_SET_REG), #undef REG }; From c0c2baaab1b553df92a24e9175440f15e6ad3e2c Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 18 Dec 2015 13:07:52 +0200 Subject: [PATCH 19/22] drm/tilcdc: Add prints on sync lost and FIFO underrun interrupts Add ratelimited prints on sync lost and FIFO underrun interrupts. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 8 ++++++++ drivers/gpu/drm/tilcdc/tilcdc_drv.c | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index b1df04625f50..5ee22c6b37e8 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -717,6 +717,14 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); } + if (stat & LCDC_SYNC_LOST) + dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", + __func__, stat); + + if (stat & LCDC_FIFO_UNDERFLOW) + dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", + __func__, stat); + return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index d96083d62e11..41ec890c4bca 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -382,7 +382,7 @@ static int tilcdc_irq_postinstall(struct drm_device *dev) tilcdc_write(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_FRAME_DONE); + LCDC_FRAME_DONE | LCDC_SYNC_LOST); } return 0; @@ -401,7 +401,7 @@ static void tilcdc_irq_uninstall(struct drm_device *dev) tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA | LCDC_V2_END_OF_FRAME0_INT_ENA | - LCDC_FRAME_DONE); + LCDC_FRAME_DONE | LCDC_SYNC_LOST); } } From 5895d08f6ff2175dabc373dada7d1bfa26123fc9 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Fri, 8 Jan 2016 14:33:09 +0200 Subject: [PATCH 20/22] drm/tilcdc: Disable sync lost interrupt if it fires on every frame Disable the sync lost interrupt if it fires on every frame for 50 consecutive frames in a row. This is relatively sure sign of the sync lost interrupt being stuck and firing on every frame even if the display otherwise appears to work OK. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 5ee22c6b37e8..248e3ea7568c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -43,6 +43,9 @@ struct tilcdc_crtc { /* Only set if an external encoder is connected */ bool simulate_vesa_sync; + + int sync_lost_count; + bool frame_intact; }; #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) @@ -662,6 +665,8 @@ out: pm_runtime_put_sync(dev->dev); } +#define SYNC_LOST_COUNT_LIMIT 50 + irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) { struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); @@ -707,6 +712,11 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) spin_unlock_irqrestore(&dev->event_lock, flags); } + + if (tilcdc_crtc->frame_intact) + tilcdc_crtc->sync_lost_count = 0; + else + tilcdc_crtc->frame_intact = true; } if (priv->rev == 2) { @@ -717,9 +727,18 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) tilcdc_write(dev, LCDC_END_OF_INT_IND_REG, 0); } - if (stat & LCDC_SYNC_LOST) + if (stat & LCDC_SYNC_LOST) { dev_err_ratelimited(dev->dev, "%s(0x%08x): Sync lost", __func__, stat); + tilcdc_crtc->frame_intact = false; + if (tilcdc_crtc->sync_lost_count++ > SYNC_LOST_COUNT_LIMIT) { + dev_err(dev->dev, + "%s(0x%08x): Sync lost flood detected, disabling the interrupt", + __func__, stat); + tilcdc_write(dev, LCDC_INT_ENABLE_CLR_REG, + LCDC_SYNC_LOST); + } + } if (stat & LCDC_FIFO_UNDERFLOW) dev_err_ratelimited(dev->dev, "%s(0x%08x): FIFO underfow", From d66284fba15014daacef64cfc610a249553534c6 Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Wed, 27 May 2015 11:58:37 +0300 Subject: [PATCH 21/22] drm/tilcdc: Initialize crtc->port Initialize port device node pointer in the tilcdc crtc. Fixes "Falling back to first CRTC" warning from tda998x driver. The tda998x encoder driver calls drm_of_find_possible_crtcs() to initialize possible_crtcs of struct drm_encoder. The crtc->port needs to be initialized for drm_of_find_possible_crtcs() to work. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 248e3ea7568c..5f4da8c86ce2 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -124,6 +124,7 @@ static void tilcdc_crtc_destroy(struct drm_crtc *crtc) tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); + of_node_put(crtc->port); drm_crtc_cleanup(crtc); drm_flip_work_cleanup(&tilcdc_crtc->unref_work); @@ -749,6 +750,7 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc) struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) { + struct tilcdc_drm_private *priv = dev->dev_private; struct tilcdc_crtc *tilcdc_crtc; struct drm_crtc *crtc; int ret; @@ -775,6 +777,24 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) drm_crtc_helper_add(crtc, &tilcdc_crtc_helper_funcs); + if (priv->is_componentized) { + struct device_node *ports = + of_get_child_by_name(dev->dev->of_node, "ports"); + + if (ports) { + crtc->port = of_get_child_by_name(ports, "port"); + of_node_put(ports); + } else { + crtc->port = + of_get_child_by_name(dev->dev->of_node, "port"); + } + if (!crtc->port) { /* This should never happen */ + dev_err(dev->dev, "Port node not found in %s\n", + dev->dev->of_node->full_name); + goto fail; + } + } + return crtc; fail: From d0ec32caef0baa490b419895ef61c8481d49f7cd Mon Sep 17 00:00:00 2001 From: Jyri Sarha Date: Tue, 23 Feb 2016 12:44:27 +0200 Subject: [PATCH 22/22] drm/tilcdc: Use devm_kzalloc() and devm_kcalloc() for private data Use devm_kzalloc() and devm_kcalloc() for private data allocation at driver load time. Signed-off-by: Jyri Sarha Reviewed-by: Tomi Valkeinen --- drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 4 +--- drivers/gpu/drm/tilcdc/tilcdc_drv.c | 19 +++++++------------ drivers/gpu/drm/tilcdc/tilcdc_panel.c | 20 ++++++-------------- drivers/gpu/drm/tilcdc/tilcdc_tfp410.c | 24 +++++++----------------- 4 files changed, 21 insertions(+), 46 deletions(-) diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index 5f4da8c86ce2..051e5e1b7ad6 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c @@ -127,8 +127,6 @@ static void tilcdc_crtc_destroy(struct drm_crtc *crtc) of_node_put(crtc->port); drm_crtc_cleanup(crtc); drm_flip_work_cleanup(&tilcdc_crtc->unref_work); - - kfree(tilcdc_crtc); } static int tilcdc_verify_fb(struct drm_crtc *crtc, struct drm_framebuffer *fb) @@ -755,7 +753,7 @@ struct drm_crtc *tilcdc_crtc_create(struct drm_device *dev) struct drm_crtc *crtc; int ret; - tilcdc_crtc = kzalloc(sizeof(*tilcdc_crtc), GFP_KERNEL); + tilcdc_crtc = devm_kzalloc(dev->dev, sizeof(*tilcdc_crtc), GFP_KERNEL); if (!tilcdc_crtc) { dev_err(dev->dev, "allocation failed\n"); return NULL; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 41ec890c4bca..709bc903524d 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -143,9 +143,6 @@ static int tilcdc_unload(struct drm_device *dev) pm_runtime_disable(dev->dev); - kfree(priv->saved_register); - kfree(priv); - return 0; } @@ -161,13 +158,12 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) u32 bpp = 0; int ret; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); + priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL); if (priv) - priv->saved_register = kcalloc(tilcdc_num_regs(), - sizeof(*priv->saved_register), - GFP_KERNEL); + priv->saved_register = + devm_kcalloc(dev->dev, tilcdc_num_regs(), + sizeof(*priv->saved_register), GFP_KERNEL); if (!priv || !priv->saved_register) { - kfree(priv); dev_err(dev->dev, "failed to allocate private data\n"); return -ENOMEM; } @@ -180,7 +176,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) priv->wq = alloc_ordered_workqueue("tilcdc", 0); if (!priv->wq) { ret = -ENOMEM; - goto fail_free_priv; + goto fail_unset_priv; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -346,10 +342,9 @@ fail_free_wq: flush_workqueue(priv->wq); destroy_workqueue(priv->wq); -fail_free_priv: +fail_unset_priv: dev->dev_private = NULL; - kfree(priv->saved_register); - kfree(priv); + return ret; } diff --git a/drivers/gpu/drm/tilcdc/tilcdc_panel.c b/drivers/gpu/drm/tilcdc/tilcdc_panel.c index 8dcf02a79b23..ff7774c17d7c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_panel.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_panel.c @@ -45,14 +45,6 @@ struct panel_encoder { }; #define to_panel_encoder(x) container_of(x, struct panel_encoder, base) - -static void panel_encoder_destroy(struct drm_encoder *encoder) -{ - struct panel_encoder *panel_encoder = to_panel_encoder(encoder); - drm_encoder_cleanup(encoder); - kfree(panel_encoder); -} - static void panel_encoder_dpms(struct drm_encoder *encoder, int mode) { struct panel_encoder *panel_encoder = to_panel_encoder(encoder); @@ -90,7 +82,7 @@ static void panel_encoder_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_funcs panel_encoder_funcs = { - .destroy = panel_encoder_destroy, + .destroy = drm_encoder_cleanup, }; static const struct drm_encoder_helper_funcs panel_encoder_helper_funcs = { @@ -107,7 +99,8 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev, struct drm_encoder *encoder; int ret; - panel_encoder = kzalloc(sizeof(*panel_encoder), GFP_KERNEL); + panel_encoder = devm_kzalloc(dev->dev, sizeof(*panel_encoder), + GFP_KERNEL); if (!panel_encoder) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -128,7 +121,7 @@ static struct drm_encoder *panel_encoder_create(struct drm_device *dev, return encoder; fail: - panel_encoder_destroy(encoder); + drm_encoder_cleanup(encoder); return NULL; } @@ -147,10 +140,8 @@ struct panel_connector { static void panel_connector_destroy(struct drm_connector *connector) { - struct panel_connector *panel_connector = to_panel_connector(connector); drm_connector_unregister(connector); drm_connector_cleanup(connector); - kfree(panel_connector); } static enum drm_connector_status panel_connector_detect( @@ -223,7 +214,8 @@ static struct drm_connector *panel_connector_create(struct drm_device *dev, struct drm_connector *connector; int ret; - panel_connector = kzalloc(sizeof(*panel_connector), GFP_KERNEL); + panel_connector = devm_kzalloc(dev->dev, sizeof(*panel_connector), + GFP_KERNEL); if (!panel_connector) { dev_err(dev->dev, "allocation failed\n"); return NULL; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c index 1c230172b402..7716f42f8aab 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_tfp410.c @@ -54,14 +54,6 @@ struct tfp410_encoder { }; #define to_tfp410_encoder(x) container_of(x, struct tfp410_encoder, base) - -static void tfp410_encoder_destroy(struct drm_encoder *encoder) -{ - struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); - drm_encoder_cleanup(encoder); - kfree(tfp410_encoder); -} - static void tfp410_encoder_dpms(struct drm_encoder *encoder, int mode) { struct tfp410_encoder *tfp410_encoder = to_tfp410_encoder(encoder); @@ -99,7 +91,7 @@ static void tfp410_encoder_mode_set(struct drm_encoder *encoder, } static const struct drm_encoder_funcs tfp410_encoder_funcs = { - .destroy = tfp410_encoder_destroy, + .destroy = drm_encoder_cleanup, }; static const struct drm_encoder_helper_funcs tfp410_encoder_helper_funcs = { @@ -116,7 +108,8 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev, struct drm_encoder *encoder; int ret; - tfp410_encoder = kzalloc(sizeof(*tfp410_encoder), GFP_KERNEL); + tfp410_encoder = devm_kzalloc(dev->dev, sizeof(*tfp410_encoder), + GFP_KERNEL); if (!tfp410_encoder) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -138,7 +131,7 @@ static struct drm_encoder *tfp410_encoder_create(struct drm_device *dev, return encoder; fail: - tfp410_encoder_destroy(encoder); + drm_encoder_cleanup(encoder); return NULL; } @@ -157,10 +150,8 @@ struct tfp410_connector { static void tfp410_connector_destroy(struct drm_connector *connector) { - struct tfp410_connector *tfp410_connector = to_tfp410_connector(connector); drm_connector_unregister(connector); drm_connector_cleanup(connector); - kfree(tfp410_connector); } static enum drm_connector_status tfp410_connector_detect( @@ -228,7 +219,8 @@ static struct drm_connector *tfp410_connector_create(struct drm_device *dev, struct drm_connector *connector; int ret; - tfp410_connector = kzalloc(sizeof(*tfp410_connector), GFP_KERNEL); + tfp410_connector = devm_kzalloc(dev->dev, sizeof(*tfp410_connector), + GFP_KERNEL); if (!tfp410_connector) { dev_err(dev->dev, "allocation failed\n"); return NULL; @@ -313,7 +305,7 @@ static int tfp410_probe(struct platform_device *pdev) return -ENXIO; } - tfp410_mod = kzalloc(sizeof(*tfp410_mod), GFP_KERNEL); + tfp410_mod = devm_kzalloc(&pdev->dev, sizeof(*tfp410_mod), GFP_KERNEL); if (!tfp410_mod) return -ENOMEM; @@ -366,7 +358,6 @@ fail_adapter: i2c_put_adapter(tfp410_mod->i2c); fail: - kfree(tfp410_mod); tilcdc_module_cleanup(mod); return ret; } @@ -380,7 +371,6 @@ static int tfp410_remove(struct platform_device *pdev) gpio_free(tfp410_mod->gpio); tilcdc_module_cleanup(mod); - kfree(tfp410_mod); return 0; }