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>
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_client_setup.h>
15 #include <drm/drm_crtc.h>
16 #include <drm/drm_drv.h>
17 #include <drm/drm_fbdev_dma.h>
18 #include <drm/drm_gem_dma_helper.h>
19 #include <drm/drm_managed.h>
20 #include <drm/drm_module.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_resume_and_get(tidss
->dev
);
41 void tidss_runtime_put(struct tidss_device
*tidss
)
45 dev_dbg(tidss
->dev
, "%s\n", __func__
);
47 pm_runtime_mark_last_busy(tidss
->dev
);
49 r
= pm_runtime_put_autosuspend(tidss
->dev
);
53 static int __maybe_unused
tidss_pm_runtime_suspend(struct device
*dev
)
55 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
57 dev_dbg(dev
, "%s\n", __func__
);
59 return dispc_runtime_suspend(tidss
->dispc
);
62 static int __maybe_unused
tidss_pm_runtime_resume(struct device
*dev
)
64 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
67 dev_dbg(dev
, "%s\n", __func__
);
69 r
= dispc_runtime_resume(tidss
->dispc
);
76 static int __maybe_unused
tidss_suspend(struct device
*dev
)
78 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
80 dev_dbg(dev
, "%s\n", __func__
);
82 return drm_mode_config_helper_suspend(&tidss
->ddev
);
85 static int __maybe_unused
tidss_resume(struct device
*dev
)
87 struct tidss_device
*tidss
= dev_get_drvdata(dev
);
89 dev_dbg(dev
, "%s\n", __func__
);
91 return drm_mode_config_helper_resume(&tidss
->ddev
);
94 static __maybe_unused
const struct dev_pm_ops tidss_pm_ops
= {
95 SET_SYSTEM_SLEEP_PM_OPS(tidss_suspend
, tidss_resume
)
96 SET_RUNTIME_PM_OPS(tidss_pm_runtime_suspend
, tidss_pm_runtime_resume
, NULL
)
99 /* DRM device Information */
101 static void tidss_release(struct drm_device
*ddev
)
103 drm_kms_helper_poll_fini(ddev
);
106 DEFINE_DRM_GEM_DMA_FOPS(tidss_fops
);
108 static const struct drm_driver tidss_driver
= {
109 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_ATOMIC
,
111 .release
= tidss_release
,
112 DRM_GEM_DMA_DRIVER_OPS_VMAP
,
113 DRM_FBDEV_DMA_DRIVER_OPS
,
115 .desc
= "TI Keystone DSS",
121 static int tidss_probe(struct platform_device
*pdev
)
123 struct device
*dev
= &pdev
->dev
;
124 struct tidss_device
*tidss
;
125 struct drm_device
*ddev
;
129 dev_dbg(dev
, "%s\n", __func__
);
131 tidss
= devm_drm_dev_alloc(&pdev
->dev
, &tidss_driver
,
132 struct tidss_device
, ddev
);
134 return PTR_ERR(tidss
);
139 tidss
->feat
= of_device_get_match_data(dev
);
141 platform_set_drvdata(pdev
, tidss
);
143 spin_lock_init(&tidss
->wait_lock
);
145 ret
= dispc_init(tidss
);
147 dev_err(dev
, "failed to initialize dispc: %d\n", ret
);
151 pm_runtime_enable(dev
);
153 pm_runtime_set_autosuspend_delay(dev
, 1000);
154 pm_runtime_use_autosuspend(dev
);
157 /* If we don't have PM, we need to call resume manually */
158 dispc_runtime_resume(tidss
->dispc
);
161 ret
= tidss_modeset_init(tidss
);
163 if (ret
!= -EPROBE_DEFER
)
164 dev_err(dev
, "failed to init DRM/KMS (%d)\n", ret
);
165 goto err_runtime_suspend
;
168 irq
= platform_get_irq(pdev
, 0);
171 goto err_runtime_suspend
;
175 ret
= tidss_irq_install(ddev
, irq
);
177 dev_err(dev
, "tidss_irq_install failed: %d\n", ret
);
178 goto err_runtime_suspend
;
181 drm_kms_helper_poll_init(ddev
);
183 drm_mode_config_reset(ddev
);
185 ret
= drm_dev_register(ddev
, 0);
187 dev_err(dev
, "failed to register DRM device\n");
188 goto err_irq_uninstall
;
191 drm_client_setup(ddev
, NULL
);
193 dev_dbg(dev
, "%s done\n", __func__
);
198 tidss_irq_uninstall(ddev
);
202 dispc_runtime_suspend(tidss
->dispc
);
204 pm_runtime_dont_use_autosuspend(dev
);
205 pm_runtime_disable(dev
);
210 static void 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 tidss_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_dont_use_autosuspend(dev
);
229 pm_runtime_disable(dev
);
231 /* devm allocated dispc goes away with the dev so mark it NULL */
234 dev_dbg(dev
, "%s done\n", __func__
);
237 static void tidss_shutdown(struct platform_device
*pdev
)
239 drm_atomic_helper_shutdown(platform_get_drvdata(pdev
));
242 static const struct of_device_id tidss_of_table
[] = {
243 { .compatible
= "ti,k2g-dss", .data
= &dispc_k2g_feats
, },
244 { .compatible
= "ti,am625-dss", .data
= &dispc_am625_feats
, },
245 { .compatible
= "ti,am62a7-dss", .data
= &dispc_am62a7_feats
, },
246 { .compatible
= "ti,am65x-dss", .data
= &dispc_am65x_feats
, },
247 { .compatible
= "ti,j721e-dss", .data
= &dispc_j721e_feats
, },
251 MODULE_DEVICE_TABLE(of
, tidss_of_table
);
253 static struct platform_driver tidss_platform_driver
= {
254 .probe
= tidss_probe
,
255 .remove
= tidss_remove
,
256 .shutdown
= tidss_shutdown
,
259 .pm
= pm_ptr(&tidss_pm_ops
),
260 .of_match_table
= tidss_of_table
,
261 .suppress_bind_attrs
= true,
265 drm_module_platform_driver(tidss_platform_driver
);
267 MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
268 MODULE_DESCRIPTION("TI Keystone DSS Driver");
269 MODULE_LICENSE("GPL v2");