| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2016 Broadcom |
|---|
| 3 | | - * |
|---|
| 4 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 5 | | - * under the terms of the GNU General Public License version 2 as published by |
|---|
| 6 | | - * the Free Software Foundation. |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 9 | | - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|---|
| 10 | | - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
|---|
| 11 | | - * more details. |
|---|
| 12 | | - * |
|---|
| 13 | | - * You should have received a copy of the GNU General Public License along with |
|---|
| 14 | | - * this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 15 | 4 | */ |
|---|
| 16 | 5 | |
|---|
| 17 | 6 | /** |
|---|
| .. | .. |
|---|
| 29 | 18 | * hopefully present. |
|---|
| 30 | 19 | */ |
|---|
| 31 | 20 | |
|---|
| 21 | +#include <linux/clk-provider.h> |
|---|
| 22 | +#include <linux/clk.h> |
|---|
| 23 | +#include <linux/completion.h> |
|---|
| 24 | +#include <linux/component.h> |
|---|
| 25 | +#include <linux/dma-mapping.h> |
|---|
| 26 | +#include <linux/dmaengine.h> |
|---|
| 27 | +#include <linux/i2c.h> |
|---|
| 28 | +#include <linux/io.h> |
|---|
| 29 | +#include <linux/of_address.h> |
|---|
| 30 | +#include <linux/of_platform.h> |
|---|
| 31 | +#include <linux/pm_runtime.h> |
|---|
| 32 | + |
|---|
| 32 | 33 | #include <drm/drm_atomic_helper.h> |
|---|
| 33 | | -#include <drm/drm_crtc_helper.h> |
|---|
| 34 | +#include <drm/drm_bridge.h> |
|---|
| 34 | 35 | #include <drm/drm_edid.h> |
|---|
| 35 | 36 | #include <drm/drm_mipi_dsi.h> |
|---|
| 36 | 37 | #include <drm/drm_of.h> |
|---|
| 37 | 38 | #include <drm/drm_panel.h> |
|---|
| 38 | | -#include <linux/clk.h> |
|---|
| 39 | | -#include <linux/clk-provider.h> |
|---|
| 40 | | -#include <linux/completion.h> |
|---|
| 41 | | -#include <linux/component.h> |
|---|
| 42 | | -#include <linux/dmaengine.h> |
|---|
| 43 | | -#include <linux/i2c.h> |
|---|
| 44 | | -#include <linux/of_address.h> |
|---|
| 45 | | -#include <linux/of_platform.h> |
|---|
| 46 | | -#include <linux/pm_runtime.h> |
|---|
| 39 | +#include <drm/drm_probe_helper.h> |
|---|
| 40 | +#include <drm/drm_simple_kms_helper.h> |
|---|
| 41 | + |
|---|
| 47 | 42 | #include "vc4_drv.h" |
|---|
| 48 | 43 | #include "vc4_regs.h" |
|---|
| 49 | 44 | |
|---|
| .. | .. |
|---|
| 186 | 181 | |
|---|
| 187 | 182 | #define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */ |
|---|
| 188 | 183 | |
|---|
| 189 | | -#define DSI0_INT_STAT 0x24 |
|---|
| 190 | | -#define DSI0_INT_EN 0x28 |
|---|
| 184 | +#define DSI0_INT_STAT 0x24 |
|---|
| 185 | +#define DSI0_INT_EN 0x28 |
|---|
| 186 | +# define DSI0_INT_FIFO_ERR BIT(25) |
|---|
| 187 | +# define DSI0_INT_CMDC_DONE_MASK VC4_MASK(24, 23) |
|---|
| 188 | +# define DSI0_INT_CMDC_DONE_SHIFT 23 |
|---|
| 189 | +# define DSI0_INT_CMDC_DONE_NO_REPEAT 1 |
|---|
| 190 | +# define DSI0_INT_CMDC_DONE_REPEAT 3 |
|---|
| 191 | +# define DSI0_INT_PHY_DIR_RTF BIT(22) |
|---|
| 192 | +# define DSI0_INT_PHY_D1_ULPS BIT(21) |
|---|
| 193 | +# define DSI0_INT_PHY_D1_STOP BIT(20) |
|---|
| 194 | +# define DSI0_INT_PHY_RXLPDT BIT(19) |
|---|
| 195 | +# define DSI0_INT_PHY_RXTRIG BIT(18) |
|---|
| 196 | +# define DSI0_INT_PHY_D0_ULPS BIT(17) |
|---|
| 197 | +# define DSI0_INT_PHY_D0_LPDT BIT(16) |
|---|
| 198 | +# define DSI0_INT_PHY_D0_FTR BIT(15) |
|---|
| 199 | +# define DSI0_INT_PHY_D0_STOP BIT(14) |
|---|
| 200 | +/* Signaled when the clock lane enters the given state. */ |
|---|
| 201 | +# define DSI0_INT_PHY_CLK_ULPS BIT(13) |
|---|
| 202 | +# define DSI0_INT_PHY_CLK_HS BIT(12) |
|---|
| 203 | +# define DSI0_INT_PHY_CLK_FTR BIT(11) |
|---|
| 204 | +/* Signaled on timeouts */ |
|---|
| 205 | +# define DSI0_INT_PR_TO BIT(10) |
|---|
| 206 | +# define DSI0_INT_TA_TO BIT(9) |
|---|
| 207 | +# define DSI0_INT_LPRX_TO BIT(8) |
|---|
| 208 | +# define DSI0_INT_HSTX_TO BIT(7) |
|---|
| 209 | +/* Contention on a line when trying to drive the line low */ |
|---|
| 210 | +# define DSI0_INT_ERR_CONT_LP1 BIT(6) |
|---|
| 211 | +# define DSI0_INT_ERR_CONT_LP0 BIT(5) |
|---|
| 212 | +/* Control error: incorrect line state sequence on data lane 0. */ |
|---|
| 213 | +# define DSI0_INT_ERR_CONTROL BIT(4) |
|---|
| 214 | +# define DSI0_INT_ERR_SYNC_ESC BIT(3) |
|---|
| 215 | +# define DSI0_INT_RX2_PKT BIT(2) |
|---|
| 216 | +# define DSI0_INT_RX1_PKT BIT(1) |
|---|
| 217 | +# define DSI0_INT_CMD_PKT BIT(0) |
|---|
| 218 | + |
|---|
| 219 | +#define DSI0_INTERRUPTS_ALWAYS_ENABLED (DSI0_INT_ERR_SYNC_ESC | \ |
|---|
| 220 | + DSI0_INT_ERR_CONTROL | \ |
|---|
| 221 | + DSI0_INT_ERR_CONT_LP0 | \ |
|---|
| 222 | + DSI0_INT_ERR_CONT_LP1 | \ |
|---|
| 223 | + DSI0_INT_HSTX_TO | \ |
|---|
| 224 | + DSI0_INT_LPRX_TO | \ |
|---|
| 225 | + DSI0_INT_TA_TO | \ |
|---|
| 226 | + DSI0_INT_PR_TO) |
|---|
| 227 | + |
|---|
| 191 | 228 | # define DSI1_INT_PHY_D3_ULPS BIT(30) |
|---|
| 192 | 229 | # define DSI1_INT_PHY_D3_STOP BIT(29) |
|---|
| 193 | 230 | # define DSI1_INT_PHY_D2_ULPS BIT(28) |
|---|
| .. | .. |
|---|
| 498 | 535 | */ |
|---|
| 499 | 536 | #define DSI1_ID 0x8c |
|---|
| 500 | 537 | |
|---|
| 538 | +struct vc4_dsi_variant { |
|---|
| 539 | + /* Whether we're on bcm2835's DSI0 or DSI1. */ |
|---|
| 540 | + unsigned int port; |
|---|
| 541 | + |
|---|
| 542 | + bool broken_axi_workaround; |
|---|
| 543 | + |
|---|
| 544 | + const char *debugfs_name; |
|---|
| 545 | + const struct debugfs_reg32 *regs; |
|---|
| 546 | + size_t nregs; |
|---|
| 547 | + |
|---|
| 548 | +}; |
|---|
| 549 | + |
|---|
| 501 | 550 | /* General DSI hardware state. */ |
|---|
| 502 | 551 | struct vc4_dsi { |
|---|
| 503 | 552 | struct platform_device *pdev; |
|---|
| .. | .. |
|---|
| 505 | 554 | struct mipi_dsi_host dsi_host; |
|---|
| 506 | 555 | struct drm_encoder *encoder; |
|---|
| 507 | 556 | struct drm_bridge *bridge; |
|---|
| 557 | + struct list_head bridge_chain; |
|---|
| 508 | 558 | |
|---|
| 509 | 559 | void __iomem *regs; |
|---|
| 510 | 560 | |
|---|
| .. | .. |
|---|
| 513 | 563 | u32 *reg_dma_mem; |
|---|
| 514 | 564 | dma_addr_t reg_paddr; |
|---|
| 515 | 565 | |
|---|
| 516 | | - /* Whether we're on bcm2835's DSI0 or DSI1. */ |
|---|
| 517 | | - int port; |
|---|
| 566 | + const struct vc4_dsi_variant *variant; |
|---|
| 518 | 567 | |
|---|
| 519 | 568 | /* DSI channel for the panel we're connected to. */ |
|---|
| 520 | 569 | u32 channel; |
|---|
| .. | .. |
|---|
| 545 | 594 | |
|---|
| 546 | 595 | struct completion xfer_completion; |
|---|
| 547 | 596 | int xfer_result; |
|---|
| 597 | + |
|---|
| 598 | + struct debugfs_regset32 regset; |
|---|
| 548 | 599 | }; |
|---|
| 549 | 600 | |
|---|
| 550 | 601 | #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) |
|---|
| .. | .. |
|---|
| 588 | 639 | #define DSI_READ(offset) readl(dsi->regs + (offset)) |
|---|
| 589 | 640 | #define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) |
|---|
| 590 | 641 | #define DSI_PORT_READ(offset) \ |
|---|
| 591 | | - DSI_READ(dsi->port ? DSI1_##offset : DSI0_##offset) |
|---|
| 642 | + DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset) |
|---|
| 592 | 643 | #define DSI_PORT_WRITE(offset, val) \ |
|---|
| 593 | | - DSI_WRITE(dsi->port ? DSI1_##offset : DSI0_##offset, val) |
|---|
| 594 | | -#define DSI_PORT_BIT(bit) (dsi->port ? DSI1_##bit : DSI0_##bit) |
|---|
| 644 | + DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val) |
|---|
| 645 | +#define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit) |
|---|
| 595 | 646 | |
|---|
| 596 | 647 | /* VC4 DSI encoder KMS struct */ |
|---|
| 597 | 648 | struct vc4_dsi_encoder { |
|---|
| .. | .. |
|---|
| 605 | 656 | return container_of(encoder, struct vc4_dsi_encoder, base.base); |
|---|
| 606 | 657 | } |
|---|
| 607 | 658 | |
|---|
| 608 | | -#define DSI_REG(reg) { reg, #reg } |
|---|
| 609 | | -static const struct { |
|---|
| 610 | | - u32 reg; |
|---|
| 611 | | - const char *name; |
|---|
| 612 | | -} dsi0_regs[] = { |
|---|
| 613 | | - DSI_REG(DSI0_CTRL), |
|---|
| 614 | | - DSI_REG(DSI0_STAT), |
|---|
| 615 | | - DSI_REG(DSI0_HSTX_TO_CNT), |
|---|
| 616 | | - DSI_REG(DSI0_LPRX_TO_CNT), |
|---|
| 617 | | - DSI_REG(DSI0_TA_TO_CNT), |
|---|
| 618 | | - DSI_REG(DSI0_PR_TO_CNT), |
|---|
| 619 | | - DSI_REG(DSI0_DISP0_CTRL), |
|---|
| 620 | | - DSI_REG(DSI0_DISP1_CTRL), |
|---|
| 621 | | - DSI_REG(DSI0_INT_STAT), |
|---|
| 622 | | - DSI_REG(DSI0_INT_EN), |
|---|
| 623 | | - DSI_REG(DSI0_PHYC), |
|---|
| 624 | | - DSI_REG(DSI0_HS_CLT0), |
|---|
| 625 | | - DSI_REG(DSI0_HS_CLT1), |
|---|
| 626 | | - DSI_REG(DSI0_HS_CLT2), |
|---|
| 627 | | - DSI_REG(DSI0_HS_DLT3), |
|---|
| 628 | | - DSI_REG(DSI0_HS_DLT4), |
|---|
| 629 | | - DSI_REG(DSI0_HS_DLT5), |
|---|
| 630 | | - DSI_REG(DSI0_HS_DLT6), |
|---|
| 631 | | - DSI_REG(DSI0_HS_DLT7), |
|---|
| 632 | | - DSI_REG(DSI0_PHY_AFEC0), |
|---|
| 633 | | - DSI_REG(DSI0_PHY_AFEC1), |
|---|
| 634 | | - DSI_REG(DSI0_ID), |
|---|
| 659 | +static const struct debugfs_reg32 dsi0_regs[] = { |
|---|
| 660 | + VC4_REG32(DSI0_CTRL), |
|---|
| 661 | + VC4_REG32(DSI0_STAT), |
|---|
| 662 | + VC4_REG32(DSI0_HSTX_TO_CNT), |
|---|
| 663 | + VC4_REG32(DSI0_LPRX_TO_CNT), |
|---|
| 664 | + VC4_REG32(DSI0_TA_TO_CNT), |
|---|
| 665 | + VC4_REG32(DSI0_PR_TO_CNT), |
|---|
| 666 | + VC4_REG32(DSI0_DISP0_CTRL), |
|---|
| 667 | + VC4_REG32(DSI0_DISP1_CTRL), |
|---|
| 668 | + VC4_REG32(DSI0_INT_STAT), |
|---|
| 669 | + VC4_REG32(DSI0_INT_EN), |
|---|
| 670 | + VC4_REG32(DSI0_PHYC), |
|---|
| 671 | + VC4_REG32(DSI0_HS_CLT0), |
|---|
| 672 | + VC4_REG32(DSI0_HS_CLT1), |
|---|
| 673 | + VC4_REG32(DSI0_HS_CLT2), |
|---|
| 674 | + VC4_REG32(DSI0_HS_DLT3), |
|---|
| 675 | + VC4_REG32(DSI0_HS_DLT4), |
|---|
| 676 | + VC4_REG32(DSI0_HS_DLT5), |
|---|
| 677 | + VC4_REG32(DSI0_HS_DLT6), |
|---|
| 678 | + VC4_REG32(DSI0_HS_DLT7), |
|---|
| 679 | + VC4_REG32(DSI0_PHY_AFEC0), |
|---|
| 680 | + VC4_REG32(DSI0_PHY_AFEC1), |
|---|
| 681 | + VC4_REG32(DSI0_ID), |
|---|
| 635 | 682 | }; |
|---|
| 636 | 683 | |
|---|
| 637 | | -static const struct { |
|---|
| 638 | | - u32 reg; |
|---|
| 639 | | - const char *name; |
|---|
| 640 | | -} dsi1_regs[] = { |
|---|
| 641 | | - DSI_REG(DSI1_CTRL), |
|---|
| 642 | | - DSI_REG(DSI1_STAT), |
|---|
| 643 | | - DSI_REG(DSI1_HSTX_TO_CNT), |
|---|
| 644 | | - DSI_REG(DSI1_LPRX_TO_CNT), |
|---|
| 645 | | - DSI_REG(DSI1_TA_TO_CNT), |
|---|
| 646 | | - DSI_REG(DSI1_PR_TO_CNT), |
|---|
| 647 | | - DSI_REG(DSI1_DISP0_CTRL), |
|---|
| 648 | | - DSI_REG(DSI1_DISP1_CTRL), |
|---|
| 649 | | - DSI_REG(DSI1_INT_STAT), |
|---|
| 650 | | - DSI_REG(DSI1_INT_EN), |
|---|
| 651 | | - DSI_REG(DSI1_PHYC), |
|---|
| 652 | | - DSI_REG(DSI1_HS_CLT0), |
|---|
| 653 | | - DSI_REG(DSI1_HS_CLT1), |
|---|
| 654 | | - DSI_REG(DSI1_HS_CLT2), |
|---|
| 655 | | - DSI_REG(DSI1_HS_DLT3), |
|---|
| 656 | | - DSI_REG(DSI1_HS_DLT4), |
|---|
| 657 | | - DSI_REG(DSI1_HS_DLT5), |
|---|
| 658 | | - DSI_REG(DSI1_HS_DLT6), |
|---|
| 659 | | - DSI_REG(DSI1_HS_DLT7), |
|---|
| 660 | | - DSI_REG(DSI1_PHY_AFEC0), |
|---|
| 661 | | - DSI_REG(DSI1_PHY_AFEC1), |
|---|
| 662 | | - DSI_REG(DSI1_ID), |
|---|
| 663 | | -}; |
|---|
| 664 | | - |
|---|
| 665 | | -static void vc4_dsi_dump_regs(struct vc4_dsi *dsi) |
|---|
| 666 | | -{ |
|---|
| 667 | | - int i; |
|---|
| 668 | | - |
|---|
| 669 | | - if (dsi->port == 0) { |
|---|
| 670 | | - for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { |
|---|
| 671 | | - DRM_INFO("0x%04x (%s): 0x%08x\n", |
|---|
| 672 | | - dsi0_regs[i].reg, dsi0_regs[i].name, |
|---|
| 673 | | - DSI_READ(dsi0_regs[i].reg)); |
|---|
| 674 | | - } |
|---|
| 675 | | - } else { |
|---|
| 676 | | - for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { |
|---|
| 677 | | - DRM_INFO("0x%04x (%s): 0x%08x\n", |
|---|
| 678 | | - dsi1_regs[i].reg, dsi1_regs[i].name, |
|---|
| 679 | | - DSI_READ(dsi1_regs[i].reg)); |
|---|
| 680 | | - } |
|---|
| 681 | | - } |
|---|
| 682 | | -} |
|---|
| 683 | | - |
|---|
| 684 | | -#ifdef CONFIG_DEBUG_FS |
|---|
| 685 | | -int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) |
|---|
| 686 | | -{ |
|---|
| 687 | | - struct drm_info_node *node = (struct drm_info_node *)m->private; |
|---|
| 688 | | - struct drm_device *drm = node->minor->dev; |
|---|
| 689 | | - struct vc4_dev *vc4 = to_vc4_dev(drm); |
|---|
| 690 | | - int dsi_index = (uintptr_t)node->info_ent->data; |
|---|
| 691 | | - struct vc4_dsi *dsi = (dsi_index == 1 ? vc4->dsi1 : NULL); |
|---|
| 692 | | - int i; |
|---|
| 693 | | - |
|---|
| 694 | | - if (!dsi) |
|---|
| 695 | | - return 0; |
|---|
| 696 | | - |
|---|
| 697 | | - if (dsi->port == 0) { |
|---|
| 698 | | - for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { |
|---|
| 699 | | - seq_printf(m, "0x%04x (%s): 0x%08x\n", |
|---|
| 700 | | - dsi0_regs[i].reg, dsi0_regs[i].name, |
|---|
| 701 | | - DSI_READ(dsi0_regs[i].reg)); |
|---|
| 702 | | - } |
|---|
| 703 | | - } else { |
|---|
| 704 | | - for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { |
|---|
| 705 | | - seq_printf(m, "0x%04x (%s): 0x%08x\n", |
|---|
| 706 | | - dsi1_regs[i].reg, dsi1_regs[i].name, |
|---|
| 707 | | - DSI_READ(dsi1_regs[i].reg)); |
|---|
| 708 | | - } |
|---|
| 709 | | - } |
|---|
| 710 | | - |
|---|
| 711 | | - return 0; |
|---|
| 712 | | -} |
|---|
| 713 | | -#endif |
|---|
| 714 | | - |
|---|
| 715 | | -static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) |
|---|
| 716 | | -{ |
|---|
| 717 | | - drm_encoder_cleanup(encoder); |
|---|
| 718 | | -} |
|---|
| 719 | | - |
|---|
| 720 | | -static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { |
|---|
| 721 | | - .destroy = vc4_dsi_encoder_destroy, |
|---|
| 684 | +static const struct debugfs_reg32 dsi1_regs[] = { |
|---|
| 685 | + VC4_REG32(DSI1_CTRL), |
|---|
| 686 | + VC4_REG32(DSI1_STAT), |
|---|
| 687 | + VC4_REG32(DSI1_HSTX_TO_CNT), |
|---|
| 688 | + VC4_REG32(DSI1_LPRX_TO_CNT), |
|---|
| 689 | + VC4_REG32(DSI1_TA_TO_CNT), |
|---|
| 690 | + VC4_REG32(DSI1_PR_TO_CNT), |
|---|
| 691 | + VC4_REG32(DSI1_DISP0_CTRL), |
|---|
| 692 | + VC4_REG32(DSI1_DISP1_CTRL), |
|---|
| 693 | + VC4_REG32(DSI1_INT_STAT), |
|---|
| 694 | + VC4_REG32(DSI1_INT_EN), |
|---|
| 695 | + VC4_REG32(DSI1_PHYC), |
|---|
| 696 | + VC4_REG32(DSI1_HS_CLT0), |
|---|
| 697 | + VC4_REG32(DSI1_HS_CLT1), |
|---|
| 698 | + VC4_REG32(DSI1_HS_CLT2), |
|---|
| 699 | + VC4_REG32(DSI1_HS_DLT3), |
|---|
| 700 | + VC4_REG32(DSI1_HS_DLT4), |
|---|
| 701 | + VC4_REG32(DSI1_HS_DLT5), |
|---|
| 702 | + VC4_REG32(DSI1_HS_DLT6), |
|---|
| 703 | + VC4_REG32(DSI1_HS_DLT7), |
|---|
| 704 | + VC4_REG32(DSI1_PHY_AFEC0), |
|---|
| 705 | + VC4_REG32(DSI1_PHY_AFEC1), |
|---|
| 706 | + VC4_REG32(DSI1_ID), |
|---|
| 722 | 707 | }; |
|---|
| 723 | 708 | |
|---|
| 724 | 709 | static void vc4_dsi_latch_ulps(struct vc4_dsi *dsi, bool latch) |
|---|
| .. | .. |
|---|
| 813 | 798 | struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); |
|---|
| 814 | 799 | struct vc4_dsi *dsi = vc4_encoder->dsi; |
|---|
| 815 | 800 | struct device *dev = &dsi->pdev->dev; |
|---|
| 801 | + struct drm_bridge *iter; |
|---|
| 816 | 802 | |
|---|
| 817 | | - drm_bridge_disable(dsi->bridge); |
|---|
| 803 | + list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { |
|---|
| 804 | + if (iter->funcs->disable) |
|---|
| 805 | + iter->funcs->disable(iter); |
|---|
| 806 | + |
|---|
| 807 | + if (iter == dsi->bridge) |
|---|
| 808 | + break; |
|---|
| 809 | + } |
|---|
| 810 | + |
|---|
| 818 | 811 | vc4_dsi_ulps(dsi, true); |
|---|
| 819 | | - drm_bridge_post_disable(dsi->bridge); |
|---|
| 812 | + |
|---|
| 813 | + list_for_each_entry_from(iter, &dsi->bridge_chain, chain_node) { |
|---|
| 814 | + if (iter->funcs->post_disable) |
|---|
| 815 | + iter->funcs->post_disable(iter); |
|---|
| 816 | + } |
|---|
| 820 | 817 | |
|---|
| 821 | 818 | clk_disable_unprepare(dsi->pll_phy_clock); |
|---|
| 822 | 819 | clk_disable_unprepare(dsi->escape_clock); |
|---|
| .. | .. |
|---|
| 853 | 850 | /* Find what divider gets us a faster clock than the requested |
|---|
| 854 | 851 | * pixel clock. |
|---|
| 855 | 852 | */ |
|---|
| 856 | | - for (divider = 1; divider < 8; divider++) { |
|---|
| 857 | | - if (parent_rate / divider < pll_clock) { |
|---|
| 858 | | - divider--; |
|---|
| 853 | + for (divider = 1; divider < 255; divider++) { |
|---|
| 854 | + if (parent_rate / (divider + 1) < pll_clock) |
|---|
| 859 | 855 | break; |
|---|
| 860 | | - } |
|---|
| 861 | 856 | } |
|---|
| 862 | 857 | |
|---|
| 863 | 858 | /* Now that we've picked a PLL divider, calculate back to its |
|---|
| .. | .. |
|---|
| 884 | 879 | struct vc4_dsi *dsi = vc4_encoder->dsi; |
|---|
| 885 | 880 | struct device *dev = &dsi->pdev->dev; |
|---|
| 886 | 881 | bool debug_dump_regs = false; |
|---|
| 882 | + struct drm_bridge *iter; |
|---|
| 887 | 883 | unsigned long hs_clock; |
|---|
| 888 | 884 | u32 ui_ns; |
|---|
| 889 | 885 | /* Minimum LP state duration in escape clock cycles. */ |
|---|
| .. | .. |
|---|
| 893 | 889 | unsigned long phy_clock; |
|---|
| 894 | 890 | int ret; |
|---|
| 895 | 891 | |
|---|
| 896 | | - ret = pm_runtime_get_sync(dev); |
|---|
| 892 | + ret = pm_runtime_resume_and_get(dev); |
|---|
| 897 | 893 | if (ret) { |
|---|
| 898 | | - DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->port); |
|---|
| 894 | + DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->variant->port); |
|---|
| 899 | 895 | return; |
|---|
| 900 | 896 | } |
|---|
| 901 | 897 | |
|---|
| 902 | 898 | if (debug_dump_regs) { |
|---|
| 903 | | - DRM_INFO("DSI regs before:\n"); |
|---|
| 904 | | - vc4_dsi_dump_regs(dsi); |
|---|
| 899 | + struct drm_printer p = drm_info_printer(&dsi->pdev->dev); |
|---|
| 900 | + dev_info(&dsi->pdev->dev, "DSI regs before:\n"); |
|---|
| 901 | + drm_print_regset32(&p, &dsi->regset); |
|---|
| 905 | 902 | } |
|---|
| 906 | 903 | |
|---|
| 907 | 904 | /* Round up the clk_set_rate() request slightly, since |
|---|
| .. | .. |
|---|
| 928 | 925 | DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT)); |
|---|
| 929 | 926 | |
|---|
| 930 | 927 | /* Set AFE CTR00/CTR1 to release powerdown of analog. */ |
|---|
| 931 | | - if (dsi->port == 0) { |
|---|
| 928 | + if (dsi->variant->port == 0) { |
|---|
| 932 | 929 | u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | |
|---|
| 933 | 930 | VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ)); |
|---|
| 934 | 931 | |
|---|
| .. | .. |
|---|
| 939 | 936 | afec0 |= DSI0_PHY_AFEC0_RESET; |
|---|
| 940 | 937 | |
|---|
| 941 | 938 | DSI_PORT_WRITE(PHY_AFEC0, afec0); |
|---|
| 939 | + |
|---|
| 940 | + /* AFEC reset hold time */ |
|---|
| 941 | + mdelay(1); |
|---|
| 942 | 942 | |
|---|
| 943 | 943 | DSI_PORT_WRITE(PHY_AFEC1, |
|---|
| 944 | 944 | VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) | |
|---|
| .. | .. |
|---|
| 1074 | 1074 | DSI_PORT_BIT(PHYC_CLANE_ENABLE) | |
|---|
| 1075 | 1075 | ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? |
|---|
| 1076 | 1076 | 0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) | |
|---|
| 1077 | | - (dsi->port == 0 ? |
|---|
| 1077 | + (dsi->variant->port == 0 ? |
|---|
| 1078 | 1078 | VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) : |
|---|
| 1079 | 1079 | VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT))); |
|---|
| 1080 | 1080 | |
|---|
| .. | .. |
|---|
| 1100 | 1100 | DSI_DISP1_ENABLE); |
|---|
| 1101 | 1101 | |
|---|
| 1102 | 1102 | /* Ungate the block. */ |
|---|
| 1103 | | - if (dsi->port == 0) |
|---|
| 1103 | + if (dsi->variant->port == 0) |
|---|
| 1104 | 1104 | DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); |
|---|
| 1105 | 1105 | else |
|---|
| 1106 | 1106 | DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); |
|---|
| 1107 | 1107 | |
|---|
| 1108 | 1108 | /* Bring AFE out of reset. */ |
|---|
| 1109 | | - if (dsi->port == 0) { |
|---|
| 1110 | | - } else { |
|---|
| 1111 | | - DSI_PORT_WRITE(PHY_AFEC0, |
|---|
| 1112 | | - DSI_PORT_READ(PHY_AFEC0) & |
|---|
| 1113 | | - ~DSI1_PHY_AFEC0_RESET); |
|---|
| 1114 | | - } |
|---|
| 1109 | + DSI_PORT_WRITE(PHY_AFEC0, |
|---|
| 1110 | + DSI_PORT_READ(PHY_AFEC0) & |
|---|
| 1111 | + ~DSI_PORT_BIT(PHY_AFEC0_RESET)); |
|---|
| 1115 | 1112 | |
|---|
| 1116 | 1113 | vc4_dsi_ulps(dsi, false); |
|---|
| 1117 | 1114 | |
|---|
| 1118 | | - drm_bridge_pre_enable(dsi->bridge); |
|---|
| 1115 | + list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { |
|---|
| 1116 | + if (iter->funcs->pre_enable) |
|---|
| 1117 | + iter->funcs->pre_enable(iter); |
|---|
| 1118 | + } |
|---|
| 1119 | 1119 | |
|---|
| 1120 | 1120 | if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { |
|---|
| 1121 | 1121 | DSI_PORT_WRITE(DISP0_CTRL, |
|---|
| .. | .. |
|---|
| 1132 | 1132 | DSI_DISP0_ENABLE); |
|---|
| 1133 | 1133 | } |
|---|
| 1134 | 1134 | |
|---|
| 1135 | | - drm_bridge_enable(dsi->bridge); |
|---|
| 1135 | + list_for_each_entry(iter, &dsi->bridge_chain, chain_node) { |
|---|
| 1136 | + if (iter->funcs->enable) |
|---|
| 1137 | + iter->funcs->enable(iter); |
|---|
| 1138 | + } |
|---|
| 1136 | 1139 | |
|---|
| 1137 | 1140 | if (debug_dump_regs) { |
|---|
| 1138 | | - DRM_INFO("DSI regs after:\n"); |
|---|
| 1139 | | - vc4_dsi_dump_regs(dsi); |
|---|
| 1141 | + struct drm_printer p = drm_info_printer(&dsi->pdev->dev); |
|---|
| 1142 | + dev_info(&dsi->pdev->dev, "DSI regs after:\n"); |
|---|
| 1143 | + drm_print_regset32(&p, &dsi->regset); |
|---|
| 1140 | 1144 | } |
|---|
| 1141 | 1145 | } |
|---|
| 1142 | 1146 | |
|---|
| .. | .. |
|---|
| 1223 | 1227 | /* Enable the appropriate interrupt for the transfer completion. */ |
|---|
| 1224 | 1228 | dsi->xfer_result = 0; |
|---|
| 1225 | 1229 | reinit_completion(&dsi->xfer_completion); |
|---|
| 1226 | | - DSI_PORT_WRITE(INT_STAT, DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); |
|---|
| 1227 | | - if (msg->rx_len) { |
|---|
| 1228 | | - DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | |
|---|
| 1229 | | - DSI1_INT_PHY_DIR_RTF)); |
|---|
| 1230 | + if (dsi->variant->port == 0) { |
|---|
| 1231 | + DSI_PORT_WRITE(INT_STAT, |
|---|
| 1232 | + DSI0_INT_CMDC_DONE_MASK | DSI1_INT_PHY_DIR_RTF); |
|---|
| 1233 | + if (msg->rx_len) { |
|---|
| 1234 | + DSI_PORT_WRITE(INT_EN, (DSI0_INTERRUPTS_ALWAYS_ENABLED | |
|---|
| 1235 | + DSI0_INT_PHY_DIR_RTF)); |
|---|
| 1236 | + } else { |
|---|
| 1237 | + DSI_PORT_WRITE(INT_EN, |
|---|
| 1238 | + (DSI0_INTERRUPTS_ALWAYS_ENABLED | |
|---|
| 1239 | + VC4_SET_FIELD(DSI0_INT_CMDC_DONE_NO_REPEAT, |
|---|
| 1240 | + DSI0_INT_CMDC_DONE))); |
|---|
| 1241 | + } |
|---|
| 1230 | 1242 | } else { |
|---|
| 1231 | | - DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | |
|---|
| 1232 | | - DSI1_INT_TXPKT1_DONE)); |
|---|
| 1243 | + DSI_PORT_WRITE(INT_STAT, |
|---|
| 1244 | + DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); |
|---|
| 1245 | + if (msg->rx_len) { |
|---|
| 1246 | + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | |
|---|
| 1247 | + DSI1_INT_PHY_DIR_RTF)); |
|---|
| 1248 | + } else { |
|---|
| 1249 | + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | |
|---|
| 1250 | + DSI1_INT_TXPKT1_DONE)); |
|---|
| 1251 | + } |
|---|
| 1233 | 1252 | } |
|---|
| 1234 | 1253 | |
|---|
| 1235 | 1254 | /* Send the packet. */ |
|---|
| .. | .. |
|---|
| 1246 | 1265 | ret = dsi->xfer_result; |
|---|
| 1247 | 1266 | } |
|---|
| 1248 | 1267 | |
|---|
| 1249 | | - DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); |
|---|
| 1268 | + DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); |
|---|
| 1250 | 1269 | |
|---|
| 1251 | 1270 | if (ret) |
|---|
| 1252 | 1271 | goto reset_fifo_and_return; |
|---|
| .. | .. |
|---|
| 1292 | 1311 | DSI_PORT_BIT(CTRL_RESET_FIFOS)); |
|---|
| 1293 | 1312 | |
|---|
| 1294 | 1313 | DSI_PORT_WRITE(TXPKT1C, 0); |
|---|
| 1295 | | - DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); |
|---|
| 1314 | + DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); |
|---|
| 1296 | 1315 | return ret; |
|---|
| 1297 | 1316 | } |
|---|
| 1298 | 1317 | |
|---|
| .. | .. |
|---|
| 1355 | 1374 | .mode_fixup = vc4_dsi_encoder_mode_fixup, |
|---|
| 1356 | 1375 | }; |
|---|
| 1357 | 1376 | |
|---|
| 1377 | +static const struct vc4_dsi_variant bcm2835_dsi1_variant = { |
|---|
| 1378 | + .port = 1, |
|---|
| 1379 | + .broken_axi_workaround = true, |
|---|
| 1380 | + .debugfs_name = "dsi1_regs", |
|---|
| 1381 | + .regs = dsi1_regs, |
|---|
| 1382 | + .nregs = ARRAY_SIZE(dsi1_regs), |
|---|
| 1383 | +}; |
|---|
| 1384 | + |
|---|
| 1358 | 1385 | static const struct of_device_id vc4_dsi_dt_match[] = { |
|---|
| 1359 | | - { .compatible = "brcm,bcm2835-dsi1", (void *)(uintptr_t)1 }, |
|---|
| 1386 | + { .compatible = "brcm,bcm2835-dsi1", &bcm2835_dsi1_variant }, |
|---|
| 1360 | 1387 | {} |
|---|
| 1361 | 1388 | }; |
|---|
| 1362 | 1389 | |
|---|
| .. | .. |
|---|
| 1367 | 1394 | if (!(stat & bit)) |
|---|
| 1368 | 1395 | return; |
|---|
| 1369 | 1396 | |
|---|
| 1370 | | - DRM_ERROR("DSI%d: %s error\n", dsi->port, type); |
|---|
| 1397 | + DRM_ERROR("DSI%d: %s error\n", dsi->variant->port, type); |
|---|
| 1371 | 1398 | *ret = IRQ_HANDLED; |
|---|
| 1372 | 1399 | } |
|---|
| 1373 | 1400 | |
|---|
| .. | .. |
|---|
| 1401 | 1428 | DSI_PORT_WRITE(INT_STAT, stat); |
|---|
| 1402 | 1429 | |
|---|
| 1403 | 1430 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1404 | | - DSI1_INT_ERR_SYNC_ESC, "LPDT sync"); |
|---|
| 1431 | + DSI_PORT_BIT(INT_ERR_SYNC_ESC), "LPDT sync"); |
|---|
| 1405 | 1432 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1406 | | - DSI1_INT_ERR_CONTROL, "data lane 0 sequence"); |
|---|
| 1433 | + DSI_PORT_BIT(INT_ERR_CONTROL), "data lane 0 sequence"); |
|---|
| 1407 | 1434 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1408 | | - DSI1_INT_ERR_CONT_LP0, "LP0 contention"); |
|---|
| 1435 | + DSI_PORT_BIT(INT_ERR_CONT_LP0), "LP0 contention"); |
|---|
| 1409 | 1436 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1410 | | - DSI1_INT_ERR_CONT_LP1, "LP1 contention"); |
|---|
| 1437 | + DSI_PORT_BIT(INT_ERR_CONT_LP1), "LP1 contention"); |
|---|
| 1411 | 1438 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1412 | | - DSI1_INT_HSTX_TO, "HSTX timeout"); |
|---|
| 1439 | + DSI_PORT_BIT(INT_HSTX_TO), "HSTX timeout"); |
|---|
| 1413 | 1440 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1414 | | - DSI1_INT_LPRX_TO, "LPRX timeout"); |
|---|
| 1441 | + DSI_PORT_BIT(INT_LPRX_TO), "LPRX timeout"); |
|---|
| 1415 | 1442 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1416 | | - DSI1_INT_TA_TO, "turnaround timeout"); |
|---|
| 1443 | + DSI_PORT_BIT(INT_TA_TO), "turnaround timeout"); |
|---|
| 1417 | 1444 | dsi_handle_error(dsi, &ret, stat, |
|---|
| 1418 | | - DSI1_INT_PR_TO, "peripheral reset timeout"); |
|---|
| 1445 | + DSI_PORT_BIT(INT_PR_TO), "peripheral reset timeout"); |
|---|
| 1419 | 1446 | |
|---|
| 1420 | | - if (stat & (DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF)) { |
|---|
| 1447 | + if (stat & ((dsi->variant->port ? DSI1_INT_TXPKT1_DONE : |
|---|
| 1448 | + DSI0_INT_CMDC_DONE_MASK) | |
|---|
| 1449 | + DSI_PORT_BIT(INT_PHY_DIR_RTF))) { |
|---|
| 1421 | 1450 | complete(&dsi->xfer_completion); |
|---|
| 1422 | 1451 | ret = IRQ_HANDLED; |
|---|
| 1423 | | - } else if (stat & DSI1_INT_HSTX_TO) { |
|---|
| 1452 | + } else if (stat & DSI_PORT_BIT(INT_HSTX_TO)) { |
|---|
| 1424 | 1453 | complete(&dsi->xfer_completion); |
|---|
| 1425 | 1454 | dsi->xfer_result = -ETIMEDOUT; |
|---|
| 1426 | 1455 | ret = IRQ_HANDLED; |
|---|
| .. | .. |
|---|
| 1440 | 1469 | struct device *dev = &dsi->pdev->dev; |
|---|
| 1441 | 1470 | const char *parent_name = __clk_get_name(dsi->pll_phy_clock); |
|---|
| 1442 | 1471 | static const struct { |
|---|
| 1443 | | - const char *dsi0_name, *dsi1_name; |
|---|
| 1472 | + const char *name; |
|---|
| 1444 | 1473 | int div; |
|---|
| 1445 | 1474 | } phy_clocks[] = { |
|---|
| 1446 | | - { "dsi0_byte", "dsi1_byte", 8 }, |
|---|
| 1447 | | - { "dsi0_ddr2", "dsi1_ddr2", 4 }, |
|---|
| 1448 | | - { "dsi0_ddr", "dsi1_ddr", 2 }, |
|---|
| 1475 | + { "byte", 8 }, |
|---|
| 1476 | + { "ddr2", 4 }, |
|---|
| 1477 | + { "ddr", 2 }, |
|---|
| 1449 | 1478 | }; |
|---|
| 1450 | 1479 | int i; |
|---|
| 1451 | 1480 | |
|---|
| .. | .. |
|---|
| 1461 | 1490 | for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) { |
|---|
| 1462 | 1491 | struct clk_fixed_factor *fix = &dsi->phy_clocks[i]; |
|---|
| 1463 | 1492 | struct clk_init_data init; |
|---|
| 1493 | + char clk_name[16]; |
|---|
| 1464 | 1494 | int ret; |
|---|
| 1495 | + |
|---|
| 1496 | + snprintf(clk_name, sizeof(clk_name), |
|---|
| 1497 | + "dsi%u_%s", dsi->variant->port, phy_clocks[i].name); |
|---|
| 1465 | 1498 | |
|---|
| 1466 | 1499 | /* We just use core fixed factor clock ops for the PHY |
|---|
| 1467 | 1500 | * clocks. The clocks are actually gated by the |
|---|
| .. | .. |
|---|
| 1479 | 1512 | memset(&init, 0, sizeof(init)); |
|---|
| 1480 | 1513 | init.parent_names = &parent_name; |
|---|
| 1481 | 1514 | init.num_parents = 1; |
|---|
| 1482 | | - if (dsi->port == 1) |
|---|
| 1483 | | - init.name = phy_clocks[i].dsi1_name; |
|---|
| 1484 | | - else |
|---|
| 1485 | | - init.name = phy_clocks[i].dsi0_name; |
|---|
| 1515 | + init.name = clk_name; |
|---|
| 1486 | 1516 | init.ops = &clk_fixed_factor_ops; |
|---|
| 1487 | 1517 | |
|---|
| 1488 | 1518 | ret = devm_clk_hw_register(dev, &fix->hw); |
|---|
| .. | .. |
|---|
| 1501 | 1531 | { |
|---|
| 1502 | 1532 | struct platform_device *pdev = to_platform_device(dev); |
|---|
| 1503 | 1533 | struct drm_device *drm = dev_get_drvdata(master); |
|---|
| 1504 | | - struct vc4_dev *vc4 = to_vc4_dev(drm); |
|---|
| 1505 | 1534 | struct vc4_dsi *dsi = dev_get_drvdata(dev); |
|---|
| 1506 | 1535 | struct vc4_dsi_encoder *vc4_dsi_encoder; |
|---|
| 1507 | 1536 | struct drm_panel *panel; |
|---|
| .. | .. |
|---|
| 1513 | 1542 | if (!match) |
|---|
| 1514 | 1543 | return -ENODEV; |
|---|
| 1515 | 1544 | |
|---|
| 1516 | | - dsi->port = (uintptr_t)match->data; |
|---|
| 1545 | + dsi->variant = match->data; |
|---|
| 1517 | 1546 | |
|---|
| 1518 | 1547 | vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder), |
|---|
| 1519 | 1548 | GFP_KERNEL); |
|---|
| 1520 | 1549 | if (!vc4_dsi_encoder) |
|---|
| 1521 | 1550 | return -ENOMEM; |
|---|
| 1522 | | - vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1; |
|---|
| 1551 | + |
|---|
| 1552 | + INIT_LIST_HEAD(&dsi->bridge_chain); |
|---|
| 1553 | + vc4_dsi_encoder->base.type = dsi->variant->port ? |
|---|
| 1554 | + VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; |
|---|
| 1523 | 1555 | vc4_dsi_encoder->dsi = dsi; |
|---|
| 1524 | 1556 | dsi->encoder = &vc4_dsi_encoder->base.base; |
|---|
| 1525 | 1557 | |
|---|
| 1526 | 1558 | dsi->regs = vc4_ioremap_regs(pdev, 0); |
|---|
| 1527 | 1559 | if (IS_ERR(dsi->regs)) |
|---|
| 1528 | 1560 | return PTR_ERR(dsi->regs); |
|---|
| 1561 | + |
|---|
| 1562 | + dsi->regset.base = dsi->regs; |
|---|
| 1563 | + dsi->regset.regs = dsi->variant->regs; |
|---|
| 1564 | + dsi->regset.nregs = dsi->variant->nregs; |
|---|
| 1529 | 1565 | |
|---|
| 1530 | 1566 | if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { |
|---|
| 1531 | 1567 | dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", |
|---|
| .. | .. |
|---|
| 1537 | 1573 | * from the ARM. It does handle writes from the DMA engine, |
|---|
| 1538 | 1574 | * so set up a channel for talking to it. |
|---|
| 1539 | 1575 | */ |
|---|
| 1540 | | - if (dsi->port == 1) { |
|---|
| 1576 | + if (dsi->variant->broken_axi_workaround) { |
|---|
| 1541 | 1577 | dsi->reg_dma_mem = dma_alloc_coherent(dev, 4, |
|---|
| 1542 | 1578 | &dsi->reg_dma_paddr, |
|---|
| 1543 | 1579 | GFP_KERNEL); |
|---|
| .. | .. |
|---|
| 1626 | 1662 | } |
|---|
| 1627 | 1663 | |
|---|
| 1628 | 1664 | if (panel) { |
|---|
| 1629 | | - dsi->bridge = devm_drm_panel_bridge_add(dev, panel, |
|---|
| 1630 | | - DRM_MODE_CONNECTOR_DSI); |
|---|
| 1665 | + dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel, |
|---|
| 1666 | + DRM_MODE_CONNECTOR_DSI); |
|---|
| 1631 | 1667 | if (IS_ERR(dsi->bridge)) |
|---|
| 1632 | 1668 | return PTR_ERR(dsi->bridge); |
|---|
| 1633 | 1669 | } |
|---|
| .. | .. |
|---|
| 1643 | 1679 | if (ret) |
|---|
| 1644 | 1680 | return ret; |
|---|
| 1645 | 1681 | |
|---|
| 1646 | | - if (dsi->port == 1) |
|---|
| 1647 | | - vc4->dsi1 = dsi; |
|---|
| 1648 | | - |
|---|
| 1649 | | - drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, |
|---|
| 1650 | | - DRM_MODE_ENCODER_DSI, NULL); |
|---|
| 1682 | + drm_simple_encoder_init(drm, dsi->encoder, DRM_MODE_ENCODER_DSI); |
|---|
| 1651 | 1683 | drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); |
|---|
| 1652 | 1684 | |
|---|
| 1653 | | - ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); |
|---|
| 1685 | + ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, 0); |
|---|
| 1654 | 1686 | if (ret) { |
|---|
| 1655 | 1687 | dev_err(dev, "bridge attach failed: %d\n", ret); |
|---|
| 1656 | 1688 | return ret; |
|---|
| .. | .. |
|---|
| 1660 | 1692 | * from our driver, since we need to sequence them within the |
|---|
| 1661 | 1693 | * encoder's enable/disable paths. |
|---|
| 1662 | 1694 | */ |
|---|
| 1663 | | - dsi->encoder->bridge = NULL; |
|---|
| 1695 | + list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain); |
|---|
| 1696 | + |
|---|
| 1697 | + vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); |
|---|
| 1664 | 1698 | |
|---|
| 1665 | 1699 | pm_runtime_enable(dev); |
|---|
| 1666 | 1700 | |
|---|
| .. | .. |
|---|
| 1670 | 1704 | static void vc4_dsi_unbind(struct device *dev, struct device *master, |
|---|
| 1671 | 1705 | void *data) |
|---|
| 1672 | 1706 | { |
|---|
| 1673 | | - struct drm_device *drm = dev_get_drvdata(master); |
|---|
| 1674 | | - struct vc4_dev *vc4 = to_vc4_dev(drm); |
|---|
| 1675 | 1707 | struct vc4_dsi *dsi = dev_get_drvdata(dev); |
|---|
| 1676 | 1708 | |
|---|
| 1677 | 1709 | if (dsi->bridge) |
|---|
| 1678 | 1710 | pm_runtime_disable(dev); |
|---|
| 1679 | 1711 | |
|---|
| 1680 | | - vc4_dsi_encoder_destroy(dsi->encoder); |
|---|
| 1681 | | - |
|---|
| 1682 | | - if (dsi->port == 1) |
|---|
| 1683 | | - vc4->dsi1 = NULL; |
|---|
| 1712 | + /* |
|---|
| 1713 | + * Restore the bridge_chain so the bridge detach procedure can happen |
|---|
| 1714 | + * normally. |
|---|
| 1715 | + */ |
|---|
| 1716 | + list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain); |
|---|
| 1717 | + drm_encoder_cleanup(dsi->encoder); |
|---|
| 1684 | 1718 | } |
|---|
| 1685 | 1719 | |
|---|
| 1686 | 1720 | static const struct component_ops vc4_dsi_ops = { |
|---|