diff options
Diffstat (limited to 'drivers/gpu/drm/omapdrm/omap_drv.c')
-rw-r--r-- | drivers/gpu/drm/omapdrm/omap_drv.c | 608 |
1 files changed, 608 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..079c54c6f94c --- /dev/null +++ b/drivers/gpu/drm/omapdrm/omap_drv.c | |||
@@ -0,0 +1,608 @@ | |||
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 | |||
33 | static int num_crtc = CONFIG_DRM_OMAP_NUM_CRTCS; | ||
34 | |||
35 | MODULE_PARM_DESC(num_crtc, "Number of overlays to use as CRTCs"); | ||
36 | module_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 | |||
50 | static 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 | |||
58 | static 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 | |||
63 | static 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 | |||
77 | static 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 | |||
184 | static 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 | |||
194 | static 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 | |||
214 | static 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 | |||
228 | static 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 | |||
238 | static 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 | |||
261 | static 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 | |||
285 | static 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 | |||
306 | struct 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 | */ | ||
329 | static 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 | return -ENOMEM; | ||
340 | |||
341 | priv->omaprev = pdata->omaprev; | ||
342 | |||
343 | dev->dev_private = priv; | ||
344 | |||
345 | priv->wq = alloc_ordered_workqueue("omapdrm", 0); | ||
346 | |||
347 | INIT_LIST_HEAD(&priv->obj_list); | ||
348 | |||
349 | omap_gem_init(dev); | ||
350 | |||
351 | ret = omap_modeset_init(dev); | ||
352 | if (ret) { | ||
353 | dev_err(dev->dev, "omap_modeset_init failed: ret=%d\n", ret); | ||
354 | dev->dev_private = NULL; | ||
355 | kfree(priv); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | ret = drm_vblank_init(dev, priv->num_crtcs); | ||
360 | if (ret) | ||
361 | dev_warn(dev->dev, "could not init vblank\n"); | ||
362 | |||
363 | priv->fbdev = omap_fbdev_init(dev); | ||
364 | if (!priv->fbdev) { | ||
365 | dev_warn(dev->dev, "omap_fbdev_init failed\n"); | ||
366 | /* well, limp along without an fbdev.. maybe X11 will work? */ | ||
367 | } | ||
368 | |||
369 | /* store off drm_device for use in pm ops */ | ||
370 | dev_set_drvdata(dev->dev, dev); | ||
371 | |||
372 | drm_kms_helper_poll_init(dev); | ||
373 | |||
374 | return 0; | ||
375 | } | ||
376 | |||
377 | static int dev_unload(struct drm_device *dev) | ||
378 | { | ||
379 | struct omap_drm_private *priv = dev->dev_private; | ||
380 | |||
381 | DBG("unload: dev=%p", dev); | ||
382 | |||
383 | drm_kms_helper_poll_fini(dev); | ||
384 | drm_vblank_cleanup(dev); | ||
385 | omap_drm_irq_uninstall(dev); | ||
386 | |||
387 | omap_fbdev_free(dev); | ||
388 | omap_modeset_free(dev); | ||
389 | omap_gem_deinit(dev); | ||
390 | |||
391 | flush_workqueue(priv->wq); | ||
392 | destroy_workqueue(priv->wq); | ||
393 | |||
394 | kfree(dev->dev_private); | ||
395 | dev->dev_private = NULL; | ||
396 | |||
397 | dev_set_drvdata(dev->dev, NULL); | ||
398 | |||
399 | return 0; | ||
400 | } | ||
401 | |||
402 | static int dev_open(struct drm_device *dev, struct drm_file *file) | ||
403 | { | ||
404 | file->driver_priv = NULL; | ||
405 | |||
406 | DBG("open: dev=%p, file=%p", dev, file); | ||
407 | |||
408 | return 0; | ||
409 | } | ||
410 | |||
411 | static int dev_firstopen(struct drm_device *dev) | ||
412 | { | ||
413 | DBG("firstopen: dev=%p", dev); | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | /** | ||
418 | * lastclose - clean up after all DRM clients have exited | ||
419 | * @dev: DRM device | ||
420 | * | ||
421 | * Take care of cleaning up after all DRM clients have exited. In the | ||
422 | * mode setting case, we want to restore the kernel's initial mode (just | ||
423 | * in case the last client left us in a bad state). | ||
424 | */ | ||
425 | static void dev_lastclose(struct drm_device *dev) | ||
426 | { | ||
427 | int i; | ||
428 | |||
429 | /* we don't support vga-switcheroo.. so just make sure the fbdev | ||
430 | * mode is active | ||
431 | */ | ||
432 | struct omap_drm_private *priv = dev->dev_private; | ||
433 | int ret; | ||
434 | |||
435 | DBG("lastclose: dev=%p", dev); | ||
436 | |||
437 | if (priv->rotation_prop) { | ||
438 | /* need to restore default rotation state.. not sure | ||
439 | * if there is a cleaner way to restore properties to | ||
440 | * default state? Maybe a flag that properties should | ||
441 | * automatically be restored to default state on | ||
442 | * lastclose? | ||
443 | */ | ||
444 | for (i = 0; i < priv->num_crtcs; i++) { | ||
445 | drm_object_property_set_value(&priv->crtcs[i]->base, | ||
446 | priv->rotation_prop, 0); | ||
447 | } | ||
448 | |||
449 | for (i = 0; i < priv->num_planes; i++) { | ||
450 | drm_object_property_set_value(&priv->planes[i]->base, | ||
451 | priv->rotation_prop, 0); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | drm_modeset_lock_all(dev); | ||
456 | ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev); | ||
457 | drm_modeset_unlock_all(dev); | ||
458 | if (ret) | ||
459 | DBG("failed to restore crtc mode"); | ||
460 | } | ||
461 | |||
462 | static void dev_preclose(struct drm_device *dev, struct drm_file *file) | ||
463 | { | ||
464 | DBG("preclose: dev=%p", dev); | ||
465 | } | ||
466 | |||
467 | static void dev_postclose(struct drm_device *dev, struct drm_file *file) | ||
468 | { | ||
469 | DBG("postclose: dev=%p, file=%p", dev, file); | ||
470 | } | ||
471 | |||
472 | static const struct vm_operations_struct omap_gem_vm_ops = { | ||
473 | .fault = omap_gem_fault, | ||
474 | .open = drm_gem_vm_open, | ||
475 | .close = drm_gem_vm_close, | ||
476 | }; | ||
477 | |||
478 | static const struct file_operations omapdriver_fops = { | ||
479 | .owner = THIS_MODULE, | ||
480 | .open = drm_open, | ||
481 | .unlocked_ioctl = drm_ioctl, | ||
482 | .release = drm_release, | ||
483 | .mmap = omap_gem_mmap, | ||
484 | .poll = drm_poll, | ||
485 | .fasync = drm_fasync, | ||
486 | .read = drm_read, | ||
487 | .llseek = noop_llseek, | ||
488 | }; | ||
489 | |||
490 | static struct drm_driver omap_drm_driver = { | ||
491 | .driver_features = | ||
492 | DRIVER_HAVE_IRQ | DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME, | ||
493 | .load = dev_load, | ||
494 | .unload = dev_unload, | ||
495 | .open = dev_open, | ||
496 | .firstopen = dev_firstopen, | ||
497 | .lastclose = dev_lastclose, | ||
498 | .preclose = dev_preclose, | ||
499 | .postclose = dev_postclose, | ||
500 | .get_vblank_counter = drm_vblank_count, | ||
501 | .enable_vblank = omap_irq_enable_vblank, | ||
502 | .disable_vblank = omap_irq_disable_vblank, | ||
503 | .irq_preinstall = omap_irq_preinstall, | ||
504 | .irq_postinstall = omap_irq_postinstall, | ||
505 | .irq_uninstall = omap_irq_uninstall, | ||
506 | .irq_handler = omap_irq_handler, | ||
507 | #ifdef CONFIG_DEBUG_FS | ||
508 | .debugfs_init = omap_debugfs_init, | ||
509 | .debugfs_cleanup = omap_debugfs_cleanup, | ||
510 | #endif | ||
511 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | ||
512 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | ||
513 | .gem_prime_export = omap_gem_prime_export, | ||
514 | .gem_prime_import = omap_gem_prime_import, | ||
515 | .gem_init_object = omap_gem_init_object, | ||
516 | .gem_free_object = omap_gem_free_object, | ||
517 | .gem_vm_ops = &omap_gem_vm_ops, | ||
518 | .dumb_create = omap_gem_dumb_create, | ||
519 | .dumb_map_offset = omap_gem_dumb_map_offset, | ||
520 | .dumb_destroy = omap_gem_dumb_destroy, | ||
521 | .ioctls = ioctls, | ||
522 | .num_ioctls = DRM_OMAP_NUM_IOCTLS, | ||
523 | .fops = &omapdriver_fops, | ||
524 | .name = DRIVER_NAME, | ||
525 | .desc = DRIVER_DESC, | ||
526 | .date = DRIVER_DATE, | ||
527 | .major = DRIVER_MAJOR, | ||
528 | .minor = DRIVER_MINOR, | ||
529 | .patchlevel = DRIVER_PATCHLEVEL, | ||
530 | }; | ||
531 | |||
532 | static int pdev_suspend(struct platform_device *pDevice, pm_message_t state) | ||
533 | { | ||
534 | DBG(""); | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | static int pdev_resume(struct platform_device *device) | ||
539 | { | ||
540 | DBG(""); | ||
541 | return 0; | ||
542 | } | ||
543 | |||
544 | static void pdev_shutdown(struct platform_device *device) | ||
545 | { | ||
546 | DBG(""); | ||
547 | } | ||
548 | |||
549 | static int pdev_probe(struct platform_device *device) | ||
550 | { | ||
551 | DBG("%s", device->name); | ||
552 | return drm_platform_init(&omap_drm_driver, device); | ||
553 | } | ||
554 | |||
555 | static int pdev_remove(struct platform_device *device) | ||
556 | { | ||
557 | DBG(""); | ||
558 | drm_platform_exit(&omap_drm_driver, device); | ||
559 | |||
560 | platform_driver_unregister(&omap_dmm_driver); | ||
561 | return 0; | ||
562 | } | ||
563 | |||
564 | #ifdef CONFIG_PM | ||
565 | static const struct dev_pm_ops omapdrm_pm_ops = { | ||
566 | .resume = omap_gem_resume, | ||
567 | }; | ||
568 | #endif | ||
569 | |||
570 | struct platform_driver pdev = { | ||
571 | .driver = { | ||
572 | .name = DRIVER_NAME, | ||
573 | .owner = THIS_MODULE, | ||
574 | #ifdef CONFIG_PM | ||
575 | .pm = &omapdrm_pm_ops, | ||
576 | #endif | ||
577 | }, | ||
578 | .probe = pdev_probe, | ||
579 | .remove = pdev_remove, | ||
580 | .suspend = pdev_suspend, | ||
581 | .resume = pdev_resume, | ||
582 | .shutdown = pdev_shutdown, | ||
583 | }; | ||
584 | |||
585 | static int __init omap_drm_init(void) | ||
586 | { | ||
587 | DBG("init"); | ||
588 | if (platform_driver_register(&omap_dmm_driver)) { | ||
589 | /* we can continue on without DMM.. so not fatal */ | ||
590 | dev_err(NULL, "DMM registration failed\n"); | ||
591 | } | ||
592 | return platform_driver_register(&pdev); | ||
593 | } | ||
594 | |||
595 | static void __exit omap_drm_fini(void) | ||
596 | { | ||
597 | DBG("fini"); | ||
598 | platform_driver_unregister(&pdev); | ||
599 | } | ||
600 | |||
601 | /* need late_initcall() so we load after dss_driver's are loaded */ | ||
602 | late_initcall(omap_drm_init); | ||
603 | module_exit(omap_drm_fini); | ||
604 | |||
605 | MODULE_AUTHOR("Rob Clark <rob@ti.com>"); | ||
606 | MODULE_DESCRIPTION("OMAP DRM Display Driver"); | ||
607 | MODULE_ALIAS("platform:" DRIVER_NAME); | ||
608 | MODULE_LICENSE("GPL v2"); | ||