diff options
Diffstat (limited to 'drivers/gpu/drm/stm/drv.c')
| -rw-r--r-- | drivers/gpu/drm/stm/drv.c | 231 |
1 files changed, 142 insertions, 89 deletions
diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index 83ab48f1fd00..56d53ac3082d 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) STMicroelectronics SA 2017 * @@ -5,101 +6,88 @@ * Yannick Fertre <yannick.fertre@st.com> * Fabien Dessenne <fabien.dessenne@st.com> * Mickael Reulier <mickael.reulier@st.com> - * - * License terms: GNU General Public License (GPL), version 2 */ +#include <linux/aperture.h> #include <linux/component.h> -#include <linux/of_platform.h> +#include <linux/dma-mapping.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <drm/clients/drm_client_setup.h> #include <drm/drm_atomic.h> #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> -#include <drm/drm_fb_cma_helper.h> -#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_drv.h> +#include <drm/drm_fbdev_dma.h> +#include <drm/drm_fourcc.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_module.h> +#include <drm/drm_print.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_vblank.h> +#include <drm/drm_managed.h> #include "ltdc.h" -#define DRIVER_NAME "stm" -#define DRIVER_DESC "STMicroelectronics SoC DRM" -#define DRIVER_DATE "20170330" -#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 ltdc_device *ldev = ddev->dev_private; - - drm_fbdev_cma_hotplug_event(ldev->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, + .fb_create = drm_gem_fb_create, .atomic_check = drm_atomic_helper_check, .atomic_commit = drm_atomic_helper_commit, }; -static void drv_lastclose(struct drm_device *ddev) +static int stm_gem_dma_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) { - struct ltdc_device *ldev = ddev->dev_private; + unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); - DRM_DEBUG("%s\n", __func__); + /* + * in order to optimize data transfer, pitch is aligned on + * 128 bytes, height is aligned on 4 bytes + */ + args->pitch = roundup(min_pitch, 128); + args->height = roundup(args->height, 4); - drm_fbdev_cma_restore_mode(ldev->fbdev); + return drm_gem_dma_dumb_create_internal(file, dev, args); } -DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops); - -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, +DEFINE_DRM_GEM_DMA_FOPS(drv_driver_fops); + +static const struct drm_driver drv_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .name = "stm", + .desc = "STMicroelectronics SoC DRM", + .major = 1, + .minor = 0, + .patchlevel = 0, .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, + DRM_GEM_DMA_DRIVER_OPS_WITH_DUMB_CREATE(stm_gem_dma_dumb_create), + DRM_FBDEV_DMA_DRIVER_OPS, }; static int drv_load(struct drm_device *ddev) { struct platform_device *pdev = to_platform_device(ddev->dev); - struct drm_fbdev_cma *fbdev; struct ltdc_device *ldev; int ret; DRM_DEBUG("%s\n", __func__); - ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL); + ldev = drmm_kzalloc(ddev, sizeof(*ldev), GFP_KERNEL); if (!ldev) return -ENOMEM; ddev->dev_private = (void *)ldev; - drm_mode_config_init(ddev); + ret = drmm_mode_config_init(ddev); + if (ret) + return ret; /* * set max width and height as default value. @@ -111,48 +99,90 @@ static int drv_load(struct drm_device *ddev) 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; + ddev->mode_config.normalize_zpos = true; ret = ltdc_load(ddev); if (ret) - goto err; + return ret; drm_mode_config_reset(ddev); drm_kms_helper_poll_init(ddev); - if (ddev->mode_config.num_connector) { - ldev = 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; - } - ldev->fbdev = fbdev; - } - platform_set_drvdata(pdev, ddev); return 0; -err: - drm_mode_config_cleanup(ddev); - return ret; } static void drv_unload(struct drm_device *ddev) { - struct ltdc_device *ldev = ddev->dev_private; - DRM_DEBUG("%s\n", __func__); - if (ldev->fbdev) { - drm_fbdev_cma_fini(ldev->fbdev); - ldev->fbdev = NULL; - } drm_kms_helper_poll_fini(ddev); + drm_atomic_helper_shutdown(ddev); ltdc_unload(ddev); - drm_mode_config_cleanup(ddev); } +static __maybe_unused int drv_suspend(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct ltdc_device *ldev = ddev->dev_private; + struct drm_atomic_state *state; + + WARN_ON(ldev->suspend_state); + + state = drm_atomic_helper_suspend(ddev); + if (IS_ERR(state)) + return PTR_ERR(state); + + ldev->suspend_state = state; + pm_runtime_force_suspend(dev); + + return 0; +} + +static __maybe_unused int drv_resume(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + struct ltdc_device *ldev = ddev->dev_private; + int ret; + + if (WARN_ON(!ldev->suspend_state)) + return -ENOENT; + + pm_runtime_force_resume(dev); + ret = drm_atomic_helper_resume(ddev, ldev->suspend_state); + if (ret) + pm_runtime_force_suspend(dev); + + ldev->suspend_state = NULL; + + return ret; +} + +static __maybe_unused int drv_runtime_suspend(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + + DRM_DEBUG_DRIVER("\n"); + ltdc_suspend(ddev); + + return 0; +} + +static __maybe_unused int drv_runtime_resume(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + + DRM_DEBUG_DRIVER("\n"); + return ltdc_resume(ddev); +} + +static const struct dev_pm_ops drv_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(drv_suspend, drv_resume) + SET_RUNTIME_PM_OPS(drv_runtime_suspend, + drv_runtime_resume, NULL) +}; + static int stm_drm_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -161,6 +191,10 @@ static int stm_drm_platform_probe(struct platform_device *pdev) DRM_DEBUG("%s\n", __func__); + ret = aperture_remove_all_conflicting_devices(drv_driver.name); + if (ret) + return ret; + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); ddev = drm_dev_alloc(&drv_driver, dev); @@ -169,21 +203,25 @@ static int stm_drm_platform_probe(struct platform_device *pdev) ret = drv_load(ddev); if (ret) - goto err_unref; + goto err_put; ret = drm_dev_register(ddev, 0); if (ret) - goto err_unref; + goto err_unload; + + drm_client_setup_with_fourcc(ddev, DRM_FORMAT_RGB565); return 0; -err_unref: - drm_dev_unref(ddev); +err_unload: + drv_unload(ddev); +err_put: + drm_dev_put(ddev); return ret; } -static int stm_drm_platform_remove(struct platform_device *pdev) +static void stm_drm_platform_remove(struct platform_device *pdev) { struct drm_device *ddev = platform_get_drvdata(pdev); @@ -191,13 +229,26 @@ static int stm_drm_platform_remove(struct platform_device *pdev) drm_dev_unregister(ddev); drv_unload(ddev); - drm_dev_unref(ddev); + drm_dev_put(ddev); +} - return 0; +static void stm_drm_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); } +static struct ltdc_plat_data stm_drm_plat_data = { + .pad_max_freq_hz = 90000000, +}; + +static struct ltdc_plat_data stm_drm_plat_data_mp25 = { + .pad_max_freq_hz = 150000000, +}; + static const struct of_device_id drv_dt_ids[] = { - { .compatible = "st,stm32-ltdc"}, + { .compatible = "st,stm32-ltdc", .data = &stm_drm_plat_data, }, + { .compatible = "st,stm32mp251-ltdc", .data = &stm_drm_plat_data_mp25, }, + { .compatible = "st,stm32mp255-ltdc", .data = &stm_drm_plat_data_mp25, }, { /* end node */ }, }; MODULE_DEVICE_TABLE(of, drv_dt_ids); @@ -205,13 +256,15 @@ 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, + .shutdown = stm_drm_platform_shutdown, .driver = { - .name = DRIVER_NAME, + .name = "stm32-display", .of_match_table = drv_dt_ids, + .pm = &drv_pm_ops, }, }; -module_platform_driver(stm_drm_platform_driver); +drm_module_platform_driver(stm_drm_platform_driver); MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); |
