Hi Benoit, On 29/06/18 20:02, Benoit Parrot wrote: > In the case where we need to support wide-display using more than one > overlay to achieve the needed display resolution, we need to be able to > dynamically assign overlays to planes instead of having the overlays > being statically mapped to planes. > > This also means that on occasion where the number of requested planes > exceeds the numbers of overlays required to display them then a failure > would be returned for the plane that cannot be handled at that time. It > is up to user space to make sure the H/W resource are not > over-subscribed. This is not a trivial patch, I think it needs much more explanation in the desc on how the patch solves this issue and what it changes. I have some small comments below here and there, but overall, I feel it's rather difficult to review, and I really think this should be split into multiple smaller patches. Some ideas on the patches it might be split to: - add plane state - add global state - add code which manages plane-id -> hw plane id - add dual plane support > Signed-off-by: Benoit Parrot <bparrot@xxxxxx> > --- > drivers/gpu/drm/omapdrm/Makefile | 1 + > drivers/gpu/drm/omapdrm/omap_drv.c | 119 ++++++++++- > drivers/gpu/drm/omapdrm/omap_drv.h | 15 +- > drivers/gpu/drm/omapdrm/omap_fb.c | 33 ++- > drivers/gpu/drm/omapdrm/omap_fb.h | 4 +- > drivers/gpu/drm/omapdrm/omap_overlay.c | 366 +++++++++++++++++++++++++++++++++ > drivers/gpu/drm/omapdrm/omap_overlay.h | 80 +++++++ > drivers/gpu/drm/omapdrm/omap_plane.c | 322 ++++++++++++++++++++++++----- > drivers/gpu/drm/omapdrm/omap_plane.h | 20 ++ > 9 files changed, 892 insertions(+), 68 deletions(-) > create mode 100644 drivers/gpu/drm/omapdrm/omap_overlay.c > create mode 100644 drivers/gpu/drm/omapdrm/omap_overlay.h > > diff --git a/drivers/gpu/drm/omapdrm/Makefile b/drivers/gpu/drm/omapdrm/Makefile > index f115253115c5..800dfd035360 100644 > --- a/drivers/gpu/drm/omapdrm/Makefile > +++ b/drivers/gpu/drm/omapdrm/Makefile > @@ -12,6 +12,7 @@ omapdrm-y := omap_drv.o \ > omap_debugfs.o \ > omap_crtc.o \ > omap_plane.o \ > + omap_overlay.o \ > omap_encoder.o \ > omap_connector.o \ > omap_fb.o \ > diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c > index ef3b0e3571ec..f0a5c3dab471 100644 > --- a/drivers/gpu/drm/omapdrm/omap_drv.c > +++ b/drivers/gpu/drm/omapdrm/omap_drv.c > @@ -16,11 +16,7 @@ > */ > > #include <linux/sys_soc.h> > - > -#include <drm/drm_atomic.h> > -#include <drm/drm_atomic_helper.h> > -#include <drm/drm_crtc_helper.h> > -#include <drm/drm_fb_helper.h> > +#include <linux/sort.h> > > #include "omap_dmm_tiler.h" > #include "omap_drv.h" > @@ -113,9 +109,100 @@ static void omap_atomic_commit_tail(struct drm_atomic_state *old_state) > > drm_atomic_helper_cleanup_planes(dev, old_state); > > + omap_overlay_disable_unassigned(old_state); > + > priv->dispc_ops->runtime_put(priv->dispc); > } > > +static int drm_atomic_state_normalized_zpos_cmp(const void *a, const void *b) > +{ > + const struct drm_plane_state *sa = *(struct drm_plane_state **)a; > + const struct drm_plane_state *sb = *(struct drm_plane_state **)b; > + > + if (sa->normalized_zpos != sb->normalized_zpos) > + return sa->normalized_zpos - sb->normalized_zpos; > + else > + return sa->plane->base.id - sb->plane->base.id; > +} > + > +static int omap_atomic_update_normalize_zpos(struct drm_device *dev, > + struct drm_atomic_state *state) > +{ > + struct drm_crtc *crtc; > + struct drm_crtc_state *old_state, *new_state; > + struct drm_plane *plane; > + int i, n, inc; > + int total_planes = dev->mode_config.num_total_plane; > + struct drm_plane_state **states; > + int ret = 0; > + > + states = kmalloc_array(total_planes, sizeof(*states), GFP_KERNEL); > + if (!states) > + return -ENOMEM; > + > + for_each_oldnew_crtc_in_state(state, crtc, old_state, new_state, i) { > + if (old_state->plane_mask == new_state->plane_mask && > + !new_state->zpos_changed) > + continue; > + > + /* Reset plane increment and index value for every crtc */ > + n = 0; > + > + /* > + * Normalization process might create new states for planes > + * which normalized_zpos has to be recalculated. > + */ > + drm_for_each_plane_mask(plane, dev, new_state->plane_mask) { > + struct drm_plane_state *plane_state = > + drm_atomic_get_plane_state(new_state->state, > + plane); > + if (IS_ERR(plane_state)) { > + ret = PTR_ERR(plane_state); > + goto done; > + } > + states[n++] = plane_state; > + } > + > + sort(states, n, sizeof(*states), > + drm_atomic_state_normalized_zpos_cmp, NULL); > + > + for (i = 0, inc = 0; i < n; i++) { > + plane = states[i]->plane; > + > + states[i]->normalized_zpos = i + inc; > + DRM_DEBUG_ATOMIC("[PLANE:%d:%s] updated normalized zpos value %d\n", > + plane->base.id, plane->name, > + states[i]->normalized_zpos); > + > + if (is_omap_plane_dual_overlay(states[i])) > + inc++; > + } > + new_state->zpos_changed = true; > + } > + > +done: > + kfree(states); > + return ret; > +} > + > +static int omap_atomic_check(struct drm_device *dev, > + struct drm_atomic_state *state) > +{ > + int ret; > + > + ret = drm_atomic_helper_check(dev, state); > + if (ret) > + return ret; > + > + if (dev->mode_config.normalize_zpos) { > + ret = omap_atomic_update_normalize_zpos(dev, state); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = { > .atomic_commit_tail = omap_atomic_commit_tail, > }; > @@ -123,7 +210,7 @@ static const struct drm_mode_config_helper_funcs omap_mode_config_helper_funcs = > static const struct drm_mode_config_funcs omap_mode_config_funcs = { > .fb_create = omap_framebuffer_create, > .output_poll_changed = drm_fb_helper_output_poll_changed, > - .atomic_check = drm_atomic_helper_check, > + .atomic_check = omap_atomic_check, > .atomic_commit = drm_atomic_helper_commit, > }; > > @@ -296,7 +383,7 @@ static int omap_modeset_init(struct drm_device *dev) > return -EINVAL; > > plane = omap_plane_init(dev, plane_idx, DRM_PLANE_TYPE_OVERLAY, > - plane_crtc_mask); > + plane_crtc_mask); > if (IS_ERR(plane)) > return PTR_ERR(plane); > > @@ -560,10 +647,18 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) > > omap_gem_init(ddev); > > + ret = omap_global_obj_init(priv); > + if (ret) > + goto err_free_drm_dev; > + > + ret = omap_hwoverlays_init(priv); > + if (ret) > + goto err_free_priv_obj; > + > ret = omap_modeset_init(ddev); > if (ret) { > dev_err(priv->dev, "omap_modeset_init failed: ret=%d\n", ret); > - goto err_free_drm_dev; > + goto err_free_overlays; > } > > /* Initialize vblank handling, start with all CRTCs disabled. */ > @@ -577,7 +672,6 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) > drm_crtc_vblank_off(priv->crtcs[i]); > > omap_fbdev_init(ddev); > - As this is a big patch already, try not to make any extra cleanups that are not needed. > drm_kms_helper_poll_init(ddev); > omap_modeset_enable_external_hpd(); > > @@ -599,6 +693,10 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) > err_cleanup_modeset: > drm_mode_config_cleanup(ddev); > omap_drm_irq_uninstall(ddev); > +err_free_overlays: > + omap_hwoverlays_destroy(priv); > +err_free_priv_obj: > + omap_global_obj_fini(priv); > err_free_drm_dev: > omap_gem_deinit(ddev); > drm_dev_unref(ddev); > @@ -632,6 +730,9 @@ static void omapdrm_cleanup(struct omap_drm_private *priv) > > drm_dev_unref(ddev); > > + omap_hwoverlays_destroy(priv); > + omap_global_obj_fini(priv); > + > destroy_workqueue(priv->wq); > > omap_disconnect_dssdevs(); > diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h > index 6eaee4df4559..eb71ea8decee 100644 > --- a/drivers/gpu/drm/omapdrm/omap_drv.h > +++ b/drivers/gpu/drm/omapdrm/omap_drv.h > @@ -23,7 +23,10 @@ > #include <linux/workqueue.h> > > #include <drm/drmP.h> > +#include <drm/drm_atomic.h> > +#include <drm/drm_atomic_helper.h> > #include <drm/drm_crtc_helper.h> > +#include <drm/drm_fb_helper.h> > #include <drm/drm_gem.h> > #include <drm/omap_drm.h> > > @@ -37,6 +40,7 @@ > #include "omap_gem.h" > #include "omap_irq.h" > #include "omap_plane.h" > +#include "omap_overlay.h" > > #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) > #define VERB(fmt, ...) if (0) DRM_DEBUG(fmt, ##__VA_ARGS__) /* verbose debug */ > @@ -66,6 +70,16 @@ struct omap_drm_private { > unsigned int num_connectors; > struct drm_connector *connectors[8]; > > + unsigned int num_ovls; > + struct omap_hw_overlay *overlays[8]; > + > + /* > + * Global private object state, Do not access directly, use > + * omap_global_get_state() > + */ > + struct drm_modeset_lock glob_state_lock; > + struct drm_private_obj glob_state; > + You're using space indentation here. > struct drm_fb_helper *fbdev; > > struct workqueue_struct *wq; > @@ -91,7 +105,6 @@ struct omap_drm_private { > unsigned int max_bandwidth; > }; > > - > int omap_debugfs_init(struct drm_minor *minor); > > #endif /* __OMAPDRM_DRV_H__ */ > diff --git a/drivers/gpu/drm/omapdrm/omap_fb.c b/drivers/gpu/drm/omapdrm/omap_fb.c > index 5fd22ca73913..b9573c163117 100644 > --- a/drivers/gpu/drm/omapdrm/omap_fb.c > +++ b/drivers/gpu/drm/omapdrm/omap_fb.c > @@ -153,7 +153,9 @@ static u32 drm_rotation_to_tiler(unsigned int drm_rot) > /* update ovl info for scanout, handles cases of multi-planar fb's, etc. > */ > void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, > - struct drm_plane_state *state, struct omap_overlay_info *info) > + struct drm_plane_state *state, > + struct omap_overlay_info *info, > + struct omap_overlay_info *r_info) > { > struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb); > const struct drm_format_info *format = omap_fb->format; > @@ -206,7 +208,8 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, > info->rotation_type = OMAP_DSS_ROT_TILER; > info->rotation = state->rotation ?: DRM_MODE_ROTATE_0; > /* Note: stride in TILER units, not pixels */ > - info->screen_width = omap_gem_tiled_stride(plane->bo, orient); > + info->screen_width = > + omap_gem_tiled_stride(plane->bo, orient); > } else { > switch (state->rotation & DRM_MODE_ROTATE_MASK) { > case 0: > @@ -221,10 +224,10 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, > break; > } > > - info->paddr = get_linear_addr(plane, format, 0, x, y); > + info->paddr = get_linear_addr(plane, format, 0, x, y); > info->rotation_type = OMAP_DSS_ROT_NONE; > - info->rotation = DRM_MODE_ROTATE_0; > - info->screen_width = plane->pitch; > + info->rotation = DRM_MODE_ROTATE_0; > + info->screen_width = plane->pitch; These are also extra changes. There are other similar ones. > } > > /* convert to pixels: */ > @@ -238,11 +241,29 @@ void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, > omap_gem_rotated_dma_addr(plane->bo, orient, x/2, y/2, > &info->p_uv_addr); > } else { > - info->p_uv_addr = get_linear_addr(plane, format, 1, x, y); > + info->p_uv_addr = > + get_linear_addr(plane, format, 1, x, y); > } > } else { > info->p_uv_addr = 0; > } > + > + if (r_info) { > + info->width /= 2; > + info->out_width /= 2; > + > + *r_info = *info; > + > + r_info->pos_x = info->pos_x + info->out_width; > + > + r_info->paddr = get_linear_addr(&omap_fb->planes[0], format, 0, > + x + info->width, y); > + if (fb->format->format == DRM_FORMAT_NV12) { > + r_info->p_uv_addr = > + get_linear_addr(&omap_fb->planes[1], format, 1, > + x + info->width, y); > + } > + } > } > > /* pin, prepare for scanout: */ > diff --git a/drivers/gpu/drm/omapdrm/omap_fb.h b/drivers/gpu/drm/omapdrm/omap_fb.h > index 94ad5f9e4404..8c116c1aac0d 100644 > --- a/drivers/gpu/drm/omapdrm/omap_fb.h > +++ b/drivers/gpu/drm/omapdrm/omap_fb.h > @@ -37,7 +37,9 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev, > int omap_framebuffer_pin(struct drm_framebuffer *fb); > void omap_framebuffer_unpin(struct drm_framebuffer *fb); > void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, > - struct drm_plane_state *state, struct omap_overlay_info *info); > + struct drm_plane_state *state, > + struct omap_overlay_info *info, > + struct omap_overlay_info *r_info); > struct drm_connector *omap_framebuffer_get_next_connector( > struct drm_framebuffer *fb, struct drm_connector *from); > bool omap_framebuffer_supports_rotation(struct drm_framebuffer *fb); > diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.c b/drivers/gpu/drm/omapdrm/omap_overlay.c > new file mode 100644 > index 000000000000..0a1327c31b69 > --- /dev/null > +++ b/drivers/gpu/drm/omapdrm/omap_overlay.c > @@ -0,0 +1,366 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ > + * Author: Benoit Parrot, <bparrot@xxxxxx> > + */ > + > +#include <drm/drm_atomic.h> > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_plane_helper.h> > + > +#include "omap_dmm_tiler.h" > +#include "omap_drv.h" > + > +/* > + * overlay funcs > + */ > +static void __maybe_unused > +omap_overlay_atomic_print_state(struct drm_printer *p, > + const struct omap_global_state *state, > + struct omap_drm_private *priv) > +{ > + int i; > + > + drm_printf(p, "\tomap_global_state=%p\n", state); > + if (state) { > + for (i = 0; i < priv->num_ovls; i++) { > + struct drm_plane *plane = > + state->overlay.hwoverlay_to_plane[i]; > + > + drm_printf(p, "\t\t[%d] plane=%p\n", i, plane); > + if (plane) > + drm_printf(p, "\t\t\t plane=%s\n", plane->name); > + } > + } > +} > + > +/* Global/shared object state funcs */ > + > +/* > + * This is a helper that returns the private state currently in operation. > + * Note that this would return the "old_state" if called in the atomic check > + * path, and the "new_state" after the atomic swap has been done. > + */ > +static struct omap_global_state * > +omap_get_existing_global_state(struct omap_drm_private *priv) > +{ > + return to_omap_global_state(priv->glob_state.state); > +} > + > +/* > + * This acquires the modeset lock set aside for global state, creates > + * a new duplicated private object state. > + */ > +static struct omap_global_state *__must_check > +omap_get_global_state(struct drm_atomic_state *s) > +{ > + struct omap_drm_private *priv = s->dev->dev_private; > + struct drm_private_state *priv_state; > + int ret; > + > + while (1) { > + ret = drm_modeset_lock(&priv->glob_state_lock, s->acquire_ctx); > + if (ret != -EDEADLK) > + break; > + > + drm_modeset_backoff(s->acquire_ctx); > + } > + > + if (ret) > + return ERR_PTR(ret); > + > + priv_state = drm_atomic_get_private_obj_state(s, &priv->glob_state); > + if (IS_ERR(priv_state)) > + return ERR_CAST(priv_state); > + > + return to_omap_global_state(priv_state); > +} > + > +static struct drm_private_state * > +omap_global_duplicate_state(struct drm_private_obj *obj) > +{ > + struct omap_global_state *state; > + > + state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); > + if (!state) > + return NULL; > + > + __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); > + > + return &state->base; > +} > + > +static void omap_global_destroy_state(struct drm_private_obj *obj, > + struct drm_private_state *state) > +{ > + struct omap_global_state *omap_state = to_omap_global_state(state); > + > + kfree(omap_state); > +} > + > +static const struct drm_private_state_funcs omap_global_state_funcs = { > + .atomic_duplicate_state = omap_global_duplicate_state, > + .atomic_destroy_state = omap_global_destroy_state, > +}; > + > +int omap_global_obj_init(struct omap_drm_private *priv) > +{ > + struct omap_global_state *state; > + > + drm_modeset_lock_init(&priv->glob_state_lock); > + > + state = kzalloc(sizeof(*state), GFP_KERNEL); > + if (!state) > + return -ENOMEM; > + > + drm_atomic_private_obj_init(&priv->glob_state, > + &state->base, > + &omap_global_state_funcs); > + return 0; > +} > + > +void omap_global_obj_fini(struct omap_drm_private *priv) > +{ > + drm_atomic_private_obj_fini(&priv->glob_state); > + drm_modeset_lock_fini(&priv->glob_state_lock); > +} > + > +static struct omap_hw_overlay * > +omap_plane_find_free_overlay(struct drm_device *dev, > + struct omap_hw_overlay_state *new_state, > + u32 caps, u32 fourcc, u32 crtc_mask) > +{ > + struct omap_drm_private *priv = dev->dev_private; > + const struct dispc_ops *ops = priv->dispc_ops; > + int i; > + > + DBG("caps: %x fourcc: %x crtc: %x\n", caps, fourcc, crtc_mask); > + > + for (i = 0; i < priv->num_ovls; i++) { > + struct omap_hw_overlay *cur = priv->overlays[i]; > + > + DBG("%d: id: %d cur->caps: %x cur->crtc: %x\n", > + cur->idx, cur->overlay_id, cur->caps, cur->possible_crtcs); > + > + /* skip if already in-use */ > + if (new_state->hwoverlay_to_plane[cur->idx]) > + continue; > + > + /* check if allowed on crtc */ > + if (!(cur->possible_crtcs & crtc_mask)) > + continue; > + > + /* skip if doesn't support some required caps: */ > + if (caps & ~cur->caps) > + continue; > + > + /* check supported format */ > + if (!ops->ovl_color_mode_supported(priv->dispc, > + cur->overlay_id, > + fourcc)) > + continue; > + > + return cur; > + } > + > + DBG("no match\n"); > + return NULL; > +} > + > +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, > + u32 caps, u32 fourcc, u32 crtc_mask, > + struct omap_hw_overlay **overlay, > + struct omap_hw_overlay **r_overlay) > +{ > + struct omap_drm_private *priv = s->dev->dev_private; > + struct omap_global_state *new_global_state, *old_global_state; > + struct omap_hw_overlay_state *old_state, *new_state; > + struct omap_hw_overlay *ovl, *r_ovl; > + > + new_global_state = omap_get_global_state(s); > + if (IS_ERR(new_global_state)) > + return PTR_ERR(new_global_state); > + > + /* > + * grab old_state after omap_get_global_state(), > + * since now we hold lock: > + */ > + old_global_state = omap_get_existing_global_state(priv); > + DBG("new_global_state: %p old_global_state: %p should be different (%d)", > + new_global_state, old_global_state, new_global_state != old_global_state); > + > + old_state = &old_global_state->overlay; > + new_state = &new_global_state->overlay; > + > + if (!*overlay) { > + ovl = omap_plane_find_free_overlay(s->dev, new_state, > + caps, fourcc, crtc_mask); > + if (!ovl) > + return -ENOMEM; > + > + new_state->hwoverlay_to_plane[ovl->idx] = plane; > + *overlay = ovl; > + > + if (r_overlay) { > + r_ovl = omap_plane_find_free_overlay(s->dev, new_state, > + caps, fourcc, > + crtc_mask); > + if (!r_ovl) { > + new_state->hwoverlay_to_plane[ovl->idx] = NULL; > + *overlay = NULL; > + return -ENOMEM; > + } > + > + new_state->hwoverlay_to_plane[r_ovl->idx] = plane; > + *r_overlay = r_ovl; > + } > + > + > + DBG("%s: assign to plane %s for caps %x", > + (*overlay)->name, plane->name, caps); > + > + if (r_overlay) { > + DBG("%s: assign to right of plane %s for caps %x", > + (*r_overlay)->name, plane->name, caps); > + } > + } > + > + return 0; > +} > + > +void omap_overlay_release(struct drm_atomic_state *s, > + struct omap_hw_overlay *overlay) > +{ > + struct omap_global_state *state = omap_get_global_state(s); > + struct omap_hw_overlay_state *new_state = &state->overlay; > + > + if (!overlay) > + return; > + > + if (WARN_ON(!new_state->hwoverlay_to_plane[overlay->idx])) > + return; > + > + DBG("%s: release from plane %s", overlay->name, > + new_state->hwoverlay_to_plane[overlay->idx]->name); > + > + new_state->hwoverlay_to_plane[overlay->idx] = NULL; > +} > + > +/* > + * This is called only from omap_atomic_commit_tail() > + * as a cleanup step to make sure hw overlay which are no longer > + * are disabled. > + * > + * I was originally taking the glob_state_lock here by calling > + * omap_get_global_state(s) but doing so here was causing all kind > + * lock related warnings, for instance: > + * WARNING: CPU: 0 PID: 68 at drivers/gpu/drm/drm_modeset_lock.c:241 > + * and > + * WARNING: CPU: 0 PID: 68 at drivers/gpu/drm/drm_modeset_lock.c:244 > + * As well as also generating these: > + * ================================== > + * WARNING: Nested lock was not taken > + * 4.18.0-rc2-00055-g5d51e5159b0a #24 Tainted: G W > + * ---------------------------------- > + * kworker/u2:3/66 is trying to lock: > + * 51abea2e (crtc_ww_class_mutex){+.+.}, at: drm_modeset_lock+0xd0/0x140 > + * > + * but this task is not holding: > + * �21 > + * > + * The only thing that worked so far was to stop trying to take that lock > + * in this particular case. It might the real solution but I would like > + * to sure. > + */ > +void omap_overlay_disable_unassigned(struct drm_atomic_state *s) > +{ > + struct omap_drm_private *priv = s->dev->dev_private; > + struct omap_hw_overlay_state *new_state; > + struct omap_global_state *old_state; > + int i; > + > + old_state = omap_get_existing_global_state(priv); > + new_state = &old_state->overlay; > + > + for (i = 0; i < priv->num_ovls; i++) { > + struct omap_hw_overlay *cur = priv->overlays[i]; > + > + if (!new_state->hwoverlay_to_plane[cur->idx]) { > + priv->dispc_ops->ovl_enable(priv->dispc, > + cur->overlay_id, > + false); > + > + /* > + * Since we are disabling this overlay in this > + * atomic cycle we can reset the avalaible crtcs > + * it can be used on > + */ > + cur->possible_crtcs = (1 << priv->num_crtcs) - 1; > + } > + } > +} > + > +void omap_overlay_destroy(struct omap_hw_overlay *overlay) > +{ > + kfree(overlay); > +} > + > +static struct omap_hw_overlay *omap_overlay_init(enum omap_plane_id overlay_id, > + enum omap_overlay_caps caps) > +{ > + struct omap_hw_overlay *overlay; > + > + overlay = kzalloc(sizeof(*overlay), GFP_KERNEL); > + if (!overlay) > + return ERR_PTR(-ENOMEM); > + > + overlay->name = overlay2name(overlay_id); > + overlay->overlay_id = overlay_id; > + overlay->caps = caps; > + /* > + * When this is called priv->num_crtcs is not known yet. > + * Use a safe mask value to start with, it will get updated to the > + * proper value after the first use. > + */ > + overlay->possible_crtcs = 0xff; > + > + return overlay; > +} > + > +int omap_hwoverlays_init(struct omap_drm_private *priv) > +{ > + static const enum omap_plane_id overlays[] = { > + OMAP_DSS_GFX, OMAP_DSS_VIDEO1, > + OMAP_DSS_VIDEO2, OMAP_DSS_VIDEO3, > + }; > + u32 num_overlays = priv->dispc_ops->get_num_ovls(priv->dispc); > + enum omap_overlay_caps caps; > + int i, ret; > + > + for (i = 0; i < num_overlays; i++) { > + struct omap_hw_overlay *overlay; > + > + caps = priv->dispc_ops->ovl_get_caps(priv->dispc, overlays[i]); > + overlay = omap_overlay_init(overlays[i], caps); > + if (IS_ERR(overlay)) { > + ret = PTR_ERR(overlay); > + dev_err(priv->dev, "failed to construct overlay for %s (%d)\n", > + overlay2name(i), ret); > + return ret; > + } > + overlay->idx = priv->num_ovls; > + priv->overlays[priv->num_ovls++] = overlay; > + } > + > + return 0; > +} > + > +void omap_hwoverlays_destroy(struct omap_drm_private *priv) > +{ > + int i; > + > + for (i = 0; i < priv->num_ovls; i++) { > + omap_overlay_destroy(priv->overlays[i]); > + priv->overlays[i] = NULL; > + } > +} > diff --git a/drivers/gpu/drm/omapdrm/omap_overlay.h b/drivers/gpu/drm/omapdrm/omap_overlay.h > new file mode 100644 > index 000000000000..ed94a260ba10 > --- /dev/null > +++ b/drivers/gpu/drm/omapdrm/omap_overlay.h > @@ -0,0 +1,80 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ > + * Author: Benoit Parrot, <bparrot@xxxxxx> > + */ > + > +#ifndef __OMAPDRM_OVERLAY_H__ > +#define __OMAPDRM_OVERLAY_H__ > + > +#include <linux/types.h> > + > +enum drm_plane_type; > + > +struct drm_device; > +struct drm_mode_object; > +struct drm_plane; > + > +/* Used to associate a HW overlay/plane to a plane */ > +struct omap_hw_overlay { > + int idx; > + > + const char *name; > + enum omap_plane_id overlay_id; > + > + enum omap_overlay_caps caps; > + /* > + * The CRTC(s) this overlay is currently allowed on. > + * When the overlay is unused and was not assigned to any crtc then > + * this will be the equal to the plane possible_crtcs otherwise it > + * will be the current crtc this overlay is displayed on. > + * When clearing the overlay to plane assignemnt while going through > + * an atomic_check sequence we need to remember which crtc the overlay > + * was on as we do not want to create flicker. We want to be able to > + * reassign the overlay to the same crtc it was previously on. > + */ > + u32 possible_crtcs; > + /* Reference to the associated drm_plane */ > + struct drm_plane *plane; > +}; > + > +/* global atomic state of assignment between pipes and planes: */ > +struct omap_hw_overlay_state { > + struct drm_plane *hwoverlay_to_plane[8]; > +}; > + > +/* Global private object state for tracking resources that are shared across > + * multiple kms objects (planes/crtcs/etc). > + */ This doesn't follow the kernel comment style. > +#define to_omap_global_state(x) container_of(x, struct omap_global_state, base) > +struct omap_global_state { > + struct drm_private_state base; > + > + struct drm_atomic_state *state; > + > + struct omap_hw_overlay_state overlay; > +}; > + > +static inline const char *overlay2name(enum omap_plane_id id) > +{ > + static const char *name[] = { > + [OMAP_DSS_GFX] = "gfx", > + [OMAP_DSS_VIDEO1] = "vid1", > + [OMAP_DSS_VIDEO2] = "vid2", > + [OMAP_DSS_VIDEO3] = "vid3", > + }; > + return name[id]; > +} > + > +int omap_hwoverlays_init(struct omap_drm_private *priv); > +void omap_global_obj_fini(struct omap_drm_private *priv); > +void omap_hwoverlays_destroy(struct omap_drm_private *priv); > +int omap_global_obj_init(struct omap_drm_private *priv); > +int omap_overlay_assign(struct drm_atomic_state *s, struct drm_plane *plane, > + u32 caps, u32 fourcc, u32 crtc_mask, > + struct omap_hw_overlay **overlay, > + struct omap_hw_overlay **r_overlay); > +void omap_overlay_release(struct drm_atomic_state *s, struct omap_hw_overlay *overlay); > +void omap_overlay_disable_unassigned(struct drm_atomic_state *s); > + > +#endif /* __OMAPDRM_OVERLAY_H__ */ > diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c > index 161233cbc9a0..01f1da155994 100644 > --- a/drivers/gpu/drm/omapdrm/omap_plane.c > +++ b/drivers/gpu/drm/omapdrm/omap_plane.c > @@ -30,10 +30,12 @@ > > struct omap_plane { > struct drm_plane base; > - enum omap_plane_id id; > + enum omap_plane_id default_id; > const char *name; > }; > > +static const char *plane_id_to_name[]; > + > static int omap_plane_prepare_fb(struct drm_plane *plane, > struct drm_plane_state *new_state) > { > @@ -50,15 +52,26 @@ static void omap_plane_cleanup_fb(struct drm_plane *plane, > omap_framebuffer_unpin(old_state->fb); > } > > +static bool plane_enabled(struct drm_plane_state *state) > +{ > + return state->visible; > +} > + > static void omap_plane_atomic_update(struct drm_plane *plane, > struct drm_plane_state *old_state) > { > struct omap_drm_private *priv = plane->dev->dev_private; > struct omap_plane *omap_plane = to_omap_plane(plane); > struct drm_plane_state *state = plane->state; > - struct omap_overlay_info info; > + struct omap_plane_state *omap_state = to_omap_plane_state(state); > + struct omap_overlay_info info, r_info; > + enum omap_plane_id ovl_id, r_ovl_id; > int ret; > + bool dual_plane = !!omap_state->r_overlay; > > + ovl_id = omap_state->overlay->overlay_id; > + DBG("[PLANE:%d:%s] overlay_id: %d\n", plane->base.id, plane->name, > + ovl_id); > DBG("%s, crtc=%p fb=%p", omap_plane->name, state->crtc, state->fb); > > memset(&info, 0, sizeof(info)); > @@ -67,75 +80,231 @@ static void omap_plane_atomic_update(struct drm_plane *plane, > info.global_alpha = 0xff; > info.zorder = state->normalized_zpos; > > + r_info = info; > + > /* update scanout: */ > - omap_framebuffer_update_scanout(state->fb, state, &info); > + omap_framebuffer_update_scanout(state->fb, state, &info, > + dual_plane ? &r_info : NULL); > > - DBG("%dx%d -> %dx%d (%d)", info.width, info.height, > - info.out_width, info.out_height, > - info.screen_width); > + DBG("%s: %dx%d -> %dx%d (%d)", > + overlay2name(ovl_id), info.width, info.height, > + info.out_width, info.out_height, info.screen_width); > DBG("%d,%d %pad %pad", info.pos_x, info.pos_y, > - &info.paddr, &info.p_uv_addr); > + &info.paddr, &info.p_uv_addr); > + > + if (dual_plane) { > + r_ovl_id = omap_state->r_overlay->overlay_id; > + /* > + * If the current plane uses 2 hw planes the very next > + * zorder is used by the r_overlay so we just use the > + * main overlay zorder + 1 > + */ > + r_info.zorder = info.zorder + 1; > + > + DBG("%s: %dx%d -> %dx%d (%d)", > + overlay2name(r_ovl_id), r_info.width, r_info.height, > + r_info.out_width, r_info.out_height, r_info.screen_width); > + DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y, > + &r_info.paddr, &r_info.p_uv_addr); > + } > > /* and finally, update omapdss: */ > - ret = priv->dispc_ops->ovl_setup(priv->dispc, omap_plane->id, &info, > + ret = priv->dispc_ops->ovl_setup(priv->dispc, ovl_id, &info, > omap_crtc_timings(state->crtc), false, > omap_crtc_channel(state->crtc)); > if (ret) { > - dev_err(plane->dev->dev, "Failed to setup plane %s\n", > + dev_err(plane->dev->dev, "Failed to setup plane1 %s\n", > omap_plane->name); > - priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, false); > + priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, false); > return; > } > > - priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, true); > + priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, true); > + > + if (dual_plane) { > + ret = priv->dispc_ops->ovl_setup(priv->dispc, r_ovl_id, &r_info, > + omap_crtc_timings(state->crtc), false, > + omap_crtc_channel(state->crtc)); > + if (ret) { > + dev_err(plane->dev->dev, "Failed to setup plane2 %s\n", > + omap_plane->name); > + priv->dispc_ops->ovl_enable(priv->dispc, r_ovl_id, false); > + priv->dispc_ops->ovl_enable(priv->dispc, ovl_id, false); > + return; > + } > + > + priv->dispc_ops->ovl_enable(priv->dispc, r_ovl_id, true); > + } > } > > static void omap_plane_atomic_disable(struct drm_plane *plane, > struct drm_plane_state *old_state) > { > struct omap_drm_private *priv = plane->dev->dev_private; > - struct omap_plane *omap_plane = to_omap_plane(plane); > + struct omap_plane_state *omap_state = to_omap_plane_state(old_state); > + bool dual_plane = !!omap_state->r_overlay; > + > + DBG("%s: check (overlay %p r_overlay %p)", plane->name, > + omap_state->overlay, omap_state->r_overlay); > + > + if (!omap_state->overlay) > + return; > > plane->state->rotation = DRM_MODE_ROTATE_0; > plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY > - ? 0 : omap_plane->id; > - > - priv->dispc_ops->ovl_enable(priv->dispc, omap_plane->id, false); > + ? 0 : omap_state->overlay->overlay_id; > + > + priv->dispc_ops->ovl_enable(priv->dispc, omap_state->overlay->overlay_id, false); > + omap_overlay_release(old_state->state, omap_state->overlay); > + omap_state->overlay = NULL; > + if (dual_plane) { > + priv->dispc_ops->ovl_enable(priv->dispc, omap_state->r_overlay->overlay_id, false); > + omap_overlay_release(old_state->state, omap_state->r_overlay); > + omap_state->r_overlay = NULL; > + } > } > > +#define FRAC_16_16(mult, div) (((mult) << 16) / (div)) > static int omap_plane_atomic_check(struct drm_plane *plane, > struct drm_plane_state *state) > { > + struct omap_drm_private *priv = plane->dev->dev_private; > + struct drm_crtc *crtc; > struct drm_crtc_state *crtc_state; > + struct drm_plane_state *old_state = plane->state; > + struct omap_plane_state *omap_state = to_omap_plane_state(state); > + const struct dispc_ops *ops = priv->dispc_ops; > + u16 width, height; > + u32 crtc_mask; > + u32 fourcc; > + u32 caps = 0; > + bool new_hw_overlay = false; > + bool new_r_hw_overlay = false; > + bool out_of_bounds = false; > + int min_scale, max_scale; > + u32 max_width, max_height; > + int ret; > > - if (!state->fb) > - return 0; > + DBG("%s: check (%d -> %d)", plane->name, > + plane_enabled(old_state), plane_enabled(state)); > + > + priv->dispc_ops->ovl_get_max_size(priv->dispc, &width, &height); > + max_width = width << 16; > + max_height = height << 16; > > - /* crtc should only be NULL when disabling (i.e., !state->fb) */ > - if (WARN_ON(!state->crtc)) > + crtc = state->crtc ? state->crtc : plane->state->crtc; > + if (!crtc) > return 0; > > - crtc_state = drm_atomic_get_existing_crtc_state(state->state, state->crtc); > + crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc); > /* we should have a crtc state if the plane is attached to a crtc */ > if (WARN_ON(!crtc_state)) > return 0; > > - if (!crtc_state->enable) > - return 0; > + /* Make sure source dimensions are within bounds. */ > + if (state->src_h > max_height) > + out_of_bounds = true; > > - if (state->crtc_x < 0 || state->crtc_y < 0) > - return -EINVAL; > + if (state->src_w > max_width) { > + if (state->src_w <= (2 * max_width)) > + new_r_hw_overlay = true; > + else > + out_of_bounds = true; > + } > > - if (state->crtc_x + state->crtc_w > crtc_state->adjusted_mode.hdisplay) > - return -EINVAL; > + if (out_of_bounds) { > + struct drm_rect src = drm_plane_state_src(state); > + DBG("Invalid source size "DRM_RECT_FP_FMT, > + DRM_RECT_FP_ARG(&src)); > + return -ERANGE; > + } > > - if (state->crtc_y + state->crtc_h > crtc_state->adjusted_mode.vdisplay) > - return -EINVAL; > + min_scale = FRAC_16_16(1, 4); > + max_scale = FRAC_16_16(8, 1); > + > + ret = drm_atomic_helper_check_plane_state(state, crtc_state, > + min_scale, max_scale, > + true, true); > + if (ret) > + return ret; > > if (state->rotation != DRM_MODE_ROTATE_0 && > !omap_framebuffer_supports_rotation(state->fb)) > return -EINVAL; > > + if (plane_enabled(state)) { > + if ((state->src_w >> 16) != state->crtc_w || > + (state->src_h >> 16) != state->crtc_h) > + caps |= OMAP_DSS_OVL_CAP_SCALE; > + > + fourcc = state->fb->format->format; > + crtc_mask = drm_crtc_mask(state->crtc); > + > + /* (re)allocate hw overlay if we don't have one or caps-mismatch: */ > + if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) { > + new_hw_overlay = true; > + } else { > + /* check if allowed on crtc */ > + if (!(omap_state->overlay->possible_crtcs & crtc_mask)) > + new_hw_overlay = true; > + > + /* check supported format */ > + if (!ops->ovl_color_mode_supported(priv->dispc, > + omap_state->overlay->overlay_id, > + fourcc)) > + new_hw_overlay = true; > + } > + /* > + * check if we need two overlays and only have 1 or > + * if we had 2 overlays but will only need 1 > + */ > + if ((new_r_hw_overlay && !omap_state->r_overlay) || > + (!new_r_hw_overlay && omap_state->r_overlay)) > + new_hw_overlay = true; > + > + if (new_hw_overlay) { > + struct omap_hw_overlay *old_ovl = > + omap_state->overlay; > + struct omap_hw_overlay *old_r_ovl = > + omap_state->r_overlay; > + struct omap_hw_overlay *new_ovl = NULL; > + struct omap_hw_overlay *new_r_ovl = NULL; > + > + omap_overlay_release(state->state, old_ovl); > + omap_overlay_release(state->state, old_r_ovl); > + > + ret = omap_overlay_assign(state->state, plane, caps, > + fourcc, crtc_mask, &new_ovl, > + new_r_hw_overlay ? > + &new_r_ovl : NULL); > + if (ret) { > + DBG("%s: failed to assign hw_overlay(s)!", > + plane->name); > + omap_state->overlay = NULL; > + omap_state->r_overlay = NULL; > + return ret; > + } > + > + omap_state->overlay = new_ovl; > + if (new_r_hw_overlay) > + omap_state->r_overlay = new_r_ovl; > + else > + omap_state->r_overlay = NULL; > + } > + } else { > + omap_overlay_release(state->state, omap_state->overlay); > + omap_overlay_release(state->state, omap_state->r_overlay); > + omap_state->overlay = NULL; > + omap_state->r_overlay = NULL; > + } > + > + if (omap_state->overlay) > + DBG("plane: %s overlay_id: %d", plane->name, > + omap_state->overlay->overlay_id); > + if (omap_state->r_overlay) > + DBG("plane: %s r_overlay_id: %d", plane->name, > + omap_state->r_overlay->overlay_id); > + > return 0; > } > > @@ -182,20 +351,77 @@ void omap_plane_install_properties(struct drm_plane *plane, > drm_object_attach_property(obj, priv->zorder_prop, 0); > } > > +/* Add duplicate and destroy state helper */ > +static struct drm_plane_state * > +omap_plane_atomic_duplicate_state(struct drm_plane *plane) > +{ > + struct omap_plane_state *state; > + struct omap_plane_state *copy; > + > + if (WARN_ON(!plane->state)) > + return NULL; > + > + state = to_omap_plane_state(plane->state); > + copy = kmemdup(state, sizeof(*state), GFP_KERNEL); > + if (copy == NULL) > + return NULL; > + > + __drm_atomic_helper_plane_duplicate_state(plane, ©->base); > + > + return ©->base; > +} > + > +static void omap_plane_atomic_destroy_state(struct drm_plane *plane, > + struct drm_plane_state *state) > +{ > + __drm_atomic_helper_plane_destroy_state(state); > + kfree(to_omap_plane_state(state)); > +} > + > static void omap_plane_reset(struct drm_plane *plane) > { > struct omap_plane *omap_plane = to_omap_plane(plane); > + struct omap_plane_state *omap_state; > + > + if (plane->state) > + omap_plane_atomic_destroy_state(plane, plane->state); > > - drm_atomic_helper_plane_reset(plane); > - if (!plane->state) > + omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL); > + if (!omap_state) > return; > > + omap_state->base.plane = plane; > + plane->state = &omap_state->base; > + plane->state->plane = plane; > + plane->state->rotation = DRM_MODE_ROTATE_0; > /* > * Set the zpos default depending on whether we are a primary or overlay > * plane. > */ > plane->state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY > - ? 0 : omap_plane->id; > + ? 0 : omap_plane->default_id; > +} > + > +static void omap_plane_atomic_print_state(struct drm_printer *p, > + const struct drm_plane_state *state) > +{ > + struct omap_plane_state *omap_state = to_omap_plane_state(state); > + > + drm_printf(p, "\toverlay=%p\n", omap_state->overlay); > + if (omap_state->overlay) { > + drm_printf(p, "\t\tidx=%d\n", omap_state->overlay->idx); > + drm_printf(p, "\t\toverlay_id=%d\n", omap_state->overlay->overlay_id); > + drm_printf(p, "\t\tcaps=0x%x\n", omap_state->overlay->caps); > + drm_printf(p, "\t\tpossible_crtcs=0x%x\n", omap_state->overlay->possible_crtcs); > + } > + > + drm_printf(p, "\tr_overlay=%p\n", omap_state->r_overlay); > + if (omap_state->r_overlay) { > + drm_printf(p, "\t\tidx=%d\n", omap_state->r_overlay->idx); > + drm_printf(p, "\t\toverlay_id=%d\n", omap_state->r_overlay->overlay_id); > + drm_printf(p, "\t\tcaps=0x%x\n", omap_state->r_overlay->caps); > + drm_printf(p, "\t\tpossible_crtcs=0x%x\n", omap_state->r_overlay->possible_crtcs); > + } > } > > static int omap_plane_atomic_set_property(struct drm_plane *plane, > @@ -233,10 +459,11 @@ static const struct drm_plane_funcs omap_plane_funcs = { > .disable_plane = drm_atomic_helper_disable_plane, > .reset = omap_plane_reset, > .destroy = omap_plane_destroy, > - .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, > - .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, > + .atomic_duplicate_state = omap_plane_atomic_duplicate_state, > + .atomic_destroy_state = omap_plane_atomic_destroy_state, > .atomic_set_property = omap_plane_atomic_set_property, > .atomic_get_property = omap_plane_atomic_get_property, > + .atomic_print_state = omap_plane_atomic_print_state, > }; > > static const char *plane_id_to_name[] = { > @@ -246,14 +473,6 @@ static const char *plane_id_to_name[] = { > [OMAP_DSS_VIDEO3] = "vid3", > }; > > -static const enum omap_plane_id plane_idx_to_id[] = { > - OMAP_DSS_GFX, > - OMAP_DSS_VIDEO1, > - OMAP_DSS_VIDEO2, > - OMAP_DSS_VIDEO3, > -}; > - > -/* initialize plane */ > struct drm_plane *omap_plane_init(struct drm_device *dev, > int idx, enum drm_plane_type type, > u32 possible_crtcs) > @@ -262,27 +481,28 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, > unsigned int num_planes = priv->dispc_ops->get_num_ovls(priv->dispc); > struct drm_plane *plane; > struct omap_plane *omap_plane; > - enum omap_plane_id id; > int ret; > u32 nformats; > const u32 *formats; > > - if (WARN_ON(idx >= ARRAY_SIZE(plane_idx_to_id))) > + if (WARN_ON(idx >= num_planes)) > return ERR_PTR(-EINVAL); > > - id = plane_idx_to_id[idx]; > - > - DBG("%s: type=%d", plane_id_to_name[id], type); > - > omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL); > if (!omap_plane) > return ERR_PTR(-ENOMEM); > > - formats = priv->dispc_ops->ovl_get_color_modes(priv->dispc, id); > + omap_plane->default_id = idx; > + omap_plane->name = plane_id_to_name[idx]; > + > + DBG("%s: type=%d", omap_plane->name, type); > + DBG(" omap_plane->default_id: %d", omap_plane->default_id); > + DBG(" crtc_mask: 0x%04x", possible_crtcs); > + > + formats = priv->dispc_ops->ovl_get_color_modes(priv->dispc, > + omap_plane->default_id); > for (nformats = 0; formats[nformats]; ++nformats) > ; > - omap_plane->id = id; > - omap_plane->name = plane_id_to_name[id]; > > plane = &omap_plane->base; > > @@ -301,7 +521,7 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, > > error: > dev_err(dev->dev, "%s(): could not create plane: %s\n", > - __func__, plane_id_to_name[id]); > + __func__, omap_plane->name); > > kfree(omap_plane); > return NULL; > diff --git a/drivers/gpu/drm/omapdrm/omap_plane.h b/drivers/gpu/drm/omapdrm/omap_plane.h > index dc5e82ad061d..9000f45a9b65 100644 > --- a/drivers/gpu/drm/omapdrm/omap_plane.h > +++ b/drivers/gpu/drm/omapdrm/omap_plane.h > @@ -28,10 +28,30 @@ struct drm_device; > struct drm_mode_object; > struct drm_plane; > > +/* > + * Atomic plane state. Subclasses the base drm_plane_state in order to > + * track assigned overlay and hw specific state. > + */ > +struct omap_plane_state { > + struct drm_plane_state base; > + > + struct omap_hw_overlay *overlay; > + struct omap_hw_overlay *r_overlay; /* right overlay */ > +}; > +#define to_omap_plane_state(x) \ > + container_of(x, struct omap_plane_state, base) > + > struct drm_plane *omap_plane_init(struct drm_device *dev, > int idx, enum drm_plane_type type, > u32 possible_crtcs); > void omap_plane_install_properties(struct drm_plane *plane, > struct drm_mode_object *obj); > > +static inline bool is_omap_plane_dual_overlay(struct drm_plane_state *state) > +{ > + struct omap_plane_state *omap_state = to_omap_plane_state(state); > + > + return !!omap_state->r_overlay; > +} > + > #endif /* __OMAPDRM_PLANE_H__ */ > -- Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki. Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/dri-devel