]>
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> | |
85f64218 | 19 | #include <linux/pm_runtime.h> |
ad49f860 LD |
20 | #include <video/videomode.h> |
21 | ||
22 | #include "malidp_drv.h" | |
23 | #include "malidp_hw.h" | |
24 | ||
e2113c03 JA |
25 | static enum drm_mode_status malidp_crtc_mode_valid(struct drm_crtc *crtc, |
26 | const struct drm_display_mode *mode) | |
ad49f860 LD |
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) { | |
ad49f860 LD |
38 | rate = clk_round_rate(hwdev->pxlclk, req_rate); |
39 | if (rate != req_rate) { | |
40 | DRM_DEBUG_DRIVER("pxlclk doesn't support %ld Hz\n", | |
41 | req_rate); | |
e2113c03 | 42 | return MODE_NOCLOCK; |
ad49f860 LD |
43 | } |
44 | } | |
45 | ||
e2113c03 | 46 | return MODE_OK; |
ad49f860 LD |
47 | } |
48 | ||
0b20a0f8 LP |
49 | static void malidp_crtc_atomic_enable(struct drm_crtc *crtc, |
50 | struct drm_crtc_state *old_state) | |
ad49f860 LD |
51 | { |
52 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
53 | struct malidp_hw_device *hwdev = malidp->dev; | |
54 | struct videomode vm; | |
85f64218 | 55 | int err = pm_runtime_get_sync(crtc->dev->dev); |
ad49f860 | 56 | |
85f64218 LD |
57 | if (err < 0) { |
58 | DRM_DEBUG_DRIVER("Failed to enable runtime power management: %d\n", err); | |
59 | return; | |
60 | } | |
ad49f860 | 61 | |
85f64218 | 62 | drm_display_mode_to_videomode(&crtc->state->adjusted_mode, &vm); |
ad49f860 LD |
63 | clk_prepare_enable(hwdev->pxlclk); |
64 | ||
9a8b0a23 | 65 | /* We rely on firmware to set mclk to a sensible level. */ |
ad49f860 LD |
66 | clk_set_rate(hwdev->pxlclk, crtc->state->adjusted_mode.crtc_clock * 1000); |
67 | ||
a6993b21 LD |
68 | hwdev->hw->modeset(hwdev, &vm); |
69 | hwdev->hw->leave_config_mode(hwdev); | |
ad49f860 LD |
70 | drm_crtc_vblank_on(crtc); |
71 | } | |
72 | ||
64581714 LP |
73 | static void malidp_crtc_atomic_disable(struct drm_crtc *crtc, |
74 | struct drm_crtc_state *old_state) | |
ad49f860 LD |
75 | { |
76 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
77 | struct malidp_hw_device *hwdev = malidp->dev; | |
85f64218 | 78 | int err; |
ad49f860 | 79 | |
54243016 LD |
80 | /* always disable planes on the CRTC that is being turned off */ |
81 | drm_atomic_helper_disable_planes_on_crtc(old_state, false); | |
82 | ||
ad49f860 | 83 | drm_crtc_vblank_off(crtc); |
a6993b21 LD |
84 | hwdev->hw->enter_config_mode(hwdev); |
85 | ||
ad49f860 | 86 | clk_disable_unprepare(hwdev->pxlclk); |
85f64218 LD |
87 | |
88 | err = pm_runtime_put(crtc->dev->dev); | |
89 | if (err < 0) { | |
90 | DRM_DEBUG_DRIVER("Failed to disable runtime power management: %d\n", err); | |
91 | } | |
ad49f860 LD |
92 | } |
93 | ||
02725d31 MA |
94 | static const struct gamma_curve_segment { |
95 | u16 start; | |
96 | u16 end; | |
97 | } segments[MALIDP_COEFFTAB_NUM_COEFFS] = { | |
98 | /* sector 0 */ | |
99 | { 0, 0 }, { 1, 1 }, { 2, 2 }, { 3, 3 }, | |
100 | { 4, 4 }, { 5, 5 }, { 6, 6 }, { 7, 7 }, | |
101 | { 8, 8 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, | |
102 | { 12, 12 }, { 13, 13 }, { 14, 14 }, { 15, 15 }, | |
103 | /* sector 1 */ | |
104 | { 16, 19 }, { 20, 23 }, { 24, 27 }, { 28, 31 }, | |
105 | /* sector 2 */ | |
106 | { 32, 39 }, { 40, 47 }, { 48, 55 }, { 56, 63 }, | |
107 | /* sector 3 */ | |
108 | { 64, 79 }, { 80, 95 }, { 96, 111 }, { 112, 127 }, | |
109 | /* sector 4 */ | |
110 | { 128, 159 }, { 160, 191 }, { 192, 223 }, { 224, 255 }, | |
111 | /* sector 5 */ | |
112 | { 256, 319 }, { 320, 383 }, { 384, 447 }, { 448, 511 }, | |
113 | /* sector 6 */ | |
114 | { 512, 639 }, { 640, 767 }, { 768, 895 }, { 896, 1023 }, | |
115 | { 1024, 1151 }, { 1152, 1279 }, { 1280, 1407 }, { 1408, 1535 }, | |
116 | { 1536, 1663 }, { 1664, 1791 }, { 1792, 1919 }, { 1920, 2047 }, | |
117 | { 2048, 2175 }, { 2176, 2303 }, { 2304, 2431 }, { 2432, 2559 }, | |
118 | { 2560, 2687 }, { 2688, 2815 }, { 2816, 2943 }, { 2944, 3071 }, | |
119 | { 3072, 3199 }, { 3200, 3327 }, { 3328, 3455 }, { 3456, 3583 }, | |
120 | { 3584, 3711 }, { 3712, 3839 }, { 3840, 3967 }, { 3968, 4095 }, | |
121 | }; | |
122 | ||
123 | #define DE_COEFTAB_DATA(a, b) ((((a) & 0xfff) << 16) | (((b) & 0xfff))) | |
124 | ||
125 | static void malidp_generate_gamma_table(struct drm_property_blob *lut_blob, | |
126 | u32 coeffs[MALIDP_COEFFTAB_NUM_COEFFS]) | |
127 | { | |
128 | struct drm_color_lut *lut = (struct drm_color_lut *)lut_blob->data; | |
129 | int i; | |
130 | ||
131 | for (i = 0; i < MALIDP_COEFFTAB_NUM_COEFFS; ++i) { | |
132 | u32 a, b, delta_in, out_start, out_end; | |
133 | ||
134 | delta_in = segments[i].end - segments[i].start; | |
135 | /* DP has 12-bit internal precision for its LUTs. */ | |
136 | out_start = drm_color_lut_extract(lut[segments[i].start].green, | |
137 | 12); | |
138 | out_end = drm_color_lut_extract(lut[segments[i].end].green, 12); | |
139 | a = (delta_in == 0) ? 0 : ((out_end - out_start) * 256) / delta_in; | |
140 | b = out_start; | |
141 | coeffs[i] = DE_COEFTAB_DATA(a, b); | |
142 | } | |
143 | } | |
144 | ||
145 | /* | |
146 | * Check if there is a new gamma LUT and if it is of an acceptable size. Also, | |
147 | * reject any LUTs that use distinct red, green, and blue curves. | |
148 | */ | |
149 | static int malidp_crtc_atomic_check_gamma(struct drm_crtc *crtc, | |
150 | struct drm_crtc_state *state) | |
151 | { | |
152 | struct malidp_crtc_state *mc = to_malidp_crtc_state(state); | |
153 | struct drm_color_lut *lut; | |
154 | size_t lut_size; | |
155 | int i; | |
156 | ||
157 | if (!state->color_mgmt_changed || !state->gamma_lut) | |
158 | return 0; | |
159 | ||
160 | if (crtc->state->gamma_lut && | |
161 | (crtc->state->gamma_lut->base.id == state->gamma_lut->base.id)) | |
162 | return 0; | |
163 | ||
164 | if (state->gamma_lut->length % sizeof(struct drm_color_lut)) | |
165 | return -EINVAL; | |
166 | ||
167 | lut_size = state->gamma_lut->length / sizeof(struct drm_color_lut); | |
168 | if (lut_size != MALIDP_GAMMA_LUT_SIZE) | |
169 | return -EINVAL; | |
170 | ||
171 | lut = (struct drm_color_lut *)state->gamma_lut->data; | |
172 | for (i = 0; i < lut_size; ++i) | |
173 | if (!((lut[i].red == lut[i].green) && | |
174 | (lut[i].red == lut[i].blue))) | |
175 | return -EINVAL; | |
176 | ||
177 | if (!state->mode_changed) { | |
178 | int ret; | |
179 | ||
180 | state->mode_changed = true; | |
181 | /* | |
182 | * Kerneldoc for drm_atomic_helper_check_modeset mandates that | |
183 | * it be invoked when the driver sets ->mode_changed. Since | |
184 | * changing the gamma LUT doesn't depend on any external | |
185 | * resources, it is safe to call it only once. | |
186 | */ | |
187 | ret = drm_atomic_helper_check_modeset(crtc->dev, state->state); | |
188 | if (ret) | |
189 | return ret; | |
190 | } | |
191 | ||
192 | malidp_generate_gamma_table(state->gamma_lut, mc->gamma_coeffs); | |
193 | return 0; | |
194 | } | |
195 | ||
6954f245 MA |
196 | /* |
197 | * Check if there is a new CTM and if it contains valid input. Valid here means | |
198 | * that the number is inside the representable range for a Q3.12 number, | |
199 | * excluding truncating the fractional part of the input data. | |
200 | * | |
201 | * The COLORADJ registers can be changed atomically. | |
202 | */ | |
203 | static int malidp_crtc_atomic_check_ctm(struct drm_crtc *crtc, | |
204 | struct drm_crtc_state *state) | |
205 | { | |
206 | struct malidp_crtc_state *mc = to_malidp_crtc_state(state); | |
207 | struct drm_color_ctm *ctm; | |
208 | int i; | |
209 | ||
210 | if (!state->color_mgmt_changed) | |
211 | return 0; | |
212 | ||
213 | if (!state->ctm) | |
214 | return 0; | |
215 | ||
216 | if (crtc->state->ctm && (crtc->state->ctm->base.id == | |
217 | state->ctm->base.id)) | |
218 | return 0; | |
219 | ||
220 | /* | |
221 | * The size of the ctm is checked in | |
222 | * drm_atomic_replace_property_blob_from_id. | |
223 | */ | |
224 | ctm = (struct drm_color_ctm *)state->ctm->data; | |
225 | for (i = 0; i < ARRAY_SIZE(ctm->matrix); ++i) { | |
226 | /* Convert from S31.32 to Q3.12. */ | |
227 | s64 val = ctm->matrix[i]; | |
228 | u32 mag = ((((u64)val) & ~BIT_ULL(63)) >> 20) & | |
229 | GENMASK_ULL(14, 0); | |
230 | ||
231 | /* | |
232 | * Convert to 2s complement and check the destination's top bit | |
233 | * for overflow. NB: Can't check before converting or it'd | |
234 | * incorrectly reject the case: | |
235 | * sign == 1 | |
236 | * mag == 0x2000 | |
237 | */ | |
238 | if (val & BIT_ULL(63)) | |
239 | mag = ~mag + 1; | |
240 | if (!!(val & BIT_ULL(63)) != !!(mag & BIT(14))) | |
241 | return -EINVAL; | |
242 | mc->coloradj_coeffs[i] = mag; | |
243 | } | |
244 | ||
245 | return 0; | |
246 | } | |
247 | ||
28ce675b MA |
248 | static int malidp_crtc_atomic_check_scaling(struct drm_crtc *crtc, |
249 | struct drm_crtc_state *state) | |
250 | { | |
c2e7f82d MA |
251 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); |
252 | struct malidp_hw_device *hwdev = malidp->dev; | |
28ce675b MA |
253 | struct malidp_crtc_state *cs = to_malidp_crtc_state(state); |
254 | struct malidp_se_config *s = &cs->scaler_config; | |
255 | struct drm_plane *plane; | |
c2e7f82d | 256 | struct videomode vm; |
28ce675b MA |
257 | const struct drm_plane_state *pstate; |
258 | u32 h_upscale_factor = 0; /* U16.16 */ | |
259 | u32 v_upscale_factor = 0; /* U16.16 */ | |
260 | u8 scaling = cs->scaled_planes_mask; | |
c2e7f82d | 261 | int ret; |
28ce675b MA |
262 | |
263 | if (!scaling) { | |
264 | s->scale_enable = false; | |
c2e7f82d | 265 | goto mclk_calc; |
28ce675b MA |
266 | } |
267 | ||
268 | /* The scaling engine can only handle one plane at a time. */ | |
269 | if (scaling & (scaling - 1)) | |
270 | return -EINVAL; | |
271 | ||
272 | drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { | |
273 | struct malidp_plane *mp = to_malidp_plane(plane); | |
28ce675b MA |
274 | u32 phase; |
275 | ||
276 | if (!(mp->layer->id & scaling)) | |
277 | continue; | |
278 | ||
279 | /* | |
280 | * Convert crtc_[w|h] to U32.32, then divide by U16.16 src_[w|h] | |
281 | * to get the U16.16 result. | |
282 | */ | |
763656d3 AB |
283 | h_upscale_factor = div_u64((u64)pstate->crtc_w << 32, |
284 | pstate->src_w); | |
285 | v_upscale_factor = div_u64((u64)pstate->crtc_h << 32, | |
286 | pstate->src_h); | |
28ce675b | 287 | |
0274e6a0 MA |
288 | s->enhancer_enable = ((h_upscale_factor >> 16) >= 2 || |
289 | (v_upscale_factor >> 16) >= 2); | |
290 | ||
28ce675b MA |
291 | s->input_w = pstate->src_w >> 16; |
292 | s->input_h = pstate->src_h >> 16; | |
293 | s->output_w = pstate->crtc_w; | |
294 | s->output_h = pstate->crtc_h; | |
295 | ||
296 | #define SE_N_PHASE 4 | |
297 | #define SE_SHIFT_N_PHASE 12 | |
298 | /* Calculate initial_phase and delta_phase for horizontal. */ | |
299 | phase = s->input_w; | |
300 | s->h_init_phase = | |
301 | ((phase << SE_N_PHASE) / s->output_w + 1) / 2; | |
302 | ||
303 | phase = s->input_w; | |
304 | phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE); | |
305 | s->h_delta_phase = phase / s->output_w; | |
306 | ||
307 | /* Same for vertical. */ | |
308 | phase = s->input_h; | |
309 | s->v_init_phase = | |
310 | ((phase << SE_N_PHASE) / s->output_h + 1) / 2; | |
311 | ||
312 | phase = s->input_h; | |
313 | phase <<= (SE_SHIFT_N_PHASE + SE_N_PHASE); | |
314 | s->v_delta_phase = phase / s->output_h; | |
315 | #undef SE_N_PHASE | |
316 | #undef SE_SHIFT_N_PHASE | |
317 | s->plane_src_id = mp->layer->id; | |
318 | } | |
319 | ||
320 | s->scale_enable = true; | |
321 | s->hcoeff = malidp_se_select_coeffs(h_upscale_factor); | |
322 | s->vcoeff = malidp_se_select_coeffs(v_upscale_factor); | |
c2e7f82d MA |
323 | |
324 | mclk_calc: | |
325 | drm_display_mode_to_videomode(&state->adjusted_mode, &vm); | |
a6993b21 | 326 | ret = hwdev->hw->se_calc_mclk(hwdev, s, &vm); |
c2e7f82d MA |
327 | if (ret < 0) |
328 | return -EINVAL; | |
28ce675b MA |
329 | return 0; |
330 | } | |
331 | ||
ad49f860 LD |
332 | static int malidp_crtc_atomic_check(struct drm_crtc *crtc, |
333 | struct drm_crtc_state *state) | |
334 | { | |
335 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
336 | struct malidp_hw_device *hwdev = malidp->dev; | |
337 | struct drm_plane *plane; | |
338 | const struct drm_plane_state *pstate; | |
339 | u32 rot_mem_free, rot_mem_usable; | |
340 | int rotated_planes = 0; | |
6954f245 | 341 | int ret; |
ad49f860 LD |
342 | |
343 | /* | |
344 | * check if there is enough rotation memory available for planes | |
345 | * that need 90° and 270° rotation. Each plane has set its required | |
346 | * memory size in the ->plane_check() callback, here we only make | |
347 | * sure that the sums are less that the total usable memory. | |
348 | * | |
349 | * The rotation memory allocation algorithm (for each plane): | |
350 | * a. If no more rotated planes exist, all remaining rotate | |
351 | * memory in the bank is available for use by the plane. | |
352 | * b. If other rotated planes exist, and plane's layer ID is | |
353 | * DE_VIDEO1, it can use all the memory from first bank if | |
354 | * secondary rotation memory bank is available, otherwise it can | |
355 | * use up to half the bank's memory. | |
356 | * c. If other rotated planes exist, and plane's layer ID is not | |
357 | * DE_VIDEO1, it can use half of the available memory | |
358 | * | |
359 | * Note: this algorithm assumes that the order in which the planes are | |
360 | * checked always has DE_VIDEO1 plane first in the list if it is | |
361 | * rotated. Because that is how we create the planes in the first | |
362 | * place, under current DRM version things work, but if ever the order | |
363 | * in which drm_atomic_crtc_state_for_each_plane() iterates over planes | |
364 | * changes, we need to pre-sort the planes before validation. | |
365 | */ | |
366 | ||
367 | /* first count the number of rotated planes */ | |
368 | drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { | |
369 | if (pstate->rotation & MALIDP_ROTATED_MASK) | |
370 | rotated_planes++; | |
371 | } | |
372 | ||
373 | rot_mem_free = hwdev->rotation_memory[0]; | |
374 | /* | |
375 | * if we have more than 1 plane using rotation memory, use the second | |
376 | * block of rotation memory as well | |
377 | */ | |
378 | if (rotated_planes > 1) | |
379 | rot_mem_free += hwdev->rotation_memory[1]; | |
380 | ||
381 | /* now validate the rotation memory requirements */ | |
382 | drm_atomic_crtc_state_for_each_plane_state(plane, pstate, state) { | |
383 | struct malidp_plane *mp = to_malidp_plane(plane); | |
384 | struct malidp_plane_state *ms = to_malidp_plane_state(pstate); | |
385 | ||
386 | if (pstate->rotation & MALIDP_ROTATED_MASK) { | |
387 | /* process current plane */ | |
388 | rotated_planes--; | |
389 | ||
390 | if (!rotated_planes) { | |
391 | /* no more rotated planes, we can use what's left */ | |
392 | rot_mem_usable = rot_mem_free; | |
393 | } else { | |
394 | if ((mp->layer->id != DE_VIDEO1) || | |
395 | (hwdev->rotation_memory[1] == 0)) | |
396 | rot_mem_usable = rot_mem_free / 2; | |
397 | else | |
398 | rot_mem_usable = hwdev->rotation_memory[0]; | |
399 | } | |
400 | ||
401 | rot_mem_free -= rot_mem_usable; | |
402 | ||
403 | if (ms->rotmem_size > rot_mem_usable) | |
404 | return -EINVAL; | |
405 | } | |
406 | } | |
407 | ||
6954f245 MA |
408 | ret = malidp_crtc_atomic_check_gamma(crtc, state); |
409 | ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state); | |
28ce675b | 410 | ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state); |
6954f245 MA |
411 | |
412 | return ret; | |
ad49f860 LD |
413 | } |
414 | ||
415 | static const struct drm_crtc_helper_funcs malidp_crtc_helper_funcs = { | |
e2113c03 | 416 | .mode_valid = malidp_crtc_mode_valid, |
ad49f860 | 417 | .atomic_check = malidp_crtc_atomic_check, |
0b20a0f8 | 418 | .atomic_enable = malidp_crtc_atomic_enable, |
64581714 | 419 | .atomic_disable = malidp_crtc_atomic_disable, |
ad49f860 LD |
420 | }; |
421 | ||
99665d07 MA |
422 | static struct drm_crtc_state *malidp_crtc_duplicate_state(struct drm_crtc *crtc) |
423 | { | |
02725d31 | 424 | struct malidp_crtc_state *state, *old_state; |
99665d07 MA |
425 | |
426 | if (WARN_ON(!crtc->state)) | |
427 | return NULL; | |
428 | ||
02725d31 | 429 | old_state = to_malidp_crtc_state(crtc->state); |
99665d07 MA |
430 | state = kmalloc(sizeof(*state), GFP_KERNEL); |
431 | if (!state) | |
432 | return NULL; | |
433 | ||
434 | __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); | |
02725d31 MA |
435 | memcpy(state->gamma_coeffs, old_state->gamma_coeffs, |
436 | sizeof(state->gamma_coeffs)); | |
6954f245 MA |
437 | memcpy(state->coloradj_coeffs, old_state->coloradj_coeffs, |
438 | sizeof(state->coloradj_coeffs)); | |
28ce675b MA |
439 | memcpy(&state->scaler_config, &old_state->scaler_config, |
440 | sizeof(state->scaler_config)); | |
441 | state->scaled_planes_mask = 0; | |
99665d07 MA |
442 | |
443 | return &state->base; | |
444 | } | |
445 | ||
446 | static void malidp_crtc_reset(struct drm_crtc *crtc) | |
447 | { | |
448 | struct malidp_crtc_state *state = NULL; | |
449 | ||
450 | if (crtc->state) { | |
451 | state = to_malidp_crtc_state(crtc->state); | |
452 | __drm_atomic_helper_crtc_destroy_state(crtc->state); | |
453 | } | |
454 | ||
455 | kfree(state); | |
456 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
457 | if (state) { | |
458 | crtc->state = &state->base; | |
459 | crtc->state->crtc = crtc; | |
460 | } | |
461 | } | |
462 | ||
463 | static void malidp_crtc_destroy_state(struct drm_crtc *crtc, | |
464 | struct drm_crtc_state *state) | |
465 | { | |
466 | struct malidp_crtc_state *mali_state = NULL; | |
467 | ||
468 | if (state) { | |
469 | mali_state = to_malidp_crtc_state(state); | |
470 | __drm_atomic_helper_crtc_destroy_state(state); | |
471 | } | |
472 | ||
473 | kfree(mali_state); | |
474 | } | |
475 | ||
d7ae94be SG |
476 | static int malidp_crtc_enable_vblank(struct drm_crtc *crtc) |
477 | { | |
478 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
479 | struct malidp_hw_device *hwdev = malidp->dev; | |
480 | ||
481 | malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK, | |
a6993b21 | 482 | hwdev->hw->map.de_irq_map.vsync_irq); |
d7ae94be SG |
483 | return 0; |
484 | } | |
485 | ||
486 | static void malidp_crtc_disable_vblank(struct drm_crtc *crtc) | |
487 | { | |
488 | struct malidp_drm *malidp = crtc_to_malidp_device(crtc); | |
489 | struct malidp_hw_device *hwdev = malidp->dev; | |
490 | ||
491 | malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, | |
a6993b21 | 492 | hwdev->hw->map.de_irq_map.vsync_irq); |
d7ae94be SG |
493 | } |
494 | ||
ad49f860 | 495 | static const struct drm_crtc_funcs malidp_crtc_funcs = { |
02725d31 | 496 | .gamma_set = drm_atomic_helper_legacy_gamma_set, |
ad49f860 LD |
497 | .destroy = drm_crtc_cleanup, |
498 | .set_config = drm_atomic_helper_set_config, | |
499 | .page_flip = drm_atomic_helper_page_flip, | |
99665d07 MA |
500 | .reset = malidp_crtc_reset, |
501 | .atomic_duplicate_state = malidp_crtc_duplicate_state, | |
502 | .atomic_destroy_state = malidp_crtc_destroy_state, | |
d7ae94be SG |
503 | .enable_vblank = malidp_crtc_enable_vblank, |
504 | .disable_vblank = malidp_crtc_disable_vblank, | |
ad49f860 LD |
505 | }; |
506 | ||
507 | int malidp_crtc_init(struct drm_device *drm) | |
508 | { | |
509 | struct malidp_drm *malidp = drm->dev_private; | |
510 | struct drm_plane *primary = NULL, *plane; | |
511 | int ret; | |
512 | ||
513 | ret = malidp_de_planes_init(drm); | |
514 | if (ret < 0) { | |
515 | DRM_ERROR("Failed to initialise planes\n"); | |
516 | return ret; | |
517 | } | |
518 | ||
519 | drm_for_each_plane(plane, drm) { | |
520 | if (plane->type == DRM_PLANE_TYPE_PRIMARY) { | |
521 | primary = plane; | |
522 | break; | |
523 | } | |
524 | } | |
525 | ||
526 | if (!primary) { | |
527 | DRM_ERROR("no primary plane found\n"); | |
528 | ret = -EINVAL; | |
529 | goto crtc_cleanup_planes; | |
530 | } | |
531 | ||
532 | ret = drm_crtc_init_with_planes(drm, &malidp->crtc, primary, NULL, | |
533 | &malidp_crtc_funcs, NULL); | |
02725d31 MA |
534 | if (ret) |
535 | goto crtc_cleanup_planes; | |
ad49f860 | 536 | |
02725d31 MA |
537 | drm_crtc_helper_add(&malidp->crtc, &malidp_crtc_helper_funcs); |
538 | drm_mode_crtc_set_gamma_size(&malidp->crtc, MALIDP_GAMMA_LUT_SIZE); | |
0274e6a0 | 539 | /* No inverse-gamma: it is per-plane. */ |
6954f245 | 540 | drm_crtc_enable_color_mgmt(&malidp->crtc, 0, true, MALIDP_GAMMA_LUT_SIZE); |
02725d31 | 541 | |
0274e6a0 MA |
542 | malidp_se_set_enh_coeffs(malidp->dev); |
543 | ||
02725d31 | 544 | return 0; |
ad49f860 LD |
545 | |
546 | crtc_cleanup_planes: | |
547 | malidp_de_planes_destroy(drm); | |
548 | ||
549 | return ret; | |
550 | } |