]>
Commit | Line | Data |
---|---|---|
b39a982d TV |
1 | /* |
2 | * linux/drivers/video/omap2/omapfb-ioctl.c | |
3 | * | |
4 | * Copyright (C) 2008 Nokia Corporation | |
5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | |
6 | * | |
7 | * Some code and ideas taken from drivers/video/omap/ driver | |
8 | * by Imre Deak. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License version 2 as published by | |
12 | * the Free Software Foundation. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | * more details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License along with | |
20 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
21 | */ | |
22 | ||
23 | #include <linux/fb.h> | |
24 | #include <linux/device.h> | |
25 | #include <linux/uaccess.h> | |
26 | #include <linux/platform_device.h> | |
27 | #include <linux/mm.h> | |
28 | #include <linux/omapfb.h> | |
29 | #include <linux/vmalloc.h> | |
a8a35931 | 30 | #include <linux/export.h> |
b39a982d | 31 | |
a0b38cc4 | 32 | #include <video/omapdss.h> |
b39a982d TV |
33 | #include <plat/vrfb.h> |
34 | #include <plat/vram.h> | |
35 | ||
36 | #include "omapfb.h" | |
37 | ||
078ff546 VS |
38 | static u8 get_mem_idx(struct omapfb_info *ofbi) |
39 | { | |
40 | if (ofbi->id == ofbi->region->id) | |
41 | return 0; | |
42 | ||
43 | return OMAPFB_MEM_IDX_ENABLED | ofbi->region->id; | |
44 | } | |
45 | ||
46 | static struct omapfb2_mem_region *get_mem_region(struct omapfb_info *ofbi, | |
47 | u8 mem_idx) | |
48 | { | |
49 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
50 | ||
51 | if (mem_idx & OMAPFB_MEM_IDX_ENABLED) | |
52 | mem_idx &= OMAPFB_MEM_IDX_MASK; | |
53 | else | |
54 | mem_idx = ofbi->id; | |
55 | ||
56 | if (mem_idx >= fbdev->num_fbs) | |
57 | return NULL; | |
58 | ||
3d84b65a | 59 | return &fbdev->regions[mem_idx]; |
078ff546 VS |
60 | } |
61 | ||
b39a982d TV |
62 | static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) |
63 | { | |
64 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
65 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
66 | struct omap_overlay *ovl; | |
078ff546 VS |
67 | struct omap_overlay_info old_info; |
68 | struct omapfb2_mem_region *old_rg, *new_rg; | |
b39a982d TV |
69 | int r = 0; |
70 | ||
71 | DBG("omapfb_setup_plane\n"); | |
72 | ||
b3e68d30 | 73 | if (ofbi->num_overlays == 0) { |
b39a982d TV |
74 | r = -EINVAL; |
75 | goto out; | |
76 | } | |
77 | ||
78 | /* XXX uses only the first overlay */ | |
79 | ovl = ofbi->overlays[0]; | |
80 | ||
3d84b65a | 81 | old_rg = ofbi->region; |
078ff546 VS |
82 | new_rg = get_mem_region(ofbi, pi->mem_idx); |
83 | if (!new_rg) { | |
84 | r = -EINVAL; | |
3d84b65a | 85 | goto out; |
078ff546 VS |
86 | } |
87 | ||
3d84b65a VS |
88 | /* Take the locks in a specific order to keep lockdep happy */ |
89 | if (old_rg->id < new_rg->id) { | |
90 | omapfb_get_mem_region(old_rg); | |
91 | omapfb_get_mem_region(new_rg); | |
92 | } else if (new_rg->id < old_rg->id) { | |
93 | omapfb_get_mem_region(new_rg); | |
94 | omapfb_get_mem_region(old_rg); | |
95 | } else | |
96 | omapfb_get_mem_region(old_rg); | |
97 | ||
078ff546 | 98 | if (pi->enabled && !new_rg->size) { |
b39a982d TV |
99 | /* |
100 | * This plane's memory was freed, can't enable it | |
101 | * until it's reallocated. | |
102 | */ | |
103 | r = -EINVAL; | |
3d84b65a | 104 | goto put_mem; |
b39a982d TV |
105 | } |
106 | ||
078ff546 VS |
107 | ovl->get_overlay_info(ovl, &old_info); |
108 | ||
109 | if (old_rg != new_rg) { | |
110 | ofbi->region = new_rg; | |
111 | set_fb_fix(fbi); | |
112 | } | |
b39a982d | 113 | |
aaa874a9 TV |
114 | if (!pi->enabled) { |
115 | r = ovl->disable(ovl); | |
116 | if (r) | |
117 | goto undo; | |
118 | } | |
b39a982d | 119 | |
aaa874a9 | 120 | if (pi->enabled) { |
078ff546 VS |
121 | r = omapfb_setup_overlay(fbi, ovl, pi->pos_x, pi->pos_y, |
122 | pi->out_width, pi->out_height); | |
123 | if (r) | |
124 | goto undo; | |
078ff546 VS |
125 | } else { |
126 | struct omap_overlay_info info; | |
b39a982d | 127 | |
078ff546 VS |
128 | ovl->get_overlay_info(ovl, &info); |
129 | ||
078ff546 VS |
130 | info.pos_x = pi->pos_x; |
131 | info.pos_y = pi->pos_y; | |
132 | info.out_width = pi->out_width; | |
133 | info.out_height = pi->out_height; | |
134 | ||
135 | r = ovl->set_overlay_info(ovl, &info); | |
b39a982d | 136 | if (r) |
078ff546 | 137 | goto undo; |
b39a982d TV |
138 | } |
139 | ||
078ff546 VS |
140 | if (ovl->manager) |
141 | ovl->manager->apply(ovl->manager); | |
142 | ||
aaa874a9 TV |
143 | if (pi->enabled) { |
144 | r = ovl->enable(ovl); | |
145 | if (r) | |
146 | goto undo; | |
147 | } | |
148 | ||
3d84b65a VS |
149 | /* Release the locks in a specific order to keep lockdep happy */ |
150 | if (old_rg->id > new_rg->id) { | |
151 | omapfb_put_mem_region(old_rg); | |
152 | omapfb_put_mem_region(new_rg); | |
153 | } else if (new_rg->id > old_rg->id) { | |
154 | omapfb_put_mem_region(new_rg); | |
155 | omapfb_put_mem_region(old_rg); | |
156 | } else | |
157 | omapfb_put_mem_region(old_rg); | |
430571d5 | 158 | |
078ff546 VS |
159 | return 0; |
160 | ||
161 | undo: | |
162 | if (old_rg != new_rg) { | |
163 | ofbi->region = old_rg; | |
164 | set_fb_fix(fbi); | |
165 | } | |
166 | ||
167 | ovl->set_overlay_info(ovl, &old_info); | |
3d84b65a VS |
168 | put_mem: |
169 | /* Release the locks in a specific order to keep lockdep happy */ | |
170 | if (old_rg->id > new_rg->id) { | |
171 | omapfb_put_mem_region(old_rg); | |
172 | omapfb_put_mem_region(new_rg); | |
173 | } else if (new_rg->id > old_rg->id) { | |
174 | omapfb_put_mem_region(new_rg); | |
175 | omapfb_put_mem_region(old_rg); | |
176 | } else | |
177 | omapfb_put_mem_region(old_rg); | |
078ff546 VS |
178 | out: |
179 | dev_err(fbdev->dev, "setup_plane failed\n"); | |
180 | ||
b39a982d TV |
181 | return r; |
182 | } | |
183 | ||
184 | static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) | |
185 | { | |
186 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
187 | ||
b3e68d30 | 188 | if (ofbi->num_overlays == 0) { |
b39a982d TV |
189 | memset(pi, 0, sizeof(*pi)); |
190 | } else { | |
b39a982d | 191 | struct omap_overlay *ovl; |
c1a9febf | 192 | struct omap_overlay_info ovli; |
b39a982d TV |
193 | |
194 | ovl = ofbi->overlays[0]; | |
c1a9febf | 195 | ovl->get_overlay_info(ovl, &ovli); |
b39a982d | 196 | |
c1a9febf TV |
197 | pi->pos_x = ovli.pos_x; |
198 | pi->pos_y = ovli.pos_y; | |
aaa874a9 | 199 | pi->enabled = ovl->is_enabled(ovl); |
b39a982d TV |
200 | pi->channel_out = 0; /* xxx */ |
201 | pi->mirror = 0; | |
078ff546 | 202 | pi->mem_idx = get_mem_idx(ofbi); |
c1a9febf TV |
203 | pi->out_width = ovli.out_width; |
204 | pi->out_height = ovli.out_height; | |
b39a982d TV |
205 | } |
206 | ||
207 | return 0; | |
208 | } | |
209 | ||
210 | static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) | |
211 | { | |
212 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
213 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
214 | struct omapfb2_mem_region *rg; | |
430571d5 | 215 | int r = 0, i; |
b39a982d TV |
216 | size_t size; |
217 | ||
2a803c88 | 218 | if (mi->type != OMAPFB_MEMTYPE_SDRAM) |
b39a982d TV |
219 | return -EINVAL; |
220 | ||
221 | size = PAGE_ALIGN(mi->size); | |
222 | ||
078ff546 | 223 | rg = ofbi->region; |
b39a982d | 224 | |
3d84b65a | 225 | down_write_nested(&rg->lock, rg->id); |
1ceafc00 | 226 | atomic_inc(&rg->lock_count); |
430571d5 | 227 | |
f3fb3bbc GI |
228 | if (rg->size == size && rg->type == mi->type) |
229 | goto out; | |
230 | ||
430571d5 VS |
231 | if (atomic_read(&rg->map_count)) { |
232 | r = -EBUSY; | |
233 | goto out; | |
234 | } | |
078ff546 VS |
235 | |
236 | for (i = 0; i < fbdev->num_fbs; i++) { | |
237 | struct omapfb_info *ofbi2 = FB2OFB(fbdev->fbs[i]); | |
238 | int j; | |
239 | ||
240 | if (ofbi2->region != rg) | |
241 | continue; | |
242 | ||
243 | for (j = 0; j < ofbi2->num_overlays; j++) { | |
aaa874a9 TV |
244 | struct omap_overlay *ovl; |
245 | ovl = ofbi2->overlays[j]; | |
246 | if (ovl->is_enabled(ovl)) { | |
078ff546 | 247 | r = -EBUSY; |
430571d5 | 248 | goto out; |
078ff546 VS |
249 | } |
250 | } | |
b39a982d TV |
251 | } |
252 | ||
f3fb3bbc GI |
253 | r = omapfb_realloc_fbmem(fbi, size, mi->type); |
254 | if (r) { | |
255 | dev_err(fbdev->dev, "realloc fbmem failed\n"); | |
256 | goto out; | |
b39a982d TV |
257 | } |
258 | ||
430571d5 | 259 | out: |
1ceafc00 | 260 | atomic_dec(&rg->lock_count); |
2f642a17 | 261 | up_write(&rg->lock); |
430571d5 VS |
262 | |
263 | return r; | |
b39a982d TV |
264 | } |
265 | ||
266 | static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) | |
267 | { | |
268 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
269 | struct omapfb2_mem_region *rg; | |
270 | ||
430571d5 | 271 | rg = omapfb_get_mem_region(ofbi->region); |
b39a982d TV |
272 | memset(mi, 0, sizeof(*mi)); |
273 | ||
274 | mi->size = rg->size; | |
275 | mi->type = rg->type; | |
276 | ||
430571d5 VS |
277 | omapfb_put_mem_region(rg); |
278 | ||
b39a982d TV |
279 | return 0; |
280 | } | |
281 | ||
282 | static int omapfb_update_window_nolock(struct fb_info *fbi, | |
283 | u32 x, u32 y, u32 w, u32 h) | |
284 | { | |
285 | struct omap_dss_device *display = fb2display(fbi); | |
286 | u16 dw, dh; | |
287 | ||
288 | if (!display) | |
289 | return 0; | |
290 | ||
291 | if (w == 0 || h == 0) | |
292 | return 0; | |
293 | ||
96adcece | 294 | display->driver->get_resolution(display, &dw, &dh); |
b39a982d TV |
295 | |
296 | if (x + w > dw || y + h > dh) | |
297 | return -EINVAL; | |
298 | ||
18946f62 | 299 | return display->driver->update(display, x, y, w, h); |
b39a982d TV |
300 | } |
301 | ||
302 | /* This function is exported for SGX driver use */ | |
303 | int omapfb_update_window(struct fb_info *fbi, | |
304 | u32 x, u32 y, u32 w, u32 h) | |
305 | { | |
306 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
307 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
308 | int r; | |
309 | ||
27b67c92 JN |
310 | if (!lock_fb_info(fbi)) |
311 | return -ENODEV; | |
238a4132 | 312 | omapfb_lock(fbdev); |
b39a982d TV |
313 | |
314 | r = omapfb_update_window_nolock(fbi, x, y, w, h); | |
315 | ||
b39a982d | 316 | omapfb_unlock(fbdev); |
238a4132 | 317 | unlock_fb_info(fbi); |
b39a982d TV |
318 | |
319 | return r; | |
320 | } | |
321 | EXPORT_SYMBOL(omapfb_update_window); | |
322 | ||
27cc213e | 323 | int omapfb_set_update_mode(struct fb_info *fbi, |
b39a982d TV |
324 | enum omapfb_update_mode mode) |
325 | { | |
326 | struct omap_dss_device *display = fb2display(fbi); | |
27cc213e TV |
327 | struct omapfb_info *ofbi = FB2OFB(fbi); |
328 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
329 | struct omapfb_display_data *d; | |
b39a982d TV |
330 | int r; |
331 | ||
27cc213e | 332 | if (!display) |
b39a982d TV |
333 | return -EINVAL; |
334 | ||
27cc213e TV |
335 | if (mode != OMAPFB_AUTO_UPDATE && mode != OMAPFB_MANUAL_UPDATE) |
336 | return -EINVAL; | |
b39a982d | 337 | |
27cc213e | 338 | omapfb_lock(fbdev); |
b39a982d | 339 | |
27cc213e | 340 | d = get_display_data(fbdev, display); |
b39a982d | 341 | |
27cc213e TV |
342 | if (d->update_mode == mode) { |
343 | omapfb_unlock(fbdev); | |
344 | return 0; | |
345 | } | |
346 | ||
347 | r = 0; | |
348 | ||
349 | if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | |
350 | if (mode == OMAPFB_AUTO_UPDATE) | |
351 | omapfb_start_auto_update(fbdev, display); | |
352 | else /* MANUAL_UPDATE */ | |
353 | omapfb_stop_auto_update(fbdev, display); | |
354 | ||
355 | d->update_mode = mode; | |
356 | } else { /* AUTO_UPDATE */ | |
357 | if (mode == OMAPFB_MANUAL_UPDATE) | |
358 | r = -EINVAL; | |
b39a982d TV |
359 | } |
360 | ||
27cc213e | 361 | omapfb_unlock(fbdev); |
b39a982d TV |
362 | |
363 | return r; | |
364 | } | |
365 | ||
27cc213e | 366 | int omapfb_get_update_mode(struct fb_info *fbi, |
b39a982d TV |
367 | enum omapfb_update_mode *mode) |
368 | { | |
369 | struct omap_dss_device *display = fb2display(fbi); | |
27cc213e TV |
370 | struct omapfb_info *ofbi = FB2OFB(fbi); |
371 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
372 | struct omapfb_display_data *d; | |
b39a982d | 373 | |
446f7bff | 374 | if (!display) |
b39a982d TV |
375 | return -EINVAL; |
376 | ||
27cc213e | 377 | omapfb_lock(fbdev); |
446f7bff | 378 | |
27cc213e | 379 | d = get_display_data(fbdev, display); |
b39a982d | 380 | |
27cc213e TV |
381 | *mode = d->update_mode; |
382 | ||
383 | omapfb_unlock(fbdev); | |
b39a982d TV |
384 | |
385 | return 0; | |
386 | } | |
387 | ||
388 | /* XXX this color key handling is a hack... */ | |
389 | static struct omapfb_color_key omapfb_color_keys[2]; | |
390 | ||
391 | static int _omapfb_set_color_key(struct omap_overlay_manager *mgr, | |
392 | struct omapfb_color_key *ck) | |
393 | { | |
394 | struct omap_overlay_manager_info info; | |
395 | enum omap_dss_trans_key_type kt; | |
396 | int r; | |
397 | ||
398 | mgr->get_manager_info(mgr, &info); | |
399 | ||
400 | if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) { | |
401 | info.trans_enabled = false; | |
402 | omapfb_color_keys[mgr->id] = *ck; | |
403 | ||
404 | r = mgr->set_manager_info(mgr, &info); | |
405 | if (r) | |
406 | return r; | |
407 | ||
408 | r = mgr->apply(mgr); | |
409 | ||
410 | return r; | |
411 | } | |
412 | ||
413 | switch (ck->key_type) { | |
414 | case OMAPFB_COLOR_KEY_GFX_DST: | |
415 | kt = OMAP_DSS_COLOR_KEY_GFX_DST; | |
416 | break; | |
417 | case OMAPFB_COLOR_KEY_VID_SRC: | |
418 | kt = OMAP_DSS_COLOR_KEY_VID_SRC; | |
419 | break; | |
420 | default: | |
421 | return -EINVAL; | |
422 | } | |
423 | ||
424 | info.default_color = ck->background; | |
425 | info.trans_key = ck->trans_key; | |
426 | info.trans_key_type = kt; | |
427 | info.trans_enabled = true; | |
428 | ||
429 | omapfb_color_keys[mgr->id] = *ck; | |
430 | ||
431 | r = mgr->set_manager_info(mgr, &info); | |
432 | if (r) | |
433 | return r; | |
434 | ||
435 | r = mgr->apply(mgr); | |
436 | ||
437 | return r; | |
438 | } | |
439 | ||
440 | static int omapfb_set_color_key(struct fb_info *fbi, | |
441 | struct omapfb_color_key *ck) | |
442 | { | |
443 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
444 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
445 | int r; | |
446 | int i; | |
447 | struct omap_overlay_manager *mgr = NULL; | |
448 | ||
449 | omapfb_lock(fbdev); | |
450 | ||
451 | for (i = 0; i < ofbi->num_overlays; i++) { | |
452 | if (ofbi->overlays[i]->manager) { | |
453 | mgr = ofbi->overlays[i]->manager; | |
454 | break; | |
455 | } | |
456 | } | |
457 | ||
458 | if (!mgr) { | |
459 | r = -EINVAL; | |
460 | goto err; | |
461 | } | |
462 | ||
463 | r = _omapfb_set_color_key(mgr, ck); | |
464 | err: | |
465 | omapfb_unlock(fbdev); | |
466 | ||
467 | return r; | |
468 | } | |
469 | ||
470 | static int omapfb_get_color_key(struct fb_info *fbi, | |
471 | struct omapfb_color_key *ck) | |
472 | { | |
473 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
474 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
475 | struct omap_overlay_manager *mgr = NULL; | |
476 | int r = 0; | |
477 | int i; | |
478 | ||
479 | omapfb_lock(fbdev); | |
480 | ||
481 | for (i = 0; i < ofbi->num_overlays; i++) { | |
482 | if (ofbi->overlays[i]->manager) { | |
483 | mgr = ofbi->overlays[i]->manager; | |
484 | break; | |
485 | } | |
486 | } | |
487 | ||
488 | if (!mgr) { | |
489 | r = -EINVAL; | |
490 | goto err; | |
491 | } | |
492 | ||
493 | *ck = omapfb_color_keys[mgr->id]; | |
494 | err: | |
495 | omapfb_unlock(fbdev); | |
496 | ||
497 | return r; | |
498 | } | |
499 | ||
500 | static int omapfb_memory_read(struct fb_info *fbi, | |
501 | struct omapfb_memory_read *mr) | |
502 | { | |
503 | struct omap_dss_device *display = fb2display(fbi); | |
504 | void *buf; | |
505 | int r; | |
506 | ||
c75d9464 | 507 | if (!display || !display->driver->memory_read) |
b39a982d TV |
508 | return -ENOENT; |
509 | ||
510 | if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size)) | |
511 | return -EFAULT; | |
512 | ||
513 | if (mr->w * mr->h * 3 > mr->buffer_size) | |
514 | return -EINVAL; | |
515 | ||
516 | buf = vmalloc(mr->buffer_size); | |
517 | if (!buf) { | |
518 | DBG("vmalloc failed\n"); | |
519 | return -ENOMEM; | |
520 | } | |
521 | ||
c75d9464 | 522 | r = display->driver->memory_read(display, buf, mr->buffer_size, |
b39a982d TV |
523 | mr->x, mr->y, mr->w, mr->h); |
524 | ||
525 | if (r > 0) { | |
526 | if (copy_to_user(mr->buffer, buf, mr->buffer_size)) | |
527 | r = -EFAULT; | |
528 | } | |
529 | ||
530 | vfree(buf); | |
531 | ||
532 | return r; | |
533 | } | |
534 | ||
535 | static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev, | |
536 | struct omapfb_ovl_colormode *mode) | |
537 | { | |
538 | int ovl_idx = mode->overlay_idx; | |
539 | int mode_idx = mode->mode_idx; | |
540 | struct omap_overlay *ovl; | |
541 | enum omap_color_mode supported_modes; | |
542 | struct fb_var_screeninfo var; | |
543 | int i; | |
544 | ||
545 | if (ovl_idx >= fbdev->num_overlays) | |
546 | return -ENODEV; | |
547 | ovl = fbdev->overlays[ovl_idx]; | |
548 | supported_modes = ovl->supported_modes; | |
549 | ||
550 | mode_idx = mode->mode_idx; | |
551 | ||
552 | for (i = 0; i < sizeof(supported_modes) * 8; i++) { | |
553 | if (!(supported_modes & (1 << i))) | |
554 | continue; | |
555 | /* | |
556 | * It's possible that the FB doesn't support a mode | |
557 | * that is supported by the overlay, so call the | |
558 | * following here. | |
559 | */ | |
560 | if (dss_mode_to_fb_mode(1 << i, &var) < 0) | |
561 | continue; | |
562 | ||
563 | mode_idx--; | |
564 | if (mode_idx < 0) | |
565 | break; | |
566 | } | |
567 | ||
568 | if (i == sizeof(supported_modes) * 8) | |
569 | return -ENOENT; | |
570 | ||
571 | mode->bits_per_pixel = var.bits_per_pixel; | |
572 | mode->nonstd = var.nonstd; | |
573 | mode->red = var.red; | |
574 | mode->green = var.green; | |
575 | mode->blue = var.blue; | |
576 | mode->transp = var.transp; | |
577 | ||
578 | return 0; | |
579 | } | |
580 | ||
581 | static int omapfb_wait_for_go(struct fb_info *fbi) | |
582 | { | |
583 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
584 | int r = 0; | |
585 | int i; | |
586 | ||
587 | for (i = 0; i < ofbi->num_overlays; ++i) { | |
588 | struct omap_overlay *ovl = ofbi->overlays[i]; | |
589 | r = ovl->wait_for_go(ovl); | |
590 | if (r) | |
591 | break; | |
592 | } | |
593 | ||
594 | return r; | |
595 | } | |
596 | ||
597 | int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) | |
598 | { | |
599 | struct omapfb_info *ofbi = FB2OFB(fbi); | |
600 | struct omapfb2_device *fbdev = ofbi->fbdev; | |
601 | struct omap_dss_device *display = fb2display(fbi); | |
952cbaaa | 602 | struct omap_overlay_manager *mgr; |
b39a982d TV |
603 | |
604 | union { | |
605 | struct omapfb_update_window_old uwnd_o; | |
606 | struct omapfb_update_window uwnd; | |
607 | struct omapfb_plane_info plane_info; | |
608 | struct omapfb_caps caps; | |
609 | struct omapfb_mem_info mem_info; | |
610 | struct omapfb_color_key color_key; | |
611 | struct omapfb_ovl_colormode ovl_colormode; | |
612 | enum omapfb_update_mode update_mode; | |
613 | int test_num; | |
614 | struct omapfb_memory_read memory_read; | |
615 | struct omapfb_vram_info vram_info; | |
616 | struct omapfb_tearsync_info tearsync_info; | |
6dd2e42b | 617 | struct omapfb_display_info display_info; |
ece350d3 | 618 | u32 crt; |
b39a982d TV |
619 | } p; |
620 | ||
621 | int r = 0; | |
622 | ||
623 | switch (cmd) { | |
624 | case OMAPFB_SYNC_GFX: | |
625 | DBG("ioctl SYNC_GFX\n"); | |
18946f62 | 626 | if (!display || !display->driver->sync) { |
b39a982d TV |
627 | /* DSS1 never returns an error here, so we neither */ |
628 | /*r = -EINVAL;*/ | |
629 | break; | |
630 | } | |
631 | ||
18946f62 | 632 | r = display->driver->sync(display); |
b39a982d TV |
633 | break; |
634 | ||
635 | case OMAPFB_UPDATE_WINDOW_OLD: | |
636 | DBG("ioctl UPDATE_WINDOW_OLD\n"); | |
18946f62 | 637 | if (!display || !display->driver->update) { |
b39a982d TV |
638 | r = -EINVAL; |
639 | break; | |
640 | } | |
641 | ||
642 | if (copy_from_user(&p.uwnd_o, | |
643 | (void __user *)arg, | |
644 | sizeof(p.uwnd_o))) { | |
645 | r = -EFAULT; | |
646 | break; | |
647 | } | |
648 | ||
649 | r = omapfb_update_window_nolock(fbi, p.uwnd_o.x, p.uwnd_o.y, | |
650 | p.uwnd_o.width, p.uwnd_o.height); | |
651 | break; | |
652 | ||
653 | case OMAPFB_UPDATE_WINDOW: | |
654 | DBG("ioctl UPDATE_WINDOW\n"); | |
18946f62 | 655 | if (!display || !display->driver->update) { |
b39a982d TV |
656 | r = -EINVAL; |
657 | break; | |
658 | } | |
659 | ||
660 | if (copy_from_user(&p.uwnd, (void __user *)arg, | |
661 | sizeof(p.uwnd))) { | |
662 | r = -EFAULT; | |
663 | break; | |
664 | } | |
665 | ||
666 | r = omapfb_update_window_nolock(fbi, p.uwnd.x, p.uwnd.y, | |
667 | p.uwnd.width, p.uwnd.height); | |
668 | break; | |
669 | ||
670 | case OMAPFB_SETUP_PLANE: | |
671 | DBG("ioctl SETUP_PLANE\n"); | |
672 | if (copy_from_user(&p.plane_info, (void __user *)arg, | |
673 | sizeof(p.plane_info))) | |
674 | r = -EFAULT; | |
675 | else | |
676 | r = omapfb_setup_plane(fbi, &p.plane_info); | |
677 | break; | |
678 | ||
679 | case OMAPFB_QUERY_PLANE: | |
680 | DBG("ioctl QUERY_PLANE\n"); | |
681 | r = omapfb_query_plane(fbi, &p.plane_info); | |
682 | if (r < 0) | |
683 | break; | |
684 | if (copy_to_user((void __user *)arg, &p.plane_info, | |
685 | sizeof(p.plane_info))) | |
686 | r = -EFAULT; | |
687 | break; | |
688 | ||
689 | case OMAPFB_SETUP_MEM: | |
690 | DBG("ioctl SETUP_MEM\n"); | |
691 | if (copy_from_user(&p.mem_info, (void __user *)arg, | |
692 | sizeof(p.mem_info))) | |
693 | r = -EFAULT; | |
694 | else | |
695 | r = omapfb_setup_mem(fbi, &p.mem_info); | |
696 | break; | |
697 | ||
698 | case OMAPFB_QUERY_MEM: | |
699 | DBG("ioctl QUERY_MEM\n"); | |
700 | r = omapfb_query_mem(fbi, &p.mem_info); | |
701 | if (r < 0) | |
702 | break; | |
703 | if (copy_to_user((void __user *)arg, &p.mem_info, | |
704 | sizeof(p.mem_info))) | |
705 | r = -EFAULT; | |
706 | break; | |
707 | ||
708 | case OMAPFB_GET_CAPS: | |
709 | DBG("ioctl GET_CAPS\n"); | |
710 | if (!display) { | |
711 | r = -EINVAL; | |
712 | break; | |
713 | } | |
714 | ||
715 | memset(&p.caps, 0, sizeof(p.caps)); | |
716 | if (display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) | |
717 | p.caps.ctrl |= OMAPFB_CAPS_MANUAL_UPDATE; | |
718 | if (display->caps & OMAP_DSS_DISPLAY_CAP_TEAR_ELIM) | |
719 | p.caps.ctrl |= OMAPFB_CAPS_TEARSYNC; | |
720 | ||
721 | if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) | |
722 | r = -EFAULT; | |
723 | break; | |
724 | ||
725 | case OMAPFB_GET_OVERLAY_COLORMODE: | |
726 | DBG("ioctl GET_OVERLAY_COLORMODE\n"); | |
727 | if (copy_from_user(&p.ovl_colormode, (void __user *)arg, | |
728 | sizeof(p.ovl_colormode))) { | |
729 | r = -EFAULT; | |
730 | break; | |
731 | } | |
732 | r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode); | |
733 | if (r < 0) | |
734 | break; | |
735 | if (copy_to_user((void __user *)arg, &p.ovl_colormode, | |
736 | sizeof(p.ovl_colormode))) | |
737 | r = -EFAULT; | |
738 | break; | |
739 | ||
740 | case OMAPFB_SET_UPDATE_MODE: | |
741 | DBG("ioctl SET_UPDATE_MODE\n"); | |
742 | if (get_user(p.update_mode, (int __user *)arg)) | |
743 | r = -EFAULT; | |
744 | else | |
745 | r = omapfb_set_update_mode(fbi, p.update_mode); | |
746 | break; | |
747 | ||
748 | case OMAPFB_GET_UPDATE_MODE: | |
749 | DBG("ioctl GET_UPDATE_MODE\n"); | |
750 | r = omapfb_get_update_mode(fbi, &p.update_mode); | |
751 | if (r) | |
752 | break; | |
753 | if (put_user(p.update_mode, | |
754 | (enum omapfb_update_mode __user *)arg)) | |
755 | r = -EFAULT; | |
756 | break; | |
757 | ||
758 | case OMAPFB_SET_COLOR_KEY: | |
759 | DBG("ioctl SET_COLOR_KEY\n"); | |
760 | if (copy_from_user(&p.color_key, (void __user *)arg, | |
761 | sizeof(p.color_key))) | |
762 | r = -EFAULT; | |
763 | else | |
764 | r = omapfb_set_color_key(fbi, &p.color_key); | |
765 | break; | |
766 | ||
767 | case OMAPFB_GET_COLOR_KEY: | |
768 | DBG("ioctl GET_COLOR_KEY\n"); | |
769 | r = omapfb_get_color_key(fbi, &p.color_key); | |
770 | if (r) | |
771 | break; | |
772 | if (copy_to_user((void __user *)arg, &p.color_key, | |
773 | sizeof(p.color_key))) | |
774 | r = -EFAULT; | |
775 | break; | |
776 | ||
ece350d3 GI |
777 | case FBIO_WAITFORVSYNC: |
778 | if (get_user(p.crt, (__u32 __user *)arg)) { | |
779 | r = -EFAULT; | |
780 | break; | |
781 | } | |
782 | if (p.crt != 0) { | |
783 | r = -ENODEV; | |
784 | break; | |
785 | } | |
786 | /* FALLTHROUGH */ | |
787 | ||
b39a982d TV |
788 | case OMAPFB_WAITFORVSYNC: |
789 | DBG("ioctl WAITFORVSYNC\n"); | |
c415187b | 790 | if (!display || !display->output || !display->output->manager) { |
b39a982d TV |
791 | r = -EINVAL; |
792 | break; | |
793 | } | |
794 | ||
952cbaaa AT |
795 | mgr = display->output->manager; |
796 | ||
797 | r = mgr->wait_for_vsync(mgr); | |
b39a982d TV |
798 | break; |
799 | ||
800 | case OMAPFB_WAITFORGO: | |
801 | DBG("ioctl WAITFORGO\n"); | |
802 | if (!display) { | |
803 | r = -EINVAL; | |
804 | break; | |
805 | } | |
806 | ||
807 | r = omapfb_wait_for_go(fbi); | |
808 | break; | |
809 | ||
810 | /* LCD and CTRL tests do the same thing for backward | |
811 | * compatibility */ | |
812 | case OMAPFB_LCD_TEST: | |
813 | DBG("ioctl LCD_TEST\n"); | |
814 | if (get_user(p.test_num, (int __user *)arg)) { | |
815 | r = -EFAULT; | |
816 | break; | |
817 | } | |
1a75ef42 | 818 | if (!display || !display->driver->run_test) { |
b39a982d TV |
819 | r = -EINVAL; |
820 | break; | |
821 | } | |
822 | ||
1a75ef42 | 823 | r = display->driver->run_test(display, p.test_num); |
b39a982d TV |
824 | |
825 | break; | |
826 | ||
827 | case OMAPFB_CTRL_TEST: | |
828 | DBG("ioctl CTRL_TEST\n"); | |
829 | if (get_user(p.test_num, (int __user *)arg)) { | |
830 | r = -EFAULT; | |
831 | break; | |
832 | } | |
1a75ef42 | 833 | if (!display || !display->driver->run_test) { |
b39a982d TV |
834 | r = -EINVAL; |
835 | break; | |
836 | } | |
837 | ||
1a75ef42 | 838 | r = display->driver->run_test(display, p.test_num); |
b39a982d TV |
839 | |
840 | break; | |
841 | ||
842 | case OMAPFB_MEMORY_READ: | |
843 | DBG("ioctl MEMORY_READ\n"); | |
844 | ||
845 | if (copy_from_user(&p.memory_read, (void __user *)arg, | |
846 | sizeof(p.memory_read))) { | |
847 | r = -EFAULT; | |
848 | break; | |
849 | } | |
850 | ||
851 | r = omapfb_memory_read(fbi, &p.memory_read); | |
852 | ||
853 | break; | |
854 | ||
855 | case OMAPFB_GET_VRAM_INFO: { | |
856 | unsigned long vram, free, largest; | |
857 | ||
858 | DBG("ioctl GET_VRAM_INFO\n"); | |
859 | ||
860 | omap_vram_get_info(&vram, &free, &largest); | |
861 | p.vram_info.total = vram; | |
862 | p.vram_info.free = free; | |
863 | p.vram_info.largest_free_block = largest; | |
864 | ||
865 | if (copy_to_user((void __user *)arg, &p.vram_info, | |
866 | sizeof(p.vram_info))) | |
867 | r = -EFAULT; | |
868 | break; | |
869 | } | |
870 | ||
871 | case OMAPFB_SET_TEARSYNC: { | |
872 | DBG("ioctl SET_TEARSYNC\n"); | |
873 | ||
874 | if (copy_from_user(&p.tearsync_info, (void __user *)arg, | |
875 | sizeof(p.tearsync_info))) { | |
876 | r = -EFAULT; | |
877 | break; | |
878 | } | |
879 | ||
93255887 | 880 | if (!display || !display->driver->enable_te) { |
b39a982d TV |
881 | r = -ENODEV; |
882 | break; | |
883 | } | |
884 | ||
225b650d TV |
885 | r = display->driver->enable_te(display, |
886 | !!p.tearsync_info.enabled); | |
b39a982d TV |
887 | |
888 | break; | |
889 | } | |
890 | ||
6dd2e42b TV |
891 | case OMAPFB_GET_DISPLAY_INFO: { |
892 | u16 xres, yres; | |
893 | ||
894 | DBG("ioctl GET_DISPLAY_INFO\n"); | |
895 | ||
896 | if (display == NULL) { | |
897 | r = -ENODEV; | |
898 | break; | |
899 | } | |
900 | ||
96adcece | 901 | display->driver->get_resolution(display, &xres, &yres); |
6dd2e42b TV |
902 | |
903 | p.display_info.xres = xres; | |
904 | p.display_info.yres = yres; | |
7a0987bf JN |
905 | |
906 | if (display->driver->get_dimensions) { | |
907 | u32 w, h; | |
908 | display->driver->get_dimensions(display, &w, &h); | |
909 | p.display_info.width = w; | |
910 | p.display_info.height = h; | |
911 | } else { | |
912 | p.display_info.width = 0; | |
913 | p.display_info.height = 0; | |
914 | } | |
6dd2e42b TV |
915 | |
916 | if (copy_to_user((void __user *)arg, &p.display_info, | |
917 | sizeof(p.display_info))) | |
918 | r = -EFAULT; | |
919 | break; | |
920 | } | |
921 | ||
b39a982d TV |
922 | default: |
923 | dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd); | |
924 | r = -EINVAL; | |
925 | } | |
926 | ||
927 | if (r < 0) | |
928 | DBG("ioctl failed: %d\n", r); | |
929 | ||
930 | return r; | |
931 | } | |
932 | ||
933 |