1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <drm/drmP.h>
21#include <drm/drm_crtc_helper.h>
22#include <drm/drm_gem_cma_helper.h>
23
24#include <linux/device.h>
25#include <linux/module.h>
26#include <linux/platform_device.h>
27
28#include "xylon_connector.h"
29#include "xylon_crtc.h"
30#include "xylon_drv.h"
31#include "xylon_encoder.h"
32#include "xylon_fb.h"
33#include "xylon_fbdev.h"
34#include "xylon_irq.h"
35
36#define DEVICE_NAME "logicvc"
37
38#define DRIVER_NAME "xylon-drm"
39#define DRIVER_DESCRIPTION "Xylon DRM driver for logiCVC IP core"
40#define DRIVER_VERSION "1.1"
41#define DRIVER_DATE "20140701"
42
43#define DRIVER_MAJOR 1
44#define DRIVER_MINOR 0
45
46static int xylon_drm_load(struct drm_device *dev, unsigned long flags)
47{
48 struct platform_device *pdev = dev->platformdev;
49 struct xylon_drm_device *xdev;
50 unsigned int bpp;
51 int ret;
52
53 xdev = devm_kzalloc(dev->dev, sizeof(*xdev), GFP_KERNEL);
54 if (!xdev)
55 return -ENOMEM;
56 xdev->dev = dev;
57
58 dev->dev_private = xdev;
59
60 drm_mode_config_init(dev);
61
62 drm_kms_helper_poll_init(dev);
63
64 xdev->crtc = xylon_drm_crtc_create(dev);
65 if (IS_ERR(xdev->crtc)) {
66 DRM_ERROR("failed create xylon crtc\n");
67 ret = PTR_ERR(xdev->crtc);
68 goto err_out;
69 }
70
71 xylon_drm_mode_config_init(dev);
72
73 xdev->encoder = xylon_drm_encoder_create(dev);
74 if (IS_ERR(xdev->encoder)) {
75 DRM_ERROR("failed create xylon encoder\n");
76 ret = PTR_ERR(xdev->encoder);
77 goto err_out;
78 }
79
80 xdev->connector = xylon_drm_connector_create(dev, xdev->encoder);
81 if (IS_ERR(xdev->connector)) {
82 DRM_ERROR("failed create xylon connector\n");
83 ret = PTR_ERR(xdev->connector);
84 goto err_out;
85 }
86
87 ret = drm_vblank_init(dev, 1);
88 if (ret) {
89 DRM_ERROR("failed initialize vblank\n");
90 goto err_out;
91 }
92 dev->vblank_disable_allowed = 1;
93
94 ret = xylon_drm_irq_install(dev);
95 if (ret < 0) {
96 DRM_ERROR("failed install irq\n");
97 goto err_irq;
98 }
99
100 ret = xylon_drm_crtc_get_param(xdev->crtc, &bpp,
101 XYLON_DRM_CRTC_BUFF_BPP);
102 if (ret) {
103 DRM_ERROR("failed get bpp\n");
104 goto err_fbdev;
105 }
106 xdev->fbdev = xylon_drm_fbdev_init(dev, bpp, 1, 1);
107 if (IS_ERR(xdev->fbdev)) {
108 DRM_ERROR("failed initialize fbdev\n");
109 ret = PTR_ERR(xdev->fbdev);
110 goto err_fbdev;
111 }
112
113 drm_helper_disable_unused_functions(dev);
114
115 platform_set_drvdata(pdev, xdev);
116
117 return 0;
118
119err_fbdev:
120 xylon_drm_irq_uninstall(dev);
121err_irq:
122 drm_vblank_cleanup(dev);
123err_out:
124 drm_mode_config_cleanup(dev);
125
126 if (ret == -EPROBE_DEFER)
127 DRM_INFO("driver load deferred, will be called again\n");
128
129 return ret;
130}
131
132static int xylon_drm_unload(struct drm_device *dev)
133{
134 struct xylon_drm_device *xdev = dev->dev_private;
135
136 xylon_drm_irq_uninstall(dev);
137
138 drm_vblank_cleanup(dev);
139
140 drm_kms_helper_poll_fini(dev);
141
142 xylon_drm_fbdev_fini(xdev->fbdev);
143
144 drm_mode_config_cleanup(dev);
145
146 return 0;
147}
148
149static void xylon_drm_preclose(struct drm_device *dev, struct drm_file *file)
150{
151 struct xylon_drm_device *xdev = dev->dev_private;
152
153 xylon_drm_crtc_cancel_page_flip(xdev->crtc, file);
154}
155
156static void xylon_drm_postclose(struct drm_device *dev, struct drm_file *file)
157{
158}
159
160static void xylon_drm_lastclose(struct drm_device *dev)
161{
162 struct xylon_drm_device *xdev = dev->dev_private;
163
164 xylon_drm_crtc_properties_restore(xdev->crtc);
165
166 xylon_drm_fbdev_restore_mode(xdev->fbdev);
167}
168
169static int xylon_drm_vblank_enable(struct drm_device *dev, int crtc)
170{
171 struct xylon_drm_device *xdev = dev->dev_private;
172
173 xylon_drm_crtc_vblank(xdev->crtc, true);
174
175 return 0;
176}
177
178static void xylon_drm_vblank_disable(struct drm_device *dev, int crtc)
179{
180 struct xylon_drm_device *xdev = dev->dev_private;
181
182 xylon_drm_crtc_vblank(xdev->crtc, false);
183}
184
185static int xylon_drm_gem_dumb_create(struct drm_file *file_priv,
186 struct drm_device *dev,
187 struct drm_mode_create_dumb *args)
188{
189 struct drm_gem_cma_object *cma_obj;
190 struct drm_gem_object *gem_obj;
191 struct xylon_drm_device *xdev = dev->dev_private;
192 unsigned int buff_width;
193 int ret;
194
195 ret = xylon_drm_crtc_get_param(xdev->crtc, &buff_width,
196 XYLON_DRM_CRTC_BUFF_WIDTH);
197 if (ret)
198 return ret;
199
200 args->pitch = buff_width * DIV_ROUND_UP(args->bpp, 8);
201 args->size = (u64)(buff_width * DIV_ROUND_UP(args->bpp, 8) *
202 args->height);
203
204 cma_obj = drm_gem_cma_create(dev, (unsigned int)args->size);
205 if (IS_ERR(cma_obj))
206 return PTR_ERR(cma_obj);
207
208 gem_obj = &cma_obj->base;
209
210 ret = drm_gem_handle_create(file_priv, gem_obj, &args->handle);
211 if (ret)
212 goto err_handle_create;
213
214 drm_gem_object_unreference_unlocked(gem_obj);
215
216 return PTR_ERR_OR_ZERO(cma_obj);
217
218err_handle_create:
219 drm_gem_cma_free_object(gem_obj);
220
221 return ret;
222}
223
224static const struct file_operations xylon_drm_fops = {
225 .owner = THIS_MODULE,
226 .open = drm_open,
227 .release = drm_release,
228 .unlocked_ioctl = drm_ioctl,
229 .mmap = drm_gem_cma_mmap,
230 .poll = drm_poll,
231 .read = drm_read,
232#ifdef CONFIG_COMPAT
233 .compat_ioctl = drm_compat_ioctl,
234#endif
235 .llseek = noop_llseek,
236};
237
238static struct drm_driver xylon_drm_driver = {
239 .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
240 DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
241 .load = xylon_drm_load,
242 .unload = xylon_drm_unload,
243 .preclose = xylon_drm_preclose,
244 .postclose = xylon_drm_postclose,
245 .lastclose = xylon_drm_lastclose,
246
247 .get_vblank_counter = drm_vblank_count,
248 .enable_vblank = xylon_drm_vblank_enable,
249 .disable_vblank = xylon_drm_vblank_disable,
250
251 .irq_preinstall = xylon_drm_irq_preinst,
252 .irq_postinstall = xylon_drm_irq_postinst,
253 .irq_uninstall = xylon_drm_irq_uninst,
254 .irq_handler = xylon_drm_irq_handler,
255
256 .gem_free_object = drm_gem_cma_free_object,
257
258 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
259 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
260 .gem_prime_export = drm_gem_prime_export,
261 .gem_prime_import = drm_gem_prime_import,
262 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
263 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
264 .gem_prime_vmap = drm_gem_cma_prime_vmap,
265 .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
266 .gem_prime_mmap = drm_gem_cma_prime_mmap,
267
268 .dumb_create = xylon_drm_gem_dumb_create,
269 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
270 .dumb_destroy = drm_gem_dumb_destroy,
271
272 .gem_vm_ops = &drm_gem_cma_vm_ops,
273
274 .fops = &xylon_drm_fops,
275
276 .name = DRIVER_NAME,
277 .desc = DRIVER_DESCRIPTION,
278 .date = DRIVER_DATE,
279 .major = DRIVER_MAJOR,
280 .minor = DRIVER_MINOR,
281};
282
283static int __maybe_unused xylon_drm_pm_suspend(struct device *dev)
284{
285 struct xylon_drm_device *xdev = dev_get_drvdata(dev);
286
287 drm_kms_helper_poll_disable(xdev->dev);
288 drm_helper_connector_dpms(xdev->connector, DRM_MODE_DPMS_SUSPEND);
289
290 return 0;
291}
292
293static int __maybe_unused xylon_drm_pm_resume(struct device *dev)
294{
295 struct xylon_drm_device *xdev = dev_get_drvdata(dev);
296
297 drm_helper_connector_dpms(xdev->connector, DRM_MODE_DPMS_ON);
298 drm_kms_helper_poll_enable(xdev->dev);
299
300 return 0;
301}
302
303static const struct dev_pm_ops xylon_drm_pm_ops = {
304 SET_SYSTEM_SLEEP_PM_OPS(xylon_drm_pm_suspend, xylon_drm_pm_resume)
305 SET_RUNTIME_PM_OPS(xylon_drm_pm_suspend, xylon_drm_pm_resume, NULL)
306};
307
308static int xylon_drm_platform_probe(struct platform_device *pdev)
309{
310 return drm_platform_init(&xylon_drm_driver, pdev);
311}
312
313static int xylon_drm_platform_remove(struct platform_device *pdev)
314{
315 struct xylon_drm_device *xdev = platform_get_drvdata(pdev);
316
317 drm_put_dev(xdev->dev);
318
319 return 0;
320}
321
322static const struct of_device_id xylon_drm_of_match[] = {
323 { .compatible = "xylon,drm-1.00.a", },
324 { },
325};
326MODULE_DEVICE_TABLE(of, xylon_drm_of_match);
327
328static struct platform_driver xylon_drm_platform_driver = {
329 .probe = xylon_drm_platform_probe,
330 .remove = xylon_drm_platform_remove,
331 .driver = {
332 .name = DRIVER_NAME,
333 .pm = &xylon_drm_pm_ops,
334 .of_match_table = xylon_drm_of_match,
335 },
336};
337
338module_platform_driver(xylon_drm_platform_driver);
339
340MODULE_AUTHOR("Xylon d.o.o.");
341MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
342MODULE_LICENSE("GPL v2");
343