Commit | Line | Data |
---|---|---|
ad49f860 LD |
1 | /* |
2 | * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. | |
3 | * Author: Liviu Dudau <Liviu.Dudau@arm.com> | |
4 | * | |
5 | * This program is free software and is provided to you under the terms of the | |
6 | * GNU General Public License version 2 as published by the Free Software | |
7 | * Foundation, and any use by you of this program is subject to the terms | |
8 | * of such GNU licence. | |
9 | * | |
10 | * ARM Mali DP500/DP550/DP650 KMS/DRM driver | |
11 | */ | |
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/clk.h> | |
15 | #include <linux/component.h> | |
16 | #include <linux/of_device.h> | |
17 | #include <linux/of_graph.h> | |
18 | #include <linux/of_reserved_mem.h> | |
85f64218 | 19 | #include <linux/pm_runtime.h> |
613c5c7f | 20 | #include <linux/debugfs.h> |
ad49f860 LD |
21 | |
22 | #include <drm/drmP.h> | |
23 | #include <drm/drm_atomic.h> | |
24 | #include <drm/drm_atomic_helper.h> | |
25 | #include <drm/drm_crtc.h> | |
26 | #include <drm/drm_crtc_helper.h> | |
bdecd835 | 27 | #include <drm/drm_fb_helper.h> |
ad49f860 LD |
28 | #include <drm/drm_fb_cma_helper.h> |
29 | #include <drm/drm_gem_cma_helper.h> | |
783f7d97 | 30 | #include <drm/drm_gem_framebuffer_helper.h> |
194b8799 | 31 | #include <drm/drm_modeset_helper.h> |
ad49f860 LD |
32 | #include <drm/drm_of.h> |
33 | ||
34 | #include "malidp_drv.h" | |
8cbc5caf | 35 | #include "malidp_mw.h" |
ad49f860 LD |
36 | #include "malidp_regs.h" |
37 | #include "malidp_hw.h" | |
38 | ||
39 | #define MALIDP_CONF_VALID_TIMEOUT 250 | |
3dae1c09 | 40 | #define AFBC_HEADER_SIZE 16 |
fd99bd8b | 41 | #define AFBC_SUPERBLK_ALIGNMENT 128 |
ad49f860 | 42 | |
02725d31 MA |
43 | static void malidp_write_gamma_table(struct malidp_hw_device *hwdev, |
44 | u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) | |
45 | { | |
46 | int i; | |
47 | /* Update all channels with a single gamma curve. */ | |
48 | const u32 gamma_write_mask = GENMASK(18, 16); | |
49 | /* | |
50 | * Always write an entire table, so the address field in | |
51 | * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask | |
52 | * directly. | |
53 | */ | |
54 | malidp_hw_write(hwdev, gamma_write_mask, | |
a6993b21 | 55 | hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); |
02725d31 MA |
56 | for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) |
57 | malidp_hw_write(hwdev, data[i], | |
a6993b21 | 58 | hwdev->hw->map.coeffs_base + |
02725d31 MA |
59 | MALIDP_COEF_TABLE_DATA); |
60 | } | |
61 | ||
62 | static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc, | |
63 | struct drm_crtc_state *old_state) | |
64 | { | |
65 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
66 | struct malidp_hw_device *hwdev = malidp->dev; | |
67 | ||
68 | if (!crtc->state->color_mgmt_changed) | |
69 | return; | |
70 | ||
71 | if (!crtc->state->gamma_lut) { | |
72 | malidp_hw_clearbits(hwdev, | |
73 | MALIDP_DISP_FUNC_GAMMA, | |
74 | MALIDP_DE_DISPLAY_FUNC); | |
75 | } else { | |
76 | struct malidp_crtc_state *mc = | |
77 | to_malidp_crtc_state(crtc->state); | |
78 | ||
79 | if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id != | |
80 | old_state->gamma_lut->base.id)) | |
81 | malidp_write_gamma_table(hwdev, mc->gamma_coeffs); | |
82 | ||
83 | malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA, | |
84 | MALIDP_DE_DISPLAY_FUNC); | |
85 | } | |
86 | } | |
87 | ||
6954f245 MA |
88 | static |
89 | void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc, | |
90 | struct drm_crtc_state *old_state) | |
91 | { | |
92 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
93 | struct malidp_hw_device *hwdev = malidp->dev; | |
94 | int i; | |
95 | ||
96 | if (!crtc->state->color_mgmt_changed) | |
97 | return; | |
98 | ||
99 | if (!crtc->state->ctm) { | |
100 | malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ, | |
101 | MALIDP_DE_DISPLAY_FUNC); | |
102 | } else { | |
103 | struct malidp_crtc_state *mc = | |
104 | to_malidp_crtc_state(crtc->state); | |
105 | ||
106 | if (!old_state->ctm || (crtc->state->ctm->base.id != | |
107 | old_state->ctm->base.id)) | |
108 | for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i) | |
109 | malidp_hw_write(hwdev, | |
110 | mc->coloradj_coeffs[i], | |
a6993b21 | 111 | hwdev->hw->map.coeffs_base + |
6954f245 MA |
112 | MALIDP_COLOR_ADJ_COEF + 4 * i); |
113 | ||
114 | malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ, | |
115 | MALIDP_DE_DISPLAY_FUNC); | |
116 | } | |
117 | } | |
118 | ||
28ce675b MA |
119 | static void malidp_atomic_commit_se_config(struct drm_crtc *crtc, |
120 | struct drm_crtc_state *old_state) | |
121 | { | |
122 | struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state); | |
123 | struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state); | |
124 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
125 | struct malidp_hw_device *hwdev = malidp->dev; | |
126 | struct malidp_se_config *s = &cs->scaler_config; | |
127 | struct malidp_se_config *old_s = &old_cs->scaler_config; | |
a6993b21 LD |
128 | u32 se_control = hwdev->hw->map.se_base + |
129 | ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? | |
28ce675b MA |
130 | 0x10 : 0xC); |
131 | u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL; | |
132 | u32 scr = se_control + MALIDP_SE_SCALING_CONTROL; | |
133 | u32 val; | |
134 | ||
135 | /* Set SE_CONTROL */ | |
136 | if (!s->scale_enable) { | |
137 | val = malidp_hw_read(hwdev, se_control); | |
138 | val &= ~MALIDP_SE_SCALING_EN; | |
139 | malidp_hw_write(hwdev, val, se_control); | |
140 | return; | |
141 | } | |
142 | ||
a6993b21 | 143 | hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s); |
28ce675b MA |
144 | val = malidp_hw_read(hwdev, se_control); |
145 | val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN; | |
146 | ||
0274e6a0 MA |
147 | val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK); |
148 | val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0; | |
149 | ||
28ce675b MA |
150 | val |= MALIDP_SE_RGBO_IF_EN; |
151 | malidp_hw_write(hwdev, val, se_control); | |
152 | ||
153 | /* Set IN_SIZE & OUT_SIZE. */ | |
154 | val = MALIDP_SE_SET_V_SIZE(s->input_h) | | |
155 | MALIDP_SE_SET_H_SIZE(s->input_w); | |
156 | malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE); | |
157 | val = MALIDP_SE_SET_V_SIZE(s->output_h) | | |
158 | MALIDP_SE_SET_H_SIZE(s->output_w); | |
159 | malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE); | |
160 | ||
161 | /* Set phase regs. */ | |
162 | malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH); | |
163 | malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH); | |
164 | malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH); | |
165 | malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH); | |
166 | } | |
167 | ||
ad49f860 LD |
168 | /* |
169 | * set the "config valid" bit and wait until the hardware acts on it | |
170 | */ | |
171 | static int malidp_set_and_wait_config_valid(struct drm_device *drm) | |
172 | { | |
173 | struct malidp_drm *malidp = drm->dev_private; | |
174 | struct malidp_hw_device *hwdev = malidp->dev; | |
175 | int ret; | |
176 | ||
0735cfdf | 177 | hwdev->hw->set_config_valid(hwdev, 1); |
ad49f860 | 178 | /* don't wait for config_valid flag if we are in config mode */ |
1cb3cbe7 LD |
179 | if (hwdev->hw->in_config_mode(hwdev)) { |
180 | atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); | |
ad49f860 | 181 | return 0; |
1cb3cbe7 | 182 | } |
ad49f860 LD |
183 | |
184 | ret = wait_event_interruptible_timeout(malidp->wq, | |
1cb3cbe7 | 185 | atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE, |
ad49f860 LD |
186 | msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); |
187 | ||
188 | return (ret > 0) ? 0 : -ETIMEDOUT; | |
189 | } | |
190 | ||
ad49f860 LD |
191 | static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) |
192 | { | |
ad49f860 LD |
193 | struct drm_device *drm = state->dev; |
194 | struct malidp_drm *malidp = drm->dev_private; | |
ad49f860 | 195 | |
d862b2d6 LD |
196 | malidp->event = malidp->crtc.state->event; |
197 | malidp->crtc.state->event = NULL; | |
ad49f860 | 198 | |
d862b2d6 LD |
199 | if (malidp->crtc.state->active) { |
200 | /* | |
201 | * if we have an event to deliver to userspace, make sure | |
202 | * the vblank is enabled as we are sending it from the IRQ | |
203 | * handler. | |
204 | */ | |
205 | if (malidp->event) | |
206 | drm_crtc_vblank_get(&malidp->crtc); | |
ad49f860 | 207 | |
d862b2d6 LD |
208 | /* only set config_valid if the CRTC is enabled */ |
209 | if (malidp_set_and_wait_config_valid(drm) < 0) | |
210 | DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); | |
211 | } else if (malidp->event) { | |
212 | /* CRTC inactive means vblank IRQ is disabled, send event directly */ | |
ad49f860 | 213 | spin_lock_irq(&drm->event_lock); |
d862b2d6 LD |
214 | drm_crtc_send_vblank_event(&malidp->crtc, malidp->event); |
215 | malidp->event = NULL; | |
ad49f860 LD |
216 | spin_unlock_irq(&drm->event_lock); |
217 | } | |
218 | drm_atomic_helper_commit_hw_done(state); | |
219 | } | |
220 | ||
221 | static void malidp_atomic_commit_tail(struct drm_atomic_state *state) | |
222 | { | |
223 | struct drm_device *drm = state->dev; | |
1cb3cbe7 | 224 | struct malidp_drm *malidp = drm->dev_private; |
02725d31 MA |
225 | struct drm_crtc *crtc; |
226 | struct drm_crtc_state *old_crtc_state; | |
227 | int i; | |
ad49f860 | 228 | |
85f64218 LD |
229 | pm_runtime_get_sync(drm->dev); |
230 | ||
1cb3cbe7 LD |
231 | /* |
232 | * set config_valid to a special value to let IRQ handlers | |
233 | * know that we are updating registers | |
234 | */ | |
235 | atomic_set(&malidp->config_valid, MALIDP_CONFIG_START); | |
0735cfdf | 236 | malidp->dev->hw->set_config_valid(malidp->dev, 0); |
1cb3cbe7 | 237 | |
ad49f860 | 238 | drm_atomic_helper_commit_modeset_disables(drm, state); |
46f1d42f | 239 | |
a8e3fb55 | 240 | for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { |
02725d31 | 241 | malidp_atomic_commit_update_gamma(crtc, old_crtc_state); |
6954f245 | 242 | malidp_atomic_commit_update_coloradj(crtc, old_crtc_state); |
28ce675b | 243 | malidp_atomic_commit_se_config(crtc, old_crtc_state); |
6954f245 | 244 | } |
02725d31 | 245 | |
8cbc5caf BS |
246 | drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); |
247 | ||
248 | malidp_mw_atomic_commit(drm, state); | |
ad49f860 | 249 | |
46f1d42f LD |
250 | drm_atomic_helper_commit_modeset_enables(drm, state); |
251 | ||
ad49f860 LD |
252 | malidp_atomic_commit_hw_done(state); |
253 | ||
85f64218 LD |
254 | pm_runtime_put(drm->dev); |
255 | ||
ad49f860 LD |
256 | drm_atomic_helper_cleanup_planes(drm, state); |
257 | } | |
258 | ||
a4b10cce | 259 | static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { |
ad49f860 LD |
260 | .atomic_commit_tail = malidp_atomic_commit_tail, |
261 | }; | |
262 | ||
3dae1c09 AKH |
263 | static bool |
264 | malidp_verify_afbc_framebuffer_caps(struct drm_device *dev, | |
265 | const struct drm_mode_fb_cmd2 *mode_cmd) | |
266 | { | |
267 | const struct drm_format_info *info; | |
268 | ||
269 | if ((mode_cmd->modifier[0] >> 56) != DRM_FORMAT_MOD_VENDOR_ARM) { | |
270 | DRM_DEBUG_KMS("Unknown modifier (not Arm)\n"); | |
271 | return false; | |
272 | } | |
273 | ||
274 | if (mode_cmd->modifier[0] & | |
275 | ~DRM_FORMAT_MOD_ARM_AFBC(AFBC_MOD_VALID_BITS)) { | |
276 | DRM_DEBUG_KMS("Unsupported modifiers\n"); | |
277 | return false; | |
278 | } | |
279 | ||
280 | info = drm_get_format_info(dev, mode_cmd); | |
281 | if (!info) { | |
282 | DRM_DEBUG_KMS("Unable to get the format information\n"); | |
283 | return false; | |
284 | } | |
285 | ||
286 | if (info->num_planes != 1) { | |
287 | DRM_DEBUG_KMS("AFBC buffers expect one plane\n"); | |
288 | return false; | |
289 | } | |
290 | ||
291 | if (mode_cmd->offsets[0] != 0) { | |
292 | DRM_DEBUG_KMS("AFBC buffers' plane offset should be 0\n"); | |
293 | return false; | |
294 | } | |
295 | ||
296 | switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) { | |
297 | case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16: | |
298 | if ((mode_cmd->width % 16) || (mode_cmd->height % 16)) { | |
299 | DRM_DEBUG_KMS("AFBC buffers must be aligned to 16 pixels\n"); | |
300 | return false; | |
301 | } | |
302 | break; | |
303 | default: | |
304 | DRM_DEBUG_KMS("Unsupported AFBC block size\n"); | |
305 | return false; | |
306 | } | |
307 | ||
308 | return true; | |
309 | } | |
310 | ||
311 | static bool | |
312 | malidp_verify_afbc_framebuffer_size(struct drm_device *dev, | |
313 | struct drm_file *file, | |
314 | const struct drm_mode_fb_cmd2 *mode_cmd) | |
315 | { | |
316 | int n_superblocks = 0; | |
317 | const struct drm_format_info *info; | |
318 | struct drm_gem_object *objs = NULL; | |
319 | u32 afbc_superblock_size = 0, afbc_superblock_height = 0; | |
320 | u32 afbc_superblock_width = 0, afbc_size = 0; | |
321 | ||
322 | switch (mode_cmd->modifier[0] & AFBC_FORMAT_MOD_BLOCK_SIZE_MASK) { | |
323 | case AFBC_FORMAT_MOD_BLOCK_SIZE_16x16: | |
324 | afbc_superblock_height = 16; | |
325 | afbc_superblock_width = 16; | |
326 | break; | |
327 | default: | |
328 | DRM_DEBUG_KMS("AFBC superblock size is not supported\n"); | |
329 | return false; | |
330 | } | |
331 | ||
332 | info = drm_get_format_info(dev, mode_cmd); | |
333 | ||
334 | n_superblocks = (mode_cmd->width / afbc_superblock_width) * | |
335 | (mode_cmd->height / afbc_superblock_height); | |
336 | ||
337 | afbc_superblock_size = info->cpp[0] * afbc_superblock_width * | |
338 | afbc_superblock_height; | |
339 | ||
fd99bd8b LD |
340 | afbc_size = ALIGN(n_superblocks * AFBC_HEADER_SIZE, AFBC_SUPERBLK_ALIGNMENT); |
341 | afbc_size += n_superblocks * ALIGN(afbc_superblock_size, AFBC_SUPERBLK_ALIGNMENT); | |
3dae1c09 AKH |
342 | |
343 | if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) { | |
344 | DRM_DEBUG_KMS("Invalid value of pitch (=%u) should be same as width (=%u) * cpp (=%u)\n", | |
345 | mode_cmd->pitches[0], mode_cmd->width, info->cpp[0]); | |
346 | return false; | |
347 | } | |
348 | ||
349 | objs = drm_gem_object_lookup(file, mode_cmd->handles[0]); | |
350 | if (!objs) { | |
351 | DRM_DEBUG_KMS("Failed to lookup GEM object\n"); | |
352 | return false; | |
353 | } | |
354 | ||
355 | if (objs->size < afbc_size) { | |
356 | DRM_DEBUG_KMS("buffer size (%zu) too small for AFBC buffer size = %u\n", | |
357 | objs->size, afbc_size); | |
358 | drm_gem_object_put_unlocked(objs); | |
359 | return false; | |
360 | } | |
361 | ||
362 | drm_gem_object_put_unlocked(objs); | |
363 | ||
364 | return true; | |
365 | } | |
366 | ||
367 | static bool | |
368 | malidp_verify_afbc_framebuffer(struct drm_device *dev, struct drm_file *file, | |
369 | const struct drm_mode_fb_cmd2 *mode_cmd) | |
370 | { | |
371 | if (malidp_verify_afbc_framebuffer_caps(dev, mode_cmd)) | |
372 | return malidp_verify_afbc_framebuffer_size(dev, file, mode_cmd); | |
373 | ||
374 | return false; | |
375 | } | |
376 | ||
377 | struct drm_framebuffer * | |
378 | malidp_fb_create(struct drm_device *dev, struct drm_file *file, | |
379 | const struct drm_mode_fb_cmd2 *mode_cmd) | |
380 | { | |
381 | if (mode_cmd->modifier[0]) { | |
382 | if (!malidp_verify_afbc_framebuffer(dev, file, mode_cmd)) | |
383 | return ERR_PTR(-EINVAL); | |
384 | } | |
385 | ||
386 | return drm_gem_fb_create(dev, file, mode_cmd); | |
387 | } | |
388 | ||
ad49f860 | 389 | static const struct drm_mode_config_funcs malidp_mode_config_funcs = { |
3dae1c09 | 390 | .fb_create = malidp_fb_create, |
ad49f860 LD |
391 | .atomic_check = drm_atomic_helper_check, |
392 | .atomic_commit = drm_atomic_helper_commit, | |
393 | }; | |
394 | ||
ad49f860 LD |
395 | static int malidp_init(struct drm_device *drm) |
396 | { | |
397 | int ret; | |
398 | struct malidp_drm *malidp = drm->dev_private; | |
399 | struct malidp_hw_device *hwdev = malidp->dev; | |
400 | ||
401 | drm_mode_config_init(drm); | |
402 | ||
403 | drm->mode_config.min_width = hwdev->min_line_size; | |
404 | drm->mode_config.min_height = hwdev->min_line_size; | |
405 | drm->mode_config.max_width = hwdev->max_line_size; | |
406 | drm->mode_config.max_height = hwdev->max_line_size; | |
407 | drm->mode_config.funcs = &malidp_mode_config_funcs; | |
408 | drm->mode_config.helper_private = &malidp_mode_config_helpers; | |
409 | ||
410 | ret = malidp_crtc_init(drm); | |
8cbc5caf BS |
411 | if (ret) |
412 | goto crtc_fail; | |
413 | ||
414 | ret = malidp_mw_connector_init(drm); | |
415 | if (ret) | |
416 | goto crtc_fail; | |
ad49f860 LD |
417 | |
418 | return 0; | |
8cbc5caf BS |
419 | |
420 | crtc_fail: | |
421 | drm_mode_config_cleanup(drm); | |
422 | return ret; | |
ad49f860 LD |
423 | } |
424 | ||
de9c4810 BS |
425 | static void malidp_fini(struct drm_device *drm) |
426 | { | |
de9c4810 BS |
427 | drm_mode_config_cleanup(drm); |
428 | } | |
429 | ||
ad49f860 LD |
430 | static int malidp_irq_init(struct platform_device *pdev) |
431 | { | |
432 | int irq_de, irq_se, ret = 0; | |
433 | struct drm_device *drm = dev_get_drvdata(&pdev->dev); | |
62862cfb AKH |
434 | struct malidp_drm *malidp = drm->dev_private; |
435 | struct malidp_hw_device *hwdev = malidp->dev; | |
ad49f860 LD |
436 | |
437 | /* fetch the interrupts from DT */ | |
438 | irq_de = platform_get_irq_byname(pdev, "DE"); | |
439 | if (irq_de < 0) { | |
440 | DRM_ERROR("no 'DE' IRQ specified!\n"); | |
441 | return irq_de; | |
442 | } | |
443 | irq_se = platform_get_irq_byname(pdev, "SE"); | |
444 | if (irq_se < 0) { | |
445 | DRM_ERROR("no 'SE' IRQ specified!\n"); | |
446 | return irq_se; | |
447 | } | |
448 | ||
449 | ret = malidp_de_irq_init(drm, irq_de); | |
450 | if (ret) | |
451 | return ret; | |
452 | ||
453 | ret = malidp_se_irq_init(drm, irq_se); | |
454 | if (ret) { | |
62862cfb | 455 | malidp_de_irq_fini(hwdev); |
ad49f860 LD |
456 | return ret; |
457 | } | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
d55f7e5d | 462 | DEFINE_DRM_GEM_CMA_FOPS(fops); |
ad49f860 | 463 | |
5ed4fdfa LD |
464 | static int malidp_dumb_create(struct drm_file *file_priv, |
465 | struct drm_device *drm, | |
466 | struct drm_mode_create_dumb *args) | |
467 | { | |
468 | struct malidp_drm *malidp = drm->dev_private; | |
469 | /* allocate for the worst case scenario, i.e. rotated buffers */ | |
470 | u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 1); | |
471 | ||
472 | args->pitch = ALIGN(DIV_ROUND_UP(args->width * args->bpp, 8), alignment); | |
473 | ||
474 | return drm_gem_cma_dumb_create_internal(file_priv, drm, args); | |
475 | } | |
476 | ||
613c5c7f AG |
477 | #ifdef CONFIG_DEBUG_FS |
478 | ||
479 | static void malidp_error_stats_init(struct malidp_error_stats *error_stats) | |
480 | { | |
481 | error_stats->num_errors = 0; | |
482 | error_stats->last_error_status = 0; | |
483 | error_stats->last_error_vblank = -1; | |
484 | } | |
485 | ||
486 | void malidp_error(struct malidp_drm *malidp, | |
487 | struct malidp_error_stats *error_stats, u32 status, | |
488 | u64 vblank) | |
489 | { | |
490 | unsigned long irqflags; | |
491 | ||
492 | spin_lock_irqsave(&malidp->errors_lock, irqflags); | |
493 | error_stats->last_error_status = status; | |
494 | error_stats->last_error_vblank = vblank; | |
495 | error_stats->num_errors++; | |
496 | spin_unlock_irqrestore(&malidp->errors_lock, irqflags); | |
497 | } | |
498 | ||
499 | void malidp_error_stats_dump(const char *prefix, | |
500 | struct malidp_error_stats error_stats, | |
501 | struct seq_file *m) | |
502 | { | |
503 | seq_printf(m, "[%s] num_errors : %d\n", prefix, | |
504 | error_stats.num_errors); | |
505 | seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix, | |
506 | error_stats.last_error_status); | |
507 | seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix, | |
508 | error_stats.last_error_vblank); | |
509 | } | |
510 | ||
511 | static int malidp_show_stats(struct seq_file *m, void *arg) | |
512 | { | |
513 | struct drm_device *drm = m->private; | |
514 | struct malidp_drm *malidp = drm->dev_private; | |
515 | unsigned long irqflags; | |
516 | struct malidp_error_stats de_errors, se_errors; | |
517 | ||
518 | spin_lock_irqsave(&malidp->errors_lock, irqflags); | |
519 | de_errors = malidp->de_errors; | |
520 | se_errors = malidp->se_errors; | |
521 | spin_unlock_irqrestore(&malidp->errors_lock, irqflags); | |
522 | malidp_error_stats_dump("DE", de_errors, m); | |
523 | malidp_error_stats_dump("SE", se_errors, m); | |
524 | return 0; | |
525 | } | |
526 | ||
527 | static int malidp_debugfs_open(struct inode *inode, struct file *file) | |
528 | { | |
529 | return single_open(file, malidp_show_stats, inode->i_private); | |
530 | } | |
531 | ||
532 | static ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf, | |
533 | size_t len, loff_t *offp) | |
534 | { | |
535 | struct seq_file *m = file->private_data; | |
536 | struct drm_device *drm = m->private; | |
537 | struct malidp_drm *malidp = drm->dev_private; | |
538 | unsigned long irqflags; | |
539 | ||
540 | spin_lock_irqsave(&malidp->errors_lock, irqflags); | |
541 | malidp_error_stats_init(&malidp->de_errors); | |
542 | malidp_error_stats_init(&malidp->se_errors); | |
543 | spin_unlock_irqrestore(&malidp->errors_lock, irqflags); | |
544 | return len; | |
545 | } | |
546 | ||
547 | static const struct file_operations malidp_debugfs_fops = { | |
548 | .owner = THIS_MODULE, | |
549 | .open = malidp_debugfs_open, | |
550 | .read = seq_read, | |
551 | .write = malidp_debugfs_write, | |
552 | .llseek = seq_lseek, | |
553 | .release = single_release, | |
554 | }; | |
555 | ||
556 | static int malidp_debugfs_init(struct drm_minor *minor) | |
557 | { | |
558 | struct malidp_drm *malidp = minor->dev->dev_private; | |
559 | struct dentry *dentry = NULL; | |
560 | ||
561 | malidp_error_stats_init(&malidp->de_errors); | |
562 | malidp_error_stats_init(&malidp->se_errors); | |
563 | spin_lock_init(&malidp->errors_lock); | |
564 | dentry = debugfs_create_file("debug", | |
565 | S_IRUGO | S_IWUSR, | |
566 | minor->debugfs_root, minor->dev, | |
567 | &malidp_debugfs_fops); | |
568 | if (!dentry) { | |
569 | DRM_ERROR("Cannot create debug file\n"); | |
570 | return -ENOMEM; | |
571 | } | |
572 | return 0; | |
573 | } | |
574 | ||
575 | #endif //CONFIG_DEBUG_FS | |
576 | ||
ad49f860 LD |
577 | static struct drm_driver malidp_driver = { |
578 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | | |
579 | DRIVER_PRIME, | |
ad49f860 LD |
580 | .gem_free_object_unlocked = drm_gem_cma_free_object, |
581 | .gem_vm_ops = &drm_gem_cma_vm_ops, | |
5ed4fdfa | 582 | .dumb_create = malidp_dumb_create, |
ad49f860 LD |
583 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
584 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
585 | .gem_prime_export = drm_gem_prime_export, | |
586 | .gem_prime_import = drm_gem_prime_import, | |
587 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | |
588 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | |
589 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | |
590 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | |
591 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | |
613c5c7f AG |
592 | #ifdef CONFIG_DEBUG_FS |
593 | .debugfs_init = malidp_debugfs_init, | |
594 | #endif | |
ad49f860 LD |
595 | .fops = &fops, |
596 | .name = "mali-dp", | |
597 | .desc = "ARM Mali Display Processor driver", | |
598 | .date = "20160106", | |
599 | .major = 1, | |
600 | .minor = 0, | |
601 | }; | |
602 | ||
603 | static const struct of_device_id malidp_drm_of_match[] = { | |
604 | { | |
605 | .compatible = "arm,mali-dp500", | |
606 | .data = &malidp_device[MALIDP_500] | |
607 | }, | |
608 | { | |
609 | .compatible = "arm,mali-dp550", | |
610 | .data = &malidp_device[MALIDP_550] | |
611 | }, | |
612 | { | |
613 | .compatible = "arm,mali-dp650", | |
614 | .data = &malidp_device[MALIDP_650] | |
615 | }, | |
616 | {}, | |
617 | }; | |
618 | MODULE_DEVICE_TABLE(of, malidp_drm_of_match); | |
619 | ||
592d8c8c MA |
620 | static bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev, |
621 | const struct of_device_id *dev_id) | |
622 | { | |
623 | u32 core_id; | |
624 | const char *compatstr_dp500 = "arm,mali-dp500"; | |
625 | bool is_dp500; | |
626 | bool dt_is_dp500; | |
627 | ||
628 | /* | |
629 | * The DP500 CORE_ID register is in a different location, so check it | |
630 | * first. If the product id field matches, then this is DP500, otherwise | |
631 | * check the DP550/650 CORE_ID register. | |
632 | */ | |
633 | core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID); | |
634 | /* Offset 0x18 will never read 0x500 on products other than DP500. */ | |
635 | is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500); | |
636 | dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500, | |
637 | sizeof(dev_id->compatible)) != NULL; | |
638 | if (is_dp500 != dt_is_dp500) { | |
639 | DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n", | |
640 | dev_id->compatible, is_dp500 ? "is" : "is not"); | |
641 | return false; | |
642 | } else if (!dt_is_dp500) { | |
643 | u16 product_id; | |
644 | char buf[32]; | |
645 | ||
646 | core_id = malidp_hw_read(hwdev, | |
647 | MALIDP550_DC_BASE + MALIDP_DE_CORE_ID); | |
648 | product_id = MALIDP_PRODUCT_ID(core_id); | |
649 | snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id); | |
650 | if (!strnstr(dev_id->compatible, buf, | |
651 | sizeof(dev_id->compatible))) { | |
652 | DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n", | |
653 | dev_id->compatible, product_id); | |
654 | return false; | |
655 | } | |
656 | } | |
657 | return true; | |
658 | } | |
659 | ||
4d6000ed MA |
660 | static bool malidp_has_sufficient_address_space(const struct resource *res, |
661 | const struct of_device_id *dev_id) | |
662 | { | |
663 | resource_size_t res_size = resource_size(res); | |
664 | const char *compatstr_dp500 = "arm,mali-dp500"; | |
665 | ||
666 | if (!strnstr(dev_id->compatible, compatstr_dp500, | |
667 | sizeof(dev_id->compatible))) | |
668 | return res_size >= MALIDP550_ADDR_SPACE_SIZE; | |
669 | else if (res_size < MALIDP500_ADDR_SPACE_SIZE) | |
670 | return false; | |
671 | return true; | |
672 | } | |
673 | ||
50c7512f LD |
674 | static ssize_t core_id_show(struct device *dev, struct device_attribute *attr, |
675 | char *buf) | |
676 | { | |
677 | struct drm_device *drm = dev_get_drvdata(dev); | |
678 | struct malidp_drm *malidp = drm->dev_private; | |
679 | ||
680 | return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id); | |
681 | } | |
682 | ||
683 | DEVICE_ATTR_RO(core_id); | |
684 | ||
685 | static int malidp_init_sysfs(struct device *dev) | |
686 | { | |
687 | int ret = device_create_file(dev, &dev_attr_core_id); | |
688 | ||
689 | if (ret) | |
690 | DRM_ERROR("failed to create device file for core_id\n"); | |
691 | ||
692 | return ret; | |
693 | } | |
694 | ||
695 | static void malidp_fini_sysfs(struct device *dev) | |
696 | { | |
697 | device_remove_file(dev, &dev_attr_core_id); | |
698 | } | |
699 | ||
ad49f860 LD |
700 | #define MAX_OUTPUT_CHANNELS 3 |
701 | ||
85f64218 LD |
702 | static int malidp_runtime_pm_suspend(struct device *dev) |
703 | { | |
704 | struct drm_device *drm = dev_get_drvdata(dev); | |
705 | struct malidp_drm *malidp = drm->dev_private; | |
706 | struct malidp_hw_device *hwdev = malidp->dev; | |
707 | ||
708 | /* we can only suspend if the hardware is in config mode */ | |
a6993b21 | 709 | WARN_ON(!hwdev->hw->in_config_mode(hwdev)); |
85f64218 | 710 | |
fbcc454e AKH |
711 | malidp_se_irq_fini(hwdev); |
712 | malidp_de_irq_fini(hwdev); | |
85f64218 LD |
713 | hwdev->pm_suspended = true; |
714 | clk_disable_unprepare(hwdev->mclk); | |
715 | clk_disable_unprepare(hwdev->aclk); | |
716 | clk_disable_unprepare(hwdev->pclk); | |
717 | ||
718 | return 0; | |
719 | } | |
720 | ||
721 | static int malidp_runtime_pm_resume(struct device *dev) | |
722 | { | |
723 | struct drm_device *drm = dev_get_drvdata(dev); | |
724 | struct malidp_drm *malidp = drm->dev_private; | |
725 | struct malidp_hw_device *hwdev = malidp->dev; | |
726 | ||
727 | clk_prepare_enable(hwdev->pclk); | |
728 | clk_prepare_enable(hwdev->aclk); | |
729 | clk_prepare_enable(hwdev->mclk); | |
730 | hwdev->pm_suspended = false; | |
fbcc454e AKH |
731 | malidp_de_irq_hw_init(hwdev); |
732 | malidp_se_irq_hw_init(hwdev); | |
85f64218 LD |
733 | |
734 | return 0; | |
735 | } | |
736 | ||
ad49f860 LD |
737 | static int malidp_bind(struct device *dev) |
738 | { | |
739 | struct resource *res; | |
740 | struct drm_device *drm; | |
741 | struct malidp_drm *malidp; | |
742 | struct malidp_hw_device *hwdev; | |
743 | struct platform_device *pdev = to_platform_device(dev); | |
592d8c8c | 744 | struct of_device_id const *dev_id; |
2e012e76 | 745 | struct drm_encoder *encoder; |
ad49f860 LD |
746 | /* number of lines for the R, G and B output */ |
747 | u8 output_width[MAX_OUTPUT_CHANNELS]; | |
748 | int ret = 0, i; | |
749 | u32 version, out_depth = 0; | |
750 | ||
751 | malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL); | |
752 | if (!malidp) | |
753 | return -ENOMEM; | |
754 | ||
755 | hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL); | |
756 | if (!hwdev) | |
757 | return -ENOMEM; | |
758 | ||
a6993b21 | 759 | hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev); |
ad49f860 LD |
760 | malidp->dev = hwdev; |
761 | ||
ad49f860 LD |
762 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
763 | hwdev->regs = devm_ioremap_resource(dev, res); | |
1a9d71f8 | 764 | if (IS_ERR(hwdev->regs)) |
ad49f860 | 765 | return PTR_ERR(hwdev->regs); |
ad49f860 LD |
766 | |
767 | hwdev->pclk = devm_clk_get(dev, "pclk"); | |
768 | if (IS_ERR(hwdev->pclk)) | |
769 | return PTR_ERR(hwdev->pclk); | |
770 | ||
771 | hwdev->aclk = devm_clk_get(dev, "aclk"); | |
772 | if (IS_ERR(hwdev->aclk)) | |
773 | return PTR_ERR(hwdev->aclk); | |
774 | ||
775 | hwdev->mclk = devm_clk_get(dev, "mclk"); | |
776 | if (IS_ERR(hwdev->mclk)) | |
777 | return PTR_ERR(hwdev->mclk); | |
778 | ||
779 | hwdev->pxlclk = devm_clk_get(dev, "pxlclk"); | |
780 | if (IS_ERR(hwdev->pxlclk)) | |
781 | return PTR_ERR(hwdev->pxlclk); | |
782 | ||
783 | /* Get the optional framebuffer memory resource */ | |
784 | ret = of_reserved_mem_device_init(dev); | |
785 | if (ret && ret != -ENODEV) | |
786 | return ret; | |
787 | ||
788 | drm = drm_dev_alloc(&malidp_driver, dev); | |
0f288605 TG |
789 | if (IS_ERR(drm)) { |
790 | ret = PTR_ERR(drm); | |
ad49f860 LD |
791 | goto alloc_fail; |
792 | } | |
793 | ||
85f64218 LD |
794 | drm->dev_private = malidp; |
795 | dev_set_drvdata(dev, drm); | |
796 | ||
797 | /* Enable power management */ | |
798 | pm_runtime_enable(dev); | |
799 | ||
800 | /* Resume device to enable the clocks */ | |
801 | if (pm_runtime_enabled(dev)) | |
802 | pm_runtime_get_sync(dev); | |
803 | else | |
804 | malidp_runtime_pm_resume(dev); | |
ad49f860 | 805 | |
592d8c8c MA |
806 | dev_id = of_match_device(malidp_drm_of_match, dev); |
807 | if (!dev_id) { | |
808 | ret = -EINVAL; | |
809 | goto query_hw_fail; | |
810 | } | |
811 | ||
4d6000ed MA |
812 | if (!malidp_has_sufficient_address_space(res, dev_id)) { |
813 | DRM_ERROR("Insufficient address space in device-tree.\n"); | |
814 | ret = -EINVAL; | |
815 | goto query_hw_fail; | |
816 | } | |
817 | ||
592d8c8c MA |
818 | if (!malidp_is_compatible_hw_id(hwdev, dev_id)) { |
819 | ret = -EINVAL; | |
820 | goto query_hw_fail; | |
821 | } | |
822 | ||
a6993b21 | 823 | ret = hwdev->hw->query_hw(hwdev); |
ad49f860 LD |
824 | if (ret) { |
825 | DRM_ERROR("Invalid HW configuration\n"); | |
826 | goto query_hw_fail; | |
827 | } | |
828 | ||
a6993b21 | 829 | version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID); |
ad49f860 LD |
830 | DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16, |
831 | (version >> 12) & 0xf, (version >> 8) & 0xf); | |
832 | ||
50c7512f LD |
833 | malidp->core_id = version; |
834 | ||
ad49f860 LD |
835 | /* set the number of lines used for output of RGB data */ |
836 | ret = of_property_read_u8_array(dev->of_node, | |
837 | "arm,malidp-output-port-lines", | |
838 | output_width, MAX_OUTPUT_CHANNELS); | |
839 | if (ret) | |
840 | goto query_hw_fail; | |
841 | ||
842 | for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) | |
843 | out_depth = (out_depth << 8) | (output_width[i] & 0xf); | |
a6993b21 | 844 | malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); |
f877006d | 845 | hwdev->output_color_depth = out_depth; |
ad49f860 | 846 | |
1cb3cbe7 | 847 | atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT); |
ad49f860 LD |
848 | init_waitqueue_head(&malidp->wq); |
849 | ||
850 | ret = malidp_init(drm); | |
851 | if (ret < 0) | |
85f64218 | 852 | goto query_hw_fail; |
ad49f860 | 853 | |
50c7512f LD |
854 | ret = malidp_init_sysfs(dev); |
855 | if (ret) | |
856 | goto init_fail; | |
857 | ||
ad49f860 | 858 | /* Set the CRTC's port so that the encoder component can find it */ |
86418f90 | 859 | malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); |
ad49f860 LD |
860 | |
861 | ret = component_bind_all(dev, drm); | |
ad49f860 LD |
862 | if (ret) { |
863 | DRM_ERROR("Failed to bind all components\n"); | |
864 | goto bind_fail; | |
865 | } | |
866 | ||
2e012e76 AG |
867 | /* We expect to have a maximum of two encoders one for the actual |
868 | * display and a virtual one for the writeback connector | |
869 | */ | |
870 | WARN_ON(drm->mode_config.num_encoder > 2); | |
871 | list_for_each_entry(encoder, &drm->mode_config.encoder_list, head) { | |
872 | encoder->possible_clones = | |
873 | (1 << drm->mode_config.num_encoder) - 1; | |
874 | } | |
875 | ||
ad49f860 LD |
876 | ret = malidp_irq_init(pdev); |
877 | if (ret < 0) | |
878 | goto irq_init_fail; | |
879 | ||
a6a7b9a2 LD |
880 | drm->irq_enabled = true; |
881 | ||
ad49f860 | 882 | ret = drm_vblank_init(drm, drm->mode_config.num_crtc); |
cabce634 | 883 | drm_crtc_vblank_reset(&malidp->crtc); |
ad49f860 LD |
884 | if (ret < 0) { |
885 | DRM_ERROR("failed to initialise vblank\n"); | |
886 | goto vblank_fail; | |
887 | } | |
85f64218 | 888 | pm_runtime_put(dev); |
ad49f860 LD |
889 | |
890 | drm_mode_config_reset(drm); | |
891 | ||
ad49f860 | 892 | drm_kms_helper_poll_init(drm); |
90731c24 BS |
893 | |
894 | ret = drm_dev_register(drm, 0); | |
895 | if (ret) | |
896 | goto register_fail; | |
897 | ||
95958098 NT |
898 | drm_fbdev_generic_setup(drm, 32); |
899 | ||
ad49f860 LD |
900 | return 0; |
901 | ||
90731c24 | 902 | register_fail: |
85f64218 | 903 | drm_kms_helper_poll_fini(drm); |
85f64218 | 904 | pm_runtime_get_sync(dev); |
ad49f860 | 905 | vblank_fail: |
62862cfb AKH |
906 | malidp_se_irq_fini(hwdev); |
907 | malidp_de_irq_fini(hwdev); | |
a6a7b9a2 | 908 | drm->irq_enabled = false; |
ad49f860 | 909 | irq_init_fail: |
109c4d18 | 910 | drm_atomic_helper_shutdown(drm); |
ad49f860 LD |
911 | component_unbind_all(dev, drm); |
912 | bind_fail: | |
3c31760e BS |
913 | of_node_put(malidp->crtc.port); |
914 | malidp->crtc.port = NULL; | |
50c7512f LD |
915 | init_fail: |
916 | malidp_fini_sysfs(dev); | |
de9c4810 | 917 | malidp_fini(drm); |
85f64218 LD |
918 | query_hw_fail: |
919 | pm_runtime_put(dev); | |
920 | if (pm_runtime_enabled(dev)) | |
921 | pm_runtime_disable(dev); | |
922 | else | |
923 | malidp_runtime_pm_suspend(dev); | |
ad49f860 LD |
924 | drm->dev_private = NULL; |
925 | dev_set_drvdata(dev, NULL); | |
0970d7a2 | 926 | drm_dev_put(drm); |
ad49f860 LD |
927 | alloc_fail: |
928 | of_reserved_mem_device_release(dev); | |
929 | ||
930 | return ret; | |
931 | } | |
932 | ||
933 | static void malidp_unbind(struct device *dev) | |
934 | { | |
935 | struct drm_device *drm = dev_get_drvdata(dev); | |
936 | struct malidp_drm *malidp = drm->dev_private; | |
62862cfb | 937 | struct malidp_hw_device *hwdev = malidp->dev; |
ad49f860 | 938 | |
90731c24 | 939 | drm_dev_unregister(drm); |
ad49f860 | 940 | drm_kms_helper_poll_fini(drm); |
85f64218 | 941 | pm_runtime_get_sync(dev); |
57085dca | 942 | drm_crtc_vblank_off(&malidp->crtc); |
62862cfb AKH |
943 | malidp_se_irq_fini(hwdev); |
944 | malidp_de_irq_fini(hwdev); | |
57085dca | 945 | drm->irq_enabled = false; |
109c4d18 | 946 | drm_atomic_helper_shutdown(drm); |
ad49f860 | 947 | component_unbind_all(dev, drm); |
3c31760e BS |
948 | of_node_put(malidp->crtc.port); |
949 | malidp->crtc.port = NULL; | |
50c7512f | 950 | malidp_fini_sysfs(dev); |
de9c4810 | 951 | malidp_fini(drm); |
85f64218 LD |
952 | pm_runtime_put(dev); |
953 | if (pm_runtime_enabled(dev)) | |
954 | pm_runtime_disable(dev); | |
955 | else | |
956 | malidp_runtime_pm_suspend(dev); | |
ad49f860 LD |
957 | drm->dev_private = NULL; |
958 | dev_set_drvdata(dev, NULL); | |
0970d7a2 | 959 | drm_dev_put(drm); |
ad49f860 LD |
960 | of_reserved_mem_device_release(dev); |
961 | } | |
962 | ||
963 | static const struct component_master_ops malidp_master_ops = { | |
964 | .bind = malidp_bind, | |
965 | .unbind = malidp_unbind, | |
966 | }; | |
967 | ||
968 | static int malidp_compare_dev(struct device *dev, void *data) | |
969 | { | |
970 | struct device_node *np = data; | |
971 | ||
972 | return dev->of_node == np; | |
973 | } | |
974 | ||
975 | static int malidp_platform_probe(struct platform_device *pdev) | |
976 | { | |
86418f90 | 977 | struct device_node *port; |
ad49f860 LD |
978 | struct component_match *match = NULL; |
979 | ||
980 | if (!pdev->dev.of_node) | |
981 | return -ENODEV; | |
982 | ||
983 | /* there is only one output port inside each device, find it */ | |
86418f90 RH |
984 | port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); |
985 | if (!port) | |
ad49f860 | 986 | return -ENODEV; |
ad49f860 | 987 | |
97ac0e47 RK |
988 | drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev, |
989 | port); | |
990 | of_node_put(port); | |
ad49f860 LD |
991 | return component_master_add_with_match(&pdev->dev, &malidp_master_ops, |
992 | match); | |
993 | } | |
994 | ||
995 | static int malidp_platform_remove(struct platform_device *pdev) | |
996 | { | |
997 | component_master_del(&pdev->dev, &malidp_master_ops); | |
998 | return 0; | |
999 | } | |
1000 | ||
85f64218 LD |
1001 | static int __maybe_unused malidp_pm_suspend(struct device *dev) |
1002 | { | |
1003 | struct drm_device *drm = dev_get_drvdata(dev); | |
85f64218 | 1004 | |
194b8799 | 1005 | return drm_mode_config_helper_suspend(drm); |
85f64218 LD |
1006 | } |
1007 | ||
1008 | static int __maybe_unused malidp_pm_resume(struct device *dev) | |
1009 | { | |
1010 | struct drm_device *drm = dev_get_drvdata(dev); | |
85f64218 | 1011 | |
194b8799 | 1012 | drm_mode_config_helper_resume(drm); |
85f64218 LD |
1013 | |
1014 | return 0; | |
1015 | } | |
1016 | ||
e368fc75 AKH |
1017 | static int __maybe_unused malidp_pm_suspend_late(struct device *dev) |
1018 | { | |
1019 | if (!pm_runtime_status_suspended(dev)) { | |
1020 | malidp_runtime_pm_suspend(dev); | |
1021 | pm_runtime_set_suspended(dev); | |
1022 | } | |
1023 | return 0; | |
1024 | } | |
1025 | ||
1026 | static int __maybe_unused malidp_pm_resume_early(struct device *dev) | |
1027 | { | |
1028 | malidp_runtime_pm_resume(dev); | |
1029 | pm_runtime_set_active(dev); | |
1030 | return 0; | |
1031 | } | |
1032 | ||
85f64218 LD |
1033 | static const struct dev_pm_ops malidp_pm_ops = { |
1034 | SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ | |
e368fc75 | 1035 | SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \ |
85f64218 LD |
1036 | SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) |
1037 | }; | |
1038 | ||
ad49f860 LD |
1039 | static struct platform_driver malidp_platform_driver = { |
1040 | .probe = malidp_platform_probe, | |
1041 | .remove = malidp_platform_remove, | |
1042 | .driver = { | |
1043 | .name = "mali-dp", | |
85f64218 | 1044 | .pm = &malidp_pm_ops, |
ad49f860 LD |
1045 | .of_match_table = malidp_drm_of_match, |
1046 | }, | |
1047 | }; | |
1048 | ||
1049 | module_platform_driver(malidp_platform_driver); | |
1050 | ||
1051 | MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); | |
1052 | MODULE_DESCRIPTION("ARM Mali DP DRM driver"); | |
1053 | MODULE_LICENSE("GPL v2"); |