]>
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 driver (crtc operations) | |
11 | */ | |
12 | ||
13 | #include <drm/drmP.h> | |
14 | #include <drm/drm_atomic.h> | |
15 | #include <drm/drm_atomic_helper.h> | |
16 | #include <drm/drm_crtc.h> | |
17 | #include <drm/drm_crtc_helper.h> | |
18 | #include <linux/clk.h> | |
19 | #include <video/videomode.h> | |
20 | ||
21 | #include "malidp_drv.h" | |
22 | #include "malidp_hw.h" | |
23 | ||
24 | static bool malidp_crtc_mode_fixup(struct drm_crtc *crtc, | |
25 | const struct drm_display_mode *mode, | |
26 | struct drm_display_mode *adjusted_mode) | |
27 | { | |
28 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
29 | struct malidp_hw_device *hwdev = malidp->dev; | |
30 | ||
31 | /* | |
32 | * check that the hardware can drive the required clock rate, | |
33 | * but skip the check if the clock is meant to be disabled (req_rate = 0) | |
34 | */ | |
35 | long rate, req_rate = mode->crtc_clock * 1000; | |
36 | ||
37 | if (req_rate) { | |
38 | rate = clk_round_rate(hwdev->mclk, req_rate); | |
39 | if (rate < req_rate) { | |
40 | DRM_DEBUG_DRIVER("mclk clock unable to reach %d kHz\n", | |
41 | mode->crtc_clock); | |
42 | return false; | |
43 | } | |
44 | ||
45 | rate = clk_round_rate(hwdev->pxlclk, req_rate); | |
46 | if (rate != req_rate) { | |
47 | DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n", | |
48 | req_rate); | |
49 | return false; | |
50 | } | |
51 | } | |
52 | ||
53 | return true; | |
54 | } | |
55 | ||
56 | static void malidp_crtc_enable(struct drm_crtc *crtc) | |
57 | { | |
58 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
59 | struct malidp_hw_device *hwdev = malidp->dev; | |
60 | struct videomode vm; | |
61 | ||
62 | drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm); | |
63 | ||
64 | clk_prepare_enable(hwdev->pxlclk); | |
65 | ||
9a8b0a23 | 66 | /* We rely on firmware to set mclk to a sensible level. */ |
ad49f860 LD |
67 | clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000); |
68 | ||
69 | hwdev->modeset(hwdev, &vm); | |
70 | hwdev->leave_config_mode(hwdev); | |
71 | drm_crtc_vblank_on(crtc); | |
72 | } | |
73 | ||
74 | static void malidp_crtc_disable(struct drm_crtc *crtc) | |
75 | { | |
76 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
77 | struct malidp_hw_device *hwdev = malidp->dev; | |
78 | ||
79 | drm_crtc_vblank_off(crtc); | |
80 | hwdev->enter_config_mode(hwdev); | |
81 | clk_disable_unprepare(hwdev->pxlclk); | |
82 | } | |
83 | ||
84 | static int malidp_crtc_atomic_check(struct drm_crtc *crtc, | |
85 | struct drm_crtc_state *state) | |
86 | { | |
87 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
88 | struct malidp_hw_device *hwdev = malidp->dev; | |
89 | struct drm_plane *plane; | |
90 | const struct drm_plane_state *pstate; | |
91 | u32 rot_mem_free, rot_mem_usable; | |
92 | int rotated_planes = 0; | |
93 | ||
94 | /* | |
95 | * check if there is enough rotation memory available for planes | |
96 | * that need 90° and 270° rotation. Each plane has set its required | |
97 | * memory size in the ->plane_check() callback, here we only make | |
98 | * sure that the sums are less that the total usable memory. | |
99 | * | |
100 | * The rotation memory allocation algorithm (for each plane): | |
101 | * a. If no more rotated planes exist, all remaining rotate | |
102 | * memory in the bank is available for use by the plane. | |
103 | * b. If other rotated planes exist, and plane's layer ID is | |
104 | * DE_VIDEO1, it can use all the memory from first bank if | |
105 | * secondary rotation memory bank is available, otherwise it can | |
106 | * use up to half the bank's memory. | |
107 | * c. If other rotated planes exist, and plane's layer ID is not | |
108 | * DE_VIDEO1, it can use half of the available memory | |
109 | * | |
110 | * Note: this algorithm assumes that the order in which the planes are | |
111 | * checked always has DE_VIDEO1 plane first in the list if it is | |
112 | * rotated. Because that is how we create the planes in the first | |
113 | * place, under current DRM version things work, but if ever the order | |
114 | * in which drm_atomic_crtc_state_for_each_plane() iterates over planes | |
115 | * changes, we need to pre-sort the planes before validation. | |
116 | */ | |
117 | ||
118 | /* first count the number of rotated planes */ | |
119 | drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { | |
120 | if (pstate->rotation & MALIDP_ROTATED_MASK) | |
121 | rotated_planes++; | |
122 | } | |
123 | ||
124 | rot_mem_free = hwdev->rotation_memory[0]; | |
125 | /* | |
126 | * if we have more than 1 plane using rotation memory, use the second | |
127 | * block of rotation memory as well | |
128 | */ | |
129 | if (rotated_planes > 1) | |
130 | rot_mem_free += hwdev->rotation_memory[1]; | |
131 | ||
132 | /* now validate the rotation memory requirements */ | |
133 | drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { | |
134 | struct malidp_plane *mp = to_malidp_plane(plane); | |
135 | struct malidp_plane_state *ms = to_malidp_plane_state(pstate); | |
136 | ||
137 | if (pstate->rotation & MALIDP_ROTATED_MASK) { | |
138 | /* process current plane */ | |
139 | rotated_planes--; | |
140 | ||
141 | if (!rotated_planes) { | |
142 | /* no more rotated planes, we can use what's left */ | |
143 | rot_mem_usable = rot_mem_free; | |
144 | } else { | |
145 | if ((mp->layer->id != DE_VIDEO1) || | |
146 | (hwdev->rotation_memory[1] == 0)) | |
147 | rot_mem_usable = rot_mem_free / 2; | |
148 | else | |
149 | rot_mem_usable = hwdev->rotation_memory[0]; | |
150 | } | |
151 | ||
152 | rot_mem_free -= rot_mem_usable; | |
153 | ||
154 | if (ms->rotmem_size > rot_mem_usable) | |
155 | return -EINVAL; | |
156 | } | |
157 | } | |
158 | ||
159 | return 0; | |
160 | } | |
161 | ||
162 | static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = { | |
163 | .mode_fixup = malidp_crtc_mode_fixup, | |
164 | .enable = malidp_crtc_enable, | |
165 | .disable = malidp_crtc_disable, | |
166 | .atomic_check = malidp_crtc_atomic_check, | |
167 | }; | |
168 | ||
169 | static const struct drm_crtc_funcs malidp_crtc_funcs = { | |
170 | .destroy = drm_crtc_cleanup, | |
171 | .set_config = drm_atomic_helper_set_config, | |
172 | .page_flip = drm_atomic_helper_page_flip, | |
173 | .reset = drm_atomic_helper_crtc_reset, | |
174 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
175 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
176 | }; | |
177 | ||
178 | int malidp_crtc_init(struct drm_device *drm) | |
179 | { | |
180 | struct malidp_drm *malidp = drm->dev_private; | |
181 | struct drm_plane *primary = NULL, *plane; | |
182 | int ret; | |
183 | ||
184 | ret = malidp_de_planes_init(drm); | |
185 | if (ret < 0) { | |
186 | DRM_ERROR("Failed to initialise planes\n"); | |
187 | return ret; | |
188 | } | |
189 | ||
190 | drm_for_each_plane(plane, drm) { | |
191 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) { | |
192 | primary = plane; | |
193 | break; | |
194 | } | |
195 | } | |
196 | ||
197 | if (!primary) { | |
198 | DRM_ERROR("no primary plane found\n"); | |
199 | ret = -EINVAL; | |
200 | goto crtc_cleanup_planes; | |
201 | } | |
202 | ||
203 | ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL, | |
204 | &malidp_crtc_funcs, NULL); | |
205 | ||
206 | if (!ret) { | |
207 | drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs); | |
208 | return 0; | |
209 | } | |
210 | ||
211 | crtc_cleanup_planes: | |
212 | malidp_de_planes_destroy(drm); | |
213 | ||
214 | return ret; | |
215 | } |