From 9d77db3c730780c8ef5ccd4b66403ff5675cfe4e Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 13 May 2024 10:30:14 +0000 Subject: [PATCH] modify sin led gpio --- kernel/drivers/gpu/drm/vc4/vc4_dsi.c | 456 ++++++++++++++++++++++++++++++-------------------------- 1 files changed, 245 insertions(+), 211 deletions(-) diff --git a/kernel/drivers/gpu/drm/vc4/vc4_dsi.c b/kernel/drivers/gpu/drm/vc4/vc4_dsi.c index 0c607eb..0bda40c 100644 --- a/kernel/drivers/gpu/drm/vc4/vc4_dsi.c +++ b/kernel/drivers/gpu/drm/vc4/vc4_dsi.c @@ -1,17 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ /** @@ -29,21 +18,27 @@ * hopefully present. */ +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/component.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/i2c.h> +#include <linux/io.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> + #include <drm/drm_atomic_helper.h> -#include <drm/drm_crtc_helper.h> +#include <drm/drm_bridge.h> #include <drm/drm_edid.h> #include <drm/drm_mipi_dsi.h> #include <drm/drm_of.h> #include <drm/drm_panel.h> -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/completion.h> -#include <linux/component.h> -#include <linux/dmaengine.h> -#include <linux/i2c.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> -#include <linux/pm_runtime.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_simple_kms_helper.h> + #include "vc4_drv.h" #include "vc4_regs.h" @@ -186,8 +181,50 @@ #define DSI0_TXPKT_PIX_FIFO 0x20 /* AKA PIX_FIFO */ -#define DSI0_INT_STAT 0x24 -#define DSI0_INT_EN 0x28 +#define DSI0_INT_STAT 0x24 +#define DSI0_INT_EN 0x28 +# define DSI0_INT_FIFO_ERR BIT(25) +# define DSI0_INT_CMDC_DONE_MASK VC4_MASK(24, 23) +# define DSI0_INT_CMDC_DONE_SHIFT 23 +# define DSI0_INT_CMDC_DONE_NO_REPEAT 1 +# define DSI0_INT_CMDC_DONE_REPEAT 3 +# define DSI0_INT_PHY_DIR_RTF BIT(22) +# define DSI0_INT_PHY_D1_ULPS BIT(21) +# define DSI0_INT_PHY_D1_STOP BIT(20) +# define DSI0_INT_PHY_RXLPDT BIT(19) +# define DSI0_INT_PHY_RXTRIG BIT(18) +# define DSI0_INT_PHY_D0_ULPS BIT(17) +# define DSI0_INT_PHY_D0_LPDT BIT(16) +# define DSI0_INT_PHY_D0_FTR BIT(15) +# define DSI0_INT_PHY_D0_STOP BIT(14) +/* Signaled when the clock lane enters the given state. */ +# define DSI0_INT_PHY_CLK_ULPS BIT(13) +# define DSI0_INT_PHY_CLK_HS BIT(12) +# define DSI0_INT_PHY_CLK_FTR BIT(11) +/* Signaled on timeouts */ +# define DSI0_INT_PR_TO BIT(10) +# define DSI0_INT_TA_TO BIT(9) +# define DSI0_INT_LPRX_TO BIT(8) +# define DSI0_INT_HSTX_TO BIT(7) +/* Contention on a line when trying to drive the line low */ +# define DSI0_INT_ERR_CONT_LP1 BIT(6) +# define DSI0_INT_ERR_CONT_LP0 BIT(5) +/* Control error: incorrect line state sequence on data lane 0. */ +# define DSI0_INT_ERR_CONTROL BIT(4) +# define DSI0_INT_ERR_SYNC_ESC BIT(3) +# define DSI0_INT_RX2_PKT BIT(2) +# define DSI0_INT_RX1_PKT BIT(1) +# define DSI0_INT_CMD_PKT BIT(0) + +#define DSI0_INTERRUPTS_ALWAYS_ENABLED (DSI0_INT_ERR_SYNC_ESC | \ + DSI0_INT_ERR_CONTROL | \ + DSI0_INT_ERR_CONT_LP0 | \ + DSI0_INT_ERR_CONT_LP1 | \ + DSI0_INT_HSTX_TO | \ + DSI0_INT_LPRX_TO | \ + DSI0_INT_TA_TO | \ + DSI0_INT_PR_TO) + # define DSI1_INT_PHY_D3_ULPS BIT(30) # define DSI1_INT_PHY_D3_STOP BIT(29) # define DSI1_INT_PHY_D2_ULPS BIT(28) @@ -498,6 +535,18 @@ */ #define DSI1_ID 0x8c +struct vc4_dsi_variant { + /* Whether we're on bcm2835's DSI0 or DSI1. */ + unsigned int port; + + bool broken_axi_workaround; + + const char *debugfs_name; + const struct debugfs_reg32 *regs; + size_t nregs; + +}; + /* General DSI hardware state. */ struct vc4_dsi { struct platform_device *pdev; @@ -505,6 +554,7 @@ struct mipi_dsi_host dsi_host; struct drm_encoder *encoder; struct drm_bridge *bridge; + struct list_head bridge_chain; void __iomem *regs; @@ -513,8 +563,7 @@ u32 *reg_dma_mem; dma_addr_t reg_paddr; - /* Whether we're on bcm2835's DSI0 or DSI1. */ - int port; + const struct vc4_dsi_variant *variant; /* DSI channel for the panel we're connected to. */ u32 channel; @@ -545,6 +594,8 @@ struct completion xfer_completion; int xfer_result; + + struct debugfs_regset32 regset; }; #define host_to_dsi(host) container_of(host, struct vc4_dsi, dsi_host) @@ -588,10 +639,10 @@ #define DSI_READ(offset) readl(dsi->regs + (offset)) #define DSI_WRITE(offset, val) dsi_dma_workaround_write(dsi, offset, val) #define DSI_PORT_READ(offset) \ - DSI_READ(dsi->port ? DSI1_##offset : DSI0_##offset) + DSI_READ(dsi->variant->port ? DSI1_##offset : DSI0_##offset) #define DSI_PORT_WRITE(offset, val) \ - DSI_WRITE(dsi->port ? DSI1_##offset : DSI0_##offset, val) -#define DSI_PORT_BIT(bit) (dsi->port ? DSI1_##bit : DSI0_##bit) + DSI_WRITE(dsi->variant->port ? DSI1_##offset : DSI0_##offset, val) +#define DSI_PORT_BIT(bit) (dsi->variant->port ? DSI1_##bit : DSI0_##bit) /* VC4 DSI encoder KMS struct */ struct vc4_dsi_encoder { @@ -605,120 +656,54 @@ return container_of(encoder, struct vc4_dsi_encoder, base.base); } -#define DSI_REG(reg) { reg, #reg } -static const struct { - u32 reg; - const char *name; -} dsi0_regs[] = { - DSI_REG(DSI0_CTRL), - DSI_REG(DSI0_STAT), - DSI_REG(DSI0_HSTX_TO_CNT), - DSI_REG(DSI0_LPRX_TO_CNT), - DSI_REG(DSI0_TA_TO_CNT), - DSI_REG(DSI0_PR_TO_CNT), - DSI_REG(DSI0_DISP0_CTRL), - DSI_REG(DSI0_DISP1_CTRL), - DSI_REG(DSI0_INT_STAT), - DSI_REG(DSI0_INT_EN), - DSI_REG(DSI0_PHYC), - DSI_REG(DSI0_HS_CLT0), - DSI_REG(DSI0_HS_CLT1), - DSI_REG(DSI0_HS_CLT2), - DSI_REG(DSI0_HS_DLT3), - DSI_REG(DSI0_HS_DLT4), - DSI_REG(DSI0_HS_DLT5), - DSI_REG(DSI0_HS_DLT6), - DSI_REG(DSI0_HS_DLT7), - DSI_REG(DSI0_PHY_AFEC0), - DSI_REG(DSI0_PHY_AFEC1), - DSI_REG(DSI0_ID), +static const struct debugfs_reg32 dsi0_regs[] = { + VC4_REG32(DSI0_CTRL), + VC4_REG32(DSI0_STAT), + VC4_REG32(DSI0_HSTX_TO_CNT), + VC4_REG32(DSI0_LPRX_TO_CNT), + VC4_REG32(DSI0_TA_TO_CNT), + VC4_REG32(DSI0_PR_TO_CNT), + VC4_REG32(DSI0_DISP0_CTRL), + VC4_REG32(DSI0_DISP1_CTRL), + VC4_REG32(DSI0_INT_STAT), + VC4_REG32(DSI0_INT_EN), + VC4_REG32(DSI0_PHYC), + VC4_REG32(DSI0_HS_CLT0), + VC4_REG32(DSI0_HS_CLT1), + VC4_REG32(DSI0_HS_CLT2), + VC4_REG32(DSI0_HS_DLT3), + VC4_REG32(DSI0_HS_DLT4), + VC4_REG32(DSI0_HS_DLT5), + VC4_REG32(DSI0_HS_DLT6), + VC4_REG32(DSI0_HS_DLT7), + VC4_REG32(DSI0_PHY_AFEC0), + VC4_REG32(DSI0_PHY_AFEC1), + VC4_REG32(DSI0_ID), }; -static const struct { - u32 reg; - const char *name; -} dsi1_regs[] = { - DSI_REG(DSI1_CTRL), - DSI_REG(DSI1_STAT), - DSI_REG(DSI1_HSTX_TO_CNT), - DSI_REG(DSI1_LPRX_TO_CNT), - DSI_REG(DSI1_TA_TO_CNT), - DSI_REG(DSI1_PR_TO_CNT), - DSI_REG(DSI1_DISP0_CTRL), - DSI_REG(DSI1_DISP1_CTRL), - DSI_REG(DSI1_INT_STAT), - DSI_REG(DSI1_INT_EN), - DSI_REG(DSI1_PHYC), - DSI_REG(DSI1_HS_CLT0), - DSI_REG(DSI1_HS_CLT1), - DSI_REG(DSI1_HS_CLT2), - DSI_REG(DSI1_HS_DLT3), - DSI_REG(DSI1_HS_DLT4), - DSI_REG(DSI1_HS_DLT5), - DSI_REG(DSI1_HS_DLT6), - DSI_REG(DSI1_HS_DLT7), - DSI_REG(DSI1_PHY_AFEC0), - DSI_REG(DSI1_PHY_AFEC1), - DSI_REG(DSI1_ID), -}; - -static void vc4_dsi_dump_regs(struct vc4_dsi *dsi) -{ - int i; - - if (dsi->port == 0) { - for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - dsi0_regs[i].reg, dsi0_regs[i].name, - DSI_READ(dsi0_regs[i].reg)); - } - } else { - for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { - DRM_INFO("0x%04x (%s): 0x%08x\n", - dsi1_regs[i].reg, dsi1_regs[i].name, - DSI_READ(dsi1_regs[i].reg)); - } - } -} - -#ifdef CONFIG_DEBUG_FS -int vc4_dsi_debugfs_regs(struct seq_file *m, void *unused) -{ - struct drm_info_node *node = (struct drm_info_node *)m->private; - struct drm_device *drm = node->minor->dev; - struct vc4_dev *vc4 = to_vc4_dev(drm); - int dsi_index = (uintptr_t)node->info_ent->data; - struct vc4_dsi *dsi = (dsi_index == 1 ? vc4->dsi1 : NULL); - int i; - - if (!dsi) - return 0; - - if (dsi->port == 0) { - for (i = 0; i < ARRAY_SIZE(dsi0_regs); i++) { - seq_printf(m, "0x%04x (%s): 0x%08x\n", - dsi0_regs[i].reg, dsi0_regs[i].name, - DSI_READ(dsi0_regs[i].reg)); - } - } else { - for (i = 0; i < ARRAY_SIZE(dsi1_regs); i++) { - seq_printf(m, "0x%04x (%s): 0x%08x\n", - dsi1_regs[i].reg, dsi1_regs[i].name, - DSI_READ(dsi1_regs[i].reg)); - } - } - - return 0; -} -#endif - -static void vc4_dsi_encoder_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); -} - -static const struct drm_encoder_funcs vc4_dsi_encoder_funcs = { - .destroy = vc4_dsi_encoder_destroy, +static const struct debugfs_reg32 dsi1_regs[] = { + VC4_REG32(DSI1_CTRL), + VC4_REG32(DSI1_STAT), + VC4_REG32(DSI1_HSTX_TO_CNT), + VC4_REG32(DSI1_LPRX_TO_CNT), + VC4_REG32(DSI1_TA_TO_CNT), + VC4_REG32(DSI1_PR_TO_CNT), + VC4_REG32(DSI1_DISP0_CTRL), + VC4_REG32(DSI1_DISP1_CTRL), + VC4_REG32(DSI1_INT_STAT), + VC4_REG32(DSI1_INT_EN), + VC4_REG32(DSI1_PHYC), + VC4_REG32(DSI1_HS_CLT0), + VC4_REG32(DSI1_HS_CLT1), + VC4_REG32(DSI1_HS_CLT2), + VC4_REG32(DSI1_HS_DLT3), + VC4_REG32(DSI1_HS_DLT4), + VC4_REG32(DSI1_HS_DLT5), + VC4_REG32(DSI1_HS_DLT6), + VC4_REG32(DSI1_HS_DLT7), + VC4_REG32(DSI1_PHY_AFEC0), + VC4_REG32(DSI1_PHY_AFEC1), + VC4_REG32(DSI1_ID), }; static void vc4_dsi_latch_ulps(struct vc4_dsi *dsi, bool latch) @@ -813,10 +798,22 @@ struct vc4_dsi_encoder *vc4_encoder = to_vc4_dsi_encoder(encoder); struct vc4_dsi *dsi = vc4_encoder->dsi; struct device *dev = &dsi->pdev->dev; + struct drm_bridge *iter; - drm_bridge_disable(dsi->bridge); + list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { + if (iter->funcs->disable) + iter->funcs->disable(iter); + + if (iter == dsi->bridge) + break; + } + vc4_dsi_ulps(dsi, true); - drm_bridge_post_disable(dsi->bridge); + + list_for_each_entry_from(iter, &dsi->bridge_chain, chain_node) { + if (iter->funcs->post_disable) + iter->funcs->post_disable(iter); + } clk_disable_unprepare(dsi->pll_phy_clock); clk_disable_unprepare(dsi->escape_clock); @@ -853,11 +850,9 @@ /* Find what divider gets us a faster clock than the requested * pixel clock. */ - for (divider = 1; divider < 8; divider++) { - if (parent_rate / divider < pll_clock) { - divider--; + for (divider = 1; divider < 255; divider++) { + if (parent_rate / (divider + 1) < pll_clock) break; - } } /* Now that we've picked a PLL divider, calculate back to its @@ -884,6 +879,7 @@ struct vc4_dsi *dsi = vc4_encoder->dsi; struct device *dev = &dsi->pdev->dev; bool debug_dump_regs = false; + struct drm_bridge *iter; unsigned long hs_clock; u32 ui_ns; /* Minimum LP state duration in escape clock cycles. */ @@ -893,15 +889,16 @@ unsigned long phy_clock; int ret; - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret) { - DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->port); + DRM_ERROR("Failed to runtime PM enable on DSI%d\n", dsi->variant->port); return; } if (debug_dump_regs) { - DRM_INFO("DSI regs before:\n"); - vc4_dsi_dump_regs(dsi); + struct drm_printer p = drm_info_printer(&dsi->pdev->dev); + dev_info(&dsi->pdev->dev, "DSI regs before:\n"); + drm_print_regset32(&p, &dsi->regset); } /* Round up the clk_set_rate() request slightly, since @@ -928,7 +925,7 @@ DSI_PORT_WRITE(STAT, DSI_PORT_READ(STAT)); /* Set AFE CTR00/CTR1 to release powerdown of analog. */ - if (dsi->port == 0) { + if (dsi->variant->port == 0) { u32 afec0 = (VC4_SET_FIELD(7, DSI_PHY_AFEC0_PTATADJ) | VC4_SET_FIELD(7, DSI_PHY_AFEC0_CTATADJ)); @@ -939,6 +936,9 @@ afec0 |= DSI0_PHY_AFEC0_RESET; DSI_PORT_WRITE(PHY_AFEC0, afec0); + + /* AFEC reset hold time */ + mdelay(1); DSI_PORT_WRITE(PHY_AFEC1, VC4_SET_FIELD(6, DSI0_PHY_AFEC1_IDR_DLANE1) | @@ -1074,7 +1074,7 @@ DSI_PORT_BIT(PHYC_CLANE_ENABLE) | ((dsi->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : DSI_PORT_BIT(PHYC_HS_CLK_CONTINUOUS)) | - (dsi->port == 0 ? + (dsi->variant->port == 0 ? VC4_SET_FIELD(lpx - 1, DSI0_PHYC_ESC_CLK_LPDT) : VC4_SET_FIELD(lpx - 1, DSI1_PHYC_ESC_CLK_LPDT))); @@ -1100,22 +1100,22 @@ DSI_DISP1_ENABLE); /* Ungate the block. */ - if (dsi->port == 0) + if (dsi->variant->port == 0) DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI0_CTRL_CTRL0); else DSI_PORT_WRITE(CTRL, DSI_PORT_READ(CTRL) | DSI1_CTRL_EN); /* Bring AFE out of reset. */ - if (dsi->port == 0) { - } else { - DSI_PORT_WRITE(PHY_AFEC0, - DSI_PORT_READ(PHY_AFEC0) & - ~DSI1_PHY_AFEC0_RESET); - } + DSI_PORT_WRITE(PHY_AFEC0, + DSI_PORT_READ(PHY_AFEC0) & + ~DSI_PORT_BIT(PHY_AFEC0_RESET)); vc4_dsi_ulps(dsi, false); - drm_bridge_pre_enable(dsi->bridge); + list_for_each_entry_reverse(iter, &dsi->bridge_chain, chain_node) { + if (iter->funcs->pre_enable) + iter->funcs->pre_enable(iter); + } if (dsi->mode_flags & MIPI_DSI_MODE_VIDEO) { DSI_PORT_WRITE(DISP0_CTRL, @@ -1132,11 +1132,15 @@ DSI_DISP0_ENABLE); } - drm_bridge_enable(dsi->bridge); + list_for_each_entry(iter, &dsi->bridge_chain, chain_node) { + if (iter->funcs->enable) + iter->funcs->enable(iter); + } if (debug_dump_regs) { - DRM_INFO("DSI regs after:\n"); - vc4_dsi_dump_regs(dsi); + struct drm_printer p = drm_info_printer(&dsi->pdev->dev); + dev_info(&dsi->pdev->dev, "DSI regs after:\n"); + drm_print_regset32(&p, &dsi->regset); } } @@ -1223,13 +1227,28 @@ /* Enable the appropriate interrupt for the transfer completion. */ dsi->xfer_result = 0; reinit_completion(&dsi->xfer_completion); - DSI_PORT_WRITE(INT_STAT, DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); - if (msg->rx_len) { - DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | - DSI1_INT_PHY_DIR_RTF)); + if (dsi->variant->port == 0) { + DSI_PORT_WRITE(INT_STAT, + DSI0_INT_CMDC_DONE_MASK | DSI1_INT_PHY_DIR_RTF); + if (msg->rx_len) { + DSI_PORT_WRITE(INT_EN, (DSI0_INTERRUPTS_ALWAYS_ENABLED | + DSI0_INT_PHY_DIR_RTF)); + } else { + DSI_PORT_WRITE(INT_EN, + (DSI0_INTERRUPTS_ALWAYS_ENABLED | + VC4_SET_FIELD(DSI0_INT_CMDC_DONE_NO_REPEAT, + DSI0_INT_CMDC_DONE))); + } } else { - DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | - DSI1_INT_TXPKT1_DONE)); + DSI_PORT_WRITE(INT_STAT, + DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF); + if (msg->rx_len) { + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | + DSI1_INT_PHY_DIR_RTF)); + } else { + DSI_PORT_WRITE(INT_EN, (DSI1_INTERRUPTS_ALWAYS_ENABLED | + DSI1_INT_TXPKT1_DONE)); + } } /* Send the packet. */ @@ -1246,7 +1265,7 @@ ret = dsi->xfer_result; } - DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); if (ret) goto reset_fifo_and_return; @@ -1292,7 +1311,7 @@ DSI_PORT_BIT(CTRL_RESET_FIFOS)); DSI_PORT_WRITE(TXPKT1C, 0); - DSI_PORT_WRITE(INT_EN, DSI1_INTERRUPTS_ALWAYS_ENABLED); + DSI_PORT_WRITE(INT_EN, DSI_PORT_BIT(INTERRUPTS_ALWAYS_ENABLED)); return ret; } @@ -1355,8 +1374,16 @@ .mode_fixup = vc4_dsi_encoder_mode_fixup, }; +static const struct vc4_dsi_variant bcm2835_dsi1_variant = { + .port = 1, + .broken_axi_workaround = true, + .debugfs_name = "dsi1_regs", + .regs = dsi1_regs, + .nregs = ARRAY_SIZE(dsi1_regs), +}; + static const struct of_device_id vc4_dsi_dt_match[] = { - { .compatible = "brcm,bcm2835-dsi1", (void *)(uintptr_t)1 }, + { .compatible = "brcm,bcm2835-dsi1", &bcm2835_dsi1_variant }, {} }; @@ -1367,7 +1394,7 @@ if (!(stat & bit)) return; - DRM_ERROR("DSI%d: %s error\n", dsi->port, type); + DRM_ERROR("DSI%d: %s error\n", dsi->variant->port, type); *ret = IRQ_HANDLED; } @@ -1401,26 +1428,28 @@ DSI_PORT_WRITE(INT_STAT, stat); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_ERR_SYNC_ESC, "LPDT sync"); + DSI_PORT_BIT(INT_ERR_SYNC_ESC), "LPDT sync"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_ERR_CONTROL, "data lane 0 sequence"); + DSI_PORT_BIT(INT_ERR_CONTROL), "data lane 0 sequence"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_ERR_CONT_LP0, "LP0 contention"); + DSI_PORT_BIT(INT_ERR_CONT_LP0), "LP0 contention"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_ERR_CONT_LP1, "LP1 contention"); + DSI_PORT_BIT(INT_ERR_CONT_LP1), "LP1 contention"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_HSTX_TO, "HSTX timeout"); + DSI_PORT_BIT(INT_HSTX_TO), "HSTX timeout"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_LPRX_TO, "LPRX timeout"); + DSI_PORT_BIT(INT_LPRX_TO), "LPRX timeout"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_TA_TO, "turnaround timeout"); + DSI_PORT_BIT(INT_TA_TO), "turnaround timeout"); dsi_handle_error(dsi, &ret, stat, - DSI1_INT_PR_TO, "peripheral reset timeout"); + DSI_PORT_BIT(INT_PR_TO), "peripheral reset timeout"); - if (stat & (DSI1_INT_TXPKT1_DONE | DSI1_INT_PHY_DIR_RTF)) { + if (stat & ((dsi->variant->port ? DSI1_INT_TXPKT1_DONE : + DSI0_INT_CMDC_DONE_MASK) | + DSI_PORT_BIT(INT_PHY_DIR_RTF))) { complete(&dsi->xfer_completion); ret = IRQ_HANDLED; - } else if (stat & DSI1_INT_HSTX_TO) { + } else if (stat & DSI_PORT_BIT(INT_HSTX_TO)) { complete(&dsi->xfer_completion); dsi->xfer_result = -ETIMEDOUT; ret = IRQ_HANDLED; @@ -1440,12 +1469,12 @@ struct device *dev = &dsi->pdev->dev; const char *parent_name = __clk_get_name(dsi->pll_phy_clock); static const struct { - const char *dsi0_name, *dsi1_name; + const char *name; int div; } phy_clocks[] = { - { "dsi0_byte", "dsi1_byte", 8 }, - { "dsi0_ddr2", "dsi1_ddr2", 4 }, - { "dsi0_ddr", "dsi1_ddr", 2 }, + { "byte", 8 }, + { "ddr2", 4 }, + { "ddr", 2 }, }; int i; @@ -1461,7 +1490,11 @@ for (i = 0; i < ARRAY_SIZE(phy_clocks); i++) { struct clk_fixed_factor *fix = &dsi->phy_clocks[i]; struct clk_init_data init; + char clk_name[16]; int ret; + + snprintf(clk_name, sizeof(clk_name), + "dsi%u_%s", dsi->variant->port, phy_clocks[i].name); /* We just use core fixed factor clock ops for the PHY * clocks. The clocks are actually gated by the @@ -1479,10 +1512,7 @@ memset(&init, 0, sizeof(init)); init.parent_names = &parent_name; init.num_parents = 1; - if (dsi->port == 1) - init.name = phy_clocks[i].dsi1_name; - else - init.name = phy_clocks[i].dsi0_name; + init.name = clk_name; init.ops = &clk_fixed_factor_ops; ret = devm_clk_hw_register(dev, &fix->hw); @@ -1501,7 +1531,6 @@ { struct platform_device *pdev = to_platform_device(dev); struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dsi *dsi = dev_get_drvdata(dev); struct vc4_dsi_encoder *vc4_dsi_encoder; struct drm_panel *panel; @@ -1513,19 +1542,26 @@ if (!match) return -ENODEV; - dsi->port = (uintptr_t)match->data; + dsi->variant = match->data; vc4_dsi_encoder = devm_kzalloc(dev, sizeof(*vc4_dsi_encoder), GFP_KERNEL); if (!vc4_dsi_encoder) return -ENOMEM; - vc4_dsi_encoder->base.type = VC4_ENCODER_TYPE_DSI1; + + INIT_LIST_HEAD(&dsi->bridge_chain); + vc4_dsi_encoder->base.type = dsi->variant->port ? + VC4_ENCODER_TYPE_DSI1 : VC4_ENCODER_TYPE_DSI0; vc4_dsi_encoder->dsi = dsi; dsi->encoder = &vc4_dsi_encoder->base.base; dsi->regs = vc4_ioremap_regs(pdev, 0); if (IS_ERR(dsi->regs)) return PTR_ERR(dsi->regs); + + dsi->regset.base = dsi->regs; + dsi->regset.regs = dsi->variant->regs; + dsi->regset.nregs = dsi->variant->nregs; if (DSI_PORT_READ(ID) != DSI_ID_VALUE) { dev_err(dev, "Port returned 0x%08x for ID instead of 0x%08x\n", @@ -1537,7 +1573,7 @@ * from the ARM. It does handle writes from the DMA engine, * so set up a channel for talking to it. */ - if (dsi->port == 1) { + if (dsi->variant->broken_axi_workaround) { dsi->reg_dma_mem = dma_alloc_coherent(dev, 4, &dsi->reg_dma_paddr, GFP_KERNEL); @@ -1626,8 +1662,8 @@ } if (panel) { - dsi->bridge = devm_drm_panel_bridge_add(dev, panel, - DRM_MODE_CONNECTOR_DSI); + dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel, + DRM_MODE_CONNECTOR_DSI); if (IS_ERR(dsi->bridge)) return PTR_ERR(dsi->bridge); } @@ -1643,14 +1679,10 @@ if (ret) return ret; - if (dsi->port == 1) - vc4->dsi1 = dsi; - - drm_encoder_init(drm, dsi->encoder, &vc4_dsi_encoder_funcs, - DRM_MODE_ENCODER_DSI, NULL); + drm_simple_encoder_init(drm, dsi->encoder, DRM_MODE_ENCODER_DSI); drm_encoder_helper_add(dsi->encoder, &vc4_dsi_encoder_helper_funcs); - ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL); + ret = drm_bridge_attach(dsi->encoder, dsi->bridge, NULL, 0); if (ret) { dev_err(dev, "bridge attach failed: %d\n", ret); return ret; @@ -1660,7 +1692,9 @@ * from our driver, since we need to sequence them within the * encoder's enable/disable paths. */ - dsi->encoder->bridge = NULL; + list_splice_init(&dsi->encoder->bridge_chain, &dsi->bridge_chain); + + vc4_debugfs_add_regset32(drm, dsi->variant->debugfs_name, &dsi->regset); pm_runtime_enable(dev); @@ -1670,17 +1704,17 @@ static void vc4_dsi_unbind(struct device *dev, struct device *master, void *data) { - struct drm_device *drm = dev_get_drvdata(master); - struct vc4_dev *vc4 = to_vc4_dev(drm); struct vc4_dsi *dsi = dev_get_drvdata(dev); if (dsi->bridge) pm_runtime_disable(dev); - vc4_dsi_encoder_destroy(dsi->encoder); - - if (dsi->port == 1) - vc4->dsi1 = NULL; + /* + * Restore the bridge_chain so the bridge detach procedure can happen + * normally. + */ + list_splice_init(&dsi->bridge_chain, &dsi->encoder->bridge_chain); + drm_encoder_cleanup(dsi->encoder); } static const struct component_ops vc4_dsi_ops = { -- Gitblit v1.6.2