]>
Commit | Line | Data |
---|---|---|
ec17f034 | 1 | // SPDX-License-Identifier: GPL-2.0 |
b759012c YF |
2 | /* |
3 | * Copyright (C) STMicroelectronics SA 2017 | |
4 | * | |
5 | * Authors: Philippe Cornu <philippe.cornu@st.com> | |
6 | * Yannick Fertre <yannick.fertre@st.com> | |
7 | * Fabien Dessenne <fabien.dessenne@st.com> | |
8 | * Mickael Reulier <mickael.reulier@st.com> | |
b759012c YF |
9 | */ |
10 | ||
11 | #include <linux/component.h> | |
2a6b4990 SR |
12 | #include <linux/dma-mapping.h> |
13 | #include <linux/module.h> | |
b759012c YF |
14 | #include <linux/of_platform.h> |
15 | ||
16 | #include <drm/drm_atomic.h> | |
17 | #include <drm/drm_atomic_helper.h> | |
2a6b4990 | 18 | #include <drm/drm_drv.h> |
b759012c | 19 | #include <drm/drm_fb_cma_helper.h> |
2a6b4990 | 20 | #include <drm/drm_fb_helper.h> |
b759012c | 21 | #include <drm/drm_gem_cma_helper.h> |
4930a433 | 22 | #include <drm/drm_gem_framebuffer_helper.h> |
fcd70cd3 | 23 | #include <drm/drm_probe_helper.h> |
2a6b4990 | 24 | #include <drm/drm_vblank.h> |
b759012c YF |
25 | |
26 | #include "ltdc.h" | |
27 | ||
b759012c YF |
28 | #define STM_MAX_FB_WIDTH 2048 |
29 | #define STM_MAX_FB_HEIGHT 2048 /* same as width to handle orientation */ | |
30 | ||
b759012c | 31 | static const struct drm_mode_config_funcs drv_mode_config_funcs = { |
4930a433 | 32 | .fb_create = drm_gem_fb_create, |
b759012c YF |
33 | .atomic_check = drm_atomic_helper_check, |
34 | .atomic_commit = drm_atomic_helper_commit, | |
35 | }; | |
36 | ||
21f815bf PC |
37 | static int stm_gem_cma_dumb_create(struct drm_file *file, |
38 | struct drm_device *dev, | |
39 | struct drm_mode_create_dumb *args) | |
40 | { | |
21f815bf PC |
41 | unsigned int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); |
42 | ||
43 | /* | |
44 | * in order to optimize data transfer, pitch is aligned on | |
45 | * 128 bytes, height is aligned on 4 bytes | |
46 | */ | |
47 | args->pitch = roundup(min_pitch, 128); | |
48 | args->height = roundup(args->height, 4); | |
21f815bf PC |
49 | |
50 | return drm_gem_cma_dumb_create_internal(file, dev, args); | |
51 | } | |
52 | ||
b759012c YF |
53 | DEFINE_DRM_GEM_CMA_FOPS(drv_driver_fops); |
54 | ||
55 | static struct drm_driver drv_driver = { | |
56 | .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | | |
57 | DRIVER_ATOMIC, | |
af5125de PC |
58 | .name = "stm", |
59 | .desc = "STMicroelectronics SoC DRM", | |
60 | .date = "20170330", | |
61 | .major = 1, | |
62 | .minor = 0, | |
63 | .patchlevel = 0, | |
b759012c | 64 | .fops = &drv_driver_fops, |
21f815bf | 65 | .dumb_create = stm_gem_cma_dumb_create, |
b759012c YF |
66 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
67 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
68 | .gem_free_object_unlocked = drm_gem_cma_free_object, | |
69 | .gem_vm_ops = &drm_gem_cma_vm_ops, | |
70 | .gem_prime_export = drm_gem_prime_export, | |
71 | .gem_prime_import = drm_gem_prime_import, | |
72 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | |
73 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | |
74 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | |
75 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | |
76 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | |
53273b52 BG |
77 | .get_scanout_position = ltdc_crtc_scanoutpos, |
78 | .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos, | |
b759012c YF |
79 | }; |
80 | ||
81 | static int drv_load(struct drm_device *ddev) | |
82 | { | |
83 | struct platform_device *pdev = to_platform_device(ddev->dev); | |
b759012c YF |
84 | struct ltdc_device *ldev; |
85 | int ret; | |
86 | ||
87 | DRM_DEBUG("%s\n", __func__); | |
88 | ||
89 | ldev = devm_kzalloc(ddev->dev, sizeof(*ldev), GFP_KERNEL); | |
90 | if (!ldev) | |
91 | return -ENOMEM; | |
92 | ||
93 | ddev->dev_private = (void *)ldev; | |
94 | ||
95 | drm_mode_config_init(ddev); | |
96 | ||
97 | /* | |
98 | * set max width and height as default value. | |
99 | * this value would be used to check framebuffer size limitation | |
100 | * at drm_mode_addfb(). | |
101 | */ | |
102 | ddev->mode_config.min_width = 0; | |
103 | ddev->mode_config.min_height = 0; | |
104 | ddev->mode_config.max_width = STM_MAX_FB_WIDTH; | |
105 | ddev->mode_config.max_height = STM_MAX_FB_HEIGHT; | |
106 | ddev->mode_config.funcs = &drv_mode_config_funcs; | |
107 | ||
108 | ret = ltdc_load(ddev); | |
109 | if (ret) | |
110 | goto err; | |
111 | ||
112 | drm_mode_config_reset(ddev); | |
113 | drm_kms_helper_poll_init(ddev); | |
114 | ||
b759012c YF |
115 | platform_set_drvdata(pdev, ddev); |
116 | ||
117 | return 0; | |
118 | err: | |
119 | drm_mode_config_cleanup(ddev); | |
120 | return ret; | |
121 | } | |
122 | ||
123 | static void drv_unload(struct drm_device *ddev) | |
124 | { | |
b759012c YF |
125 | DRM_DEBUG("%s\n", __func__); |
126 | ||
b759012c YF |
127 | drm_kms_helper_poll_fini(ddev); |
128 | ltdc_unload(ddev); | |
129 | drm_mode_config_cleanup(ddev); | |
130 | } | |
131 | ||
132 | static int stm_drm_platform_probe(struct platform_device *pdev) | |
133 | { | |
134 | struct device *dev = &pdev->dev; | |
135 | struct drm_device *ddev; | |
136 | int ret; | |
137 | ||
138 | DRM_DEBUG("%s\n", __func__); | |
139 | ||
140 | dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); | |
141 | ||
142 | ddev = drm_dev_alloc(&drv_driver, dev); | |
143 | if (IS_ERR(ddev)) | |
144 | return PTR_ERR(ddev); | |
145 | ||
146 | ret = drv_load(ddev); | |
147 | if (ret) | |
a1a6fa3d | 148 | goto err_put; |
b759012c YF |
149 | |
150 | ret = drm_dev_register(ddev, 0); | |
151 | if (ret) | |
a1a6fa3d | 152 | goto err_put; |
b759012c | 153 | |
e0ce0e2a NT |
154 | drm_fbdev_generic_setup(ddev, 16); |
155 | ||
b759012c YF |
156 | return 0; |
157 | ||
a1a6fa3d TZ |
158 | err_put: |
159 | drm_dev_put(ddev); | |
b759012c YF |
160 | |
161 | return ret; | |
162 | } | |
163 | ||
164 | static int stm_drm_platform_remove(struct platform_device *pdev) | |
165 | { | |
166 | struct drm_device *ddev = platform_get_drvdata(pdev); | |
167 | ||
168 | DRM_DEBUG("%s\n", __func__); | |
169 | ||
170 | drm_dev_unregister(ddev); | |
171 | drv_unload(ddev); | |
a1a6fa3d | 172 | drm_dev_put(ddev); |
b759012c YF |
173 | |
174 | return 0; | |
175 | } | |
176 | ||
177 | static const struct of_device_id drv_dt_ids[] = { | |
178 | { .compatible = "st,stm32-ltdc"}, | |
179 | { /* end node */ }, | |
180 | }; | |
181 | MODULE_DEVICE_TABLE(of, drv_dt_ids); | |
182 | ||
183 | static struct platform_driver stm_drm_platform_driver = { | |
184 | .probe = stm_drm_platform_probe, | |
185 | .remove = stm_drm_platform_remove, | |
186 | .driver = { | |
af5125de | 187 | .name = "stm32-display", |
b759012c YF |
188 | .of_match_table = drv_dt_ids, |
189 | }, | |
190 | }; | |
191 | ||
192 | module_platform_driver(stm_drm_platform_driver); | |
193 | ||
194 | MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); | |
195 | MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); | |
196 | MODULE_AUTHOR("Fabien Dessenne <fabien.dessenne@st.com>"); | |
197 | MODULE_AUTHOR("Mickael Reulier <mickael.reulier@st.com>"); | |
198 | MODULE_DESCRIPTION("STMicroelectronics ST DRM LTDC driver"); | |
199 | MODULE_LICENSE("GPL v2"); |