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