1e2842570SBenjamin Gaignard // SPDX-License-Identifier: GPL-2.0
29e1f05b2SVincent Abriou /*
39e1f05b2SVincent Abriou * Copyright (C) STMicroelectronics SA 2014
49e1f05b2SVincent Abriou * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
59e1f05b2SVincent Abriou */
69e1f05b2SVincent Abriou
79e1f05b2SVincent Abriou #include <linux/component.h>
85e2f97a9SSam Ravnborg #include <linux/dma-mapping.h>
99e1f05b2SVincent Abriou #include <linux/kernel.h>
109e1f05b2SVincent Abriou #include <linux/module.h>
11*722d4f06SRob Herring #include <linux/of.h>
129e1f05b2SVincent Abriou #include <linux/of_platform.h>
13*722d4f06SRob Herring #include <linux/platform_device.h>
149e1f05b2SVincent Abriou
159e1f05b2SVincent Abriou #include <drm/drm_atomic.h>
169e1f05b2SVincent Abriou #include <drm/drm_atomic_helper.h>
175e2f97a9SSam Ravnborg #include <drm/drm_debugfs.h>
185e2f97a9SSam Ravnborg #include <drm/drm_drv.h>
19000a0134SThomas Zimmermann #include <drm/drm_fbdev_dma.h>
204a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
2180cad29fSNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
2297ac0e47SRussell King #include <drm/drm_of.h>
23fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
249e1f05b2SVincent Abriou
259e1f05b2SVincent Abriou #include "sti_drv.h"
26bf8f9e4aSVincent Abriou #include "sti_plane.h"
279e1f05b2SVincent Abriou
289e1f05b2SVincent Abriou #define DRIVER_NAME "sti"
299e1f05b2SVincent Abriou #define DRIVER_DESC "STMicroelectronics SoC DRM"
309e1f05b2SVincent Abriou #define DRIVER_DATE "20140601"
319e1f05b2SVincent Abriou #define DRIVER_MAJOR 1
329e1f05b2SVincent Abriou #define DRIVER_MINOR 0
339e1f05b2SVincent Abriou
349e1f05b2SVincent Abriou #define STI_MAX_FB_HEIGHT 4096
359e1f05b2SVincent Abriou #define STI_MAX_FB_WIDTH 4096
369e1f05b2SVincent Abriou
sti_drm_fps_get(void * data,u64 * val)37bf8f9e4aSVincent Abriou static int sti_drm_fps_get(void *data, u64 *val)
38bf8f9e4aSVincent Abriou {
39bf8f9e4aSVincent Abriou struct drm_device *drm_dev = data;
40bf8f9e4aSVincent Abriou struct drm_plane *p;
41bf8f9e4aSVincent Abriou unsigned int i = 0;
42bf8f9e4aSVincent Abriou
43bf8f9e4aSVincent Abriou *val = 0;
44bf8f9e4aSVincent Abriou list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) {
45bf8f9e4aSVincent Abriou struct sti_plane *plane = to_sti_plane(p);
46bf8f9e4aSVincent Abriou
47bf8f9e4aSVincent Abriou *val |= plane->fps_info.output << i;
48bf8f9e4aSVincent Abriou i++;
49bf8f9e4aSVincent Abriou }
50bf8f9e4aSVincent Abriou
51bf8f9e4aSVincent Abriou return 0;
52bf8f9e4aSVincent Abriou }
53bf8f9e4aSVincent Abriou
sti_drm_fps_set(void * data,u64 val)54bf8f9e4aSVincent Abriou static int sti_drm_fps_set(void *data, u64 val)
55bf8f9e4aSVincent Abriou {
56bf8f9e4aSVincent Abriou struct drm_device *drm_dev = data;
57bf8f9e4aSVincent Abriou struct drm_plane *p;
58bf8f9e4aSVincent Abriou unsigned int i = 0;
59bf8f9e4aSVincent Abriou
60bf8f9e4aSVincent Abriou list_for_each_entry(p, &drm_dev->mode_config.plane_list, head) {
61bf8f9e4aSVincent Abriou struct sti_plane *plane = to_sti_plane(p);
62bf8f9e4aSVincent Abriou
63c462c2f5SVincent Abriou memset(&plane->fps_info, 0, sizeof(plane->fps_info));
64bf8f9e4aSVincent Abriou plane->fps_info.output = (val >> i) & 1;
65c462c2f5SVincent Abriou
66bf8f9e4aSVincent Abriou i++;
67bf8f9e4aSVincent Abriou }
68bf8f9e4aSVincent Abriou
69bf8f9e4aSVincent Abriou return 0;
70bf8f9e4aSVincent Abriou }
71bf8f9e4aSVincent Abriou
72bf8f9e4aSVincent Abriou DEFINE_SIMPLE_ATTRIBUTE(sti_drm_fps_fops,
73bf8f9e4aSVincent Abriou sti_drm_fps_get, sti_drm_fps_set, "%llu\n");
74bf8f9e4aSVincent Abriou
sti_drm_fps_dbg_show(struct seq_file * s,void * data)75bf8f9e4aSVincent Abriou static int sti_drm_fps_dbg_show(struct seq_file *s, void *data)
76bf8f9e4aSVincent Abriou {
77bf8f9e4aSVincent Abriou struct drm_info_node *node = s->private;
78bf8f9e4aSVincent Abriou struct drm_device *dev = node->minor->dev;
79bf8f9e4aSVincent Abriou struct drm_plane *p;
80bf8f9e4aSVincent Abriou
81bf8f9e4aSVincent Abriou list_for_each_entry(p, &dev->mode_config.plane_list, head) {
82bf8f9e4aSVincent Abriou struct sti_plane *plane = to_sti_plane(p);
83bf8f9e4aSVincent Abriou
84bf8f9e4aSVincent Abriou seq_printf(s, "%s%s\n",
85bf8f9e4aSVincent Abriou plane->fps_info.fps_str,
86bf8f9e4aSVincent Abriou plane->fps_info.fips_str);
87bf8f9e4aSVincent Abriou }
88bf8f9e4aSVincent Abriou
89bf8f9e4aSVincent Abriou return 0;
90bf8f9e4aSVincent Abriou }
91bf8f9e4aSVincent Abriou
92bf8f9e4aSVincent Abriou static struct drm_info_list sti_drm_dbg_list[] = {
93bf8f9e4aSVincent Abriou {"fps_get", sti_drm_fps_dbg_show, 0},
94bf8f9e4aSVincent Abriou };
95bf8f9e4aSVincent Abriou
sti_drm_dbg_init(struct drm_minor * minor)967ce84471SWambui Karuga static void sti_drm_dbg_init(struct drm_minor *minor)
97bf8f9e4aSVincent Abriou {
9854ac836bSWambui Karuga drm_debugfs_create_files(sti_drm_dbg_list,
99bf8f9e4aSVincent Abriou ARRAY_SIZE(sti_drm_dbg_list),
100bf8f9e4aSVincent Abriou minor->debugfs_root, minor);
101bf8f9e4aSVincent Abriou
102150c6624SGreg Kroah-Hartman debugfs_create_file("fps_show", S_IRUGO | S_IWUSR, minor->debugfs_root,
103150c6624SGreg Kroah-Hartman minor->dev, &sti_drm_fps_fops);
104bf8f9e4aSVincent Abriou
105bf8f9e4aSVincent Abriou DRM_INFO("%s: debugfs installed\n", DRIVER_NAME);
106bf8f9e4aSVincent Abriou }
107bf8f9e4aSVincent Abriou
108c5de4853SVille Syrjälä static const struct drm_mode_config_funcs sti_mode_config_funcs = {
10980cad29fSNoralf Trønnes .fb_create = drm_gem_fb_create,
110352f9a84SPeter Ujfalusi .atomic_check = drm_atomic_helper_check,
1115e60f595SFabien DESSENNE .atomic_commit = drm_atomic_helper_commit,
1129e1f05b2SVincent Abriou };
1139e1f05b2SVincent Abriou
sti_mode_config_init(struct drm_device * dev)1149e1f05b2SVincent Abriou static void sti_mode_config_init(struct drm_device *dev)
1159e1f05b2SVincent Abriou {
1169e1f05b2SVincent Abriou dev->mode_config.min_width = 0;
1179e1f05b2SVincent Abriou dev->mode_config.min_height = 0;
1189e1f05b2SVincent Abriou
1199e1f05b2SVincent Abriou /*
1209e1f05b2SVincent Abriou * set max width and height as default value.
1219e1f05b2SVincent Abriou * this value would be used to check framebuffer size limitation
1229e1f05b2SVincent Abriou * at drm_mode_addfb().
1239e1f05b2SVincent Abriou */
124738be9d6SVincent Abriou dev->mode_config.max_width = STI_MAX_FB_WIDTH;
125738be9d6SVincent Abriou dev->mode_config.max_height = STI_MAX_FB_HEIGHT;
1269e1f05b2SVincent Abriou
1279e1f05b2SVincent Abriou dev->mode_config.funcs = &sti_mode_config_funcs;
128352f9a84SPeter Ujfalusi
129352f9a84SPeter Ujfalusi dev->mode_config.normalize_zpos = true;
1309e1f05b2SVincent Abriou }
1319e1f05b2SVincent Abriou
1324a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(sti_driver_fops);
1339e1f05b2SVincent Abriou
13470a59dd8SDaniel Vetter static const struct drm_driver sti_driver = {
1350424fdafSDaniel Vetter .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
1369e1f05b2SVincent Abriou .fops = &sti_driver_fops,
1374a83c26aSDanilo Krummrich DRM_GEM_DMA_DRIVER_OPS,
1389e1f05b2SVincent Abriou
139bf8f9e4aSVincent Abriou .debugfs_init = sti_drm_dbg_init,
140bf8f9e4aSVincent Abriou
1419e1f05b2SVincent Abriou .name = DRIVER_NAME,
1429e1f05b2SVincent Abriou .desc = DRIVER_DESC,
1439e1f05b2SVincent Abriou .date = DRIVER_DATE,
1449e1f05b2SVincent Abriou .major = DRIVER_MAJOR,
1459e1f05b2SVincent Abriou .minor = DRIVER_MINOR,
1469e1f05b2SVincent Abriou };
1479e1f05b2SVincent Abriou
sti_init(struct drm_device * ddev)14884601dbdSBenjamin Gaignard static int sti_init(struct drm_device *ddev)
14984601dbdSBenjamin Gaignard {
15084601dbdSBenjamin Gaignard struct sti_private *private;
15184601dbdSBenjamin Gaignard
15284601dbdSBenjamin Gaignard private = kzalloc(sizeof(*private), GFP_KERNEL);
15384601dbdSBenjamin Gaignard if (!private)
15484601dbdSBenjamin Gaignard return -ENOMEM;
15584601dbdSBenjamin Gaignard
15684601dbdSBenjamin Gaignard ddev->dev_private = (void *)private;
15784601dbdSBenjamin Gaignard dev_set_drvdata(ddev->dev, ddev);
15884601dbdSBenjamin Gaignard private->drm_dev = ddev;
15984601dbdSBenjamin Gaignard
16084601dbdSBenjamin Gaignard drm_mode_config_init(ddev);
16184601dbdSBenjamin Gaignard
16284601dbdSBenjamin Gaignard sti_mode_config_init(ddev);
16384601dbdSBenjamin Gaignard
16484601dbdSBenjamin Gaignard drm_kms_helper_poll_init(ddev);
16584601dbdSBenjamin Gaignard
16684601dbdSBenjamin Gaignard return 0;
16784601dbdSBenjamin Gaignard }
16884601dbdSBenjamin Gaignard
sti_cleanup(struct drm_device * ddev)16984601dbdSBenjamin Gaignard static void sti_cleanup(struct drm_device *ddev)
17084601dbdSBenjamin Gaignard {
17184601dbdSBenjamin Gaignard struct sti_private *private = ddev->dev_private;
17284601dbdSBenjamin Gaignard
17384601dbdSBenjamin Gaignard drm_kms_helper_poll_fini(ddev);
174a2b50babSDaniel Vetter drm_atomic_helper_shutdown(ddev);
175a2b50babSDaniel Vetter drm_mode_config_cleanup(ddev);
176b7e05db3SVincent Abriou component_unbind_all(ddev->dev, ddev);
17784601dbdSBenjamin Gaignard kfree(private);
17884601dbdSBenjamin Gaignard ddev->dev_private = NULL;
17984601dbdSBenjamin Gaignard }
18084601dbdSBenjamin Gaignard
sti_bind(struct device * dev)1819e1f05b2SVincent Abriou static int sti_bind(struct device *dev)
1829e1f05b2SVincent Abriou {
18384601dbdSBenjamin Gaignard struct drm_device *ddev;
18484601dbdSBenjamin Gaignard int ret;
18584601dbdSBenjamin Gaignard
18684601dbdSBenjamin Gaignard ddev = drm_dev_alloc(&sti_driver, dev);
1870f288605STom Gundersen if (IS_ERR(ddev))
1880f288605STom Gundersen return PTR_ERR(ddev);
18984601dbdSBenjamin Gaignard
19084601dbdSBenjamin Gaignard ret = sti_init(ddev);
19184601dbdSBenjamin Gaignard if (ret)
192a08eac47SThomas Zimmermann goto err_drm_dev_put;
19384601dbdSBenjamin Gaignard
19484601dbdSBenjamin Gaignard ret = component_bind_all(ddev->dev, ddev);
19584601dbdSBenjamin Gaignard if (ret)
19684601dbdSBenjamin Gaignard goto err_cleanup;
19784601dbdSBenjamin Gaignard
19884601dbdSBenjamin Gaignard ret = drm_dev_register(ddev, 0);
19984601dbdSBenjamin Gaignard if (ret)
200a2b50babSDaniel Vetter goto err_cleanup;
20184601dbdSBenjamin Gaignard
20284601dbdSBenjamin Gaignard drm_mode_config_reset(ddev);
20384601dbdSBenjamin Gaignard
204000a0134SThomas Zimmermann drm_fbdev_dma_setup(ddev, 32);
2058953e9eeSVincent Abriou
20684601dbdSBenjamin Gaignard return 0;
20784601dbdSBenjamin Gaignard
20884601dbdSBenjamin Gaignard err_cleanup:
20984601dbdSBenjamin Gaignard sti_cleanup(ddev);
210a08eac47SThomas Zimmermann err_drm_dev_put:
211a08eac47SThomas Zimmermann drm_dev_put(ddev);
21284601dbdSBenjamin Gaignard return ret;
2139e1f05b2SVincent Abriou }
2149e1f05b2SVincent Abriou
sti_unbind(struct device * dev)2159e1f05b2SVincent Abriou static void sti_unbind(struct device *dev)
2169e1f05b2SVincent Abriou {
21784601dbdSBenjamin Gaignard struct drm_device *ddev = dev_get_drvdata(dev);
21884601dbdSBenjamin Gaignard
21984601dbdSBenjamin Gaignard drm_dev_unregister(ddev);
22084601dbdSBenjamin Gaignard sti_cleanup(ddev);
221a08eac47SThomas Zimmermann drm_dev_put(ddev);
2229e1f05b2SVincent Abriou }
2239e1f05b2SVincent Abriou
2249e1f05b2SVincent Abriou static const struct component_master_ops sti_ops = {
2259e1f05b2SVincent Abriou .bind = sti_bind,
2269e1f05b2SVincent Abriou .unbind = sti_unbind,
2279e1f05b2SVincent Abriou };
2289e1f05b2SVincent Abriou
sti_platform_probe(struct platform_device * pdev)2299e1f05b2SVincent Abriou static int sti_platform_probe(struct platform_device *pdev)
2309e1f05b2SVincent Abriou {
2319e1f05b2SVincent Abriou struct device *dev = &pdev->dev;
2329e1f05b2SVincent Abriou struct device_node *node = dev->of_node;
2339e1f05b2SVincent Abriou struct device_node *child_np;
2349e1f05b2SVincent Abriou struct component_match *match = NULL;
2359e1f05b2SVincent Abriou
2369e1f05b2SVincent Abriou dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
2379e1f05b2SVincent Abriou
238ac7d3af8SBenjamin Gaignard devm_of_platform_populate(dev);
2399e1f05b2SVincent Abriou
2409e1f05b2SVincent Abriou child_np = of_get_next_available_child(node, NULL);
2419e1f05b2SVincent Abriou
2429e1f05b2SVincent Abriou while (child_np) {
24350a912a8SYong Wu drm_of_component_match_add(dev, &match, component_compare_of,
24497ac0e47SRussell King child_np);
2459e1f05b2SVincent Abriou child_np = of_get_next_available_child(node, child_np);
2469e1f05b2SVincent Abriou }
2479e1f05b2SVincent Abriou
2489e1f05b2SVincent Abriou return component_master_add_with_match(dev, &sti_ops, match);
2499e1f05b2SVincent Abriou }
2509e1f05b2SVincent Abriou
sti_platform_remove(struct platform_device * pdev)2519a865e45SUwe Kleine-König static void sti_platform_remove(struct platform_device *pdev)
2529e1f05b2SVincent Abriou {
2539e1f05b2SVincent Abriou component_master_del(&pdev->dev, &sti_ops);
2549e1f05b2SVincent Abriou }
2559e1f05b2SVincent Abriou
2569e1f05b2SVincent Abriou static const struct of_device_id sti_dt_ids[] = {
2579e1f05b2SVincent Abriou { .compatible = "st,sti-display-subsystem", },
2589e1f05b2SVincent Abriou { /* end node */ },
2599e1f05b2SVincent Abriou };
2609e1f05b2SVincent Abriou MODULE_DEVICE_TABLE(of, sti_dt_ids);
2619e1f05b2SVincent Abriou
2629e1f05b2SVincent Abriou static struct platform_driver sti_platform_driver = {
2639e1f05b2SVincent Abriou .probe = sti_platform_probe,
2649a865e45SUwe Kleine-König .remove_new = sti_platform_remove,
2659e1f05b2SVincent Abriou .driver = {
2669e1f05b2SVincent Abriou .name = DRIVER_NAME,
2679e1f05b2SVincent Abriou .of_match_table = sti_dt_ids,
2689e1f05b2SVincent Abriou },
2699e1f05b2SVincent Abriou };
2709e1f05b2SVincent Abriou
271dcec16efSThierry Reding static struct platform_driver * const drivers[] = {
272dcec16efSThierry Reding &sti_tvout_driver,
273dcec16efSThierry Reding &sti_hqvdp_driver,
274dcec16efSThierry Reding &sti_hdmi_driver,
275dcec16efSThierry Reding &sti_hda_driver,
276dcec16efSThierry Reding &sti_dvo_driver,
277dcec16efSThierry Reding &sti_vtg_driver,
278dcec16efSThierry Reding &sti_compositor_driver,
279dcec16efSThierry Reding &sti_platform_driver,
280dcec16efSThierry Reding };
281dcec16efSThierry Reding
sti_drm_init(void)282dcec16efSThierry Reding static int sti_drm_init(void)
283dcec16efSThierry Reding {
28489ec0023SJavier Martinez Canillas if (drm_firmware_drivers_only())
28589ec0023SJavier Martinez Canillas return -ENODEV;
28689ec0023SJavier Martinez Canillas
287dcec16efSThierry Reding return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
288dcec16efSThierry Reding }
289dcec16efSThierry Reding module_init(sti_drm_init);
290dcec16efSThierry Reding
sti_drm_exit(void)291dcec16efSThierry Reding static void sti_drm_exit(void)
292dcec16efSThierry Reding {
293dcec16efSThierry Reding platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
294dcec16efSThierry Reding }
295dcec16efSThierry Reding module_exit(sti_drm_exit);
2969e1f05b2SVincent Abriou
2979e1f05b2SVincent Abriou MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
2989e1f05b2SVincent Abriou MODULE_DESCRIPTION("STMicroelectronics SoC DRM driver");
2999e1f05b2SVincent Abriou MODULE_LICENSE("GPL");
300