.. | .. |
---|
| 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 = { |
---|