| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2015, The Linux Foundation. All rights reserved. |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 5 | | - * it under the terms of the GNU General Public License version 2 and |
|---|
| 6 | | - * only version 2 as published by the Free Software Foundation. |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 9 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 10 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 11 | | - * GNU General Public License for more details. |
|---|
| 12 | 4 | */ |
|---|
| 13 | 5 | |
|---|
| 14 | 6 | #include "msm_kms.h" |
|---|
| .. | .. |
|---|
| 233 | 225 | return dsi_bridge->id; |
|---|
| 234 | 226 | } |
|---|
| 235 | 227 | |
|---|
| 228 | +static bool dsi_mgr_is_cmd_mode(struct msm_dsi *msm_dsi) |
|---|
| 229 | +{ |
|---|
| 230 | + unsigned long host_flags = msm_dsi_host_get_mode_flags(msm_dsi->host); |
|---|
| 231 | + return !(host_flags & MIPI_DSI_MODE_VIDEO); |
|---|
| 232 | +} |
|---|
| 233 | + |
|---|
| 234 | +void msm_dsi_manager_setup_encoder(int id) |
|---|
| 235 | +{ |
|---|
| 236 | + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| 237 | + struct msm_drm_private *priv = msm_dsi->dev->dev_private; |
|---|
| 238 | + struct msm_kms *kms = priv->kms; |
|---|
| 239 | + struct drm_encoder *encoder = msm_dsi_get_encoder(msm_dsi); |
|---|
| 240 | + |
|---|
| 241 | + if (encoder && kms->funcs->set_encoder_mode) |
|---|
| 242 | + kms->funcs->set_encoder_mode(kms, encoder, |
|---|
| 243 | + dsi_mgr_is_cmd_mode(msm_dsi)); |
|---|
| 244 | +} |
|---|
| 245 | + |
|---|
| 246 | +static int msm_dsi_manager_panel_init(struct drm_connector *conn, u8 id) |
|---|
| 247 | +{ |
|---|
| 248 | + struct msm_drm_private *priv = conn->dev->dev_private; |
|---|
| 249 | + struct msm_kms *kms = priv->kms; |
|---|
| 250 | + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| 251 | + struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); |
|---|
| 252 | + struct msm_dsi *master_dsi, *slave_dsi; |
|---|
| 253 | + struct drm_panel *panel; |
|---|
| 254 | + |
|---|
| 255 | + if (IS_DUAL_DSI() && !IS_MASTER_DSI_LINK(id)) { |
|---|
| 256 | + master_dsi = other_dsi; |
|---|
| 257 | + slave_dsi = msm_dsi; |
|---|
| 258 | + } else { |
|---|
| 259 | + master_dsi = msm_dsi; |
|---|
| 260 | + slave_dsi = other_dsi; |
|---|
| 261 | + } |
|---|
| 262 | + |
|---|
| 263 | + /* |
|---|
| 264 | + * There is only 1 panel in the global panel list for dual DSI mode. |
|---|
| 265 | + * Therefore slave dsi should get the drm_panel instance from master |
|---|
| 266 | + * dsi. |
|---|
| 267 | + */ |
|---|
| 268 | + panel = msm_dsi_host_get_panel(master_dsi->host); |
|---|
| 269 | + if (IS_ERR(panel)) { |
|---|
| 270 | + DRM_ERROR("Could not find panel for %u (%ld)\n", msm_dsi->id, |
|---|
| 271 | + PTR_ERR(panel)); |
|---|
| 272 | + return PTR_ERR(panel); |
|---|
| 273 | + } |
|---|
| 274 | + |
|---|
| 275 | + if (!panel || !IS_DUAL_DSI()) |
|---|
| 276 | + goto out; |
|---|
| 277 | + |
|---|
| 278 | + drm_object_attach_property(&conn->base, |
|---|
| 279 | + conn->dev->mode_config.tile_property, 0); |
|---|
| 280 | + |
|---|
| 281 | + /* |
|---|
| 282 | + * Set split display info to kms once dual DSI panel is connected to |
|---|
| 283 | + * both hosts. |
|---|
| 284 | + */ |
|---|
| 285 | + if (other_dsi && other_dsi->panel && kms->funcs->set_split_display) { |
|---|
| 286 | + kms->funcs->set_split_display(kms, master_dsi->encoder, |
|---|
| 287 | + slave_dsi->encoder, |
|---|
| 288 | + dsi_mgr_is_cmd_mode(msm_dsi)); |
|---|
| 289 | + } |
|---|
| 290 | + |
|---|
| 291 | +out: |
|---|
| 292 | + msm_dsi->panel = panel; |
|---|
| 293 | + return 0; |
|---|
| 294 | +} |
|---|
| 295 | + |
|---|
| 236 | 296 | static enum drm_connector_status dsi_mgr_connector_detect( |
|---|
| 237 | 297 | struct drm_connector *connector, bool force) |
|---|
| 238 | 298 | { |
|---|
| 239 | 299 | int id = dsi_mgr_connector_get_id(connector); |
|---|
| 240 | 300 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| 241 | | - struct msm_dsi *other_dsi = dsi_mgr_get_other_dsi(id); |
|---|
| 242 | | - struct msm_drm_private *priv = connector->dev->dev_private; |
|---|
| 243 | | - struct msm_kms *kms = priv->kms; |
|---|
| 244 | | - |
|---|
| 245 | | - DBG("id=%d", id); |
|---|
| 246 | | - if (!msm_dsi->panel) { |
|---|
| 247 | | - msm_dsi->panel = msm_dsi_host_get_panel(msm_dsi->host, |
|---|
| 248 | | - &msm_dsi->device_flags); |
|---|
| 249 | | - |
|---|
| 250 | | - /* There is only 1 panel in the global panel list |
|---|
| 251 | | - * for dual DSI mode. Therefore slave dsi should get |
|---|
| 252 | | - * the drm_panel instance from master dsi, and |
|---|
| 253 | | - * keep using the panel flags got from the current DSI link. |
|---|
| 254 | | - */ |
|---|
| 255 | | - if (!msm_dsi->panel && IS_DUAL_DSI() && |
|---|
| 256 | | - !IS_MASTER_DSI_LINK(id) && other_dsi) |
|---|
| 257 | | - msm_dsi->panel = msm_dsi_host_get_panel( |
|---|
| 258 | | - other_dsi->host, NULL); |
|---|
| 259 | | - |
|---|
| 260 | | - |
|---|
| 261 | | - if (msm_dsi->panel && kms->funcs->set_encoder_mode) { |
|---|
| 262 | | - bool cmd_mode = !(msm_dsi->device_flags & |
|---|
| 263 | | - MIPI_DSI_MODE_VIDEO); |
|---|
| 264 | | - struct drm_encoder *encoder = |
|---|
| 265 | | - msm_dsi_get_encoder(msm_dsi); |
|---|
| 266 | | - |
|---|
| 267 | | - kms->funcs->set_encoder_mode(kms, encoder, cmd_mode); |
|---|
| 268 | | - } |
|---|
| 269 | | - |
|---|
| 270 | | - if (msm_dsi->panel && IS_DUAL_DSI()) |
|---|
| 271 | | - drm_object_attach_property(&connector->base, |
|---|
| 272 | | - connector->dev->mode_config.tile_property, 0); |
|---|
| 273 | | - |
|---|
| 274 | | - /* Set split display info to kms once dual DSI panel is |
|---|
| 275 | | - * connected to both hosts. |
|---|
| 276 | | - */ |
|---|
| 277 | | - if (msm_dsi->panel && IS_DUAL_DSI() && |
|---|
| 278 | | - other_dsi && other_dsi->panel) { |
|---|
| 279 | | - bool cmd_mode = !(msm_dsi->device_flags & |
|---|
| 280 | | - MIPI_DSI_MODE_VIDEO); |
|---|
| 281 | | - struct drm_encoder *encoder = msm_dsi_get_encoder( |
|---|
| 282 | | - dsi_mgr_get_dsi(DSI_ENCODER_MASTER)); |
|---|
| 283 | | - struct drm_encoder *slave_enc = msm_dsi_get_encoder( |
|---|
| 284 | | - dsi_mgr_get_dsi(DSI_ENCODER_SLAVE)); |
|---|
| 285 | | - |
|---|
| 286 | | - if (kms->funcs->set_split_display) |
|---|
| 287 | | - kms->funcs->set_split_display(kms, encoder, |
|---|
| 288 | | - slave_enc, cmd_mode); |
|---|
| 289 | | - else |
|---|
| 290 | | - pr_err("mdp does not support dual DSI\n"); |
|---|
| 291 | | - } |
|---|
| 292 | | - } |
|---|
| 293 | 301 | |
|---|
| 294 | 302 | return msm_dsi->panel ? connector_status_connected : |
|---|
| 295 | 303 | connector_status_disconnected; |
|---|
| .. | .. |
|---|
| 320 | 328 | * In dual DSI mode, we have one connector that can be |
|---|
| 321 | 329 | * attached to the drm_panel. |
|---|
| 322 | 330 | */ |
|---|
| 323 | | - drm_panel_attach(panel, connector); |
|---|
| 324 | | - num = drm_panel_get_modes(panel); |
|---|
| 331 | + num = drm_panel_get_modes(panel, connector); |
|---|
| 325 | 332 | if (!num) |
|---|
| 326 | 333 | return 0; |
|---|
| 327 | 334 | |
|---|
| .. | .. |
|---|
| 424 | 431 | } |
|---|
| 425 | 432 | } |
|---|
| 426 | 433 | |
|---|
| 427 | | - if (panel) { |
|---|
| 428 | | - ret = drm_panel_enable(panel); |
|---|
| 429 | | - if (ret) { |
|---|
| 430 | | - pr_err("%s: enable panel %d failed, %d\n", __func__, id, |
|---|
| 431 | | - ret); |
|---|
| 432 | | - goto panel_en_fail; |
|---|
| 433 | | - } |
|---|
| 434 | | - } |
|---|
| 435 | | - |
|---|
| 436 | 434 | return; |
|---|
| 437 | 435 | |
|---|
| 438 | | -panel_en_fail: |
|---|
| 439 | | - if (is_dual_dsi && msm_dsi1) |
|---|
| 440 | | - msm_dsi_host_disable(msm_dsi1->host); |
|---|
| 441 | 436 | host1_en_fail: |
|---|
| 442 | 437 | msm_dsi_host_disable(host); |
|---|
| 443 | 438 | host_en_fail: |
|---|
| .. | .. |
|---|
| 456 | 451 | |
|---|
| 457 | 452 | static void dsi_mgr_bridge_enable(struct drm_bridge *bridge) |
|---|
| 458 | 453 | { |
|---|
| 459 | | - DBG(""); |
|---|
| 454 | + int id = dsi_mgr_bridge_get_id(bridge); |
|---|
| 455 | + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| 456 | + struct drm_panel *panel = msm_dsi->panel; |
|---|
| 457 | + bool is_dual_dsi = IS_DUAL_DSI(); |
|---|
| 458 | + int ret; |
|---|
| 459 | + |
|---|
| 460 | + DBG("id=%d", id); |
|---|
| 461 | + if (!msm_dsi_device_connected(msm_dsi)) |
|---|
| 462 | + return; |
|---|
| 463 | + |
|---|
| 464 | + /* Do nothing with the host if it is slave-DSI in case of dual DSI */ |
|---|
| 465 | + if (is_dual_dsi && !IS_MASTER_DSI_LINK(id)) |
|---|
| 466 | + return; |
|---|
| 467 | + |
|---|
| 468 | + if (panel) { |
|---|
| 469 | + ret = drm_panel_enable(panel); |
|---|
| 470 | + if (ret) { |
|---|
| 471 | + pr_err("%s: enable panel %d failed, %d\n", __func__, id, |
|---|
| 472 | + ret); |
|---|
| 473 | + } |
|---|
| 474 | + } |
|---|
| 460 | 475 | } |
|---|
| 461 | 476 | |
|---|
| 462 | 477 | static void dsi_mgr_bridge_disable(struct drm_bridge *bridge) |
|---|
| 463 | 478 | { |
|---|
| 464 | | - DBG(""); |
|---|
| 479 | + int id = dsi_mgr_bridge_get_id(bridge); |
|---|
| 480 | + struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| 481 | + struct drm_panel *panel = msm_dsi->panel; |
|---|
| 482 | + bool is_dual_dsi = IS_DUAL_DSI(); |
|---|
| 483 | + int ret; |
|---|
| 484 | + |
|---|
| 485 | + DBG("id=%d", id); |
|---|
| 486 | + if (!msm_dsi_device_connected(msm_dsi)) |
|---|
| 487 | + return; |
|---|
| 488 | + |
|---|
| 489 | + /* Do nothing with the host if it is slave-DSI in case of dual DSI */ |
|---|
| 490 | + if (is_dual_dsi && !IS_MASTER_DSI_LINK(id)) |
|---|
| 491 | + return; |
|---|
| 492 | + |
|---|
| 493 | + if (panel) { |
|---|
| 494 | + ret = drm_panel_disable(panel); |
|---|
| 495 | + if (ret) |
|---|
| 496 | + pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, |
|---|
| 497 | + ret); |
|---|
| 498 | + } |
|---|
| 465 | 499 | } |
|---|
| 466 | 500 | |
|---|
| 467 | 501 | static void dsi_mgr_bridge_post_disable(struct drm_bridge *bridge) |
|---|
| .. | .. |
|---|
| 487 | 521 | */ |
|---|
| 488 | 522 | if (is_dual_dsi && !IS_MASTER_DSI_LINK(id)) |
|---|
| 489 | 523 | goto disable_phy; |
|---|
| 490 | | - |
|---|
| 491 | | - if (panel) { |
|---|
| 492 | | - ret = drm_panel_disable(panel); |
|---|
| 493 | | - if (ret) |
|---|
| 494 | | - pr_err("%s: Panel %d OFF failed, %d\n", __func__, id, |
|---|
| 495 | | - ret); |
|---|
| 496 | | - } |
|---|
| 497 | 524 | |
|---|
| 498 | 525 | ret = msm_dsi_host_disable(host); |
|---|
| 499 | 526 | if (ret) |
|---|
| .. | .. |
|---|
| 532 | 559 | } |
|---|
| 533 | 560 | |
|---|
| 534 | 561 | static void dsi_mgr_bridge_mode_set(struct drm_bridge *bridge, |
|---|
| 535 | | - struct drm_display_mode *mode, |
|---|
| 536 | | - struct drm_display_mode *adjusted_mode) |
|---|
| 562 | + const struct drm_display_mode *mode, |
|---|
| 563 | + const struct drm_display_mode *adjusted_mode) |
|---|
| 537 | 564 | { |
|---|
| 538 | 565 | int id = dsi_mgr_bridge_get_id(bridge); |
|---|
| 539 | 566 | struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| .. | .. |
|---|
| 541 | 568 | struct mipi_dsi_host *host = msm_dsi->host; |
|---|
| 542 | 569 | bool is_dual_dsi = IS_DUAL_DSI(); |
|---|
| 543 | 570 | |
|---|
| 544 | | - DBG("set mode: %d:\"%s\" %d %d %d %d %d %d %d %d %d %d 0x%x 0x%x", |
|---|
| 545 | | - mode->base.id, mode->name, |
|---|
| 546 | | - mode->vrefresh, mode->clock, |
|---|
| 547 | | - mode->hdisplay, mode->hsync_start, |
|---|
| 548 | | - mode->hsync_end, mode->htotal, |
|---|
| 549 | | - mode->vdisplay, mode->vsync_start, |
|---|
| 550 | | - mode->vsync_end, mode->vtotal, |
|---|
| 551 | | - mode->type, mode->flags); |
|---|
| 571 | + DBG("set mode: " DRM_MODE_FMT, DRM_MODE_ARG(mode)); |
|---|
| 552 | 572 | |
|---|
| 553 | 573 | if (is_dual_dsi && !IS_MASTER_DSI_LINK(id)) |
|---|
| 554 | 574 | return; |
|---|
| .. | .. |
|---|
| 615 | 635 | |
|---|
| 616 | 636 | drm_connector_attach_encoder(connector, msm_dsi->encoder); |
|---|
| 617 | 637 | |
|---|
| 638 | + ret = msm_dsi_manager_panel_init(connector, id); |
|---|
| 639 | + if (ret) { |
|---|
| 640 | + DRM_DEV_ERROR(msm_dsi->dev->dev, "init panel failed %d\n", ret); |
|---|
| 641 | + goto fail; |
|---|
| 642 | + } |
|---|
| 643 | + |
|---|
| 618 | 644 | return connector; |
|---|
| 645 | + |
|---|
| 646 | +fail: |
|---|
| 647 | + connector->funcs->destroy(connector); |
|---|
| 648 | + return ERR_PTR(ret); |
|---|
| 619 | 649 | } |
|---|
| 620 | 650 | |
|---|
| 621 | 651 | bool msm_dsi_manager_validate_current_config(u8 id) |
|---|
| .. | .. |
|---|
| 658 | 688 | bridge = &dsi_bridge->base; |
|---|
| 659 | 689 | bridge->funcs = &dsi_mgr_bridge_funcs; |
|---|
| 660 | 690 | |
|---|
| 661 | | - ret = drm_bridge_attach(encoder, bridge, NULL); |
|---|
| 691 | + ret = drm_bridge_attach(encoder, bridge, NULL, 0); |
|---|
| 662 | 692 | if (ret) |
|---|
| 663 | 693 | goto fail; |
|---|
| 664 | 694 | |
|---|
| .. | .. |
|---|
| 687 | 717 | encoder = msm_dsi->encoder; |
|---|
| 688 | 718 | |
|---|
| 689 | 719 | /* link the internal dsi bridge to the external bridge */ |
|---|
| 690 | | - drm_bridge_attach(encoder, ext_bridge, int_bridge); |
|---|
| 720 | + drm_bridge_attach(encoder, ext_bridge, int_bridge, 0); |
|---|
| 691 | 721 | |
|---|
| 692 | 722 | /* |
|---|
| 693 | 723 | * we need the drm_connector created by the external bridge |
|---|
| .. | .. |
|---|
| 771 | 801 | return true; |
|---|
| 772 | 802 | } |
|---|
| 773 | 803 | |
|---|
| 774 | | -void msm_dsi_manager_attach_dsi_device(int id, u32 device_flags) |
|---|
| 775 | | -{ |
|---|
| 776 | | - struct msm_dsi *msm_dsi = dsi_mgr_get_dsi(id); |
|---|
| 777 | | - struct drm_device *dev = msm_dsi->dev; |
|---|
| 778 | | - struct msm_drm_private *priv; |
|---|
| 779 | | - struct msm_kms *kms; |
|---|
| 780 | | - struct drm_encoder *encoder; |
|---|
| 781 | | - bool cmd_mode; |
|---|
| 782 | | - |
|---|
| 783 | | - /* |
|---|
| 784 | | - * drm_device pointer is assigned to msm_dsi only in the modeset_init |
|---|
| 785 | | - * path. If mipi_dsi_attach() happens in DSI driver's probe path |
|---|
| 786 | | - * (generally the case when we're connected to a drm_panel of the type |
|---|
| 787 | | - * mipi_dsi_device), this would be NULL. In such cases, try to set the |
|---|
| 788 | | - * encoder mode in the DSI connector's detect() op. |
|---|
| 789 | | - */ |
|---|
| 790 | | - if (!dev) |
|---|
| 791 | | - return; |
|---|
| 792 | | - |
|---|
| 793 | | - priv = dev->dev_private; |
|---|
| 794 | | - kms = priv->kms; |
|---|
| 795 | | - encoder = msm_dsi_get_encoder(msm_dsi); |
|---|
| 796 | | - cmd_mode = !(device_flags & |
|---|
| 797 | | - MIPI_DSI_MODE_VIDEO); |
|---|
| 798 | | - |
|---|
| 799 | | - if (encoder && kms->funcs->set_encoder_mode) |
|---|
| 800 | | - kms->funcs->set_encoder_mode(kms, encoder, cmd_mode); |
|---|
| 801 | | -} |
|---|
| 802 | | - |
|---|
| 803 | 804 | int msm_dsi_manager_register(struct msm_dsi *msm_dsi) |
|---|
| 804 | 805 | { |
|---|
| 805 | 806 | struct msm_dsi_manager *msm_dsim = &msm_dsim_glb; |
|---|
| .. | .. |
|---|
| 844 | 845 | |
|---|
| 845 | 846 | if (msm_dsi->host) |
|---|
| 846 | 847 | msm_dsi_host_unregister(msm_dsi->host); |
|---|
| 847 | | - msm_dsim->dsi[msm_dsi->id] = NULL; |
|---|
| 848 | + |
|---|
| 849 | + if (msm_dsi->id >= 0) |
|---|
| 850 | + msm_dsim->dsi[msm_dsi->id] = NULL; |
|---|
| 848 | 851 | } |
|---|
| 849 | 852 | |
|---|