aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/omapdrm/omap_drv.c
diff options
context:
space:
mode:
authorRob Clark <robdclark@gmail.com>2013-02-11 12:43:09 -0500
committerRob Clark <robdclark@gmail.com>2013-02-16 17:38:06 -0500
commit8bb0daffb0b8e45188066255b4203446eae181f1 (patch)
treec1a324b863df57becdfab54c9325231bbb853b56 /drivers/gpu/drm/omapdrm/omap_drv.c
parenta4462f246c8821f625f45bce52c7ca7e0207dffe (diff)
drm/omap: move out of staging
Now that the omapdss interface has been reworked so that omapdrm can use dispc directly, we have been able to fix the remaining functional kms issues with omapdrm. And in the mean time the PM sequencing and many other of that open issues have been solved. So I think it makes sense to finally move omapdrm out of staging. Signed-off-by: Rob Clark <robdclark@gmail.com>
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_drv.c')
-rw-r--r--drivers/gpu/drm/omapdrm/omap_drv.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
new file mode 100644
index 000000000000..9083538bd16a
--- /dev/null
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -0,0 +1,610 @@
1/*
2 * drivers/gpu/drm/omapdrm/omap_drv.c
3 *
4 * Copyright (C) 2011 Texas Instruments
5 * Author: Rob Clark <rob@ti.com>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published by
9 * the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 * more details.
15 *
16 * You should have received a copy of the GNU General Public License along with
17 * this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "omap_drv.h"
21
22#include "drm_crtc_helper.h"
23#include "drm_fb_helper.h"
24#include "omap_dmm_tiler.h"
25
26#define DRIVER_NAME MODULE_NAME
27#define DRIVER_DESC "OMAP DRM"
28#define DRIVER_DATE "20110917"
29#define DRIVER_MAJOR 1
30#define DRIVER_MINOR 0
31#define DRIVER_PATCHLEVEL 0
32
33static int num_crtc = CONFIG_DRM_OMAP_NUM_CRTCS;
34
35MODULE_PARM_DESC(num_crtc, "Number of overlays to use as CRTCs");
36module_param(num_crtc, int, 0600);
37
38/*
39 * mode config funcs
40 */
41
42/* Notes about mapping DSS and DRM entities:
43 * CRTC: overlay
44 * encoder: manager.. with some extension to allow one primary CRTC
45 * and zero or more video CRTC's to be mapped to one encoder?
46 * connector: dssdev.. manager can be attached/detached from different
47 * devices
48 */
49
50static void omap_fb_output_poll_changed(struct drm_device *dev)
51{
52 struct omap_drm_private *priv = dev->dev_private;
53 DBG("dev=%p", dev);
54 if (priv->fbdev)
55 drm_fb_helper_hotplug_event(priv->fbdev);
56}
57
58static const struct drm_mode_config_funcs omap_mode_config_funcs = {
59 .fb_create = omap_framebuffer_create,
60 .output_poll_changed = omap_fb_output_poll_changed,
61};
62
63static int get_connector_type(struct omap_dss_device *dssdev)
64{
65 switch (dssdev->type) {
66 case OMAP_DISPLAY_TYPE_HDMI:
67 return DRM_MODE_CONNECTOR_HDMIA;
68 case OMAP_DISPLAY_TYPE_DPI:
69 if (!strcmp(dssdev->name, "dvi"))
70 return DRM_MODE_CONNECTOR_DVID;
71 /* fallthrough */
72 default:
73 return DRM_MODE_CONNECTOR_Unknown;
74 }
75}
76
77static int omap_modeset_init(struct drm_device *dev)
78{
79 struct omap_drm_private *priv = dev->dev_private;
80 struct omap_dss_device *dssdev = NULL;
81 int num_ovls = dss_feat_get_num_ovls();
82 int id;
83
84 drm_mode_config_init(dev);
85
86 omap_drm_irq_install(dev);
87
88 /*
89 * Create private planes and CRTCs for the last NUM_CRTCs overlay
90 * plus manager:
91 */
92 for (id = 0; id < min(num_crtc, num_ovls); id++) {
93 struct drm_plane *plane;
94 struct drm_crtc *crtc;
95
96 plane = omap_plane_init(dev, id, true);
97 crtc = omap_crtc_init(dev, plane, pipe2chan(id), id);
98
99 BUG_ON(priv->num_crtcs >= ARRAY_SIZE(priv->crtcs));
100 priv->crtcs[id] = crtc;
101 priv->num_crtcs++;
102
103 priv->planes[id] = plane;
104 priv->num_planes++;
105 }
106
107 /*
108 * Create normal planes for the remaining overlays:
109 */
110 for (; id < num_ovls; id++) {
111 struct drm_plane *plane = omap_plane_init(dev, id, false);
112
113 BUG_ON(priv->num_planes >= ARRAY_SIZE(priv->planes));
114 priv->planes[priv->num_planes++] = plane;
115 }
116
117 for_each_dss_dev(dssdev) {
118 struct drm_connector *connector;
119 struct drm_encoder *encoder;
120
121 if (!dssdev->driver) {
122 dev_warn(dev->dev, "%s has no driver.. skipping it\n",
123 dssdev->name);
124 return 0;
125 }
126
127 if (!(dssdev->driver->get_timings ||
128 dssdev->driver->read_edid)) {
129 dev_warn(dev->dev, "%s driver does not support "
130 "get_timings or read_edid.. skipping it!\n",
131 dssdev->name);
132 return 0;
133 }
134
135 encoder = omap_encoder_init(dev, dssdev);
136
137 if (!encoder) {
138 dev_err(dev->dev, "could not create encoder: %s\n",
139 dssdev->name);
140 return -ENOMEM;
141 }
142
143 connector = omap_connector_init(dev,
144 get_connector_type(dssdev), dssdev, encoder);
145
146 if (!connector) {
147 dev_err(dev->dev, "could not create connector: %s\n",
148 dssdev->name);
149 return -ENOMEM;
150 }
151
152 BUG_ON(priv->num_encoders >= ARRAY_SIZE(priv->encoders));
153 BUG_ON(priv->num_connectors >= ARRAY_SIZE(priv->connectors));
154
155 priv->encoders[priv->num_encoders++] = encoder;
156 priv->connectors[priv->num_connectors++] = connector;
157
158 drm_mode_connector_attach_encoder(connector, encoder);
159
160 /* figure out which crtc's we can connect the encoder to: */
161 encoder->possible_crtcs = 0;
162 for (id = 0; id < priv->num_crtcs; id++) {
163 enum omap_dss_output_id supported_outputs =
164 dss_feat_get_supported_outputs(pipe2chan(id));
165 if (supported_outputs & dssdev->output->id)
166 encoder->possible_crtcs |= (1 << id);
167 }
168 }
169
170 dev->mode_config.min_width = 32;
171 dev->mode_config.min_height = 32;
172
173 /* note: eventually will need some cpu_is_omapXYZ() type stuff here
174 * to fill in these limits properly on different OMAP generations..
175 */
176 dev->mode_config.max_width = 2048;
177 dev->mode_config.max_height = 2048;
178
179 dev->mode_config.funcs = &omap_mode_config_funcs;
180
181 return 0;
182}
183
184static void omap_modeset_free(struct drm_device *dev)
185{
186 drm_mode_config_cleanup(dev);
187}
188
189/*
190 * drm ioctl funcs
191 */
192
193
194static int ioctl_get_param(struct drm_device *dev, void *data,
195 struct drm_file *file_priv)
196{
197 struct omap_drm_private *priv = dev->dev_private;
198 struct drm_omap_param *args = data;
199
200 DBG("%p: param=%llu", dev, args->param);
201
202 switch (args->param) {
203 case OMAP_PARAM_CHIPSET_ID:
204 args->value = priv->omaprev;
205 break;
206 default:
207 DBG("unknown parameter %lld", args->param);
208 return -EINVAL;
209 }
210
211 return 0;
212}
213
214static int ioctl_set_param(struct drm_device *dev, void *data,
215 struct drm_file *file_priv)
216{
217 struct drm_omap_param *args = data;
218
219 switch (args->param) {
220 default:
221 DBG("unknown parameter %lld", args->param);
222 return -EINVAL;
223 }
224
225 return 0;
226}
227
228static int ioctl_gem_new(struct drm_device *dev, void *data,
229 struct drm_file *file_priv)
230{
231 struct drm_omap_gem_new *args = data;
232 VERB("%p:%p: size=0x%08x, flags=%08x", dev, file_priv,
233 args->size.bytes, args->flags);
234 return omap_gem_new_handle(dev, file_priv, args->size,
235 args->flags, &args->handle);
236}
237
238static int ioctl_gem_cpu_prep(struct drm_device *dev, void *data,
239 struct drm_file *file_priv)
240{
241 struct drm_omap_gem_cpu_prep *args = data;
242 struct drm_gem_object *obj;
243 int ret;
244
245 VERB("%p:%p: handle=%d, op=%x", dev, file_priv, args->handle, args->op);
246
247 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
248 if (!obj)
249 return -ENOENT;
250
251 ret = omap_gem_op_sync(obj, args->op);
252
253 if (!ret)
254 ret = omap_gem_op_start(obj, args->op);
255
256 drm_gem_object_unreference_unlocked(obj);
257
258 return ret;
259}
260
261static int ioctl_gem_cpu_fini(struct drm_device *dev, void *data,
262 struct drm_file *file_priv)
263{
264 struct drm_omap_gem_cpu_fini *args = data;
265 struct drm_gem_object *obj;
266 int ret;
267
268 VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
269
270 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
271 if (!obj)
272 return -ENOENT;
273
274 /* XXX flushy, flushy */
275 ret = 0;
276
277 if (!ret)
278 ret = omap_gem_op_finish(obj, args->op);
279
280 drm_gem_object_unreference_unlocked(obj);
281
282 return ret;
283}
284
285static int ioctl_gem_info(struct drm_device *dev, void *data,
286 struct drm_file *file_priv)
287{
288 struct drm_omap_gem_info *args = data;
289 struct drm_gem_object *obj;
290 int ret = 0;
291
292 VERB("%p:%p: handle=%d", dev, file_priv, args->handle);
293
294 obj = drm_gem_object_lookup(dev, file_priv, args->handle);
295 if (!obj)
296 return -ENOENT;
297
298 args->size = omap_gem_mmap_size(obj);
299 args->offset = omap_gem_mmap_offset(obj);
300
301 drm_gem_object_unreference_unlocked(obj);
302
303 return ret;
304}
305
306struct drm_ioctl_desc ioctls[DRM_COMMAND_END - DRM_COMMAND_BASE] = {
307 DRM_IOCTL_DEF_DRV(OMAP_GET_PARAM, ioctl_get_param, DRM_UNLOCKED|DRM_AUTH),
308 DRM_IOCTL_DEF_DRV(OMAP_SET_PARAM, ioctl_set_param, DRM_UNLOCKED|DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
309 DRM_IOCTL_DEF_DRV(OMAP_GEM_NEW, ioctl_gem_new, DRM_UNLOCKED|DRM_AUTH),
310 DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_PREP, ioctl_gem_cpu_prep, DRM_UNLOCKED|DRM_AUTH),
311 DRM_IOCTL_DEF_DRV(OMAP_GEM_CPU_FINI, ioctl_gem_cpu_fini, DRM_UNLOCKED|DRM_AUTH),
312 DRM_IOCTL_DEF_DRV(OMAP_GEM_INFO, ioctl_gem_info, DRM_UNLOCKED|DRM_AUTH),
313};
314
315/*
316 * drm driver funcs
317 */
318
319/**
320 * load - setup chip and create an initial config
321 * @dev: DRM device
322 * @flags: startup flags
323 *
324 * The driver load routine has to do several things:
325 * - initialize the memory manager
326 * - allocate initial config memory
327 * - setup the DRM framebuffer with the allocated memory
328 */
329static int dev_load(struct drm_device *dev, unsigned long flags)
330{
331 struct omap_drm_platform_data *pdata = dev->dev->platform_data;
332 struct omap_drm_private *priv;
333 int ret;
334
335 DBG("load: dev=%p", dev);
336
337 priv = kzalloc(sizeof(*priv), GFP_KERNEL);
338 if (!priv) {
339 dev_err(dev->dev, "could not allocate priv\n");
340 return -ENOMEM;
341 }
342
343 priv->omaprev = pdata->omaprev;
344
345 dev->dev_private = priv;
346
347 priv->wq = alloc_ordered_workqueue("omapdrm", 0);
348
349 INIT_LIST_HEAD(&priv->obj_list);
350
351 omap_gem_init(dev);
352
353 ret = omap_modeset_init(dev);
354 if (ret) {
355 dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret);
356 dev->dev_private = NULL;
357 kfree(priv);
358 return ret;
359 }
360
361 ret = drm_vblank_init(dev, priv->num_crtcs);
362 if (ret)
363 dev_warn(dev->dev, "could not init vblank\n");
364
365 priv->fbdev = omap_fbdev_init(dev);
366 if (!priv->fbdev) {
367 dev_warn(dev->dev, "omap_fbdev_init failed\n");
368 /* well, limp along without an fbdev.. maybe X11 will work? */
369 }
370
371 /* store off drm_device for use in pm ops */
372 dev_set_drvdata(dev->dev, dev);
373
374 drm_kms_helper_poll_init(dev);
375
376 return 0;
377}
378
379static int dev_unload(struct drm_device *dev)
380{
381 struct omap_drm_private *priv = dev->dev_private;
382
383 DBG("unload: dev=%p", dev);
384
385 drm_kms_helper_poll_fini(dev);
386 drm_vblank_cleanup(dev);
387 omap_drm_irq_uninstall(dev);
388
389 omap_fbdev_free(dev);
390 omap_modeset_free(dev);
391 omap_gem_deinit(dev);
392
393 flush_workqueue(priv->wq);
394 destroy_workqueue(priv->wq);
395
396 kfree(dev->dev_private);
397 dev->dev_private = NULL;
398
399 dev_set_drvdata(dev->dev, NULL);
400
401 return 0;
402}
403
404static int dev_open(struct drm_device *dev, struct drm_file *file)
405{
406 file->driver_priv = NULL;
407
408 DBG("open: dev=%p, file=%p", dev, file);
409
410 return 0;
411}
412
413static int dev_firstopen(struct drm_device *dev)
414{
415 DBG("firstopen: dev=%p", dev);
416 return 0;
417}
418
419/**
420 * lastclose - clean up after all DRM clients have exited
421 * @dev: DRM device
422 *
423 * Take care of cleaning up after all DRM clients have exited. In the
424 * mode setting case, we want to restore the kernel's initial mode (just
425 * in case the last client left us in a bad state).
426 */
427static void dev_lastclose(struct drm_device *dev)
428{
429 int i;
430
431 /* we don't support vga-switcheroo.. so just make sure the fbdev
432 * mode is active
433 */
434 struct omap_drm_private *priv = dev->dev_private;
435 int ret;
436
437 DBG("lastclose: dev=%p", dev);
438
439 if (priv->rotation_prop) {
440 /* need to restore default rotation state.. not sure
441 * if there is a cleaner way to restore properties to
442 * default state? Maybe a flag that properties should
443 * automatically be restored to default state on
444 * lastclose?
445 */
446 for (i = 0; i < priv->num_crtcs; i++) {
447 drm_object_property_set_value(&priv->crtcs[i]->base,
448 priv->rotation_prop, 0);
449 }
450
451 for (i = 0; i < priv->num_planes; i++) {
452 drm_object_property_set_value(&priv->planes[i]->base,
453 priv->rotation_prop, 0);
454 }
455 }
456
457 drm_modeset_lock_all(dev);
458 ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
459 drm_modeset_unlock_all(dev);
460 if (ret)
461 DBG("failed to restore crtc mode");
462}
463
464static void dev_preclose(struct drm_device *dev, struct drm_file *file)
465{
466 DBG("preclose: dev=%p", dev);
467}
468
469static void dev_postclose(struct drm_device *dev, struct drm_file *file)
470{
471 DBG("postclose: dev=%p, file=%p", dev, file);
472}
473
474static const struct vm_operations_struct omap_gem_vm_ops = {
475 .fault = omap_gem_fault,
476 .open = drm_gem_vm_open,
477 .close = drm_gem_vm_close,
478};
479
480static const struct file_operations omapdriver_fops = {
481 .owner = THIS_MODULE,
482 .open = drm_open,
483 .unlocked_ioctl = drm_ioctl,
484 .release = drm_release,
485 .mmap = omap_gem_mmap,
486 .poll = drm_poll,
487 .fasync = drm_fasync,
488 .read = drm_read,
489 .llseek = noop_llseek,
490};
491
492static struct drm_driver omap_drm_driver = {
493 .driver_features =
494 DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME,
495 .load = dev_load,
496 .unload = dev_unload,
497 .open = dev_open,
498 .firstopen = dev_firstopen,
499 .lastclose = dev_lastclose,
500 .preclose = dev_preclose,
501 .postclose = dev_postclose,
502 .get_vblank_counter = drm_vblank_count,
503 .enable_vblank = omap_irq_enable_vblank,
504 .disable_vblank = omap_irq_disable_vblank,
505 .irq_preinstall = omap_irq_preinstall,
506 .irq_postinstall = omap_irq_postinstall,
507 .irq_uninstall = omap_irq_uninstall,
508 .irq_handler = omap_irq_handler,
509#ifdef CONFIG_DEBUG_FS
510 .debugfs_init = omap_debugfs_init,
511 .debugfs_cleanup = omap_debugfs_cleanup,
512#endif
513 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
514 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
515 .gem_prime_export = omap_gem_prime_export,
516 .gem_prime_import = omap_gem_prime_import,
517 .gem_init_object = omap_gem_init_object,
518 .gem_free_object = omap_gem_free_object,
519 .gem_vm_ops = &omap_gem_vm_ops,
520 .dumb_create = omap_gem_dumb_create,
521 .dumb_map_offset = omap_gem_dumb_map_offset,
522 .dumb_destroy = omap_gem_dumb_destroy,
523 .ioctls = ioctls,
524 .num_ioctls = DRM_OMAP_NUM_IOCTLS,
525 .fops = &omapdriver_fops,
526 .name = DRIVER_NAME,
527 .desc = DRIVER_DESC,
528 .date = DRIVER_DATE,
529 .major = DRIVER_MAJOR,
530 .minor = DRIVER_MINOR,
531 .patchlevel = DRIVER_PATCHLEVEL,
532};
533
534static int pdev_suspend(struct platform_device *pDevice, pm_message_t state)
535{
536 DBG("");
537 return 0;
538}
539
540static int pdev_resume(struct platform_device *device)
541{
542 DBG("");
543 return 0;
544}
545
546static void pdev_shutdown(struct platform_device *device)
547{
548 DBG("");
549}
550
551static int pdev_probe(struct platform_device *device)
552{
553 DBG("%s", device->name);
554 return drm_platform_init(&omap_drm_driver, device);
555}
556
557static int pdev_remove(struct platform_device *device)
558{
559 DBG("");
560 drm_platform_exit(&omap_drm_driver, device);
561
562 platform_driver_unregister(&omap_dmm_driver);
563 return 0;
564}
565
566#ifdef CONFIG_PM
567static const struct dev_pm_ops omapdrm_pm_ops = {
568 .resume = omap_gem_resume,
569};
570#endif
571
572struct platform_driver pdev = {
573 .driver = {
574 .name = DRIVER_NAME,
575 .owner = THIS_MODULE,
576#ifdef CONFIG_PM
577 .pm = &omapdrm_pm_ops,
578#endif
579 },
580 .probe = pdev_probe,
581 .remove = pdev_remove,
582 .suspend = pdev_suspend,
583 .resume = pdev_resume,
584 .shutdown = pdev_shutdown,
585};
586
587static int __init omap_drm_init(void)
588{
589 DBG("init");
590 if (platform_driver_register(&omap_dmm_driver)) {
591 /* we can continue on without DMM.. so not fatal */
592 dev_err(NULL, "DMM registration failed\n");
593 }
594 return platform_driver_register(&pdev);
595}
596
597static void __exit omap_drm_fini(void)
598{
599 DBG("fini");
600 platform_driver_unregister(&pdev);
601}
602
603/* need late_initcall() so we load after dss_driver's are loaded */
604late_initcall(omap_drm_init);
605module_exit(omap_drm_fini);
606
607MODULE_AUTHOR("Rob Clark <rob@ti.com>");
608MODULE_DESCRIPTION("OMAP DRM Display Driver");
609MODULE_ALIAS("platform:" DRIVER_NAME);
610MODULE_LICENSE("GPL v2");