]>
Commit | Line | Data |
---|---|---|
32a1795f JS |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
9410113f | 3 | * Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/ |
32a1795f JS |
4 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> |
5 | */ | |
6 | ||
7 | #include <linux/console.h> | |
8 | #include <linux/of_device.h> | |
9 | #include <linux/module.h> | |
10 | #include <linux/pm_runtime.h> | |
11 | ||
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> | |
afeeabb8 | 20 | #include <drm/drm_managed.h> |
32a1795f JS |
21 | #include <drm/drm_probe_helper.h> |
22 | ||
23 | #include "tidss_dispc.h" | |
24 | #include "tidss_drv.h" | |
25 | #include "tidss_kms.h" | |
26 | #include "tidss_irq.h" | |
27 | ||
28 | /* Power management */ | |
29 | ||
30 | int tidss_runtime_get(struct tidss_device *tidss) | |
31 | { | |
32 | int r; | |
33 | ||
34 | dev_dbg(tidss->dev, "%s\n", __func__); | |
35 | ||
36 | r = pm_runtime_get_sync(tidss->dev); | |
37 | WARN_ON(r < 0); | |
38 | return r < 0 ? r : 0; | |
39 | } | |
40 | ||
41 | void tidss_runtime_put(struct tidss_device *tidss) | |
42 | { | |
43 | int r; | |
44 | ||
45 | dev_dbg(tidss->dev, "%s\n", __func__); | |
46 | ||
47 | r = pm_runtime_put_sync(tidss->dev); | |
48 | WARN_ON(r < 0); | |
49 | } | |
50 | ||
51 | static int __maybe_unused tidss_pm_runtime_suspend(struct device *dev) | |
52 | { | |
53 | struct tidss_device *tidss = dev_get_drvdata(dev); | |
54 | ||
55 | dev_dbg(dev, "%s\n", __func__); | |
56 | ||
57 | return dispc_runtime_suspend(tidss->dispc); | |
58 | } | |
59 | ||
60 | static int __maybe_unused tidss_pm_runtime_resume(struct device *dev) | |
61 | { | |
62 | struct tidss_device *tidss = dev_get_drvdata(dev); | |
63 | int r; | |
64 | ||
65 | dev_dbg(dev, "%s\n", __func__); | |
66 | ||
67 | r = dispc_runtime_resume(tidss->dispc); | |
68 | if (r) | |
69 | return r; | |
70 | ||
71 | return 0; | |
72 | } | |
73 | ||
74 | static int __maybe_unused tidss_suspend(struct device *dev) | |
75 | { | |
76 | struct tidss_device *tidss = dev_get_drvdata(dev); | |
77 | ||
78 | dev_dbg(dev, "%s\n", __func__); | |
79 | ||
80 | return drm_mode_config_helper_suspend(&tidss->ddev); | |
81 | } | |
82 | ||
83 | static int __maybe_unused tidss_resume(struct device *dev) | |
84 | { | |
85 | struct tidss_device *tidss = dev_get_drvdata(dev); | |
86 | ||
87 | dev_dbg(dev, "%s\n", __func__); | |
88 | ||
89 | return drm_mode_config_helper_resume(&tidss->ddev); | |
90 | } | |
91 | ||
92 | #ifdef CONFIG_PM | |
93 | ||
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) | |
98 | }; | |
99 | ||
100 | #endif /* CONFIG_PM */ | |
101 | ||
102 | /* DRM device Information */ | |
103 | ||
104 | static void tidss_release(struct drm_device *ddev) | |
105 | { | |
32a1795f | 106 | drm_kms_helper_poll_fini(ddev); |
32a1795f JS |
107 | } |
108 | ||
109 | DEFINE_DRM_GEM_CMA_FOPS(tidss_fops); | |
110 | ||
111 | static struct drm_driver tidss_driver = { | |
112 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC, | |
113 | .fops = &tidss_fops, | |
114 | .release = tidss_release, | |
06d66201 | 115 | DRM_GEM_CMA_DRIVER_OPS_VMAP, |
32a1795f JS |
116 | .name = "tidss", |
117 | .desc = "TI Keystone DSS", | |
118 | .date = "20180215", | |
119 | .major = 1, | |
120 | .minor = 0, | |
121 | ||
122 | .irq_preinstall = tidss_irq_preinstall, | |
123 | .irq_postinstall = tidss_irq_postinstall, | |
124 | .irq_handler = tidss_irq_handler, | |
125 | .irq_uninstall = tidss_irq_uninstall, | |
126 | }; | |
127 | ||
128 | static int tidss_probe(struct platform_device *pdev) | |
129 | { | |
130 | struct device *dev = &pdev->dev; | |
131 | struct tidss_device *tidss; | |
132 | struct drm_device *ddev; | |
133 | int ret; | |
134 | int irq; | |
135 | ||
136 | dev_dbg(dev, "%s\n", __func__); | |
137 | ||
b3b13400 DV |
138 | tidss = devm_drm_dev_alloc(&pdev->dev, &tidss_driver, |
139 | struct tidss_device, ddev); | |
140 | if (IS_ERR(tidss)) | |
141 | return PTR_ERR(tidss); | |
32a1795f JS |
142 | |
143 | ddev = &tidss->ddev; | |
144 | ||
32a1795f JS |
145 | tidss->dev = dev; |
146 | tidss->feat = of_device_get_match_data(dev); | |
147 | ||
148 | platform_set_drvdata(pdev, tidss); | |
149 | ||
32a1795f JS |
150 | ret = dispc_init(tidss); |
151 | if (ret) { | |
152 | dev_err(dev, "failed to initialize dispc: %d\n", ret); | |
153 | return ret; | |
154 | } | |
155 | ||
156 | pm_runtime_enable(dev); | |
157 | ||
158 | #ifndef CONFIG_PM | |
159 | /* If we don't have PM, we need to call resume manually */ | |
160 | dispc_runtime_resume(tidss->dispc); | |
161 | #endif | |
162 | ||
163 | ret = tidss_modeset_init(tidss); | |
164 | if (ret < 0) { | |
165 | if (ret != -EPROBE_DEFER) | |
166 | dev_err(dev, "failed to init DRM/KMS (%d)\n", ret); | |
167 | goto err_runtime_suspend; | |
168 | } | |
169 | ||
170 | irq = platform_get_irq(pdev, 0); | |
171 | if (irq < 0) { | |
172 | ret = irq; | |
173 | goto err_runtime_suspend; | |
174 | } | |
175 | ||
176 | ret = drm_irq_install(ddev, irq); | |
177 | if (ret) { | |
178 | dev_err(dev, "drm_irq_install failed: %d\n", ret); | |
179 | goto err_runtime_suspend; | |
180 | } | |
181 | ||
182 | drm_kms_helper_poll_init(ddev); | |
183 | ||
184 | drm_mode_config_reset(ddev); | |
185 | ||
186 | ret = drm_dev_register(ddev, 0); | |
187 | if (ret) { | |
188 | dev_err(dev, "failed to register DRM device\n"); | |
189 | goto err_irq_uninstall; | |
190 | } | |
191 | ||
192 | drm_fbdev_generic_setup(ddev, 32); | |
193 | ||
194 | dev_dbg(dev, "%s done\n", __func__); | |
195 | ||
196 | return 0; | |
197 | ||
198 | err_irq_uninstall: | |
199 | drm_irq_uninstall(ddev); | |
200 | ||
201 | err_runtime_suspend: | |
202 | #ifndef CONFIG_PM | |
203 | dispc_runtime_suspend(tidss->dispc); | |
204 | #endif | |
205 | pm_runtime_disable(dev); | |
206 | ||
207 | return ret; | |
208 | } | |
209 | ||
210 | static int tidss_remove(struct platform_device *pdev) | |
211 | { | |
212 | struct device *dev = &pdev->dev; | |
213 | struct tidss_device *tidss = platform_get_drvdata(pdev); | |
214 | struct drm_device *ddev = &tidss->ddev; | |
215 | ||
216 | dev_dbg(dev, "%s\n", __func__); | |
217 | ||
218 | drm_dev_unregister(ddev); | |
219 | ||
220 | drm_atomic_helper_shutdown(ddev); | |
221 | ||
222 | drm_irq_uninstall(ddev); | |
223 | ||
224 | #ifndef CONFIG_PM | |
225 | /* If we don't have PM, we need to call suspend manually */ | |
226 | dispc_runtime_suspend(tidss->dispc); | |
227 | #endif | |
228 | pm_runtime_disable(dev); | |
229 | ||
230 | /* devm allocated dispc goes away with the dev so mark it NULL */ | |
231 | dispc_remove(tidss); | |
232 | ||
233 | dev_dbg(dev, "%s done\n", __func__); | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
238 | static void tidss_shutdown(struct platform_device *pdev) | |
239 | { | |
240 | drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); | |
241 | } | |
242 | ||
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, }, | |
247 | { } | |
248 | }; | |
249 | ||
250 | MODULE_DEVICE_TABLE(of, tidss_of_table); | |
251 | ||
252 | static struct platform_driver tidss_platform_driver = { | |
253 | .probe = tidss_probe, | |
254 | .remove = tidss_remove, | |
255 | .shutdown = tidss_shutdown, | |
256 | .driver = { | |
257 | .name = "tidss", | |
258 | #ifdef CONFIG_PM | |
259 | .pm = &tidss_pm_ops, | |
260 | #endif | |
261 | .of_match_table = tidss_of_table, | |
262 | .suppress_bind_attrs = true, | |
263 | }, | |
264 | }; | |
265 | ||
266 | module_platform_driver(tidss_platform_driver); | |
267 | ||
268 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | |
269 | MODULE_DESCRIPTION("TI Keystone DSS Driver"); | |
270 | MODULE_LICENSE("GPL v2"); |