diff options
Diffstat (limited to 'drivers/gpu/drm/sti/sti_drv.c')
-rw-r--r-- | drivers/gpu/drm/sti/sti_drv.c | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c new file mode 100644 index 000000000..7abf010a3 --- /dev/null +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics SA 2014 + * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics. + */ + +#include <linux/component.h> +#include <linux/dma-mapping.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_platform.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_debugfs.h> +#include <drm/drm_drv.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_gem_dma_helper.h> +#include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_probe_helper.h> + +#include "sti_drv.h" +#include "sti_plane.h" + +#define DRIVER_NAME "sti" +#define DRIVER_DESC "STMicroelectronics SoC DRM" +#define DRIVER_DATE "20140601" +#define DRIVER_MAJOR 1 +#define DRIVER_MINOR 0 + +#define STI_MAX_FB_HEIGHT 4096 +#define STI_MAX_FB_WIDTH 4096 + +static int sti_drm_fps_get(void *data, u64 *val) +{ + struct drm_device *drm_dev = data; + struct drm_plane *p; + unsigned int i = 0; + + *val = 0; + list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { + struct sti_plane *plane = to_sti_plane(p); + + *val |= plane->fps_info.output << i; + i++; + } + + return 0; +} + +static int sti_drm_fps_set(void *data, u64 val) +{ + struct drm_device *drm_dev = data; + struct drm_plane *p; + unsigned int i = 0; + + list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) { + struct sti_plane *plane = to_sti_plane(p); + + memset(&plane->fps_info, 0, sizeof(plane->fps_info)); + plane->fps_info.output = (val >> i) & 1; + + i++; + } + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops, + sti_drm_fps_get, sti_drm_fps_set, "%llu\n"); + +static int sti_drm_fps_dbg_show(struct seq_file *s, void *data) +{ + struct drm_info_node *node = s->private; + struct drm_device *dev = node->minor->dev; + struct drm_plane *p; + + list_for_each_entry(p, &dev->mode_config.plane_list, head) { + struct sti_plane *plane = to_sti_plane(p); + + seq_printf(s, "%s%s\n", + plane->fps_info.fps_str, + plane->fps_info.fips_str); + } + + return 0; +} + +static struct drm_info_list sti_drm_dbg_list[] = { + {"fps_get", sti_drm_fps_dbg_show, 0}, +}; + +static void sti_drm_dbg_init(struct drm_minor *minor) +{ + drm_debugfs_create_files(sti_drm_dbg_list, + ARRAY_SIZE(sti_drm_dbg_list), + minor->debugfs_root, minor); + + debugfs_create_file("fps_show", S_IRUGO | S_IWUSR, minor->debugfs_root, + minor->dev, &sti_drm_fps_fops); + + DRM_INFO("%s: debugfs installed\n", DRIVER_NAME); +} + +static const struct drm_mode_config_funcs sti_mode_config_funcs = { + .fb_create = drm_gem_fb_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void sti_mode_config_init(struct drm_device *dev) +{ + dev->mode_config.min_width = 0; + dev->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(). + */ + dev->mode_config.max_width = STI_MAX_FB_WIDTH; + dev->mode_config.max_height = STI_MAX_FB_HEIGHT; + + dev->mode_config.funcs = &sti_mode_config_funcs; + + dev->mode_config.normalize_zpos = true; +} + +DEFINE_DRM_GEM_DMA_FOPS(sti_driver_fops); + +static const struct drm_driver sti_driver = { + .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC, + .fops = &sti_driver_fops, + DRM_GEM_DMA_DRIVER_OPS, + + .debugfs_init = sti_drm_dbg_init, + + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = DRIVER_DATE, + .major = DRIVER_MAJOR, + .minor = DRIVER_MINOR, +}; + +static int sti_init(struct drm_device *ddev) +{ + struct sti_private *private; + + private = kzalloc(sizeof(*private), GFP_KERNEL); + if (!private) + return -ENOMEM; + + ddev->dev_private = (void *)private; + dev_set_drvdata(ddev->dev, ddev); + private->drm_dev = ddev; + + drm_mode_config_init(ddev); + + sti_mode_config_init(ddev); + + drm_kms_helper_poll_init(ddev); + + return 0; +} + +static void sti_cleanup(struct drm_device *ddev) +{ + struct sti_private *private = ddev->dev_private; + + drm_kms_helper_poll_fini(ddev); + drm_atomic_helper_shutdown(ddev); + drm_mode_config_cleanup(ddev); + component_unbind_all(ddev->dev, ddev); + kfree(private); + ddev->dev_private = NULL; +} + +static int sti_bind(struct device *dev) +{ + struct drm_device *ddev; + int ret; + + ddev = drm_dev_alloc(&sti_driver, dev); + if (IS_ERR(ddev)) + return PTR_ERR(ddev); + + ret = sti_init(ddev); + if (ret) + goto err_drm_dev_put; + + ret = component_bind_all(ddev->dev, ddev); + if (ret) + goto err_cleanup; + + ret = drm_dev_register(ddev, 0); + if (ret) + goto err_cleanup; + + drm_mode_config_reset(ddev); + + drm_fbdev_generic_setup(ddev, 32); + + return 0; + +err_cleanup: + sti_cleanup(ddev); +err_drm_dev_put: + drm_dev_put(ddev); + return ret; +} + +static void sti_unbind(struct device *dev) +{ + struct drm_device *ddev = dev_get_drvdata(dev); + + drm_dev_unregister(ddev); + sti_cleanup(ddev); + drm_dev_put(ddev); +} + +static const struct component_master_ops sti_ops = { + .bind = sti_bind, + .unbind = sti_unbind, +}; + +static int sti_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)); + + devm_of_platform_populate(dev); + + child_np = of_get_next_available_child(node, NULL); + + while (child_np) { + drm_of_component_match_add(dev, &match, component_compare_of, + child_np); + child_np = of_get_next_available_child(node, child_np); + } + + return component_master_add_with_match(dev, &sti_ops, match); +} + +static int sti_platform_remove(struct platform_device *pdev) +{ + component_master_del(&pdev->dev, &sti_ops); + + return 0; +} + +static const struct of_device_id sti_dt_ids[] = { + { .compatible = "st,sti-display-subsystem", }, + { /* end node */ }, +}; +MODULE_DEVICE_TABLE(of, sti_dt_ids); + +static struct platform_driver sti_platform_driver = { + .probe = sti_platform_probe, + .remove = sti_platform_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = sti_dt_ids, + }, +}; + +static struct platform_driver * const drivers[] = { + &sti_tvout_driver, + &sti_hqvdp_driver, + &sti_hdmi_driver, + &sti_hda_driver, + &sti_dvo_driver, + &sti_vtg_driver, + &sti_compositor_driver, + &sti_platform_driver, +}; + +static int sti_drm_init(void) +{ + if (drm_firmware_drivers_only()) + return -ENODEV; + + return platform_register_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_init(sti_drm_init); + +static void sti_drm_exit(void) +{ + platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); +} +module_exit(sti_drm_exit); + +MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>"); +MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver"); +MODULE_LICENSE("GPL"); |