aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tilcdc/tilcdc_drv.c
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-01-08 16:04:28 -0500
committerRob Clark <robdclark@gmail.com>2013-02-19 17:57:44 -0500
commit16ea975eac671fa40a78594a116a44fef8e3f4a9 (patch)
tree4e83f07ea5856e0b33e8fd0918f11aff4b25547b /drivers/gpu/drm/tilcdc/tilcdc_drv.c
parentb3d3de80133d32723224329f87edecaff230fefc (diff)
drm/tilcdc: add TI LCD Controller DRM driver (v4)
A simple DRM/KMS driver for the TI LCD Controller found in various smaller TI parts (AM33xx, OMAPL138, etc). This driver uses the CMA helpers. Currently only the TFP410 DVI encoder is supported (tested with beaglebone + DVI cape). There are also various LCD displays, for which support can be added (as I get hw to test on), and an external i2c HDMI encoder found on some boards. The display controller supports a single CRTC. And the encoder+ connector are split out into sub-devices. Depending on which LCD or external encoder is actually present, the appropriate output module(s) will be loaded. v1: original v2: fix fb refcnting and few other cleanups v3: get +/- vsync/hsync from timings rather than panel-info, add option DT max-bandwidth field so driver doesn't attempt to pick a display mode with too high memory bandwidth, and other small cleanups v4: remove some unneeded stuff from panel-info struct, properly set high bits for hfp/hsw/hbp for rev 2, add DT bindings docs Signed-off-by: Rob Clark <robdclark@gmail.com> Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Tested-by: Koen Kooi <koen@dominion.thruhere.net>
Diffstat (limited to 'drivers/gpu/drm/tilcdc/tilcdc_drv.c')
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c605
1 files changed, 605 insertions, 0 deletions
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
new file mode 100644
index 000000000000..f6defbfaaffb
--- /dev/null
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -0,0 +1,605 @@
1/*
2 * Copyright (C) 2012 Texas Instruments
3 * Author: Rob Clark <robdclark@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published by
7 * the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18/* LCDC DRM driver, based on da8xx-fb */
19
20#include "tilcdc_drv.h"
21#include "tilcdc_regs.h"
22#include "tilcdc_tfp410.h"
23
24#include "drm_fb_helper.h"
25
26static LIST_HEAD(module_list);
27
28void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
29 const struct tilcdc_module_ops *funcs)
30{
31 mod->name = name;
32 mod->funcs = funcs;
33 INIT_LIST_HEAD(&mod->list);
34 list_add(&mod->list, &module_list);
35}
36
37void tilcdc_module_cleanup(struct tilcdc_module *mod)
38{
39 list_del(&mod->list);
40}
41
42static struct of_device_id tilcdc_of_match[];
43
44static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev,
45 struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
46{
47 return drm_fb_cma_create(dev, file_priv, mode_cmd);
48}
49
50static void tilcdc_fb_output_poll_changed(struct drm_device *dev)
51{
52 struct tilcdc_drm_private *priv = dev->dev_private;
53 if (priv->fbdev)
54 drm_fbdev_cma_hotplug_event(priv->fbdev);
55}
56
57static const struct drm_mode_config_funcs mode_config_funcs = {
58 .fb_create = tilcdc_fb_create,
59 .output_poll_changed = tilcdc_fb_output_poll_changed,
60};
61
62static int modeset_init(struct drm_device *dev)
63{
64 struct tilcdc_drm_private *priv = dev->dev_private;
65 struct tilcdc_module *mod;
66
67 drm_mode_config_init(dev);
68
69 priv->crtc = tilcdc_crtc_create(dev);
70
71 list_for_each_entry(mod, &module_list, list) {
72 DBG("loading module: %s", mod->name);
73 mod->funcs->modeset_init(mod, dev);
74 }
75
76 if ((priv->num_encoders = 0) || (priv->num_connectors == 0)) {
77 /* oh nos! */
78 dev_err(dev->dev, "no encoders/connectors found\n");
79 return -ENXIO;
80 }
81
82 dev->mode_config.min_width = 0;
83 dev->mode_config.min_height = 0;
84 dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc);
85 dev->mode_config.max_height = 2048;
86 dev->mode_config.funcs = &mode_config_funcs;
87
88 return 0;
89}
90
91#ifdef CONFIG_CPU_FREQ
92static int cpufreq_transition(struct notifier_block *nb,
93 unsigned long val, void *data)
94{
95 struct tilcdc_drm_private *priv = container_of(nb,
96 struct tilcdc_drm_private, freq_transition);
97 if (val == CPUFREQ_POSTCHANGE) {
98 if (priv->lcd_fck_rate != clk_get_rate(priv->clk)) {
99 priv->lcd_fck_rate = clk_get_rate(priv->clk);
100 tilcdc_crtc_update_clk(priv->crtc);
101 }
102 }
103
104 return 0;
105}
106#endif
107
108/*
109 * DRM operations:
110 */
111
112static int tilcdc_unload(struct drm_device *dev)
113{
114 struct tilcdc_drm_private *priv = dev->dev_private;
115 struct tilcdc_module *mod, *cur;
116
117 drm_kms_helper_poll_fini(dev);
118 drm_mode_config_cleanup(dev);
119 drm_vblank_cleanup(dev);
120
121 pm_runtime_get_sync(dev->dev);
122 drm_irq_uninstall(dev);
123 pm_runtime_put_sync(dev->dev);
124
125#ifdef CONFIG_CPU_FREQ
126 cpufreq_unregister_notifier(&priv->freq_transition,
127 CPUFREQ_TRANSITION_NOTIFIER);
128#endif
129
130 if (priv->clk)
131 clk_put(priv->clk);
132
133 if (priv->mmio)
134 iounmap(priv->mmio);
135
136 flush_workqueue(priv->wq);
137 destroy_workqueue(priv->wq);
138
139 dev->dev_private = NULL;
140
141 pm_runtime_disable(dev->dev);
142
143 list_for_each_entry_safe(mod, cur, &module_list, list) {
144 DBG("destroying module: %s", mod->name);
145 mod->funcs->destroy(mod);
146 }
147
148 kfree(priv);
149
150 return 0;
151}
152
153static int tilcdc_load(struct drm_device *dev, unsigned long flags)
154{
155 struct platform_device *pdev = dev->platformdev;
156 struct device_node *node = pdev->dev.of_node;
157 struct tilcdc_drm_private *priv;
158 struct resource *res;
159 int ret;
160
161 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
162 if (!priv) {
163 dev_err(dev->dev, "failed to allocate private data\n");
164 return -ENOMEM;
165 }
166
167 dev->dev_private = priv;
168
169 priv->wq = alloc_ordered_workqueue("tilcdc", 0);
170
171 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
172 if (!res) {
173 dev_err(dev->dev, "failed to get memory resource\n");
174 ret = -EINVAL;
175 goto fail;
176 }
177
178 priv->mmio = ioremap_nocache(res->start, resource_size(res));
179 if (!priv->mmio) {
180 dev_err(dev->dev, "failed to ioremap\n");
181 ret = -ENOMEM;
182 goto fail;
183 }
184
185 priv->clk = clk_get(dev->dev, "fck");
186 if (IS_ERR(priv->clk)) {
187 dev_err(dev->dev, "failed to get functional clock\n");
188 ret = -ENODEV;
189 goto fail;
190 }
191
192 priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck");
193 if (IS_ERR(priv->clk)) {
194 dev_err(dev->dev, "failed to get display clock\n");
195 ret = -ENODEV;
196 goto fail;
197 }
198
199#ifdef CONFIG_CPU_FREQ
200 priv->lcd_fck_rate = clk_get_rate(priv->clk);
201 priv->freq_transition.notifier_call = cpufreq_transition;
202 ret = cpufreq_register_notifier(&priv->freq_transition,
203 CPUFREQ_TRANSITION_NOTIFIER);
204 if (ret) {
205 dev_err(dev->dev, "failed to register cpufreq notifier\n");
206 goto fail;
207 }
208#endif
209
210 if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth))
211 priv->max_bandwidth = 1280 * 1024 * 60;
212
213 pm_runtime_enable(dev->dev);
214
215 /* Determine LCD IP Version */
216 pm_runtime_get_sync(dev->dev);
217 switch (tilcdc_read(dev, LCDC_PID_REG)) {
218 case 0x4c100102:
219 priv->rev = 1;
220 break;
221 case 0x4f200800:
222 case 0x4f201000:
223 priv->rev = 2;
224 break;
225 default:
226 dev_warn(dev->dev, "Unknown PID Reg value 0x%08x, "
227 "defaulting to LCD revision 1\n",
228 tilcdc_read(dev, LCDC_PID_REG));
229 priv->rev = 1;
230 break;
231 }
232
233 pm_runtime_put_sync(dev->dev);
234
235 ret = modeset_init(dev);
236 if (ret < 0) {
237 dev_err(dev->dev, "failed to initialize mode setting\n");
238 goto fail;
239 }
240
241 ret = drm_vblank_init(dev, 1);
242 if (ret < 0) {
243 dev_err(dev->dev, "failed to initialize vblank\n");
244 goto fail;
245 }
246
247 pm_runtime_get_sync(dev->dev);
248 ret = drm_irq_install(dev);
249 pm_runtime_put_sync(dev->dev);
250 if (ret < 0) {
251 dev_err(dev->dev, "failed to install IRQ handler\n");
252 goto fail;
253 }
254
255 platform_set_drvdata(pdev, dev);
256
257 priv->fbdev = drm_fbdev_cma_init(dev, 16,
258 dev->mode_config.num_crtc,
259 dev->mode_config.num_connector);
260
261 drm_kms_helper_poll_init(dev);
262
263 return 0;
264
265fail:
266 tilcdc_unload(dev);
267 return ret;
268}
269
270static void tilcdc_preclose(struct drm_device *dev, struct drm_file *file)
271{
272 struct tilcdc_drm_private *priv = dev->dev_private;
273
274 tilcdc_crtc_cancel_page_flip(priv->crtc, file);
275}
276
277static void tilcdc_lastclose(struct drm_device *dev)
278{
279 struct tilcdc_drm_private *priv = dev->dev_private;
280 drm_fbdev_cma_restore_mode(priv->fbdev);
281}
282
283static irqreturn_t tilcdc_irq(DRM_IRQ_ARGS)
284{
285 struct drm_device *dev = arg;
286 struct tilcdc_drm_private *priv = dev->dev_private;
287 return tilcdc_crtc_irq(priv->crtc);
288}
289
290static void tilcdc_irq_preinstall(struct drm_device *dev)
291{
292 tilcdc_clear_irqstatus(dev, 0xffffffff);
293}
294
295static int tilcdc_irq_postinstall(struct drm_device *dev)
296{
297 struct tilcdc_drm_private *priv = dev->dev_private;
298
299 /* enable FIFO underflow irq: */
300 if (priv->rev == 1) {
301 tilcdc_set(dev, LCDC_RASTER_CTRL_REG, LCDC_V1_UNDERFLOW_INT_ENA);
302 } else {
303 tilcdc_set(dev, LCDC_INT_ENABLE_SET_REG, LCDC_V2_UNDERFLOW_INT_ENA);
304 }
305
306 return 0;
307}
308
309static void tilcdc_irq_uninstall(struct drm_device *dev)
310{
311 struct tilcdc_drm_private *priv = dev->dev_private;
312
313 /* disable irqs that we might have enabled: */
314 if (priv->rev == 1) {
315 tilcdc_clear(dev, LCDC_RASTER_CTRL_REG,
316 LCDC_V1_UNDERFLOW_INT_ENA | LCDC_V1_PL_INT_ENA);
317 tilcdc_clear(dev, LCDC_DMA_CTRL_REG, LCDC_V1_END_OF_FRAME_INT_ENA);
318 } else {
319 tilcdc_clear(dev, LCDC_INT_ENABLE_SET_REG,
320 LCDC_V2_UNDERFLOW_INT_ENA | LCDC_V2_PL_INT_ENA |
321 LCDC_V2_END_OF_FRAME0_INT_ENA | LCDC_V2_END_OF_FRAME1_INT_ENA |
322 LCDC_FRAME_DONE);
323 }
324
325}
326
327static void enable_vblank(struct drm_device *dev, bool enable)
328{
329 struct tilcdc_drm_private *priv = dev->dev_private;
330 u32 reg, mask;
331
332 if (priv->rev == 1) {
333 reg = LCDC_DMA_CTRL_REG;
334 mask = LCDC_V1_END_OF_FRAME_INT_ENA;
335 } else {
336 reg = LCDC_INT_ENABLE_SET_REG;
337 mask = LCDC_V2_END_OF_FRAME0_INT_ENA |
338 LCDC_V2_END_OF_FRAME1_INT_ENA | LCDC_FRAME_DONE;
339 }
340
341 if (enable)
342 tilcdc_set(dev, reg, mask);
343 else
344 tilcdc_clear(dev, reg, mask);
345}
346
347static int tilcdc_enable_vblank(struct drm_device *dev, int crtc)
348{
349 enable_vblank(dev, true);
350 return 0;
351}
352
353static void tilcdc_disable_vblank(struct drm_device *dev, int crtc)
354{
355 enable_vblank(dev, false);
356}
357
358#if defined(CONFIG_DEBUG_FS) || defined(CONFIG_PM_SLEEP)
359static const struct {
360 const char *name;
361 uint8_t rev;
362 uint8_t save;
363 uint32_t reg;
364} registers[] = {
365#define REG(rev, save, reg) { #reg, rev, save, reg }
366 /* exists in revision 1: */
367 REG(1, false, LCDC_PID_REG),
368 REG(1, true, LCDC_CTRL_REG),
369 REG(1, false, LCDC_STAT_REG),
370 REG(1, true, LCDC_RASTER_CTRL_REG),
371 REG(1, true, LCDC_RASTER_TIMING_0_REG),
372 REG(1, true, LCDC_RASTER_TIMING_1_REG),
373 REG(1, true, LCDC_RASTER_TIMING_2_REG),
374 REG(1, true, LCDC_DMA_CTRL_REG),
375 REG(1, true, LCDC_DMA_FB_BASE_ADDR_0_REG),
376 REG(1, true, LCDC_DMA_FB_CEILING_ADDR_0_REG),
377 REG(1, true, LCDC_DMA_FB_BASE_ADDR_1_REG),
378 REG(1, true, LCDC_DMA_FB_CEILING_ADDR_1_REG),
379 /* new in revision 2: */
380 REG(2, false, LCDC_RAW_STAT_REG),
381 REG(2, false, LCDC_MASKED_STAT_REG),
382 REG(2, false, LCDC_INT_ENABLE_SET_REG),
383 REG(2, false, LCDC_INT_ENABLE_CLR_REG),
384 REG(2, false, LCDC_END_OF_INT_IND_REG),
385 REG(2, true, LCDC_CLK_ENABLE_REG),
386 REG(2, true, LCDC_INT_ENABLE_SET_REG),
387#undef REG
388};
389#endif
390
391#ifdef CONFIG_DEBUG_FS
392static int tilcdc_regs_show(struct seq_file *m, void *arg)
393{
394 struct drm_info_node *node = (struct drm_info_node *) m->private;
395 struct drm_device *dev = node->minor->dev;
396 struct tilcdc_drm_private *priv = dev->dev_private;
397 unsigned i;
398
399 pm_runtime_get_sync(dev->dev);
400
401 seq_printf(m, "revision: %d\n", priv->rev);
402
403 for (i = 0; i < ARRAY_SIZE(registers); i++)
404 if (priv->rev >= registers[i].rev)
405 seq_printf(m, "%s:\t %08x\n", registers[i].name,
406 tilcdc_read(dev, registers[i].reg));
407
408 pm_runtime_put_sync(dev->dev);
409
410 return 0;
411}
412
413static int tilcdc_mm_show(struct seq_file *m, void *arg)
414{
415 struct drm_info_node *node = (struct drm_info_node *) m->private;
416 struct drm_device *dev = node->minor->dev;
417 return drm_mm_dump_table(m, dev->mm_private);
418}
419
420static struct drm_info_list tilcdc_debugfs_list[] = {
421 { "regs", tilcdc_regs_show, 0 },
422 { "mm", tilcdc_mm_show, 0 },
423 { "fb", drm_fb_cma_debugfs_show, 0 },
424};
425
426static int tilcdc_debugfs_init(struct drm_minor *minor)
427{
428 struct drm_device *dev = minor->dev;
429 struct tilcdc_module *mod;
430 int ret;
431
432 ret = drm_debugfs_create_files(tilcdc_debugfs_list,
433 ARRAY_SIZE(tilcdc_debugfs_list),
434 minor->debugfs_root, minor);
435
436 list_for_each_entry(mod, &module_list, list)
437 if (mod->funcs->debugfs_init)
438 mod->funcs->debugfs_init(mod, minor);
439
440 if (ret) {
441 dev_err(dev->dev, "could not install tilcdc_debugfs_list\n");
442 return ret;
443 }
444
445 return ret;
446}
447
448static void tilcdc_debugfs_cleanup(struct drm_minor *minor)
449{
450 struct tilcdc_module *mod;
451 drm_debugfs_remove_files(tilcdc_debugfs_list,
452 ARRAY_SIZE(tilcdc_debugfs_list), minor);
453
454 list_for_each_entry(mod, &module_list, list)
455 if (mod->funcs->debugfs_cleanup)
456 mod->funcs->debugfs_cleanup(mod, minor);
457}
458#endif
459
460static const struct file_operations fops = {
461 .owner = THIS_MODULE,
462 .open = drm_open,
463 .release = drm_release,
464 .unlocked_ioctl = drm_ioctl,
465#ifdef CONFIG_COMPAT
466 .compat_ioctl = drm_compat_ioctl,
467#endif
468 .poll = drm_poll,
469 .read = drm_read,
470 .fasync = drm_fasync,
471 .llseek = no_llseek,
472 .mmap = drm_gem_cma_mmap,
473};
474
475static struct drm_driver tilcdc_driver = {
476 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET,
477 .load = tilcdc_load,
478 .unload = tilcdc_unload,
479 .preclose = tilcdc_preclose,
480 .lastclose = tilcdc_lastclose,
481 .irq_handler = tilcdc_irq,
482 .irq_preinstall = tilcdc_irq_preinstall,
483 .irq_postinstall = tilcdc_irq_postinstall,
484 .irq_uninstall = tilcdc_irq_uninstall,
485 .get_vblank_counter = drm_vblank_count,
486 .enable_vblank = tilcdc_enable_vblank,
487 .disable_vblank = tilcdc_disable_vblank,
488 .gem_free_object = drm_gem_cma_free_object,
489 .gem_vm_ops = &drm_gem_cma_vm_ops,
490 .dumb_create = drm_gem_cma_dumb_create,
491 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
492 .dumb_destroy = drm_gem_cma_dumb_destroy,
493#ifdef CONFIG_DEBUG_FS
494 .debugfs_init = tilcdc_debugfs_init,
495 .debugfs_cleanup = tilcdc_debugfs_cleanup,
496#endif
497 .fops = &fops,
498 .name = "tilcdc",
499 .desc = "TI LCD Controller DRM",
500 .date = "20121205",
501 .major = 1,
502 .minor = 0,
503};
504
505/*
506 * Power management:
507 */
508
509#ifdef CONFIG_PM_SLEEP
510static int tilcdc_pm_suspend(struct device *dev)
511{
512 struct drm_device *ddev = dev_get_drvdata(dev);
513 struct tilcdc_drm_private *priv = ddev->dev_private;
514 unsigned i, n = 0;
515
516 drm_kms_helper_poll_disable(ddev);
517
518 /* Save register state: */
519 for (i = 0; i < ARRAY_SIZE(registers); i++)
520 if (registers[i].save && (priv->rev >= registers[i].rev))
521 priv->saved_register[n++] = tilcdc_read(ddev, registers[i].reg);
522
523 return 0;
524}
525
526static int tilcdc_pm_resume(struct device *dev)
527{
528 struct drm_device *ddev = dev_get_drvdata(dev);
529 struct tilcdc_drm_private *priv = ddev->dev_private;
530 unsigned i, n = 0;
531
532 /* Restore register state: */
533 for (i = 0; i < ARRAY_SIZE(registers); i++)
534 if (registers[i].save && (priv->rev >= registers[i].rev))
535 tilcdc_write(ddev, registers[i].reg, priv->saved_register[n++]);
536
537 drm_kms_helper_poll_enable(ddev);
538
539 return 0;
540}
541#endif
542
543static const struct dev_pm_ops tilcdc_pm_ops = {
544 SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume)
545};
546
547/*
548 * Platform driver:
549 */
550
551static int tilcdc_pdev_probe(struct platform_device *pdev)
552{
553 /* bail out early if no DT data: */
554 if (!pdev->dev.of_node) {
555 dev_err(&pdev->dev, "device-tree data is missing\n");
556 return -ENXIO;
557 }
558
559 return drm_platform_init(&tilcdc_driver, pdev);
560}
561
562static int tilcdc_pdev_remove(struct platform_device *pdev)
563{
564 drm_platform_exit(&tilcdc_driver, pdev);
565
566 return 0;
567}
568
569static struct of_device_id tilcdc_of_match[] = {
570 { .compatible = "ti,am33xx-tilcdc", },
571 { },
572};
573MODULE_DEVICE_TABLE(of, tilcdc_of_match);
574
575static struct platform_driver tilcdc_platform_driver = {
576 .probe = tilcdc_pdev_probe,
577 .remove = tilcdc_pdev_remove,
578 .driver = {
579 .owner = THIS_MODULE,
580 .name = "tilcdc",
581 .pm = &tilcdc_pm_ops,
582 .of_match_table = tilcdc_of_match,
583 },
584};
585
586static int __init tilcdc_drm_init(void)
587{
588 DBG("init");
589 tilcdc_tfp410_init();
590 return platform_driver_register(&tilcdc_platform_driver);
591}
592
593static void __exit tilcdc_drm_fini(void)
594{
595 DBG("fini");
596 tilcdc_tfp410_fini();
597 platform_driver_unregister(&tilcdc_platform_driver);
598}
599
600module_init(tilcdc_drm_init);
601module_exit(tilcdc_drm_fini);
602
603MODULE_AUTHOR("Rob Clark <robdclark@gmail.com");
604MODULE_DESCRIPTION("TI LCD Controller DRM Driver");
605MODULE_LICENSE("GPL");