Hello,
Here's the second version of my current stack of pending patches for the omapdrm driver.
All comments received for v1 have been taken into account. Changes since v1 include
- Rebase on top to Tomi's 4.8/omapdrm branch and drop already merged patches (git://git.kernel.org/pub/scm/linux/kernel/git/tomba/linux.git) - Drop "drm: omapdrm: Remove buffer synchronization support" (23/23) - Replace "drm: omapdrm: fb: Don't store format BPP for each plane" (02/23) and "drm: omapdrm: fb: Store number of planes in format structure" (03/23) with "drm: omapdrm: fb: Use format information provided by the DRM core" (02/20)
Individual changelogs are available in the patches.
The most notable change in this series is still the rework of the IRQ handling code (patches 06/20 to 19/20) that, beside simplifying the code, ensures that the vblank count and timestamp get updated properly in order to be reported to userspace.
The series is now based on top of the "[PATCH 0/4] Centralize format information" series I have just sent to the dri-devel mailing list.
Laurent Pinchart (20): drm: omapdrm: fb: Limit number of planes per framebuffer to two drm: omapdrm: fb: Use format information provided by the DRM core drm: omapdrm: fb: Simplify objects lookup when creating framebuffer drm: omapdrm: fb: Simplify mode command checks when creating framebuffer drm: omapdrm: fb: Turn framebuffer creation error messages into debug drm: omapdrm: Handle FIFO underflow IRQs internally drm: omapdrm: Handle CRTC error IRQs directly drm: omapdrm: Handle OCP error IRQ directly drm: omapdrm: Use atomic state instead of local device state drm: omapdrm: Only commit planes on active CRTCs drm: omapdrm: Check DSS manager state in the enable/disable helpers drm: omapdrm: Prevent processing the same event multiple times drm: omapdrm: Use a spinlock to protect the CRTC pending flag drm: omapdrm: Keep vblank interrupt enabled while CRTC is active drm: omapdrm: Don't expose the omap_irq_(un)register() functions drm: omapdrm: Remove unused parameter from omap_drm_irq handler drm: omapdrm: Don't call DISPC power handling in IRQ wait functions drm: omapdrm: Make pipe2vbl function static drm: omapdrm: Simplify IRQ wait implementation drm: omapdrm: Remove global variables
drivers/gpu/drm/omapdrm/dss/dispc.c | 1 - drivers/gpu/drm/omapdrm/dss/output.c | 6 + drivers/gpu/drm/omapdrm/omap_crtc.c | 121 ++++++++++-------- drivers/gpu/drm/omapdrm/omap_drv.c | 6 +- drivers/gpu/drm/omapdrm/omap_drv.h | 51 +------- drivers/gpu/drm/omapdrm/omap_fb.c | 170 ++++++++++++------------ drivers/gpu/drm/omapdrm/omap_irq.c | 242 +++++++++++++++++++---------------- drivers/gpu/drm/omapdrm/omap_plane.c | 24 ---- 8 files changed, 305 insertions(+), 316 deletions(-)
The only multi-planar format supported by the driver is NV12, there will thus never be more than two planes per framebuffer.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 530567cc25b7..4214138d5795 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -36,7 +36,7 @@ struct format { struct { int stride_bpp; /* this times width is stride */ int sub_y; /* sub-sample in y dimension */ - } planes[4]; + } planes[2]; bool yuv; };
@@ -90,7 +90,7 @@ struct omap_framebuffer { struct drm_framebuffer base; int pin_count; const struct format *format; - struct plane planes[4]; + struct plane planes[2]; /* lock for pinning (pin_count and planes.paddr) */ struct mutex lock; };
On 07/06/16 02:42, Laurent Pinchart wrote:
The only multi-planar format supported by the driver is NV12, there will thus never be more than two planes per framebuffer.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com
drivers/gpu/drm/omapdrm/omap_fb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 530567cc25b7..4214138d5795 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -36,7 +36,7 @@ struct format { struct { int stride_bpp; /* this times width is stride */ int sub_y; /* sub-sample in y dimension */
- } planes[4];
- } planes[2]; bool yuv;
};
@@ -90,7 +90,7 @@ struct omap_framebuffer { struct drm_framebuffer base; int pin_count; const struct format *format;
- struct plane planes[4];
- struct plane planes[2]; /* lock for pinning (pin_count and planes.paddr) */ struct mutex lock;
};
Reviewed-by: Tomi Valkeinen tomi.valkeinen@ti.com
Tomi
The driver stores in a custom structure named format several pieces of information about the format that are available in the DRM core. Remove them and get the information from the DRM core instead.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_fb.c | 91 ++++++++++++++++++++------------------- 1 file changed, 46 insertions(+), 45 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 4214138d5795..d87caf3f8a8f 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -29,37 +29,30 @@ * framebuffer funcs */
-/* per-format info: */ -struct format { +/* DSS to DRM formats mapping */ +static const struct { enum omap_color_mode dss_format; uint32_t pixel_format; - struct { - int stride_bpp; /* this times width is stride */ - int sub_y; /* sub-sample in y dimension */ - } planes[2]; - bool yuv; -}; - -static const struct format formats[] = { +} formats[] = { /* 16bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565, {{2, 1}}, false }, /* RGB16-565 */ - { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444, {{2, 1}}, false }, /* RGB12x-4444 */ - { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444, {{2, 1}}, false }, /* xRGB12-4444 */ - { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444, {{2, 1}}, false }, /* RGBA12-4444 */ - { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444, {{2, 1}}, false }, /* ARGB16-4444 */ - { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555, {{2, 1}}, false }, /* xRGB15-1555 */ - { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555, {{2, 1}}, false }, /* ARGB16-1555 */ + { OMAP_DSS_COLOR_RGB16, DRM_FORMAT_RGB565 }, /* RGB16-565 */ + { OMAP_DSS_COLOR_RGB12U, DRM_FORMAT_RGBX4444 }, /* RGB12x-4444 */ + { OMAP_DSS_COLOR_RGBX16, DRM_FORMAT_XRGB4444 }, /* xRGB12-4444 */ + { OMAP_DSS_COLOR_RGBA16, DRM_FORMAT_RGBA4444 }, /* RGBA12-4444 */ + { OMAP_DSS_COLOR_ARGB16, DRM_FORMAT_ARGB4444 }, /* ARGB16-4444 */ + { OMAP_DSS_COLOR_XRGB16_1555, DRM_FORMAT_XRGB1555 }, /* xRGB15-1555 */ + { OMAP_DSS_COLOR_ARGB16_1555, DRM_FORMAT_ARGB1555 }, /* ARGB16-1555 */ /* 24bpp RGB: */ - { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888, {{3, 1}}, false }, /* RGB24-888 */ + { OMAP_DSS_COLOR_RGB24P, DRM_FORMAT_RGB888 }, /* RGB24-888 */ /* 32bpp [A]RGB: */ - { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888, {{4, 1}}, false }, /* RGBx24-8888 */ - { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888, {{4, 1}}, false }, /* xRGB24-8888 */ - { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888, {{4, 1}}, false }, /* RGBA32-8888 */ - { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888, {{4, 1}}, false }, /* ARGB32-8888 */ + { OMAP_DSS_COLOR_RGBX32, DRM_FORMAT_RGBX8888 }, /* RGBx24-8888 */ + { OMAP_DSS_COLOR_RGB24U, DRM_FORMAT_XRGB8888 }, /* xRGB24-8888 */ + { OMAP_DSS_COLOR_RGBA32, DRM_FORMAT_RGBA8888 }, /* RGBA32-8888 */ + { OMAP_DSS_COLOR_ARGB32, DRM_FORMAT_ARGB8888 }, /* ARGB32-8888 */ /* YUV: */ - { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12, {{1, 1}, {1, 2}}, true }, - { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV, {{2, 1}}, true }, - { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY, {{2, 1}}, true }, + { OMAP_DSS_COLOR_NV12, DRM_FORMAT_NV12 }, + { OMAP_DSS_COLOR_YUV2, DRM_FORMAT_YUYV }, + { OMAP_DSS_COLOR_UYVY, DRM_FORMAT_UYVY }, };
/* convert from overlay's pixel formats bitmask to an array of fourcc's */ @@ -89,7 +82,8 @@ struct plane { struct omap_framebuffer { struct drm_framebuffer base; int pin_count; - const struct format *format; + const struct drm_format_info *format; + enum omap_color_mode dss_format; struct plane planes[2]; /* lock for pinning (pin_count and planes.paddr) */ struct mutex lock; @@ -136,13 +130,13 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = { };
static uint32_t get_linear_addr(struct plane *plane, - const struct format *format, int n, int x, int y) + const struct drm_format_info *format, int n, int x, int y) { uint32_t offset;
- offset = plane->offset + - (x * format->planes[n].stride_bpp) + - (y * plane->pitch / format->planes[n].sub_y); + offset = plane->offset + + (x * format->cpp[n] / (n == 1 ? 1 : format->hsub)) + + (y * plane->pitch / (n == 1 ? 1 : format->vsub));
return plane->paddr + offset; } @@ -161,11 +155,11 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, struct omap_drm_window *win, struct omap_overlay_info *info) { struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); - const struct format *format = omap_fb->format; + const struct drm_format_info *format = omap_fb->format; struct plane *plane = &omap_fb->planes[0]; uint32_t x, y, orient = 0;
- info->color_mode = format->dss_format; + info->color_mode = omap_fb->dss_format;
info->pos_x = win->crtc_x; info->pos_y = win->crtc_y; @@ -239,9 +233,9 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, }
/* convert to pixels: */ - info->screen_width /= format->planes[0].stride_bpp; + info->screen_width /= format->cpp[0];
- if (format->dss_format == OMAP_DSS_COLOR_NV12) { + if (omap_fb->dss_format == OMAP_DSS_COLOR_NV12) { plane = &omap_fb->planes[1];
if (info->rotation_type == OMAP_DSS_ROT_TILER) { @@ -390,23 +384,26 @@ struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, const struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_object **bos) { + const struct drm_format_info *format = NULL; struct omap_framebuffer *omap_fb = NULL; struct drm_framebuffer *fb = NULL; - const struct format *format = NULL; - int ret, i, n = drm_format_num_planes(mode_cmd->pixel_format); + enum omap_color_mode dss_format = 0; + int ret, i;
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format);
+ format = drm_format_info(mode_cmd->pixel_format); + for (i = 0; i < ARRAY_SIZE(formats); i++) { if (formats[i].pixel_format == mode_cmd->pixel_format) { - format = &formats[i]; + dss_format = formats[i].dss_format; break; } }
- if (!format) { + if (!format || !dss_format) { dev_err(dev->dev, "unsupported pixel format: %4.4s\n", (char *)&mode_cmd->pixel_format); ret = -EINVAL; @@ -421,28 +418,32 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
fb = &omap_fb->base; omap_fb->format = format; + omap_fb->dss_format = dss_format; mutex_init(&omap_fb->lock);
- for (i = 0; i < n; i++) { + for (i = 0; i < format->num_planes; i++) { struct plane *plane = &omap_fb->planes[i]; - int size, pitch = mode_cmd->pitches[i]; + unsigned int pitch = mode_cmd->pitches[i]; + unsigned int hsub = i == 0 ? 1 : format->hsub; + unsigned int vsub = i == 0 ? 1 : format->vsub; + unsigned int size;
- if (pitch < (mode_cmd->width * format->planes[i].stride_bpp)) { + if (pitch < mode_cmd->width * format->cpp[i] / hsub) { dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n", - pitch, mode_cmd->width * format->planes[i].stride_bpp); + pitch, mode_cmd->width * format->cpp[i] / hsub); ret = -EINVAL; goto fail; }
- if (pitch % format->planes[i].stride_bpp != 0) { + if (pitch % format->cpp[i] != 0) { dev_err(dev->dev, "buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n", - pitch, format->planes[i].stride_bpp); + pitch, format->cpp[i]); ret = -EINVAL; goto fail; }
- size = pitch * mode_cmd->height / format->planes[i].sub_y; + size = pitch * mode_cmd->height / vsub;
if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) { dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
Merge the single-user objects_lookup inline function into its caller, allowing reuse of the error code path.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Tomi Valkeinen tomi.valkeinen@ti.com --- drivers/gpu/drm/omapdrm/omap_drv.h | 25 ------------------------- drivers/gpu/drm/omapdrm/omap_fb.c | 29 ++++++++++++++++++----------- 2 files changed, 18 insertions(+), 36 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 51dbb820ff1d..799f26b0573a 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -241,29 +241,4 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, uint32_t pipe2vbl(struct drm_crtc *crtc); struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder);
-/* should these be made into common util helpers? - */ - -static inline int objects_lookup( - struct drm_file *filp, uint32_t pixel_format, - struct drm_gem_object **bos, const uint32_t *handles) -{ - int i, n = drm_format_num_planes(pixel_format); - - for (i = 0; i < n; i++) { - bos[i] = drm_gem_object_lookup(filp, handles[i]); - if (!bos[i]) - goto fail; - - } - - return 0; - -fail: - while (--i > 0) - drm_gem_object_unreference_unlocked(bos[i]); - - return -ENOENT; -} - #endif /* __OMAP_DRV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index d87caf3f8a8f..dc30a7501fe0 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -362,22 +362,29 @@ void omap_framebuffer_describe(struct drm_framebuffer *fb, struct seq_file *m) struct drm_framebuffer *omap_framebuffer_create(struct drm_device *dev, struct drm_file *file, const struct drm_mode_fb_cmd2 *mode_cmd) { + unsigned int num_planes = drm_format_num_planes(mode_cmd->pixel_format); struct drm_gem_object *bos[4]; struct drm_framebuffer *fb; - int ret; + int i;
- ret = objects_lookup(file, mode_cmd->pixel_format, - bos, mode_cmd->handles); - if (ret) - return ERR_PTR(ret); + for (i = 0; i < num_planes; i++) { + bos[i] = drm_gem_object_lookup(file, mode_cmd->handles[i]); + if (!bos[i]) { + fb = ERR_PTR(-ENOENT); + goto error; + } + }
fb = omap_framebuffer_init(dev, mode_cmd, bos); - if (IS_ERR(fb)) { - int i, n = drm_format_num_planes(mode_cmd->pixel_format); - for (i = 0; i < n; i++) - drm_gem_object_unreference_unlocked(bos[i]); - return fb; - } + if (IS_ERR(fb)) + goto error; + + return fb; + +error: + while (--i > 0) + drm_gem_object_unreference_unlocked(bos[i]); + return fb; }
The hardware requires all planes to have an identical pitch in number of pixels. Given that all supported formats use the same number of bytes per pixel in all planes, framebuffer creation checks can be simplified. The implementations assumes that no format use more than two planes which is true with the existing hardware.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- Changes since v1:
- Clarify commit message and mention explicitly in the code that only one and two planes formats are supported. - Rebase on top of the switch to drm_format_info() --- drivers/gpu/drm/omapdrm/omap_fb.c | 58 +++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index dc30a7501fe0..3eca8c36ee92 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -395,6 +395,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, struct omap_framebuffer *omap_fb = NULL; struct drm_framebuffer *fb = NULL; enum omap_color_mode dss_format = 0; + unsigned int pitch = mode_cmd->pitches[0]; int ret, i;
DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", @@ -428,41 +429,44 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, omap_fb->dss_format = dss_format; mutex_init(&omap_fb->lock);
+ /* + * The code below assumes that no format use more than two planes, and + * that the two planes of multiplane formats need the same number of + * bytes per pixel. + */ + if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) { + dev_err(dev->dev, "pitches differ between planes 0 and 1\n"); + ret = -EINVAL; + goto fail; + } + + if (pitch < mode_cmd->width * format->cpp[0]) { + dev_err(dev->dev, + "provided buffer pitch is too small! %u < %u\n", + pitch, mode_cmd->width * format->cpp[0]); + ret = -EINVAL; + goto fail; + } + + if (pitch % format->cpp[0]) { + dev_err(dev->dev, + "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n", + pitch, format->cpp[0]); + ret = -EINVAL; + goto fail; + } + for (i = 0; i < format->num_planes; i++) { struct plane *plane = &omap_fb->planes[i]; - unsigned int pitch = mode_cmd->pitches[i]; - unsigned int hsub = i == 0 ? 1 : format->hsub; unsigned int vsub = i == 0 ? 1 : format->vsub; unsigned int size;
- if (pitch < mode_cmd->width * format->cpp[i] / hsub) { - dev_err(dev->dev, "provided buffer pitch is too small! %d < %d\n", - pitch, mode_cmd->width * format->cpp[i] / hsub); - ret = -EINVAL; - goto fail; - } - - if (pitch % format->cpp[i] != 0) { - dev_err(dev->dev, - "buffer pitch (%d bytes) is not a multiple of pixel size (%d bytes)\n", - pitch, format->cpp[i]); - ret = -EINVAL; - goto fail; - } - size = pitch * mode_cmd->height / vsub;
- if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) { - dev_err(dev->dev, "provided buffer object is too small! %d < %d\n", - bos[i]->size - mode_cmd->offsets[i], size); - ret = -EINVAL; - goto fail; - } - - if (i > 0 && pitch != mode_cmd->pitches[i - 1]) { + if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) { dev_err(dev->dev, - "pitches are not the same between framebuffer planes %d != %d\n", - pitch, mode_cmd->pitches[i - 1]); + "provided buffer object is too small! %d < %d\n", + bos[i]->size - mode_cmd->offsets[i], size); ret = -EINVAL; goto fail; }
Don't print userspace parameters validation failures as error messages to avoid giving userspace the ability to flood the kernel log.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Tomi Valkeinen tomi.valkeinen@ti.com --- drivers/gpu/drm/omapdrm/omap_fb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c index 3eca8c36ee92..bc0fb805696e 100644 --- a/drivers/gpu/drm/omapdrm/omap_fb.c +++ b/drivers/gpu/drm/omapdrm/omap_fb.c @@ -412,8 +412,8 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, }
if (!format || !dss_format) { - dev_err(dev->dev, "unsupported pixel format: %4.4s\n", - (char *)&mode_cmd->pixel_format); + dev_dbg(dev->dev, "unsupported pixel format: %4.4s\n", + (char *)&mode_cmd->pixel_format); ret = -EINVAL; goto fail; } @@ -435,13 +435,13 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, * bytes per pixel. */ if (format->num_planes == 2 && pitch != mode_cmd->pitches[1]) { - dev_err(dev->dev, "pitches differ between planes 0 and 1\n"); + dev_dbg(dev->dev, "pitches differ between planes 0 and 1\n"); ret = -EINVAL; goto fail; }
if (pitch < mode_cmd->width * format->cpp[0]) { - dev_err(dev->dev, + dev_dbg(dev->dev, "provided buffer pitch is too small! %u < %u\n", pitch, mode_cmd->width * format->cpp[0]); ret = -EINVAL; @@ -449,7 +449,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, }
if (pitch % format->cpp[0]) { - dev_err(dev->dev, + dev_dbg(dev->dev, "buffer pitch (%u bytes) is not a multiple of pixel size (%u bytes)\n", pitch, format->cpp[0]); ret = -EINVAL; @@ -464,7 +464,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, size = pitch * mode_cmd->height / vsub;
if (size > omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i]) { - dev_err(dev->dev, + dev_dbg(dev->dev, "provided buffer object is too small! %d < %d\n", bos[i]->size - mode_cmd->offsets[i], size); ret = -EINVAL;
As the FIFO underflow IRQ handler just prints an error message to the kernel log, simplify the code by not registering one IRQ handler per plane but print the messages directly from the main IRQ handler.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- Changes since v1:
- Only register error IRQs that exist on the HW --- drivers/gpu/drm/omapdrm/omap_drv.c | 4 +-- drivers/gpu/drm/omapdrm/omap_drv.h | 2 +- drivers/gpu/drm/omapdrm/omap_irq.c | 66 ++++++++++++++++++++++++++++++++++-- drivers/gpu/drm/omapdrm/omap_plane.c | 24 ------------- 4 files changed, 66 insertions(+), 30 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 6c0647486945..65f280f5d947 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -322,8 +322,6 @@ static int omap_modeset_init(struct drm_device *dev)
drm_mode_config_init(dev);
- omap_drm_irq_install(dev); - ret = omap_modeset_init_properties(dev); if (ret < 0) return ret; @@ -492,6 +490,8 @@ static int omap_modeset_init(struct drm_device *dev)
drm_mode_config_reset(dev);
+ omap_drm_irq_install(dev); + return 0; }
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 799f26b0573a..edd8518b8ad4 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -103,7 +103,7 @@ struct omap_drm_private {
/* irq handling: */ struct list_head irq_list; /* list of omap_drm_irq */ - uint32_t vblank_mask; /* irq bits set for userspace vblank */ + uint32_t irq_mask; /* enabled irqs in addition to irq_list */ struct omap_drm_irq error_handler;
/* atomic commit */ diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 60e1e8016708..245263b09759 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -32,7 +32,7 @@ static void omap_irq_update(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; struct omap_drm_irq *irq; - uint32_t irqmask = priv->vblank_mask; + uint32_t irqmask = priv->irq_mask;
assert_spin_locked(&list_lock);
@@ -153,7 +153,7 @@ int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe) DBG("dev=%p, crtc=%u", dev, pipe);
spin_lock_irqsave(&list_lock, flags); - priv->vblank_mask |= pipe2vbl(crtc); + priv->irq_mask |= pipe2vbl(crtc); omap_irq_update(dev); spin_unlock_irqrestore(&list_lock, flags);
@@ -178,11 +178,52 @@ void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe) DBG("dev=%p, crtc=%u", dev, pipe);
spin_lock_irqsave(&list_lock, flags); - priv->vblank_mask &= ~pipe2vbl(crtc); + priv->irq_mask &= ~pipe2vbl(crtc); omap_irq_update(dev); spin_unlock_irqrestore(&list_lock, flags); }
+static void omap_irq_fifo_underflow(struct omap_drm_private *priv, + u32 irqstatus) +{ + static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL, + DEFAULT_RATELIMIT_BURST); + static const struct { + const char *name; + u32 mask; + } sources[] = { + { "gfx", DISPC_IRQ_GFX_FIFO_UNDERFLOW }, + { "vid1", DISPC_IRQ_VID1_FIFO_UNDERFLOW }, + { "vid2", DISPC_IRQ_VID2_FIFO_UNDERFLOW }, + { "vid3", DISPC_IRQ_VID3_FIFO_UNDERFLOW }, + }; + + const u32 mask = DISPC_IRQ_GFX_FIFO_UNDERFLOW + | DISPC_IRQ_VID1_FIFO_UNDERFLOW + | DISPC_IRQ_VID2_FIFO_UNDERFLOW + | DISPC_IRQ_VID3_FIFO_UNDERFLOW; + unsigned int i; + + spin_lock(&list_lock); + irqstatus &= priv->irq_mask & mask; + spin_unlock(&list_lock); + + if (!irqstatus) + return; + + if (!__ratelimit(&_rs)) + return; + + drm_err("error: FIFO underflow on "); + + for (i = 0; i < ARRAY_SIZE(sources); ++i) { + if (sources[i].mask & irqstatus) + pr_cont("%s ", sources[i].name); + } + + pr_cont("(0x%08x)\n", irqstatus); +} + static irqreturn_t omap_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -205,6 +246,8 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) drm_handle_vblank(dev, id); }
+ omap_irq_fifo_underflow(priv, irqstatus); + spin_lock_irqsave(&list_lock, flags); list_for_each_entry_safe(handler, n, &priv->irq_list, node) { if (handler->irqmask & irqstatus) { @@ -218,6 +261,13 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) return IRQ_HANDLED; }
+static const u32 error_irqs[] = { + [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, + [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, +}; + /* * We need a special version, instead of just using drm_irq_install(), * because we need to register the irq via omapdss. Once omapdss and @@ -229,10 +279,20 @@ int omap_drm_irq_install(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; struct omap_drm_irq *error_handler = &priv->error_handler; + unsigned int max_planes; + unsigned int i; int ret;
INIT_LIST_HEAD(&priv->irq_list);
+ priv->irq_mask = 0; + + max_planes = min(ARRAY_SIZE(priv->planes), ARRAY_SIZE(error_irqs)); + for (i = 0; i < max_planes; ++i) { + if (priv->planes[i]) + priv->irq_mask |= error_irqs[i]; + } + dispc_runtime_get(); dispc_clear_irqstatus(0xffffffff); dispc_runtime_put(); diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 5252ab720e70..037bd1f7c70a 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -43,8 +43,6 @@ struct omap_plane {
uint32_t nformats; uint32_t formats[32]; - - struct omap_drm_irq error_irq; };
struct omap_plane_state { @@ -200,8 +198,6 @@ static void omap_plane_destroy(struct drm_plane *plane)
DBG("%s", omap_plane->name);
- omap_irq_unregister(plane->dev, &omap_plane->error_irq); - drm_plane_cleanup(plane);
kfree(omap_plane); @@ -320,14 +316,6 @@ static const struct drm_plane_funcs omap_plane_funcs = { .atomic_get_property = omap_plane_atomic_get_property, };
-static void omap_plane_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) -{ - struct omap_plane *omap_plane = - container_of(irq, struct omap_plane, error_irq); - DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_plane->name, - irqstatus); -} - static const char *plane_names[] = { [OMAP_DSS_GFX] = "gfx", [OMAP_DSS_VIDEO1] = "vid1", @@ -335,13 +323,6 @@ static const char *plane_names[] = { [OMAP_DSS_VIDEO3] = "vid3", };
-static const uint32_t error_irqs[] = { - [OMAP_DSS_GFX] = DISPC_IRQ_GFX_FIFO_UNDERFLOW, - [OMAP_DSS_VIDEO1] = DISPC_IRQ_VID1_FIFO_UNDERFLOW, - [OMAP_DSS_VIDEO2] = DISPC_IRQ_VID2_FIFO_UNDERFLOW, - [OMAP_DSS_VIDEO3] = DISPC_IRQ_VID3_FIFO_UNDERFLOW, -}; - /* initialize plane */ struct drm_plane *omap_plane_init(struct drm_device *dev, int id, enum drm_plane_type type) @@ -365,10 +346,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
plane = &omap_plane->base;
- omap_plane->error_irq.irqmask = error_irqs[id]; - omap_plane->error_irq.irq = omap_plane_error_irq; - omap_irq_register(dev, &omap_plane->error_irq); - ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, &omap_plane_funcs, omap_plane->formats, omap_plane->nformats, type, NULL); @@ -382,7 +359,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, return plane;
error: - omap_irq_unregister(plane->dev, &omap_plane->error_irq); kfree(omap_plane); return NULL; }
Instead of going through a complicated registration mechanism, just expose the CRTC error IRQ function and call it directly from the main IRQ handler.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com Reviewed-by: Tomi Valkeinen tomi.valkeinen@ti.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 12 ++---------- drivers/gpu/drm/omapdrm/omap_drv.h | 1 + drivers/gpu/drm/omapdrm/omap_irq.c | 8 ++++++++ 3 files changed, 11 insertions(+), 10 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 075f2bb44867..6359d7933b93 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -37,7 +37,6 @@ struct omap_crtc { struct omap_video_timings timings;
struct omap_drm_irq vblank_irq; - struct omap_drm_irq error_irq;
bool ignore_digit_sync_lost;
@@ -275,10 +274,9 @@ static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) spin_unlock_irqrestore(&dev->event_lock, flags); }
-static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus) { - struct omap_crtc *omap_crtc = - container_of(irq, struct omap_crtc, error_irq); + struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
if (omap_crtc->ignore_digit_sync_lost) { irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT; @@ -325,7 +323,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc) DBG("%s", omap_crtc->name);
WARN_ON(omap_crtc->vblank_irq.registered); - omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
drm_crtc_cleanup(crtc);
@@ -520,11 +517,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc); omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
- omap_crtc->error_irq.irqmask = - dispc_mgr_get_sync_lost_irq(channel); - omap_crtc->error_irq.irq = omap_crtc_error_irq; - omap_irq_register(dev, &omap_crtc->error_irq); - ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &omap_crtc_funcs, NULL); if (ret < 0) { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index edd8518b8ad4..ffc958eea5a3 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -156,6 +156,7 @@ void omap_crtc_pre_uninit(void); struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_plane *plane, enum omap_channel channel, int id); int omap_crtc_wait_pending(struct drm_crtc *crtc); +void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus);
struct drm_plane *omap_plane_init(struct drm_device *dev, int id, enum drm_plane_type type); diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 245263b09759..c21ed4afd1ac 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -241,9 +241,13 @@ static irqreturn_t omap_irq_handler(int irq, void *arg)
for (id = 0; id < priv->num_crtcs; id++) { struct drm_crtc *crtc = priv->crtcs[id]; + enum omap_channel channel = omap_crtc_channel(crtc);
if (irqstatus & pipe2vbl(crtc)) drm_handle_vblank(dev, id); + + if (irqstatus & dispc_mgr_get_sync_lost_irq(channel)) + omap_crtc_error_irq(crtc, irqstatus); }
omap_irq_fifo_underflow(priv, irqstatus); @@ -279,6 +283,7 @@ int omap_drm_irq_install(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; struct omap_drm_irq *error_handler = &priv->error_handler; + unsigned int num_mgrs = dss_feat_get_num_mgrs(); unsigned int max_planes; unsigned int i; int ret; @@ -293,6 +298,9 @@ int omap_drm_irq_install(struct drm_device *dev) priv->irq_mask |= error_irqs[i]; }
+ for (i = 0; i < num_mgrs; ++i) + priv->irq_mask |= dispc_mgr_get_sync_lost_irq(i); + dispc_runtime_get(); dispc_clear_irqstatus(0xffffffff); dispc_runtime_put();
Instead of going through a complicated registration mechanism, just call the OCP error IRQ handler directly from the main IRQ handler.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- Changes since v1:
- Rename IRQ handler to omap_irq_ocp_error_handler() - Replace hex error value with "OCP error" message --- drivers/gpu/drm/omapdrm/omap_drv.h | 1 - drivers/gpu/drm/omapdrm/omap_irq.c | 28 ++++++++++------------------ 2 files changed, 10 insertions(+), 19 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index ffc958eea5a3..4bbfe0869a48 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -104,7 +104,6 @@ struct omap_drm_private { /* irq handling: */ struct list_head irq_list; /* list of omap_drm_irq */ uint32_t irq_mask; /* enabled irqs in addition to irq_list */ - struct omap_drm_irq error_handler;
/* atomic commit */ struct { diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index c21ed4afd1ac..d0730178888f 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -21,12 +21,6 @@
static DEFINE_SPINLOCK(list_lock);
-static void omap_irq_error_handler(struct omap_drm_irq *irq, - uint32_t irqstatus) -{ - DRM_ERROR("errors: %08x\n", irqstatus); -} - /* call with list_lock and dispc runtime held */ static void omap_irq_update(struct drm_device *dev) { @@ -224,6 +218,14 @@ static void omap_irq_fifo_underflow(struct omap_drm_private *priv, pr_cont("(0x%08x)\n", irqstatus); }
+static void omap_irq_ocp_error_handler(u32 irqstatus) +{ + if (!(irqstatus & DISPC_IRQ_OCP_ERR)) + return; + + DRM_ERROR("OCP error\n"); +} + static irqreturn_t omap_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; @@ -250,6 +252,7 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) omap_crtc_error_irq(crtc, irqstatus); }
+ omap_irq_ocp_error_handler(irqstatus); omap_irq_fifo_underflow(priv, irqstatus);
spin_lock_irqsave(&list_lock, flags); @@ -282,7 +285,6 @@ static const u32 error_irqs[] = { int omap_drm_irq_install(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - struct omap_drm_irq *error_handler = &priv->error_handler; unsigned int num_mgrs = dss_feat_get_num_mgrs(); unsigned int max_planes; unsigned int i; @@ -290,7 +292,7 @@ int omap_drm_irq_install(struct drm_device *dev)
INIT_LIST_HEAD(&priv->irq_list);
- priv->irq_mask = 0; + priv->irq_mask = DISPC_IRQ_OCP_ERR;
max_planes = min(ARRAY_SIZE(priv->planes), ARRAY_SIZE(error_irqs)); for (i = 0; i < max_planes; ++i) { @@ -309,16 +311,6 @@ int omap_drm_irq_install(struct drm_device *dev) if (ret < 0) return ret;
- error_handler->irq = omap_irq_error_handler; - error_handler->irqmask = DISPC_IRQ_OCP_ERR; - - /* for now ignore DISPC_IRQ_SYNC_LOST_DIGIT.. really I think - * we just need to ignore it while enabling tv-out - */ - error_handler->irqmask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; - - omap_irq_register(dev, error_handler); - dev->irq_enabled = true;
return 0;
Instead of conditioning planes update based on the hardware device state, use the CRTC state stored in the atomic state. This reduces the dependency from the DRM layer to the DSS layer.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 6359d7933b93..4c56d6a68390 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -381,18 +381,23 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
WARN_ON(omap_crtc->vblank_irq.registered);
- if (dispc_mgr_is_enabled(omap_crtc->channel)) { + /* + * Only flush the CRTC if it is currently active. CRTCs that require a + * mode set are disabled prior plane updates and enabled afterwards. + * They are thus not active, regardless of what their state report. + */ + if (!crtc->state->active || drm_atomic_crtc_needs_modeset(crtc->state)) + return;
- DBG("%s: GO", omap_crtc->name); + DBG("%s: GO", omap_crtc->name);
- rmb(); - WARN_ON(omap_crtc->pending); - omap_crtc->pending = true; - wmb(); + rmb(); + WARN_ON(omap_crtc->pending); + omap_crtc->pending = true; + wmb();
- dispc_mgr_go(omap_crtc->channel); - omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); - } + dispc_mgr_go(omap_crtc->channel); + omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); }
static bool omap_crtc_is_plane_prop(struct drm_device *dev,
The DRM core supports skipping plane update for inactive CRTCs for hardware that don't need it or can't cope with it. That's our case, so use the DRM core infrastructure instead of reinventing it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 6 ++++-- drivers/gpu/drm/omapdrm/omap_drv.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 4c56d6a68390..37b5f7096c6e 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -384,9 +384,11 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, /* * Only flush the CRTC if it is currently active. CRTCs that require a * mode set are disabled prior plane updates and enabled afterwards. - * They are thus not active, regardless of what their state report. + * They are thus not active, regardless of what their state report, and + * the DRM core could thus call this function even though the CRTC is + * currently disabled. Do nothing in that case. */ - if (!crtc->state->active || drm_atomic_crtc_needs_modeset(crtc->state)) + if (drm_atomic_crtc_needs_modeset(crtc->state)) return;
DBG("%s: GO", omap_crtc->name); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 65f280f5d947..70c5f24e67f7 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -96,7 +96,7 @@ static void omap_atomic_complete(struct omap_atomic_state_commit *commit) dispc_runtime_get();
drm_atomic_helper_commit_modeset_disables(dev, old_state); - drm_atomic_helper_commit_planes(dev, old_state, false); + drm_atomic_helper_commit_planes(dev, old_state, true); drm_atomic_helper_commit_modeset_enables(dev, old_state);
omap_atomic_wait_for_completion(dev, old_state);
The omapdrm DSS manager enable/disable operations check the DSS manager state to avoid double enabling/disabling. Move that code to the DSS manager to decrease the dependency of the DRM layer to the DSS layer.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/dss/dispc.c | 1 - drivers/gpu/drm/omapdrm/dss/output.c | 6 ++++++ drivers/gpu/drm/omapdrm/omap_crtc.c | 3 --- 3 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 7b78da6d51cf..f51f10a2c829 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -2887,7 +2887,6 @@ bool dispc_mgr_is_enabled(enum omap_channel channel) { return !!mgr_fld_read(channel, DISPC_MGR_FLD_ENABLE); } -EXPORT_SYMBOL(dispc_mgr_is_enabled);
void dispc_wb_enable(bool enable) { diff --git a/drivers/gpu/drm/omapdrm/dss/output.c b/drivers/gpu/drm/omapdrm/dss/output.c index 829232ad8c81..e6f67d76ec95 100644 --- a/drivers/gpu/drm/omapdrm/dss/output.c +++ b/drivers/gpu/drm/omapdrm/dss/output.c @@ -218,12 +218,18 @@ EXPORT_SYMBOL(dss_mgr_set_lcd_config);
int dss_mgr_enable(enum omap_channel channel) { + if (dispc_mgr_is_enabled(channel)) + return 0; + return dss_mgr_ops->enable(channel); } EXPORT_SYMBOL(dss_mgr_enable);
void dss_mgr_disable(enum omap_channel channel) { + if (!dispc_mgr_is_enabled(channel)) + return; + dss_mgr_ops->disable(channel); } EXPORT_SYMBOL(dss_mgr_disable); diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 37b5f7096c6e..14fad7777b2c 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -139,9 +139,6 @@ static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable) return; }
- if (dispc_mgr_is_enabled(channel) == enable) - return; - if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) { /* * Digit output produces some sync lost interrupts during the
The vblank interrupt is disabled after one occurrence, preventing the atomic update event from being processed twice. However, this also prevents the software frame counter from being updated correctly that would require vblank interrupts to be kept enabled while the CRTC is active.
In preparation for vblank interrupt fixes, make sure that the atomic update event will be processed once only when the vblank interrupt will be kept enabled.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index 14fad7777b2c..c4877d626577 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -42,6 +42,7 @@ struct omap_crtc {
bool pending; wait_queue_head_t pending_wait; + struct drm_pending_vblank_event *event; };
/* ----------------------------------------------------------------------------- @@ -257,11 +258,15 @@ static const struct dss_mgr_ops mgr_ops = {
static void omap_crtc_complete_page_flip(struct drm_crtc *crtc) { + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); struct drm_pending_vblank_event *event; struct drm_device *dev = crtc->dev; unsigned long flags;
- event = crtc->state->event; + spin_lock_irqsave(&dev->event_lock, flags); + event = omap_crtc->event; + omap_crtc->event = NULL; + spin_unlock_irqrestore(&dev->event_lock, flags);
if (!event) return; @@ -367,12 +372,23 @@ static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc) }
static void omap_crtc_atomic_begin(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) + struct drm_crtc_state *old_crtc_state) { + struct drm_pending_vblank_event *event = crtc->state->event; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + unsigned long flags; + + if (event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + omap_crtc->event = event; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + } }
static void omap_crtc_atomic_flush(struct drm_crtc *crtc, - struct drm_crtc_state *old_crtc_state) + struct drm_crtc_state *old_crtc_state) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
The flag will need to be accessed atomically in the vblank interrupt handler, memory barriers won't be enough.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index c4877d626577..c83e836dfa29 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -68,6 +68,19 @@ enum omap_channel omap_crtc_channel(struct drm_crtc *crtc) return omap_crtc->channel; }
+static bool omap_crtc_is_pending(struct drm_crtc *crtc) +{ + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + unsigned long flags; + bool pending; + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + pending = omap_crtc->pending; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + + return pending; +} + int omap_crtc_wait_pending(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); @@ -77,7 +90,7 @@ int omap_crtc_wait_pending(struct drm_crtc *crtc) * a single frame refresh even on slower displays. */ return wait_event_timeout(omap_crtc->pending_wait, - !omap_crtc->pending, + !omap_crtc_is_pending(crtc), msecs_to_jiffies(250)); }
@@ -294,6 +307,7 @@ static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) struct omap_crtc *omap_crtc = container_of(irq, struct omap_crtc, vblank_irq); struct drm_device *dev = omap_crtc->base.dev; + struct drm_crtc *crtc = &omap_crtc->base;
if (dispc_mgr_go_busy(omap_crtc->channel)) return; @@ -302,10 +316,10 @@ static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
__omap_irq_unregister(dev, &omap_crtc->vblank_irq);
- rmb(); + spin_lock(&crtc->dev->event_lock); WARN_ON(!omap_crtc->pending); omap_crtc->pending = false; - wmb(); + spin_unlock(&crtc->dev->event_lock);
/* wake up userspace */ omap_crtc_complete_page_flip(&omap_crtc->base); @@ -337,10 +351,10 @@ static void omap_crtc_enable(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name);
- rmb(); + spin_lock_irq(&crtc->dev->event_lock); WARN_ON(omap_crtc->pending); omap_crtc->pending = true; - wmb(); + spin_unlock_irq(&crtc->dev->event_lock);
omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
@@ -406,10 +420,10 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
DBG("%s: GO", omap_crtc->name);
- rmb(); + spin_lock_irq(&crtc->dev->event_lock); WARN_ON(omap_crtc->pending); omap_crtc->pending = true; - wmb(); + spin_unlock_irq(&crtc->dev->event_lock);
dispc_mgr_go(omap_crtc->channel); omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
Instead of going through a complicated private IRQ registration mechanism, handle the vblank interrupt activation with the standard drm_crtc_vblank_get() and drm_crtc_vblank_put() mechanism. This will let the DRM core keep the vblank interrupt enabled as long as needed to update the frame counter.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 38 ++++++++++++++----------------------- drivers/gpu/drm/omapdrm/omap_drv.h | 1 + drivers/gpu/drm/omapdrm/omap_irq.c | 4 +++- 3 files changed, 18 insertions(+), 25 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index c83e836dfa29..a2587fd403a0 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -36,8 +36,6 @@ struct omap_crtc {
struct omap_video_timings timings;
- struct omap_drm_irq vblank_irq; - bool ignore_digit_sync_lost;
bool pending; @@ -302,25 +300,24 @@ void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus) DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus); }
-static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +void omap_crtc_vblank_irq(struct drm_crtc *crtc) { - struct omap_crtc *omap_crtc = - container_of(irq, struct omap_crtc, vblank_irq); - struct drm_device *dev = omap_crtc->base.dev; - struct drm_crtc *crtc = &omap_crtc->base; + struct omap_crtc *omap_crtc = to_omap_crtc(crtc); + bool pending;
if (dispc_mgr_go_busy(omap_crtc->channel)) return;
DBG("%s: apply done", omap_crtc->name);
- __omap_irq_unregister(dev, &omap_crtc->vblank_irq); - spin_lock(&crtc->dev->event_lock); - WARN_ON(!omap_crtc->pending); + pending = omap_crtc->pending; omap_crtc->pending = false; spin_unlock(&crtc->dev->event_lock);
+ if (pending) + drm_crtc_vblank_put(crtc); + /* wake up userspace */ omap_crtc_complete_page_flip(&omap_crtc->base);
@@ -338,8 +335,6 @@ static void omap_crtc_destroy(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name);
- WARN_ON(omap_crtc->vblank_irq.registered); - drm_crtc_cleanup(crtc);
kfree(omap_crtc); @@ -351,14 +346,13 @@ static void omap_crtc_enable(struct drm_crtc *crtc)
DBG("%s", omap_crtc->name);
+ drm_crtc_vblank_on(crtc); + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + spin_lock_irq(&crtc->dev->event_lock); WARN_ON(omap_crtc->pending); omap_crtc->pending = true; spin_unlock_irq(&crtc->dev->event_lock); - - omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); - - drm_crtc_vblank_on(crtc); }
static void omap_crtc_disable(struct drm_crtc *crtc) @@ -406,8 +400,6 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc, { struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
- WARN_ON(omap_crtc->vblank_irq.registered); - /* * Only flush the CRTC if it is currently active. CRTCs that require a * mode set are disabled prior plane updates and enabled afterwards. @@ -420,13 +412,14 @@ static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
DBG("%s: GO", omap_crtc->name);
+ dispc_mgr_go(omap_crtc->channel); + + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + spin_lock_irq(&crtc->dev->event_lock); WARN_ON(omap_crtc->pending); omap_crtc->pending = true; spin_unlock_irq(&crtc->dev->event_lock); - - dispc_mgr_go(omap_crtc->channel); - omap_irq_register(crtc->dev, &omap_crtc->vblank_irq); }
static bool omap_crtc_is_plane_prop(struct drm_device *dev, @@ -548,9 +541,6 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, omap_crtc->channel = channel; omap_crtc->name = channel_names[channel];
- omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc); - omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq; - ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL, &omap_crtc_funcs, NULL); if (ret < 0) { diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 4bbfe0869a48..36b20c085026 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -156,6 +156,7 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, struct drm_plane *plane, enum omap_channel channel, int id); int omap_crtc_wait_pending(struct drm_crtc *crtc); void omap_crtc_error_irq(struct drm_crtc *crtc, uint32_t irqstatus); +void omap_crtc_vblank_irq(struct drm_crtc *crtc);
struct drm_plane *omap_plane_init(struct drm_device *dev, int id, enum drm_plane_type type); diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index d0730178888f..b0aa33d3570c 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -245,8 +245,10 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) struct drm_crtc *crtc = priv->crtcs[id]; enum omap_channel channel = omap_crtc_channel(crtc);
- if (irqstatus & pipe2vbl(crtc)) + if (irqstatus & pipe2vbl(crtc)) { drm_handle_vblank(dev, id); + omap_crtc_vblank_irq(crtc); + }
if (irqstatus & dispc_mgr_get_sync_lost_irq(channel)) omap_crtc_error_irq(crtc, irqstatus);
The IRQ registration functions are not used outside of their compilation unit, make them static. As the __omap_irq_(un)register() functions are only called by their omap_irq_(un)register() counterparts, merge them together.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- Changes since v1:
- Split the omap_drm_irq irqstatus parameter removal change out --- drivers/gpu/drm/omapdrm/omap_drv.h | 4 ---- drivers/gpu/drm/omapdrm/omap_irq.c | 23 +++++------------------ 2 files changed, 5 insertions(+), 22 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 36b20c085026..e15f6b384f65 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -128,10 +128,6 @@ int omap_gem_resume(struct device *dev);
int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe); void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe); -void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); -void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); -void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq); -void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq); void omap_drm_irq_uninstall(struct drm_device *dev); int omap_drm_irq_install(struct drm_device *dev);
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index b0aa33d3570c..c14530922f42 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -39,11 +39,12 @@ static void omap_irq_update(struct drm_device *dev) dispc_read_irqenable(); /* flush posted write */ }
-void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) +static void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) { struct omap_drm_private *priv = dev->dev_private; unsigned long flags;
+ dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags);
if (!WARN_ON(irq->registered)) { @@ -53,21 +54,15 @@ void __omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) }
spin_unlock_irqrestore(&list_lock, flags); -} - -void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) -{ - dispc_runtime_get(); - - __omap_irq_register(dev, irq); - dispc_runtime_put(); }
-void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) +static void omap_irq_unregister(struct drm_device *dev, + struct omap_drm_irq *irq) { unsigned long flags;
+ dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags);
if (!WARN_ON(!irq->registered)) { @@ -77,14 +72,6 @@ void __omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) }
spin_unlock_irqrestore(&list_lock, flags); -} - -void omap_irq_unregister(struct drm_device *dev, struct omap_drm_irq *irq) -{ - dispc_runtime_get(); - - __omap_irq_unregister(dev, irq); - dispc_runtime_put(); }
The only omap_drm_irq handler doesn't use the irqstatus parameter passed to the function. Remove it.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- Changes since v1:
- New patch --- drivers/gpu/drm/omapdrm/omap_drv.h | 2 +- drivers/gpu/drm/omapdrm/omap_irq.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index e15f6b384f65..5200fde1f38e 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -59,7 +59,7 @@ struct omap_drm_irq { struct list_head node; uint32_t irqmask; bool registered; - void (*irq)(struct omap_drm_irq *irq, uint32_t irqstatus); + void (*irq)(struct omap_drm_irq *irq); };
/* For KMS code that needs to wait for a certain # of IRQs: diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index c14530922f42..058ffdc0244d 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -82,7 +82,7 @@ struct omap_irq_wait {
static DECLARE_WAIT_QUEUE_HEAD(wait_event);
-static void wait_irq(struct omap_drm_irq *irq, uint32_t irqstatus) +static void wait_irq(struct omap_drm_irq *irq) { struct omap_irq_wait *wait = container_of(irq, struct omap_irq_wait, irq); @@ -248,7 +248,7 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) list_for_each_entry_safe(handler, n, &priv->irq_list, node) { if (handler->irqmask & irqstatus) { spin_unlock_irqrestore(&list_lock, flags); - handler->irq(handler, handler->irqmask & irqstatus); + handler->irq(handler); spin_lock_irqsave(&list_lock, flags); } }
The IRQ wait functions are called from the DSS enable and disable operations only, where the DISPC is guaranteed to be enabled. There's no need for manual DISPC power management there.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_irq.c | 4 ---- 1 file changed, 4 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 058ffdc0244d..23e1ef5c42bf 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -44,7 +44,6 @@ static void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) struct omap_drm_private *priv = dev->dev_private; unsigned long flags;
- dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags);
if (!WARN_ON(irq->registered)) { @@ -54,7 +53,6 @@ static void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) }
spin_unlock_irqrestore(&list_lock, flags); - dispc_runtime_put(); }
static void omap_irq_unregister(struct drm_device *dev, @@ -62,7 +60,6 @@ static void omap_irq_unregister(struct drm_device *dev, { unsigned long flags;
- dispc_runtime_get(); spin_lock_irqsave(&list_lock, flags);
if (!WARN_ON(!irq->registered)) { @@ -72,7 +69,6 @@ static void omap_irq_unregister(struct drm_device *dev, }
spin_unlock_irqrestore(&list_lock, flags); - dispc_runtime_put(); }
struct omap_irq_wait {
The function is only used in omap_irq.c, move it there and make it static.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_crtc.c | 7 ------- drivers/gpu/drm/omapdrm/omap_drv.h | 1 - drivers/gpu/drm/omapdrm/omap_irq.c | 7 ++++++- 3 files changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c index a2587fd403a0..0d97dfd6108f 100644 --- a/drivers/gpu/drm/omapdrm/omap_crtc.c +++ b/drivers/gpu/drm/omapdrm/omap_crtc.c @@ -47,13 +47,6 @@ struct omap_crtc { * Helper Functions */
-uint32_t pipe2vbl(struct drm_crtc *crtc) -{ - struct omap_crtc *omap_crtc = to_omap_crtc(crtc); - - return dispc_mgr_get_vsync_irq(omap_crtc->channel); -} - struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc) { struct omap_crtc *omap_crtc = to_omap_crtc(crtc); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 5200fde1f38e..447cf0e013d8 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -235,7 +235,6 @@ struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, struct dma_buf *buffer);
/* map crtc to vblank mask */ -uint32_t pipe2vbl(struct drm_crtc *crtc); struct omap_dss_device *omap_encoder_get_dssdev(struct drm_encoder *encoder);
#endif /* __OMAP_DRV_H__ */ diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 23e1ef5c42bf..dbcf9af4c7c8 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -108,6 +108,11 @@ int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, return 0; }
+static uint32_t pipe2vbl(struct drm_crtc *crtc) +{ + return dispc_mgr_get_vsync_irq(omap_crtc_channel(crtc)); +} + /** * enable_vblank - enable vblank interrupt events * @dev: DRM device @@ -228,7 +233,7 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) struct drm_crtc *crtc = priv->crtcs[id]; enum omap_channel channel = omap_crtc_channel(crtc);
- if (irqstatus & pipe2vbl(crtc)) { + if (irqstatus & dispc_mgr_get_vsync_irq(channel)) { drm_handle_vblank(dev, id); omap_crtc_vblank_irq(crtc); }
Now that the IRQ list is used for IRQ wait only we can merge omap_drm_irq and omap_irq_wait and simplify the implementation.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_drv.h | 17 +------ drivers/gpu/drm/omapdrm/omap_irq.c | 94 ++++++++++++++------------------------ 2 files changed, 37 insertions(+), 74 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 447cf0e013d8..c250ffe270cc 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -49,19 +49,6 @@ struct omap_drm_window { uint32_t src_w, src_h; };
-/* For transiently registering for different DSS irqs that various parts - * of the KMS code need during setup/configuration. We these are not - * necessarily the same as what drm_vblank_get/put() are requesting, and - * the hysteresis in drm_vblank_put() is not necessarily desirable for - * internal housekeeping related irq usage. - */ -struct omap_drm_irq { - struct list_head node; - uint32_t irqmask; - bool registered; - void (*irq)(struct omap_drm_irq *irq); -}; - /* For KMS code that needs to wait for a certain # of IRQs: */ struct omap_irq_wait; @@ -102,8 +89,8 @@ struct omap_drm_private { struct drm_property *zorder_prop;
/* irq handling: */ - struct list_head irq_list; /* list of omap_drm_irq */ - uint32_t irq_mask; /* enabled irqs in addition to irq_list */ + struct list_head wait_list; /* list of omap_irq_wait */ + uint32_t irq_mask; /* enabled irqs in addition to wait_list */
/* atomic commit */ struct { diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index dbcf9af4c7c8..947c58d956c6 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -21,17 +21,23 @@
static DEFINE_SPINLOCK(list_lock);
+struct omap_irq_wait { + struct list_head node; + uint32_t irqmask; + int count; +}; + /* call with list_lock and dispc runtime held */ static void omap_irq_update(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; - struct omap_drm_irq *irq; + struct omap_irq_wait *wait; uint32_t irqmask = priv->irq_mask;
assert_spin_locked(&list_lock);
- list_for_each_entry(irq, &priv->irq_list, node) - irqmask |= irq->irqmask; + list_for_each_entry(wait, &priv->wait_list, node) + irqmask |= wait->irqmask;
DBG("irqmask=%08x", irqmask);
@@ -39,61 +45,29 @@ static void omap_irq_update(struct drm_device *dev) dispc_read_irqenable(); /* flush posted write */ }
-static void omap_irq_register(struct drm_device *dev, struct omap_drm_irq *irq) -{ - struct omap_drm_private *priv = dev->dev_private; - unsigned long flags; - - spin_lock_irqsave(&list_lock, flags); - - if (!WARN_ON(irq->registered)) { - irq->registered = true; - list_add(&irq->node, &priv->irq_list); - omap_irq_update(dev); - } - - spin_unlock_irqrestore(&list_lock, flags); -} - -static void omap_irq_unregister(struct drm_device *dev, - struct omap_drm_irq *irq) -{ - unsigned long flags; - - spin_lock_irqsave(&list_lock, flags); - - if (!WARN_ON(!irq->registered)) { - irq->registered = false; - list_del(&irq->node); - omap_irq_update(dev); - } - - spin_unlock_irqrestore(&list_lock, flags); -} - -struct omap_irq_wait { - struct omap_drm_irq irq; - int count; -}; - static DECLARE_WAIT_QUEUE_HEAD(wait_event);
-static void wait_irq(struct omap_drm_irq *irq) +static void omap_irq_wait_irq(struct omap_irq_wait *wait) { - struct omap_irq_wait *wait = - container_of(irq, struct omap_irq_wait, irq); wait->count--; - wake_up_all(&wait_event); + wake_up(&wait_event); }
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, uint32_t irqmask, int count) { + struct omap_drm_private *priv = dev->dev_private; struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); - wait->irq.irq = wait_irq; - wait->irq.irqmask = irqmask; + unsigned long flags; + + wait->irqmask = irqmask; wait->count = count; - omap_irq_register(dev, &wait->irq); + + spin_lock_irqsave(&list_lock, flags); + list_add(&wait->node, &priv->wait_list); + omap_irq_update(dev); + spin_unlock_irqrestore(&list_lock, flags); + return wait; }
@@ -101,11 +75,16 @@ int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, unsigned long timeout) { int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout); - omap_irq_unregister(dev, &wait->irq); + unsigned long flags; + + spin_lock_irqsave(&list_lock, flags); + list_del(&wait->node); + omap_irq_update(dev); + spin_unlock_irqrestore(&list_lock, flags); + kfree(wait); - if (ret == 0) - return -1; - return 0; + + return ret == 0 ? -1 : 0; }
static uint32_t pipe2vbl(struct drm_crtc *crtc) @@ -218,7 +197,7 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) { struct drm_device *dev = (struct drm_device *) arg; struct omap_drm_private *priv = dev->dev_private; - struct omap_drm_irq *handler, *n; + struct omap_irq_wait *wait, *n; unsigned long flags; unsigned int id; u32 irqstatus; @@ -246,12 +225,9 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) omap_irq_fifo_underflow(priv, irqstatus);
spin_lock_irqsave(&list_lock, flags); - list_for_each_entry_safe(handler, n, &priv->irq_list, node) { - if (handler->irqmask & irqstatus) { - spin_unlock_irqrestore(&list_lock, flags); - handler->irq(handler); - spin_lock_irqsave(&list_lock, flags); - } + list_for_each_entry_safe(wait, n, &priv->wait_list, node) { + if (wait->irqmask & irqstatus) + omap_irq_wait_irq(wait); } spin_unlock_irqrestore(&list_lock, flags);
@@ -280,7 +256,7 @@ int omap_drm_irq_install(struct drm_device *dev) unsigned int i; int ret;
- INIT_LIST_HEAD(&priv->irq_list); + INIT_LIST_HEAD(&priv->wait_list);
priv->irq_mask = DISPC_IRQ_OCP_ERR;
Move the list of pending IRQ wait instances to the omap_drm_private structure and the wait queue head to the IRQ wait structure.
Signed-off-by: Laurent Pinchart laurent.pinchart@ideasonboard.com --- drivers/gpu/drm/omapdrm/omap_drv.h | 3 ++- drivers/gpu/drm/omapdrm/omap_irq.c | 42 ++++++++++++++++++++------------------ 2 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index c250ffe270cc..a842b42b6053 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -89,7 +89,8 @@ struct omap_drm_private { struct drm_property *zorder_prop;
/* irq handling: */ - struct list_head wait_list; /* list of omap_irq_wait */ + spinlock_t wait_lock; /* protects the wait_list */ + struct list_head wait_list; /* list of omap_irq_wait */ uint32_t irq_mask; /* enabled irqs in addition to wait_list */
/* atomic commit */ diff --git a/drivers/gpu/drm/omapdrm/omap_irq.c b/drivers/gpu/drm/omapdrm/omap_irq.c index 947c58d956c6..53f951d88bbe 100644 --- a/drivers/gpu/drm/omapdrm/omap_irq.c +++ b/drivers/gpu/drm/omapdrm/omap_irq.c @@ -19,22 +19,21 @@
#include "omap_drv.h"
-static DEFINE_SPINLOCK(list_lock); - struct omap_irq_wait { struct list_head node; + wait_queue_head_t wq; uint32_t irqmask; int count; };
-/* call with list_lock and dispc runtime held */ +/* call with wait_lock and dispc runtime held */ static void omap_irq_update(struct drm_device *dev) { struct omap_drm_private *priv = dev->dev_private; struct omap_irq_wait *wait; uint32_t irqmask = priv->irq_mask;
- assert_spin_locked(&list_lock); + assert_spin_locked(&priv->wait_lock);
list_for_each_entry(wait, &priv->wait_list, node) irqmask |= wait->irqmask; @@ -45,12 +44,10 @@ static void omap_irq_update(struct drm_device *dev) dispc_read_irqenable(); /* flush posted write */ }
-static DECLARE_WAIT_QUEUE_HEAD(wait_event); - static void omap_irq_wait_irq(struct omap_irq_wait *wait) { wait->count--; - wake_up(&wait_event); + wake_up(&wait->wq); }
struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, @@ -60,13 +57,14 @@ struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, struct omap_irq_wait *wait = kzalloc(sizeof(*wait), GFP_KERNEL); unsigned long flags;
+ init_waitqueue_head(&wait->wq); wait->irqmask = irqmask; wait->count = count;
- spin_lock_irqsave(&list_lock, flags); + spin_lock_irqsave(&priv->wait_lock, flags); list_add(&wait->node, &priv->wait_list); omap_irq_update(dev); - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags);
return wait; } @@ -74,13 +72,16 @@ struct omap_irq_wait * omap_irq_wait_init(struct drm_device *dev, int omap_irq_wait(struct drm_device *dev, struct omap_irq_wait *wait, unsigned long timeout) { - int ret = wait_event_timeout(wait_event, (wait->count <= 0), timeout); + struct omap_drm_private *priv = dev->dev_private; unsigned long flags; + int ret; + + ret = wait_event_timeout(wait->wq, (wait->count <= 0), timeout);
- spin_lock_irqsave(&list_lock, flags); + spin_lock_irqsave(&priv->wait_lock, flags); list_del(&wait->node); omap_irq_update(dev); - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags);
kfree(wait);
@@ -113,10 +114,10 @@ int omap_irq_enable_vblank(struct drm_device *dev, unsigned int pipe)
DBG("dev=%p, crtc=%u", dev, pipe);
- spin_lock_irqsave(&list_lock, flags); + spin_lock_irqsave(&priv->wait_lock, flags); priv->irq_mask |= pipe2vbl(crtc); omap_irq_update(dev); - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags);
return 0; } @@ -138,10 +139,10 @@ void omap_irq_disable_vblank(struct drm_device *dev, unsigned int pipe)
DBG("dev=%p, crtc=%u", dev, pipe);
- spin_lock_irqsave(&list_lock, flags); + spin_lock_irqsave(&priv->wait_lock, flags); priv->irq_mask &= ~pipe2vbl(crtc); omap_irq_update(dev); - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags); }
static void omap_irq_fifo_underflow(struct omap_drm_private *priv, @@ -165,9 +166,9 @@ static void omap_irq_fifo_underflow(struct omap_drm_private *priv, | DISPC_IRQ_VID3_FIFO_UNDERFLOW; unsigned int i;
- spin_lock(&list_lock); + spin_lock(&priv->wait_lock); irqstatus &= priv->irq_mask & mask; - spin_unlock(&list_lock); + spin_unlock(&priv->wait_lock);
if (!irqstatus) return; @@ -224,12 +225,12 @@ static irqreturn_t omap_irq_handler(int irq, void *arg) omap_irq_ocp_error_handler(irqstatus); omap_irq_fifo_underflow(priv, irqstatus);
- spin_lock_irqsave(&list_lock, flags); + spin_lock_irqsave(&priv->wait_lock, flags); list_for_each_entry_safe(wait, n, &priv->wait_list, node) { if (wait->irqmask & irqstatus) omap_irq_wait_irq(wait); } - spin_unlock_irqrestore(&list_lock, flags); + spin_unlock_irqrestore(&priv->wait_lock, flags);
return IRQ_HANDLED; } @@ -256,6 +257,7 @@ int omap_drm_irq_install(struct drm_device *dev) unsigned int i; int ret;
+ spin_lock_init(&priv->wait_lock); INIT_LIST_HEAD(&priv->wait_list);
priv->irq_mask = DISPC_IRQ_OCP_ERR;
dri-devel@lists.freedesktop.org