/drivers/video/omap2/dss/overlay.c
C | 764 lines | 569 code | 152 blank | 43 comment | 103 complexity | e292d456590d4b5fc90bfde10ae52569 MD5 | raw file
Possible License(s): LGPL-2.0, AGPL-1.0, GPL-2.0
1/* 2 * linux/drivers/video/omap2/dss/overlay.c 3 * 4 * Copyright (C) 2009 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#define DSS_SUBSYS_NAME "OVERLAY" 24 25#include <linux/kernel.h> 26#include <linux/module.h> 27#include <linux/err.h> 28#include <linux/sysfs.h> 29#include <linux/kobject.h> 30#include <linux/platform_device.h> 31#include <linux/delay.h> 32#include <linux/slab.h> 33 34#include <video/omapdss.h> 35#include <plat/cpu.h> 36 37#include "dss.h" 38#include "dss_features.h" 39 40static int num_overlays; 41static struct list_head overlay_list; 42 43static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) 44{ 45 return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); 46} 47 48static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) 49{ 50 return snprintf(buf, PAGE_SIZE, "%s\n", 51 ovl->manager ? ovl->manager->name : "<none>"); 52} 53 54static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, 55 size_t size) 56{ 57 int i, r; 58 struct omap_overlay_manager *mgr = NULL; 59 struct omap_overlay_manager *old_mgr; 60 int len = size; 61 62 if (buf[size-1] == '\n') 63 --len; 64 65 if (len > 0) { 66 for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { 67 mgr = omap_dss_get_overlay_manager(i); 68 69 if (sysfs_streq(buf, mgr->name)) 70 break; 71 72 mgr = NULL; 73 } 74 } 75 76 if (len > 0 && mgr == NULL) 77 return -EINVAL; 78 79 if (mgr) 80 DSSDBG("manager %s found\n", mgr->name); 81 82 if (mgr == ovl->manager) 83 return size; 84 85 old_mgr = ovl->manager; 86 87 r = dispc_runtime_get(); 88 if (r) 89 return r; 90 91 /* detach old manager */ 92 if (old_mgr) { 93 r = ovl->unset_manager(ovl); 94 if (r) { 95 DSSERR("detach failed\n"); 96 goto err; 97 } 98 99 r = old_mgr->apply(old_mgr); 100 if (r) 101 goto err; 102 } 103 104 if (mgr) { 105 r = ovl->set_manager(ovl, mgr); 106 if (r) { 107 DSSERR("Failed to attach overlay\n"); 108 goto err; 109 } 110 111 r = mgr->apply(mgr); 112 if (r) 113 goto err; 114 } 115 116 dispc_runtime_put(); 117 118 return size; 119 120err: 121 dispc_runtime_put(); 122 return r; 123} 124 125static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) 126{ 127 return snprintf(buf, PAGE_SIZE, "%d,%d\n", 128 ovl->info.width, ovl->info.height); 129} 130 131static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) 132{ 133 return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width); 134} 135 136static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) 137{ 138 return snprintf(buf, PAGE_SIZE, "%d,%d\n", 139 ovl->info.pos_x, ovl->info.pos_y); 140} 141 142static ssize_t overlay_position_store(struct omap_overlay *ovl, 143 const char *buf, size_t size) 144{ 145 int r; 146 char *last; 147 struct omap_overlay_info info; 148 149 ovl->get_overlay_info(ovl, &info); 150 151 info.pos_x = simple_strtoul(buf, &last, 10); 152 ++last; 153 if (last - buf >= size) 154 return -EINVAL; 155 156 info.pos_y = simple_strtoul(last, &last, 10); 157 158 r = ovl->set_overlay_info(ovl, &info); 159 if (r) 160 return r; 161 162 if (ovl->manager) { 163 r = ovl->manager->apply(ovl->manager); 164 if (r) 165 return r; 166 } 167 168 return size; 169} 170 171static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) 172{ 173 return snprintf(buf, PAGE_SIZE, "%d,%d\n", 174 ovl->info.out_width, ovl->info.out_height); 175} 176 177static ssize_t overlay_output_size_store(struct omap_overlay *ovl, 178 const char *buf, size_t size) 179{ 180 int r; 181 char *last; 182 struct omap_overlay_info info; 183 184 ovl->get_overlay_info(ovl, &info); 185 186 info.out_width = simple_strtoul(buf, &last, 10); 187 ++last; 188 if (last - buf >= size) 189 return -EINVAL; 190 191 info.out_height = simple_strtoul(last, &last, 10); 192 193 r = ovl->set_overlay_info(ovl, &info); 194 if (r) 195 return r; 196 197 if (ovl->manager) { 198 r = ovl->manager->apply(ovl->manager); 199 if (r) 200 return r; 201 } 202 203 return size; 204} 205 206static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) 207{ 208 return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled); 209} 210 211static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, 212 size_t size) 213{ 214 int r, enable; 215 struct omap_overlay_info info; 216 217 ovl->get_overlay_info(ovl, &info); 218 219 r = kstrtoint(buf, 0, &enable); 220 if (r) 221 return r; 222 223 info.enabled = !!enable; 224 225 r = ovl->set_overlay_info(ovl, &info); 226 if (r) 227 return r; 228 229 if (ovl->manager) { 230 r = ovl->manager->apply(ovl->manager); 231 if (r) 232 return r; 233 } 234 235 return size; 236} 237 238static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) 239{ 240 return snprintf(buf, PAGE_SIZE, "%d\n", 241 ovl->info.global_alpha); 242} 243 244static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, 245 const char *buf, size_t size) 246{ 247 int r; 248 u8 alpha; 249 struct omap_overlay_info info; 250 251 if (!dss_has_feature(FEAT_GLOBAL_ALPHA)) 252 return -ENODEV; 253 254 r = kstrtou8(buf, 0, &alpha); 255 if (r) 256 return r; 257 258 ovl->get_overlay_info(ovl, &info); 259 260 /* Video1 plane does not support global alpha 261 * to always make it 255 completely opaque 262 */ 263 if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && 264 ovl->id == OMAP_DSS_VIDEO1) 265 info.global_alpha = 255; 266 else 267 info.global_alpha = alpha; 268 269 r = ovl->set_overlay_info(ovl, &info); 270 if (r) 271 return r; 272 273 if (ovl->manager) { 274 r = ovl->manager->apply(ovl->manager); 275 if (r) 276 return r; 277 } 278 279 return size; 280} 281 282static ssize_t overlay_pre_mult_alpha_show(struct omap_overlay *ovl, 283 char *buf) 284{ 285 return snprintf(buf, PAGE_SIZE, "%d\n", 286 ovl->info.pre_mult_alpha); 287} 288 289static ssize_t overlay_pre_mult_alpha_store(struct omap_overlay *ovl, 290 const char *buf, size_t size) 291{ 292 int r; 293 u8 alpha; 294 struct omap_overlay_info info; 295 296 r = kstrtou8(buf, 0, &alpha); 297 if (r) 298 return r; 299 300 ovl->get_overlay_info(ovl, &info); 301 302 /* only GFX and Video2 plane support pre alpha multiplied 303 * set zero for Video1 plane 304 */ 305 if (!dss_has_feature(FEAT_GLOBAL_ALPHA_VID1) && 306 ovl->id == OMAP_DSS_VIDEO1) 307 info.pre_mult_alpha = 0; 308 else 309 info.pre_mult_alpha = alpha; 310 311 r = ovl->set_overlay_info(ovl, &info); 312 if (r) 313 return r; 314 315 if (ovl->manager) { 316 r = ovl->manager->apply(ovl->manager); 317 if (r) 318 return r; 319 } 320 321 return size; 322} 323 324struct overlay_attribute { 325 struct attribute attr; 326 ssize_t (*show)(struct omap_overlay *, char *); 327 ssize_t (*store)(struct omap_overlay *, const char *, size_t); 328}; 329 330#define OVERLAY_ATTR(_name, _mode, _show, _store) \ 331 struct overlay_attribute overlay_attr_##_name = \ 332 __ATTR(_name, _mode, _show, _store) 333 334static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); 335static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, 336 overlay_manager_show, overlay_manager_store); 337static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); 338static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); 339static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, 340 overlay_position_show, overlay_position_store); 341static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, 342 overlay_output_size_show, overlay_output_size_store); 343static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, 344 overlay_enabled_show, overlay_enabled_store); 345static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, 346 overlay_global_alpha_show, overlay_global_alpha_store); 347static OVERLAY_ATTR(pre_mult_alpha, S_IRUGO|S_IWUSR, 348 overlay_pre_mult_alpha_show, 349 overlay_pre_mult_alpha_store); 350 351static struct attribute *overlay_sysfs_attrs[] = { 352 &overlay_attr_name.attr, 353 &overlay_attr_manager.attr, 354 &overlay_attr_input_size.attr, 355 &overlay_attr_screen_width.attr, 356 &overlay_attr_position.attr, 357 &overlay_attr_output_size.attr, 358 &overlay_attr_enabled.attr, 359 &overlay_attr_global_alpha.attr, 360 &overlay_attr_pre_mult_alpha.attr, 361 NULL 362}; 363 364static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, 365 char *buf) 366{ 367 struct omap_overlay *overlay; 368 struct overlay_attribute *overlay_attr; 369 370 overlay = container_of(kobj, struct omap_overlay, kobj); 371 overlay_attr = container_of(attr, struct overlay_attribute, attr); 372 373 if (!overlay_attr->show) 374 return -ENOENT; 375 376 return overlay_attr->show(overlay, buf); 377} 378 379static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, 380 const char *buf, size_t size) 381{ 382 struct omap_overlay *overlay; 383 struct overlay_attribute *overlay_attr; 384 385 overlay = container_of(kobj, struct omap_overlay, kobj); 386 overlay_attr = container_of(attr, struct overlay_attribute, attr); 387 388 if (!overlay_attr->store) 389 return -ENOENT; 390 391 return overlay_attr->store(overlay, buf, size); 392} 393 394static const struct sysfs_ops overlay_sysfs_ops = { 395 .show = overlay_attr_show, 396 .store = overlay_attr_store, 397}; 398 399static struct kobj_type overlay_ktype = { 400 .sysfs_ops = &overlay_sysfs_ops, 401 .default_attrs = overlay_sysfs_attrs, 402}; 403 404/* Check if overlay parameters are compatible with display */ 405int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) 406{ 407 struct omap_overlay_info *info; 408 u16 outw, outh; 409 u16 dw, dh; 410 411 if (!dssdev) 412 return 0; 413 414 if (!ovl->info.enabled) 415 return 0; 416 417 info = &ovl->info; 418 419 if (info->paddr == 0) { 420 DSSDBG("check_overlay failed: paddr 0\n"); 421 return -EINVAL; 422 } 423 424 dssdev->driver->get_resolution(dssdev, &dw, &dh); 425 426 DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n", 427 ovl->id, 428 info->pos_x, info->pos_y, 429 info->width, info->height, 430 info->out_width, info->out_height, 431 dw, dh); 432 433 if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { 434 outw = info->width; 435 outh = info->height; 436 } else { 437 if (info->out_width == 0) 438 outw = info->width; 439 else 440 outw = info->out_width; 441 442 if (info->out_height == 0) 443 outh = info->height; 444 else 445 outh = info->out_height; 446 } 447 448 if (dw < info->pos_x + outw) { 449 DSSDBG("check_overlay failed 1: %d < %d + %d\n", 450 dw, info->pos_x, outw); 451 return -EINVAL; 452 } 453 454 if (dh < info->pos_y + outh) { 455 DSSDBG("check_overlay failed 2: %d < %d + %d\n", 456 dh, info->pos_y, outh); 457 return -EINVAL; 458 } 459 460 if ((ovl->supported_modes & info->color_mode) == 0) { 461 DSSERR("overlay doesn't support mode %d\n", info->color_mode); 462 return -EINVAL; 463 } 464 465 return 0; 466} 467 468static int dss_ovl_set_overlay_info(struct omap_overlay *ovl, 469 struct omap_overlay_info *info) 470{ 471 int r; 472 struct omap_overlay_info old_info; 473 474 old_info = ovl->info; 475 ovl->info = *info; 476 477 if (ovl->manager) { 478 r = dss_check_overlay(ovl, ovl->manager->device); 479 if (r) { 480 ovl->info = old_info; 481 return r; 482 } 483 } 484 485 ovl->info_dirty = true; 486 487 return 0; 488} 489 490static void dss_ovl_get_overlay_info(struct omap_overlay *ovl, 491 struct omap_overlay_info *info) 492{ 493 *info = ovl->info; 494} 495 496static int dss_ovl_wait_for_go(struct omap_overlay *ovl) 497{ 498 return dss_mgr_wait_for_go_ovl(ovl); 499} 500 501static int omap_dss_set_manager(struct omap_overlay *ovl, 502 struct omap_overlay_manager *mgr) 503{ 504 if (!mgr) 505 return -EINVAL; 506 507 if (ovl->manager) { 508 DSSERR("overlay '%s' already has a manager '%s'\n", 509 ovl->name, ovl->manager->name); 510 return -EINVAL; 511 } 512 513 if (ovl->info.enabled) { 514 DSSERR("overlay has to be disabled to change the manager\n"); 515 return -EINVAL; 516 } 517 518 ovl->manager = mgr; 519 520 /* XXX: When there is an overlay on a DSI manual update display, and 521 * the overlay is first disabled, then moved to tv, and enabled, we 522 * seem to get SYNC_LOST_DIGIT error. 523 * 524 * Waiting doesn't seem to help, but updating the manual update display 525 * after disabling the overlay seems to fix this. This hints that the 526 * overlay is perhaps somehow tied to the LCD output until the output 527 * is updated. 528 * 529 * Userspace workaround for this is to update the LCD after disabling 530 * the overlay, but before moving the overlay to TV. 531 */ 532 dispc_set_channel_out(ovl->id, mgr->id); 533 534 return 0; 535} 536 537static int omap_dss_unset_manager(struct omap_overlay *ovl) 538{ 539 int r; 540 541 if (!ovl->manager) { 542 DSSERR("failed to detach overlay: manager not set\n"); 543 return -EINVAL; 544 } 545 546 if (ovl->info.enabled) { 547 DSSERR("overlay has to be disabled to unset the manager\n"); 548 return -EINVAL; 549 } 550 551 r = ovl->wait_for_go(ovl); 552 if (r) 553 return r; 554 555 ovl->manager = NULL; 556 557 return 0; 558} 559 560int omap_dss_get_num_overlays(void) 561{ 562 return num_overlays; 563} 564EXPORT_SYMBOL(omap_dss_get_num_overlays); 565 566struct omap_overlay *omap_dss_get_overlay(int num) 567{ 568 int i = 0; 569 struct omap_overlay *ovl; 570 571 list_for_each_entry(ovl, &overlay_list, list) { 572 if (i++ == num) 573 return ovl; 574 } 575 576 return NULL; 577} 578EXPORT_SYMBOL(omap_dss_get_overlay); 579 580static void omap_dss_add_overlay(struct omap_overlay *overlay) 581{ 582 ++num_overlays; 583 list_add_tail(&overlay->list, &overlay_list); 584} 585 586static struct omap_overlay *dispc_overlays[MAX_DSS_OVERLAYS]; 587 588void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) 589{ 590 mgr->num_overlays = dss_feat_get_num_ovls(); 591 mgr->overlays = dispc_overlays; 592} 593 594#ifdef L4_EXAMPLE 595static struct omap_overlay *l4_overlays[1]; 596void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr) 597{ 598 mgr->num_overlays = 1; 599 mgr->overlays = l4_overlays; 600} 601#endif 602 603void dss_init_overlays(struct platform_device *pdev) 604{ 605 int i, r; 606 607 INIT_LIST_HEAD(&overlay_list); 608 609 num_overlays = 0; 610 611 for (i = 0; i < dss_feat_get_num_ovls(); ++i) { 612 struct omap_overlay *ovl; 613 ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); 614 615 BUG_ON(ovl == NULL); 616 617 switch (i) { 618 case 0: 619 ovl->name = "gfx"; 620 ovl->id = OMAP_DSS_GFX; 621 ovl->caps = OMAP_DSS_OVL_CAP_DISPC; 622 ovl->info.global_alpha = 255; 623 break; 624 case 1: 625 ovl->name = "vid1"; 626 ovl->id = OMAP_DSS_VIDEO1; 627 ovl->caps = OMAP_DSS_OVL_CAP_SCALE | 628 OMAP_DSS_OVL_CAP_DISPC; 629 ovl->info.global_alpha = 255; 630 break; 631 case 2: 632 ovl->name = "vid2"; 633 ovl->id = OMAP_DSS_VIDEO2; 634 ovl->caps = OMAP_DSS_OVL_CAP_SCALE | 635 OMAP_DSS_OVL_CAP_DISPC; 636 ovl->info.global_alpha = 255; 637 break; 638 } 639 640 ovl->set_manager = &omap_dss_set_manager; 641 ovl->unset_manager = &omap_dss_unset_manager; 642 ovl->set_overlay_info = &dss_ovl_set_overlay_info; 643 ovl->get_overlay_info = &dss_ovl_get_overlay_info; 644 ovl->wait_for_go = &dss_ovl_wait_for_go; 645 646 ovl->supported_modes = 647 dss_feat_get_supported_color_modes(ovl->id); 648 649 omap_dss_add_overlay(ovl); 650 651 r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, 652 &pdev->dev.kobj, "overlay%d", i); 653 654 if (r) { 655 DSSERR("failed to create sysfs file\n"); 656 continue; 657 } 658 659 dispc_overlays[i] = ovl; 660 } 661 662#ifdef L4_EXAMPLE 663 { 664 struct omap_overlay *ovl; 665 ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); 666 667 BUG_ON(ovl == NULL); 668 669 ovl->name = "l4"; 670 ovl->supported_modes = OMAP_DSS_COLOR_RGB24U; 671 672 ovl->set_manager = &omap_dss_set_manager; 673 ovl->unset_manager = &omap_dss_unset_manager; 674 ovl->set_overlay_info = &dss_ovl_set_overlay_info; 675 ovl->get_overlay_info = &dss_ovl_get_overlay_info; 676 677 omap_dss_add_overlay(ovl); 678 679 r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, 680 &pdev->dev.kobj, "overlayl4"); 681 682 if (r) 683 DSSERR("failed to create sysfs file\n"); 684 685 l4_overlays[0] = ovl; 686 } 687#endif 688} 689 690/* connect overlays to the new device, if not already connected. if force 691 * selected, connect always. */ 692void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) 693{ 694 int i; 695 struct omap_overlay_manager *lcd_mgr; 696 struct omap_overlay_manager *tv_mgr; 697 struct omap_overlay_manager *lcd2_mgr = NULL; 698 struct omap_overlay_manager *mgr = NULL; 699 700 lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); 701 tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); 702 if (dss_has_feature(FEAT_MGR_LCD2)) 703 lcd2_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD2); 704 705 if (dssdev->channel == OMAP_DSS_CHANNEL_LCD2) { 706 if (!lcd2_mgr->device || force) { 707 if (lcd2_mgr->device) 708 lcd2_mgr->unset_device(lcd2_mgr); 709 lcd2_mgr->set_device(lcd2_mgr, dssdev); 710 mgr = lcd2_mgr; 711 } 712 } else if (dssdev->type != OMAP_DISPLAY_TYPE_VENC 713 && dssdev->type != OMAP_DISPLAY_TYPE_HDMI) { 714 if (!lcd_mgr->device || force) { 715 if (lcd_mgr->device) 716 lcd_mgr->unset_device(lcd_mgr); 717 lcd_mgr->set_device(lcd_mgr, dssdev); 718 mgr = lcd_mgr; 719 } 720 } 721 722 if (dssdev->type == OMAP_DISPLAY_TYPE_VENC 723 || dssdev->type == OMAP_DISPLAY_TYPE_HDMI) { 724 if (!tv_mgr->device || force) { 725 if (tv_mgr->device) 726 tv_mgr->unset_device(tv_mgr); 727 tv_mgr->set_device(tv_mgr, dssdev); 728 mgr = tv_mgr; 729 } 730 } 731 732 if (mgr) { 733 dispc_runtime_get(); 734 735 for (i = 0; i < dss_feat_get_num_ovls(); i++) { 736 struct omap_overlay *ovl; 737 ovl = omap_dss_get_overlay(i); 738 if (!ovl->manager || force) { 739 if (ovl->manager) 740 omap_dss_unset_manager(ovl); 741 omap_dss_set_manager(ovl, mgr); 742 } 743 } 744 745 dispc_runtime_put(); 746 } 747} 748 749void dss_uninit_overlays(struct platform_device *pdev) 750{ 751 struct omap_overlay *ovl; 752 753 while (!list_empty(&overlay_list)) { 754 ovl = list_first_entry(&overlay_list, 755 struct omap_overlay, list); 756 list_del(&ovl->list); 757 kobject_del(&ovl->kobj); 758 kobject_put(&ovl->kobj); 759 kfree(ovl); 760 } 761 762 num_overlays = 0; 763} 764