]>
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> | |
85f64218 | 16 | #include <linux/console.h> |
ad49f860 LD |
17 | #include <linux/of_device.h> |
18 | #include <linux/of_graph.h> | |
19 | #include <linux/of_reserved_mem.h> | |
85f64218 | 20 | #include <linux/pm_runtime.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> | |
ad49f860 LD |
27 | #include <drm/drm_fb_cma_helper.h> |
28 | #include <drm/drm_gem_cma_helper.h> | |
783f7d97 | 29 | #include <drm/drm_gem_framebuffer_helper.h> |
ad49f860 LD |
30 | #include <drm/drm_of.h> |
31 | ||
32 | #include "malidp_drv.h" | |
33 | #include "malidp_regs.h" | |
34 | #include "malidp_hw.h" | |
35 | ||
36 | #define MALIDP_CONF_VALID_TIMEOUT 250 | |
37 | ||
02725d31 MA |
38 | static void malidp_write_gamma_table(struct malidp_hw_device *hwdev, |
39 | u32 data[MALIDP_COEFFTAB_NUM_COEFFS]) | |
40 | { | |
41 | int i; | |
42 | /* Update all channels with a single gamma curve. */ | |
43 | const u32 gamma_write_mask = GENMASK(18, 16); | |
44 | /* | |
45 | * Always write an entire table, so the address field in | |
46 | * DE_COEFFTAB_ADDR is 0 and we can use the gamma_write_mask bitmask | |
47 | * directly. | |
48 | */ | |
49 | malidp_hw_write(hwdev, gamma_write_mask, | |
a6993b21 | 50 | hwdev->hw->map.coeffs_base + MALIDP_COEF_TABLE_ADDR); |
02725d31 MA |
51 | for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) |
52 | malidp_hw_write(hwdev, data[i], | |
a6993b21 | 53 | hwdev->hw->map.coeffs_base + |
02725d31 MA |
54 | MALIDP_COEF_TABLE_DATA); |
55 | } | |
56 | ||
57 | static void malidp_atomic_commit_update_gamma(struct drm_crtc *crtc, | |
58 | struct drm_crtc_state *old_state) | |
59 | { | |
60 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
61 | struct malidp_hw_device *hwdev = malidp->dev; | |
62 | ||
63 | if (!crtc->state->color_mgmt_changed) | |
64 | return; | |
65 | ||
66 | if (!crtc->state->gamma_lut) { | |
67 | malidp_hw_clearbits(hwdev, | |
68 | MALIDP_DISP_FUNC_GAMMA, | |
69 | MALIDP_DE_DISPLAY_FUNC); | |
70 | } else { | |
71 | struct malidp_crtc_state *mc = | |
72 | to_malidp_crtc_state(crtc->state); | |
73 | ||
74 | if (!old_state->gamma_lut || (crtc->state->gamma_lut->base.id != | |
75 | old_state->gamma_lut->base.id)) | |
76 | malidp_write_gamma_table(hwdev, mc->gamma_coeffs); | |
77 | ||
78 | malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_GAMMA, | |
79 | MALIDP_DE_DISPLAY_FUNC); | |
80 | } | |
81 | } | |
82 | ||
6954f245 MA |
83 | static |
84 | void malidp_atomic_commit_update_coloradj(struct drm_crtc *crtc, | |
85 | struct drm_crtc_state *old_state) | |
86 | { | |
87 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
88 | struct malidp_hw_device *hwdev = malidp->dev; | |
89 | int i; | |
90 | ||
91 | if (!crtc->state->color_mgmt_changed) | |
92 | return; | |
93 | ||
94 | if (!crtc->state->ctm) { | |
95 | malidp_hw_clearbits(hwdev, MALIDP_DISP_FUNC_CADJ, | |
96 | MALIDP_DE_DISPLAY_FUNC); | |
97 | } else { | |
98 | struct malidp_crtc_state *mc = | |
99 | to_malidp_crtc_state(crtc->state); | |
100 | ||
101 | if (!old_state->ctm || (crtc->state->ctm->base.id != | |
102 | old_state->ctm->base.id)) | |
103 | for (i = 0; i < MALIDP_COLORADJ_NUM_COEFFS; ++i) | |
104 | malidp_hw_write(hwdev, | |
105 | mc->coloradj_coeffs[i], | |
a6993b21 | 106 | hwdev->hw->map.coeffs_base + |
6954f245 MA |
107 | MALIDP_COLOR_ADJ_COEF + 4 * i); |
108 | ||
109 | malidp_hw_setbits(hwdev, MALIDP_DISP_FUNC_CADJ, | |
110 | MALIDP_DE_DISPLAY_FUNC); | |
111 | } | |
112 | } | |
113 | ||
28ce675b MA |
114 | static void malidp_atomic_commit_se_config(struct drm_crtc *crtc, |
115 | struct drm_crtc_state *old_state) | |
116 | { | |
117 | struct malidp_crtc_state *cs = to_malidp_crtc_state(crtc->state); | |
118 | struct malidp_crtc_state *old_cs = to_malidp_crtc_state(old_state); | |
119 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
120 | struct malidp_hw_device *hwdev = malidp->dev; | |
121 | struct malidp_se_config *s = &cs->scaler_config; | |
122 | struct malidp_se_config *old_s = &old_cs->scaler_config; | |
a6993b21 LD |
123 | u32 se_control = hwdev->hw->map.se_base + |
124 | ((hwdev->hw->map.features & MALIDP_REGMAP_HAS_CLEARIRQ) ? | |
28ce675b MA |
125 | 0x10 : 0xC); |
126 | u32 layer_control = se_control + MALIDP_SE_LAYER_CONTROL; | |
127 | u32 scr = se_control + MALIDP_SE_SCALING_CONTROL; | |
128 | u32 val; | |
129 | ||
130 | /* Set SE_CONTROL */ | |
131 | if (!s->scale_enable) { | |
132 | val = malidp_hw_read(hwdev, se_control); | |
133 | val &= ~MALIDP_SE_SCALING_EN; | |
134 | malidp_hw_write(hwdev, val, se_control); | |
135 | return; | |
136 | } | |
137 | ||
a6993b21 | 138 | hwdev->hw->se_set_scaling_coeffs(hwdev, s, old_s); |
28ce675b MA |
139 | val = malidp_hw_read(hwdev, se_control); |
140 | val |= MALIDP_SE_SCALING_EN | MALIDP_SE_ALPHA_EN; | |
141 | ||
0274e6a0 MA |
142 | val &= ~MALIDP_SE_ENH(MALIDP_SE_ENH_MASK); |
143 | val |= s->enhancer_enable ? MALIDP_SE_ENH(3) : 0; | |
144 | ||
28ce675b MA |
145 | val |= MALIDP_SE_RGBO_IF_EN; |
146 | malidp_hw_write(hwdev, val, se_control); | |
147 | ||
148 | /* Set IN_SIZE & OUT_SIZE. */ | |
149 | val = MALIDP_SE_SET_V_SIZE(s->input_h) | | |
150 | MALIDP_SE_SET_H_SIZE(s->input_w); | |
151 | malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_IN_SIZE); | |
152 | val = MALIDP_SE_SET_V_SIZE(s->output_h) | | |
153 | MALIDP_SE_SET_H_SIZE(s->output_w); | |
154 | malidp_hw_write(hwdev, val, layer_control + MALIDP_SE_L0_OUT_SIZE); | |
155 | ||
156 | /* Set phase regs. */ | |
157 | malidp_hw_write(hwdev, s->h_init_phase, scr + MALIDP_SE_H_INIT_PH); | |
158 | malidp_hw_write(hwdev, s->h_delta_phase, scr + MALIDP_SE_H_DELTA_PH); | |
159 | malidp_hw_write(hwdev, s->v_init_phase, scr + MALIDP_SE_V_INIT_PH); | |
160 | malidp_hw_write(hwdev, s->v_delta_phase, scr + MALIDP_SE_V_DELTA_PH); | |
161 | } | |
162 | ||
ad49f860 LD |
163 | /* |
164 | * set the "config valid" bit and wait until the hardware acts on it | |
165 | */ | |
166 | static int malidp_set_and_wait_config_valid(struct drm_device *drm) | |
167 | { | |
168 | struct malidp_drm *malidp = drm->dev_private; | |
169 | struct malidp_hw_device *hwdev = malidp->dev; | |
170 | int ret; | |
171 | ||
aad38963 | 172 | atomic_set(&malidp->config_valid, 0); |
a6993b21 | 173 | hwdev->hw->set_config_valid(hwdev); |
ad49f860 | 174 | /* don't wait for config_valid flag if we are in config mode */ |
a6993b21 | 175 | if (hwdev->hw->in_config_mode(hwdev)) |
ad49f860 LD |
176 | return 0; |
177 | ||
178 | ret = wait_event_interruptible_timeout(malidp->wq, | |
179 | atomic_read(&malidp->config_valid) == 1, | |
180 | msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); | |
181 | ||
182 | return (ret > 0) ? 0 : -ETIMEDOUT; | |
183 | } | |
184 | ||
185 | static void malidp_output_poll_changed(struct drm_device *drm) | |
186 | { | |
187 | struct malidp_drm *malidp = drm->dev_private; | |
188 | ||
189 | drm_fbdev_cma_hotplug_event(malidp->fbdev); | |
190 | } | |
191 | ||
192 | static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) | |
193 | { | |
194 | struct drm_pending_vblank_event *event; | |
195 | struct drm_device *drm = state->dev; | |
196 | struct malidp_drm *malidp = drm->dev_private; | |
ad49f860 | 197 | |
46f1d42f LD |
198 | if (malidp->crtc.enabled) { |
199 | /* only set config_valid if the CRTC is enabled */ | |
200 | if (malidp_set_and_wait_config_valid(drm)) | |
201 | DRM_DEBUG_DRIVER("timed out waiting for updated configuration\n"); | |
202 | } | |
ad49f860 LD |
203 | |
204 | event = malidp->crtc.state->event; | |
205 | if (event) { | |
206 | malidp->crtc.state->event = NULL; | |
207 | ||
208 | spin_lock_irq(&drm->event_lock); | |
209 | if (drm_crtc_vblank_get(&malidp->crtc) == 0) | |
210 | drm_crtc_arm_vblank_event(&malidp->crtc, event); | |
211 | else | |
212 | drm_crtc_send_vblank_event(&malidp->crtc, event); | |
213 | spin_unlock_irq(&drm->event_lock); | |
214 | } | |
215 | drm_atomic_helper_commit_hw_done(state); | |
216 | } | |
217 | ||
218 | static void malidp_atomic_commit_tail(struct drm_atomic_state *state) | |
219 | { | |
220 | struct drm_device *drm = state->dev; | |
02725d31 MA |
221 | struct drm_crtc *crtc; |
222 | struct drm_crtc_state *old_crtc_state; | |
223 | int i; | |
ad49f860 | 224 | |
85f64218 LD |
225 | pm_runtime_get_sync(drm->dev); |
226 | ||
ad49f860 | 227 | drm_atomic_helper_commit_modeset_disables(drm, state); |
46f1d42f | 228 | |
a8e3fb55 | 229 | for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { |
02725d31 | 230 | malidp_atomic_commit_update_gamma(crtc, old_crtc_state); |
6954f245 | 231 | malidp_atomic_commit_update_coloradj(crtc, old_crtc_state); |
28ce675b | 232 | malidp_atomic_commit_se_config(crtc, old_crtc_state); |
6954f245 | 233 | } |
02725d31 | 234 | |
fe37ed6a | 235 | drm_atomic_helper_commit_planes(drm, state, 0); |
ad49f860 | 236 | |
46f1d42f LD |
237 | drm_atomic_helper_commit_modeset_enables(drm, state); |
238 | ||
ad49f860 LD |
239 | malidp_atomic_commit_hw_done(state); |
240 | ||
241 | drm_atomic_helper_wait_for_vblanks(drm, state); | |
242 | ||
85f64218 LD |
243 | pm_runtime_put(drm->dev); |
244 | ||
ad49f860 LD |
245 | drm_atomic_helper_cleanup_planes(drm, state); |
246 | } | |
247 | ||
a4b10cce | 248 | static const struct drm_mode_config_helper_funcs malidp_mode_config_helpers = { |
ad49f860 LD |
249 | .atomic_commit_tail = malidp_atomic_commit_tail, |
250 | }; | |
251 | ||
252 | static const struct drm_mode_config_funcs malidp_mode_config_funcs = { | |
783f7d97 | 253 | .fb_create = drm_gem_fb_create, |
ad49f860 LD |
254 | .output_poll_changed = malidp_output_poll_changed, |
255 | .atomic_check = drm_atomic_helper_check, | |
256 | .atomic_commit = drm_atomic_helper_commit, | |
257 | }; | |
258 | ||
ad49f860 LD |
259 | static int malidp_init(struct drm_device *drm) |
260 | { | |
261 | int ret; | |
262 | struct malidp_drm *malidp = drm->dev_private; | |
263 | struct malidp_hw_device *hwdev = malidp->dev; | |
264 | ||
265 | drm_mode_config_init(drm); | |
266 | ||
267 | drm->mode_config.min_width = hwdev->min_line_size; | |
268 | drm->mode_config.min_height = hwdev->min_line_size; | |
269 | drm->mode_config.max_width = hwdev->max_line_size; | |
270 | drm->mode_config.max_height = hwdev->max_line_size; | |
271 | drm->mode_config.funcs = &malidp_mode_config_funcs; | |
272 | drm->mode_config.helper_private = &malidp_mode_config_helpers; | |
273 | ||
274 | ret = malidp_crtc_init(drm); | |
275 | if (ret) { | |
276 | drm_mode_config_cleanup(drm); | |
277 | return ret; | |
278 | } | |
279 | ||
280 | return 0; | |
281 | } | |
282 | ||
de9c4810 BS |
283 | static void malidp_fini(struct drm_device *drm) |
284 | { | |
285 | malidp_de_planes_destroy(drm); | |
286 | drm_mode_config_cleanup(drm); | |
287 | } | |
288 | ||
ad49f860 LD |
289 | static int malidp_irq_init(struct platform_device *pdev) |
290 | { | |
291 | int irq_de, irq_se, ret = 0; | |
292 | struct drm_device *drm = dev_get_drvdata(&pdev->dev); | |
293 | ||
294 | /* fetch the interrupts from DT */ | |
295 | irq_de = platform_get_irq_byname(pdev, "DE"); | |
296 | if (irq_de < 0) { | |
297 | DRM_ERROR("no 'DE' IRQ specified!\n"); | |
298 | return irq_de; | |
299 | } | |
300 | irq_se = platform_get_irq_byname(pdev, "SE"); | |
301 | if (irq_se < 0) { | |
302 | DRM_ERROR("no 'SE' IRQ specified!\n"); | |
303 | return irq_se; | |
304 | } | |
305 | ||
306 | ret = malidp_de_irq_init(drm, irq_de); | |
307 | if (ret) | |
308 | return ret; | |
309 | ||
310 | ret = malidp_se_irq_init(drm, irq_se); | |
311 | if (ret) { | |
312 | malidp_de_irq_fini(drm); | |
313 | return ret; | |
314 | } | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | static void malidp_lastclose(struct drm_device *drm) | |
320 | { | |
321 | struct malidp_drm *malidp = drm->dev_private; | |
322 | ||
323 | drm_fbdev_cma_restore_mode(malidp->fbdev); | |
324 | } | |
325 | ||
d55f7e5d | 326 | DEFINE_DRM_GEM_CMA_FOPS(fops); |
ad49f860 LD |
327 | |
328 | static struct drm_driver malidp_driver = { | |
329 | .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | | |
330 | DRIVER_PRIME, | |
331 | .lastclose = malidp_lastclose, | |
ad49f860 LD |
332 | .gem_free_object_unlocked = drm_gem_cma_free_object, |
333 | .gem_vm_ops = &drm_gem_cma_vm_ops, | |
334 | .dumb_create = drm_gem_cma_dumb_create, | |
ad49f860 LD |
335 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, |
336 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
337 | .gem_prime_export = drm_gem_prime_export, | |
338 | .gem_prime_import = drm_gem_prime_import, | |
339 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | |
340 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | |
341 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | |
342 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | |
343 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | |
344 | .fops = &fops, | |
345 | .name = "mali-dp", | |
346 | .desc = "ARM Mali Display Processor driver", | |
347 | .date = "20160106", | |
348 | .major = 1, | |
349 | .minor = 0, | |
350 | }; | |
351 | ||
352 | static const struct of_device_id malidp_drm_of_match[] = { | |
353 | { | |
354 | .compatible = "arm,mali-dp500", | |
355 | .data = &malidp_device[MALIDP_500] | |
356 | }, | |
357 | { | |
358 | .compatible = "arm,mali-dp550", | |
359 | .data = &malidp_device[MALIDP_550] | |
360 | }, | |
361 | { | |
362 | .compatible = "arm,mali-dp650", | |
363 | .data = &malidp_device[MALIDP_650] | |
364 | }, | |
365 | {}, | |
366 | }; | |
367 | MODULE_DEVICE_TABLE(of, malidp_drm_of_match); | |
368 | ||
592d8c8c MA |
369 | static bool malidp_is_compatible_hw_id(struct malidp_hw_device *hwdev, |
370 | const struct of_device_id *dev_id) | |
371 | { | |
372 | u32 core_id; | |
373 | const char *compatstr_dp500 = "arm,mali-dp500"; | |
374 | bool is_dp500; | |
375 | bool dt_is_dp500; | |
376 | ||
377 | /* | |
378 | * The DP500 CORE_ID register is in a different location, so check it | |
379 | * first. If the product id field matches, then this is DP500, otherwise | |
380 | * check the DP550/650 CORE_ID register. | |
381 | */ | |
382 | core_id = malidp_hw_read(hwdev, MALIDP500_DC_BASE + MALIDP_DE_CORE_ID); | |
383 | /* Offset 0x18 will never read 0x500 on products other than DP500. */ | |
384 | is_dp500 = (MALIDP_PRODUCT_ID(core_id) == 0x500); | |
385 | dt_is_dp500 = strnstr(dev_id->compatible, compatstr_dp500, | |
386 | sizeof(dev_id->compatible)) != NULL; | |
387 | if (is_dp500 != dt_is_dp500) { | |
388 | DRM_ERROR("Device-tree expects %s, but hardware %s DP500.\n", | |
389 | dev_id->compatible, is_dp500 ? "is" : "is not"); | |
390 | return false; | |
391 | } else if (!dt_is_dp500) { | |
392 | u16 product_id; | |
393 | char buf[32]; | |
394 | ||
395 | core_id = malidp_hw_read(hwdev, | |
396 | MALIDP550_DC_BASE + MALIDP_DE_CORE_ID); | |
397 | product_id = MALIDP_PRODUCT_ID(core_id); | |
398 | snprintf(buf, sizeof(buf), "arm,mali-dp%X", product_id); | |
399 | if (!strnstr(dev_id->compatible, buf, | |
400 | sizeof(dev_id->compatible))) { | |
401 | DRM_ERROR("Device-tree expects %s, but hardware is DP%03X.\n", | |
402 | dev_id->compatible, product_id); | |
403 | return false; | |
404 | } | |
405 | } | |
406 | return true; | |
407 | } | |
408 | ||
4d6000ed MA |
409 | static bool malidp_has_sufficient_address_space(const struct resource *res, |
410 | const struct of_device_id *dev_id) | |
411 | { | |
412 | resource_size_t res_size = resource_size(res); | |
413 | const char *compatstr_dp500 = "arm,mali-dp500"; | |
414 | ||
415 | if (!strnstr(dev_id->compatible, compatstr_dp500, | |
416 | sizeof(dev_id->compatible))) | |
417 | return res_size >= MALIDP550_ADDR_SPACE_SIZE; | |
418 | else if (res_size < MALIDP500_ADDR_SPACE_SIZE) | |
419 | return false; | |
420 | return true; | |
421 | } | |
422 | ||
50c7512f LD |
423 | static ssize_t core_id_show(struct device *dev, struct device_attribute *attr, |
424 | char *buf) | |
425 | { | |
426 | struct drm_device *drm = dev_get_drvdata(dev); | |
427 | struct malidp_drm *malidp = drm->dev_private; | |
428 | ||
429 | return snprintf(buf, PAGE_SIZE, "%08x\n", malidp->core_id); | |
430 | } | |
431 | ||
432 | DEVICE_ATTR_RO(core_id); | |
433 | ||
434 | static int malidp_init_sysfs(struct device *dev) | |
435 | { | |
436 | int ret = device_create_file(dev, &dev_attr_core_id); | |
437 | ||
438 | if (ret) | |
439 | DRM_ERROR("failed to create device file for core_id\n"); | |
440 | ||
441 | return ret; | |
442 | } | |
443 | ||
444 | static void malidp_fini_sysfs(struct device *dev) | |
445 | { | |
446 | device_remove_file(dev, &dev_attr_core_id); | |
447 | } | |
448 | ||
ad49f860 LD |
449 | #define MAX_OUTPUT_CHANNELS 3 |
450 | ||
85f64218 LD |
451 | static int malidp_runtime_pm_suspend(struct device *dev) |
452 | { | |
453 | struct drm_device *drm = dev_get_drvdata(dev); | |
454 | struct malidp_drm *malidp = drm->dev_private; | |
455 | struct malidp_hw_device *hwdev = malidp->dev; | |
456 | ||
457 | /* we can only suspend if the hardware is in config mode */ | |
a6993b21 | 458 | WARN_ON(!hwdev->hw->in_config_mode(hwdev)); |
85f64218 LD |
459 | |
460 | hwdev->pm_suspended = true; | |
461 | clk_disable_unprepare(hwdev->mclk); | |
462 | clk_disable_unprepare(hwdev->aclk); | |
463 | clk_disable_unprepare(hwdev->pclk); | |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
468 | static int malidp_runtime_pm_resume(struct device *dev) | |
469 | { | |
470 | struct drm_device *drm = dev_get_drvdata(dev); | |
471 | struct malidp_drm *malidp = drm->dev_private; | |
472 | struct malidp_hw_device *hwdev = malidp->dev; | |
473 | ||
474 | clk_prepare_enable(hwdev->pclk); | |
475 | clk_prepare_enable(hwdev->aclk); | |
476 | clk_prepare_enable(hwdev->mclk); | |
477 | hwdev->pm_suspended = false; | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
ad49f860 LD |
482 | static int malidp_bind(struct device *dev) |
483 | { | |
484 | struct resource *res; | |
485 | struct drm_device *drm; | |
486 | struct malidp_drm *malidp; | |
487 | struct malidp_hw_device *hwdev; | |
488 | struct platform_device *pdev = to_platform_device(dev); | |
592d8c8c | 489 | struct of_device_id const *dev_id; |
ad49f860 LD |
490 | /* number of lines for the R, G and B output */ |
491 | u8 output_width[MAX_OUTPUT_CHANNELS]; | |
492 | int ret = 0, i; | |
493 | u32 version, out_depth = 0; | |
494 | ||
495 | malidp = devm_kzalloc(dev, sizeof(*malidp), GFP_KERNEL); | |
496 | if (!malidp) | |
497 | return -ENOMEM; | |
498 | ||
499 | hwdev = devm_kzalloc(dev, sizeof(*hwdev), GFP_KERNEL); | |
500 | if (!hwdev) | |
501 | return -ENOMEM; | |
502 | ||
a6993b21 | 503 | hwdev->hw = (struct malidp_hw *)of_device_get_match_data(dev); |
ad49f860 LD |
504 | malidp->dev = hwdev; |
505 | ||
ad49f860 LD |
506 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
507 | hwdev->regs = devm_ioremap_resource(dev, res); | |
1a9d71f8 | 508 | if (IS_ERR(hwdev->regs)) |
ad49f860 | 509 | return PTR_ERR(hwdev->regs); |
ad49f860 LD |
510 | |
511 | hwdev->pclk = devm_clk_get(dev, "pclk"); | |
512 | if (IS_ERR(hwdev->pclk)) | |
513 | return PTR_ERR(hwdev->pclk); | |
514 | ||
515 | hwdev->aclk = devm_clk_get(dev, "aclk"); | |
516 | if (IS_ERR(hwdev->aclk)) | |
517 | return PTR_ERR(hwdev->aclk); | |
518 | ||
519 | hwdev->mclk = devm_clk_get(dev, "mclk"); | |
520 | if (IS_ERR(hwdev->mclk)) | |
521 | return PTR_ERR(hwdev->mclk); | |
522 | ||
523 | hwdev->pxlclk = devm_clk_get(dev, "pxlclk"); | |
524 | if (IS_ERR(hwdev->pxlclk)) | |
525 | return PTR_ERR(hwdev->pxlclk); | |
526 | ||
527 | /* Get the optional framebuffer memory resource */ | |
528 | ret = of_reserved_mem_device_init(dev); | |
529 | if (ret && ret != -ENODEV) | |
530 | return ret; | |
531 | ||
532 | drm = drm_dev_alloc(&malidp_driver, dev); | |
0f288605 TG |
533 | if (IS_ERR(drm)) { |
534 | ret = PTR_ERR(drm); | |
ad49f860 LD |
535 | goto alloc_fail; |
536 | } | |
537 | ||
85f64218 LD |
538 | drm->dev_private = malidp; |
539 | dev_set_drvdata(dev, drm); | |
540 | ||
541 | /* Enable power management */ | |
542 | pm_runtime_enable(dev); | |
543 | ||
544 | /* Resume device to enable the clocks */ | |
545 | if (pm_runtime_enabled(dev)) | |
546 | pm_runtime_get_sync(dev); | |
547 | else | |
548 | malidp_runtime_pm_resume(dev); | |
ad49f860 | 549 | |
592d8c8c MA |
550 | dev_id = of_match_device(malidp_drm_of_match, dev); |
551 | if (!dev_id) { | |
552 | ret = -EINVAL; | |
553 | goto query_hw_fail; | |
554 | } | |
555 | ||
4d6000ed MA |
556 | if (!malidp_has_sufficient_address_space(res, dev_id)) { |
557 | DRM_ERROR("Insufficient address space in device-tree.\n"); | |
558 | ret = -EINVAL; | |
559 | goto query_hw_fail; | |
560 | } | |
561 | ||
592d8c8c MA |
562 | if (!malidp_is_compatible_hw_id(hwdev, dev_id)) { |
563 | ret = -EINVAL; | |
564 | goto query_hw_fail; | |
565 | } | |
566 | ||
a6993b21 | 567 | ret = hwdev->hw->query_hw(hwdev); |
ad49f860 LD |
568 | if (ret) { |
569 | DRM_ERROR("Invalid HW configuration\n"); | |
570 | goto query_hw_fail; | |
571 | } | |
572 | ||
a6993b21 | 573 | version = malidp_hw_read(hwdev, hwdev->hw->map.dc_base + MALIDP_DE_CORE_ID); |
ad49f860 LD |
574 | DRM_INFO("found ARM Mali-DP%3x version r%dp%d\n", version >> 16, |
575 | (version >> 12) & 0xf, (version >> 8) & 0xf); | |
576 | ||
50c7512f LD |
577 | malidp->core_id = version; |
578 | ||
ad49f860 LD |
579 | /* set the number of lines used for output of RGB data */ |
580 | ret = of_property_read_u8_array(dev->of_node, | |
581 | "arm,malidp-output-port-lines", | |
582 | output_width, MAX_OUTPUT_CHANNELS); | |
583 | if (ret) | |
584 | goto query_hw_fail; | |
585 | ||
586 | for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) | |
587 | out_depth = (out_depth << 8) | (output_width[i] & 0xf); | |
a6993b21 | 588 | malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); |
ad49f860 | 589 | |
ad49f860 LD |
590 | atomic_set(&malidp->config_valid, 0); |
591 | init_waitqueue_head(&malidp->wq); | |
592 | ||
593 | ret = malidp_init(drm); | |
594 | if (ret < 0) | |
85f64218 | 595 | goto query_hw_fail; |
ad49f860 | 596 | |
50c7512f LD |
597 | ret = malidp_init_sysfs(dev); |
598 | if (ret) | |
599 | goto init_fail; | |
600 | ||
ad49f860 | 601 | /* Set the CRTC's port so that the encoder component can find it */ |
86418f90 | 602 | malidp->crtc.port = of_graph_get_port_by_id(dev->of_node, 0); |
ad49f860 LD |
603 | |
604 | ret = component_bind_all(dev, drm); | |
ad49f860 LD |
605 | if (ret) { |
606 | DRM_ERROR("Failed to bind all components\n"); | |
607 | goto bind_fail; | |
608 | } | |
609 | ||
610 | ret = malidp_irq_init(pdev); | |
611 | if (ret < 0) | |
612 | goto irq_init_fail; | |
613 | ||
a6a7b9a2 LD |
614 | drm->irq_enabled = true; |
615 | ||
ad49f860 LD |
616 | ret = drm_vblank_init(drm, drm->mode_config.num_crtc); |
617 | if (ret < 0) { | |
618 | DRM_ERROR("failed to initialise vblank\n"); | |
619 | goto vblank_fail; | |
620 | } | |
85f64218 | 621 | pm_runtime_put(dev); |
ad49f860 LD |
622 | |
623 | drm_mode_config_reset(drm); | |
624 | ||
e4563f6b | 625 | malidp->fbdev = drm_fbdev_cma_init(drm, 32, |
ad49f860 LD |
626 | drm->mode_config.num_connector); |
627 | ||
628 | if (IS_ERR(malidp->fbdev)) { | |
629 | ret = PTR_ERR(malidp->fbdev); | |
630 | malidp->fbdev = NULL; | |
631 | goto fbdev_fail; | |
632 | } | |
633 | ||
634 | drm_kms_helper_poll_init(drm); | |
90731c24 BS |
635 | |
636 | ret = drm_dev_register(drm, 0); | |
637 | if (ret) | |
638 | goto register_fail; | |
639 | ||
ad49f860 LD |
640 | return 0; |
641 | ||
90731c24 BS |
642 | register_fail: |
643 | if (malidp->fbdev) { | |
644 | drm_fbdev_cma_fini(malidp->fbdev); | |
645 | malidp->fbdev = NULL; | |
646 | } | |
85f64218 | 647 | drm_kms_helper_poll_fini(drm); |
ad49f860 | 648 | fbdev_fail: |
85f64218 | 649 | pm_runtime_get_sync(dev); |
ad49f860 LD |
650 | vblank_fail: |
651 | malidp_se_irq_fini(drm); | |
652 | malidp_de_irq_fini(drm); | |
a6a7b9a2 | 653 | drm->irq_enabled = false; |
ad49f860 LD |
654 | irq_init_fail: |
655 | component_unbind_all(dev, drm); | |
656 | bind_fail: | |
3c31760e BS |
657 | of_node_put(malidp->crtc.port); |
658 | malidp->crtc.port = NULL; | |
50c7512f LD |
659 | init_fail: |
660 | malidp_fini_sysfs(dev); | |
de9c4810 | 661 | malidp_fini(drm); |
85f64218 LD |
662 | query_hw_fail: |
663 | pm_runtime_put(dev); | |
664 | if (pm_runtime_enabled(dev)) | |
665 | pm_runtime_disable(dev); | |
666 | else | |
667 | malidp_runtime_pm_suspend(dev); | |
ad49f860 LD |
668 | drm->dev_private = NULL; |
669 | dev_set_drvdata(dev, NULL); | |
0970d7a2 | 670 | drm_dev_put(drm); |
ad49f860 LD |
671 | alloc_fail: |
672 | of_reserved_mem_device_release(dev); | |
673 | ||
674 | return ret; | |
675 | } | |
676 | ||
677 | static void malidp_unbind(struct device *dev) | |
678 | { | |
679 | struct drm_device *drm = dev_get_drvdata(dev); | |
680 | struct malidp_drm *malidp = drm->dev_private; | |
ad49f860 | 681 | |
90731c24 | 682 | drm_dev_unregister(drm); |
ad49f860 LD |
683 | if (malidp->fbdev) { |
684 | drm_fbdev_cma_fini(malidp->fbdev); | |
685 | malidp->fbdev = NULL; | |
686 | } | |
687 | drm_kms_helper_poll_fini(drm); | |
85f64218 | 688 | pm_runtime_get_sync(dev); |
ad49f860 LD |
689 | malidp_se_irq_fini(drm); |
690 | malidp_de_irq_fini(drm); | |
ad49f860 | 691 | component_unbind_all(dev, drm); |
3c31760e BS |
692 | of_node_put(malidp->crtc.port); |
693 | malidp->crtc.port = NULL; | |
50c7512f | 694 | malidp_fini_sysfs(dev); |
de9c4810 | 695 | malidp_fini(drm); |
85f64218 LD |
696 | pm_runtime_put(dev); |
697 | if (pm_runtime_enabled(dev)) | |
698 | pm_runtime_disable(dev); | |
699 | else | |
700 | malidp_runtime_pm_suspend(dev); | |
ad49f860 LD |
701 | drm->dev_private = NULL; |
702 | dev_set_drvdata(dev, NULL); | |
0970d7a2 | 703 | drm_dev_put(drm); |
ad49f860 LD |
704 | of_reserved_mem_device_release(dev); |
705 | } | |
706 | ||
707 | static const struct component_master_ops malidp_master_ops = { | |
708 | .bind = malidp_bind, | |
709 | .unbind = malidp_unbind, | |
710 | }; | |
711 | ||
712 | static int malidp_compare_dev(struct device *dev, void *data) | |
713 | { | |
714 | struct device_node *np = data; | |
715 | ||
716 | return dev->of_node == np; | |
717 | } | |
718 | ||
719 | static int malidp_platform_probe(struct platform_device *pdev) | |
720 | { | |
86418f90 | 721 | struct device_node *port; |
ad49f860 LD |
722 | struct component_match *match = NULL; |
723 | ||
724 | if (!pdev->dev.of_node) | |
725 | return -ENODEV; | |
726 | ||
727 | /* there is only one output port inside each device, find it */ | |
86418f90 RH |
728 | port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0); |
729 | if (!port) | |
ad49f860 | 730 | return -ENODEV; |
ad49f860 | 731 | |
97ac0e47 RK |
732 | drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev, |
733 | port); | |
734 | of_node_put(port); | |
ad49f860 LD |
735 | return component_master_add_with_match(&pdev->dev, &malidp_master_ops, |
736 | match); | |
737 | } | |
738 | ||
739 | static int malidp_platform_remove(struct platform_device *pdev) | |
740 | { | |
741 | component_master_del(&pdev->dev, &malidp_master_ops); | |
742 | return 0; | |
743 | } | |
744 | ||
85f64218 LD |
745 | static int __maybe_unused malidp_pm_suspend(struct device *dev) |
746 | { | |
747 | struct drm_device *drm = dev_get_drvdata(dev); | |
748 | struct malidp_drm *malidp = drm->dev_private; | |
749 | ||
750 | drm_kms_helper_poll_disable(drm); | |
751 | console_lock(); | |
752 | drm_fbdev_cma_set_suspend(malidp->fbdev, 1); | |
753 | console_unlock(); | |
754 | malidp->pm_state = drm_atomic_helper_suspend(drm); | |
755 | if (IS_ERR(malidp->pm_state)) { | |
756 | console_lock(); | |
757 | drm_fbdev_cma_set_suspend(malidp->fbdev, 0); | |
758 | console_unlock(); | |
759 | drm_kms_helper_poll_enable(drm); | |
760 | return PTR_ERR(malidp->pm_state); | |
761 | } | |
762 | ||
763 | return 0; | |
764 | } | |
765 | ||
766 | static int __maybe_unused malidp_pm_resume(struct device *dev) | |
767 | { | |
768 | struct drm_device *drm = dev_get_drvdata(dev); | |
769 | struct malidp_drm *malidp = drm->dev_private; | |
770 | ||
771 | drm_atomic_helper_resume(drm, malidp->pm_state); | |
772 | console_lock(); | |
773 | drm_fbdev_cma_set_suspend(malidp->fbdev, 0); | |
774 | console_unlock(); | |
775 | drm_kms_helper_poll_enable(drm); | |
776 | ||
777 | return 0; | |
778 | } | |
779 | ||
780 | static const struct dev_pm_ops malidp_pm_ops = { | |
781 | SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ | |
782 | SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) | |
783 | }; | |
784 | ||
ad49f860 LD |
785 | static struct platform_driver malidp_platform_driver = { |
786 | .probe = malidp_platform_probe, | |
787 | .remove = malidp_platform_remove, | |
788 | .driver = { | |
789 | .name = "mali-dp", | |
85f64218 | 790 | .pm = &malidp_pm_ops, |
ad49f860 LD |
791 | .of_match_table = malidp_drm_of_match, |
792 | }, | |
793 | }; | |
794 | ||
795 | module_platform_driver(malidp_platform_driver); | |
796 | ||
797 | MODULE_AUTHOR("Liviu Dudau <Liviu.Dudau@arm.com>"); | |
798 | MODULE_DESCRIPTION("ARM Mali DP DRM driver"); | |
799 | MODULE_LICENSE("GPL v2"); |