Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c
index 808d9fb..3ab4fbf 100644
--- a/drivers/gpu/drm/stm/ltdc.c
+++ b/drivers/gpu/drm/stm/ltdc.c
@@ -10,18 +10,27 @@
#include <linux/clk.h>
#include <linux/component.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
#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>
#include <drm/drm_atomic_helper.h>
-#include <drm/drm_crtc_helper.h>
-#include <drm/drm_fb_cma_helper.h>
-#include <drm/drm_gem_cma_helper.h>
-#include <drm/drm_of.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_device.h>
+#include <drm/drm_fb_cma_helper.h>
+#include <drm/drm_fourcc.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_of.h>
#include <drm/drm_plane_helper.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_vblank.h>
#include <video/videomode.h>
@@ -148,6 +157,8 @@
#define IER_TERRIE BIT(2) /* Transfer ERRor Interrupt Enable */
#define IER_RRIE BIT(3) /* Register Reload Interrupt enable */
+#define CPSR_CYPOS GENMASK(15, 0) /* Current Y position */
+
#define ISR_LIF BIT(0) /* Line Interrupt Flag */
#define ISR_FUIF BIT(1) /* Fifo Underrun Interrupt Flag */
#define ISR_TERRIF BIT(2) /* Transfer ERRor Interrupt Flag */
@@ -223,6 +234,11 @@
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);
@@ -417,8 +433,8 @@
/* 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);
@@ -430,6 +446,7 @@
struct drm_crtc_state *old_state)
{
struct ltdc_device *ldev = crtc_to_ltdc(crtc);
+ struct drm_device *ddev = crtc->dev;
DRM_DEBUG_DRIVER("\n");
@@ -443,6 +460,8 @@
/* 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
@@ -491,33 +510,55 @@
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);
- clk_disable(ldev->pixel_clk);
+ if (runtime_active)
+ pm_runtime_put_sync(ddev->dev);
+
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);
@@ -546,7 +587,7 @@
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)
@@ -578,6 +619,7 @@
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");
@@ -590,12 +632,12 @@
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);
}
}
@@ -626,6 +668,53 @@
reg_clear(ldev->regs, LTDC_IER, IER_LIE);
}
+bool ltdc_crtc_scanoutpos(struct drm_device *ddev, unsigned int pipe,
+ bool in_vblank_irq, int *vpos, int *hpos,
+ ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+ int line, vactive_start, vactive_end, vtotal;
+
+ if (stime)
+ *stime = ktime_get();
+
+ /* The active area starts after vsync + front porch and ends
+ * at vsync + front porc + display size.
+ * The total height also include back porch.
+ * We have 3 possible cases to handle:
+ * - line < vactive_start: vpos = line - vactive_start and will be
+ * negative
+ * - vactive_start < line < vactive_end: vpos = line - vactive_start
+ * and will be positive
+ * - line > vactive_end: vpos = line - vtotal - vactive_start
+ * and will negative
+ *
+ * Computation for the two first cases are identical so we can
+ * simplify the code and only test if line > vactive_end
+ */
+ 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;
+
+ if (etime)
+ *etime = ktime_get();
+
+ return true;
+}
+
static const struct drm_crtc_funcs ltdc_crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_atomic_helper_set_config,
@@ -646,7 +735,7 @@
struct drm_plane_state *state)
{
struct drm_framebuffer *fb = state->fb;
- u32 src_x, src_y, src_w, src_h;
+ u32 src_w, src_h;
DRM_DEBUG_DRIVER("\n");
@@ -654,8 +743,6 @@
return 0;
/* convert src_ from 16:16 format */
- src_x = state->src_x >> 16;
- src_y = state->src_y >> 16;
src_w = state->src_w >> 16;
src_h = state->src_h >> 16;
@@ -729,7 +816,7 @@
/* 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,
@@ -772,11 +859,11 @@
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);
@@ -814,6 +901,16 @@
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,
@@ -822,9 +919,11 @@
.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 = {
+ .prepare_fb = drm_gem_fb_prepare_fb,
.atomic_check = ltdc_plane_atomic_check,
.atomic_update = ltdc_plane_atomic_update,
.atomic_disable = ltdc_plane_atomic_disable,
@@ -840,6 +939,7 @@
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 */
@@ -868,7 +968,7 @@
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;
@@ -971,10 +1071,13 @@
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);
@@ -1012,6 +1115,30 @@
return 0;
}
+void ltdc_suspend(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+
+ DRM_DEBUG_DRIVER("\n");
+ clk_disable_unprepare(ldev->pixel_clk);
+}
+
+int ltdc_resume(struct drm_device *ddev)
+{
+ struct ltdc_device *ldev = ddev->dev_private;
+ int ret;
+
+ DRM_DEBUG_DRIVER("\n");
+
+ ret = clk_prepare_enable(ldev->pixel_clk);
+ if (ret) {
+ DRM_ERROR("failed to enable pixel clock (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
int ltdc_load(struct drm_device *ddev)
{
struct platform_device *pdev = to_platform_device(ddev->dev);
@@ -1051,8 +1178,9 @@
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)) {
@@ -1060,6 +1188,12 @@
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)) {
@@ -1068,8 +1202,15 @@
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;
@@ -1082,15 +1223,6 @@
}
}
- 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) {
@@ -1099,7 +1231,7 @@
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++) {
@@ -1129,6 +1261,8 @@
goto err;
}
+ ddev->mode_config.allow_fb_modifiers = true;
+
ret = ltdc_crtc_init(ddev, crtc);
if (ret) {
DRM_ERROR("Failed to init crtc\n");
@@ -1144,8 +1278,11 @@
/* 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]);
@@ -1157,7 +1294,6 @@
void ltdc_unload(struct drm_device *ddev)
{
- struct ltdc_device *ldev = ddev->dev_private;
int i;
DRM_DEBUG_DRIVER("\n");
@@ -1165,7 +1301,7 @@
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>");