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 19 #include <drm/exynos_drm.h> 20 21 #include <drm/drm_edid.h> 22 #include <drm/drm_crtc_helper.h> 23 24 #include "exynos_drm_drv.h" 25 #include "exynos_drm_crtc.h" 26 #include "exynos_drm_encoder.h" 27 #include "exynos_drm_vidi.h" 28 29 /* vidi has totally three virtual windows. */ 30 #define WINDOWS_NR 3 31 32 #define ctx_from_connector(c) container_of(c, struct vidi_context, \ 33 connector) 34 35 struct vidi_win_data { 36 unsigned int offset_x; 37 unsigned int offset_y; 38 unsigned int ovl_width; 39 unsigned int ovl_height; 40 unsigned int fb_width; 41 unsigned int fb_height; 42 unsigned int bpp; 43 dma_addr_t dma_addr; 44 unsigned int buf_offsize; 45 unsigned int line_size; /* bytes */ 46 bool enabled; 47 }; 48 49 struct vidi_context { 50 struct exynos_drm_manager manager; 51 struct exynos_drm_display display; 52 struct platform_device *pdev; 53 struct drm_device *drm_dev; 54 struct drm_crtc *crtc; 55 struct drm_encoder *encoder; 56 struct drm_connector connector; 57 struct vidi_win_data win_data[WINDOWS_NR]; 58 struct edid *raw_edid; 59 unsigned int clkdiv; 60 unsigned int default_win; 61 unsigned long irq_flags; 62 unsigned int connected; 63 bool vblank_on; 64 bool suspended; 65 bool direct_vblank; 66 struct work_struct work; 67 struct mutex lock; 68 int pipe; 69 }; 70 71 static inline struct vidi_context *manager_to_vidi(struct exynos_drm_manager *m) 72 { 73 return container_of(m, struct vidi_context, manager); 74 } 75 76 static inline struct vidi_context *display_to_vidi(struct exynos_drm_display *d) 77 { 78 return container_of(d, struct vidi_context, display); 79 } 80 81 static const char fake_edid_info[] = { 82 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05, 83 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78, 84 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd, 85 0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 86 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00, 87 0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 88 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00, 89 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18, 90 0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 91 0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47, 92 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1, 93 0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83, 94 0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00, 95 0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c, 96 0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a, 97 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00, 98 0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c, 99 0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 100 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 101 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 103 0x00, 0x00, 0x00, 0x06 104 }; 105 106 static void vidi_apply(struct exynos_drm_manager *mgr) 107 { 108 struct vidi_context *ctx = manager_to_vidi(mgr); 109 struct exynos_drm_manager_ops *mgr_ops = mgr->ops; 110 struct vidi_win_data *win_data; 111 int i; 112 113 for (i = 0; i < WINDOWS_NR; i++) { 114 win_data = &ctx->win_data[i]; 115 if (win_data->enabled && (mgr_ops && mgr_ops->win_commit)) 116 mgr_ops->win_commit(mgr, i); 117 } 118 119 if (mgr_ops && mgr_ops->commit) 120 mgr_ops->commit(mgr); 121 } 122 123 static void vidi_commit(struct exynos_drm_manager *mgr) 124 { 125 struct vidi_context *ctx = manager_to_vidi(mgr); 126 127 if (ctx->suspended) 128 return; 129 } 130 131 static int vidi_enable_vblank(struct exynos_drm_manager *mgr) 132 { 133 struct vidi_context *ctx = manager_to_vidi(mgr); 134 135 if (ctx->suspended) 136 return -EPERM; 137 138 if (!test_and_set_bit(0, &ctx->irq_flags)) 139 ctx->vblank_on = true; 140 141 ctx->direct_vblank = true; 142 143 /* 144 * in case of page flip request, vidi_finish_pageflip function 145 * will not be called because direct_vblank is true and then 146 * that function will be called by manager_ops->win_commit callback 147 */ 148 schedule_work(&ctx->work); 149 150 return 0; 151 } 152 153 static void vidi_disable_vblank(struct exynos_drm_manager *mgr) 154 { 155 struct vidi_context *ctx = manager_to_vidi(mgr); 156 157 if (ctx->suspended) 158 return; 159 160 if (test_and_clear_bit(0, &ctx->irq_flags)) 161 ctx->vblank_on = false; 162 } 163 164 static void vidi_win_mode_set(struct exynos_drm_manager *mgr, 165 struct exynos_drm_overlay *overlay) 166 { 167 struct vidi_context *ctx = manager_to_vidi(mgr); 168 struct vidi_win_data *win_data; 169 int win; 170 unsigned long offset; 171 172 if (!overlay) { 173 DRM_ERROR("overlay is NULL\n"); 174 return; 175 } 176 177 win = overlay->zpos; 178 if (win == DEFAULT_ZPOS) 179 win = ctx->default_win; 180 181 if (win < 0 || win >= WINDOWS_NR) 182 return; 183 184 offset = overlay->fb_x * (overlay->bpp >> 3); 185 offset += overlay->fb_y * overlay->pitch; 186 187 DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch); 188 189 win_data = &ctx->win_data[win]; 190 191 win_data->offset_x = overlay->crtc_x; 192 win_data->offset_y = overlay->crtc_y; 193 win_data->ovl_width = overlay->crtc_width; 194 win_data->ovl_height = overlay->crtc_height; 195 win_data->fb_width = overlay->fb_width; 196 win_data->fb_height = overlay->fb_height; 197 win_data->dma_addr = overlay->dma_addr[0] + offset; 198 win_data->bpp = overlay->bpp; 199 win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) * 200 (overlay->bpp >> 3); 201 win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3); 202 203 /* 204 * some parts of win_data should be transferred to user side 205 * through specific ioctl. 206 */ 207 208 DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n", 209 win_data->offset_x, win_data->offset_y); 210 DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n", 211 win_data->ovl_width, win_data->ovl_height); 212 DRM_DEBUG_KMS("paddr = 0x%lx\n", (unsigned long)win_data->dma_addr); 213 DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n", 214 overlay->fb_width, overlay->crtc_width); 215 } 216 217 static void vidi_win_commit(struct exynos_drm_manager *mgr, int zpos) 218 { 219 struct vidi_context *ctx = manager_to_vidi(mgr); 220 struct vidi_win_data *win_data; 221 int win = zpos; 222 223 if (ctx->suspended) 224 return; 225 226 if (win == DEFAULT_ZPOS) 227 win = ctx->default_win; 228 229 if (win < 0 || win >= WINDOWS_NR) 230 return; 231 232 win_data = &ctx->win_data[win]; 233 234 win_data->enabled = true; 235 236 DRM_DEBUG_KMS("dma_addr = %pad\n", &win_data->dma_addr); 237 238 if (ctx->vblank_on) 239 schedule_work(&ctx->work); 240 } 241 242 static void vidi_win_disable(struct exynos_drm_manager *mgr, int zpos) 243 { 244 struct vidi_context *ctx = manager_to_vidi(mgr); 245 struct vidi_win_data *win_data; 246 int win = zpos; 247 248 if (win == DEFAULT_ZPOS) 249 win = ctx->default_win; 250 251 if (win < 0 || win >= WINDOWS_NR) 252 return; 253 254 win_data = &ctx->win_data[win]; 255 win_data->enabled = false; 256 257 /* TODO. */ 258 } 259 260 static int vidi_power_on(struct exynos_drm_manager *mgr, bool enable) 261 { 262 struct vidi_context *ctx = manager_to_vidi(mgr); 263 264 DRM_DEBUG_KMS("%s\n", __FILE__); 265 266 if (enable != false && enable != true) 267 return -EINVAL; 268 269 if (enable) { 270 ctx->suspended = false; 271 272 /* if vblank was enabled status, enable it again. */ 273 if (test_and_clear_bit(0, &ctx->irq_flags)) 274 vidi_enable_vblank(mgr); 275 276 vidi_apply(mgr); 277 } else { 278 ctx->suspended = true; 279 } 280 281 return 0; 282 } 283 284 static void vidi_dpms(struct exynos_drm_manager *mgr, int mode) 285 { 286 struct vidi_context *ctx = manager_to_vidi(mgr); 287 288 DRM_DEBUG_KMS("%d\n", mode); 289 290 mutex_lock(&ctx->lock); 291 292 switch (mode) { 293 case DRM_MODE_DPMS_ON: 294 vidi_power_on(mgr, true); 295 break; 296 case DRM_MODE_DPMS_STANDBY: 297 case DRM_MODE_DPMS_SUSPEND: 298 case DRM_MODE_DPMS_OFF: 299 vidi_power_on(mgr, false); 300 break; 301 default: 302 DRM_DEBUG_KMS("unspecified mode %d\n", mode); 303 break; 304 } 305 306 mutex_unlock(&ctx->lock); 307 } 308 309 static int vidi_mgr_initialize(struct exynos_drm_manager *mgr, 310 struct drm_device *drm_dev) 311 { 312 struct vidi_context *ctx = manager_to_vidi(mgr); 313 struct exynos_drm_private *priv = drm_dev->dev_private; 314 315 mgr->drm_dev = ctx->drm_dev = drm_dev; 316 mgr->pipe = ctx->pipe = priv->pipe++; 317 318 return 0; 319 } 320 321 static struct exynos_drm_manager_ops vidi_manager_ops = { 322 .dpms = vidi_dpms, 323 .commit = vidi_commit, 324 .enable_vblank = vidi_enable_vblank, 325 .disable_vblank = vidi_disable_vblank, 326 .win_mode_set = vidi_win_mode_set, 327 .win_commit = vidi_win_commit, 328 .win_disable = vidi_win_disable, 329 }; 330 331 static void vidi_fake_vblank_handler(struct work_struct *work) 332 { 333 struct vidi_context *ctx = container_of(work, struct vidi_context, 334 work); 335 336 if (ctx->pipe < 0) 337 return; 338 339 /* refresh rate is about 50Hz. */ 340 usleep_range(16000, 20000); 341 342 mutex_lock(&ctx->lock); 343 344 if (ctx->direct_vblank) { 345 drm_handle_vblank(ctx->drm_dev, ctx->pipe); 346 ctx->direct_vblank = false; 347 mutex_unlock(&ctx->lock); 348 return; 349 } 350 351 mutex_unlock(&ctx->lock); 352 353 exynos_drm_crtc_finish_pageflip(ctx->drm_dev, ctx->pipe); 354 } 355 356 static int vidi_show_connection(struct device *dev, 357 struct device_attribute *attr, char *buf) 358 { 359 struct vidi_context *ctx = dev_get_drvdata(dev); 360 int rc; 361 362 mutex_lock(&ctx->lock); 363 364 rc = sprintf(buf, "%d\n", ctx->connected); 365 366 mutex_unlock(&ctx->lock); 367 368 return rc; 369 } 370 371 static int vidi_store_connection(struct device *dev, 372 struct device_attribute *attr, 373 const char *buf, size_t len) 374 { 375 struct vidi_context *ctx = dev_get_drvdata(dev); 376 int ret; 377 378 ret = kstrtoint(buf, 0, &ctx->connected); 379 if (ret) 380 return ret; 381 382 if (ctx->connected > 1) 383 return -EINVAL; 384 385 /* use fake edid data for test. */ 386 if (!ctx->raw_edid) 387 ctx->raw_edid = (struct edid *)fake_edid_info; 388 389 /* if raw_edid isn't same as fake data then it can't be tested. */ 390 if (ctx->raw_edid != (struct edid *)fake_edid_info) { 391 DRM_DEBUG_KMS("edid data is not fake data.\n"); 392 return -EINVAL; 393 } 394 395 DRM_DEBUG_KMS("requested connection.\n"); 396 397 drm_helper_hpd_irq_event(ctx->drm_dev); 398 399 return len; 400 } 401 402 static DEVICE_ATTR(connection, 0644, vidi_show_connection, 403 vidi_store_connection); 404 405 int vidi_connection_ioctl(struct drm_device *drm_dev, void *data, 406 struct drm_file *file_priv) 407 { 408 struct vidi_context *ctx = NULL; 409 struct drm_encoder *encoder; 410 struct exynos_drm_display *display; 411 struct drm_exynos_vidi_connection *vidi = data; 412 413 if (!vidi) { 414 DRM_DEBUG_KMS("user data for vidi is null.\n"); 415 return -EINVAL; 416 } 417 418 if (vidi->connection > 1) { 419 DRM_DEBUG_KMS("connection should be 0 or 1.\n"); 420 return -EINVAL; 421 } 422 423 list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list, 424 head) { 425 display = exynos_drm_get_display(encoder); 426 427 if (display->type == EXYNOS_DISPLAY_TYPE_VIDI) { 428 ctx = display_to_vidi(display); 429 break; 430 } 431 } 432 433 if (!ctx) { 434 DRM_DEBUG_KMS("not found virtual device type encoder.\n"); 435 return -EINVAL; 436 } 437 438 if (ctx->connected == vidi->connection) { 439 DRM_DEBUG_KMS("same connection request.\n"); 440 return -EINVAL; 441 } 442 443 if (vidi->connection) { 444 struct edid *raw_edid = (struct edid *)(uint32_t)vidi->edid; 445 if (!drm_edid_is_valid(raw_edid)) { 446 DRM_DEBUG_KMS("edid data is invalid.\n"); 447 return -EINVAL; 448 } 449 ctx->raw_edid = drm_edid_duplicate(raw_edid); 450 if (!ctx->raw_edid) { 451 DRM_DEBUG_KMS("failed to allocate raw_edid.\n"); 452 return -ENOMEM; 453 } 454 } else { 455 /* 456 * with connection = 0, free raw_edid 457 * only if raw edid data isn't same as fake data. 458 */ 459 if (ctx->raw_edid && ctx->raw_edid != 460 (struct edid *)fake_edid_info) { 461 kfree(ctx->raw_edid); 462 ctx->raw_edid = NULL; 463 } 464 } 465 466 ctx->connected = vidi->connection; 467 drm_helper_hpd_irq_event(ctx->drm_dev); 468 469 return 0; 470 } 471 472 static enum drm_connector_status vidi_detect(struct drm_connector *connector, 473 bool force) 474 { 475 struct vidi_context *ctx = ctx_from_connector(connector); 476 477 /* 478 * connection request would come from user side 479 * to do hotplug through specific ioctl. 480 */ 481 return ctx->connected ? connector_status_connected : 482 connector_status_disconnected; 483 } 484 485 static void vidi_connector_destroy(struct drm_connector *connector) 486 { 487 } 488 489 static struct drm_connector_funcs vidi_connector_funcs = { 490 .dpms = drm_helper_connector_dpms, 491 .fill_modes = drm_helper_probe_single_connector_modes, 492 .detect = vidi_detect, 493 .destroy = vidi_connector_destroy, 494 }; 495 496 static int vidi_get_modes(struct drm_connector *connector) 497 { 498 struct vidi_context *ctx = ctx_from_connector(connector); 499 struct edid *edid; 500 int edid_len; 501 502 /* 503 * the edid data comes from user side and it would be set 504 * to ctx->raw_edid through specific ioctl. 505 */ 506 if (!ctx->raw_edid) { 507 DRM_DEBUG_KMS("raw_edid is null.\n"); 508 return -EFAULT; 509 } 510 511 edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH; 512 edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL); 513 if (!edid) { 514 DRM_DEBUG_KMS("failed to allocate edid\n"); 515 return -ENOMEM; 516 } 517 518 drm_mode_connector_update_edid_property(connector, edid); 519 520 return drm_add_edid_modes(connector, edid); 521 } 522 523 static struct drm_encoder *vidi_best_encoder(struct drm_connector *connector) 524 { 525 struct vidi_context *ctx = ctx_from_connector(connector); 526 527 return ctx->encoder; 528 } 529 530 static struct drm_connector_helper_funcs vidi_connector_helper_funcs = { 531 .get_modes = vidi_get_modes, 532 .best_encoder = vidi_best_encoder, 533 }; 534 535 static int vidi_create_connector(struct exynos_drm_display *display, 536 struct drm_encoder *encoder) 537 { 538 struct vidi_context *ctx = display_to_vidi(display); 539 struct drm_connector *connector = &ctx->connector; 540 int ret; 541 542 ctx->encoder = encoder; 543 connector->polled = DRM_CONNECTOR_POLL_HPD; 544 545 ret = drm_connector_init(ctx->drm_dev, connector, 546 &vidi_connector_funcs, DRM_MODE_CONNECTOR_VIRTUAL); 547 if (ret) { 548 DRM_ERROR("Failed to initialize connector with drm\n"); 549 return ret; 550 } 551 552 drm_connector_helper_add(connector, &vidi_connector_helper_funcs); 553 drm_connector_register(connector); 554 drm_mode_connector_attach_encoder(connector, encoder); 555 556 return 0; 557 } 558 559 560 static struct exynos_drm_display_ops vidi_display_ops = { 561 .create_connector = vidi_create_connector, 562 }; 563 564 static int vidi_bind(struct device *dev, struct device *master, void *data) 565 { 566 struct vidi_context *ctx = dev_get_drvdata(dev); 567 struct drm_device *drm_dev = data; 568 struct drm_crtc *crtc = ctx->crtc; 569 int ret; 570 571 vidi_mgr_initialize(&ctx->manager, drm_dev); 572 573 ret = exynos_drm_crtc_create(&ctx->manager); 574 if (ret) { 575 DRM_ERROR("failed to create crtc.\n"); 576 return ret; 577 } 578 579 ret = exynos_drm_create_enc_conn(drm_dev, &ctx->display); 580 if (ret) { 581 crtc->funcs->destroy(crtc); 582 DRM_ERROR("failed to create encoder and connector.\n"); 583 return ret; 584 } 585 586 return 0; 587 } 588 589 590 static void vidi_unbind(struct device *dev, struct device *master, void *data) 591 { 592 } 593 594 static const struct component_ops vidi_component_ops = { 595 .bind = vidi_bind, 596 .unbind = vidi_unbind, 597 }; 598 599 static int vidi_probe(struct platform_device *pdev) 600 { 601 struct vidi_context *ctx; 602 int ret; 603 604 ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); 605 if (!ctx) 606 return -ENOMEM; 607 608 ctx->manager.type = EXYNOS_DISPLAY_TYPE_VIDI; 609 ctx->manager.ops = &vidi_manager_ops; 610 ctx->display.type = EXYNOS_DISPLAY_TYPE_VIDI; 611 ctx->display.ops = &vidi_display_ops; 612 ctx->default_win = 0; 613 ctx->pdev = pdev; 614 615 ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC, 616 ctx->manager.type); 617 if (ret) 618 return ret; 619 620 ret = exynos_drm_component_add(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR, 621 ctx->display.type); 622 if (ret) 623 goto err_del_crtc_component; 624 625 INIT_WORK(&ctx->work, vidi_fake_vblank_handler); 626 627 mutex_init(&ctx->lock); 628 629 platform_set_drvdata(pdev, ctx); 630 631 ret = device_create_file(&pdev->dev, &dev_attr_connection); 632 if (ret < 0) { 633 DRM_ERROR("failed to create connection sysfs.\n"); 634 goto err_del_conn_component; 635 } 636 637 ret = component_add(&pdev->dev, &vidi_component_ops); 638 if (ret) 639 goto err_remove_file; 640 641 return ret; 642 643 err_remove_file: 644 device_remove_file(&pdev->dev, &dev_attr_connection); 645 err_del_conn_component: 646 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); 647 err_del_crtc_component: 648 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 649 650 return ret; 651 } 652 653 static int vidi_remove(struct platform_device *pdev) 654 { 655 struct vidi_context *ctx = platform_get_drvdata(pdev); 656 657 if (ctx->raw_edid != (struct edid *)fake_edid_info) { 658 kfree(ctx->raw_edid); 659 ctx->raw_edid = NULL; 660 661 return -EINVAL; 662 } 663 664 component_del(&pdev->dev, &vidi_component_ops); 665 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CONNECTOR); 666 exynos_drm_component_del(&pdev->dev, EXYNOS_DEVICE_TYPE_CRTC); 667 668 return 0; 669 } 670 671 struct platform_driver vidi_driver = { 672 .probe = vidi_probe, 673 .remove = vidi_remove, 674 .driver = { 675 .name = "exynos-drm-vidi", 676 .owner = THIS_MODULE, 677 }, 678 }; 679 680 int exynos_drm_probe_vidi(void) 681 { 682 struct platform_device *pdev; 683 int ret; 684 685 pdev = platform_device_register_simple("exynos-drm-vidi", -1, NULL, 0); 686 if (IS_ERR(pdev)) 687 return PTR_ERR(pdev); 688 689 ret = platform_driver_register(&vidi_driver); 690 if (ret) { 691 platform_device_unregister(pdev); 692 return ret; 693 } 694 695 return ret; 696 } 697 698 static int exynos_drm_remove_vidi_device(struct device *dev, void *data) 699 { 700 platform_device_unregister(to_platform_device(dev)); 701 702 return 0; 703 } 704 705 void exynos_drm_remove_vidi(void) 706 { 707 int ret = driver_for_each_device(&vidi_driver.driver, NULL, NULL, 708 exynos_drm_remove_vidi_device); 709 /* silence compiler warning */ 710 (void)ret; 711 712 platform_driver_unregister(&vidi_driver); 713 } 714