linux/drivers/gpu/drm/omapdrm/omap_crtc.c
<<
>>
Prefs
   1/*
   2 * drivers/gpu/drm/omapdrm/omap_crtc.c
   3 *
   4 * Copyright (C) 2011 Texas Instruments
   5 * Author: Rob Clark <rob@ti.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License version 2 as published by
   9 * the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope that it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 *
  16 * You should have received a copy of the GNU General Public License along with
  17 * this program.  If not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include <drm/drm_atomic.h>
  21#include <drm/drm_atomic_helper.h>
  22#include <drm/drm_crtc.h>
  23#include <drm/drm_crtc_helper.h>
  24#include <drm/drm_mode.h>
  25#include <drm/drm_plane_helper.h>
  26
  27#include "omap_drv.h"
  28
  29#define to_omap_crtc(x) container_of(x, struct omap_crtc, base)
  30
  31struct omap_crtc {
  32        struct drm_crtc base;
  33
  34        const char *name;
  35        enum omap_channel channel;
  36
  37        struct omap_video_timings timings;
  38
  39        struct omap_drm_irq vblank_irq;
  40        struct omap_drm_irq error_irq;
  41
  42        bool ignore_digit_sync_lost;
  43
  44        bool pending;
  45        wait_queue_head_t pending_wait;
  46};
  47
  48/* -----------------------------------------------------------------------------
  49 * Helper Functions
  50 */
  51
  52uint32_t pipe2vbl(struct drm_crtc *crtc)
  53{
  54        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
  55
  56        return dispc_mgr_get_vsync_irq(omap_crtc->channel);
  57}
  58
  59struct omap_video_timings *omap_crtc_timings(struct drm_crtc *crtc)
  60{
  61        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
  62        return &omap_crtc->timings;
  63}
  64
  65enum omap_channel omap_crtc_channel(struct drm_crtc *crtc)
  66{
  67        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
  68        return omap_crtc->channel;
  69}
  70
  71int omap_crtc_wait_pending(struct drm_crtc *crtc)
  72{
  73        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
  74
  75        /*
  76         * Timeout is set to a "sufficiently" high value, which should cover
  77         * a single frame refresh even on slower displays.
  78         */
  79        return wait_event_timeout(omap_crtc->pending_wait,
  80                                  !omap_crtc->pending,
  81                                  msecs_to_jiffies(250));
  82}
  83
  84/* -----------------------------------------------------------------------------
  85 * DSS Manager Functions
  86 */
  87
  88/*
  89 * Manager-ops, callbacks from output when they need to configure
  90 * the upstream part of the video pipe.
  91 *
  92 * Most of these we can ignore until we add support for command-mode
  93 * panels.. for video-mode the crtc-helpers already do an adequate
  94 * job of sequencing the setup of the video pipe in the proper order
  95 */
  96
  97/* ovl-mgr-id -> crtc */
  98static struct omap_crtc *omap_crtcs[8];
  99static struct omap_dss_device *omap_crtc_output[8];
 100
 101/* we can probably ignore these until we support command-mode panels: */
 102static int omap_crtc_dss_connect(enum omap_channel channel,
 103                struct omap_dss_device *dst)
 104{
 105        if (omap_crtc_output[channel])
 106                return -EINVAL;
 107
 108        if ((dispc_mgr_get_supported_outputs(channel) & dst->id) == 0)
 109                return -EINVAL;
 110
 111        omap_crtc_output[channel] = dst;
 112        dst->dispc_channel_connected = true;
 113
 114        return 0;
 115}
 116
 117static void omap_crtc_dss_disconnect(enum omap_channel channel,
 118                struct omap_dss_device *dst)
 119{
 120        omap_crtc_output[channel] = NULL;
 121        dst->dispc_channel_connected = false;
 122}
 123
 124static void omap_crtc_dss_start_update(enum omap_channel channel)
 125{
 126}
 127
 128/* Called only from the encoder enable/disable and suspend/resume handlers. */
 129static void omap_crtc_set_enabled(struct drm_crtc *crtc, bool enable)
 130{
 131        struct drm_device *dev = crtc->dev;
 132        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 133        enum omap_channel channel = omap_crtc->channel;
 134        struct omap_irq_wait *wait;
 135        u32 framedone_irq, vsync_irq;
 136        int ret;
 137
 138        if (omap_crtc_output[channel]->output_type == OMAP_DISPLAY_TYPE_HDMI) {
 139                dispc_mgr_enable(channel, enable);
 140                return;
 141        }
 142
 143        if (dispc_mgr_is_enabled(channel) == enable)
 144                return;
 145
 146        if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
 147                /*
 148                 * Digit output produces some sync lost interrupts during the
 149                 * first frame when enabling, so we need to ignore those.
 150                 */
 151                omap_crtc->ignore_digit_sync_lost = true;
 152        }
 153
 154        framedone_irq = dispc_mgr_get_framedone_irq(channel);
 155        vsync_irq = dispc_mgr_get_vsync_irq(channel);
 156
 157        if (enable) {
 158                wait = omap_irq_wait_init(dev, vsync_irq, 1);
 159        } else {
 160                /*
 161                 * When we disable the digit output, we need to wait for
 162                 * FRAMEDONE to know that DISPC has finished with the output.
 163                 *
 164                 * OMAP2/3 does not have FRAMEDONE irq for digit output, and in
 165                 * that case we need to use vsync interrupt, and wait for both
 166                 * even and odd frames.
 167                 */
 168
 169                if (framedone_irq)
 170                        wait = omap_irq_wait_init(dev, framedone_irq, 1);
 171                else
 172                        wait = omap_irq_wait_init(dev, vsync_irq, 2);
 173        }
 174
 175        dispc_mgr_enable(channel, enable);
 176
 177        ret = omap_irq_wait(dev, wait, msecs_to_jiffies(100));
 178        if (ret) {
 179                dev_err(dev->dev, "%s: timeout waiting for %s\n",
 180                                omap_crtc->name, enable ? "enable" : "disable");
 181        }
 182
 183        if (omap_crtc->channel == OMAP_DSS_CHANNEL_DIGIT) {
 184                omap_crtc->ignore_digit_sync_lost = false;
 185                /* make sure the irq handler sees the value above */
 186                mb();
 187        }
 188}
 189
 190
 191static int omap_crtc_dss_enable(enum omap_channel channel)
 192{
 193        struct omap_crtc *omap_crtc = omap_crtcs[channel];
 194        struct omap_overlay_manager_info info;
 195
 196        memset(&info, 0, sizeof(info));
 197        info.default_color = 0x00000000;
 198        info.trans_key = 0x00000000;
 199        info.trans_key_type = OMAP_DSS_COLOR_KEY_GFX_DST;
 200        info.trans_enabled = false;
 201
 202        dispc_mgr_setup(omap_crtc->channel, &info);
 203        dispc_mgr_set_timings(omap_crtc->channel,
 204                        &omap_crtc->timings);
 205        omap_crtc_set_enabled(&omap_crtc->base, true);
 206
 207        return 0;
 208}
 209
 210static void omap_crtc_dss_disable(enum omap_channel channel)
 211{
 212        struct omap_crtc *omap_crtc = omap_crtcs[channel];
 213
 214        omap_crtc_set_enabled(&omap_crtc->base, false);
 215}
 216
 217static void omap_crtc_dss_set_timings(enum omap_channel channel,
 218                const struct omap_video_timings *timings)
 219{
 220        struct omap_crtc *omap_crtc = omap_crtcs[channel];
 221        DBG("%s", omap_crtc->name);
 222        omap_crtc->timings = *timings;
 223}
 224
 225static void omap_crtc_dss_set_lcd_config(enum omap_channel channel,
 226                const struct dss_lcd_mgr_config *config)
 227{
 228        struct omap_crtc *omap_crtc = omap_crtcs[channel];
 229        DBG("%s", omap_crtc->name);
 230        dispc_mgr_set_lcd_config(omap_crtc->channel, config);
 231}
 232
 233static int omap_crtc_dss_register_framedone(
 234                enum omap_channel channel,
 235                void (*handler)(void *), void *data)
 236{
 237        return 0;
 238}
 239
 240static void omap_crtc_dss_unregister_framedone(
 241                enum omap_channel channel,
 242                void (*handler)(void *), void *data)
 243{
 244}
 245
 246static const struct dss_mgr_ops mgr_ops = {
 247        .connect = omap_crtc_dss_connect,
 248        .disconnect = omap_crtc_dss_disconnect,
 249        .start_update = omap_crtc_dss_start_update,
 250        .enable = omap_crtc_dss_enable,
 251        .disable = omap_crtc_dss_disable,
 252        .set_timings = omap_crtc_dss_set_timings,
 253        .set_lcd_config = omap_crtc_dss_set_lcd_config,
 254        .register_framedone_handler = omap_crtc_dss_register_framedone,
 255        .unregister_framedone_handler = omap_crtc_dss_unregister_framedone,
 256};
 257
 258/* -----------------------------------------------------------------------------
 259 * Setup, Flush and Page Flip
 260 */
 261
 262static void omap_crtc_complete_page_flip(struct drm_crtc *crtc)
 263{
 264        struct drm_pending_vblank_event *event;
 265        struct drm_device *dev = crtc->dev;
 266        unsigned long flags;
 267
 268        event = crtc->state->event;
 269
 270        if (!event)
 271                return;
 272
 273        spin_lock_irqsave(&dev->event_lock, flags);
 274        drm_crtc_send_vblank_event(crtc, event);
 275        spin_unlock_irqrestore(&dev->event_lock, flags);
 276}
 277
 278static void omap_crtc_error_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 279{
 280        struct omap_crtc *omap_crtc =
 281                        container_of(irq, struct omap_crtc, error_irq);
 282
 283        if (omap_crtc->ignore_digit_sync_lost) {
 284                irqstatus &= ~DISPC_IRQ_SYNC_LOST_DIGIT;
 285                if (!irqstatus)
 286                        return;
 287        }
 288
 289        DRM_ERROR_RATELIMITED("%s: errors: %08x\n", omap_crtc->name, irqstatus);
 290}
 291
 292static void omap_crtc_vblank_irq(struct omap_drm_irq *irq, uint32_t irqstatus)
 293{
 294        struct omap_crtc *omap_crtc =
 295                        container_of(irq, struct omap_crtc, vblank_irq);
 296        struct drm_device *dev = omap_crtc->base.dev;
 297
 298        if (dispc_mgr_go_busy(omap_crtc->channel))
 299                return;
 300
 301        DBG("%s: apply done", omap_crtc->name);
 302
 303        __omap_irq_unregister(dev, &omap_crtc->vblank_irq);
 304
 305        rmb();
 306        WARN_ON(!omap_crtc->pending);
 307        omap_crtc->pending = false;
 308        wmb();
 309
 310        /* wake up userspace */
 311        omap_crtc_complete_page_flip(&omap_crtc->base);
 312
 313        /* wake up omap_atomic_complete */
 314        wake_up(&omap_crtc->pending_wait);
 315}
 316
 317/* -----------------------------------------------------------------------------
 318 * CRTC Functions
 319 */
 320
 321static void omap_crtc_destroy(struct drm_crtc *crtc)
 322{
 323        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 324
 325        DBG("%s", omap_crtc->name);
 326
 327        WARN_ON(omap_crtc->vblank_irq.registered);
 328        omap_irq_unregister(crtc->dev, &omap_crtc->error_irq);
 329
 330        drm_crtc_cleanup(crtc);
 331
 332        kfree(omap_crtc);
 333}
 334
 335static void omap_crtc_enable(struct drm_crtc *crtc)
 336{
 337        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 338
 339        DBG("%s", omap_crtc->name);
 340
 341        rmb();
 342        WARN_ON(omap_crtc->pending);
 343        omap_crtc->pending = true;
 344        wmb();
 345
 346        omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
 347
 348        drm_crtc_vblank_on(crtc);
 349}
 350
 351static void omap_crtc_disable(struct drm_crtc *crtc)
 352{
 353        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 354
 355        DBG("%s", omap_crtc->name);
 356
 357        drm_crtc_vblank_off(crtc);
 358}
 359
 360static void omap_crtc_mode_set_nofb(struct drm_crtc *crtc)
 361{
 362        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 363        struct drm_display_mode *mode = &crtc->state->adjusted_mode;
 364
 365        DBG("%s: set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x",
 366            omap_crtc->name, mode->base.id, mode->name,
 367            mode->vrefresh, mode->clock,
 368            mode->hdisplay, mode->hsync_start, mode->hsync_end, mode->htotal,
 369            mode->vdisplay, mode->vsync_start, mode->vsync_end, mode->vtotal,
 370            mode->type, mode->flags);
 371
 372        copy_timings_drm_to_omap(&omap_crtc->timings, mode);
 373}
 374
 375static void omap_crtc_atomic_begin(struct drm_crtc *crtc,
 376                                  struct drm_crtc_state *old_crtc_state)
 377{
 378}
 379
 380static void omap_crtc_atomic_flush(struct drm_crtc *crtc,
 381                                  struct drm_crtc_state *old_crtc_state)
 382{
 383        struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
 384
 385        WARN_ON(omap_crtc->vblank_irq.registered);
 386
 387        if (dispc_mgr_is_enabled(omap_crtc->channel)) {
 388
 389                DBG("%s: GO", omap_crtc->name);
 390
 391                rmb();
 392                WARN_ON(omap_crtc->pending);
 393                omap_crtc->pending = true;
 394                wmb();
 395
 396                dispc_mgr_go(omap_crtc->channel);
 397                omap_irq_register(crtc->dev, &omap_crtc->vblank_irq);
 398        }
 399}
 400
 401static bool omap_crtc_is_plane_prop(struct drm_device *dev,
 402        struct drm_property *property)
 403{
 404        struct omap_drm_private *priv = dev->dev_private;
 405
 406        return property == priv->zorder_prop ||
 407                property == dev->mode_config.rotation_property;
 408}
 409
 410static int omap_crtc_atomic_set_property(struct drm_crtc *crtc,
 411                                         struct drm_crtc_state *state,
 412                                         struct drm_property *property,
 413                                         uint64_t val)
 414{
 415        struct drm_device *dev = crtc->dev;
 416
 417        if (omap_crtc_is_plane_prop(dev, property)) {
 418                struct drm_plane_state *plane_state;
 419                struct drm_plane *plane = crtc->primary;
 420
 421                /*
 422                 * Delegate property set to the primary plane. Get the plane
 423                 * state and set the property directly.
 424                 */
 425
 426                plane_state = drm_atomic_get_plane_state(state->state, plane);
 427                if (IS_ERR(plane_state))
 428                        return PTR_ERR(plane_state);
 429
 430                return drm_atomic_plane_set_property(plane, plane_state,
 431                                property, val);
 432        }
 433
 434        return -EINVAL;
 435}
 436
 437static int omap_crtc_atomic_get_property(struct drm_crtc *crtc,
 438                                         const struct drm_crtc_state *state,
 439                                         struct drm_property *property,
 440                                         uint64_t *val)
 441{
 442        struct drm_device *dev = crtc->dev;
 443
 444        if (omap_crtc_is_plane_prop(dev, property)) {
 445                /*
 446                 * Delegate property get to the primary plane. The
 447                 * drm_atomic_plane_get_property() function isn't exported, but
 448                 * can be called through drm_object_property_get_value() as that
 449                 * will call drm_atomic_get_property() for atomic drivers.
 450                 */
 451                return drm_object_property_get_value(&crtc->primary->base,
 452                                property, val);
 453        }
 454
 455        return -EINVAL;
 456}
 457
 458static const struct drm_crtc_funcs omap_crtc_funcs = {
 459        .reset = drm_atomic_helper_crtc_reset,
 460        .set_config = drm_atomic_helper_set_config,
 461        .destroy = omap_crtc_destroy,
 462        .page_flip = drm_atomic_helper_page_flip,
 463        .set_property = drm_atomic_helper_crtc_set_property,
 464        .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
 465        .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
 466        .atomic_set_property = omap_crtc_atomic_set_property,
 467        .atomic_get_property = omap_crtc_atomic_get_property,
 468};
 469
 470static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
 471        .mode_set_nofb = omap_crtc_mode_set_nofb,
 472        .disable = omap_crtc_disable,
 473        .enable = omap_crtc_enable,
 474        .atomic_begin = omap_crtc_atomic_begin,
 475        .atomic_flush = omap_crtc_atomic_flush,
 476};
 477
 478/* -----------------------------------------------------------------------------
 479 * Init and Cleanup
 480 */
 481
 482static const char *channel_names[] = {
 483        [OMAP_DSS_CHANNEL_LCD] = "lcd",
 484        [OMAP_DSS_CHANNEL_DIGIT] = "tv",
 485        [OMAP_DSS_CHANNEL_LCD2] = "lcd2",
 486        [OMAP_DSS_CHANNEL_LCD3] = "lcd3",
 487};
 488
 489void omap_crtc_pre_init(void)
 490{
 491        dss_install_mgr_ops(&mgr_ops);
 492}
 493
 494void omap_crtc_pre_uninit(void)
 495{
 496        dss_uninstall_mgr_ops();
 497}
 498
 499/* initialize crtc */
 500struct drm_crtc *omap_crtc_init(struct drm_device *dev,
 501                struct drm_plane *plane, enum omap_channel channel, int id)
 502{
 503        struct drm_crtc *crtc = NULL;
 504        struct omap_crtc *omap_crtc;
 505        int ret;
 506
 507        DBG("%s", channel_names[channel]);
 508
 509        omap_crtc = kzalloc(sizeof(*omap_crtc), GFP_KERNEL);
 510        if (!omap_crtc)
 511                return NULL;
 512
 513        crtc = &omap_crtc->base;
 514
 515        init_waitqueue_head(&omap_crtc->pending_wait);
 516
 517        omap_crtc->channel = channel;
 518        omap_crtc->name = channel_names[channel];
 519
 520        omap_crtc->vblank_irq.irqmask = pipe2vbl(crtc);
 521        omap_crtc->vblank_irq.irq = omap_crtc_vblank_irq;
 522
 523        omap_crtc->error_irq.irqmask =
 524                        dispc_mgr_get_sync_lost_irq(channel);
 525        omap_crtc->error_irq.irq = omap_crtc_error_irq;
 526        omap_irq_register(dev, &omap_crtc->error_irq);
 527
 528        ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
 529                                        &omap_crtc_funcs, NULL);
 530        if (ret < 0) {
 531                kfree(omap_crtc);
 532                return NULL;
 533        }
 534
 535        drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
 536
 537        omap_plane_install_properties(crtc->primary, &crtc->base);
 538
 539        omap_crtcs[channel] = omap_crtc;
 540
 541        return crtc;
 542}
 543