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