1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
4 * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
7 #include <linux/console.h>
8 #include <linux/of_device.h>
9 #include <linux/module.h>
10 #include <linux/pm_runtime.h>
12 #include <drm/drm_atomic.h>
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_crtc.h>
15 #include <drm/drm_crtc_helper.h>
16 #include <drm/drm_drv.h>
17 #include <drm/drm_fb_helper.h>
18 #include <drm/drm_gem_cma_helper.h>
19 #include <drm/drm_irq.h>
20 #include <drm/drm_managed.h>
21 #include <drm/drm_probe_helper.h>
23 #include "tidss_dispc.h"
24 #include "tidss_drv.h"
25 #include "tidss_kms.h"
26 #include "tidss_irq.h"
28 /* Power management */
30 int tidss_runtime_get(struct tidss_device
*tidss
)
34 dev_dbg(tidss
->dev
, "%s\n", __func__
);
36 r
= pm_runtime_get_sync(tidss
->dev
);
41 void tidss_runtime_put(struct tidss_device
*tidss
)
45 dev_dbg(tidss
->dev
, "%s\n", __func__
);
47 r
= pm_runtime_put_sync(tidss
->dev
);
51 static int __maybe_unused
tidss_pm_runtime_suspend(struct device
*dev
)
53 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
55 dev_dbg(dev
, "%s\n", __func__
);
57 return dispc_runtime_suspend(tidss
->dispc
);
60 static int __maybe_unused
tidss_pm_runtime_resume(struct device
*dev
)
62 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
65 dev_dbg(dev
, "%s\n", __func__
);
67 r
= dispc_runtime_resume(tidss
->dispc
);
74 static int __maybe_unused
tidss_suspend(struct device
*dev
)
76 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
78 dev_dbg(dev
, "%s\n", __func__
);
80 return drm_mode_config_helper_suspend(&tidss
->ddev
);
83 static int __maybe_unused
tidss_resume(struct device
*dev
)
85 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
87 dev_dbg(dev
, "%s\n", __func__
);
89 return drm_mode_config_helper_resume(&tidss
->ddev
);
94 static const struct dev_pm_ops tidss_pm_ops
= {
95 .runtime_suspend
= tidss_pm_runtime_suspend
,
96 .runtime_resume
= tidss_pm_runtime_resume
,
97 SET_SYSTEM_SLEEP_PM_OPS(tidss_suspend
, tidss_resume
)
100 #endif /* CONFIG_PM */
102 /* DRM device Information */
104 static void tidss_release(struct drm_device
*ddev
)
106 drm_kms_helper_poll_fini(ddev
);
109 DEFINE_DRM_GEM_CMA_FOPS(tidss_fops
);
111 static const struct drm_driver tidss_driver
= {
112 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_ATOMIC
,
114 .release
= tidss_release
,
115 DRM_GEM_CMA_DRIVER_OPS_VMAP
,
117 .desc
= "TI Keystone DSS",
122 .irq_preinstall
= tidss_irq_preinstall
,
123 .irq_postinstall
= tidss_irq_postinstall
,
124 .irq_handler
= tidss_irq_handler
,
125 .irq_uninstall
= tidss_irq_uninstall
,
128 static int tidss_probe(struct platform_device
*pdev
)
130 struct device
*dev
= &pdev
->dev
;
131 struct tidss_device
*tidss
;
132 struct drm_device
*ddev
;
136 dev_dbg(dev
, "%s\n", __func__
);
138 tidss
= devm_drm_dev_alloc(&pdev
->dev
, &tidss_driver
,
139 struct tidss_device
, ddev
);
141 return PTR_ERR(tidss
);
146 tidss
->feat
= of_device_get_match_data(dev
);
148 platform_set_drvdata(pdev
, tidss
);
150 ret
= dispc_init(tidss
);
152 dev_err(dev
, "failed to initialize dispc: %d\n", ret
);
156 pm_runtime_enable(dev
);
159 /* If we don't have PM, we need to call resume manually */
160 dispc_runtime_resume(tidss
->dispc
);
163 ret
= tidss_modeset_init(tidss
);
165 if (ret
!= -EPROBE_DEFER
)
166 dev_err(dev
, "failed to init DRM/KMS (%d)\n", ret
);
167 goto err_runtime_suspend
;
170 irq
= platform_get_irq(pdev
, 0);
173 goto err_runtime_suspend
;
176 ret
= drm_irq_install(ddev
, irq
);
178 dev_err(dev
, "drm_irq_install failed: %d\n", ret
);
179 goto err_runtime_suspend
;
182 drm_kms_helper_poll_init(ddev
);
184 drm_mode_config_reset(ddev
);
186 ret
= drm_dev_register(ddev
, 0);
188 dev_err(dev
, "failed to register DRM device\n");
189 goto err_irq_uninstall
;
192 drm_fbdev_generic_setup(ddev
, 32);
194 dev_dbg(dev
, "%s done\n", __func__
);
199 drm_irq_uninstall(ddev
);
203 dispc_runtime_suspend(tidss
->dispc
);
205 pm_runtime_disable(dev
);
210 static int tidss_remove(struct platform_device
*pdev
)
212 struct device
*dev
= &pdev
->dev
;
213 struct tidss_device
*tidss
= platform_get_drvdata(pdev
);
214 struct drm_device
*ddev
= &tidss
->ddev
;
216 dev_dbg(dev
, "%s\n", __func__
);
218 drm_dev_unregister(ddev
);
220 drm_atomic_helper_shutdown(ddev
);
222 drm_irq_uninstall(ddev
);
225 /* If we don't have PM, we need to call suspend manually */
226 dispc_runtime_suspend(tidss
->dispc
);
228 pm_runtime_disable(dev
);
230 /* devm allocated dispc goes away with the dev so mark it NULL */
233 dev_dbg(dev
, "%s done\n", __func__
);
238 static void tidss_shutdown(struct platform_device
*pdev
)
240 drm_atomic_helper_shutdown(platform_get_drvdata(pdev
));
243 static const struct of_device_id tidss_of_table
[] = {
244 { .compatible
= "ti,k2g-dss", .data
= &dispc_k2g_feats
, },
245 { .compatible
= "ti,am65x-dss", .data
= &dispc_am65x_feats
, },
246 { .compatible
= "ti,j721e-dss", .data
= &dispc_j721e_feats
, },
250 MODULE_DEVICE_TABLE(of
, tidss_of_table
);
252 static struct platform_driver tidss_platform_driver
= {
253 .probe
= tidss_probe
,
254 .remove
= tidss_remove
,
255 .shutdown
= tidss_shutdown
,
261 .of_match_table
= tidss_of_table
,
262 .suppress_bind_attrs
= true,
266 module_platform_driver(tidss_platform_driver
);
268 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
269 MODULE_DESCRIPTION("TI Keystone DSS Driver");
270 MODULE_LICENSE("GPL v2");