Message ID | 20230512-amlogic-v6-4-upstream-dsi-ccf-vim3-v5-12-56eb7a4d5b8e@linaro.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/meson: add support for MIPI DSI Display | expand |
Le mar. 30 mai 2023 à 09:38, Neil Armstrong <neil.armstrong@linaro.org> a écrit : > > The Amlogic G12A/G12B/SM1 SoCs embeds a Synopsys DW-MIPI-DSI transceiver > (ver 1.21a), with a custom glue managing the IP resets, clock and data > inputs similar to the DW-HDMI Glue on other Amlogic SoCs. > > This adds support for the Glue managing the transceiver, mimicing the init > flow provided by Amlogic to setup the ENCL encoder, the glue, the transceiver, > the digital D-PHY and the Analog PHY in the proper way. > > An optional "MEAS" clock can be enabled to measure the delay between each > vsync feeding the DW-MIPI-DSI transceiver. > > Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> > Reviewed-by: Jagan Teki <jagan@amarulasolutions.com> > Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org> > --- > drivers/gpu/drm/meson/Kconfig | 7 + > drivers/gpu/drm/meson/Makefile | 1 + > drivers/gpu/drm/meson/meson_dw_mipi_dsi.c | 352 ++++++++++++++++++++++++++++++ > drivers/gpu/drm/meson/meson_dw_mipi_dsi.h | 160 ++++++++++++++ > 4 files changed, 520 insertions(+) > > diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig > index 823909da87db..615fdd0ce41b 100644 > --- a/drivers/gpu/drm/meson/Kconfig > +++ b/drivers/gpu/drm/meson/Kconfig > @@ -17,3 +17,10 @@ config DRM_MESON_DW_HDMI > default y if DRM_MESON > select DRM_DW_HDMI > imply DRM_DW_HDMI_I2S_AUDIO > + > +config DRM_MESON_DW_MIPI_DSI > + tristate "MIPI DSI Synopsys Controller support for Amlogic Meson Display" > + depends on DRM_MESON > + default y if DRM_MESON > + select DRM_DW_MIPI_DSI > + select GENERIC_PHY_MIPI_DPHY > diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile > index 833e18c20603..43071bdbd4b9 100644 > --- a/drivers/gpu/drm/meson/Makefile > +++ b/drivers/gpu/drm/meson/Makefile > @@ -6,3 +6,4 @@ meson-drm-y += meson_encoder_hdmi.o meson_encoder_dsi.o > > obj-$(CONFIG_DRM_MESON) += meson-drm.o > obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o > +obj-$(CONFIG_DRM_MESON_DW_MIPI_DSI) += meson_dw_mipi_dsi.o > diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c > new file mode 100644 > index 000000000000..dd505ac37976 > --- /dev/null > +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c > @@ -0,0 +1,352 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * Copyright (C) 2021 BayLibre, SAS > + * Author: Neil Armstrong <narmstrong@baylibre.com> > + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. > + */ > + > +#include <linux/clk.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/of_graph.h> > +#include <linux/reset.h> > +#include <linux/phy/phy.h> > +#include <linux/bitfield.h> > + > +#include <video/mipi_display.h> > + > +#include <drm/bridge/dw_mipi_dsi.h> > +#include <drm/drm_mipi_dsi.h> > + > +#include <drm/drm_atomic_helper.h> > +#include <drm/drm_device.h> > +#include <drm/drm_probe_helper.h> > +#include <drm/drm_print.h> > + > +#include "meson_drv.h" > +#include "meson_dw_mipi_dsi.h" > +#include "meson_registers.h" > +#include "meson_venc.h" > + > +#define DRIVER_NAME "meson-dw-mipi-dsi" > +#define DRIVER_DESC "Amlogic Meson MIPI-DSI DRM driver" > + > +struct meson_dw_mipi_dsi { > + struct meson_drm *priv; > + struct device *dev; > + void __iomem *base; > + struct phy *phy; > + union phy_configure_opts phy_opts; > + struct dw_mipi_dsi *dmd; > + struct dw_mipi_dsi_plat_data pdata; > + struct mipi_dsi_device *dsi_device; > + const struct drm_display_mode *mode; > + struct clk *bit_clk; > + struct clk *px_clk; > + struct reset_control *top_rst; > +}; > + > +#define encoder_to_meson_dw_mipi_dsi(x) \ > + container_of(x, struct meson_dw_mipi_dsi, encoder) > + > +static void meson_dw_mipi_dsi_hw_init(struct meson_dw_mipi_dsi *mipi_dsi) > +{ > + /* Software reset */ > + writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | > + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, > + MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | > + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, > + mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); > + writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | > + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, > + 0, mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); > + > + /* Enable clocks */ > + writel_bits_relaxed(MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, > + MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, > + mipi_dsi->base + MIPI_DSI_TOP_CLK_CNTL); > + > + /* Take memory out of power down */ > + writel_relaxed(0, mipi_dsi->base + MIPI_DSI_TOP_MEM_PD); > +} > + > +static int dw_mipi_dsi_phy_init(void *priv_data) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + unsigned int dpi_data_format, venc_data_width; > + int ret; > + > + /* Set the bit clock rate to hs_clk_rate */ > + ret = clk_set_rate(mipi_dsi->bit_clk, > + mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate); > + if (ret) { > + dev_err(mipi_dsi->dev, "Failed to set DSI Bit clock rate %lu (ret %d)\n", > + mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, ret); > + return ret; > + } > + > + /* Make sure the rate of the bit clock is not modified by someone else */ > + ret = clk_rate_exclusive_get(mipi_dsi->bit_clk); > + if (ret) { > + dev_err(mipi_dsi->dev, > + "Failed to set the exclusivity on the bit clock rate (ret %d)\n", ret); > + return ret; > + } > + > + ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000); > + > + if (ret) { > + dev_err(mipi_dsi->dev, "Failed to set DSI Pixel clock rate %u (%d)\n", > + mipi_dsi->mode->clock * 1000, ret); > + return ret; > + } > + > + switch (mipi_dsi->dsi_device->format) { > + case MIPI_DSI_FMT_RGB888: > + dpi_data_format = DPI_COLOR_24BIT; > + venc_data_width = VENC_IN_COLOR_24B; > + break; > + case MIPI_DSI_FMT_RGB666: > + dpi_data_format = DPI_COLOR_18BIT_CFG_2; > + venc_data_width = VENC_IN_COLOR_18B; > + break; > + case MIPI_DSI_FMT_RGB666_PACKED: > + case MIPI_DSI_FMT_RGB565: > + return -EINVAL; > + }; > + > + /* Configure color format for DPI register */ > + writel_relaxed(FIELD_PREP(MIPI_DSI_TOP_DPI_COLOR_MODE, dpi_data_format) | > + FIELD_PREP(MIPI_DSI_TOP_IN_COLOR_MODE, venc_data_width) | > + FIELD_PREP(MIPI_DSI_TOP_COMP2_SEL, 2) | > + FIELD_PREP(MIPI_DSI_TOP_COMP1_SEL, 1) | > + FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0), > + mipi_dsi->base + MIPI_DSI_TOP_CNTL); > + > + return phy_configure(mipi_dsi->phy, &mipi_dsi->phy_opts); > +} > + > +static void dw_mipi_dsi_phy_power_on(void *priv_data) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + > + if (phy_power_on(mipi_dsi->phy)) > + dev_warn(mipi_dsi->dev, "Failed to power on PHY\n"); > +} > + > +static void dw_mipi_dsi_phy_power_off(void *priv_data) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + > + if (phy_power_off(mipi_dsi->phy)) > + dev_warn(mipi_dsi->dev, "Failed to power off PHY\n"); > + > + /* Remove the exclusivity on the bit clock rate */ > + clk_rate_exclusive_put(mipi_dsi->bit_clk); > +} > + > +static int > +dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, > + unsigned long mode_flags, u32 lanes, u32 format, > + unsigned int *lane_mbps) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + int bpp; > + > + mipi_dsi->mode = mode; > + > + bpp = mipi_dsi_pixel_format_to_bpp(mipi_dsi->dsi_device->format); > + > + phy_mipi_dphy_get_default_config(mode->clock * 1000, > + bpp, mipi_dsi->dsi_device->lanes, > + &mipi_dsi->phy_opts.mipi_dphy); > + > + *lane_mbps = DIV_ROUND_UP(mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); > + > + return 0; > +} > + > +static int > +dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, > + struct dw_mipi_dsi_dphy_timing *timing) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + > + switch (mipi_dsi->mode->hdisplay) { > + case 240: > + case 768: > + case 1920: > + case 2560: > + timing->clk_lp2hs = 23; > + timing->clk_hs2lp = 38; > + timing->data_lp2hs = 15; > + timing->data_hs2lp = 9; > + break; > + > + default: > + timing->clk_lp2hs = 37; > + timing->clk_hs2lp = 135; > + timing->data_lp2hs = 50; > + timing->data_hs2lp = 3; > + } > + > + return 0; > +} > + > +static int > +dw_mipi_dsi_get_esc_clk_rate(void *priv_data, unsigned int *esc_clk_rate) > +{ > + *esc_clk_rate = 4; /* Mhz */ > + > + return 0; > +} > + > +static const struct dw_mipi_dsi_phy_ops meson_dw_mipi_dsi_phy_ops = { > + .init = dw_mipi_dsi_phy_init, > + .power_on = dw_mipi_dsi_phy_power_on, > + .power_off = dw_mipi_dsi_phy_power_off, > + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, > + .get_timing = dw_mipi_dsi_phy_get_timing, > + .get_esc_clk_rate = dw_mipi_dsi_get_esc_clk_rate, > +}; > + > +static int meson_dw_mipi_dsi_host_attach(void *priv_data, > + struct mipi_dsi_device *device) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + int ret; > + > + mipi_dsi->dsi_device = device; > + > + switch (device->format) { > + case MIPI_DSI_FMT_RGB888: > + break; > + case MIPI_DSI_FMT_RGB666: > + break; > + case MIPI_DSI_FMT_RGB666_PACKED: > + case MIPI_DSI_FMT_RGB565: > + dev_err(mipi_dsi->dev, "invalid pixel format %d\n", device->format); > + return -EINVAL; > + }; > + > + ret = phy_init(mipi_dsi->phy); > + if (ret) > + return ret; > + > + meson_dw_mipi_dsi_hw_init(mipi_dsi); > + > + return 0; > +} > + > +static int meson_dw_mipi_dsi_host_detach(void *priv_data, > + struct mipi_dsi_device *device) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; > + > + if (device == mipi_dsi->dsi_device) > + mipi_dsi->dsi_device = NULL; > + else > + return -EINVAL; > + > + return phy_exit(mipi_dsi->phy); > +} > + > +static const struct dw_mipi_dsi_host_ops meson_dw_mipi_dsi_host_ops = { > + .attach = meson_dw_mipi_dsi_host_attach, > + .detach = meson_dw_mipi_dsi_host_detach, > +}; > + > +static int meson_dw_mipi_dsi_probe(struct platform_device *pdev) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi; > + struct device *dev = &pdev->dev; > + > + mipi_dsi = devm_kzalloc(dev, sizeof(*mipi_dsi), GFP_KERNEL); > + if (!mipi_dsi) > + return -ENOMEM; > + > + mipi_dsi->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(mipi_dsi->base)) > + return PTR_ERR(mipi_dsi->base); > + > + mipi_dsi->phy = devm_phy_get(dev, "dphy"); > + if (IS_ERR(mipi_dsi->phy)) > + return dev_err_probe(dev, PTR_ERR(mipi_dsi->phy), > + "failed to get mipi dphy\n"); > + > + mipi_dsi->bit_clk = devm_clk_get_enabled(dev, "bit"); > + if (IS_ERR(mipi_dsi->bit_clk)) { > + int ret = PTR_ERR(mipi_dsi->bit_clk); > + > + /* TOFIX GP0 on some platforms fails to lock in early boot, defer probe */ > + if (ret == -EIO) > + ret = -EPROBE_DEFER; > + > + return dev_err_probe(dev, ret, "Unable to get enabled bit_clk\n"); > + } > + > + mipi_dsi->px_clk = devm_clk_get_enabled(dev, "px"); > + if (IS_ERR(mipi_dsi->px_clk)) > + return dev_err_probe(dev, PTR_ERR(mipi_dsi->px_clk), > + "Unable to get enabled px_clk\n"); > + > + /* > + * We use a TOP reset signal because the APB reset signal > + * is handled by the TOP control registers. > + */ > + mipi_dsi->top_rst = devm_reset_control_get_exclusive(dev, "top"); > + if (IS_ERR(mipi_dsi->top_rst)) > + return dev_err_probe(dev, PTR_ERR(mipi_dsi->top_rst), > + "Unable to get reset control\n"); > + > + reset_control_assert(mipi_dsi->top_rst); > + usleep_range(10, 20); > + reset_control_deassert(mipi_dsi->top_rst); > + > + /* MIPI DSI Controller */ > + > + mipi_dsi->dev = dev; > + mipi_dsi->pdata.base = mipi_dsi->base; > + mipi_dsi->pdata.max_data_lanes = 4; > + mipi_dsi->pdata.phy_ops = &meson_dw_mipi_dsi_phy_ops; > + mipi_dsi->pdata.host_ops = &meson_dw_mipi_dsi_host_ops; > + mipi_dsi->pdata.priv_data = mipi_dsi; > + platform_set_drvdata(pdev, mipi_dsi); > + > + mipi_dsi->dmd = dw_mipi_dsi_probe(pdev, &mipi_dsi->pdata); > + if (IS_ERR(mipi_dsi->dmd)) > + return dev_err_probe(dev, PTR_ERR(mipi_dsi->dmd), > + "Failed to probe dw_mipi_dsi\n"); > + > + return 0; > +} > + > +static int meson_dw_mipi_dsi_remove(struct platform_device *pdev) > +{ > + struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev); > + > + dw_mipi_dsi_remove(mipi_dsi->dmd); > + > + return 0; > +} > + > +static const struct of_device_id meson_dw_mipi_dsi_of_table[] = { > + { .compatible = "amlogic,meson-g12a-dw-mipi-dsi", }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table); > + > +static struct platform_driver meson_dw_mipi_dsi_platform_driver = { > + .probe = meson_dw_mipi_dsi_probe, > + .remove = meson_dw_mipi_dsi_remove, > + .driver = { > + .name = DRIVER_NAME, > + .of_match_table = meson_dw_mipi_dsi_of_table, > + }, > +}; > +module_platform_driver(meson_dw_mipi_dsi_platform_driver); > + > +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); > +MODULE_DESCRIPTION(DRIVER_DESC); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h > new file mode 100644 > index 000000000000..e1bd6b85d6a3 > --- /dev/null > +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h > @@ -0,0 +1,160 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2020 BayLibre, SAS > + * Author: Neil Armstrong <narmstrong@baylibre.com> > + * Copyright (C) 2018 Amlogic, Inc. All rights reserved. > + */ > + > +#ifndef __MESON_DW_MIPI_DSI_H > +#define __MESON_DW_MIPI_DSI_H > + > +/* Top-level registers */ > +/* [31: 4] Reserved. Default 0. > + * [3] RW timing_rst_n: Default 1. > + * 1=Assert SW reset of timing feature. 0=Release reset. > + * [2] RW dpi_rst_n: Default 1. > + * 1=Assert SW reset on mipi_dsi_host_dpi block. 0=Release reset. > + * [1] RW intr_rst_n: Default 1. > + * 1=Assert SW reset on mipi_dsi_host_intr block. 0=Release reset. > + * [0] RW dwc_rst_n: Default 1. > + * 1=Assert SW reset on IP core. 0=Release reset. > + */ > +#define MIPI_DSI_TOP_SW_RESET 0x3c0 > + > +#define MIPI_DSI_TOP_SW_RESET_DWC BIT(0) > +#define MIPI_DSI_TOP_SW_RESET_INTR BIT(1) > +#define MIPI_DSI_TOP_SW_RESET_DPI BIT(2) > +#define MIPI_DSI_TOP_SW_RESET_TIMING BIT(3) > + > +/* [31: 5] Reserved. Default 0. > + * [4] RW manual_edpihalt: Default 0. > + * 1=Manual suspend VencL; 0=do not suspend VencL. > + * [3] RW auto_edpihalt_en: Default 0. > + * 1=Enable IP's edpihalt signal to suspend VencL; > + * 0=IP's edpihalt signal does not affect VencL. > + * [2] RW clock_freerun: Apply to auto-clock gate only. Default 0. > + * 0=Default, use auto-clock gating to save power; > + * 1=use free-run clock, disable auto-clock gating, for debug mode. > + * [1] RW enable_pixclk: A manual clock gate option, due to DWC IP does not > + * have auto-clock gating. 1=Enable pixclk. Default 0. > + * [0] RW enable_sysclk: A manual clock gate option, due to DWC IP does not > + * have auto-clock gating. 1=Enable sysclk. Default 0. > + */ > +#define MIPI_DSI_TOP_CLK_CNTL 0x3c4 > + > +#define MIPI_DSI_TOP_CLK_SYSCLK_EN BIT(0) > +#define MIPI_DSI_TOP_CLK_PIXCLK_EN BIT(1) > + > +/* [31:24] Reserved. Default 0. > + * [23:20] RW dpi_color_mode: Define DPI pixel format. Default 0. > + * 0=16-bit RGB565 config 1; > + * 1=16-bit RGB565 config 2; > + * 2=16-bit RGB565 config 3; > + * 3=18-bit RGB666 config 1; > + * 4=18-bit RGB666 config 2; > + * 5=24-bit RGB888; > + * 6=20-bit YCbCr 4:2:2; > + * 7=24-bit YCbCr 4:2:2; > + * 8=16-bit YCbCr 4:2:2; > + * 9=30-bit RGB; > + * 10=36-bit RGB; > + * 11=12-bit YCbCr 4:2:0. > + * [19] Reserved. Default 0. > + * [18:16] RW in_color_mode: Define VENC data width. Default 0. > + * 0=30-bit pixel; > + * 1=24-bit pixel; > + * 2=18-bit pixel, RGB666; > + * 3=16-bit pixel, RGB565. > + * [15:14] RW chroma_subsample: Define method of chroma subsampling. Default 0. > + * Applicable to YUV422 or YUV420 only. > + * 0=Use even pixel's chroma; > + * 1=Use odd pixel's chroma; > + * 2=Use averaged value between even and odd pair. > + * [13:12] RW comp2_sel: Select which component to be Cr or B: Default 2. > + * 0=comp0; 1=comp1; 2=comp2. > + * [11:10] RW comp1_sel: Select which component to be Cb or G: Default 1. > + * 0=comp0; 1=comp1; 2=comp2. > + * [9: 8] RW comp0_sel: Select which component to be Y or R: Default 0. > + * 0=comp0; 1=comp1; 2=comp2. > + * [7] Reserved. Default 0. > + * [6] RW de_pol: Default 0. > + * If DE input is active low, set to 1 to invert to active high. > + * [5] RW hsync_pol: Default 0. > + * If HS input is active low, set to 1 to invert to active high. > + * [4] RW vsync_pol: Default 0. > + * If VS input is active low, set to 1 to invert to active high. > + * [3] RW dpicolorm: Signal to IP. Default 0. > + * [2] RW dpishutdn: Signal to IP. Default 0. > + * [1] Reserved. Default 0. > + * [0] Reserved. Default 0. > + */ > +#define MIPI_DSI_TOP_CNTL 0x3c8 > + > +/* VENC data width */ > +#define VENC_IN_COLOR_30B 0x0 > +#define VENC_IN_COLOR_24B 0x1 > +#define VENC_IN_COLOR_18B 0x2 > +#define VENC_IN_COLOR_16B 0x3 > + > +/* DPI pixel format */ > +#define DPI_COLOR_16BIT_CFG_1 0 > +#define DPI_COLOR_16BIT_CFG_2 1 > +#define DPI_COLOR_16BIT_CFG_3 2 > +#define DPI_COLOR_18BIT_CFG_1 3 > +#define DPI_COLOR_18BIT_CFG_2 4 > +#define DPI_COLOR_24BIT 5 > +#define DPI_COLOR_20BIT_YCBCR_422 6 > +#define DPI_COLOR_24BIT_YCBCR_422 7 > +#define DPI_COLOR_16BIT_YCBCR_422 8 > +#define DPI_COLOR_30BIT 9 > +#define DPI_COLOR_36BIT 10 > +#define DPI_COLOR_12BIT_YCBCR_420 11 > + > +#define MIPI_DSI_TOP_DPI_COLOR_MODE GENMASK(23, 20) > +#define MIPI_DSI_TOP_IN_COLOR_MODE GENMASK(18, 16) > +#define MIPI_DSI_TOP_CHROMA_SUBSAMPLE GENMASK(15, 14) > +#define MIPI_DSI_TOP_COMP2_SEL GENMASK(13, 12) > +#define MIPI_DSI_TOP_COMP1_SEL GENMASK(11, 10) > +#define MIPI_DSI_TOP_COMP0_SEL GENMASK(9, 8) > +#define MIPI_DSI_TOP_DE_INVERT BIT(6) > +#define MIPI_DSI_TOP_HSYNC_INVERT BIT(5) > +#define MIPI_DSI_TOP_VSYNC_INVERT BIT(4) > +#define MIPI_DSI_TOP_DPICOLORM BIT(3) > +#define MIPI_DSI_TOP_DPISHUTDN BIT(2) > + > +#define MIPI_DSI_TOP_SUSPEND_CNTL 0x3cc > +#define MIPI_DSI_TOP_SUSPEND_LINE 0x3d0 > +#define MIPI_DSI_TOP_SUSPEND_PIX 0x3d4 > +#define MIPI_DSI_TOP_MEAS_CNTL 0x3d8 > +/* [0] R stat_edpihalt: edpihalt signal from IP. Default 0. */ > +#define MIPI_DSI_TOP_STAT 0x3dc > +#define MIPI_DSI_TOP_MEAS_STAT_TE0 0x3e0 > +#define MIPI_DSI_TOP_MEAS_STAT_TE1 0x3e4 > +#define MIPI_DSI_TOP_MEAS_STAT_VS0 0x3e8 > +#define MIPI_DSI_TOP_MEAS_STAT_VS1 0x3ec > +/* [31:16] RW intr_stat/clr. Default 0. > + * For each bit, read as this interrupt level status, > + * write 1 to clear. > + * [31:22] Reserved > + * [ 21] stat/clr of eof interrupt > + * [ 21] vde_fall interrupt > + * [ 19] stat/clr of de_rise interrupt > + * [ 18] stat/clr of vs_fall interrupt > + * [ 17] stat/clr of vs_rise interrupt > + * [ 16] stat/clr of dwc_edpite interrupt > + * [15: 0] RW intr_enable. Default 0. > + * For each bit, 1=enable this interrupt, 0=disable. > + * [15: 6] Reserved > + * [ 5] eof interrupt > + * [ 4] de_fall interrupt > + * [ 3] de_rise interrupt > + * [ 2] vs_fall interrupt > + * [ 1] vs_rise interrupt > + * [ 0] dwc_edpite interrupt > + */ > +#define MIPI_DSI_TOP_INTR_CNTL_STAT 0x3f0 > +// 31: 2 Reserved. Default 0. > +// 1: 0 RW mem_pd. Default 3. > +#define MIPI_DSI_TOP_MEM_PD 0x3f4 > + > +#endif /* __MESON_DW_MIPI_DSI_H */ > > -- > 2.34.1 > Reviewed-by: Nicolas Belin <nbelin@baylibre.com> Tested-by: Nicolas Belin <nbelin@baylibre.com> # on Khadas VIM3 + TS050 Panel Thanks, Nicolas
diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig index 823909da87db..615fdd0ce41b 100644 --- a/drivers/gpu/drm/meson/Kconfig +++ b/drivers/gpu/drm/meson/Kconfig @@ -17,3 +17,10 @@ config DRM_MESON_DW_HDMI default y if DRM_MESON select DRM_DW_HDMI imply DRM_DW_HDMI_I2S_AUDIO + +config DRM_MESON_DW_MIPI_DSI + tristate "MIPI DSI Synopsys Controller support for Amlogic Meson Display" + depends on DRM_MESON + default y if DRM_MESON + select DRM_DW_MIPI_DSI + select GENERIC_PHY_MIPI_DPHY diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile index 833e18c20603..43071bdbd4b9 100644 --- a/drivers/gpu/drm/meson/Makefile +++ b/drivers/gpu/drm/meson/Makefile @@ -6,3 +6,4 @@ meson-drm-y += meson_encoder_hdmi.o meson_encoder_dsi.o obj-$(CONFIG_DRM_MESON) += meson-drm.o obj-$(CONFIG_DRM_MESON_DW_HDMI) += meson_dw_hdmi.o +obj-$(CONFIG_DRM_MESON_DW_MIPI_DSI) += meson_dw_mipi_dsi.o diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c new file mode 100644 index 000000000000..dd505ac37976 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.c @@ -0,0 +1,352 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + */ + +#include <linux/clk.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/reset.h> +#include <linux/phy/phy.h> +#include <linux/bitfield.h> + +#include <video/mipi_display.h> + +#include <drm/bridge/dw_mipi_dsi.h> +#include <drm/drm_mipi_dsi.h> + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_device.h> +#include <drm/drm_probe_helper.h> +#include <drm/drm_print.h> + +#include "meson_drv.h" +#include "meson_dw_mipi_dsi.h" +#include "meson_registers.h" +#include "meson_venc.h" + +#define DRIVER_NAME "meson-dw-mipi-dsi" +#define DRIVER_DESC "Amlogic Meson MIPI-DSI DRM driver" + +struct meson_dw_mipi_dsi { + struct meson_drm *priv; + struct device *dev; + void __iomem *base; + struct phy *phy; + union phy_configure_opts phy_opts; + struct dw_mipi_dsi *dmd; + struct dw_mipi_dsi_plat_data pdata; + struct mipi_dsi_device *dsi_device; + const struct drm_display_mode *mode; + struct clk *bit_clk; + struct clk *px_clk; + struct reset_control *top_rst; +}; + +#define encoder_to_meson_dw_mipi_dsi(x) \ + container_of(x, struct meson_dw_mipi_dsi, encoder) + +static void meson_dw_mipi_dsi_hw_init(struct meson_dw_mipi_dsi *mipi_dsi) +{ + /* Software reset */ + writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, + MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, + mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); + writel_bits_relaxed(MIPI_DSI_TOP_SW_RESET_DWC | MIPI_DSI_TOP_SW_RESET_INTR | + MIPI_DSI_TOP_SW_RESET_DPI | MIPI_DSI_TOP_SW_RESET_TIMING, + 0, mipi_dsi->base + MIPI_DSI_TOP_SW_RESET); + + /* Enable clocks */ + writel_bits_relaxed(MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, + MIPI_DSI_TOP_CLK_SYSCLK_EN | MIPI_DSI_TOP_CLK_PIXCLK_EN, + mipi_dsi->base + MIPI_DSI_TOP_CLK_CNTL); + + /* Take memory out of power down */ + writel_relaxed(0, mipi_dsi->base + MIPI_DSI_TOP_MEM_PD); +} + +static int dw_mipi_dsi_phy_init(void *priv_data) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + unsigned int dpi_data_format, venc_data_width; + int ret; + + /* Set the bit clock rate to hs_clk_rate */ + ret = clk_set_rate(mipi_dsi->bit_clk, + mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate); + if (ret) { + dev_err(mipi_dsi->dev, "Failed to set DSI Bit clock rate %lu (ret %d)\n", + mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, ret); + return ret; + } + + /* Make sure the rate of the bit clock is not modified by someone else */ + ret = clk_rate_exclusive_get(mipi_dsi->bit_clk); + if (ret) { + dev_err(mipi_dsi->dev, + "Failed to set the exclusivity on the bit clock rate (ret %d)\n", ret); + return ret; + } + + ret = clk_set_rate(mipi_dsi->px_clk, mipi_dsi->mode->clock * 1000); + + if (ret) { + dev_err(mipi_dsi->dev, "Failed to set DSI Pixel clock rate %u (%d)\n", + mipi_dsi->mode->clock * 1000, ret); + return ret; + } + + switch (mipi_dsi->dsi_device->format) { + case MIPI_DSI_FMT_RGB888: + dpi_data_format = DPI_COLOR_24BIT; + venc_data_width = VENC_IN_COLOR_24B; + break; + case MIPI_DSI_FMT_RGB666: + dpi_data_format = DPI_COLOR_18BIT_CFG_2; + venc_data_width = VENC_IN_COLOR_18B; + break; + case MIPI_DSI_FMT_RGB666_PACKED: + case MIPI_DSI_FMT_RGB565: + return -EINVAL; + }; + + /* Configure color format for DPI register */ + writel_relaxed(FIELD_PREP(MIPI_DSI_TOP_DPI_COLOR_MODE, dpi_data_format) | + FIELD_PREP(MIPI_DSI_TOP_IN_COLOR_MODE, venc_data_width) | + FIELD_PREP(MIPI_DSI_TOP_COMP2_SEL, 2) | + FIELD_PREP(MIPI_DSI_TOP_COMP1_SEL, 1) | + FIELD_PREP(MIPI_DSI_TOP_COMP0_SEL, 0), + mipi_dsi->base + MIPI_DSI_TOP_CNTL); + + return phy_configure(mipi_dsi->phy, &mipi_dsi->phy_opts); +} + +static void dw_mipi_dsi_phy_power_on(void *priv_data) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + if (phy_power_on(mipi_dsi->phy)) + dev_warn(mipi_dsi->dev, "Failed to power on PHY\n"); +} + +static void dw_mipi_dsi_phy_power_off(void *priv_data) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + if (phy_power_off(mipi_dsi->phy)) + dev_warn(mipi_dsi->dev, "Failed to power off PHY\n"); + + /* Remove the exclusivity on the bit clock rate */ + clk_rate_exclusive_put(mipi_dsi->bit_clk); +} + +static int +dw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode, + unsigned long mode_flags, u32 lanes, u32 format, + unsigned int *lane_mbps) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + int bpp; + + mipi_dsi->mode = mode; + + bpp = mipi_dsi_pixel_format_to_bpp(mipi_dsi->dsi_device->format); + + phy_mipi_dphy_get_default_config(mode->clock * 1000, + bpp, mipi_dsi->dsi_device->lanes, + &mipi_dsi->phy_opts.mipi_dphy); + + *lane_mbps = DIV_ROUND_UP(mipi_dsi->phy_opts.mipi_dphy.hs_clk_rate, USEC_PER_SEC); + + return 0; +} + +static int +dw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps, + struct dw_mipi_dsi_dphy_timing *timing) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + switch (mipi_dsi->mode->hdisplay) { + case 240: + case 768: + case 1920: + case 2560: + timing->clk_lp2hs = 23; + timing->clk_hs2lp = 38; + timing->data_lp2hs = 15; + timing->data_hs2lp = 9; + break; + + default: + timing->clk_lp2hs = 37; + timing->clk_hs2lp = 135; + timing->data_lp2hs = 50; + timing->data_hs2lp = 3; + } + + return 0; +} + +static int +dw_mipi_dsi_get_esc_clk_rate(void *priv_data, unsigned int *esc_clk_rate) +{ + *esc_clk_rate = 4; /* Mhz */ + + return 0; +} + +static const struct dw_mipi_dsi_phy_ops meson_dw_mipi_dsi_phy_ops = { + .init = dw_mipi_dsi_phy_init, + .power_on = dw_mipi_dsi_phy_power_on, + .power_off = dw_mipi_dsi_phy_power_off, + .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, + .get_timing = dw_mipi_dsi_phy_get_timing, + .get_esc_clk_rate = dw_mipi_dsi_get_esc_clk_rate, +}; + +static int meson_dw_mipi_dsi_host_attach(void *priv_data, + struct mipi_dsi_device *device) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + int ret; + + mipi_dsi->dsi_device = device; + + switch (device->format) { + case MIPI_DSI_FMT_RGB888: + break; + case MIPI_DSI_FMT_RGB666: + break; + case MIPI_DSI_FMT_RGB666_PACKED: + case MIPI_DSI_FMT_RGB565: + dev_err(mipi_dsi->dev, "invalid pixel format %d\n", device->format); + return -EINVAL; + }; + + ret = phy_init(mipi_dsi->phy); + if (ret) + return ret; + + meson_dw_mipi_dsi_hw_init(mipi_dsi); + + return 0; +} + +static int meson_dw_mipi_dsi_host_detach(void *priv_data, + struct mipi_dsi_device *device) +{ + struct meson_dw_mipi_dsi *mipi_dsi = priv_data; + + if (device == mipi_dsi->dsi_device) + mipi_dsi->dsi_device = NULL; + else + return -EINVAL; + + return phy_exit(mipi_dsi->phy); +} + +static const struct dw_mipi_dsi_host_ops meson_dw_mipi_dsi_host_ops = { + .attach = meson_dw_mipi_dsi_host_attach, + .detach = meson_dw_mipi_dsi_host_detach, +}; + +static int meson_dw_mipi_dsi_probe(struct platform_device *pdev) +{ + struct meson_dw_mipi_dsi *mipi_dsi; + struct device *dev = &pdev->dev; + + mipi_dsi = devm_kzalloc(dev, sizeof(*mipi_dsi), GFP_KERNEL); + if (!mipi_dsi) + return -ENOMEM; + + mipi_dsi->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mipi_dsi->base)) + return PTR_ERR(mipi_dsi->base); + + mipi_dsi->phy = devm_phy_get(dev, "dphy"); + if (IS_ERR(mipi_dsi->phy)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->phy), + "failed to get mipi dphy\n"); + + mipi_dsi->bit_clk = devm_clk_get_enabled(dev, "bit"); + if (IS_ERR(mipi_dsi->bit_clk)) { + int ret = PTR_ERR(mipi_dsi->bit_clk); + + /* TOFIX GP0 on some platforms fails to lock in early boot, defer probe */ + if (ret == -EIO) + ret = -EPROBE_DEFER; + + return dev_err_probe(dev, ret, "Unable to get enabled bit_clk\n"); + } + + mipi_dsi->px_clk = devm_clk_get_enabled(dev, "px"); + if (IS_ERR(mipi_dsi->px_clk)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->px_clk), + "Unable to get enabled px_clk\n"); + + /* + * We use a TOP reset signal because the APB reset signal + * is handled by the TOP control registers. + */ + mipi_dsi->top_rst = devm_reset_control_get_exclusive(dev, "top"); + if (IS_ERR(mipi_dsi->top_rst)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->top_rst), + "Unable to get reset control\n"); + + reset_control_assert(mipi_dsi->top_rst); + usleep_range(10, 20); + reset_control_deassert(mipi_dsi->top_rst); + + /* MIPI DSI Controller */ + + mipi_dsi->dev = dev; + mipi_dsi->pdata.base = mipi_dsi->base; + mipi_dsi->pdata.max_data_lanes = 4; + mipi_dsi->pdata.phy_ops = &meson_dw_mipi_dsi_phy_ops; + mipi_dsi->pdata.host_ops = &meson_dw_mipi_dsi_host_ops; + mipi_dsi->pdata.priv_data = mipi_dsi; + platform_set_drvdata(pdev, mipi_dsi); + + mipi_dsi->dmd = dw_mipi_dsi_probe(pdev, &mipi_dsi->pdata); + if (IS_ERR(mipi_dsi->dmd)) + return dev_err_probe(dev, PTR_ERR(mipi_dsi->dmd), + "Failed to probe dw_mipi_dsi\n"); + + return 0; +} + +static int meson_dw_mipi_dsi_remove(struct platform_device *pdev) +{ + struct meson_dw_mipi_dsi *mipi_dsi = platform_get_drvdata(pdev); + + dw_mipi_dsi_remove(mipi_dsi->dmd); + + return 0; +} + +static const struct of_device_id meson_dw_mipi_dsi_of_table[] = { + { .compatible = "amlogic,meson-g12a-dw-mipi-dsi", }, + { } +}; +MODULE_DEVICE_TABLE(of, meson_dw_mipi_dsi_of_table); + +static struct platform_driver meson_dw_mipi_dsi_platform_driver = { + .probe = meson_dw_mipi_dsi_probe, + .remove = meson_dw_mipi_dsi_remove, + .driver = { + .name = DRIVER_NAME, + .of_match_table = meson_dw_mipi_dsi_of_table, + }, +}; +module_platform_driver(meson_dw_mipi_dsi_platform_driver); + +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h new file mode 100644 index 000000000000..e1bd6b85d6a3 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_dw_mipi_dsi.h @@ -0,0 +1,160 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2020 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2018 Amlogic, Inc. All rights reserved. + */ + +#ifndef __MESON_DW_MIPI_DSI_H +#define __MESON_DW_MIPI_DSI_H + +/* Top-level registers */ +/* [31: 4] Reserved. Default 0. + * [3] RW timing_rst_n: Default 1. + * 1=Assert SW reset of timing feature. 0=Release reset. + * [2] RW dpi_rst_n: Default 1. + * 1=Assert SW reset on mipi_dsi_host_dpi block. 0=Release reset. + * [1] RW intr_rst_n: Default 1. + * 1=Assert SW reset on mipi_dsi_host_intr block. 0=Release reset. + * [0] RW dwc_rst_n: Default 1. + * 1=Assert SW reset on IP core. 0=Release reset. + */ +#define MIPI_DSI_TOP_SW_RESET 0x3c0 + +#define MIPI_DSI_TOP_SW_RESET_DWC BIT(0) +#define MIPI_DSI_TOP_SW_RESET_INTR BIT(1) +#define MIPI_DSI_TOP_SW_RESET_DPI BIT(2) +#define MIPI_DSI_TOP_SW_RESET_TIMING BIT(3) + +/* [31: 5] Reserved. Default 0. + * [4] RW manual_edpihalt: Default 0. + * 1=Manual suspend VencL; 0=do not suspend VencL. + * [3] RW auto_edpihalt_en: Default 0. + * 1=Enable IP's edpihalt signal to suspend VencL; + * 0=IP's edpihalt signal does not affect VencL. + * [2] RW clock_freerun: Apply to auto-clock gate only. Default 0. + * 0=Default, use auto-clock gating to save power; + * 1=use free-run clock, disable auto-clock gating, for debug mode. + * [1] RW enable_pixclk: A manual clock gate option, due to DWC IP does not + * have auto-clock gating. 1=Enable pixclk. Default 0. + * [0] RW enable_sysclk: A manual clock gate option, due to DWC IP does not + * have auto-clock gating. 1=Enable sysclk. Default 0. + */ +#define MIPI_DSI_TOP_CLK_CNTL 0x3c4 + +#define MIPI_DSI_TOP_CLK_SYSCLK_EN BIT(0) +#define MIPI_DSI_TOP_CLK_PIXCLK_EN BIT(1) + +/* [31:24] Reserved. Default 0. + * [23:20] RW dpi_color_mode: Define DPI pixel format. Default 0. + * 0=16-bit RGB565 config 1; + * 1=16-bit RGB565 config 2; + * 2=16-bit RGB565 config 3; + * 3=18-bit RGB666 config 1; + * 4=18-bit RGB666 config 2; + * 5=24-bit RGB888; + * 6=20-bit YCbCr 4:2:2; + * 7=24-bit YCbCr 4:2:2; + * 8=16-bit YCbCr 4:2:2; + * 9=30-bit RGB; + * 10=36-bit RGB; + * 11=12-bit YCbCr 4:2:0. + * [19] Reserved. Default 0. + * [18:16] RW in_color_mode: Define VENC data width. Default 0. + * 0=30-bit pixel; + * 1=24-bit pixel; + * 2=18-bit pixel, RGB666; + * 3=16-bit pixel, RGB565. + * [15:14] RW chroma_subsample: Define method of chroma subsampling. Default 0. + * Applicable to YUV422 or YUV420 only. + * 0=Use even pixel's chroma; + * 1=Use odd pixel's chroma; + * 2=Use averaged value between even and odd pair. + * [13:12] RW comp2_sel: Select which component to be Cr or B: Default 2. + * 0=comp0; 1=comp1; 2=comp2. + * [11:10] RW comp1_sel: Select which component to be Cb or G: Default 1. + * 0=comp0; 1=comp1; 2=comp2. + * [9: 8] RW comp0_sel: Select which component to be Y or R: Default 0. + * 0=comp0; 1=comp1; 2=comp2. + * [7] Reserved. Default 0. + * [6] RW de_pol: Default 0. + * If DE input is active low, set to 1 to invert to active high. + * [5] RW hsync_pol: Default 0. + * If HS input is active low, set to 1 to invert to active high. + * [4] RW vsync_pol: Default 0. + * If VS input is active low, set to 1 to invert to active high. + * [3] RW dpicolorm: Signal to IP. Default 0. + * [2] RW dpishutdn: Signal to IP. Default 0. + * [1] Reserved. Default 0. + * [0] Reserved. Default 0. + */ +#define MIPI_DSI_TOP_CNTL 0x3c8 + +/* VENC data width */ +#define VENC_IN_COLOR_30B 0x0 +#define VENC_IN_COLOR_24B 0x1 +#define VENC_IN_COLOR_18B 0x2 +#define VENC_IN_COLOR_16B 0x3 + +/* DPI pixel format */ +#define DPI_COLOR_16BIT_CFG_1 0 +#define DPI_COLOR_16BIT_CFG_2 1 +#define DPI_COLOR_16BIT_CFG_3 2 +#define DPI_COLOR_18BIT_CFG_1 3 +#define DPI_COLOR_18BIT_CFG_2 4 +#define DPI_COLOR_24BIT 5 +#define DPI_COLOR_20BIT_YCBCR_422 6 +#define DPI_COLOR_24BIT_YCBCR_422 7 +#define DPI_COLOR_16BIT_YCBCR_422 8 +#define DPI_COLOR_30BIT 9 +#define DPI_COLOR_36BIT 10 +#define DPI_COLOR_12BIT_YCBCR_420 11 + +#define MIPI_DSI_TOP_DPI_COLOR_MODE GENMASK(23, 20) +#define MIPI_DSI_TOP_IN_COLOR_MODE GENMASK(18, 16) +#define MIPI_DSI_TOP_CHROMA_SUBSAMPLE GENMASK(15, 14) +#define MIPI_DSI_TOP_COMP2_SEL GENMASK(13, 12) +#define MIPI_DSI_TOP_COMP1_SEL GENMASK(11, 10) +#define MIPI_DSI_TOP_COMP0_SEL GENMASK(9, 8) +#define MIPI_DSI_TOP_DE_INVERT BIT(6) +#define MIPI_DSI_TOP_HSYNC_INVERT BIT(5) +#define MIPI_DSI_TOP_VSYNC_INVERT BIT(4) +#define MIPI_DSI_TOP_DPICOLORM BIT(3) +#define MIPI_DSI_TOP_DPISHUTDN BIT(2) + +#define MIPI_DSI_TOP_SUSPEND_CNTL 0x3cc +#define MIPI_DSI_TOP_SUSPEND_LINE 0x3d0 +#define MIPI_DSI_TOP_SUSPEND_PIX 0x3d4 +#define MIPI_DSI_TOP_MEAS_CNTL 0x3d8 +/* [0] R stat_edpihalt: edpihalt signal from IP. Default 0. */ +#define MIPI_DSI_TOP_STAT 0x3dc +#define MIPI_DSI_TOP_MEAS_STAT_TE0 0x3e0 +#define MIPI_DSI_TOP_MEAS_STAT_TE1 0x3e4 +#define MIPI_DSI_TOP_MEAS_STAT_VS0 0x3e8 +#define MIPI_DSI_TOP_MEAS_STAT_VS1 0x3ec +/* [31:16] RW intr_stat/clr. Default 0. + * For each bit, read as this interrupt level status, + * write 1 to clear. + * [31:22] Reserved + * [ 21] stat/clr of eof interrupt + * [ 21] vde_fall interrupt + * [ 19] stat/clr of de_rise interrupt + * [ 18] stat/clr of vs_fall interrupt + * [ 17] stat/clr of vs_rise interrupt + * [ 16] stat/clr of dwc_edpite interrupt + * [15: 0] RW intr_enable. Default 0. + * For each bit, 1=enable this interrupt, 0=disable. + * [15: 6] Reserved + * [ 5] eof interrupt + * [ 4] de_fall interrupt + * [ 3] de_rise interrupt + * [ 2] vs_fall interrupt + * [ 1] vs_rise interrupt + * [ 0] dwc_edpite interrupt + */ +#define MIPI_DSI_TOP_INTR_CNTL_STAT 0x3f0 +// 31: 2 Reserved. Default 0. +// 1: 0 RW mem_pd. Default 3. +#define MIPI_DSI_TOP_MEM_PD 0x3f4 + +#endif /* __MESON_DW_MIPI_DSI_H */