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