diff options
Diffstat (limited to 'drivers/gpu/drm/imx/lcdifv3/lcdifv3-plane.c')
-rw-r--r-- | drivers/gpu/drm/imx/lcdifv3/lcdifv3-plane.c | 202 |
1 files changed, 202 insertions, 0 deletions
diff --git a/drivers/gpu/drm/imx/lcdifv3/lcdifv3-plane.c b/drivers/gpu/drm/imx/lcdifv3/lcdifv3-plane.c new file mode 100644 index 000000000000..5c32acbe8def --- /dev/null +++ b/drivers/gpu/drm/imx/lcdifv3/lcdifv3-plane.c @@ -0,0 +1,202 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019,2020 NXP + */ + +#include <linux/module.h> +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_plane.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_rect.h> +#include <video/imx-lcdifv3.h> + +#include "lcdifv3-plane.h" + +static uint32_t lcdifv3_pixel_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_XRGB1555, +}; + +static int lcdifv3_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + int ret; + struct drm_plane_state *old_state = plane->state; + struct drm_framebuffer *fb = plane_state->fb; + struct drm_framebuffer *old_fb = old_state->fb; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *mode; + + /* 'fb' should also be NULL which has been checked in + * the core sanity check function 'drm_atomic_plane_check()' + */ + if (!plane_state->crtc) { + WARN_ON(fb); + return 0; + } + + /* lcdifv3 crtc can only display from (0,0) for each plane */ + if (plane_state->crtc_x || plane_state->crtc_y) + return -EINVAL; + + crtc_state = drm_atomic_get_existing_crtc_state(plane_state->state, + plane_state->crtc); + mode = &crtc_state->adjusted_mode; + + ret = drm_atomic_helper_check_plane_state(plane_state, crtc_state, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); + if (ret) + return ret; + + if (!plane_state->visible) + return -EINVAL; + + /* force 'mode_changed' when fb pitches changed, since + * the pitch related registers configuration of LCDIF + * can not be done when LCDIF is running. + */ + if (old_fb && likely(!crtc_state->mode_changed)) { + if (old_fb->pitches[0] != fb->pitches[0]) + crtc_state->mode_changed = true; + } + + return 0; +} + +static void lcdifv3_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct lcdifv3_plane *lcdifv3_plane = to_lcdifv3_plane(plane); + struct lcdifv3_soc *lcdifv3 = lcdifv3_plane->lcdifv3; + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_gem_cma_object *gem_obj = NULL; + u32 fb_addr, src_off, src_w, fb_idx, cpp, stride; + bool crop; + + /* plane and crtc is disabling */ + if (!fb) + return; + + /* TODO: for now we just update the next buf addr + * and the fb pixel format, since the mode set will + * be done in crtc's ->enable() helper func + */ + if (plane->type == DRM_PLANE_TYPE_PRIMARY) + lcdifv3_set_pix_fmt(lcdifv3, fb->format->format); + + switch (plane->type) { + case DRM_PLANE_TYPE_PRIMARY: + /* TODO: only support RGB */ + gem_obj = drm_fb_cma_get_gem_obj(fb, 0); + src_off = (state->src_y >> 16) * fb->pitches[0] + + (state->src_x >> 16) * fb->format->cpp[0]; + fb_addr = gem_obj->paddr + fb->offsets[0] + src_off; + fb_idx = 0; + break; + default: + /* TODO: add overlay later */ + return; + } + + lcdifv3_set_fb_addr(lcdifv3, fb_idx, fb_addr); + + /* config horizontal cropping if crtc needs modeset */ + if (unlikely(drm_atomic_crtc_needs_modeset(state->crtc->state))) { + cpp = fb->format->cpp[0]; + stride = DIV_ROUND_UP(fb->pitches[0], cpp); + + src_w = state->src_w >> 16; + WARN_ON(src_w > fb->width); + + crop = src_w != stride ? true : false; + lcdifv3_set_fb_hcrop(lcdifv3, src_w, fb->pitches[0], crop); + } +} + +static void lcdifv3_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + + WARN_ON(fb); + + /* TODO: CRTC disabled has been done by CRTC helper function, + * so it seems that no more required, the only possible thing + * is to set next buf addr to 0 in CRTC + */ +} + +static const struct drm_plane_helper_funcs lcdifv3_plane_helper_funcs = { + .atomic_check = lcdifv3_plane_atomic_check, + .atomic_update = lcdifv3_plane_atomic_update, + .atomic_disable = lcdifv3_plane_atomic_disable, +}; + +static void lcdifv3_plane_destroy(struct drm_plane *plane) +{ + struct lcdifv3_plane *lcdifv3_plane = to_lcdifv3_plane(plane); + + drm_plane_cleanup(plane); + kfree(lcdifv3_plane); +} + +static const struct drm_plane_funcs lcdifv3_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = lcdifv3_plane_destroy, + .reset = drm_atomic_helper_plane_reset, + .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, +}; + +struct lcdifv3_plane *lcdifv3_plane_init(struct drm_device *dev, + struct lcdifv3_soc *lcdifv3, + unsigned int possible_crtcs, + enum drm_plane_type type, + unsigned int zpos) +{ + int ret; + struct lcdifv3_plane *lcdifv3_plane; + + /* lcdifv3 doesn't support fb modifiers */ + if (zpos || dev->mode_config.allow_fb_modifiers) + return ERR_PTR(-EINVAL); + + lcdifv3_plane = kzalloc(sizeof(*lcdifv3_plane), GFP_KERNEL); + if (!lcdifv3_plane) + return ERR_PTR(-ENOMEM); + + lcdifv3_plane->lcdifv3 = lcdifv3; + + drm_plane_helper_add(&lcdifv3_plane->base, &lcdifv3_plane_helper_funcs); + ret = drm_universal_plane_init(dev, &lcdifv3_plane->base, possible_crtcs, + &lcdifv3_plane_funcs, lcdifv3_pixel_formats, + ARRAY_SIZE(lcdifv3_pixel_formats), NULL, + type, NULL); + if (ret) { + kfree(lcdifv3_plane); + return ERR_PTR(ret); + } + + ret = drm_plane_create_zpos_immutable_property(&lcdifv3_plane->base, zpos); + if (ret) { + kfree(lcdifv3_plane); + return ERR_PTR(ret); + } + + return lcdifv3_plane; +} |