diff options
author | Fancy Fang <chen.fang@nxp.com> | 2018-06-06 22:52:35 +0800 |
---|---|---|
committer | Leonard Crestez <leonard.crestez@nxp.com> | 2018-08-24 12:41:33 +0300 |
commit | c30528f1567ca9cad24117e15f443ff45bcbc895 (patch) | |
tree | 7ce07b76983ed6f851d60663c0a2c250eb173e6c /drivers/gpu | |
parent | 2d1c4ecde1362e2a41cdf436c4cb87a1acc0036d (diff) |
MLK-18535-5 drm/imx: add LCDIF DRM/KMS driver
This is a new DRM/KMS driver for LCDIF which conforms
to the IMX DRM Core framework. It provides support for
CRTCs, Planes and mode config of KMS.
Signed-off-by: Fancy Fang <chen.fang@nxp.com>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/imx/Kconfig | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/Kconfig | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/Makefile | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/lcdif-crtc.c | 367 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/lcdif-kms.c | 46 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/lcdif-kms.h | 21 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/lcdif-plane.c | 231 | ||||
-rw-r--r-- | drivers/gpu/drm/imx/lcdif/lcdif-plane.h | 37 |
9 files changed, 717 insertions, 1 deletions
diff --git a/drivers/gpu/drm/imx/Kconfig b/drivers/gpu/drm/imx/Kconfig index cee1a0b58cb1..b3105f444098 100644 --- a/drivers/gpu/drm/imx/Kconfig +++ b/drivers/gpu/drm/imx/Kconfig @@ -5,7 +5,7 @@ config DRM_IMX select DRM_GEM_CMA_HELPER select DRM_KMS_CMA_HELPER depends on DRM - depends on IMX_IPUV3_CORE || IMX_DPU_CORE || IMX_DCSS_CORE + depends on IMX_IPUV3_CORE || IMX_DPU_CORE || IMX_DCSS_CORE || IMX_LCDIF_CORE help enable i.MX graphics support @@ -51,3 +51,4 @@ source "drivers/gpu/drm/imx/ipuv3/Kconfig" source "drivers/gpu/drm/imx/dpu/Kconfig" source "drivers/gpu/drm/imx/hdp/Kconfig" source "drivers/gpu/drm/imx/dcss/Kconfig" +source "drivers/gpu/drm/imx/lcdif/Kconfig" diff --git a/drivers/gpu/drm/imx/Makefile b/drivers/gpu/drm/imx/Makefile index 1fc8a8b2125f..8fe7b576a56e 100644 --- a/drivers/gpu/drm/imx/Makefile +++ b/drivers/gpu/drm/imx/Makefile @@ -12,3 +12,4 @@ obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3/ obj-$(CONFIG_DRM_IMX_DPU) += dpu/ obj-$(CONFIG_DRM_IMX_HDP) += hdp/ obj-$(CONFIG_DRM_IMX_DCSS) += dcss/ +obj-$(CONFIG_DRM_IMX_LCDIF) += lcdif/ diff --git a/drivers/gpu/drm/imx/lcdif/Kconfig b/drivers/gpu/drm/imx/lcdif/Kconfig new file mode 100644 index 000000000000..4460ffacd1f7 --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/Kconfig @@ -0,0 +1,8 @@ +config DRM_IMX_LCDIF + tristate "i.MX LCDIF controller DRM driver" + depends on DRM_IMX + depends on IMX_LCDIF_CORE + default y if DRM_IMX=y + default m if DRM_IMX=m + help + enable i.MX LCDIF controller DRM driver under DRM_IMX. diff --git a/drivers/gpu/drm/imx/lcdif/Makefile b/drivers/gpu/drm/imx/lcdif/Makefile new file mode 100644 index 000000000000..fcdecff9e861 --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/Makefile @@ -0,0 +1,4 @@ +ccflags-y += -Idrivers/gpu/drm/imx + +imx-lcdif-crtc-objs := lcdif-crtc.o lcdif-plane.o lcdif-kms.o +obj-$(CONFIG_DRM_IMX_LCDIF) += imx-lcdif-crtc.o diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c b/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c new file mode 100644 index 000000000000..f14ca734923a --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/lcdif-crtc.c @@ -0,0 +1,367 @@ +/* + * Copyright 2018 NXP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/component.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <video/imx-lcdif.h> +#include <video/videomode.h> + +#include "imx-drm.h" +#include "lcdif-plane.h" +#include "lcdif-kms.h" + +struct lcdif_crtc { + struct device *dev; + + struct drm_crtc base; + struct imx_drm_crtc *imx_crtc; + struct lcdif_plane *plane[2]; + + int vbl_irq; + u32 pix_fmt; /* drm fourcc */ +}; + +#define to_lcdif_crtc(crtc) container_of(crtc, struct lcdif_crtc, base) + +static void lcdif_crtc_destroy(struct drm_crtc *crtc) +{ + struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc); + + imx_drm_remove_crtc(lcdif_crtc->imx_crtc); + lcdif_crtc->imx_crtc = NULL; +} + +static void lcdif_crtc_reset(struct drm_crtc *crtc) +{ + struct imx_crtc_state *state; + + if (crtc->state) { + __drm_atomic_helper_crtc_destroy_state(crtc->state); + + state = to_imx_crtc_state(crtc->state); + kfree(state); + crtc->state = NULL; + } + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return; + + crtc->state = &state->base; + crtc->state->crtc = crtc; +} + +static struct drm_crtc_state *lcdif_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct imx_crtc_state *state, *orig_state; + + if (WARN_ON(!crtc->state)) + return NULL; + + state = kzalloc(sizeof(*state), GFP_KERNEL); + if (!state) + return NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); + + orig_state = to_imx_crtc_state(crtc->state); + state->bus_format = orig_state->bus_format; + state->bus_flags = orig_state->bus_flags; + state->di_hsync_pin = orig_state->di_hsync_pin; + state->di_vsync_pin = orig_state->di_vsync_pin; + + return &state->base; +} + +static void lcdif_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + __drm_atomic_helper_crtc_destroy_state(state); + kfree(to_imx_crtc_state(state)); +} + +static const struct drm_crtc_funcs lcdif_crtc_funcs = { + .set_config = drm_atomic_helper_set_config, + .destroy = lcdif_crtc_destroy, + .page_flip = drm_atomic_helper_page_flip, + .reset = lcdif_crtc_reset, + .atomic_duplicate_state = lcdif_crtc_duplicate_state, + .atomic_destroy_state = lcdif_crtc_destroy_state, +}; + +static int lcdif_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + return 0; +} + +static void lcdif_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + drm_crtc_vblank_on(crtc); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc)); + drm_crtc_arm_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); +} + +static void lcdif_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + /* LCDIF doesn't have command buffer */ + return; +} + +static void lcdif_crtc_enable(struct drm_crtc *crtc) +{ + struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc); + struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent); + struct drm_display_mode *mode = &crtc->state->adjusted_mode; + struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc->state); + struct videomode vm; + + drm_display_mode_to_videomode(mode, &vm); + + if (imx_crtc_state->bus_flags & DRM_BUS_FLAG_DE_HIGH) + vm.flags |= DISPLAY_FLAGS_DE_HIGH; + else + vm.flags |= DISPLAY_FLAGS_DE_LOW; + + if (imx_crtc_state->bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) + vm.flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; + else + vm.flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; + + pm_runtime_get_sync(lcdif_crtc->dev->parent); + + lcdif_set_mode(lcdif, &vm); + + /* defer the lcdif controller enable to plane update, + * since until then the lcdif config is complete to + * enable the controller to run actually. + */ +} + +static void lcdif_crtc_atomic_disable(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc); + struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent); + + spin_lock_irq(&crtc->dev->event_lock); + if (crtc->state->event) { + drm_crtc_send_vblank_event(crtc, crtc->state->event); + crtc->state->event = NULL; + } + spin_unlock_irq(&crtc->dev->event_lock); + + drm_crtc_vblank_off(crtc); + + lcdif_disable_controller(lcdif); + + pm_runtime_put(lcdif_crtc->dev->parent); +} + +static const struct drm_crtc_helper_funcs lcdif_helper_funcs = { + .atomic_check = lcdif_crtc_atomic_check, + .atomic_begin = lcdif_crtc_atomic_begin, + .atomic_flush = lcdif_crtc_atomic_flush, + .enable = lcdif_crtc_enable, + .atomic_disable = lcdif_crtc_atomic_disable, +}; + +static int lcdif_enable_vblank(struct drm_crtc *crtc) +{ + struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc); + struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent); + + lcdif_vblank_irq_enable(lcdif); + enable_irq(lcdif_crtc->vbl_irq); + + return 0; +} + +static void lcdif_disable_vblank(struct drm_crtc *crtc) +{ + struct lcdif_crtc *lcdif_crtc = to_lcdif_crtc(crtc); + struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent); + + disable_irq_nosync(lcdif_crtc->vbl_irq); + lcdif_vblank_irq_disable(lcdif); +} + +static const struct imx_drm_crtc_helper_funcs lcdif_crtc_helper_funcs = { + .enable_vblank = lcdif_enable_vblank, + .disable_vblank = lcdif_disable_vblank, + .crtc_funcs = &lcdif_crtc_funcs, + .crtc_helper_funcs = &lcdif_helper_funcs, +}; + +static irqreturn_t lcdif_crtc_vblank_irq_handler(int irq, void *dev_id) +{ + struct lcdif_crtc *lcdif_crtc = dev_id; + struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent); + + drm_crtc_handle_vblank(&lcdif_crtc->base); + + lcdif_vblank_irq_clear(lcdif); + + return IRQ_HANDLED; +} + +static int lcdif_crtc_init(struct lcdif_crtc *lcdif_crtc, + struct lcdif_client_platformdata *pdata, + struct drm_device *drm) +{ + int ret; + struct lcdif_plane *primary = lcdif_crtc->plane[0]; + struct lcdif_soc *lcdif = dev_get_drvdata(lcdif_crtc->dev->parent); + + /* Primary plane + * The 'possible_crtcs' of primary plane will be + * recalculated during the 'crtc' initialization + * later. + */ + primary = lcdif_plane_init(drm, lcdif, 0, DRM_PLANE_TYPE_PRIMARY, 0); + if (IS_ERR(primary)) + return PTR_ERR(primary); + lcdif_crtc->plane[0] = primary; + + /* TODO: Overlay plane */ + + ret = imx_drm_add_crtc(drm, &lcdif_crtc->base, + &lcdif_crtc->imx_crtc, &primary->base, + &lcdif_crtc_helper_funcs, pdata->of_node); + if (ret) { + dev_err(lcdif_crtc->dev, "failed to init crtc\n"); + goto primary_plane_deinit; + } + + lcdif_crtc->vbl_irq = lcdif_vblank_irq_get(lcdif); + WARN_ON(lcdif_crtc->vbl_irq < 0); + + ret = devm_request_irq(lcdif_crtc->dev, lcdif_crtc->vbl_irq, + lcdif_crtc_vblank_irq_handler, 0, + dev_name(lcdif_crtc->dev), lcdif_crtc); + if (ret) { + dev_err(lcdif_crtc->dev, + "vblank irq request failed: %d\n", ret); + goto primary_plane_deinit; + } + + disable_irq(lcdif_crtc->vbl_irq); + + return 0; + +primary_plane_deinit: + lcdif_plane_deinit(drm, primary); + + return ret; +} + +static int lcdif_crtc_bind(struct device *dev, struct device *master, + void *data) +{ + int ret; + struct drm_device *drm = data; + struct lcdif_crtc *lcdif_crtc; + struct lcdif_client_platformdata *pdata = dev->platform_data; + + dev_dbg(dev, "%s: lcdif crtc bind begin\n", __func__); + + lcdif_crtc = devm_kzalloc(dev, sizeof(*lcdif_crtc), GFP_KERNEL); + if (!lcdif_crtc) + return -ENOMEM; + + lcdif_crtc->dev = dev; + + ret = lcdif_crtc_init(lcdif_crtc, pdata, drm); + if (ret) + return ret; + + if (!drm->mode_config.funcs) + drm->mode_config.funcs = &lcdif_drm_mode_config_funcs; + + if (!drm->mode_config.helper_private) + drm->mode_config.helper_private = &lcdif_drm_mode_config_helpers; + + dev_set_drvdata(dev, lcdif_crtc); + + dev_dbg(dev, "%s: lcdif crtc bind end\n", __func__); + + return 0; +} + +static void lcdif_crtc_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = data; + struct lcdif_crtc *lcdif_crtc = dev_get_drvdata(dev); + + if (lcdif_crtc->imx_crtc) { + imx_drm_remove_crtc(lcdif_crtc->imx_crtc); + lcdif_crtc->imx_crtc = NULL; + } + + lcdif_plane_deinit(drm, lcdif_crtc->plane[0]); +} + +static const struct component_ops lcdif_crtc_ops = { + .bind = lcdif_crtc_bind, + .unbind = lcdif_crtc_unbind, +}; + +static int lcdif_crtc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + + dev_dbg(&pdev->dev, "%s: lcdif crtc probe begin\n", __func__); + + if (!dev->platform_data) { + dev_err(dev, "no platform data\n"); + return -EINVAL; + } + + return component_add(dev, &lcdif_crtc_ops); +} + +static int lcdif_crtc_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &lcdif_crtc_ops); + + return 0; +} + +static struct platform_driver lcdif_crtc_driver = { + .probe = lcdif_crtc_probe, + .remove = lcdif_crtc_remove, + .driver = { + .name = "imx-lcdif-crtc", + }, +}; +module_platform_driver(lcdif_crtc_driver); + +MODULE_DESCRIPTION("NXP i.MX LCDIF DRM CRTC driver"); +MODULE_AUTHOR("Fancy Fang <chen.fang@nxp.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-kms.c b/drivers/gpu/drm/imx/lcdif/lcdif-kms.c new file mode 100644 index 000000000000..f013bf0ce7f8 --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/lcdif-kms.c @@ -0,0 +1,46 @@ +/* + * Copyright 2018 NXP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_fb_cma_helper.h> + +static void lcdif_drm_atomic_commit_tail(struct drm_atomic_state *state) +{ + struct drm_device *dev = state->dev; + + drm_atomic_helper_commit_modeset_disables(dev, state); + + drm_atomic_helper_commit_modeset_enables(dev, state); + + drm_atomic_helper_commit_planes(dev, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); + + drm_atomic_helper_commit_hw_done(state); + + drm_atomic_helper_wait_for_vblanks(dev, state); + + drm_atomic_helper_cleanup_planes(dev, state); +} + +const struct drm_mode_config_funcs lcdif_drm_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +struct drm_mode_config_helper_funcs lcdif_drm_mode_config_helpers = { + .atomic_commit_tail = lcdif_drm_atomic_commit_tail, +}; diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-kms.h b/drivers/gpu/drm/imx/lcdif/lcdif-kms.h new file mode 100644 index 000000000000..fcf7d257c6b7 --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/lcdif-kms.h @@ -0,0 +1,21 @@ +/* + * Copyright 2018 NXP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LCDIF_KMS_H +#define __LCDIF_KMS_H + +extern const struct drm_mode_config_funcs lcdif_drm_mode_config_funcs; +extern struct drm_mode_config_helper_funcs lcdif_drm_mode_config_helpers; + +#endif diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-plane.c b/drivers/gpu/drm/imx/lcdif/lcdif-plane.c new file mode 100644 index 000000000000..418fd4b3f264 --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/lcdif-plane.c @@ -0,0 +1,231 @@ +/* + * Copyright 2018 NXP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#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-lcdif.h> + +#include "lcdif-plane.h" + +static uint32_t lcdif_pixel_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_BGR565, +}; + +static int lcdif_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *plane_state) +{ + int ret; + struct drm_framebuffer *fb = plane_state->fb; + struct drm_crtc_state *crtc_state; + struct drm_display_mode *mode; + struct drm_rect clip = { 0 }; + unsigned int flags; + + /* '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; + } + + /* lcdif 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; + + /* check fb pixel format matches bus format */ + flags = mode->private_flags & 0xffff; + + switch (fb->pixel_format) { + case DRM_FORMAT_RGB565: + if (flags != MEDIA_BUS_FMT_RGB565_1X16) + return -EINVAL; + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + if (flags != MEDIA_BUS_FMT_RGB888_1X24) + return -EINVAL; + break; + default: + /* TODO: add other formats support later */ + return -EINVAL; + } + + clip.x2 = mode->hdisplay; + clip.y2 = mode->vdisplay; + + ret = drm_plane_helper_check_state(plane_state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + false, true); + if (ret) + return ret; + + if (!plane_state->visible) + return -EINVAL; + + return 0; +} + +static void lcdif_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct lcdif_plane *lcdif_plane = to_lcdif_plane(plane); + struct lcdif_soc *lcdif = lcdif_plane->lcdif; + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct drm_framebuffer *old_fb = old_state->fb; + struct drm_gem_cma_object *gem_obj = NULL; + u32 fb_addr, src_off, fb_idx; + + /* 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 && + (!old_fb || fb->pixel_format != old_fb->pixel_format)) + lcdif_set_pix_fmt(lcdif, fb->pixel_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->bits_per_pixel; + fb_addr = gem_obj->paddr + fb->offsets[0] + src_off; + fb_idx = 0; + break; + default: + /* TODO: add overlay later */ + return; + } + + lcdif_set_fb_addr(lcdif, fb_idx, fb_addr); + + lcdif_enable_controller(lcdif); +} + +static void lcdif_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 lcdif_plane_helper_funcs = { + .atomic_check = lcdif_plane_atomic_check, + .atomic_update = lcdif_plane_atomic_update, + .atomic_disable = lcdif_plane_atomic_disable, +}; + +static void lcdif_plane_destroy(struct drm_plane *plane) +{ + struct lcdif_plane *lcdif_plane = to_lcdif_plane(plane); + + drm_plane_cleanup(plane); + kfree(lcdif_plane); +} + +static const struct drm_plane_funcs lcdif_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .destroy = lcdif_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, + .set_property = drm_atomic_helper_plane_set_property, +}; + +struct lcdif_plane *lcdif_plane_init(struct drm_device *dev, + struct lcdif_soc *lcdif, + unsigned int possible_crtcs, + enum drm_plane_type type, + unsigned int zpos) +{ + int ret; + struct lcdif_plane *lcdif_plane; + + /* lcdif doesn't support fb modifiers */ + if (zpos || dev->mode_config.allow_fb_modifiers) + return ERR_PTR(-EINVAL); + + lcdif_plane = kzalloc(sizeof(*lcdif_plane), GFP_KERNEL); + if (!lcdif_plane) + return ERR_PTR(-ENOMEM); + + lcdif_plane->lcdif = lcdif; + + drm_plane_helper_add(&lcdif_plane->base, &lcdif_plane_helper_funcs); + ret = drm_universal_plane_init(dev, &lcdif_plane->base, possible_crtcs, + &lcdif_plane_funcs, lcdif_pixel_formats, + ARRAY_SIZE(lcdif_pixel_formats), NULL, + type, NULL); + if (ret) { + kfree(lcdif_plane); + return ERR_PTR(ret); + } + + ret = drm_plane_create_zpos_immutable_property(&lcdif_plane->base, zpos); + if (ret) { + kfree(lcdif_plane); + return ERR_PTR(ret); + } + + return lcdif_plane; +} + +void lcdif_plane_deinit(struct drm_device *dev, + struct lcdif_plane *lcdif_plane) +{ + struct drm_plane *plane = &lcdif_plane->base; + + if (plane->zpos_property) + drm_property_destroy(dev, plane->zpos_property); + + lcdif_plane_destroy(plane); +} diff --git a/drivers/gpu/drm/imx/lcdif/lcdif-plane.h b/drivers/gpu/drm/imx/lcdif/lcdif-plane.h new file mode 100644 index 000000000000..acd7aead606a --- /dev/null +++ b/drivers/gpu/drm/imx/lcdif/lcdif-plane.h @@ -0,0 +1,37 @@ +/* + * Copyright 2018 NXP + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __LCDIF_PLANE_H +#define __LCDIF_PLANE_H + +#include <drm/drm_plane.h> +#include <video/imx-lcdif.h> + +struct lcdif_plane { + struct drm_plane base; + struct lcdif_soc *lcdif; +}; + +#define to_lcdif_plane(plane) container_of(plane, struct lcdif_plane, base) + +struct lcdif_plane *lcdif_plane_init(struct drm_device *drm, + struct lcdif_soc *lcdif, + unsigned int possible_crtcs, + enum drm_plane_type type, + unsigned int zpos); + +void lcdif_plane_deinit(struct drm_device *dev, + struct lcdif_plane *lcdif_plane); + +#endif |