From mboxrd@z Thu Jan 1 00:00:00 1970 From: yannick.fertre@st.com (Yannick Fertre) Date: Mon, 16 Jan 2017 14:28:59 +0100 Subject: [PATCH v1 2/7] drm/st: Add STM32 LTDC driver In-Reply-To: <1484573344-11609-1-git-send-email-yannick.fertre@st.com> References: <1484573344-11609-1-git-send-email-yannick.fertre@st.com> Message-ID: <1484573344-11609-3-git-send-email-yannick.fertre@st.com> To: linux-arm-kernel@lists.infradead.org List-Id: linux-arm-kernel.lists.infradead.org This patch adds support for the STM32 LCD-TFT display controller. Signed-off-by: Yannick Fertre --- drivers/gpu/drm/Kconfig | 2 + drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/st/Kconfig | 14 + drivers/gpu/drm/st/Makefile | 7 + drivers/gpu/drm/st/drv.c | 279 +++++++++ drivers/gpu/drm/st/drv.h | 25 + drivers/gpu/drm/st/ltdc.c | 1438 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/st/ltdc.h | 20 + 8 files changed, 1786 insertions(+) create mode 100644 drivers/gpu/drm/st/Kconfig create mode 100644 drivers/gpu/drm/st/Makefile create mode 100644 drivers/gpu/drm/st/drv.c create mode 100644 drivers/gpu/drm/st/drv.h create mode 100644 drivers/gpu/drm/st/ltdc.c create mode 100644 drivers/gpu/drm/st/ltdc.h diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 6f3f9e6..d8e6f92 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -237,6 +237,8 @@ source "drivers/gpu/drm/fsl-dcu/Kconfig" source "drivers/gpu/drm/tegra/Kconfig" +source "drivers/gpu/drm/st/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 92de399..7434c09 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_ST) += st/ obj-$(CONFIG_DRM_STI) += sti/ obj-$(CONFIG_DRM_IMX) += imx/ obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ diff --git a/drivers/gpu/drm/st/Kconfig b/drivers/gpu/drm/st/Kconfig new file mode 100644 index 0000000..fa0ac0c --- /dev/null +++ b/drivers/gpu/drm/st/Kconfig @@ -0,0 +1,14 @@ +config DRM_ST + 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 + help + Choose this option if you have an ST STMicroelectronics SoC. + + To compile this driver as a module, choose M here: the module + will be called st-drm. diff --git a/drivers/gpu/drm/st/Makefile b/drivers/gpu/drm/st/Makefile new file mode 100644 index 0000000..b2a9025 --- /dev/null +++ b/drivers/gpu/drm/st/Makefile @@ -0,0 +1,7 @@ +ccflags-y := -Iinclude/drm + +st-drm-y := \ + drv.o \ + ltdc.o + +obj-$(CONFIG_DRM_ST) += st-drm.o diff --git a/drivers/gpu/drm/st/drv.c b/drivers/gpu/drm/st/drv.c new file mode 100644 index 0000000..a275019 --- /dev/null +++ b/drivers/gpu/drm/st/drv.c @@ -0,0 +1,279 @@ +/* + * 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 "st" +#define DRIVER_DESC "STMicroelectronics SoC DRM" +#define DRIVER_DATE "20170110" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +#define ST_MAX_FB_WIDTH 2048 +#define ST_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */ + +static void st_output_poll_changed(struct drm_device *ddev) +{ + struct st_private *priv = ddev->dev_private; + + drm_fbdev_cma_hotplug_event(priv->fbdev); +} + +static const struct drm_mode_config_funcs st_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + .output_poll_changed = st_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void st_mode_config_init(struct drm_device *ddev) +{ + ddev->mode_config.min_width = 0; + ddev->mode_config.min_height = 0; + + /* + * 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.max_width = ST_MAX_FB_WIDTH; + ddev->mode_config.max_height = ST_MAX_FB_HEIGHT; + ddev->mode_config.funcs = &st_mode_config_funcs; +} + +static const struct file_operations st_driver_fops = { + .owner = THIS_MODULE, + .open = drm_open, + .get_unmapped_area = drm_gem_cma_get_unmapped_area, + .mmap = drm_gem_cma_mmap, + .poll = drm_poll, + .read = drm_read, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .release = drm_release, +}; + +static struct drm_driver st_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | + DRIVER_ATOMIC, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + .fops = &st_driver_fops, + + .get_vblank_counter = drm_vblank_no_hw_counter, + .enable_vblank = ltdc_crtc_enable_vblank, + .disable_vblank = ltdc_crtc_disable_vblank, + + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .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, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int st_init(struct drm_device *ddev) +{ + struct st_private *priv; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + ddev->dev_private = (void *)priv; + dev_set_drvdata(ddev->dev, ddev); + priv->ddev = ddev; + + drm_mode_config_init(ddev); + + st_mode_config_init(ddev); + + drm_kms_helper_poll_init(ddev); + + return 0; +} + +static void st_cleanup(struct drm_device *ddev) +{ + struct st_private *priv = ddev->dev_private; + + if (priv->fbdev) { + drm_fbdev_cma_fini(priv->fbdev); + priv->fbdev = NULL; + } + + drm_kms_helper_poll_fini(ddev); + drm_vblank_cleanup(ddev); + kfree(priv); + ddev->dev_private = NULL; +} + +static int st_bind(struct device *dev) +{ + struct drm_device *ddev; + struct st_private *priv; + struct drm_fbdev_cma *fbdev; + int ret; + + ddev = drm_dev_alloc(&st_driver, dev); + if (IS_ERR(ddev)) + return PTR_ERR(ddev); + + ddev->platformdev = to_platform_device(dev); + + ret = st_init(ddev); + if (ret) + goto err_drm_dev_unref; + + ret = component_bind_all(ddev->dev, ddev); + if (ret) + goto err_cleanup; + + ret = drm_dev_register(ddev, 0); + if (ret) + goto err_register; + + drm_mode_config_reset(ddev); + + if (ddev->mode_config.num_connector) { + priv = ddev->dev_private; + fbdev = drm_fbdev_cma_init(ddev, 16, + ddev->mode_config.num_crtc, + ddev->mode_config.num_connector); + if (IS_ERR(fbdev)) { + DRM_DEBUG_DRIVER("Warning: fails to create fbdev\n"); + fbdev = NULL; + } + priv->fbdev = fbdev; + } + + return 0; + +err_register: + component_unbind_all(ddev->dev, ddev); +err_cleanup: + st_cleanup(ddev); +err_drm_dev_unref: + drm_dev_unref(ddev); + return ret; +} + +static void st_unbind(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + + drm_dev_unregister(ddev); + st_cleanup(ddev); + drm_dev_unref(ddev); +} + +static const struct component_master_ops st_ops = { + .bind = st_bind, + .unbind = st_unbind, +}; + +static int st_platform_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct device_node *child_np; + struct component_match *match = NULL; + + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); + + of_platform_populate(node, NULL, NULL, dev); + + child_np = of_get_next_available_child(node, NULL); + + while (child_np) { + component_match_add(dev, &match, compare_of, child_np); + of_node_put(child_np); + child_np = of_get_next_available_child(node, child_np); + } + + return component_master_add_with_match(dev, &st_ops, match); +} + +static int st_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &st_ops); + of_platform_depopulate(&pdev->dev); + + return 0; +} + +static const struct of_device_id st_dt_ids[] = { + { .compatible = "st,display-subsystem", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, st_dt_ids); + +static struct platform_driver st_platform_driver = { + .probe = st_platform_probe, + .remove = st_platform_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = st_dt_ids, + }, +}; + +static struct platform_driver * const drivers[] = { + <dc_driver, + &st_platform_driver, +}; + +static int st_drm_init(void) +{ + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_init(st_drm_init); + +static void st_drm_exit(void) +{ + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_exit(st_drm_exit); + +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/st/drv.h b/drivers/gpu/drm/st/drv.h new file mode 100644 index 0000000..d3da1bc --- /dev/null +++ b/drivers/gpu/drm/st/drv.h @@ -0,0 +1,25 @@ +/* + * 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 st_private { + struct ltdc *ltdc; + struct drm_device *ddev; + struct drm_fbdev_cma *fbdev; +}; + +extern struct platform_driver ltdc_driver; + +#endif diff --git a/drivers/gpu/drm/st/ltdc.c b/drivers/gpu/drm/st/ltdc.c new file mode 100644 index 0000000..46a7ab2 --- /dev/null +++ b/drivers/gpu/drm/st/ltdc.c @@ -0,0 +1,1438 @@ +/* + * 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