From mboxrd@z Thu Jan 1 00:00:00 1970 From: eric@anholt.net (Eric Anholt) Date: Tue, 21 Mar 2017 13:55:18 -0700 Subject: [PATCH v4 2/8] drm/stm: Add STM32 LTDC driver In-Reply-To: <1489597334-16896-3-git-send-email-yannick.fertre@st.com> References: <1489597334-16896-1-git-send-email-yannick.fertre@st.com> <1489597334-16896-3-git-send-email-yannick.fertre@st.com> Message-ID: <87shm61hah.fsf@eliezer.anholt.net> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org Yannick Fertre writes: > This controller provides output signals to interface directly a variety > of LCD and TFT panels. These output signals are: RGB signals > (up to 24bpp), vertical & horizontal synchronisations, data enable and > the pixel clock. I've got some feedback inline that hopefully helps cut some boilerplate DRM code. This looks like a complete driver, and some nice hardware as far as modesetting goes. If you're planning on going through drm-misc, I'd do another pass at review so I could give an Ack. > Change-Id: Ic1d6ade06ab7115c62e98dd21dc3981fb5948d1c > Signed-off-by: Yannick Fertre > --- > drivers/gpu/drm/Kconfig | 3 +- > drivers/gpu/drm/Makefile | 1 + > drivers/gpu/drm/stm/Kconfig | 16 + > drivers/gpu/drm/stm/Makefile | 7 + > drivers/gpu/drm/stm/drv.c | 232 +++++++ > drivers/gpu/drm/stm/drv.h | 22 + > drivers/gpu/drm/stm/ltdc.c | 1422 ++++++++++++++++++++++++++++++++++++++++++ > drivers/gpu/drm/stm/ltdc.h | 22 + > 8 files changed, 1724 insertions(+), 1 deletion(-) > create mode 100644 drivers/gpu/drm/stm/Kconfig > create mode 100644 drivers/gpu/drm/stm/Makefile > create mode 100644 drivers/gpu/drm/stm/drv.c > create mode 100644 drivers/gpu/drm/stm/drv.h > create mode 100644 drivers/gpu/drm/stm/ltdc.c > create mode 100644 drivers/gpu/drm/stm/ltdc.h > > diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig > index 78d7fc0..dd5762a 100644 > --- a/drivers/gpu/drm/Kconfig > +++ b/drivers/gpu/drm/Kconfig > @@ -203,7 +203,6 @@ config DRM_VGEM > as used by Mesa's software renderer for enhanced performance. > If M is selected the module will be called vgem. > > - > source "drivers/gpu/drm/exynos/Kconfig" > > source "drivers/gpu/drm/rockchip/Kconfig" > @@ -246,6 +245,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig" > > source "drivers/gpu/drm/tegra/Kconfig" > > +source "drivers/gpu/drm/stm/Kconfig" > + > source "drivers/gpu/drm/panel/Kconfig" > > source "drivers/gpu/drm/bridge/Kconfig" > diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile > index 59aae43..320fd86 100644 > --- a/drivers/gpu/drm/Makefile > +++ b/drivers/gpu/drm/Makefile > @@ -81,6 +81,7 @@ obj-$(CONFIG_DRM_BOCHS) += bochs/ > obj-$(CONFIG_DRM_VIRTIO_GPU) += virtio/ > obj-$(CONFIG_DRM_MSM) += msm/ > obj-$(CONFIG_DRM_TEGRA) += tegra/ > +obj-$(CONFIG_DRM_STM) += stm/ > obj-$(CONFIG_DRM_STI) += sti/ > obj-$(CONFIG_DRM_IMX) += imx/ > obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ > diff --git a/drivers/gpu/drm/stm/Kconfig b/drivers/gpu/drm/stm/Kconfig > new file mode 100644 > index 0000000..8ef8a09 > --- /dev/null > +++ b/drivers/gpu/drm/stm/Kconfig > @@ -0,0 +1,16 @@ > +config DRM_STM > + tristate "DRM Support for STMicroelectronics SoC Series" > + depends on DRM && (ARCH_STM32 || ARCH_MULTIPLATFORM) > + select DRM_KMS_HELPER > + select DRM_GEM_CMA_HELPER > + select DRM_KMS_CMA_HELPER > + select DRM_PANEL > + select VIDEOMODE_HELPERS > + select FB_PROVIDE_GET_FB_UNMAPPED_AREA > + default y > + > + help > + Enable support for the on-chip display controller on > + STMicroelectronics STM32 MCUs. Funny indentation here? > + To compile this driver as a module, choose M here: the module > + will be called stm-drm. > diff --git a/drivers/gpu/drm/stm/Makefile b/drivers/gpu/drm/stm/Makefile > new file mode 100644 > index 0000000..e114d45 > --- /dev/null > +++ b/drivers/gpu/drm/stm/Makefile > @@ -0,0 +1,7 @@ > +ccflags-y := -Iinclude/drm > + > +stm-drm-y := \ > + drv.o \ > + ltdc.o > + > +obj-$(CONFIG_DRM_STM) += stm-drm.o > diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c > new file mode 100644 > index 0000000..d5c46c5 > --- /dev/null > +++ b/drivers/gpu/drm/stm/drv.c > @@ -0,0 +1,232 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2017 > + * > + * Authors: Philippe Cornu > + * Yannick Fertre > + * Fabien Dessenne > + * Mickael Reulier > + * > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > + > +#include "drv.h" > +#include "ltdc.h" > + > +#define DRIVER_NAME "stm" > +#define DRIVER_DESC "STMicroelectronics SoC DRM" > +#define DRIVER_DATE "20170209" > +#define DRIVER_MAJOR 1 > +#define DRIVER_MINOR 0 > +#define DRIVER_PATCH_LEVEL 0 > + > +#define STM_MAX_FB_WIDTH 2048 > +#define STM_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */ > + > +static void drv_output_poll_changed(struct drm_device *ddev) > +{ > + struct stm_private *priv = ddev->dev_private; > + > + drm_fbdev_cma_hotplug_event(priv->fbdev); > +} > + > +static const struct drm_mode_config_funcs drv_mode_config_funcs = { > + .fb_create = drm_fb_cma_create, > + .output_poll_changed = drv_output_poll_changed, > + .atomic_check = drm_atomic_helper_check, > + .atomic_commit = drm_atomic_helper_commit, > +}; > + > +static const struct file_operations drv_driver_fops = { > + .owner = THIS_MODULE, > + .open = drm_open, > + .release = drm_release, > + .unlocked_ioctl = drm_ioctl, > + .compat_ioctl = drm_compat_ioctl, > + .poll = drm_poll, > + .read = drm_read, > + .mmap = drm_gem_cma_mmap, > + .get_unmapped_area = drm_gem_cma_get_unmapped_area, > +}; This looks like almost a candidate for the new DEFINE_DRM_GEM_CMA_FOPS(). Maybe that helper should conditionally add the drm_gem_cma_get_unmapped_area reference on !MMU? > + > +static void drv_lastclose(struct drm_device *ddev) > +{ > + struct stm_private *priv = ddev->dev_private; > + > + DRM_DEBUG("%s\n", __func__); > + > + drm_fbdev_cma_restore_mode(priv->fbdev); > +} > + > +static struct drm_driver drv_driver = { > + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | > + DRIVER_ATOMIC, > + .lastclose = drv_lastclose, > + .name = DRIVER_NAME, > + .desc = DRIVER_DESC, > + .date = DRIVER_DATE, > + .major = DRIVER_MAJOR, > + .minor = DRIVER_MINOR, > + .patchlevel = DRIVER_PATCH_LEVEL, > + .fops = &drv_driver_fops, > + .dumb_create = drm_gem_cma_dumb_create, > + .dumb_map_offset = drm_gem_cma_dumb_map_offset, > + .dumb_destroy = drm_gem_dumb_destroy, > + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, > + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, > + .gem_free_object_unlocked = drm_gem_cma_free_object, > + .gem_vm_ops = &drm_gem_cma_vm_ops, > + .gem_prime_export = drm_gem_prime_export, > + .gem_prime_import = drm_gem_prime_import, > + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, > + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, > + .gem_prime_vmap = drm_gem_cma_prime_vmap, > + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, > + .gem_prime_mmap = drm_gem_cma_prime_mmap, > + .enable_vblank = ltdc_crtc_enable_vblank, > + .disable_vblank = ltdc_crtc_disable_vblank, > +}; > + > +static int drv_load(struct drm_device *ddev) > +{ > + struct platform_device *pdev = to_platform_device(ddev->dev); > + struct drm_fbdev_cma *fbdev; > + struct stm_private *priv; > + int ret; > + > + DRM_DEBUG("%s\n", __func__); > + > + priv = devm_kzalloc(ddev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + ddev->dev_private = (void *)priv; > + > + drm_mode_config_init(ddev); > + > + /* > + * set max width and height as default value. > + * this value would be used to check framebuffer size limitation > + * at drm_mode_addfb(). > + */ > + ddev->mode_config.min_width = 0; > + ddev->mode_config.min_height = 0; > + ddev->mode_config.max_width = STM_MAX_FB_WIDTH; > + ddev->mode_config.max_height = STM_MAX_FB_HEIGHT; > + ddev->mode_config.funcs = &drv_mode_config_funcs; > + > + ret = ltdc_load(ddev); > + if (ret) > + return ret; > + > + drm_mode_config_reset(ddev); > + drm_kms_helper_poll_init(ddev); > + > + if (ddev->mode_config.num_connector) { > + priv = ddev->dev_private; > + fbdev = drm_fbdev_cma_init(ddev, 16, > + ddev->mode_config.num_connector); > + if (IS_ERR(fbdev)) { > + DRM_DEBUG("Warning: fails to create fbdev\n"); > + fbdev = NULL; > + } > + priv->fbdev = fbdev; > + } > + > + platform_set_drvdata(pdev, priv); > + > + return 0; > +} > + > +static void drv_unload(struct drm_device *ddev) > +{ > + struct stm_private *priv = ddev->dev_private; > + > + DRM_DEBUG("%s\n", __func__); > + > + ltdc_unload(ddev); > + > + if (priv->fbdev) { > + drm_fbdev_cma_fini(priv->fbdev); > + priv->fbdev = NULL; > + } > + > + drm_kms_helper_poll_fini(ddev); > + drm_vblank_cleanup(ddev); > + drm_mode_config_cleanup(ddev); > +} > + > +static int stm_drm_platform_probe(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct drm_device *ddev; > + int ret; > + > + DRM_DEBUG("%s\n", __func__); > + > + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); > + > + ddev = drm_dev_alloc(&drv_driver, dev); > + if (IS_ERR(ddev)) > + return PTR_ERR(ddev); > + > + ret = drv_load(ddev); > + if (ret) > + goto err_unref; > + > + ret = drm_dev_register(ddev, 0); > + if (ret) > + goto err_unref; > + > + return 0; > + > +err_unref: > + drm_dev_unref(ddev); > + > + return ret; > +} > + > +static int stm_drm_platform_remove(struct platform_device *pdev) > +{ > + struct drm_device *ddev = platform_get_drvdata(pdev); > + > + DRM_DEBUG("%s\n", __func__); > + > + drm_dev_unregister(ddev); > + drv_unload(ddev); > + drm_dev_unref(ddev); > + > + return 0; > +} > + > +static const struct of_device_id drv_dt_ids[] = { > + { .compatible = "st,stm32-ltdc"}, > + { /* end node */ }, > +}; > +MODULE_DEVICE_TABLE(of, drv_dt_ids); > + > +static struct platform_driver stm_drm_platform_driver = { > + .probe = stm_drm_platform_probe, > + .remove = stm_drm_platform_remove, > + .driver = { > + .name = DRIVER_NAME, > + .of_match_table = drv_dt_ids, > + }, > +}; > + > +module_platform_driver(stm_drm_platform_driver); > + > +MODULE_AUTHOR("Philippe Cornu "); > +MODULE_AUTHOR("Yannick Fertre "); > +MODULE_AUTHOR("Fabien Dessenne "); > +MODULE_AUTHOR("Mickael Reulier "); > +MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/gpu/drm/stm/drv.h b/drivers/gpu/drm/stm/drv.h > new file mode 100644 > index 0000000..edff073 > --- /dev/null > +++ b/drivers/gpu/drm/stm/drv.h > @@ -0,0 +1,22 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2017 > + * > + * Authors: Philippe Cornu > + * Yannick Fertre > + * Fabien Dessenne > + * Mickael Reulier > + * > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#ifndef _DRV_H_ > +#define _DRV_H_ > + > +#include > + > +struct stm_private { > + struct ltdc *ltdc; > + struct drm_fbdev_cma *fbdev; > +}; > + Any reason not to just put the fbdev in ltdc and drop this struct? > +#endif > diff --git a/drivers/gpu/drm/stm/ltdc.c b/drivers/gpu/drm/stm/ltdc.c > new file mode 100644 > index 0000000..560375d > --- /dev/null > +++ b/drivers/gpu/drm/stm/ltdc.c > @@ -0,0 +1,1422 @@ > +/* > + * Copyright (C) STMicroelectronics SA 2017 > + * > + * Authors: Philippe Cornu > + * Yannick Fertre > + * Fabien Dessenne > + * Mickael Reulier > + * > + * License terms: GNU General Public License (GPL), version 2 > + */ > + > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include