]>
Commit | Line | Data |
---|---|---|
caab277b | 1 | // SPDX-License-Identifier: GPL-2.0-only |
25fdd593 JS |
2 | /* |
3 | * Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. | |
4 | * Copyright (C) 2013 Red Hat | |
5 | * Author: Rob Clark <robdclark@gmail.com> | |
25fdd593 JS |
6 | */ |
7 | ||
8 | #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ | |
9 | ||
10 | #include <drm/drm_crtc.h> | |
11 | #include <linux/debugfs.h> | |
12 | #include <linux/of_irq.h> | |
13 | #include <linux/dma-buf.h> | |
14 | ||
15 | #include "msm_drv.h" | |
16 | #include "msm_mmu.h" | |
17 | #include "msm_gem.h" | |
18 | ||
19 | #include "dpu_kms.h" | |
20 | #include "dpu_core_irq.h" | |
21 | #include "dpu_formats.h" | |
22 | #include "dpu_hw_vbif.h" | |
23 | #include "dpu_vbif.h" | |
24 | #include "dpu_encoder.h" | |
25 | #include "dpu_plane.h" | |
26 | #include "dpu_crtc.h" | |
27 | ||
28 | #define CREATE_TRACE_POINTS | |
29 | #include "dpu_trace.h" | |
30 | ||
31 | static const char * const iommu_ports[] = { | |
32 | "mdp_0", | |
33 | }; | |
34 | ||
35 | /* | |
36 | * To enable overall DRM driver logging | |
37 | * # echo 0x2 > /sys/module/drm/parameters/debug | |
38 | * | |
39 | * To enable DRM driver h/w logging | |
40 | * # echo <mask> > /sys/kernel/debug/dri/0/debug/hw_log_mask | |
41 | * | |
42 | * See dpu_hw_mdss.h for h/w logging mask definitions (search for DPU_DBG_MASK_) | |
43 | */ | |
44 | #define DPU_DEBUGFS_DIR "msm_dpu" | |
45 | #define DPU_DEBUGFS_HWMASKNAME "hw_log_mask" | |
46 | ||
47 | static int dpu_kms_hw_init(struct msm_kms *kms); | |
48 | static int _dpu_kms_mmu_destroy(struct dpu_kms *dpu_kms); | |
49 | ||
50 | static unsigned long dpu_iomap_size(struct platform_device *pdev, | |
51 | const char *name) | |
52 | { | |
53 | struct resource *res; | |
54 | ||
55 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name); | |
56 | if (!res) { | |
57 | DRM_ERROR("failed to get memory resource: %s\n", name); | |
58 | return 0; | |
59 | } | |
60 | ||
61 | return resource_size(res); | |
62 | } | |
63 | ||
64 | #ifdef CONFIG_DEBUG_FS | |
65 | static int _dpu_danger_signal_status(struct seq_file *s, | |
66 | bool danger_status) | |
67 | { | |
68 | struct dpu_kms *kms = (struct dpu_kms *)s->private; | |
69 | struct msm_drm_private *priv; | |
70 | struct dpu_danger_safe_status status; | |
71 | int i; | |
72 | ||
3d688410 | 73 | if (!kms->dev || !kms->dev->dev_private || !kms->hw_mdp) { |
25fdd593 JS |
74 | DPU_ERROR("invalid arg(s)\n"); |
75 | return 0; | |
76 | } | |
77 | ||
78 | priv = kms->dev->dev_private; | |
79 | memset(&status, 0, sizeof(struct dpu_danger_safe_status)); | |
80 | ||
81 | pm_runtime_get_sync(&kms->pdev->dev); | |
82 | if (danger_status) { | |
83 | seq_puts(s, "\nDanger signal status:\n"); | |
84 | if (kms->hw_mdp->ops.get_danger_status) | |
85 | kms->hw_mdp->ops.get_danger_status(kms->hw_mdp, | |
86 | &status); | |
87 | } else { | |
88 | seq_puts(s, "\nSafe signal status:\n"); | |
89 | if (kms->hw_mdp->ops.get_danger_status) | |
90 | kms->hw_mdp->ops.get_danger_status(kms->hw_mdp, | |
91 | &status); | |
92 | } | |
93 | pm_runtime_put_sync(&kms->pdev->dev); | |
94 | ||
95 | seq_printf(s, "MDP : 0x%x\n", status.mdp); | |
96 | ||
97 | for (i = SSPP_VIG0; i < SSPP_MAX; i++) | |
98 | seq_printf(s, "SSPP%d : 0x%x \t", i - SSPP_VIG0, | |
99 | status.sspp[i]); | |
100 | seq_puts(s, "\n"); | |
101 | ||
102 | return 0; | |
103 | } | |
104 | ||
105 | #define DEFINE_DPU_DEBUGFS_SEQ_FOPS(__prefix) \ | |
106 | static int __prefix ## _open(struct inode *inode, struct file *file) \ | |
107 | { \ | |
108 | return single_open(file, __prefix ## _show, inode->i_private); \ | |
109 | } \ | |
110 | static const struct file_operations __prefix ## _fops = { \ | |
111 | .owner = THIS_MODULE, \ | |
112 | .open = __prefix ## _open, \ | |
113 | .release = single_release, \ | |
114 | .read = seq_read, \ | |
115 | .llseek = seq_lseek, \ | |
116 | } | |
117 | ||
118 | static int dpu_debugfs_danger_stats_show(struct seq_file *s, void *v) | |
119 | { | |
120 | return _dpu_danger_signal_status(s, true); | |
121 | } | |
122 | DEFINE_DPU_DEBUGFS_SEQ_FOPS(dpu_debugfs_danger_stats); | |
123 | ||
124 | static int dpu_debugfs_safe_stats_show(struct seq_file *s, void *v) | |
125 | { | |
126 | return _dpu_danger_signal_status(s, false); | |
127 | } | |
128 | DEFINE_DPU_DEBUGFS_SEQ_FOPS(dpu_debugfs_safe_stats); | |
129 | ||
3d688410 | 130 | static void dpu_debugfs_danger_init(struct dpu_kms *dpu_kms, |
25fdd593 JS |
131 | struct dentry *parent) |
132 | { | |
3d688410 JC |
133 | struct dentry *entry = debugfs_create_dir("danger", parent); |
134 | if (IS_ERR_OR_NULL(entry)) | |
135 | return; | |
25fdd593 | 136 | |
3d688410 | 137 | debugfs_create_file("danger_status", 0600, entry, |
25fdd593 | 138 | dpu_kms, &dpu_debugfs_danger_stats_fops); |
3d688410 | 139 | debugfs_create_file("safe_status", 0600, entry, |
25fdd593 | 140 | dpu_kms, &dpu_debugfs_safe_stats_fops); |
25fdd593 JS |
141 | } |
142 | ||
143 | static int _dpu_debugfs_show_regset32(struct seq_file *s, void *data) | |
144 | { | |
3d688410 JC |
145 | struct dpu_debugfs_regset32 *regset = s->private; |
146 | struct dpu_kms *dpu_kms = regset->dpu_kms; | |
25fdd593 JS |
147 | struct drm_device *dev; |
148 | struct msm_drm_private *priv; | |
149 | void __iomem *base; | |
150 | uint32_t i, addr; | |
151 | ||
3d688410 | 152 | if (!dpu_kms->mmio) |
25fdd593 JS |
153 | return 0; |
154 | ||
155 | dev = dpu_kms->dev; | |
156 | if (!dev) | |
157 | return 0; | |
158 | ||
159 | priv = dev->dev_private; | |
160 | if (!priv) | |
161 | return 0; | |
162 | ||
163 | base = dpu_kms->mmio + regset->offset; | |
164 | ||
165 | /* insert padding spaces, if needed */ | |
166 | if (regset->offset & 0xF) { | |
167 | seq_printf(s, "[%x]", regset->offset & ~0xF); | |
168 | for (i = 0; i < (regset->offset & 0xF); i += 4) | |
169 | seq_puts(s, " "); | |
170 | } | |
171 | ||
172 | pm_runtime_get_sync(&dpu_kms->pdev->dev); | |
173 | ||
174 | /* main register output */ | |
175 | for (i = 0; i < regset->blk_len; i += 4) { | |
176 | addr = regset->offset + i; | |
177 | if ((addr & 0xF) == 0x0) | |
178 | seq_printf(s, i ? "\n[%x]" : "[%x]", addr); | |
179 | seq_printf(s, " %08x", readl_relaxed(base + i)); | |
180 | } | |
181 | seq_puts(s, "\n"); | |
182 | pm_runtime_put_sync(&dpu_kms->pdev->dev); | |
183 | ||
184 | return 0; | |
185 | } | |
186 | ||
187 | static int dpu_debugfs_open_regset32(struct inode *inode, | |
188 | struct file *file) | |
189 | { | |
190 | return single_open(file, _dpu_debugfs_show_regset32, inode->i_private); | |
191 | } | |
192 | ||
193 | static const struct file_operations dpu_fops_regset32 = { | |
194 | .open = dpu_debugfs_open_regset32, | |
195 | .read = seq_read, | |
196 | .llseek = seq_lseek, | |
197 | .release = single_release, | |
198 | }; | |
199 | ||
200 | void dpu_debugfs_setup_regset32(struct dpu_debugfs_regset32 *regset, | |
201 | uint32_t offset, uint32_t length, struct dpu_kms *dpu_kms) | |
202 | { | |
203 | if (regset) { | |
204 | regset->offset = offset; | |
205 | regset->blk_len = length; | |
206 | regset->dpu_kms = dpu_kms; | |
207 | } | |
208 | } | |
209 | ||
210 | void *dpu_debugfs_create_regset32(const char *name, umode_t mode, | |
211 | void *parent, struct dpu_debugfs_regset32 *regset) | |
212 | { | |
213 | if (!name || !regset || !regset->dpu_kms || !regset->blk_len) | |
214 | return NULL; | |
215 | ||
216 | /* make sure offset is a multiple of 4 */ | |
217 | regset->offset = round_down(regset->offset, 4); | |
218 | ||
219 | return debugfs_create_file(name, mode, parent, | |
220 | regset, &dpu_fops_regset32); | |
221 | } | |
222 | ||
223 | static int _dpu_debugfs_init(struct dpu_kms *dpu_kms) | |
224 | { | |
3d688410 JC |
225 | void *p = dpu_hw_util_get_log_mask_ptr(); |
226 | struct dentry *entry; | |
25fdd593 | 227 | |
3d688410 | 228 | if (!p) |
25fdd593 JS |
229 | return -EINVAL; |
230 | ||
3d688410 JC |
231 | entry = debugfs_create_dir("debug", dpu_kms->dev->primary->debugfs_root); |
232 | if (IS_ERR_OR_NULL(entry)) | |
233 | return -ENODEV; | |
25fdd593 | 234 | |
25fdd593 | 235 | /* allow root to be NULL */ |
3d688410 | 236 | debugfs_create_x32(DPU_DEBUGFS_HWMASKNAME, 0600, entry, p); |
25fdd593 | 237 | |
3d688410 JC |
238 | dpu_debugfs_danger_init(dpu_kms, entry); |
239 | dpu_debugfs_vbif_init(dpu_kms, entry); | |
240 | dpu_debugfs_core_irq_init(dpu_kms, entry); | |
25fdd593 | 241 | |
3d688410 | 242 | return dpu_core_perf_debugfs_init(dpu_kms, entry); |
25fdd593 JS |
243 | } |
244 | #endif | |
245 | ||
246 | static int dpu_kms_enable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) | |
247 | { | |
248 | return dpu_crtc_vblank(crtc, true); | |
249 | } | |
250 | ||
251 | static void dpu_kms_disable_vblank(struct msm_kms *kms, struct drm_crtc *crtc) | |
252 | { | |
253 | dpu_crtc_vblank(crtc, false); | |
254 | } | |
255 | ||
256 | static void dpu_kms_prepare_commit(struct msm_kms *kms, | |
257 | struct drm_atomic_state *state) | |
258 | { | |
259 | struct dpu_kms *dpu_kms; | |
260 | struct msm_drm_private *priv; | |
261 | struct drm_device *dev; | |
4b8c6279 SP |
262 | struct drm_crtc *crtc; |
263 | struct drm_crtc_state *crtc_state; | |
25fdd593 | 264 | struct drm_encoder *encoder; |
4b8c6279 | 265 | int i; |
25fdd593 JS |
266 | |
267 | if (!kms) | |
268 | return; | |
269 | dpu_kms = to_dpu_kms(kms); | |
270 | dev = dpu_kms->dev; | |
271 | ||
272 | if (!dev || !dev->dev_private) | |
273 | return; | |
274 | priv = dev->dev_private; | |
275 | pm_runtime_get_sync(&dpu_kms->pdev->dev); | |
276 | ||
4b8c6279 SP |
277 | /* Call prepare_commit for all affected encoders */ |
278 | for_each_new_crtc_in_state(state, crtc, crtc_state, i) { | |
279 | drm_for_each_encoder_mask(encoder, crtc->dev, | |
280 | crtc_state->encoder_mask) { | |
25fdd593 | 281 | dpu_encoder_prepare_commit(encoder); |
4b8c6279 SP |
282 | } |
283 | } | |
25fdd593 JS |
284 | } |
285 | ||
286 | /* | |
287 | * Override the encoder enable since we need to setup the inline rotator and do | |
288 | * some crtc magic before enabling any bridge that might be present. | |
289 | */ | |
290 | void dpu_kms_encoder_enable(struct drm_encoder *encoder) | |
291 | { | |
292 | const struct drm_encoder_helper_funcs *funcs = encoder->helper_private; | |
4b8c6279 SP |
293 | struct drm_device *dev = encoder->dev; |
294 | struct drm_crtc *crtc; | |
25fdd593 JS |
295 | |
296 | /* Forward this enable call to the commit hook */ | |
297 | if (funcs && funcs->commit) | |
298 | funcs->commit(encoder); | |
299 | ||
1dfdb0e1 | 300 | WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); |
4b8c6279 SP |
301 | drm_for_each_crtc(crtc, dev) { |
302 | if (!(crtc->state->encoder_mask & drm_encoder_mask(encoder))) | |
303 | continue; | |
304 | ||
25fdd593 | 305 | trace_dpu_kms_enc_enable(DRMID(crtc)); |
50bcc689 | 306 | dpu_crtc_commit_kickoff(crtc, false); |
25fdd593 JS |
307 | } |
308 | } | |
309 | ||
310 | static void dpu_kms_commit(struct msm_kms *kms, struct drm_atomic_state *state) | |
311 | { | |
312 | struct drm_crtc *crtc; | |
313 | struct drm_crtc_state *crtc_state; | |
314 | int i; | |
315 | ||
316 | for_each_new_crtc_in_state(state, crtc, crtc_state, i) { | |
317 | /* If modeset is required, kickoff is run in encoder_enable */ | |
318 | if (drm_atomic_crtc_needs_modeset(crtc_state)) | |
319 | continue; | |
320 | ||
321 | if (crtc->state->active) { | |
322 | trace_dpu_kms_commit(DRMID(crtc)); | |
50bcc689 SP |
323 | dpu_crtc_commit_kickoff(crtc, |
324 | state->legacy_cursor_update); | |
25fdd593 JS |
325 | } |
326 | } | |
327 | } | |
328 | ||
329 | static void dpu_kms_complete_commit(struct msm_kms *kms, | |
330 | struct drm_atomic_state *old_state) | |
331 | { | |
332 | struct dpu_kms *dpu_kms; | |
333 | struct msm_drm_private *priv; | |
334 | struct drm_crtc *crtc; | |
335 | struct drm_crtc_state *old_crtc_state; | |
336 | int i; | |
337 | ||
338 | if (!kms || !old_state) | |
339 | return; | |
340 | dpu_kms = to_dpu_kms(kms); | |
341 | ||
342 | if (!dpu_kms->dev || !dpu_kms->dev->dev_private) | |
343 | return; | |
344 | priv = dpu_kms->dev->dev_private; | |
345 | ||
346 | DPU_ATRACE_BEGIN("kms_complete_commit"); | |
347 | ||
348 | for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i) | |
349 | dpu_crtc_complete_commit(crtc, old_crtc_state); | |
350 | ||
351 | pm_runtime_put_sync(&dpu_kms->pdev->dev); | |
352 | ||
353 | DPU_ATRACE_END("kms_complete_commit"); | |
354 | } | |
355 | ||
356 | static void dpu_kms_wait_for_commit_done(struct msm_kms *kms, | |
357 | struct drm_crtc *crtc) | |
358 | { | |
359 | struct drm_encoder *encoder; | |
360 | struct drm_device *dev; | |
361 | int ret; | |
362 | ||
363 | if (!kms || !crtc || !crtc->state) { | |
364 | DPU_ERROR("invalid params\n"); | |
365 | return; | |
366 | } | |
367 | ||
368 | dev = crtc->dev; | |
369 | ||
370 | if (!crtc->state->enable) { | |
371 | DPU_DEBUG("[crtc:%d] not enable\n", crtc->base.id); | |
372 | return; | |
373 | } | |
374 | ||
375 | if (!crtc->state->active) { | |
376 | DPU_DEBUG("[crtc:%d] not active\n", crtc->base.id); | |
377 | return; | |
378 | } | |
379 | ||
380 | list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { | |
381 | if (encoder->crtc != crtc) | |
382 | continue; | |
383 | /* | |
384 | * Wait for post-flush if necessary to delay before | |
385 | * plane_cleanup. For example, wait for vsync in case of video | |
386 | * mode panels. This may be a no-op for command mode panels. | |
387 | */ | |
388 | trace_dpu_kms_wait_for_commit_done(DRMID(crtc)); | |
389 | ret = dpu_encoder_wait_for_event(encoder, MSM_ENC_COMMIT_DONE); | |
390 | if (ret && ret != -EWOULDBLOCK) { | |
391 | DPU_ERROR("wait for commit done returned %d\n", ret); | |
392 | break; | |
393 | } | |
394 | } | |
395 | } | |
396 | ||
a802ee99 | 397 | static int _dpu_kms_initialize_dsi(struct drm_device *dev, |
25fdd593 JS |
398 | struct msm_drm_private *priv, |
399 | struct dpu_kms *dpu_kms) | |
400 | { | |
401 | struct drm_encoder *encoder = NULL; | |
a802ee99 JS |
402 | int i, rc = 0; |
403 | ||
404 | if (!(priv->dsi[0] || priv->dsi[1])) | |
405 | return rc; | |
25fdd593 JS |
406 | |
407 | /*TODO: Support two independent DSI connectors */ | |
2c043eef | 408 | encoder = dpu_encoder_init(dev, DRM_MODE_ENCODER_DSI); |
a802ee99 | 409 | if (IS_ERR(encoder)) { |
25fdd593 | 410 | DPU_ERROR("encoder init failed for dsi display\n"); |
a802ee99 | 411 | return PTR_ERR(encoder); |
25fdd593 JS |
412 | } |
413 | ||
414 | priv->encoders[priv->num_encoders++] = encoder; | |
415 | ||
416 | for (i = 0; i < ARRAY_SIZE(priv->dsi); i++) { | |
a802ee99 JS |
417 | if (!priv->dsi[i]) |
418 | continue; | |
25fdd593 JS |
419 | |
420 | rc = msm_dsi_modeset_init(priv->dsi[i], dev, encoder); | |
421 | if (rc) { | |
422 | DPU_ERROR("modeset_init failed for dsi[%d], rc = %d\n", | |
423 | i, rc); | |
a802ee99 | 424 | break; |
25fdd593 JS |
425 | } |
426 | } | |
a802ee99 JS |
427 | |
428 | return rc; | |
25fdd593 JS |
429 | } |
430 | ||
431 | /** | |
432 | * _dpu_kms_setup_displays - create encoders, bridges and connectors | |
433 | * for underlying displays | |
434 | * @dev: Pointer to drm device structure | |
435 | * @priv: Pointer to private drm device data | |
436 | * @dpu_kms: Pointer to dpu kms structure | |
437 | * Returns: Zero on success | |
438 | */ | |
a802ee99 | 439 | static int _dpu_kms_setup_displays(struct drm_device *dev, |
25fdd593 JS |
440 | struct msm_drm_private *priv, |
441 | struct dpu_kms *dpu_kms) | |
442 | { | |
25fdd593 JS |
443 | /** |
444 | * Extend this function to initialize other | |
445 | * types of displays | |
446 | */ | |
a802ee99 JS |
447 | |
448 | return _dpu_kms_initialize_dsi(dev, priv, dpu_kms); | |
25fdd593 JS |
449 | } |
450 | ||
451 | static void _dpu_kms_drm_obj_destroy(struct dpu_kms *dpu_kms) | |
452 | { | |
453 | struct msm_drm_private *priv; | |
454 | int i; | |
455 | ||
456 | if (!dpu_kms) { | |
457 | DPU_ERROR("invalid dpu_kms\n"); | |
458 | return; | |
459 | } else if (!dpu_kms->dev) { | |
460 | DPU_ERROR("invalid dev\n"); | |
461 | return; | |
462 | } else if (!dpu_kms->dev->dev_private) { | |
463 | DPU_ERROR("invalid dev_private\n"); | |
464 | return; | |
465 | } | |
466 | priv = dpu_kms->dev->dev_private; | |
467 | ||
468 | for (i = 0; i < priv->num_crtcs; i++) | |
469 | priv->crtcs[i]->funcs->destroy(priv->crtcs[i]); | |
470 | priv->num_crtcs = 0; | |
471 | ||
472 | for (i = 0; i < priv->num_planes; i++) | |
473 | priv->planes[i]->funcs->destroy(priv->planes[i]); | |
474 | priv->num_planes = 0; | |
475 | ||
476 | for (i = 0; i < priv->num_connectors; i++) | |
477 | priv->connectors[i]->funcs->destroy(priv->connectors[i]); | |
478 | priv->num_connectors = 0; | |
479 | ||
480 | for (i = 0; i < priv->num_encoders; i++) | |
481 | priv->encoders[i]->funcs->destroy(priv->encoders[i]); | |
482 | priv->num_encoders = 0; | |
483 | } | |
484 | ||
485 | static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms) | |
486 | { | |
487 | struct drm_device *dev; | |
488 | struct drm_plane *primary_planes[MAX_PLANES], *plane; | |
07ca1fc0 | 489 | struct drm_plane *cursor_planes[MAX_PLANES] = { NULL }; |
25fdd593 JS |
490 | struct drm_crtc *crtc; |
491 | ||
492 | struct msm_drm_private *priv; | |
493 | struct dpu_mdss_cfg *catalog; | |
494 | ||
07ca1fc0 | 495 | int primary_planes_idx = 0, cursor_planes_idx = 0, i, ret; |
25fdd593 JS |
496 | int max_crtc_count; |
497 | ||
498 | if (!dpu_kms || !dpu_kms->dev || !dpu_kms->dev->dev) { | |
499 | DPU_ERROR("invalid dpu_kms\n"); | |
500 | return -EINVAL; | |
501 | } | |
502 | ||
503 | dev = dpu_kms->dev; | |
504 | priv = dev->dev_private; | |
505 | catalog = dpu_kms->catalog; | |
506 | ||
507 | /* | |
508 | * Create encoder and query display drivers to create | |
509 | * bridges and connectors | |
510 | */ | |
a802ee99 JS |
511 | ret = _dpu_kms_setup_displays(dev, priv, dpu_kms); |
512 | if (ret) | |
513 | goto fail; | |
25fdd593 JS |
514 | |
515 | max_crtc_count = min(catalog->mixer_count, priv->num_encoders); | |
516 | ||
07ca1fc0 | 517 | /* Create the planes, keeping track of one primary/cursor per crtc */ |
25fdd593 | 518 | for (i = 0; i < catalog->sspp_count; i++) { |
07ca1fc0 SK |
519 | enum drm_plane_type type; |
520 | ||
521 | if ((catalog->sspp[i].features & BIT(DPU_SSPP_CURSOR)) | |
522 | && cursor_planes_idx < max_crtc_count) | |
523 | type = DRM_PLANE_TYPE_CURSOR; | |
524 | else if (primary_planes_idx < max_crtc_count) | |
525 | type = DRM_PLANE_TYPE_PRIMARY; | |
526 | else | |
527 | type = DRM_PLANE_TYPE_OVERLAY; | |
528 | ||
529 | DPU_DEBUG("Create plane type %d with features %lx (cur %lx)\n", | |
530 | type, catalog->sspp[i].features, | |
531 | catalog->sspp[i].features & BIT(DPU_SSPP_CURSOR)); | |
532 | ||
533 | plane = dpu_plane_init(dev, catalog->sspp[i].id, type, | |
534 | (1UL << max_crtc_count) - 1, 0); | |
25fdd593 JS |
535 | if (IS_ERR(plane)) { |
536 | DPU_ERROR("dpu_plane_init failed\n"); | |
537 | ret = PTR_ERR(plane); | |
538 | goto fail; | |
539 | } | |
540 | priv->planes[priv->num_planes++] = plane; | |
541 | ||
07ca1fc0 SK |
542 | if (type == DRM_PLANE_TYPE_CURSOR) |
543 | cursor_planes[cursor_planes_idx++] = plane; | |
544 | else if (type == DRM_PLANE_TYPE_PRIMARY) | |
25fdd593 JS |
545 | primary_planes[primary_planes_idx++] = plane; |
546 | } | |
547 | ||
548 | max_crtc_count = min(max_crtc_count, primary_planes_idx); | |
549 | ||
550 | /* Create one CRTC per encoder */ | |
551 | for (i = 0; i < max_crtc_count; i++) { | |
07ca1fc0 | 552 | crtc = dpu_crtc_init(dev, primary_planes[i], cursor_planes[i]); |
25fdd593 JS |
553 | if (IS_ERR(crtc)) { |
554 | ret = PTR_ERR(crtc); | |
555 | goto fail; | |
556 | } | |
557 | priv->crtcs[priv->num_crtcs++] = crtc; | |
558 | } | |
559 | ||
560 | /* All CRTCs are compatible with all encoders */ | |
561 | for (i = 0; i < priv->num_encoders; i++) | |
562 | priv->encoders[i]->possible_crtcs = (1 << priv->num_crtcs) - 1; | |
563 | ||
564 | return 0; | |
565 | fail: | |
566 | _dpu_kms_drm_obj_destroy(dpu_kms); | |
567 | return ret; | |
568 | } | |
569 | ||
570 | #ifdef CONFIG_DEBUG_FS | |
571 | static int dpu_kms_debugfs_init(struct msm_kms *kms, struct drm_minor *minor) | |
572 | { | |
3d688410 | 573 | return _dpu_debugfs_init(to_dpu_kms(kms)); |
25fdd593 JS |
574 | } |
575 | #endif | |
576 | ||
577 | static long dpu_kms_round_pixclk(struct msm_kms *kms, unsigned long rate, | |
578 | struct drm_encoder *encoder) | |
579 | { | |
580 | return rate; | |
581 | } | |
582 | ||
583 | static void _dpu_kms_hw_destroy(struct dpu_kms *dpu_kms) | |
584 | { | |
585 | struct drm_device *dev; | |
586 | int i; | |
587 | ||
588 | dev = dpu_kms->dev; | |
589 | if (!dev) | |
590 | return; | |
591 | ||
592 | if (dpu_kms->hw_intr) | |
593 | dpu_hw_intr_destroy(dpu_kms->hw_intr); | |
594 | dpu_kms->hw_intr = NULL; | |
595 | ||
25fdd593 | 596 | /* safe to call these more than once during shutdown */ |
25fdd593 JS |
597 | _dpu_kms_mmu_destroy(dpu_kms); |
598 | ||
599 | if (dpu_kms->catalog) { | |
600 | for (i = 0; i < dpu_kms->catalog->vbif_count; i++) { | |
601 | u32 vbif_idx = dpu_kms->catalog->vbif[i].id; | |
602 | ||
603 | if ((vbif_idx < VBIF_MAX) && dpu_kms->hw_vbif[vbif_idx]) | |
604 | dpu_hw_vbif_destroy(dpu_kms->hw_vbif[vbif_idx]); | |
605 | } | |
606 | } | |
607 | ||
608 | if (dpu_kms->rm_init) | |
609 | dpu_rm_destroy(&dpu_kms->rm); | |
610 | dpu_kms->rm_init = false; | |
611 | ||
612 | if (dpu_kms->catalog) | |
613 | dpu_hw_catalog_deinit(dpu_kms->catalog); | |
614 | dpu_kms->catalog = NULL; | |
615 | ||
25fdd593 JS |
616 | if (dpu_kms->vbif[VBIF_NRT]) |
617 | devm_iounmap(&dpu_kms->pdev->dev, dpu_kms->vbif[VBIF_NRT]); | |
618 | dpu_kms->vbif[VBIF_NRT] = NULL; | |
619 | ||
620 | if (dpu_kms->vbif[VBIF_RT]) | |
621 | devm_iounmap(&dpu_kms->pdev->dev, dpu_kms->vbif[VBIF_RT]); | |
622 | dpu_kms->vbif[VBIF_RT] = NULL; | |
623 | ||
7579cb05 JS |
624 | if (dpu_kms->hw_mdp) |
625 | dpu_hw_mdp_destroy(dpu_kms->hw_mdp); | |
626 | dpu_kms->hw_mdp = NULL; | |
627 | ||
25fdd593 JS |
628 | if (dpu_kms->mmio) |
629 | devm_iounmap(&dpu_kms->pdev->dev, dpu_kms->mmio); | |
630 | dpu_kms->mmio = NULL; | |
631 | } | |
632 | ||
633 | static void dpu_kms_destroy(struct msm_kms *kms) | |
634 | { | |
635 | struct dpu_kms *dpu_kms; | |
636 | ||
637 | if (!kms) { | |
638 | DPU_ERROR("invalid kms\n"); | |
639 | return; | |
640 | } | |
641 | ||
642 | dpu_kms = to_dpu_kms(kms); | |
643 | ||
25fdd593 JS |
644 | _dpu_kms_hw_destroy(dpu_kms); |
645 | } | |
646 | ||
fba33cae | 647 | static void _dpu_kms_set_encoder_mode(struct msm_kms *kms, |
25fdd593 JS |
648 | struct drm_encoder *encoder, |
649 | bool cmd_mode) | |
650 | { | |
651 | struct msm_display_info info; | |
652 | struct msm_drm_private *priv = encoder->dev->dev_private; | |
653 | int i, rc = 0; | |
654 | ||
655 | memset(&info, 0, sizeof(info)); | |
656 | ||
657 | info.intf_type = encoder->encoder_type; | |
658 | info.capabilities = cmd_mode ? MSM_DISPLAY_CAP_CMD_MODE : | |
659 | MSM_DISPLAY_CAP_VID_MODE; | |
660 | ||
661 | /* TODO: No support for DSI swap */ | |
662 | for (i = 0; i < ARRAY_SIZE(priv->dsi); i++) { | |
663 | if (priv->dsi[i]) { | |
664 | info.h_tile_instance[info.num_of_h_tiles] = i; | |
665 | info.num_of_h_tiles++; | |
666 | } | |
667 | } | |
668 | ||
669 | rc = dpu_encoder_setup(encoder->dev, encoder, &info); | |
670 | if (rc) | |
671 | DPU_ERROR("failed to setup DPU encoder %d: rc:%d\n", | |
672 | encoder->base.id, rc); | |
673 | } | |
674 | ||
8fe62a63 JC |
675 | static irqreturn_t dpu_irq(struct msm_kms *kms) |
676 | { | |
677 | struct dpu_kms *dpu_kms = to_dpu_kms(kms); | |
678 | ||
679 | return dpu_core_irq(dpu_kms); | |
680 | } | |
681 | ||
682 | static void dpu_irq_preinstall(struct msm_kms *kms) | |
683 | { | |
684 | struct dpu_kms *dpu_kms = to_dpu_kms(kms); | |
685 | ||
686 | dpu_core_irq_preinstall(dpu_kms); | |
687 | } | |
688 | ||
689 | static void dpu_irq_uninstall(struct msm_kms *kms) | |
690 | { | |
691 | struct dpu_kms *dpu_kms = to_dpu_kms(kms); | |
692 | ||
693 | dpu_core_irq_uninstall(dpu_kms); | |
694 | } | |
695 | ||
25fdd593 JS |
696 | static const struct msm_kms_funcs kms_funcs = { |
697 | .hw_init = dpu_kms_hw_init, | |
698 | .irq_preinstall = dpu_irq_preinstall, | |
25fdd593 JS |
699 | .irq_uninstall = dpu_irq_uninstall, |
700 | .irq = dpu_irq, | |
701 | .prepare_commit = dpu_kms_prepare_commit, | |
702 | .commit = dpu_kms_commit, | |
703 | .complete_commit = dpu_kms_complete_commit, | |
704 | .wait_for_crtc_commit_done = dpu_kms_wait_for_commit_done, | |
705 | .enable_vblank = dpu_kms_enable_vblank, | |
706 | .disable_vblank = dpu_kms_disable_vblank, | |
707 | .check_modified_format = dpu_format_check_modified_format, | |
708 | .get_format = dpu_get_msm_format, | |
709 | .round_pixclk = dpu_kms_round_pixclk, | |
25fdd593 JS |
710 | .destroy = dpu_kms_destroy, |
711 | .set_encoder_mode = _dpu_kms_set_encoder_mode, | |
712 | #ifdef CONFIG_DEBUG_FS | |
713 | .debugfs_init = dpu_kms_debugfs_init, | |
714 | #endif | |
715 | }; | |
716 | ||
25fdd593 JS |
717 | static int _dpu_kms_mmu_destroy(struct dpu_kms *dpu_kms) |
718 | { | |
719 | struct msm_mmu *mmu; | |
720 | ||
721 | mmu = dpu_kms->base.aspace->mmu; | |
722 | ||
723 | mmu->funcs->detach(mmu, (const char **)iommu_ports, | |
724 | ARRAY_SIZE(iommu_ports)); | |
725 | msm_gem_address_space_put(dpu_kms->base.aspace); | |
726 | ||
727 | return 0; | |
728 | } | |
729 | ||
730 | static int _dpu_kms_mmu_init(struct dpu_kms *dpu_kms) | |
731 | { | |
732 | struct iommu_domain *domain; | |
733 | struct msm_gem_address_space *aspace; | |
734 | int ret; | |
735 | ||
736 | domain = iommu_domain_alloc(&platform_bus_type); | |
737 | if (!domain) | |
738 | return 0; | |
739 | ||
01665c64 JS |
740 | domain->geometry.aperture_start = 0x1000; |
741 | domain->geometry.aperture_end = 0xffffffff; | |
742 | ||
25fdd593 JS |
743 | aspace = msm_gem_address_space_create(dpu_kms->dev->dev, |
744 | domain, "dpu1"); | |
745 | if (IS_ERR(aspace)) { | |
746 | ret = PTR_ERR(aspace); | |
747 | goto fail; | |
748 | } | |
749 | ||
750 | dpu_kms->base.aspace = aspace; | |
751 | ||
752 | ret = aspace->mmu->funcs->attach(aspace->mmu, iommu_ports, | |
753 | ARRAY_SIZE(iommu_ports)); | |
754 | if (ret) { | |
755 | DPU_ERROR("failed to attach iommu %d\n", ret); | |
756 | msm_gem_address_space_put(aspace); | |
757 | goto fail; | |
758 | } | |
759 | ||
760 | return 0; | |
761 | fail: | |
762 | _dpu_kms_mmu_destroy(dpu_kms); | |
763 | ||
764 | return ret; | |
765 | } | |
766 | ||
767 | static struct dss_clk *_dpu_kms_get_clk(struct dpu_kms *dpu_kms, | |
768 | char *clock_name) | |
769 | { | |
770 | struct dss_module_power *mp = &dpu_kms->mp; | |
771 | int i; | |
772 | ||
773 | for (i = 0; i < mp->num_clk; i++) { | |
774 | if (!strcmp(mp->clk_config[i].clk_name, clock_name)) | |
775 | return &mp->clk_config[i]; | |
776 | } | |
777 | ||
778 | return NULL; | |
779 | } | |
780 | ||
781 | u64 dpu_kms_get_clk_rate(struct dpu_kms *dpu_kms, char *clock_name) | |
782 | { | |
783 | struct dss_clk *clk; | |
784 | ||
785 | clk = _dpu_kms_get_clk(dpu_kms, clock_name); | |
786 | if (!clk) | |
787 | return -EINVAL; | |
788 | ||
789 | return clk_get_rate(clk->clk); | |
790 | } | |
791 | ||
25fdd593 JS |
792 | static int dpu_kms_hw_init(struct msm_kms *kms) |
793 | { | |
794 | struct dpu_kms *dpu_kms; | |
795 | struct drm_device *dev; | |
796 | struct msm_drm_private *priv; | |
797 | int i, rc = -EINVAL; | |
798 | ||
799 | if (!kms) { | |
800 | DPU_ERROR("invalid kms\n"); | |
0841851f | 801 | return rc; |
25fdd593 JS |
802 | } |
803 | ||
804 | dpu_kms = to_dpu_kms(kms); | |
805 | dev = dpu_kms->dev; | |
806 | if (!dev) { | |
807 | DPU_ERROR("invalid device\n"); | |
0841851f | 808 | return rc; |
25fdd593 JS |
809 | } |
810 | ||
25fdd593 JS |
811 | priv = dev->dev_private; |
812 | if (!priv) { | |
813 | DPU_ERROR("invalid private data\n"); | |
27bc773a | 814 | return rc; |
25fdd593 JS |
815 | } |
816 | ||
817 | dpu_kms->mmio = msm_ioremap(dpu_kms->pdev, "mdp", "mdp"); | |
818 | if (IS_ERR(dpu_kms->mmio)) { | |
819 | rc = PTR_ERR(dpu_kms->mmio); | |
820 | DPU_ERROR("mdp register memory map failed: %d\n", rc); | |
821 | dpu_kms->mmio = NULL; | |
822 | goto error; | |
823 | } | |
824 | DRM_DEBUG("mapped dpu address space @%pK\n", dpu_kms->mmio); | |
825 | dpu_kms->mmio_len = dpu_iomap_size(dpu_kms->pdev, "mdp"); | |
826 | ||
827 | dpu_kms->vbif[VBIF_RT] = msm_ioremap(dpu_kms->pdev, "vbif", "vbif"); | |
828 | if (IS_ERR(dpu_kms->vbif[VBIF_RT])) { | |
829 | rc = PTR_ERR(dpu_kms->vbif[VBIF_RT]); | |
830 | DPU_ERROR("vbif register memory map failed: %d\n", rc); | |
831 | dpu_kms->vbif[VBIF_RT] = NULL; | |
832 | goto error; | |
833 | } | |
834 | dpu_kms->vbif_len[VBIF_RT] = dpu_iomap_size(dpu_kms->pdev, "vbif"); | |
835 | dpu_kms->vbif[VBIF_NRT] = msm_ioremap(dpu_kms->pdev, "vbif_nrt", "vbif_nrt"); | |
836 | if (IS_ERR(dpu_kms->vbif[VBIF_NRT])) { | |
837 | dpu_kms->vbif[VBIF_NRT] = NULL; | |
838 | DPU_DEBUG("VBIF NRT is not defined"); | |
839 | } else { | |
840 | dpu_kms->vbif_len[VBIF_NRT] = dpu_iomap_size(dpu_kms->pdev, | |
841 | "vbif_nrt"); | |
842 | } | |
843 | ||
844 | dpu_kms->reg_dma = msm_ioremap(dpu_kms->pdev, "regdma", "regdma"); | |
845 | if (IS_ERR(dpu_kms->reg_dma)) { | |
846 | dpu_kms->reg_dma = NULL; | |
847 | DPU_DEBUG("REG_DMA is not defined"); | |
848 | } else { | |
849 | dpu_kms->reg_dma_len = dpu_iomap_size(dpu_kms->pdev, "regdma"); | |
850 | } | |
851 | ||
25fdd593 JS |
852 | pm_runtime_get_sync(&dpu_kms->pdev->dev); |
853 | ||
3804a982 | 854 | dpu_kms->core_rev = readl_relaxed(dpu_kms->mmio + 0x0); |
25fdd593 JS |
855 | |
856 | pr_info("dpu hardware revision:0x%x\n", dpu_kms->core_rev); | |
857 | ||
858 | dpu_kms->catalog = dpu_hw_catalog_init(dpu_kms->core_rev); | |
859 | if (IS_ERR_OR_NULL(dpu_kms->catalog)) { | |
860 | rc = PTR_ERR(dpu_kms->catalog); | |
861 | if (!dpu_kms->catalog) | |
862 | rc = -EINVAL; | |
863 | DPU_ERROR("catalog init failed: %d\n", rc); | |
864 | dpu_kms->catalog = NULL; | |
865 | goto power_error; | |
866 | } | |
867 | ||
25fdd593 JS |
868 | /* |
869 | * Now we need to read the HW catalog and initialize resources such as | |
870 | * clocks, regulators, GDSC/MMAGIC, ioremap the register ranges etc | |
871 | */ | |
872 | rc = _dpu_kms_mmu_init(dpu_kms); | |
873 | if (rc) { | |
874 | DPU_ERROR("dpu_kms_mmu_init failed: %d\n", rc); | |
875 | goto power_error; | |
876 | } | |
877 | ||
3763f1a5 | 878 | rc = dpu_rm_init(&dpu_kms->rm, dpu_kms->catalog, dpu_kms->mmio); |
25fdd593 JS |
879 | if (rc) { |
880 | DPU_ERROR("rm init failed: %d\n", rc); | |
881 | goto power_error; | |
882 | } | |
883 | ||
884 | dpu_kms->rm_init = true; | |
885 | ||
7579cb05 JS |
886 | dpu_kms->hw_mdp = dpu_hw_mdptop_init(MDP_TOP, dpu_kms->mmio, |
887 | dpu_kms->catalog); | |
888 | if (IS_ERR(dpu_kms->hw_mdp)) { | |
25fdd593 | 889 | rc = PTR_ERR(dpu_kms->hw_mdp); |
25fdd593 JS |
890 | DPU_ERROR("failed to get hw_mdp: %d\n", rc); |
891 | dpu_kms->hw_mdp = NULL; | |
892 | goto power_error; | |
893 | } | |
894 | ||
895 | for (i = 0; i < dpu_kms->catalog->vbif_count; i++) { | |
896 | u32 vbif_idx = dpu_kms->catalog->vbif[i].id; | |
897 | ||
898 | dpu_kms->hw_vbif[i] = dpu_hw_vbif_init(vbif_idx, | |
899 | dpu_kms->vbif[vbif_idx], dpu_kms->catalog); | |
900 | if (IS_ERR_OR_NULL(dpu_kms->hw_vbif[vbif_idx])) { | |
901 | rc = PTR_ERR(dpu_kms->hw_vbif[vbif_idx]); | |
902 | if (!dpu_kms->hw_vbif[vbif_idx]) | |
903 | rc = -EINVAL; | |
904 | DPU_ERROR("failed to init vbif %d: %d\n", vbif_idx, rc); | |
905 | dpu_kms->hw_vbif[vbif_idx] = NULL; | |
906 | goto power_error; | |
907 | } | |
908 | } | |
909 | ||
910 | rc = dpu_core_perf_init(&dpu_kms->perf, dev, dpu_kms->catalog, | |
25fdd593 JS |
911 | _dpu_kms_get_clk(dpu_kms, "core")); |
912 | if (rc) { | |
913 | DPU_ERROR("failed to init perf %d\n", rc); | |
914 | goto perf_err; | |
915 | } | |
916 | ||
917 | dpu_kms->hw_intr = dpu_hw_intr_init(dpu_kms->mmio, dpu_kms->catalog); | |
918 | if (IS_ERR_OR_NULL(dpu_kms->hw_intr)) { | |
919 | rc = PTR_ERR(dpu_kms->hw_intr); | |
920 | DPU_ERROR("hw_intr init failed: %d\n", rc); | |
921 | dpu_kms->hw_intr = NULL; | |
922 | goto hw_intr_init_err; | |
923 | } | |
924 | ||
25fdd593 JS |
925 | dev->mode_config.min_width = 0; |
926 | dev->mode_config.min_height = 0; | |
927 | ||
928 | /* | |
929 | * max crtc width is equal to the max mixer width * 2 and max height is | |
930 | * is 4K | |
931 | */ | |
932 | dev->mode_config.max_width = | |
933 | dpu_kms->catalog->caps->max_mixer_width * 2; | |
934 | dev->mode_config.max_height = 4096; | |
935 | ||
936 | /* | |
937 | * Support format modifiers for compression etc. | |
938 | */ | |
939 | dev->mode_config.allow_fb_modifiers = true; | |
940 | ||
918ce5b9 FK |
941 | /* |
942 | * _dpu_kms_drm_obj_init should create the DRM related objects | |
943 | * i.e. CRTCs, planes, encoders, connectors and so forth | |
944 | */ | |
945 | rc = _dpu_kms_drm_obj_init(dpu_kms); | |
946 | if (rc) { | |
947 | DPU_ERROR("modeset init failed: %d\n", rc); | |
948 | goto drm_obj_init_err; | |
949 | } | |
950 | ||
c24b6330 | 951 | dpu_vbif_init_memtypes(dpu_kms); |
25fdd593 JS |
952 | |
953 | pm_runtime_put_sync(&dpu_kms->pdev->dev); | |
954 | ||
955 | return 0; | |
956 | ||
957 | drm_obj_init_err: | |
958 | dpu_core_perf_destroy(&dpu_kms->perf); | |
959 | hw_intr_init_err: | |
960 | perf_err: | |
961 | power_error: | |
962 | pm_runtime_put_sync(&dpu_kms->pdev->dev); | |
963 | error: | |
964 | _dpu_kms_hw_destroy(dpu_kms); | |
27bc773a | 965 | |
25fdd593 JS |
966 | return rc; |
967 | } | |
968 | ||
969 | struct msm_kms *dpu_kms_init(struct drm_device *dev) | |
970 | { | |
971 | struct msm_drm_private *priv; | |
972 | struct dpu_kms *dpu_kms; | |
973 | int irq; | |
974 | ||
975 | if (!dev || !dev->dev_private) { | |
976 | DPU_ERROR("drm device node invalid\n"); | |
977 | return ERR_PTR(-EINVAL); | |
978 | } | |
979 | ||
980 | priv = dev->dev_private; | |
981 | dpu_kms = to_dpu_kms(priv->kms); | |
982 | ||
983 | irq = irq_of_parse_and_map(dpu_kms->pdev->dev.of_node, 0); | |
984 | if (irq < 0) { | |
985 | DPU_ERROR("failed to get irq: %d\n", irq); | |
986 | return ERR_PTR(irq); | |
987 | } | |
988 | dpu_kms->base.irq = irq; | |
989 | ||
990 | return &dpu_kms->base; | |
991 | } | |
992 | ||
993 | static int dpu_bind(struct device *dev, struct device *master, void *data) | |
994 | { | |
995 | struct drm_device *ddev = dev_get_drvdata(master); | |
996 | struct platform_device *pdev = to_platform_device(dev); | |
997 | struct msm_drm_private *priv = ddev->dev_private; | |
998 | struct dpu_kms *dpu_kms; | |
999 | struct dss_module_power *mp; | |
1000 | int ret = 0; | |
1001 | ||
1002 | dpu_kms = devm_kzalloc(&pdev->dev, sizeof(*dpu_kms), GFP_KERNEL); | |
1003 | if (!dpu_kms) | |
1004 | return -ENOMEM; | |
1005 | ||
1006 | mp = &dpu_kms->mp; | |
1007 | ret = msm_dss_parse_clock(pdev, mp); | |
1008 | if (ret) { | |
1009 | DPU_ERROR("failed to parse clocks, ret=%d\n", ret); | |
1010 | return ret; | |
1011 | } | |
1012 | ||
25fdd593 JS |
1013 | platform_set_drvdata(pdev, dpu_kms); |
1014 | ||
1015 | msm_kms_init(&dpu_kms->base, &kms_funcs); | |
1016 | dpu_kms->dev = ddev; | |
1017 | dpu_kms->pdev = pdev; | |
1018 | ||
1019 | pm_runtime_enable(&pdev->dev); | |
1020 | dpu_kms->rpm_enabled = true; | |
1021 | ||
1022 | priv->kms = &dpu_kms->base; | |
1023 | return ret; | |
1024 | } | |
1025 | ||
1026 | static void dpu_unbind(struct device *dev, struct device *master, void *data) | |
1027 | { | |
1028 | struct platform_device *pdev = to_platform_device(dev); | |
1029 | struct dpu_kms *dpu_kms = platform_get_drvdata(pdev); | |
1030 | struct dss_module_power *mp = &dpu_kms->mp; | |
1031 | ||
25fdd593 JS |
1032 | msm_dss_put_clk(mp->clk_config, mp->num_clk); |
1033 | devm_kfree(&pdev->dev, mp->clk_config); | |
1034 | mp->num_clk = 0; | |
1035 | ||
1036 | if (dpu_kms->rpm_enabled) | |
1037 | pm_runtime_disable(&pdev->dev); | |
1038 | } | |
1039 | ||
1040 | static const struct component_ops dpu_ops = { | |
1041 | .bind = dpu_bind, | |
1042 | .unbind = dpu_unbind, | |
1043 | }; | |
1044 | ||
1045 | static int dpu_dev_probe(struct platform_device *pdev) | |
1046 | { | |
1047 | return component_add(&pdev->dev, &dpu_ops); | |
1048 | } | |
1049 | ||
1050 | static int dpu_dev_remove(struct platform_device *pdev) | |
1051 | { | |
1052 | component_del(&pdev->dev, &dpu_ops); | |
1053 | return 0; | |
1054 | } | |
1055 | ||
78918cd0 | 1056 | static int __maybe_unused dpu_runtime_suspend(struct device *dev) |
25fdd593 JS |
1057 | { |
1058 | int rc = -1; | |
1059 | struct platform_device *pdev = to_platform_device(dev); | |
1060 | struct dpu_kms *dpu_kms = platform_get_drvdata(pdev); | |
1061 | struct drm_device *ddev; | |
1062 | struct dss_module_power *mp = &dpu_kms->mp; | |
1063 | ||
1064 | ddev = dpu_kms->dev; | |
1065 | if (!ddev) { | |
1066 | DPU_ERROR("invalid drm_device\n"); | |
0841851f | 1067 | return rc; |
25fdd593 JS |
1068 | } |
1069 | ||
25fdd593 JS |
1070 | rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, false); |
1071 | if (rc) | |
1072 | DPU_ERROR("clock disable failed rc:%d\n", rc); | |
1073 | ||
25fdd593 JS |
1074 | return rc; |
1075 | } | |
1076 | ||
78918cd0 | 1077 | static int __maybe_unused dpu_runtime_resume(struct device *dev) |
25fdd593 JS |
1078 | { |
1079 | int rc = -1; | |
1080 | struct platform_device *pdev = to_platform_device(dev); | |
1081 | struct dpu_kms *dpu_kms = platform_get_drvdata(pdev); | |
18a63b3c | 1082 | struct drm_encoder *encoder; |
25fdd593 JS |
1083 | struct drm_device *ddev; |
1084 | struct dss_module_power *mp = &dpu_kms->mp; | |
1085 | ||
1086 | ddev = dpu_kms->dev; | |
1087 | if (!ddev) { | |
1088 | DPU_ERROR("invalid drm_device\n"); | |
0841851f | 1089 | return rc; |
25fdd593 JS |
1090 | } |
1091 | ||
1092 | rc = msm_dss_enable_clk(mp->clk_config, mp->num_clk, true); | |
1093 | if (rc) { | |
1094 | DPU_ERROR("clock enable failed rc:%d\n", rc); | |
0841851f | 1095 | return rc; |
25fdd593 JS |
1096 | } |
1097 | ||
c24b6330 SP |
1098 | dpu_vbif_init_memtypes(dpu_kms); |
1099 | ||
18a63b3c SP |
1100 | drm_for_each_encoder(encoder, ddev) |
1101 | dpu_encoder_virt_runtime_resume(encoder); | |
3cf63cd5 | 1102 | |
25fdd593 JS |
1103 | return rc; |
1104 | } | |
1105 | ||
1106 | static const struct dev_pm_ops dpu_pm_ops = { | |
1107 | SET_RUNTIME_PM_OPS(dpu_runtime_suspend, dpu_runtime_resume, NULL) | |
1108 | }; | |
1109 | ||
1110 | static const struct of_device_id dpu_dt_match[] = { | |
1111 | { .compatible = "qcom,sdm845-dpu", }, | |
1112 | {} | |
1113 | }; | |
1114 | MODULE_DEVICE_TABLE(of, dpu_dt_match); | |
1115 | ||
1116 | static struct platform_driver dpu_driver = { | |
1117 | .probe = dpu_dev_probe, | |
1118 | .remove = dpu_dev_remove, | |
1119 | .driver = { | |
1120 | .name = "msm_dpu", | |
1121 | .of_match_table = dpu_dt_match, | |
1122 | .pm = &dpu_pm_ops, | |
1123 | }, | |
1124 | }; | |
1125 | ||
1126 | void __init msm_dpu_register(void) | |
1127 | { | |
1128 | platform_driver_register(&dpu_driver); | |
1129 | } | |
1130 | ||
1131 | void __exit msm_dpu_unregister(void) | |
1132 | { | |
1133 | platform_driver_unregister(&dpu_driver); | |
1134 | } |