linux/drivers/gpu/drm/exynos/exynos_drm_vidi.c
<<
>>
Prefs
   1/* exynos_drm_vidi.c
   2 *
   3 * Copyright (C) 2012 Samsung Electronics Co.Ltd
   4 * Authors:
   5 *      Inki Dae <inki.dae@samsung.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 as published by the
   9 * Free Software Foundation;  either version 2 of the  License, or (at your
  10 * option) any later version.
  11 *
  12 */
  13#include <drm/drmP.h>
  14
  15#include <linux/kernel.h>
  16#include <linux/platform_device.h>
  17#include <linux/component.h>
  18#include <linux/timer.h>
  19
  20#include <drm/exynos_drm.h>
  21
  22#include <drm/drm_edid.h>
  23#include <drm/drm_crtc_helper.h>
  24#include <drm/drm_atomic_helper.h>
  25
  26#include "exynos_drm_drv.h"
  27#include "exynos_drm_crtc.h"
  28#include "exynos_drm_fb.h"
  29#include "exynos_drm_plane.h"
  30#include "exynos_drm_vidi.h"
  31
  32/* VIDI uses fixed refresh rate of 50Hz */
  33#define VIDI_REFRESH_TIME (1000 / 50)
  34
  35/* vidi has totally three virtual windows. */
  36#define WINDOWS_NR              3
  37
  38#define ctx_from_connector(c)   container_of(c, struct vidi_context, \
  39                                        connector)
  40
  41struct vidi_context {
  42        struct drm_encoder              encoder;
  43        struct platform_device          *pdev;
  44        struct drm_device               *drm_dev;
  45        struct exynos_drm_crtc          *crtc;
  46        struct drm_connector            connector;
  47        struct exynos_drm_plane         planes[WINDOWS_NR];
  48        struct edid                     *raw_edid;
  49        unsigned int                    clkdiv;
  50        unsigned int                    connected;
  51        bool                            suspended;
  52        struct timer_list               timer;
  53        struct mutex                    lock;
  54};
  55
  56static inline struct vidi_context *encoder_to_vidi(struct drm_encoder *e)
  57{
  58        return container_of(e, struct vidi_context, encoder);
  59}
  60
  61static const char fake_edid_info[] = {
  62        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
  63        0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
  64        0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd,
  65        0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
  66        0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00,
  67        0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e,
  68        0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
  69        0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
  70        0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
  71        0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
  72        0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1,
  73        0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83,
  74        0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00,
  75        0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c,
  76        0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a,
  77        0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00,
  78        0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
  79        0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
  80        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  81        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  82        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  83        0x00, 0x00, 0x00, 0x06
  84};
  85
  86static const uint32_t formats[] = {
  87        DRM_FORMAT_XRGB8888,
  88        DRM_FORMAT_ARGB8888,
  89        DRM_FORMAT_NV12,
  90};
  91
  92static const enum drm_plane_type vidi_win_types[WINDOWS_NR] = {
  93        DRM_PLANE_TYPE_PRIMARY,
  94        DRM_PLANE_TYPE_OVERLAY,
  95        DRM_PLANE_TYPE_CURSOR,
  96};
  97
  98static int vidi_enable_vblank(struct exynos_drm_crtc *crtc)
  99{
 100        struct vidi_context *ctx = crtc->ctx;
 101
 102        if (ctx->suspended)
 103                return -EPERM;
 104
 105        mod_timer(&ctx->timer,
 106                jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
 107
 108        return 0;
 109}
 110
 111static void vidi_disable_vblank(struct exynos_drm_crtc *crtc)
 112{
 113}
 114
 115static void vidi_update_plane(struct exynos_drm_crtc *crtc,
 116                              struct exynos_drm_plane *plane)
 117{
 118        struct drm_plane_state *state = plane->base.state;
 119        struct vidi_context *ctx = crtc->ctx;
 120        dma_addr_t addr;
 121
 122        if (ctx->suspended)
 123                return;
 124
 125        addr = exynos_drm_fb_dma_addr(state->fb, 0);
 126        DRM_DEBUG_KMS("dma_addr = %pad\n", &addr);
 127}
 128
 129static void vidi_enable(struct exynos_drm_crtc *crtc)
 130{
 131        struct vidi_context *ctx = crtc->ctx;
 132
 133        mutex_lock(&ctx->lock);
 134
 135        ctx->suspended = false;
 136
 137        mutex_unlock(&ctx->lock);
 138
 139        drm_crtc_vblank_on(&crtc->base);
 140}
 141
 142static void vidi_disable(struct exynos_drm_crtc *crtc)
 143{
 144        struct vidi_context *ctx = crtc->ctx;
 145
 146        drm_crtc_vblank_off(&crtc->base);
 147
 148        mutex_lock(&ctx->lock);
 149
 150        ctx->suspended = true;
 151
 152        mutex_unlock(&ctx->lock);
 153}
 154
 155static const struct exynos_drm_crtc_ops vidi_crtc_ops = {
 156        .enable = vidi_enable,
 157        .disable = vidi_disable,
 158        .enable_vblank = vidi_enable_vblank,
 159        .disable_vblank = vidi_disable_vblank,
 160        .update_plane = vidi_update_plane,
 161        .atomic_flush = exynos_crtc_handle_event,
 162};
 163
 164static void vidi_fake_vblank_timer(struct timer_list *t)
 165{
 166        struct vidi_context *ctx = from_timer(ctx, t, timer);
 167
 168        if (drm_crtc_handle_vblank(&ctx->crtc->base))
 169                mod_timer(&ctx->timer,
 170                        jiffies + msecs_to_jiffies(VIDI_REFRESH_TIME) - 1);
 171}
 172
 173static ssize_t vidi_show_connection(struct device *dev,
 174                                struct device_attribute *attr, char *buf)
 175{
 176        struct vidi_context *ctx = dev_get_drvdata(dev);
 177        int rc;
 178
 179        mutex_lock(&ctx->lock);
 180
 181        rc = sprintf(buf, "%d\n", ctx->connected);
 182
 183        mutex_unlock(&ctx->lock);
 184
 185        return rc;
 186}
 187
 188static ssize_t vidi_store_connection(struct device *dev,
 189                                struct device_attribute *attr,
 190                                const char *buf, size_t len)
 191{
 192        struct vidi_context *ctx = dev_get_drvdata(dev);
 193        int ret;
 194
 195        ret = kstrtoint(buf, 0, &ctx->connected);
 196        if (ret)
 197                return ret;
 198
 199        if (ctx->connected > 1)
 200                return -EINVAL;
 201
 202        /* use fake edid data for test. */
 203        if (!ctx->raw_edid)
 204                ctx->raw_edid = (struct edid *)fake_edid_info;
 205
 206        /* if raw_edid isn't same as fake data then it can't be tested. */
 207        if (ctx->raw_edid != (struct edid *)fake_edid_info) {
 208                DRM_DEBUG_KMS("edid data is not fake data.\n");
 209                return -EINVAL;
 210        }
 211
 212        DRM_DEBUG_KMS("requested connection.\n");
 213
 214        drm_helper_hpd_irq_event(ctx->drm_dev);
 215
 216        return len;
 217}
 218
 219static DEVICE_ATTR(connection, 0644, vidi_show_connection,
 220                        vidi_store_connection);
 221
 222int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
 223                                struct drm_file *file_priv)
 224{
 225        struct vidi_context *ctx = dev_get_drvdata(drm_dev->dev);
 226        struct drm_exynos_vidi_connection *vidi = data;
 227
 228        if (!vidi) {
 229                DRM_DEBUG_KMS("user data for vidi is null.\n");
 230                return -EINVAL;
 231        }
 232
 233        if (vidi->connection > 1) {
 234                DRM_DEBUG_KMS("connection should be 0 or 1.\n");
 235                return -EINVAL;
 236        }
 237
 238        if (ctx->connected == vidi->connection) {
 239                DRM_DEBUG_KMS("same connection request.\n");
 240                return -EINVAL;
 241        }
 242
 243        if (vidi->connection) {
 244                struct edid *raw_edid;
 245
 246                raw_edid = (struct edid *)(unsigned long)vidi->edid;
 247                if (!drm_edid_is_valid(raw_edid)) {
 248                        DRM_DEBUG_KMS("edid data is invalid.\n");
 249                        return -EINVAL;
 250                }
 251                ctx->raw_edid = drm_edid_duplicate(raw_edid);
 252                if (!ctx->raw_edid) {
 253                        DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
 254                        return -ENOMEM;
 255                }
 256        } else {
 257                /*
 258                 * with connection = 0, free raw_edid
 259                 * only if raw edid data isn't same as fake data.
 260                 */
 261                if (ctx->raw_edid && ctx->raw_edid !=
 262                                (struct edid *)fake_edid_info) {
 263                        kfree(ctx->raw_edid);
 264                        ctx->raw_edid = NULL;
 265                }
 266        }
 267
 268        ctx->connected = vidi->connection;
 269        drm_helper_hpd_irq_event(ctx->drm_dev);
 270
 271        return 0;
 272}
 273
 274static enum drm_connector_status vidi_detect(struct drm_connector *connector,
 275                        bool force)
 276{
 277        struct vidi_context *ctx = ctx_from_connector(connector);
 278
 279        /*
 280         * connection request would come from user side
 281         * to do hotplug through specific ioctl.
 282         */
 283        return ctx->connected ? connector_status_connected :
 284                        connector_status_disconnected;
 285}
 286
 287static void vidi_connector_destroy(struct drm_connector *connector)
 288{
 289}
 290
 291static const struct drm_connector_funcs vidi_connector_funcs = {
 292        .fill_modes = drm_helper_probe_single_connector_modes,
 293        .detect = vidi_detect,
 294        .destroy = vidi_connector_destroy,
 295        .reset = drm_atomic_helper_connector_reset,
 296        .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 297        .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 298};
 299
 300static int vidi_get_modes(struct drm_connector *connector)
 301{
 302        struct vidi_context *ctx = ctx_from_connector(connector);
 303        struct edid *edid;
 304        int edid_len;
 305
 306        /*
 307         * the edid data comes from user side and it would be set
 308         * to ctx->raw_edid through specific ioctl.
 309         */
 310        if (!ctx->raw_edid) {
 311                DRM_DEBUG_KMS("raw_edid is null.\n");
 312                return -EFAULT;
 313        }
 314
 315        edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH;
 316        edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);
 317        if (!edid) {
 318                DRM_DEBUG_KMS("failed to allocate edid\n");
 319                return -ENOMEM;
 320        }
 321
 322        drm_mode_connector_update_edid_property(connector, edid);
 323
 324        return drm_add_edid_modes(connector, edid);
 325}
 326
 327static const struct drm_connector_helper_funcs vidi_connector_helper_funcs = {
 328        .get_modes = vidi_get_modes,
 329};
 330
 331static int vidi_create_connector(struct drm_encoder *encoder)
 332{
 333        struct vidi_context *ctx = encoder_to_vidi(encoder);
 334        struct drm_connector *connector = &ctx->connector;
 335        int ret;
 336
 337        connector->polled = DRM_CONNECTOR_POLL_HPD;
 338
 339        ret = drm_connector_init(ctx->drm_dev, connector,
 340                        &vidi_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL);
 341        if (ret) {
 342                DRM_ERROR("Failed to initialize connector with drm\n");
 343                return ret;
 344        }
 345
 346        drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
 347        drm_mode_connector_attach_encoder(connector, encoder);
 348
 349        return 0;
 350}
 351
 352static void exynos_vidi_mode_set(struct drm_encoder *encoder,
 353                               struct drm_display_mode *mode,
 354                               struct drm_display_mode *adjusted_mode)
 355{
 356}
 357
 358static void exynos_vidi_enable(struct drm_encoder *encoder)
 359{
 360}
 361
 362static void exynos_vidi_disable(struct drm_encoder *encoder)
 363{
 364}
 365
 366static const struct drm_encoder_helper_funcs exynos_vidi_encoder_helper_funcs = {
 367        .mode_set = exynos_vidi_mode_set,
 368        .enable = exynos_vidi_enable,
 369        .disable = exynos_vidi_disable,
 370};
 371
 372static const struct drm_encoder_funcs exynos_vidi_encoder_funcs = {
 373        .destroy = drm_encoder_cleanup,
 374};
 375
 376static int vidi_bind(struct device *dev, struct device *master, void *data)
 377{
 378        struct vidi_context *ctx = dev_get_drvdata(dev);
 379        struct drm_device *drm_dev = data;
 380        struct drm_encoder *encoder = &ctx->encoder;
 381        struct exynos_drm_plane *exynos_plane;
 382        struct exynos_drm_plane_config plane_config = { 0 };
 383        unsigned int i;
 384        int ret;
 385
 386        ctx->drm_dev = drm_dev;
 387
 388        plane_config.pixel_formats = formats;
 389        plane_config.num_pixel_formats = ARRAY_SIZE(formats);
 390
 391        for (i = 0; i < WINDOWS_NR; i++) {
 392                plane_config.zpos = i;
 393                plane_config.type = vidi_win_types[i];
 394
 395                ret = exynos_plane_init(drm_dev, &ctx->planes[i], i,
 396                                        &plane_config);
 397                if (ret)
 398                        return ret;
 399        }
 400
 401        exynos_plane = &ctx->planes[DEFAULT_WIN];
 402        ctx->crtc = exynos_drm_crtc_create(drm_dev, &exynos_plane->base,
 403                        EXYNOS_DISPLAY_TYPE_VIDI, &vidi_crtc_ops, ctx);
 404        if (IS_ERR(ctx->crtc)) {
 405                DRM_ERROR("failed to create crtc.\n");
 406                return PTR_ERR(ctx->crtc);
 407        }
 408
 409        drm_encoder_init(drm_dev, encoder, &exynos_vidi_encoder_funcs,
 410                         DRM_MODE_ENCODER_TMDS, NULL);
 411
 412        drm_encoder_helper_add(encoder, &exynos_vidi_encoder_helper_funcs);
 413
 414        ret = exynos_drm_set_possible_crtcs(encoder, EXYNOS_DISPLAY_TYPE_VIDI);
 415        if (ret < 0)
 416                return ret;
 417
 418        ret = vidi_create_connector(encoder);
 419        if (ret) {
 420                DRM_ERROR("failed to create connector ret = %d\n", ret);
 421                drm_encoder_cleanup(encoder);
 422                return ret;
 423        }
 424
 425        return 0;
 426}
 427
 428
 429static void vidi_unbind(struct device *dev, struct device *master, void *data)
 430{
 431        struct vidi_context *ctx = dev_get_drvdata(dev);
 432
 433        del_timer_sync(&ctx->timer);
 434}
 435
 436static const struct component_ops vidi_component_ops = {
 437        .bind   = vidi_bind,
 438        .unbind = vidi_unbind,
 439};
 440
 441static int vidi_probe(struct platform_device *pdev)
 442{
 443        struct vidi_context *ctx;
 444        int ret;
 445
 446        ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
 447        if (!ctx)
 448                return -ENOMEM;
 449
 450        ctx->pdev = pdev;
 451
 452        timer_setup(&ctx->timer, vidi_fake_vblank_timer, 0);
 453
 454        mutex_init(&ctx->lock);
 455
 456        platform_set_drvdata(pdev, ctx);
 457
 458        ret = device_create_file(&pdev->dev, &dev_attr_connection);
 459        if (ret < 0) {
 460                DRM_ERROR("failed to create connection sysfs.\n");
 461                return ret;
 462        }
 463
 464        ret = component_add(&pdev->dev, &vidi_component_ops);
 465        if (ret)
 466                goto err_remove_file;
 467
 468        return ret;
 469
 470err_remove_file:
 471        device_remove_file(&pdev->dev, &dev_attr_connection);
 472
 473        return ret;
 474}
 475
 476static int vidi_remove(struct platform_device *pdev)
 477{
 478        struct vidi_context *ctx = platform_get_drvdata(pdev);
 479
 480        if (ctx->raw_edid != (struct edid *)fake_edid_info) {
 481                kfree(ctx->raw_edid);
 482                ctx->raw_edid = NULL;
 483
 484                return -EINVAL;
 485        }
 486
 487        component_del(&pdev->dev, &vidi_component_ops);
 488
 489        return 0;
 490}
 491
 492struct platform_driver vidi_driver = {
 493        .probe          = vidi_probe,
 494        .remove         = vidi_remove,
 495        .driver         = {
 496                .name   = "exynos-drm-vidi",
 497                .owner  = THIS_MODULE,
 498        },
 499};
 500