Commit | Line | Data |
---|---|---|
1c248b7d ID |
1 | /* exynos_drm_crtc.c |
2 | * | |
3 | * Copyright (c) 2011 Samsung Electronics Co., Ltd. | |
4 | * Authors: | |
5 | * Inki Dae <inki.dae@samsung.com> | |
6 | * Joonyoung Shim <jy0922.shim@samsung.com> | |
7 | * Seung-Woo Kim <sw0312.kim@samsung.com> | |
8 | * | |
d81aecb5 ID |
9 | * This program is free software; you can redistribute it and/or modify it |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation; either version 2 of the License, or (at your | |
12 | * option) any later version. | |
1c248b7d ID |
13 | */ |
14 | ||
760285e7 DH |
15 | #include <drm/drmP.h> |
16 | #include <drm/drm_crtc_helper.h> | |
4ea9526b GP |
17 | #include <drm/drm_atomic.h> |
18 | #include <drm/drm_atomic_helper.h> | |
1ca582f1 | 19 | #include <drm/drm_encoder.h> |
1c248b7d | 20 | |
e30655d0 | 21 | #include "exynos_drm_crtc.h" |
1c248b7d | 22 | #include "exynos_drm_drv.h" |
b5d2eb3b | 23 | #include "exynos_drm_plane.h" |
1c248b7d | 24 | |
0b20a0f8 LP |
25 | static void exynos_drm_crtc_atomic_enable(struct drm_crtc *crtc, |
26 | struct drm_crtc_state *old_state) | |
1c248b7d | 27 | { |
d2716c89 | 28 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
1c248b7d | 29 | |
3cecda03 GP |
30 | if (exynos_crtc->ops->enable) |
31 | exynos_crtc->ops->enable(exynos_crtc); | |
080be03d | 32 | |
63498e30 | 33 | drm_crtc_vblank_on(crtc); |
1c248b7d ID |
34 | } |
35 | ||
64581714 LP |
36 | static void exynos_drm_crtc_atomic_disable(struct drm_crtc *crtc, |
37 | struct drm_crtc_state *old_state) | |
3fc4867c | 38 | { |
63498e30 | 39 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
3fc4867c | 40 | |
63498e30 GP |
41 | drm_crtc_vblank_off(crtc); |
42 | ||
3cecda03 GP |
43 | if (exynos_crtc->ops->disable) |
44 | exynos_crtc->ops->disable(exynos_crtc); | |
41cbf0fd ID |
45 | |
46 | if (crtc->state->event && !crtc->state->active) { | |
47 | spin_lock_irq(&crtc->dev->event_lock); | |
48 | drm_crtc_send_vblank_event(crtc, crtc->state->event); | |
49 | spin_unlock_irq(&crtc->dev->event_lock); | |
50 | ||
51 | crtc->state->event = NULL; | |
52 | } | |
3fc4867c GP |
53 | } |
54 | ||
5625b341 AH |
55 | static int exynos_crtc_atomic_check(struct drm_crtc *crtc, |
56 | struct drm_crtc_state *state) | |
57 | { | |
58 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
59 | ||
c4e07407 AH |
60 | if (!state->enable) |
61 | return 0; | |
62 | ||
5625b341 AH |
63 | if (exynos_crtc->ops->atomic_check) |
64 | return exynos_crtc->ops->atomic_check(exynos_crtc, state); | |
65 | ||
66 | return 0; | |
67 | } | |
68 | ||
613d2b27 ML |
69 | static void exynos_crtc_atomic_begin(struct drm_crtc *crtc, |
70 | struct drm_crtc_state *old_crtc_state) | |
9d5ab6a0 GP |
71 | { |
72 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
73 | ||
d29c2c14 MS |
74 | if (exynos_crtc->ops->atomic_begin) |
75 | exynos_crtc->ops->atomic_begin(exynos_crtc); | |
9d5ab6a0 GP |
76 | } |
77 | ||
613d2b27 ML |
78 | static void exynos_crtc_atomic_flush(struct drm_crtc *crtc, |
79 | struct drm_crtc_state *old_crtc_state) | |
9d5ab6a0 | 80 | { |
d9220d47 | 81 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
d9220d47 | 82 | |
d29c2c14 MS |
83 | if (exynos_crtc->ops->atomic_flush) |
84 | exynos_crtc->ops->atomic_flush(exynos_crtc); | |
a392276d AH |
85 | } |
86 | ||
c3653fed AH |
87 | static enum drm_mode_status exynos_crtc_mode_valid(struct drm_crtc *crtc, |
88 | const struct drm_display_mode *mode) | |
89 | { | |
90 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
91 | ||
92 | if (exynos_crtc->ops->mode_valid) | |
93 | return exynos_crtc->ops->mode_valid(exynos_crtc, mode); | |
94 | ||
95 | return MODE_OK; | |
96 | } | |
97 | ||
2466db97 AH |
98 | static bool exynos_crtc_mode_fixup(struct drm_crtc *crtc, |
99 | const struct drm_display_mode *mode, | |
100 | struct drm_display_mode *adjusted_mode) | |
101 | { | |
102 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
103 | ||
104 | if (exynos_crtc->ops->mode_fixup) | |
105 | return exynos_crtc->ops->mode_fixup(exynos_crtc, mode, | |
106 | adjusted_mode); | |
107 | ||
108 | return true; | |
109 | } | |
110 | ||
111 | ||
a392276d | 112 | static const struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { |
c3653fed | 113 | .mode_valid = exynos_crtc_mode_valid, |
2466db97 | 114 | .mode_fixup = exynos_crtc_mode_fixup, |
a392276d AH |
115 | .atomic_check = exynos_crtc_atomic_check, |
116 | .atomic_begin = exynos_crtc_atomic_begin, | |
117 | .atomic_flush = exynos_crtc_atomic_flush, | |
0b20a0f8 | 118 | .atomic_enable = exynos_drm_crtc_atomic_enable, |
64581714 | 119 | .atomic_disable = exynos_drm_crtc_atomic_disable, |
a392276d AH |
120 | }; |
121 | ||
122 | void exynos_crtc_handle_event(struct exynos_drm_crtc *exynos_crtc) | |
123 | { | |
124 | struct drm_crtc *crtc = &exynos_crtc->base; | |
125 | struct drm_pending_vblank_event *event = crtc->state->event; | |
126 | unsigned long flags; | |
9276dff7 | 127 | |
73b7b44f AH |
128 | if (!event) |
129 | return; | |
130 | crtc->state->event = NULL; | |
131 | ||
132 | WARN_ON(drm_crtc_vblank_get(crtc) != 0); | |
9276dff7 | 133 | |
73b7b44f AH |
134 | spin_lock_irqsave(&crtc->dev->event_lock, flags); |
135 | drm_crtc_arm_vblank_event(crtc, event); | |
136 | spin_unlock_irqrestore(&crtc->dev->event_lock, flags); | |
9d5ab6a0 GP |
137 | } |
138 | ||
1c248b7d ID |
139 | static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) |
140 | { | |
141 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
1c248b7d ID |
142 | |
143 | drm_crtc_cleanup(crtc); | |
144 | kfree(exynos_crtc); | |
145 | } | |
146 | ||
64b0e1d6 SG |
147 | static int exynos_drm_crtc_enable_vblank(struct drm_crtc *crtc) |
148 | { | |
149 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
150 | ||
151 | if (exynos_crtc->ops->enable_vblank) | |
152 | return exynos_crtc->ops->enable_vblank(exynos_crtc); | |
153 | ||
154 | return 0; | |
155 | } | |
156 | ||
157 | static void exynos_drm_crtc_disable_vblank(struct drm_crtc *crtc) | |
158 | { | |
159 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
160 | ||
161 | if (exynos_crtc->ops->disable_vblank) | |
162 | exynos_crtc->ops->disable_vblank(exynos_crtc); | |
163 | } | |
164 | ||
800ba2b5 | 165 | static const struct drm_crtc_funcs exynos_crtc_funcs = { |
47a7deff | 166 | .set_config = drm_atomic_helper_set_config, |
9d5ab6a0 | 167 | .page_flip = drm_atomic_helper_page_flip, |
1c248b7d | 168 | .destroy = exynos_drm_crtc_destroy, |
4ea9526b GP |
169 | .reset = drm_atomic_helper_crtc_reset, |
170 | .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, | |
171 | .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, | |
64b0e1d6 SG |
172 | .enable_vblank = exynos_drm_crtc_enable_vblank, |
173 | .disable_vblank = exynos_drm_crtc_disable_vblank, | |
1c248b7d ID |
174 | }; |
175 | ||
93bca243 | 176 | struct exynos_drm_crtc *exynos_drm_crtc_create(struct drm_device *drm_dev, |
f3aaf762 | 177 | struct drm_plane *plane, |
f3aaf762 KK |
178 | enum exynos_drm_output_type type, |
179 | const struct exynos_drm_crtc_ops *ops, | |
180 | void *ctx) | |
1c248b7d ID |
181 | { |
182 | struct exynos_drm_crtc *exynos_crtc; | |
1c248b7d | 183 | struct drm_crtc *crtc; |
72ed6ccd | 184 | int ret; |
1c248b7d | 185 | |
1c248b7d | 186 | exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); |
38bb5253 | 187 | if (!exynos_crtc) |
93bca243 | 188 | return ERR_PTR(-ENOMEM); |
1c248b7d | 189 | |
5d1741ad | 190 | exynos_crtc->type = type; |
93bca243 GP |
191 | exynos_crtc->ops = ops; |
192 | exynos_crtc->ctx = ctx; | |
b5d2eb3b | 193 | |
357193cd | 194 | crtc = &exynos_crtc->base; |
1c248b7d | 195 | |
eb88e422 | 196 | ret = drm_crtc_init_with_planes(drm_dev, crtc, plane, NULL, |
f9882876 | 197 | &exynos_crtc_funcs, NULL); |
72ed6ccd AH |
198 | if (ret < 0) |
199 | goto err_crtc; | |
200 | ||
1c248b7d ID |
201 | drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); |
202 | ||
93bca243 | 203 | return exynos_crtc; |
72ed6ccd AH |
204 | |
205 | err_crtc: | |
206 | plane->funcs->destroy(plane); | |
72ed6ccd | 207 | kfree(exynos_crtc); |
93bca243 | 208 | return ERR_PTR(ret); |
1c248b7d ID |
209 | } |
210 | ||
1ca582f1 | 211 | struct exynos_drm_crtc *exynos_drm_crtc_get_by_type(struct drm_device *drm_dev, |
cf67cc9a | 212 | enum exynos_drm_output_type out_type) |
f37cd5e8 ID |
213 | { |
214 | struct drm_crtc *crtc; | |
215 | ||
d644951c AH |
216 | drm_for_each_crtc(crtc, drm_dev) |
217 | if (to_exynos_crtc(crtc)->type == out_type) | |
1ca582f1 | 218 | return to_exynos_crtc(crtc); |
f37cd5e8 | 219 | |
e9497dc2 | 220 | return ERR_PTR(-ENODEV); |
1ca582f1 AH |
221 | } |
222 | ||
223 | int exynos_drm_set_possible_crtcs(struct drm_encoder *encoder, | |
224 | enum exynos_drm_output_type out_type) | |
225 | { | |
226 | struct exynos_drm_crtc *crtc = exynos_drm_crtc_get_by_type(encoder->dev, | |
227 | out_type); | |
228 | ||
229 | if (IS_ERR(crtc)) | |
230 | return PTR_ERR(crtc); | |
231 | ||
232 | encoder->possible_crtcs = drm_crtc_mask(&crtc->base); | |
233 | ||
234 | return 0; | |
f37cd5e8 | 235 | } |
5595d4d8 YC |
236 | |
237 | void exynos_drm_crtc_te_handler(struct drm_crtc *crtc) | |
238 | { | |
93bca243 | 239 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
5595d4d8 | 240 | |
93bca243 GP |
241 | if (exynos_crtc->ops->te_handler) |
242 | exynos_crtc->ops->te_handler(exynos_crtc); | |
5595d4d8 | 243 | } |