]>
Commit | Line | Data |
---|---|---|
1a396789 BB |
1 | /* |
2 | * Copyright (C) 2014 Traphandler | |
3 | * Copyright (C) 2014 Free Electrons | |
4 | * | |
5 | * Author: Jean-Jacques Hiblot <jjhiblot@traphandler.com> | |
6 | * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License version 2 as published by | |
10 | * the Free Software Foundation. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
13 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
15 | * more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along with | |
18 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
20 | ||
21 | #include <linux/clk.h> | |
22 | #include <linux/pm.h> | |
23 | #include <linux/pm_runtime.h> | |
16e6004e | 24 | #include <linux/pinctrl/consumer.h> |
1a396789 BB |
25 | |
26 | #include <drm/drm_crtc.h> | |
27 | #include <drm/drm_crtc_helper.h> | |
28 | #include <drm/drmP.h> | |
29 | ||
30 | #include <video/videomode.h> | |
31 | ||
32 | #include "atmel_hlcdc_dc.h" | |
33 | ||
aca63b76 BB |
34 | /** |
35 | * Atmel HLCDC CRTC state structure | |
36 | * | |
37 | * @base: base CRTC state | |
38 | * @output_mode: RGBXXX output mode | |
39 | */ | |
40 | struct atmel_hlcdc_crtc_state { | |
41 | struct drm_crtc_state base; | |
42 | unsigned int output_mode; | |
43 | }; | |
44 | ||
45 | static inline struct atmel_hlcdc_crtc_state * | |
46 | drm_crtc_state_to_atmel_hlcdc_crtc_state(struct drm_crtc_state *state) | |
47 | { | |
48 | return container_of(state, struct atmel_hlcdc_crtc_state, base); | |
49 | } | |
50 | ||
1a396789 BB |
51 | /** |
52 | * Atmel HLCDC CRTC structure | |
53 | * | |
54 | * @base: base DRM CRTC structure | |
55 | * @hlcdc: pointer to the atmel_hlcdc structure provided by the MFD device | |
56 | * @event: pointer to the current page flip event | |
57 | * @id: CRTC id (returned by drm_crtc_index) | |
2389fc13 | 58 | * @enabled: CRTC state |
1a396789 BB |
59 | */ |
60 | struct atmel_hlcdc_crtc { | |
61 | struct drm_crtc base; | |
62 | struct atmel_hlcdc_dc *dc; | |
63 | struct drm_pending_vblank_event *event; | |
64 | int id; | |
2389fc13 | 65 | bool enabled; |
1a396789 BB |
66 | }; |
67 | ||
68 | static inline struct atmel_hlcdc_crtc * | |
69 | drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc) | |
70 | { | |
71 | return container_of(crtc, struct atmel_hlcdc_crtc, base); | |
72 | } | |
73 | ||
2389fc13 | 74 | static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c) |
1a396789 | 75 | { |
1a396789 BB |
76 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); |
77 | struct regmap *regmap = crtc->dc->hlcdc->regmap; | |
2389fc13 | 78 | struct drm_display_mode *adj = &c->state->adjusted_mode; |
aca63b76 | 79 | struct atmel_hlcdc_crtc_state *state; |
1a396789 BB |
80 | unsigned long mode_rate; |
81 | struct videomode vm; | |
82 | unsigned long prate; | |
83 | unsigned int cfg; | |
84 | int div; | |
85 | ||
1a396789 BB |
86 | vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay; |
87 | vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end; | |
88 | vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start; | |
89 | vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay; | |
90 | vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end; | |
91 | vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start; | |
92 | ||
93 | regmap_write(regmap, ATMEL_HLCDC_CFG(1), | |
94 | (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16)); | |
95 | ||
96 | regmap_write(regmap, ATMEL_HLCDC_CFG(2), | |
97 | (vm.vfront_porch - 1) | (vm.vback_porch << 16)); | |
98 | ||
99 | regmap_write(regmap, ATMEL_HLCDC_CFG(3), | |
100 | (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16)); | |
101 | ||
102 | regmap_write(regmap, ATMEL_HLCDC_CFG(4), | |
103 | (adj->crtc_hdisplay - 1) | | |
104 | ((adj->crtc_vdisplay - 1) << 16)); | |
105 | ||
0bb59cb0 | 106 | cfg = 0; |
1a396789 BB |
107 | |
108 | prate = clk_get_rate(crtc->dc->hlcdc->sys_clk); | |
2389fc13 | 109 | mode_rate = adj->crtc_clock * 1000; |
1a396789 BB |
110 | if ((prate / 2) < mode_rate) { |
111 | prate *= 2; | |
112 | cfg |= ATMEL_HLCDC_CLKSEL; | |
113 | } | |
114 | ||
115 | div = DIV_ROUND_UP(prate, mode_rate); | |
116 | if (div < 2) | |
117 | div = 2; | |
118 | ||
119 | cfg |= ATMEL_HLCDC_CLKDIV(div); | |
120 | ||
121 | regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), | |
122 | ATMEL_HLCDC_CLKSEL | ATMEL_HLCDC_CLKDIV_MASK | | |
123 | ATMEL_HLCDC_CLKPOL, cfg); | |
124 | ||
125 | cfg = 0; | |
126 | ||
2389fc13 | 127 | if (adj->flags & DRM_MODE_FLAG_NVSYNC) |
1a396789 BB |
128 | cfg |= ATMEL_HLCDC_VSPOL; |
129 | ||
2389fc13 | 130 | if (adj->flags & DRM_MODE_FLAG_NHSYNC) |
1a396789 BB |
131 | cfg |= ATMEL_HLCDC_HSPOL; |
132 | ||
aca63b76 BB |
133 | state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state); |
134 | cfg |= state->output_mode << 8; | |
135 | ||
1a396789 BB |
136 | regmap_update_bits(regmap, ATMEL_HLCDC_CFG(5), |
137 | ATMEL_HLCDC_HSPOL | ATMEL_HLCDC_VSPOL | | |
138 | ATMEL_HLCDC_VSPDLYS | ATMEL_HLCDC_VSPDLYE | | |
139 | ATMEL_HLCDC_DISPPOL | ATMEL_HLCDC_DISPDLY | | |
140 | ATMEL_HLCDC_VSPSU | ATMEL_HLCDC_VSPHO | | |
aca63b76 | 141 | ATMEL_HLCDC_GUARDTIME_MASK | ATMEL_HLCDC_MODE_MASK, |
1a396789 | 142 | cfg); |
1a396789 BB |
143 | } |
144 | ||
5ac44c8b BB |
145 | static bool atmel_hlcdc_crtc_mode_fixup(struct drm_crtc *c, |
146 | const struct drm_display_mode *mode, | |
147 | struct drm_display_mode *adjusted_mode) | |
148 | { | |
149 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); | |
150 | ||
151 | return atmel_hlcdc_dc_mode_valid(crtc->dc, adjusted_mode) == MODE_OK; | |
152 | } | |
153 | ||
2389fc13 | 154 | static void atmel_hlcdc_crtc_disable(struct drm_crtc *c) |
1a396789 | 155 | { |
2389fc13 BB |
156 | struct drm_device *dev = c->dev; |
157 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); | |
158 | struct regmap *regmap = crtc->dc->hlcdc->regmap; | |
159 | unsigned int status; | |
160 | ||
161 | if (!crtc->enabled) | |
162 | return; | |
163 | ||
164 | drm_crtc_vblank_off(c); | |
165 | ||
166 | pm_runtime_get_sync(dev->dev); | |
167 | ||
168 | regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_DISP); | |
169 | while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && | |
170 | (status & ATMEL_HLCDC_DISP)) | |
171 | cpu_relax(); | |
172 | ||
173 | regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_SYNC); | |
174 | while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && | |
175 | (status & ATMEL_HLCDC_SYNC)) | |
176 | cpu_relax(); | |
177 | ||
178 | regmap_write(regmap, ATMEL_HLCDC_DIS, ATMEL_HLCDC_PIXEL_CLK); | |
179 | while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && | |
180 | (status & ATMEL_HLCDC_PIXEL_CLK)) | |
181 | cpu_relax(); | |
182 | ||
183 | clk_disable_unprepare(crtc->dc->hlcdc->sys_clk); | |
16e6004e | 184 | pinctrl_pm_select_sleep_state(dev->dev); |
2389fc13 BB |
185 | |
186 | pm_runtime_allow(dev->dev); | |
187 | ||
188 | pm_runtime_put_sync(dev->dev); | |
189 | ||
190 | crtc->enabled = false; | |
1a396789 BB |
191 | } |
192 | ||
2389fc13 | 193 | static void atmel_hlcdc_crtc_enable(struct drm_crtc *c) |
1a396789 | 194 | { |
2389fc13 BB |
195 | struct drm_device *dev = c->dev; |
196 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); | |
197 | struct regmap *regmap = crtc->dc->hlcdc->regmap; | |
198 | unsigned int status; | |
199 | ||
200 | if (crtc->enabled) | |
201 | return; | |
202 | ||
203 | pm_runtime_get_sync(dev->dev); | |
204 | ||
205 | pm_runtime_forbid(dev->dev); | |
206 | ||
16e6004e | 207 | pinctrl_pm_select_default_state(dev->dev); |
2389fc13 BB |
208 | clk_prepare_enable(crtc->dc->hlcdc->sys_clk); |
209 | ||
210 | regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK); | |
211 | while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && | |
212 | !(status & ATMEL_HLCDC_PIXEL_CLK)) | |
213 | cpu_relax(); | |
214 | ||
215 | ||
216 | regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_SYNC); | |
217 | while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && | |
218 | !(status & ATMEL_HLCDC_SYNC)) | |
219 | cpu_relax(); | |
220 | ||
221 | regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_DISP); | |
222 | while (!regmap_read(regmap, ATMEL_HLCDC_SR, &status) && | |
223 | !(status & ATMEL_HLCDC_DISP)) | |
224 | cpu_relax(); | |
225 | ||
226 | pm_runtime_put_sync(dev->dev); | |
227 | ||
228 | drm_crtc_vblank_on(c); | |
229 | ||
230 | crtc->enabled = true; | |
1a396789 BB |
231 | } |
232 | ||
f026eb6e SR |
233 | void atmel_hlcdc_crtc_suspend(struct drm_crtc *c) |
234 | { | |
235 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); | |
236 | ||
237 | if (crtc->enabled) { | |
238 | atmel_hlcdc_crtc_disable(c); | |
239 | /* save enable state for resume */ | |
240 | crtc->enabled = true; | |
241 | } | |
242 | } | |
243 | ||
244 | void atmel_hlcdc_crtc_resume(struct drm_crtc *c) | |
245 | { | |
246 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); | |
247 | ||
248 | if (crtc->enabled) { | |
249 | crtc->enabled = false; | |
250 | atmel_hlcdc_crtc_enable(c); | |
251 | } | |
252 | } | |
253 | ||
aca63b76 BB |
254 | #define ATMEL_HLCDC_RGB444_OUTPUT BIT(0) |
255 | #define ATMEL_HLCDC_RGB565_OUTPUT BIT(1) | |
256 | #define ATMEL_HLCDC_RGB666_OUTPUT BIT(2) | |
257 | #define ATMEL_HLCDC_RGB888_OUTPUT BIT(3) | |
258 | #define ATMEL_HLCDC_OUTPUT_MODE_MASK GENMASK(3, 0) | |
259 | ||
260 | static int atmel_hlcdc_crtc_select_output_mode(struct drm_crtc_state *state) | |
261 | { | |
262 | unsigned int output_fmts = ATMEL_HLCDC_OUTPUT_MODE_MASK; | |
263 | struct atmel_hlcdc_crtc_state *hstate; | |
264 | struct drm_connector_state *cstate; | |
265 | struct drm_connector *connector; | |
266 | struct atmel_hlcdc_crtc *crtc; | |
267 | int i; | |
268 | ||
269 | crtc = drm_crtc_to_atmel_hlcdc_crtc(state->crtc); | |
270 | ||
271 | for_each_connector_in_state(state->state, connector, cstate, i) { | |
272 | struct drm_display_info *info = &connector->display_info; | |
273 | unsigned int supported_fmts = 0; | |
274 | int j; | |
275 | ||
276 | if (!cstate->crtc) | |
277 | continue; | |
278 | ||
279 | for (j = 0; j < info->num_bus_formats; j++) { | |
280 | switch (info->bus_formats[j]) { | |
281 | case MEDIA_BUS_FMT_RGB444_1X12: | |
282 | supported_fmts |= ATMEL_HLCDC_RGB444_OUTPUT; | |
283 | break; | |
284 | case MEDIA_BUS_FMT_RGB565_1X16: | |
285 | supported_fmts |= ATMEL_HLCDC_RGB565_OUTPUT; | |
286 | break; | |
287 | case MEDIA_BUS_FMT_RGB666_1X18: | |
288 | supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT; | |
289 | break; | |
290 | case MEDIA_BUS_FMT_RGB888_1X24: | |
291 | supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT; | |
292 | break; | |
293 | default: | |
294 | break; | |
295 | } | |
296 | } | |
297 | ||
298 | if (crtc->dc->desc->conflicting_output_formats) | |
299 | output_fmts &= supported_fmts; | |
300 | else | |
301 | output_fmts |= supported_fmts; | |
302 | } | |
303 | ||
304 | if (!output_fmts) | |
305 | return -EINVAL; | |
306 | ||
307 | hstate = drm_crtc_state_to_atmel_hlcdc_crtc_state(state); | |
308 | hstate->output_mode = fls(output_fmts) - 1; | |
309 | ||
310 | return 0; | |
311 | } | |
312 | ||
2389fc13 BB |
313 | static int atmel_hlcdc_crtc_atomic_check(struct drm_crtc *c, |
314 | struct drm_crtc_state *s) | |
1a396789 | 315 | { |
aca63b76 | 316 | int ret; |
2389fc13 | 317 | |
aca63b76 BB |
318 | ret = atmel_hlcdc_crtc_select_output_mode(s); |
319 | if (ret) | |
320 | return ret; | |
321 | ||
ebab87ab BB |
322 | ret = atmel_hlcdc_plane_prepare_disc_area(s); |
323 | if (ret) | |
324 | return ret; | |
325 | ||
326 | return atmel_hlcdc_plane_prepare_ahb_routing(s); | |
1a396789 BB |
327 | } |
328 | ||
613d2b27 ML |
329 | static void atmel_hlcdc_crtc_atomic_begin(struct drm_crtc *c, |
330 | struct drm_crtc_state *old_s) | |
1a396789 | 331 | { |
2389fc13 | 332 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); |
1a396789 | 333 | |
2389fc13 BB |
334 | if (c->state->event) { |
335 | c->state->event->pipe = drm_crtc_index(c); | |
1a396789 | 336 | |
2389fc13 | 337 | WARN_ON(drm_crtc_vblank_get(c) != 0); |
1a396789 | 338 | |
2389fc13 BB |
339 | crtc->event = c->state->event; |
340 | c->state->event = NULL; | |
1a396789 BB |
341 | } |
342 | } | |
343 | ||
613d2b27 ML |
344 | static void atmel_hlcdc_crtc_atomic_flush(struct drm_crtc *crtc, |
345 | struct drm_crtc_state *old_s) | |
2389fc13 BB |
346 | { |
347 | /* TODO: write common plane control register if available */ | |
348 | } | |
349 | ||
1a396789 | 350 | static const struct drm_crtc_helper_funcs lcdc_crtc_helper_funcs = { |
5ac44c8b | 351 | .mode_fixup = atmel_hlcdc_crtc_mode_fixup, |
2389fc13 BB |
352 | .mode_set = drm_helper_crtc_mode_set, |
353 | .mode_set_nofb = atmel_hlcdc_crtc_mode_set_nofb, | |
354 | .mode_set_base = drm_helper_crtc_mode_set_base, | |
1a396789 | 355 | .disable = atmel_hlcdc_crtc_disable, |
2389fc13 BB |
356 | .enable = atmel_hlcdc_crtc_enable, |
357 | .atomic_check = atmel_hlcdc_crtc_atomic_check, | |
358 | .atomic_begin = atmel_hlcdc_crtc_atomic_begin, | |
359 | .atomic_flush = atmel_hlcdc_crtc_atomic_flush, | |
1a396789 BB |
360 | }; |
361 | ||
362 | static void atmel_hlcdc_crtc_destroy(struct drm_crtc *c) | |
363 | { | |
364 | struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c); | |
365 | ||
366 | drm_crtc_cleanup(c); | |
367 | kfree(crtc); | |
368 | } | |
369 | ||
1a396789 BB |
370 | static void atmel_hlcdc_crtc_finish_page_flip(struct atmel_hlcdc_crtc *crtc) |
371 | { | |
372 | struct drm_device *dev = crtc->base.dev; | |
373 | unsigned long flags; | |
374 | ||
375 | spin_lock_irqsave(&dev->event_lock, flags); | |
376 | if (crtc->event) { | |
81767317 | 377 | drm_crtc_send_vblank_event(&crtc->base, crtc->event); |
23a25ed3 | 378 | drm_crtc_vblank_put(&crtc->base); |
1a396789 BB |
379 | crtc->event = NULL; |
380 | } | |
381 | spin_unlock_irqrestore(&dev->event_lock, flags); | |
382 | } | |
383 | ||
384 | void atmel_hlcdc_crtc_irq(struct drm_crtc *c) | |
385 | { | |
548ebe1e | 386 | drm_crtc_handle_vblank(c); |
1a396789 BB |
387 | atmel_hlcdc_crtc_finish_page_flip(drm_crtc_to_atmel_hlcdc_crtc(c)); |
388 | } | |
389 | ||
1ba7db07 | 390 | static void atmel_hlcdc_crtc_reset(struct drm_crtc *crtc) |
aca63b76 BB |
391 | { |
392 | struct atmel_hlcdc_crtc_state *state; | |
393 | ||
aca63b76 | 394 | if (crtc->state) { |
c2e4c994 | 395 | __drm_atomic_helper_crtc_destroy_state(crtc->state); |
aca63b76 BB |
396 | state = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); |
397 | kfree(state); | |
c2e4c994 | 398 | crtc->state = NULL; |
aca63b76 BB |
399 | } |
400 | ||
401 | state = kzalloc(sizeof(*state), GFP_KERNEL); | |
402 | if (state) { | |
403 | crtc->state = &state->base; | |
404 | crtc->state->crtc = crtc; | |
405 | } | |
406 | } | |
407 | ||
408 | static struct drm_crtc_state * | |
409 | atmel_hlcdc_crtc_duplicate_state(struct drm_crtc *crtc) | |
410 | { | |
411 | struct atmel_hlcdc_crtc_state *state, *cur; | |
412 | ||
413 | if (WARN_ON(!crtc->state)) | |
414 | return NULL; | |
415 | ||
416 | state = kmalloc(sizeof(*state), GFP_KERNEL); | |
58a2ab3a DC |
417 | if (!state) |
418 | return NULL; | |
419 | __drm_atomic_helper_crtc_duplicate_state(crtc, &state->base); | |
aca63b76 BB |
420 | |
421 | cur = drm_crtc_state_to_atmel_hlcdc_crtc_state(crtc->state); | |
422 | state->output_mode = cur->output_mode; | |
423 | ||
424 | return &state->base; | |
425 | } | |
426 | ||
427 | static void atmel_hlcdc_crtc_destroy_state(struct drm_crtc *crtc, | |
428 | struct drm_crtc_state *s) | |
429 | { | |
430 | struct atmel_hlcdc_crtc_state *state; | |
431 | ||
432 | state = drm_crtc_state_to_atmel_hlcdc_crtc_state(s); | |
ec2dc6a0 | 433 | __drm_atomic_helper_crtc_destroy_state(s); |
aca63b76 BB |
434 | kfree(state); |
435 | } | |
436 | ||
1a396789 | 437 | static const struct drm_crtc_funcs atmel_hlcdc_crtc_funcs = { |
2389fc13 BB |
438 | .page_flip = drm_atomic_helper_page_flip, |
439 | .set_config = drm_atomic_helper_set_config, | |
1a396789 | 440 | .destroy = atmel_hlcdc_crtc_destroy, |
aca63b76 BB |
441 | .reset = atmel_hlcdc_crtc_reset, |
442 | .atomic_duplicate_state = atmel_hlcdc_crtc_duplicate_state, | |
443 | .atomic_destroy_state = atmel_hlcdc_crtc_destroy_state, | |
1a396789 BB |
444 | }; |
445 | ||
446 | int atmel_hlcdc_crtc_create(struct drm_device *dev) | |
447 | { | |
448 | struct atmel_hlcdc_dc *dc = dev->dev_private; | |
449 | struct atmel_hlcdc_planes *planes = dc->planes; | |
450 | struct atmel_hlcdc_crtc *crtc; | |
451 | int ret; | |
452 | int i; | |
453 | ||
454 | crtc = kzalloc(sizeof(*crtc), GFP_KERNEL); | |
455 | if (!crtc) | |
456 | return -ENOMEM; | |
457 | ||
1a396789 BB |
458 | crtc->dc = dc; |
459 | ||
460 | ret = drm_crtc_init_with_planes(dev, &crtc->base, | |
461 | &planes->primary->base, | |
462 | planes->cursor ? &planes->cursor->base : NULL, | |
f9882876 | 463 | &atmel_hlcdc_crtc_funcs, NULL); |
1a396789 BB |
464 | if (ret < 0) |
465 | goto fail; | |
466 | ||
467 | crtc->id = drm_crtc_index(&crtc->base); | |
468 | ||
469 | if (planes->cursor) | |
470 | planes->cursor->base.possible_crtcs = 1 << crtc->id; | |
471 | ||
472 | for (i = 0; i < planes->noverlays; i++) | |
473 | planes->overlays[i]->base.possible_crtcs = 1 << crtc->id; | |
474 | ||
475 | drm_crtc_helper_add(&crtc->base, &lcdc_crtc_helper_funcs); | |
8c4b4b0d | 476 | drm_crtc_vblank_reset(&crtc->base); |
1a396789 BB |
477 | |
478 | dc->crtc = &crtc->base; | |
479 | ||
480 | return 0; | |
481 | ||
482 | fail: | |
483 | atmel_hlcdc_crtc_destroy(&crtc->base); | |
484 | return ret; | |
485 | } |