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 | * | |
9 | * Permission is hereby granted, free of charge, to any person obtaining a | |
10 | * copy of this software and associated documentation files (the "Software"), | |
11 | * to deal in the Software without restriction, including without limitation | |
12 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
13 | * and/or sell copies of the Software, and to permit persons to whom the | |
14 | * Software is furnished to do so, subject to the following conditions: | |
15 | * | |
16 | * The above copyright notice and this permission notice (including the next | |
17 | * paragraph) shall be included in all copies or substantial portions of the | |
18 | * Software. | |
19 | * | |
20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
23 | * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
24 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
25 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
26 | * OTHER DEALINGS IN THE SOFTWARE. | |
27 | */ | |
28 | ||
760285e7 DH |
29 | #include <drm/drmP.h> |
30 | #include <drm/drm_crtc_helper.h> | |
1c248b7d ID |
31 | |
32 | #include "exynos_drm_drv.h" | |
1c248b7d | 33 | #include "exynos_drm_encoder.h" |
b5d2eb3b | 34 | #include "exynos_drm_plane.h" |
1c248b7d ID |
35 | |
36 | #define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\ | |
37 | drm_crtc) | |
38 | ||
3b8d1cf8 JS |
39 | enum exynos_crtc_mode { |
40 | CRTC_MODE_NORMAL, /* normal mode */ | |
41 | CRTC_MODE_BLANK, /* The private plane of crtc is blank */ | |
42 | }; | |
43 | ||
1c248b7d ID |
44 | /* |
45 | * Exynos specific crtc structure. | |
46 | * | |
47 | * @drm_crtc: crtc object. | |
b5d2eb3b | 48 | * @drm_plane: pointer of private plane object for this crtc |
1c248b7d ID |
49 | * @pipe: a crtc index created at load() with a new crtc object creation |
50 | * and the crtc object would be set to private->crtc array | |
51 | * to get a crtc object corresponding to this pipe from private->crtc | |
52 | * array when irq interrupt occured. the reason of using this pipe is that | |
53 | * drm framework doesn't support multiple irq yet. | |
54 | * we can refer to the crtc to current hardware interrupt occured through | |
55 | * this pipe value. | |
ec05da95 | 56 | * @dpms: store the crtc dpms value |
3b8d1cf8 | 57 | * @mode: store the crtc mode value |
1c248b7d ID |
58 | */ |
59 | struct exynos_drm_crtc { | |
60 | struct drm_crtc drm_crtc; | |
b5d2eb3b | 61 | struct drm_plane *plane; |
1c248b7d | 62 | unsigned int pipe; |
ec05da95 | 63 | unsigned int dpms; |
3b8d1cf8 | 64 | enum exynos_crtc_mode mode; |
1c248b7d ID |
65 | }; |
66 | ||
1c248b7d ID |
67 | static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode) |
68 | { | |
ec05da95 | 69 | struct drm_device *dev = crtc->dev; |
d2716c89 | 70 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
1c248b7d | 71 | |
d2716c89 JS |
72 | DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode); |
73 | ||
ec05da95 ID |
74 | if (exynos_crtc->dpms == mode) { |
75 | DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n"); | |
76 | return; | |
77 | } | |
78 | ||
79 | mutex_lock(&dev->struct_mutex); | |
80 | ||
cf5188ac JS |
81 | exynos_drm_fn_encoder(crtc, &mode, exynos_drm_encoder_crtc_dpms); |
82 | exynos_crtc->dpms = mode; | |
ec05da95 ID |
83 | |
84 | mutex_unlock(&dev->struct_mutex); | |
1c248b7d ID |
85 | } |
86 | ||
87 | static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) | |
88 | { | |
89 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
90 | ||
91 | /* drm framework doesn't check NULL. */ | |
92 | } | |
93 | ||
94 | static void exynos_drm_crtc_commit(struct drm_crtc *crtc) | |
95 | { | |
d2716c89 JS |
96 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
97 | ||
1c248b7d ID |
98 | DRM_DEBUG_KMS("%s\n", __FILE__); |
99 | ||
4070d212 | 100 | exynos_plane_commit(exynos_crtc->plane); |
cf5188ac | 101 | exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_ON); |
1c248b7d ID |
102 | } |
103 | ||
104 | static bool | |
105 | exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc, | |
e811f5ae | 106 | const struct drm_display_mode *mode, |
1c248b7d ID |
107 | struct drm_display_mode *adjusted_mode) |
108 | { | |
109 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
110 | ||
111 | /* drm framework doesn't check NULL */ | |
112 | return true; | |
113 | } | |
114 | ||
115 | static int | |
116 | exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, | |
117 | struct drm_display_mode *adjusted_mode, int x, int y, | |
118 | struct drm_framebuffer *old_fb) | |
119 | { | |
aeb29224 | 120 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
4070d212 JS |
121 | struct drm_plane *plane = exynos_crtc->plane; |
122 | unsigned int crtc_w; | |
123 | unsigned int crtc_h; | |
d249ce02 | 124 | int pipe = exynos_crtc->pipe; |
aeb29224 JS |
125 | int ret; |
126 | ||
1c248b7d ID |
127 | DRM_DEBUG_KMS("%s\n", __FILE__); |
128 | ||
bebab8ff JS |
129 | exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_ON); |
130 | ||
1de425b0 ID |
131 | /* |
132 | * copy the mode data adjusted by mode_fixup() into crtc->mode | |
133 | * so that hardware can be seet to proper mode. | |
134 | */ | |
135 | memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode)); | |
1c248b7d | 136 | |
4070d212 JS |
137 | crtc_w = crtc->fb->width - x; |
138 | crtc_h = crtc->fb->height - y; | |
139 | ||
140 | ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, | |
141 | x, y, crtc_w, crtc_h); | |
aeb29224 JS |
142 | if (ret) |
143 | return ret; | |
144 | ||
4070d212 JS |
145 | plane->crtc = crtc; |
146 | plane->fb = crtc->fb; | |
147 | ||
d249ce02 | 148 | exynos_drm_fn_encoder(crtc, &pipe, exynos_drm_encoder_crtc_pipe); |
aeb29224 JS |
149 | |
150 | return 0; | |
1c248b7d ID |
151 | } |
152 | ||
153 | static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, | |
154 | struct drm_framebuffer *old_fb) | |
155 | { | |
4070d212 JS |
156 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); |
157 | struct drm_plane *plane = exynos_crtc->plane; | |
158 | unsigned int crtc_w; | |
159 | unsigned int crtc_h; | |
1c248b7d ID |
160 | int ret; |
161 | ||
162 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
163 | ||
4070d212 JS |
164 | crtc_w = crtc->fb->width - x; |
165 | crtc_h = crtc->fb->height - y; | |
166 | ||
167 | ret = exynos_plane_mode_set(plane, crtc, crtc->fb, 0, 0, crtc_w, crtc_h, | |
168 | x, y, crtc_w, crtc_h); | |
1c248b7d ID |
169 | if (ret) |
170 | return ret; | |
171 | ||
bebab8ff | 172 | exynos_drm_crtc_commit(crtc); |
1c248b7d | 173 | |
4070d212 | 174 | return 0; |
1c248b7d ID |
175 | } |
176 | ||
177 | static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc) | |
178 | { | |
179 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
180 | /* drm framework doesn't check NULL */ | |
181 | } | |
182 | ||
a365d9eb JS |
183 | static void exynos_drm_crtc_disable(struct drm_crtc *crtc) |
184 | { | |
185 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
186 | ||
187 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
188 | ||
189 | exynos_plane_dpms(exynos_crtc->plane, DRM_MODE_DPMS_OFF); | |
190 | exynos_drm_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); | |
191 | } | |
192 | ||
1c248b7d ID |
193 | static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = { |
194 | .dpms = exynos_drm_crtc_dpms, | |
195 | .prepare = exynos_drm_crtc_prepare, | |
196 | .commit = exynos_drm_crtc_commit, | |
197 | .mode_fixup = exynos_drm_crtc_mode_fixup, | |
198 | .mode_set = exynos_drm_crtc_mode_set, | |
199 | .mode_set_base = exynos_drm_crtc_mode_set_base, | |
200 | .load_lut = exynos_drm_crtc_load_lut, | |
a365d9eb | 201 | .disable = exynos_drm_crtc_disable, |
1c248b7d ID |
202 | }; |
203 | ||
204 | static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc, | |
205 | struct drm_framebuffer *fb, | |
206 | struct drm_pending_vblank_event *event) | |
207 | { | |
208 | struct drm_device *dev = crtc->dev; | |
209 | struct exynos_drm_private *dev_priv = dev->dev_private; | |
210 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
211 | struct drm_framebuffer *old_fb = crtc->fb; | |
212 | int ret = -EINVAL; | |
213 | ||
214 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
215 | ||
216 | mutex_lock(&dev->struct_mutex); | |
217 | ||
ccf4d883 ID |
218 | if (event) { |
219 | /* | |
220 | * the pipe from user always is 0 so we can set pipe number | |
221 | * of current owner to event. | |
222 | */ | |
223 | event->pipe = exynos_crtc->pipe; | |
224 | ||
1c248b7d ID |
225 | ret = drm_vblank_get(dev, exynos_crtc->pipe); |
226 | if (ret) { | |
227 | DRM_DEBUG("failed to acquire vblank counter\n"); | |
ccf4d883 ID |
228 | list_del(&event->base.link); |
229 | ||
1c248b7d ID |
230 | goto out; |
231 | } | |
232 | ||
c5614ae3 ID |
233 | list_add_tail(&event->base.link, |
234 | &dev_priv->pageflip_event_list); | |
235 | ||
1c248b7d | 236 | crtc->fb = fb; |
4070d212 JS |
237 | ret = exynos_drm_crtc_mode_set_base(crtc, crtc->x, crtc->y, |
238 | NULL); | |
1c248b7d ID |
239 | if (ret) { |
240 | crtc->fb = old_fb; | |
241 | drm_vblank_put(dev, exynos_crtc->pipe); | |
ccf4d883 | 242 | list_del(&event->base.link); |
1c248b7d ID |
243 | |
244 | goto out; | |
245 | } | |
1c248b7d ID |
246 | } |
247 | out: | |
248 | mutex_unlock(&dev->struct_mutex); | |
249 | return ret; | |
250 | } | |
251 | ||
252 | static void exynos_drm_crtc_destroy(struct drm_crtc *crtc) | |
253 | { | |
254 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
255 | struct exynos_drm_private *private = crtc->dev->dev_private; | |
256 | ||
257 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
258 | ||
259 | private->crtc[exynos_crtc->pipe] = NULL; | |
260 | ||
261 | drm_crtc_cleanup(crtc); | |
262 | kfree(exynos_crtc); | |
263 | } | |
264 | ||
3b8d1cf8 JS |
265 | static int exynos_drm_crtc_set_property(struct drm_crtc *crtc, |
266 | struct drm_property *property, | |
267 | uint64_t val) | |
268 | { | |
269 | struct drm_device *dev = crtc->dev; | |
270 | struct exynos_drm_private *dev_priv = dev->dev_private; | |
271 | struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc); | |
272 | ||
273 | DRM_DEBUG_KMS("%s\n", __func__); | |
274 | ||
275 | if (property == dev_priv->crtc_mode_property) { | |
276 | enum exynos_crtc_mode mode = val; | |
277 | ||
278 | if (mode == exynos_crtc->mode) | |
279 | return 0; | |
280 | ||
281 | exynos_crtc->mode = mode; | |
282 | ||
283 | switch (mode) { | |
284 | case CRTC_MODE_NORMAL: | |
285 | exynos_drm_crtc_commit(crtc); | |
286 | break; | |
287 | case CRTC_MODE_BLANK: | |
288 | exynos_plane_dpms(exynos_crtc->plane, | |
289 | DRM_MODE_DPMS_OFF); | |
290 | break; | |
291 | default: | |
292 | break; | |
293 | } | |
294 | ||
295 | return 0; | |
296 | } | |
297 | ||
298 | return -EINVAL; | |
299 | } | |
300 | ||
1c248b7d ID |
301 | static struct drm_crtc_funcs exynos_crtc_funcs = { |
302 | .set_config = drm_crtc_helper_set_config, | |
303 | .page_flip = exynos_drm_crtc_page_flip, | |
304 | .destroy = exynos_drm_crtc_destroy, | |
3b8d1cf8 JS |
305 | .set_property = exynos_drm_crtc_set_property, |
306 | }; | |
307 | ||
308 | static const struct drm_prop_enum_list mode_names[] = { | |
309 | { CRTC_MODE_NORMAL, "normal" }, | |
310 | { CRTC_MODE_BLANK, "blank" }, | |
1c248b7d ID |
311 | }; |
312 | ||
3b8d1cf8 JS |
313 | static void exynos_drm_crtc_attach_mode_property(struct drm_crtc *crtc) |
314 | { | |
315 | struct drm_device *dev = crtc->dev; | |
316 | struct exynos_drm_private *dev_priv = dev->dev_private; | |
317 | struct drm_property *prop; | |
318 | ||
319 | DRM_DEBUG_KMS("%s\n", __func__); | |
320 | ||
321 | prop = dev_priv->crtc_mode_property; | |
322 | if (!prop) { | |
323 | prop = drm_property_create_enum(dev, 0, "mode", mode_names, | |
324 | ARRAY_SIZE(mode_names)); | |
325 | if (!prop) | |
326 | return; | |
327 | ||
328 | dev_priv->crtc_mode_property = prop; | |
329 | } | |
330 | ||
331 | drm_object_attach_property(&crtc->base, prop, 0); | |
332 | } | |
333 | ||
1c248b7d ID |
334 | int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr) |
335 | { | |
336 | struct exynos_drm_crtc *exynos_crtc; | |
337 | struct exynos_drm_private *private = dev->dev_private; | |
338 | struct drm_crtc *crtc; | |
339 | ||
340 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
341 | ||
342 | exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL); | |
343 | if (!exynos_crtc) { | |
344 | DRM_ERROR("failed to allocate exynos crtc\n"); | |
345 | return -ENOMEM; | |
346 | } | |
347 | ||
348 | exynos_crtc->pipe = nr; | |
ec05da95 | 349 | exynos_crtc->dpms = DRM_MODE_DPMS_OFF; |
b5d2eb3b JS |
350 | exynos_crtc->plane = exynos_plane_init(dev, 1 << nr, true); |
351 | if (!exynos_crtc->plane) { | |
352 | kfree(exynos_crtc); | |
353 | return -ENOMEM; | |
354 | } | |
355 | ||
1c248b7d ID |
356 | crtc = &exynos_crtc->drm_crtc; |
357 | ||
358 | private->crtc[nr] = crtc; | |
359 | ||
360 | drm_crtc_init(dev, crtc, &exynos_crtc_funcs); | |
361 | drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs); | |
362 | ||
3b8d1cf8 JS |
363 | exynos_drm_crtc_attach_mode_property(crtc); |
364 | ||
1c248b7d ID |
365 | return 0; |
366 | } | |
367 | ||
368 | int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc) | |
369 | { | |
370 | struct exynos_drm_private *private = dev->dev_private; | |
ec05da95 ID |
371 | struct exynos_drm_crtc *exynos_crtc = |
372 | to_exynos_crtc(private->crtc[crtc]); | |
1c248b7d ID |
373 | |
374 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
375 | ||
ec05da95 ID |
376 | if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) |
377 | return -EPERM; | |
378 | ||
1c248b7d ID |
379 | exynos_drm_fn_encoder(private->crtc[crtc], &crtc, |
380 | exynos_drm_enable_vblank); | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc) | |
386 | { | |
387 | struct exynos_drm_private *private = dev->dev_private; | |
ec05da95 ID |
388 | struct exynos_drm_crtc *exynos_crtc = |
389 | to_exynos_crtc(private->crtc[crtc]); | |
1c248b7d ID |
390 | |
391 | DRM_DEBUG_KMS("%s\n", __FILE__); | |
392 | ||
ec05da95 ID |
393 | if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) |
394 | return; | |
395 | ||
1c248b7d ID |
396 | exynos_drm_fn_encoder(private->crtc[crtc], &crtc, |
397 | exynos_drm_disable_vblank); | |
398 | } |