blob: c35a248c5e35900fbfbb2adc943ce56103a4ea2c [file] [log] [blame]
Tomi Valkeinen559d6702009-11-03 11:23:50 +02001/*
2 * linux/drivers/video/omap2/dss/core.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#define DSS_SUBSYS_NAME "CORE"
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/clk.h>
28#include <linux/err.h>
29#include <linux/platform_device.h>
30#include <linux/seq_file.h>
31#include <linux/debugfs.h>
32#include <linux/io.h>
33#include <linux/device.h>
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020034#include <linux/regulator/consumer.h>
Tomi Valkeinen2b8501d2012-06-26 15:52:16 +030035#include <linux/suspend.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020036
Tomi Valkeinena0b38cc2011-05-11 14:05:07 +030037#include <video/omapdss.h>
Tomi Valkeinen559d6702009-11-03 11:23:50 +020038
39#include "dss.h"
Archit Tanejaa0acb552010-09-15 19:20:00 +053040#include "dss_features.h"
Tomi Valkeinen559d6702009-11-03 11:23:50 +020041
42static struct {
43 struct platform_device *pdev;
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020044
45 struct regulator *vdds_dsi_reg;
46 struct regulator *vdds_sdi_reg;
Tomi Valkeinenc018c672012-02-23 13:00:51 +020047
48 const char *default_display_name;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020049} core;
50
Tomi Valkeinen559d6702009-11-03 11:23:50 +020051static char *def_disp_name;
52module_param_named(def_disp, def_disp_name, charp, 0);
Jani Nikulaac425ed2011-02-16 04:53:44 -060053MODULE_PARM_DESC(def_disp, "default display name");
Tomi Valkeinen559d6702009-11-03 11:23:50 +020054
55#ifdef DEBUG
Rusty Russell90ab5ee2012-01-13 09:32:20 +103056bool dss_debug;
Tomi Valkeinen559d6702009-11-03 11:23:50 +020057module_param_named(debug, dss_debug, bool, 0644);
58#endif
59
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +020060/* REGULATORS */
61
62struct regulator *dss_get_vdds_dsi(void)
63{
64 struct regulator *reg;
65
66 if (core.vdds_dsi_reg != NULL)
67 return core.vdds_dsi_reg;
68
69 reg = regulator_get(&core.pdev->dev, "vdds_dsi");
70 if (!IS_ERR(reg))
71 core.vdds_dsi_reg = reg;
72
73 return reg;
74}
75
76struct regulator *dss_get_vdds_sdi(void)
77{
78 struct regulator *reg;
79
80 if (core.vdds_sdi_reg != NULL)
81 return core.vdds_sdi_reg;
82
83 reg = regulator_get(&core.pdev->dev, "vdds_sdi");
84 if (!IS_ERR(reg))
85 core.vdds_sdi_reg = reg;
86
87 return reg;
88}
89
Tomi Valkeinen00928ea2012-02-20 11:50:06 +020090int dss_get_ctx_loss_count(struct device *dev)
91{
92 struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
93 int cnt;
94
95 if (!board_data->get_context_loss_count)
96 return -ENOENT;
97
98 cnt = board_data->get_context_loss_count(dev);
99
100 WARN_ONCE(cnt < 0, "get_context_loss_count failed: %d\n", cnt);
101
102 return cnt;
103}
104
105int dss_dsi_enable_pads(int dsi_id, unsigned lane_mask)
106{
107 struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
108
109 if (!board_data->dsi_enable_pads)
110 return -ENOENT;
111
112 return board_data->dsi_enable_pads(dsi_id, lane_mask);
113}
114
115void dss_dsi_disable_pads(int dsi_id, unsigned lane_mask)
116{
117 struct omap_dss_board_info *board_data = core.pdev->dev.platform_data;
118
119 if (!board_data->dsi_enable_pads)
120 return;
121
122 return board_data->dsi_disable_pads(dsi_id, lane_mask);
123}
124
Tomi Valkeinena8081d32012-03-08 12:52:38 +0200125int dss_set_min_bus_tput(struct device *dev, unsigned long tput)
126{
127 struct omap_dss_board_info *pdata = core.pdev->dev.platform_data;
128
129 if (pdata->set_min_bus_tput)
130 return pdata->set_min_bus_tput(dev, tput);
131 else
132 return 0;
133}
134
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200135#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200136static int dss_debug_show(struct seq_file *s, void *unused)
137{
138 void (*func)(struct seq_file *) = s->private;
139 func(s);
140 return 0;
141}
142
143static int dss_debug_open(struct inode *inode, struct file *file)
144{
145 return single_open(file, dss_debug_show, inode->i_private);
146}
147
148static const struct file_operations dss_debug_fops = {
149 .open = dss_debug_open,
150 .read = seq_read,
151 .llseek = seq_lseek,
152 .release = single_release,
153};
154
155static struct dentry *dss_debugfs_dir;
156
157static int dss_initialize_debugfs(void)
158{
159 dss_debugfs_dir = debugfs_create_dir("omapdss", NULL);
160 if (IS_ERR(dss_debugfs_dir)) {
161 int err = PTR_ERR(dss_debugfs_dir);
162 dss_debugfs_dir = NULL;
163 return err;
164 }
165
166 debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir,
167 &dss_debug_dump_clocks, &dss_debug_fops);
168
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200169 return 0;
170}
171
172static void dss_uninitialize_debugfs(void)
173{
174 if (dss_debugfs_dir)
175 debugfs_remove_recursive(dss_debugfs_dir);
176}
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200177
178int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
179{
180 struct dentry *d;
181
182 d = debugfs_create_file(name, S_IRUGO, dss_debugfs_dir,
183 write, &dss_debug_fops);
184
185 if (IS_ERR(d))
186 return PTR_ERR(d);
187
188 return 0;
189}
Jani Nikula368a1482010-05-07 11:58:41 +0200190#else /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
191static inline int dss_initialize_debugfs(void)
192{
193 return 0;
194}
195static inline void dss_uninitialize_debugfs(void)
196{
197}
Tomi Valkeinen0e9a1262012-05-23 16:45:09 +0300198int dss_debugfs_create_file(const char *name, void (*write)(struct seq_file *))
Tomi Valkeinene40402c2012-03-02 18:01:07 +0200199{
200 return 0;
201}
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200202#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */
203
204/* PLATFORM DEVICE */
Tomi Valkeinen2b8501d2012-06-26 15:52:16 +0300205static int omap_dss_pm_notif(struct notifier_block *b, unsigned long v, void *d)
206{
207 DSSDBG("pm notif %lu\n", v);
208
209 switch (v)
210 {
211 case PM_SUSPEND_PREPARE:
212 DSSDBG("suspending displays\n");
213 return dss_suspend_all_devices();
214
215 case PM_POST_SUSPEND:
216 DSSDBG("resuming displays\n");
217 return dss_resume_all_devices();
218
219 default:
220 return 0;
221 }
222}
223
224static struct notifier_block omap_dss_pm_notif_block =
225{
226 .notifier_call = omap_dss_pm_notif,
227};
228
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +0200229static int __init omap_dss_probe(struct platform_device *pdev)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200230{
231 struct omap_dss_board_info *pdata = pdev->dev.platform_data;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200232 int r;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200233
234 core.pdev = pdev;
235
Archit Tanejaa0acb552010-09-15 19:20:00 +0530236 dss_features_init();
237
Tomi Valkeinen58f255482011-11-04 09:48:54 +0200238 dss_apply_init();
239
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200240 dss_init_overlay_managers(pdev);
241 dss_init_overlays(pdev);
242
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200243 r = dss_initialize_debugfs();
244 if (r)
Jani Nikulafce064c2010-05-07 11:58:42 +0200245 goto err_debugfs;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200246
Tomi Valkeinenc018c672012-02-23 13:00:51 +0200247 if (def_disp_name)
248 core.default_display_name = def_disp_name;
249 else if (pdata->default_device)
250 core.default_display_name = pdata->default_device->name;
251
Tomi Valkeinen2b8501d2012-06-26 15:52:16 +0300252 register_pm_notifier(&omap_dss_pm_notif_block);
253
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200254 return 0;
255
Jani Nikulafce064c2010-05-07 11:58:42 +0200256err_debugfs:
Jani Nikulafce064c2010-05-07 11:58:42 +0200257
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200258 return r;
259}
260
261static int omap_dss_remove(struct platform_device *pdev)
262{
Tomi Valkeinen2b8501d2012-06-26 15:52:16 +0300263 unregister_pm_notifier(&omap_dss_pm_notif_block);
264
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200265 dss_uninitialize_debugfs();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200266
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200267 dss_uninit_overlays(pdev);
268 dss_uninit_overlay_managers(pdev);
269
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200270 return 0;
271}
272
273static void omap_dss_shutdown(struct platform_device *pdev)
274{
275 DSSDBG("shutdown\n");
276 dss_disable_all_devices();
277}
278
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200279static struct platform_driver omap_dss_driver = {
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200280 .remove = omap_dss_remove,
281 .shutdown = omap_dss_shutdown,
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200282 .driver = {
283 .name = "omapdss",
284 .owner = THIS_MODULE,
285 },
286};
287
288/* BUS */
289static int dss_bus_match(struct device *dev, struct device_driver *driver)
290{
291 struct omap_dss_device *dssdev = to_dss_device(dev);
292
293 DSSDBG("bus_match. dev %s/%s, drv %s\n",
294 dev_name(dev), dssdev->driver_name, driver->name);
295
296 return strcmp(dssdev->driver_name, driver->name) == 0;
297}
298
299static ssize_t device_name_show(struct device *dev,
300 struct device_attribute *attr, char *buf)
301{
302 struct omap_dss_device *dssdev = to_dss_device(dev);
303 return snprintf(buf, PAGE_SIZE, "%s\n",
304 dssdev->name ?
305 dssdev->name : "");
306}
307
308static struct device_attribute default_dev_attrs[] = {
309 __ATTR(name, S_IRUGO, device_name_show, NULL),
310 __ATTR_NULL,
311};
312
313static ssize_t driver_name_show(struct device_driver *drv, char *buf)
314{
315 struct omap_dss_driver *dssdrv = to_dss_driver(drv);
316 return snprintf(buf, PAGE_SIZE, "%s\n",
317 dssdrv->driver.name ?
318 dssdrv->driver.name : "");
319}
320static struct driver_attribute default_drv_attrs[] = {
321 __ATTR(name, S_IRUGO, driver_name_show, NULL),
322 __ATTR_NULL,
323};
324
325static struct bus_type dss_bus_type = {
326 .name = "omapdss",
327 .match = dss_bus_match,
328 .dev_attrs = default_dev_attrs,
329 .drv_attrs = default_drv_attrs,
330};
331
332static void dss_bus_release(struct device *dev)
333{
334 DSSDBG("bus_release\n");
335}
336
337static struct device dss_bus = {
338 .release = dss_bus_release,
339};
340
341struct bus_type *dss_get_bus(void)
342{
343 return &dss_bus_type;
344}
345
346/* DRIVER */
347static int dss_driver_probe(struct device *dev)
348{
349 int r;
350 struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
351 struct omap_dss_device *dssdev = to_dss_device(dev);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200352 bool force;
353
354 DSSDBG("driver_probe: dev %s/%s, drv %s\n",
355 dev_name(dev), dssdev->driver_name,
356 dssdrv->driver.name);
357
358 dss_init_device(core.pdev, dssdev);
359
Tomi Valkeinenc018c672012-02-23 13:00:51 +0200360 force = core.default_display_name &&
361 strcmp(core.default_display_name, dssdev->name) == 0;
Tomi Valkeinene020f9a2010-02-17 13:36:48 +0200362 dss_recheck_connections(dssdev, force);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200363
364 r = dssdrv->probe(dssdev);
365
366 if (r) {
367 DSSERR("driver probe failed: %d\n", r);
Tomi Valkeinenc121b152010-02-17 11:50:07 +0200368 dss_uninit_device(core.pdev, dssdev);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200369 return r;
370 }
371
372 DSSDBG("probe done for device %s\n", dev_name(dev));
373
374 dssdev->driver = dssdrv;
375
376 return 0;
377}
378
379static int dss_driver_remove(struct device *dev)
380{
381 struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver);
382 struct omap_dss_device *dssdev = to_dss_device(dev);
383
384 DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev),
385 dssdev->driver_name);
386
387 dssdrv->remove(dssdev);
388
389 dss_uninit_device(core.pdev, dssdev);
390
391 dssdev->driver = NULL;
392
393 return 0;
394}
395
396int omap_dss_register_driver(struct omap_dss_driver *dssdriver)
397{
398 dssdriver->driver.bus = &dss_bus_type;
399 dssdriver->driver.probe = dss_driver_probe;
400 dssdriver->driver.remove = dss_driver_remove;
Tomi Valkeinen96adcec2010-01-11 13:54:33 +0200401
402 if (dssdriver->get_resolution == NULL)
403 dssdriver->get_resolution = omapdss_default_get_resolution;
Tomi Valkeinena2699502010-01-11 14:33:40 +0200404 if (dssdriver->get_recommended_bpp == NULL)
405 dssdriver->get_recommended_bpp =
406 omapdss_default_get_recommended_bpp;
Grazvydas Ignotas4b6430f2012-03-15 20:00:23 +0200407 if (dssdriver->get_timings == NULL)
408 dssdriver->get_timings = omapdss_default_get_timings;
Tomi Valkeinen96adcec2010-01-11 13:54:33 +0200409
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200410 return driver_register(&dssdriver->driver);
411}
412EXPORT_SYMBOL(omap_dss_register_driver);
413
414void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver)
415{
416 driver_unregister(&dssdriver->driver);
417}
418EXPORT_SYMBOL(omap_dss_unregister_driver);
419
420/* DEVICE */
421static void reset_device(struct device *dev, int check)
422{
423 u8 *dev_p = (u8 *)dev;
424 u8 *dev_end = dev_p + sizeof(*dev);
425 void *saved_pdata;
426
427 saved_pdata = dev->platform_data;
428 if (check) {
429 /*
430 * Check if there is any other setting than platform_data
431 * in struct device; warn that these will be reset by our
432 * init.
433 */
434 dev->platform_data = NULL;
435 while (dev_p < dev_end) {
436 if (*dev_p) {
437 WARN("%s: struct device fields will be "
438 "discarded\n",
439 __func__);
440 break;
441 }
442 dev_p++;
443 }
444 }
445 memset(dev, 0, sizeof(*dev));
446 dev->platform_data = saved_pdata;
447}
448
449
450static void omap_dss_dev_release(struct device *dev)
451{
452 reset_device(dev, 0);
453}
454
Tomi Valkeinen35deca32012-03-01 15:45:53 +0200455int omap_dss_register_device(struct omap_dss_device *dssdev,
456 struct device *parent, int disp_num)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200457{
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200458 WARN_ON(!dssdev->driver_name);
459
460 reset_device(&dssdev->dev, 1);
461 dssdev->dev.bus = &dss_bus_type;
Tomi Valkeinen35deca32012-03-01 15:45:53 +0200462 dssdev->dev.parent = parent;
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200463 dssdev->dev.release = omap_dss_dev_release;
Tomi Valkeinen35deca32012-03-01 15:45:53 +0200464 dev_set_name(&dssdev->dev, "display%d", disp_num);
Tomi Valkeinene020f9a2010-02-17 13:36:48 +0200465 return device_register(&dssdev->dev);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200466}
467
Tomi Valkeinen35deca32012-03-01 15:45:53 +0200468void omap_dss_unregister_device(struct omap_dss_device *dssdev)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200469{
470 device_unregister(&dssdev->dev);
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200471}
472
Tomi Valkeinen35deca32012-03-01 15:45:53 +0200473static int dss_unregister_dss_dev(struct device *dev, void *data)
474{
475 struct omap_dss_device *dssdev = to_dss_device(dev);
476 omap_dss_unregister_device(dssdev);
477 return 0;
478}
479
480void omap_dss_unregister_child_devices(struct device *parent)
481{
482 device_for_each_child(parent, NULL, dss_unregister_dss_dev);
483}
484
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200485/* BUS */
Tomi Valkeinen6e7e8f02012-02-17 17:41:13 +0200486static int __init omap_dss_bus_register(void)
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200487{
488 int r;
489
490 r = bus_register(&dss_bus_type);
491 if (r) {
492 DSSERR("bus register failed\n");
493 return r;
494 }
495
496 dev_set_name(&dss_bus, "omapdss");
497 r = device_register(&dss_bus);
498 if (r) {
499 DSSERR("bus driver register failed\n");
500 bus_unregister(&dss_bus_type);
501 return r;
502 }
503
504 return 0;
505}
506
507/* INIT */
Tomi Valkeinen461395c2012-03-02 17:37:53 +0200508static int (*dss_output_drv_reg_funcs[])(void) __initdata = {
509#ifdef CONFIG_OMAP2_DSS_DPI
510 dpi_init_platform_driver,
511#endif
512#ifdef CONFIG_OMAP2_DSS_SDI
513 sdi_init_platform_driver,
514#endif
515#ifdef CONFIG_OMAP2_DSS_RFBI
516 rfbi_init_platform_driver,
517#endif
518#ifdef CONFIG_OMAP2_DSS_VENC
519 venc_init_platform_driver,
520#endif
521#ifdef CONFIG_OMAP2_DSS_DSI
522 dsi_init_platform_driver,
523#endif
524#ifdef CONFIG_OMAP4_DSS_HDMI
525 hdmi_init_platform_driver,
526#endif
527};
528
529static void (*dss_output_drv_unreg_funcs[])(void) __exitdata = {
530#ifdef CONFIG_OMAP2_DSS_DPI
531 dpi_uninit_platform_driver,
532#endif
533#ifdef CONFIG_OMAP2_DSS_SDI
534 sdi_uninit_platform_driver,
535#endif
536#ifdef CONFIG_OMAP2_DSS_RFBI
537 rfbi_uninit_platform_driver,
538#endif
539#ifdef CONFIG_OMAP2_DSS_VENC
540 venc_uninit_platform_driver,
541#endif
542#ifdef CONFIG_OMAP2_DSS_DSI
543 dsi_uninit_platform_driver,
544#endif
545#ifdef CONFIG_OMAP4_DSS_HDMI
546 hdmi_uninit_platform_driver,
547#endif
548};
549
550static bool dss_output_drv_loaded[ARRAY_SIZE(dss_output_drv_reg_funcs)];
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200551
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200552static int __init omap_dss_register_drivers(void)
553{
554 int r;
Tomi Valkeinen461395c2012-03-02 17:37:53 +0200555 int i;
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200556
Tomi Valkeinen11436e12012-03-07 12:53:18 +0200557 r = platform_driver_probe(&omap_dss_driver, omap_dss_probe);
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200558 if (r)
559 return r;
560
561 r = dss_init_platform_driver();
562 if (r) {
563 DSSERR("Failed to initialize DSS platform driver\n");
564 goto err_dss;
565 }
566
567 r = dispc_init_platform_driver();
568 if (r) {
569 DSSERR("Failed to initialize dispc platform driver\n");
570 goto err_dispc;
571 }
572
Tomi Valkeinen461395c2012-03-02 17:37:53 +0200573 /*
574 * It's ok if the output-driver register fails. It happens, for example,
575 * when there is no output-device (e.g. SDI for OMAP4).
576 */
577 for (i = 0; i < ARRAY_SIZE(dss_output_drv_reg_funcs); ++i) {
578 r = dss_output_drv_reg_funcs[i]();
579 if (r == 0)
580 dss_output_drv_loaded[i] = true;
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200581 }
582
583 return 0;
584
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200585err_dispc:
586 dss_uninit_platform_driver();
587err_dss:
588 platform_driver_unregister(&omap_dss_driver);
589
590 return r;
591}
592
593static void __exit omap_dss_unregister_drivers(void)
594{
Tomi Valkeinen461395c2012-03-02 17:37:53 +0200595 int i;
596
597 for (i = 0; i < ARRAY_SIZE(dss_output_drv_unreg_funcs); ++i) {
598 if (dss_output_drv_loaded[i])
599 dss_output_drv_unreg_funcs[i]();
600 }
601
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200602 dispc_uninit_platform_driver();
603 dss_uninit_platform_driver();
604
605 platform_driver_unregister(&omap_dss_driver);
606}
607
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200608#ifdef CONFIG_OMAP2_DSS_MODULE
609static void omap_dss_bus_unregister(void)
610{
611 device_unregister(&dss_bus);
612
613 bus_unregister(&dss_bus_type);
614}
615
616static int __init omap_dss_init(void)
617{
618 int r;
619
620 r = omap_dss_bus_register();
621 if (r)
622 return r;
623
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200624 r = omap_dss_register_drivers();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200625 if (r) {
626 omap_dss_bus_unregister();
627 return r;
628 }
629
630 return 0;
631}
632
633static void __exit omap_dss_exit(void)
634{
Tomi Valkeinen8a2cfea2010-02-04 17:03:41 +0200635 if (core.vdds_dsi_reg != NULL) {
636 regulator_put(core.vdds_dsi_reg);
637 core.vdds_dsi_reg = NULL;
638 }
639
640 if (core.vdds_sdi_reg != NULL) {
641 regulator_put(core.vdds_sdi_reg);
642 core.vdds_sdi_reg = NULL;
643 }
644
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200645 omap_dss_unregister_drivers();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200646
647 omap_dss_bus_unregister();
648}
649
650module_init(omap_dss_init);
651module_exit(omap_dss_exit);
652#else
653static int __init omap_dss_init(void)
654{
655 return omap_dss_bus_register();
656}
657
658static int __init omap_dss_init2(void)
659{
Tomi Valkeinendc7e57f2012-03-19 15:05:02 +0200660 return omap_dss_register_drivers();
Tomi Valkeinen559d6702009-11-03 11:23:50 +0200661}
662
663core_initcall(omap_dss_init);
664device_initcall(omap_dss_init2);
665#endif
666
667MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>");
668MODULE_DESCRIPTION("OMAP2/3 Display Subsystem");
669MODULE_LICENSE("GPL v2");
670