linux/drivers/video/omap2/dss/overlay.c
<<
>>
Prefs
   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 (strncmp(buf, mgr->name, len) == 0)
  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        /* XXX: on manual update display, in auto update mode, a bug happens
 452         * here. When an overlay is first enabled on LCD, then it's disabled,
 453         * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT
 454         * errors. Waiting before changing the channel_out fixes it. I'm
 455         * guessing that the overlay is still somehow being used for the LCD,
 456         * but I don't understand how or why. */
 457        msleep(40);
 458        dispc_set_channel_out(ovl->id, mgr->id);
 459        dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1);
 460
 461        return 0;
 462}
 463
 464static int omap_dss_unset_manager(struct omap_overlay *ovl)
 465{
 466        int r;
 467
 468        if (!ovl->manager) {
 469                DSSERR("failed to detach overlay: manager not set\n");
 470                return -EINVAL;
 471        }
 472
 473        if (ovl->info.enabled) {
 474                DSSERR("overlay has to be disabled to unset the manager\n");
 475                return -EINVAL;
 476        }
 477
 478        r = ovl->wait_for_go(ovl);
 479        if (r)
 480                return r;
 481
 482        ovl->manager = NULL;
 483
 484        return 0;
 485}
 486
 487int omap_dss_get_num_overlays(void)
 488{
 489        return num_overlays;
 490}
 491EXPORT_SYMBOL(omap_dss_get_num_overlays);
 492
 493struct omap_overlay *omap_dss_get_overlay(int num)
 494{
 495        int i = 0;
 496        struct omap_overlay *ovl;
 497
 498        list_for_each_entry(ovl, &overlay_list, list) {
 499                if (i++ == num)
 500                        return ovl;
 501        }
 502
 503        return NULL;
 504}
 505EXPORT_SYMBOL(omap_dss_get_overlay);
 506
 507static void omap_dss_add_overlay(struct omap_overlay *overlay)
 508{
 509        ++num_overlays;
 510        list_add_tail(&overlay->list, &overlay_list);
 511}
 512
 513static struct omap_overlay *dispc_overlays[3];
 514
 515void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr)
 516{
 517        mgr->num_overlays = 3;
 518        mgr->overlays = dispc_overlays;
 519}
 520
 521#ifdef L4_EXAMPLE
 522static struct omap_overlay *l4_overlays[1];
 523void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr)
 524{
 525        mgr->num_overlays = 1;
 526        mgr->overlays = l4_overlays;
 527}
 528#endif
 529
 530void dss_init_overlays(struct platform_device *pdev)
 531{
 532        int i, r;
 533
 534        INIT_LIST_HEAD(&overlay_list);
 535
 536        num_overlays = 0;
 537
 538        for (i = 0; i < 3; ++i) {
 539                struct omap_overlay *ovl;
 540                ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
 541
 542                BUG_ON(ovl == NULL);
 543
 544                switch (i) {
 545                case 0:
 546                        ovl->name = "gfx";
 547                        ovl->id = OMAP_DSS_GFX;
 548                        ovl->supported_modes = cpu_is_omap34xx() ?
 549                                OMAP_DSS_COLOR_GFX_OMAP3 :
 550                                OMAP_DSS_COLOR_GFX_OMAP2;
 551                        ovl->caps = OMAP_DSS_OVL_CAP_DISPC;
 552                        ovl->info.global_alpha = 255;
 553                        break;
 554                case 1:
 555                        ovl->name = "vid1";
 556                        ovl->id = OMAP_DSS_VIDEO1;
 557                        ovl->supported_modes = cpu_is_omap34xx() ?
 558                                OMAP_DSS_COLOR_VID1_OMAP3 :
 559                                OMAP_DSS_COLOR_VID_OMAP2;
 560                        ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
 561                                OMAP_DSS_OVL_CAP_DISPC;
 562                        ovl->info.global_alpha = 255;
 563                        break;
 564                case 2:
 565                        ovl->name = "vid2";
 566                        ovl->id = OMAP_DSS_VIDEO2;
 567                        ovl->supported_modes = cpu_is_omap34xx() ?
 568                                OMAP_DSS_COLOR_VID2_OMAP3 :
 569                                OMAP_DSS_COLOR_VID_OMAP2;
 570                        ovl->caps = OMAP_DSS_OVL_CAP_SCALE |
 571                                OMAP_DSS_OVL_CAP_DISPC;
 572                        ovl->info.global_alpha = 255;
 573                        break;
 574                }
 575
 576                ovl->set_manager = &omap_dss_set_manager;
 577                ovl->unset_manager = &omap_dss_unset_manager;
 578                ovl->set_overlay_info = &dss_ovl_set_overlay_info;
 579                ovl->get_overlay_info = &dss_ovl_get_overlay_info;
 580                ovl->wait_for_go = &dss_ovl_wait_for_go;
 581
 582                omap_dss_add_overlay(ovl);
 583
 584                r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
 585                                &pdev->dev.kobj, "overlay%d", i);
 586
 587                if (r) {
 588                        DSSERR("failed to create sysfs file\n");
 589                        continue;
 590                }
 591
 592                dispc_overlays[i] = ovl;
 593        }
 594
 595#ifdef L4_EXAMPLE
 596        {
 597                struct omap_overlay *ovl;
 598                ovl = kzalloc(sizeof(*ovl), GFP_KERNEL);
 599
 600                BUG_ON(ovl == NULL);
 601
 602                ovl->name = "l4";
 603                ovl->supported_modes = OMAP_DSS_COLOR_RGB24U;
 604
 605                ovl->set_manager = &omap_dss_set_manager;
 606                ovl->unset_manager = &omap_dss_unset_manager;
 607                ovl->set_overlay_info = &dss_ovl_set_overlay_info;
 608                ovl->get_overlay_info = &dss_ovl_get_overlay_info;
 609
 610                omap_dss_add_overlay(ovl);
 611
 612                r = kobject_init_and_add(&ovl->kobj, &overlay_ktype,
 613                                &pdev->dev.kobj, "overlayl4");
 614
 615                if (r)
 616                        DSSERR("failed to create sysfs file\n");
 617
 618                l4_overlays[0] = ovl;
 619        }
 620#endif
 621}
 622
 623/* connect overlays to the new device, if not already connected. if force
 624 * selected, connect always. */
 625void dss_recheck_connections(struct omap_dss_device *dssdev, bool force)
 626{
 627        int i;
 628        struct omap_overlay_manager *lcd_mgr;
 629        struct omap_overlay_manager *tv_mgr;
 630        struct omap_overlay_manager *mgr = NULL;
 631
 632        lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD);
 633        tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV);
 634
 635        if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) {
 636                if (!lcd_mgr->device || force) {
 637                        if (lcd_mgr->device)
 638                                lcd_mgr->unset_device(lcd_mgr);
 639                        lcd_mgr->set_device(lcd_mgr, dssdev);
 640                        mgr = lcd_mgr;
 641                }
 642        }
 643
 644        if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) {
 645                if (!tv_mgr->device || force) {
 646                        if (tv_mgr->device)
 647                                tv_mgr->unset_device(tv_mgr);
 648                        tv_mgr->set_device(tv_mgr, dssdev);
 649                        mgr = tv_mgr;
 650                }
 651        }
 652
 653        if (mgr) {
 654                for (i = 0; i < 3; i++) {
 655                        struct omap_overlay *ovl;
 656                        ovl = omap_dss_get_overlay(i);
 657                        if (!ovl->manager || force) {
 658                                if (ovl->manager)
 659                                        omap_dss_unset_manager(ovl);
 660                                omap_dss_set_manager(ovl, mgr);
 661                        }
 662                }
 663        }
 664}
 665
 666void dss_uninit_overlays(struct platform_device *pdev)
 667{
 668        struct omap_overlay *ovl;
 669
 670        while (!list_empty(&overlay_list)) {
 671                ovl = list_first_entry(&overlay_list,
 672                                struct omap_overlay, list);
 673                list_del(&ovl->list);
 674                kobject_del(&ovl->kobj);
 675                kobject_put(&ovl->kobj);
 676                kfree(ovl);
 677        }
 678
 679        num_overlays = 0;
 680}
 681
 682
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.