drm: Change the return type of the unload hook to void
[linux-2.6-microblaze.git] / drivers / gpu / drm / fsl-dcu / fsl_dcu_drm_drv.c
1 /*
2  * Copyright 2015 Freescale Semiconductor, Inc.
3  *
4  * Freescale DCU drm device driver
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  */
11
12 #include <linux/clk.h>
13 #include <linux/clk-provider.h>
14 #include <linux/console.h>
15 #include <linux/io.h>
16 #include <linux/mfd/syscon.h>
17 #include <linux/mm.h>
18 #include <linux/module.h>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
21 #include <linux/pm.h>
22 #include <linux/pm_runtime.h>
23 #include <linux/regmap.h>
24
25 #include <drm/drmP.h>
26 #include <drm/drm_atomic_helper.h>
27 #include <drm/drm_crtc_helper.h>
28 #include <drm/drm_fb_cma_helper.h>
29 #include <drm/drm_gem_cma_helper.h>
30
31 #include "fsl_dcu_drm_crtc.h"
32 #include "fsl_dcu_drm_drv.h"
33 #include "fsl_tcon.h"
34
35 static int legacyfb_depth = 24;
36 module_param(legacyfb_depth, int, 0444);
37
38 static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
39 {
40         if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE)
41                 return true;
42
43         return false;
44 }
45
46 static const struct regmap_config fsl_dcu_regmap_config = {
47         .reg_bits = 32,
48         .reg_stride = 4,
49         .val_bits = 32,
50
51         .volatile_reg = fsl_dcu_drm_is_volatile_reg,
52 };
53
54 static int fsl_dcu_drm_irq_init(struct drm_device *dev)
55 {
56         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
57         int ret;
58
59         ret = drm_irq_install(dev, fsl_dev->irq);
60         if (ret < 0)
61                 dev_err(dev->dev, "failed to install IRQ handler\n");
62
63         regmap_write(fsl_dev->regmap, DCU_INT_STATUS, 0);
64         regmap_write(fsl_dev->regmap, DCU_INT_MASK, ~0);
65
66         return ret;
67 }
68
69 static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
70 {
71         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
72         int ret;
73
74         ret = fsl_dcu_drm_modeset_init(fsl_dev);
75         if (ret < 0) {
76                 dev_err(dev->dev, "failed to initialize mode setting\n");
77                 return ret;
78         }
79
80         ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
81         if (ret < 0) {
82                 dev_err(dev->dev, "failed to initialize vblank\n");
83                 goto done;
84         }
85
86         ret = fsl_dcu_drm_irq_init(dev);
87         if (ret < 0)
88                 goto done;
89         dev->irq_enabled = true;
90
91         if (legacyfb_depth != 16 && legacyfb_depth != 24 &&
92             legacyfb_depth != 32) {
93                 dev_warn(dev->dev,
94                         "Invalid legacyfb_depth.  Defaulting to 24bpp\n");
95                 legacyfb_depth = 24;
96         }
97         fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1, 1);
98         if (IS_ERR(fsl_dev->fbdev)) {
99                 ret = PTR_ERR(fsl_dev->fbdev);
100                 fsl_dev->fbdev = NULL;
101                 goto done;
102         }
103
104         return 0;
105 done:
106         drm_kms_helper_poll_fini(dev);
107
108         if (fsl_dev->fbdev)
109                 drm_fbdev_cma_fini(fsl_dev->fbdev);
110
111         drm_mode_config_cleanup(dev);
112         drm_vblank_cleanup(dev);
113         drm_irq_uninstall(dev);
114         dev->dev_private = NULL;
115
116         return ret;
117 }
118
119 static void fsl_dcu_unload(struct drm_device *dev)
120 {
121         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
122
123         drm_crtc_force_disable_all(dev);
124         drm_kms_helper_poll_fini(dev);
125
126         if (fsl_dev->fbdev)
127                 drm_fbdev_cma_fini(fsl_dev->fbdev);
128
129         drm_mode_config_cleanup(dev);
130         drm_vblank_cleanup(dev);
131         drm_irq_uninstall(dev);
132
133         dev->dev_private = NULL;
134 }
135
136 static irqreturn_t fsl_dcu_drm_irq(int irq, void *arg)
137 {
138         struct drm_device *dev = arg;
139         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
140         unsigned int int_status;
141         int ret;
142
143         ret = regmap_read(fsl_dev->regmap, DCU_INT_STATUS, &int_status);
144         if (ret) {
145                 dev_err(dev->dev, "read DCU_INT_STATUS failed\n");
146                 return IRQ_NONE;
147         }
148
149         if (int_status & DCU_INT_STATUS_VBLANK)
150                 drm_handle_vblank(dev, 0);
151
152         regmap_write(fsl_dev->regmap, DCU_INT_STATUS, int_status);
153
154         return IRQ_HANDLED;
155 }
156
157 static int fsl_dcu_drm_enable_vblank(struct drm_device *dev, unsigned int pipe)
158 {
159         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
160         unsigned int value;
161
162         regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
163         value &= ~DCU_INT_MASK_VBLANK;
164         regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
165
166         return 0;
167 }
168
169 static void fsl_dcu_drm_disable_vblank(struct drm_device *dev,
170                                        unsigned int pipe)
171 {
172         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
173         unsigned int value;
174
175         regmap_read(fsl_dev->regmap, DCU_INT_MASK, &value);
176         value |= DCU_INT_MASK_VBLANK;
177         regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
178 }
179
180 static void fsl_dcu_drm_lastclose(struct drm_device *dev)
181 {
182         struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
183
184         drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
185 }
186
187 static const struct file_operations fsl_dcu_drm_fops = {
188         .owner          = THIS_MODULE,
189         .open           = drm_open,
190         .release        = drm_release,
191         .unlocked_ioctl = drm_ioctl,
192         .compat_ioctl   = drm_compat_ioctl,
193         .poll           = drm_poll,
194         .read           = drm_read,
195         .llseek         = no_llseek,
196         .mmap           = drm_gem_cma_mmap,
197 };
198
199 static struct drm_driver fsl_dcu_drm_driver = {
200         .driver_features        = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
201                                 | DRIVER_PRIME | DRIVER_ATOMIC,
202         .lastclose              = fsl_dcu_drm_lastclose,
203         .load                   = fsl_dcu_load,
204         .unload                 = fsl_dcu_unload,
205         .irq_handler            = fsl_dcu_drm_irq,
206         .get_vblank_counter     = drm_vblank_no_hw_counter,
207         .enable_vblank          = fsl_dcu_drm_enable_vblank,
208         .disable_vblank         = fsl_dcu_drm_disable_vblank,
209         .gem_free_object_unlocked = drm_gem_cma_free_object,
210         .gem_vm_ops             = &drm_gem_cma_vm_ops,
211         .prime_handle_to_fd     = drm_gem_prime_handle_to_fd,
212         .prime_fd_to_handle     = drm_gem_prime_fd_to_handle,
213         .gem_prime_import       = drm_gem_prime_import,
214         .gem_prime_export       = drm_gem_prime_export,
215         .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
216         .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
217         .gem_prime_vmap         = drm_gem_cma_prime_vmap,
218         .gem_prime_vunmap       = drm_gem_cma_prime_vunmap,
219         .gem_prime_mmap         = drm_gem_cma_prime_mmap,
220         .dumb_create            = drm_gem_cma_dumb_create,
221         .dumb_map_offset        = drm_gem_cma_dumb_map_offset,
222         .dumb_destroy           = drm_gem_dumb_destroy,
223         .fops                   = &fsl_dcu_drm_fops,
224         .name                   = "fsl-dcu-drm",
225         .desc                   = "Freescale DCU DRM",
226         .date                   = "20160425",
227         .major                  = 1,
228         .minor                  = 1,
229 };
230
231 #ifdef CONFIG_PM_SLEEP
232 static int fsl_dcu_drm_pm_suspend(struct device *dev)
233 {
234         struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
235
236         if (!fsl_dev)
237                 return 0;
238
239         disable_irq(fsl_dev->irq);
240         drm_kms_helper_poll_disable(fsl_dev->drm);
241
242         console_lock();
243         drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 1);
244         console_unlock();
245
246         fsl_dev->state = drm_atomic_helper_suspend(fsl_dev->drm);
247         if (IS_ERR(fsl_dev->state)) {
248                 console_lock();
249                 drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 0);
250                 console_unlock();
251
252                 drm_kms_helper_poll_enable(fsl_dev->drm);
253                 enable_irq(fsl_dev->irq);
254                 return PTR_ERR(fsl_dev->state);
255         }
256
257         clk_disable_unprepare(fsl_dev->pix_clk);
258         clk_disable_unprepare(fsl_dev->clk);
259
260         return 0;
261 }
262
263 static int fsl_dcu_drm_pm_resume(struct device *dev)
264 {
265         struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev);
266         int ret;
267
268         if (!fsl_dev)
269                 return 0;
270
271         ret = clk_prepare_enable(fsl_dev->clk);
272         if (ret < 0) {
273                 dev_err(dev, "failed to enable dcu clk\n");
274                 return ret;
275         }
276
277         if (fsl_dev->tcon)
278                 fsl_tcon_bypass_enable(fsl_dev->tcon);
279         fsl_dcu_drm_init_planes(fsl_dev->drm);
280         drm_atomic_helper_resume(fsl_dev->drm, fsl_dev->state);
281
282         console_lock();
283         drm_fbdev_cma_set_suspend(fsl_dev->fbdev, 0);
284         console_unlock();
285
286         drm_kms_helper_poll_enable(fsl_dev->drm);
287         enable_irq(fsl_dev->irq);
288
289         return 0;
290 }
291 #endif
292
293 static const struct dev_pm_ops fsl_dcu_drm_pm_ops = {
294         SET_SYSTEM_SLEEP_PM_OPS(fsl_dcu_drm_pm_suspend, fsl_dcu_drm_pm_resume)
295 };
296
297 static const struct fsl_dcu_soc_data fsl_dcu_ls1021a_data = {
298         .name = "ls1021a",
299         .total_layer = 16,
300         .max_layer = 4,
301         .layer_regs = LS1021A_LAYER_REG_NUM,
302 };
303
304 static const struct fsl_dcu_soc_data fsl_dcu_vf610_data = {
305         .name = "vf610",
306         .total_layer = 64,
307         .max_layer = 6,
308         .layer_regs = VF610_LAYER_REG_NUM,
309 };
310
311 static const struct of_device_id fsl_dcu_of_match[] = {
312         {
313                 .compatible = "fsl,ls1021a-dcu",
314                 .data = &fsl_dcu_ls1021a_data,
315         }, {
316                 .compatible = "fsl,vf610-dcu",
317                 .data = &fsl_dcu_vf610_data,
318         }, {
319         },
320 };
321 MODULE_DEVICE_TABLE(of, fsl_dcu_of_match);
322
323 static int fsl_dcu_drm_probe(struct platform_device *pdev)
324 {
325         struct fsl_dcu_drm_device *fsl_dev;
326         struct drm_device *drm;
327         struct device *dev = &pdev->dev;
328         struct resource *res;
329         void __iomem *base;
330         struct drm_driver *driver = &fsl_dcu_drm_driver;
331         struct clk *pix_clk_in;
332         char pix_clk_name[32];
333         const char *pix_clk_in_name;
334         const struct of_device_id *id;
335         int ret;
336         u8 div_ratio_shift = 0;
337
338         fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
339         if (!fsl_dev)
340                 return -ENOMEM;
341
342         id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
343         if (!id)
344                 return -ENODEV;
345         fsl_dev->soc = id->data;
346
347         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
348         base = devm_ioremap_resource(dev, res);
349         if (IS_ERR(base)) {
350                 ret = PTR_ERR(base);
351                 return ret;
352         }
353
354         fsl_dev->irq = platform_get_irq(pdev, 0);
355         if (fsl_dev->irq < 0) {
356                 dev_err(dev, "failed to get irq\n");
357                 return fsl_dev->irq;
358         }
359
360         fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
361                         &fsl_dcu_regmap_config);
362         if (IS_ERR(fsl_dev->regmap)) {
363                 dev_err(dev, "regmap init failed\n");
364                 return PTR_ERR(fsl_dev->regmap);
365         }
366
367         fsl_dev->clk = devm_clk_get(dev, "dcu");
368         if (IS_ERR(fsl_dev->clk)) {
369                 dev_err(dev, "failed to get dcu clock\n");
370                 return PTR_ERR(fsl_dev->clk);
371         }
372         ret = clk_prepare_enable(fsl_dev->clk);
373         if (ret < 0) {
374                 dev_err(dev, "failed to enable dcu clk\n");
375                 return ret;
376         }
377
378         pix_clk_in = devm_clk_get(dev, "pix");
379         if (IS_ERR(pix_clk_in)) {
380                 /* legancy binding, use dcu clock as pixel clock input */
381                 pix_clk_in = fsl_dev->clk;
382         }
383
384         if (of_property_read_bool(dev->of_node, "big-endian"))
385                 div_ratio_shift = 24;
386
387         pix_clk_in_name = __clk_get_name(pix_clk_in);
388         snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
389         fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
390                         pix_clk_in_name, 0, base + DCU_DIV_RATIO,
391                         div_ratio_shift, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
392         if (IS_ERR(fsl_dev->pix_clk)) {
393                 dev_err(dev, "failed to register pix clk\n");
394                 ret = PTR_ERR(fsl_dev->pix_clk);
395                 goto disable_clk;
396         }
397
398         fsl_dev->tcon = fsl_tcon_init(dev);
399
400         drm = drm_dev_alloc(driver, dev);
401         if (IS_ERR(drm)) {
402                 ret = PTR_ERR(drm);
403                 goto unregister_pix_clk;
404         }
405
406         fsl_dev->dev = dev;
407         fsl_dev->drm = drm;
408         fsl_dev->np = dev->of_node;
409         drm->dev_private = fsl_dev;
410         dev_set_drvdata(dev, fsl_dev);
411
412         ret = drm_dev_register(drm, 0);
413         if (ret < 0)
414                 goto unref;
415
416         return 0;
417
418 unref:
419         drm_dev_unref(drm);
420 unregister_pix_clk:
421         clk_unregister(fsl_dev->pix_clk);
422 disable_clk:
423         clk_disable_unprepare(fsl_dev->clk);
424         return ret;
425 }
426
427 static int fsl_dcu_drm_remove(struct platform_device *pdev)
428 {
429         struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
430
431         drm_dev_unregister(fsl_dev->drm);
432         drm_dev_unref(fsl_dev->drm);
433         clk_disable_unprepare(fsl_dev->clk);
434         clk_unregister(fsl_dev->pix_clk);
435
436         return 0;
437 }
438
439 static struct platform_driver fsl_dcu_drm_platform_driver = {
440         .probe          = fsl_dcu_drm_probe,
441         .remove         = fsl_dcu_drm_remove,
442         .driver         = {
443                 .name   = "fsl-dcu",
444                 .pm     = &fsl_dcu_drm_pm_ops,
445                 .of_match_table = fsl_dcu_of_match,
446         },
447 };
448
449 module_platform_driver(fsl_dcu_drm_platform_driver);
450
451 MODULE_DESCRIPTION("Freescale DCU DRM Driver");
452 MODULE_LICENSE("GPL");