Hi Jagan,

Thank you for the patch.

On Mon, Jun 21, 2021 at 12:54:17PM +0530, Jagan Teki wrote:
> Samsung SEC MIPI DSIM Bridge controller is MIPI DSI bridge
> available in NXP's i.MX8M Mini and Nano Processors.
> 
> Add bridge driver for it.
> 
> Cc: Andrzej Hajda <a.ha...@samsung.com>
> Cc: Neil Armstrong <narmstr...@baylibre.com>
> Cc: Robert Foss <robert.f...@linaro.org>
> Cc: Laurent Pinchart <laurent.pinch...@ideasonboard.com>
> Signed-off-by: Jagan Teki <ja...@amarulasolutions.com>
> ---
>  drivers/gpu/drm/bridge/Kconfig    |   15 +
>  drivers/gpu/drm/bridge/Makefile   |    1 +
>  drivers/gpu/drm/bridge/sec-dsim.c | 1535 +++++++++++++++++++++++++++++
>  3 files changed, 1551 insertions(+)
>  create mode 100644 drivers/gpu/drm/bridge/sec-dsim.c
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 19109c0b5481..a183eb165a35 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -189,6 +189,21 @@ config DRM_PARADE_PS8640
>         The PS8640 is a high-performance and low-power
>         MIPI DSI to eDP converter
>  
> +config DRM_SEC_MIPI_DSIM
> +     tristate "Samsung SEC MIPI DSIM Bridge controller"
> +     depends on DRM
> +     depends on COMMON_CLK
> +     depends on OF && HAS_IOMEM
> +     select DRM_KMS_HELPER
> +     select DRM_MIPI_DSI
> +     select DRM_PANEL_BRIDGE
> +     select GENERIC_PHY_MIPI_DPHY
> +     select MFD_SYSCON
> +     select REGMAP_MMIO
> +     help
> +       This enables the Samsung SEC MIPI DSIM Bridge controller as
> +       for example found on NXP's i.MX8M Mini and Nano Processors.
> +
>  config DRM_SIL_SII8620
>       tristate "Silicon Image SII8620 HDMI/MHL bridge"
>       depends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile
> index 88e4edf81087..ff802a4ffe65 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -12,6 +12,7 @@ obj-$(CONFIG_DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW) += 
> megachips-stdpxxxx-ge-b850v
>  obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o
>  obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o
>  obj-$(CONFIG_DRM_PARADE_PS8640) += parade-ps8640.o
> +obj-$(CONFIG_DRM_SEC_MIPI_DSIM) += sec-dsim.o
>  obj-$(CONFIG_DRM_SIL_SII8620) += sil-sii8620.o
>  obj-$(CONFIG_DRM_SII902X) += sii902x.o
>  obj-$(CONFIG_DRM_SII9234) += sii9234.o
> diff --git a/drivers/gpu/drm/bridge/sec-dsim.c 
> b/drivers/gpu/drm/bridge/sec-dsim.c
> new file mode 100644
> index 000000000000..5b6645bb94e7
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/sec-dsim.c
> @@ -0,0 +1,1535 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Samsung SEC MIPI DSIM Bridge
> + *
> + * Copyright (C) 2018 NXP
> + * Copyright (c) 2014 Samsung Electronics Co., Ltd
> + * Copyright (C) 2021 Amarula Solutions(India)
> + *
> + * Based on the drivers/gpu/drm/exynos/exynos_drm_dsi.c
> + *
> + * Authors:
> + * Tomasz Figa <t.f...@samsung.com>
> + * Andrzej Hajda <a.ha...@samsung.com>
> + * Fancy Fang <chen.f...@nxp.com>
> + * Jagan Teki <ja...@amarulasolutions.com>
> + */
> +
> +#include <asm/unaligned.h>
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/completion.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/of_graph.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_print.h>
> +
> +#include <video/mipi_display.h>
> +
> +#define DRIVER_NAME                  "sec-dsim"
> +
> +/* dsim registers */
> +#define DSIM_VERSION                 0x00
> +#define DSIM_STATUS                  0x04
> +#define DSIM_RGB_STATUS                      0x08
> +#define DSIM_SWRST                   0x0c
> +#define DSIM_CLKCTRL                 0x10
> +#define DSIM_TIMEOUT                 0x14
> +#define DSIM_CONFIG                  0x18
> +#define DSIM_ESCMODE                 0x1c
> +#define DSIM_MDRESOL                 0x20
> +#define DSIM_MVPORCH                 0x24
> +#define DSIM_MHPORCH                 0x28
> +#define DSIM_MSYNC                   0x2c
> +#define DSIM_SDRESOL                 0x30
> +#define DSIM_INTSRC                  0x34
> +#define DSIM_INTMSK                  0x38
> +#define DSIM_PKTHDR                  0x3c
> +#define DSIM_PAYLOAD                 0x40
> +#define DSIM_RXFIFO                  0x44
> +#define DSIM_FIFOTHLD                        0x48
> +#define DSIM_FIFOCTRL                        0x4c
> +#define DSIM_MEMACCHR                        0x50
> +#define DSIM_MULTI_PKT                       0x78
> +#define DSIM_PLLCTRL_1G                      0x90
> +#define DSIM_PLLCTRL                 0x94
> +#define DSIM_PLLCTRL1                        0x98
> +#define DSIM_PLLCTRL2                        0x9c
> +#define DSIM_PLLTMR                  0xa0

Looking at the register set, it seems to match the Exynos 5433,
supported by drivers/gpu/drm/exynos/exynos_drm_dsi.c. Can we leverage
that driver instead of adding a new one for the same IP core ?

> +
> +/* register bit fields */
> +#define STATUS_PLLSTABLE             BIT(31)
> +#define STATUS_SWRSTRLS                      BIT(20)
> +#define STATUS_TXREADYHSCLK          BIT(10)
> +#define STATUS_ULPSCLK                       BIT(9)
> +#define STATUS_STOPSTATECLK          BIT(8)
> +
> +#define CLKCTRL_TXREQUESTHSCLK               BIT(31)
> +#define CLKCTRL_ESCCLKEN             BIT(28)
> +#define CLKCTRL_PLLBYPASS            BIT(27)
> +#define CLKCTRL_BYTECLKSRC_MASK              GENMASK(26, 25)
> +#define CLKCTRL_BYTECLKSRC(x)                
> FIELD_PREP(CLKCTRL_BYTECLKSRC_MASK, (x))
> +#define CLKCTRL_BYTECLKEN            BIT(24)
> +#define CLKCTRL_LANEESCDATAEN_MASK   GENMASK(23, 20)
> +#define CLKCTRL_LANEESCDATAEN(x)     FIELD_PREP(CLKCTRL_LANEESCDATAEN_MASK, 
> (x))
> +#define CLKCTRL_LANEESCCLKEN         BIT(19)
> +#define CLKCTRL_ESCPRESCALER_MASK    GENMASK(15, 0)
> +#define CLKCTRL_ESCPRESCALER(x)              
> FIELD_PREP(CLKCTRL_ESCPRESCALER_MASK, (x))
> +
> +#define TIMEOUT_BTAOUT_MASK          GENMASK(23, 16)
> +#define TIMEOUT_BTAOUT(x)            FIELD_PREP(TIMEOUT_BTAOUT_MASK, (x))
> +#define TIMEOUT_LPDRTOUT_MASK                GENMASK(15, 0)
> +#define TIMEOUT_LPDRTOUT(x)          FIELD_PREP(TIMEOUT_LPDRTOUT_MASK, (x))
> +
> +#define CONFIG_NON_CONTINUOUS_CLOCK_LANE     BIT(31)
> +#define CONFIG_CLKLANE_STOP_START    BIT(30)
> +#define CONFIG_MFLUSH_VS             BIT(29)
> +#define CONFIG_EOT_R03                       BIT(28)
> +#define CONFIG_SYNCINFORM            BIT(27)
> +#define CONFIG_BURSTMODE             BIT(26)
> +#define CONFIG_VIDEOMODE             BIT(25)
> +#define CONFIG_AUTOMODE                      BIT(24)
> +#define CONFIG_HSEDISABLEMODE                BIT(23)
> +#define CONFIG_HFPDISABLEMODE                BIT(22)
> +#define CONFIG_HBPDISABLEMODE                BIT(21)
> +#define CONFIG_HSADISABLEMODE                BIT(20)
> +
> +#define CONFIG_MAINPIXFORMAT_MASK    GENMASK(14, 12)
> +#define CONFIG_MAINPIXFORMAT(x)              
> FIELD_PREP(CONFIG_MAINPIXFORMAT_MASK, (x))
> +#define CONFIG_NUMOFDATLANE_MASK     GENMASK(6, 5)
> +#define CONFIG_NUMOFDATLANE(x)               
> FIELD_PREP(CONFIG_NUMOFDATLANE_MASK, (x))
> +#define CONFIG_LANEEN_MASK           GENMASK(4, 0)
> +#define CONFIG_LANEEN(x)             FIELD_PREP(GENMASK(4, 1), (x))
> +#define CONFIG_CLKLANEEN             BIT(0)
> +
> +#define ESCMODE_STOPSTATE_CN_MASK    GENMASK(31, 21)
> +#define ESCMODE_STOPSTATE_CN(x)              
> FIELD_PREP(ESCMODE_STOPSTATE_CN_MASK, (x))
> +#define ESCMODE_CMDLPDT                      BIT(7)
> +
> +#define MDRESOL_MAINSTANDBY          BIT(31)
> +#define MVPORCH_MAINVRESOL_MASK              GENMASK(27, 16)
> +#define MVPORCH_MAINVRESOL(x)                
> FIELD_PREP(MVPORCH_MAINVRESOL_MASK, (x))
> +#define MVPORCH_MAINHRESOL_MASK              GENMASK(11, 0)
> +#define MVPORCH_MAINHRESOL(x)                
> FIELD_PREP(MVPORCH_MAINHRESOL_MASK, (x))
> +#define MVPORCH_CMDALLOW_MASK                GENMASK(31, 28)
> +#define MVPORCH_CMDALLOW(x)          FIELD_PREP(MVPORCH_CMDALLOW_MASK, (x))
> +#define MVPORCH_STABLEVFP_MASK               GENMASK(26, 16)
> +#define MVPORCH_STABLEVFP(x)         FIELD_PREP(MVPORCH_STABLEVFP_MASK, (x))
> +#define MVPORCH_MAINVBP_MASK         GENMASK(10, 0)
> +#define MVPORCH_MAINVBP(x)           FIELD_PREP(MVPORCH_MAINVBP_MASK, (x))
> +#define MVPORCH_MAINHFP_MASK         GENMASK(31, 16)
> +#define MVPORCH_MAINHFP(x)           FIELD_PREP(MVPORCH_MAINHFP_MASK, (x))
> +#define MVPORCH_MAINHBP_MASK         GENMASK(15, 0)
> +#define MVPORCH_MAINHBP(x)           FIELD_PREP(MVPORCH_MAINHBP_MASK, (x))
> +#define MVPORCH_MAINVSA_MASK         GENMASK(31, 22)
> +#define MVPORCH_MAINVSA(x)           FIELD_PREP(MVPORCH_MAINVSA_MASK, (x))
> +#define MVPORCH_MAINHSA_MASK         GENMASK(15, 0)
> +#define MVPORCH_MAINHSA(x)           FIELD_PREP(MVPORCH_MAINHSA_MASK, (x))
> +
> +#define INTSRC_PLLSTABLE             BIT(31)
> +#define INTSRC_SWRSTRELEASE          BIT(30)
> +#define INTSRC_SFRPLFIFOEMPTY                BIT(29)
> +#define INTSRC_SFRPHFIFOEMPTY                BIT(28)
> +#define INTSRC_FRAMEDONE             BIT(24)
> +#define INTSRC_LPDRTOUT                      BIT(21)
> +#define INTSRC_TATOUT                        BIT(20)
> +#define INTSRC_RXDATDONE             BIT(18)
> +#define INTSRC_RXTE                  BIT(17)
> +#define INTSRC_RXACK                 BIT(16)
> +#define INTSRC_MASK                  (INTSRC_PLLSTABLE       |       \
> +                                      INTSRC_SWRSTRELEASE    |       \
> +                                      INTSRC_SFRPLFIFOEMPTY  |       \
> +                                      INTSRC_SFRPHFIFOEMPTY  |       \
> +                                      INTSRC_FRAMEDONE       |       \
> +                                      INTSRC_LPDRTOUT        |       \
> +                                      INTSRC_TATOUT          |       \
> +                                      INTSRC_RXDATDONE       |       \
> +                                      INTSRC_RXTE            |       \
> +                                      INTSRC_RXACK)
> +
> +#define INTMSK_MSKPLLSTABLE          BIT(31)
> +#define INTMSK_MSKSWRELEASE          BIT(30)
> +#define INTMSK_MSKSFRPLFIFOEMPTY     BIT(29)
> +#define INTMSK_MSKSFRPHFIFOEMPTY     BIT(28)
> +#define INTMSK_MSKFRAMEDONE          BIT(24)
> +#define INTMSK_MSKLPDRTOUT           BIT(21)
> +#define INTMSK_MSKTATOUT             BIT(20)
> +#define INTMSK_MSKRXDATDONE          BIT(18)
> +#define INTMSK_MSKRXTE                       BIT(17)
> +#define INTMSK_MSKRXACK                      BIT(16)
> +
> +#define PKTHDR_DATA1_MASK            GENMASK(23, 16)
> +#define PKTHDR_DATA1(x)                      FIELD_PREP(PKTHDR_DATA1_MASK, 
> (x))
> +#define PKTHDR_DATA1_GET(x)          FIELD_GET(PKTHDR_DATA1_MASK, (x))
> +#define PKTHDR_WC_MASK                       GENMASK(23, 8)
> +#define PKTHDR_WC_GET(x)             FIELD_GET(PKTHDR_WC_MASK, (x))
> +#define PKTHDR_DATA0_MASK            GENMASK(15, 8)
> +#define PKTHDR_DATA0(x)                      FIELD_PREP(PKTHDR_DATA0_MASK, 
> (x))
> +#define PKTHDR_DATA0_GET(x)          FIELD_GET(PKTHDR_DATA0_MASK, (x))
> +#define PKTHDR_DI_MASK                       GENMASK(7, 0)
> +#define PKTHDR_DI(x)                 FIELD_PREP(PKTHDR_DI_MASK, (x))
> +#define PKTHDR_DT_MASK                       GENMASK(5, 0)
> +#define PKTHDR_DT_GET(x)             FIELD_GET(PKTHDR_DT_MASK, (x))
> +
> +#define FIFOCTRL_FULLRX                      BIT(25)
> +#define FIFOCTRL_EMPTYRX             BIT(24)
> +#define FIFOCTRL_FULLHSFR            BIT(23)
> +#define FIFOCTRL_EMPTYHSFR           BIT(22)
> +#define FIFOCTRL_FULLLSFR            BIT(21)
> +#define FIFOCTRL_EMPTYLSFR           BIT(20)
> +#define FIFOCTRL_FULLHMAIN           BIT(11)
> +#define FIFOCTRL_EMPTYHMAIN          BIT(10)
> +#define FIFOCTRL_FULLLMAIN           BIT(9)
> +#define FIFOCTRL_EMPTYLMAIN          BIT(8)
> +#define FIFOCTRL_NINITRX             BIT(4)
> +#define FIFOCTRL_NINITSFR            BIT(3)
> +#define FIFOCTRL_NINITI80            BIT(2)
> +#define FIFOCTRL_NINITSUB            BIT(1)
> +#define FIFOCTRL_NINITMAIN           BIT(0)
> +#define FIFOCTRL_INIT_MASK           GENMASK(4, 0)
> +
> +#define PLLCTRL_PLLEN                        BIT(23)
> +#define PLLCTRL_PMS_P_MASK           GENMASK(18, 14)
> +#define PLLCTRL_PMS_P(x)             FIELD_PREP(PLLCTRL_PMS_P_MASK, (x))
> +#define PLLCTRL_PMS_M_MASK           GENMASK(12, 4)
> +#define PLLCTRL_PMS_M(x)             FIELD_PREP(PLLCTRL_PMS_M_MASK, (x))
> +#define PLLCTRL_PMS_S_MASK           GENMASK(2, 1)
> +#define PLLCTRL_PMS_S(x)             FIELD_PREP(PLLCTRL_PMS_S_MASK, (x))
> +
> +/* dsim all irqs index */
> +#define PLLSTABLE                    1
> +#define SWRSTRELEASE                 2
> +#define SFRPLFIFOEMPTY                       3
> +#define SFRPHFIFOEMPTY                       4
> +#define SYNCOVERRIDE                 5
> +#define BUSTURNOVER                  6
> +#define FRAMEDONE                    7
> +#define LPDRTOUT                     8
> +#define TATOUT                               9
> +#define RXDATDONE                    10
> +#define RXTE                         11
> +#define RXACK                                12
> +#define ERRRXECC                     13
> +#define ERRRXCRC                     14
> +#define ERRESC3                              15
> +#define ERRESC2                              16
> +#define ERRESC1                              17
> +#define ERRESC0                              18
> +#define ERRSYNC3                     19
> +#define ERRSYNC2                     20
> +#define ERRSYNC1                     21
> +#define ERRSYNC0                     22
> +#define ERRCONTROL3                  23
> +#define ERRCONTROL2                  24
> +#define ERRCONTROL1                  25
> +#define ERRCONTROL0                  26
> +
> +#define MIPI_FIFO_TIMEOUT            msecs_to_jiffies(250)
> +
> +#define DSIM_HFP_PKT_OVERHEAD                6
> +#define DSIM_HBP_PKT_OVERHEAD                6
> +#define DSIM_HSA_PKT_OVERHEAD                6
> +
> +struct sec_dsim_plat_data {
> +     unsigned int version;
> +     unsigned int pll_timer;
> +     unsigned int max_freq_hz;
> +     unsigned int esc_stop_state_cnt;
> +};
> +
> +struct sec_dsim {
> +     struct mipi_dsi_host host;
> +     struct drm_bridge bridge;
> +     struct drm_bridge *panel_bridge;
> +     struct device *dev;
> +
> +     struct clk *clk_phy_ref;
> +     struct clk *clk_bus;
> +     struct phy *phy;
> +
> +     struct regmap *regmap;
> +     struct drm_display_mode mode;
> +     int irq;
> +     unsigned int pll_clk_hz;
> +     unsigned int burst_clk_hz;
> +     unsigned int esc_clk_hz;
> +     unsigned int lanes;
> +     unsigned int channel;
> +     enum mipi_dsi_pixel_format format;
> +     unsigned long mode_flags;
> +
> +     struct completion pll_stable;
> +     struct completion ph_tx_done;
> +     struct completion pl_tx_done;
> +     struct completion rx_done;
> +     const struct sec_dsim_plat_data *pdata;
> +};
> +
> +static const struct regmap_config sec_dsim_regmap_config = {
> +     .reg_bits = 8,
> +     .val_bits = 32,
> +     .reg_stride = 4,
> +     .max_register = DSIM_PLLTMR,
> +     .name = DRIVER_NAME,
> +};
> +
> +static inline struct sec_dsim *host_to_dsim(struct mipi_dsi_host *host)
> +{
> +     return container_of(host, struct sec_dsim, host);
> +}
> +
> +static inline struct sec_dsim *bridge_to_dsim(struct drm_bridge *bridge)
> +{
> +     return container_of(bridge, struct sec_dsim, bridge);
> +}
> +
> +/* used for CEA standard modes */
> +struct dsim_hblank_par {
> +     char *name;             /* drm display mode name */
> +     int vrefresh;
> +     int hfp_wc;
> +     int hbp_wc;
> +     int hsa_wc;
> +     int lanes;
> +};
> +
> +#define DSIM_HBLANK_PARAM(nm, vf, hfp, hbp, hsa, num)        \
> +     .name     = (nm),                               \
> +     .vrefresh = (vf),                               \
> +     .hfp_wc   = (hfp),                              \
> +     .hbp_wc   = (hbp),                              \
> +     .hsa_wc   = (hsa),                              \
> +     .lanes    = (num)
> +
> +static const struct dsim_hblank_par hblank_4lanes[] = {
> +     /* {  88, 148, 44 } */
> +     { DSIM_HBLANK_PARAM("1920x1080", 60,  60, 105,  27, 4), },
> +     /* { 528, 148, 44 } */
> +     { DSIM_HBLANK_PARAM("1920x1080", 50, 390, 105,  27, 4), },
> +     /* {  88, 148, 44 } */
> +     { DSIM_HBLANK_PARAM("1920x1080", 30,  60, 105,  27, 4), },
> +     /* { 110, 220, 40 } */
> +     { DSIM_HBLANK_PARAM("1280x720",  60,  78, 159,  24, 4), },
> +     /* { 440, 220, 40 } */
> +     { DSIM_HBLANK_PARAM("1280x720",  50, 324, 159,  24, 4), },
> +     /* {  16,  60, 62 } */
> +     { DSIM_HBLANK_PARAM("720x480",   60,   6,  39,  40, 4), },
> +     /* {  12,  68, 64 } */
> +     { DSIM_HBLANK_PARAM("720x576",   50,   3,  45,  42, 4), },
> +     /* {  16,  48, 96 } */
> +     { DSIM_HBLANK_PARAM("640x480",   60,   6,  30,  66, 4), },
> +};
> +
> +static const struct dsim_hblank_par hblank_2lanes[] = {
> +     /* {  88, 148, 44 } */
> +     { DSIM_HBLANK_PARAM("1920x1080", 30, 114, 210,  60, 2), },
> +     /* { 110, 220, 40 } */
> +     { DSIM_HBLANK_PARAM("1280x720",  60, 159, 320,  40, 2), },
> +     /* { 440, 220, 40 } */
> +     { DSIM_HBLANK_PARAM("1280x720",  50, 654, 320,  40, 2), },
> +     /* {  16,  60, 62 } */
> +     { DSIM_HBLANK_PARAM("720x480",   60,  16,  66,  88, 2), },
> +     /* {  12,  68, 64 } */
> +     { DSIM_HBLANK_PARAM("720x576",   50,  12,  96,  72, 2), },
> +     /* {  16,  48, 96 } */
> +     { DSIM_HBLANK_PARAM("640x480",   60,  18,  66, 138, 2), },
> +};
> +
> +static
> +const struct dsim_hblank_par *sec_dsim_get_hblank_par(struct sec_dsim *dsim)
> +{
> +     struct drm_display_mode *mode = &dsim->mode;
> +     const struct dsim_hblank_par *hpar, *hblank;
> +     int i, size;
> +
> +     if (unlikely(!mode->name))
> +             return NULL;
> +
> +     switch (dsim->lanes) {
> +     case 2:
> +             hblank = hblank_2lanes;
> +             size   = ARRAY_SIZE(hblank_2lanes);
> +             break;
> +     case 4:
> +             hblank = hblank_4lanes;
> +             size   = ARRAY_SIZE(hblank_4lanes);
> +             break;
> +     default:
> +             DRM_DEV_ERROR(dsim->dev,
> +                           "No hblank data for mode %s with %d lanes\n",
> +                           mode->name, dsim->lanes);
> +             return NULL;
> +     }
> +
> +     for (i = 0; i < size; i++) {
> +             hpar = &hblank[i];
> +
> +             if (!strcmp(mode->name, hpar->name)) {
> +                     if (drm_mode_vrefresh(mode) != hpar->vrefresh)
> +                             continue;
> +
> +                     /* found */
> +                     return hpar;
> +             }
> +     }
> +
> +     return NULL;
> +}
> +
> +static void dsim_write(struct sec_dsim *dsim, unsigned int reg, u32 val)
> +{
> +     int ret;
> +
> +     ret = regmap_write(dsim->regmap, reg, val);
> +     if (ret < 0)
> +             DRM_DEV_ERROR(dsim->dev,
> +                           "failed to write sec dsim reg 0x%x: %d\n",
> +                           reg, ret);
> +}
> +
> +static u32 dsim_read(struct sec_dsim *dsim, u32 reg)
> +{
> +     unsigned int val;
> +     int ret;
> +
> +     ret = regmap_read(dsim->regmap, reg, &val);
> +     if (ret < 0)
> +             DRM_DEV_ERROR(dsim->dev,
> +                           "failed to read sec dsim reg 0x%x: %d\n",
> +                           reg, ret);
> +
> +     return val;
> +}
> +
> +static void __maybe_unused sec_dsim_irq_mask(struct sec_dsim *dsim,
> +                                               int irq_idx)
> +{
> +     uint32_t intmsk;
> +
> +     intmsk = dsim_read(dsim, DSIM_INTMSK);
> +
> +     switch (irq_idx) {
> +     case PLLSTABLE:
> +             intmsk |= INTMSK_MSKPLLSTABLE;
> +             break;
> +     case SWRSTRELEASE:
> +             intmsk |= INTMSK_MSKSWRELEASE;
> +             break;
> +     case SFRPLFIFOEMPTY:
> +             intmsk |= INTMSK_MSKSFRPLFIFOEMPTY;
> +             break;
> +     case SFRPHFIFOEMPTY:
> +             intmsk |= INTMSK_MSKSFRPHFIFOEMPTY;
> +             break;
> +     case FRAMEDONE:
> +             intmsk |= INTMSK_MSKFRAMEDONE;
> +             break;
> +     case LPDRTOUT:
> +             intmsk |= INTMSK_MSKLPDRTOUT;
> +             break;
> +     case TATOUT:
> +             intmsk |= INTMSK_MSKTATOUT;
> +             break;
> +     case RXDATDONE:
> +             intmsk |= INTMSK_MSKRXDATDONE;
> +             break;
> +     case RXTE:
> +             intmsk |= INTMSK_MSKRXTE;
> +             break;
> +     case RXACK:
> +             intmsk |= INTMSK_MSKRXACK;
> +             break;
> +     default:
> +             /* unsupported irq */
> +             return;
> +     }
> +
> +     dsim_write(dsim, DSIM_INTMSK, intmsk);
> +}
> +
> +static void sec_dsim_irq_unmask(struct sec_dsim *dsim,
> +                                  int irq_idx)
> +{
> +     uint32_t intmsk;
> +
> +     intmsk = dsim_read(dsim, DSIM_INTMSK);
> +
> +     switch (irq_idx) {
> +     case PLLSTABLE:
> +             intmsk &= ~INTMSK_MSKPLLSTABLE;
> +             break;
> +     case SWRSTRELEASE:
> +             intmsk &= ~INTMSK_MSKSWRELEASE;
> +             break;
> +     case SFRPLFIFOEMPTY:
> +             intmsk &= ~INTMSK_MSKSFRPLFIFOEMPTY;
> +             break;
> +     case SFRPHFIFOEMPTY:
> +             intmsk &= ~INTMSK_MSKSFRPHFIFOEMPTY;
> +             break;
> +     case FRAMEDONE:
> +             intmsk &= ~INTMSK_MSKFRAMEDONE;
> +             break;
> +     case LPDRTOUT:
> +             intmsk &= ~INTMSK_MSKLPDRTOUT;
> +             break;
> +     case TATOUT:
> +             intmsk &= ~INTMSK_MSKTATOUT;
> +             break;
> +     case RXDATDONE:
> +             intmsk &= ~INTMSK_MSKRXDATDONE;
> +             break;
> +     case RXTE:
> +             intmsk &= ~INTMSK_MSKRXTE;
> +             break;
> +     case RXACK:
> +             intmsk &= ~INTMSK_MSKRXACK;
> +             break;
> +     default:
> +             /* unsupported irq */
> +             return;
> +     }
> +
> +     dsim_write(dsim, DSIM_INTMSK, intmsk);
> +}
> +
> +/* write 1 clear irq */
> +static void sec_dsim_irq_clear(struct sec_dsim *dsim,
> +                                 int irq_idx)
> +{
> +     uint32_t intsrc = 0;
> +
> +     switch (irq_idx) {
> +     case PLLSTABLE:
> +             intsrc |= INTSRC_PLLSTABLE;
> +             break;
> +     case SWRSTRELEASE:
> +             intsrc |= INTSRC_SWRSTRELEASE;
> +             break;
> +     case SFRPLFIFOEMPTY:
> +             intsrc |= INTSRC_SFRPLFIFOEMPTY;
> +             break;
> +     case SFRPHFIFOEMPTY:
> +             intsrc |= INTSRC_SFRPHFIFOEMPTY;
> +             break;
> +     case FRAMEDONE:
> +             intsrc |= INTSRC_FRAMEDONE;
> +             break;
> +     case LPDRTOUT:
> +             intsrc |= INTSRC_LPDRTOUT;
> +             break;
> +     case TATOUT:
> +             intsrc |= INTSRC_TATOUT;
> +             break;
> +     case RXDATDONE:
> +             intsrc |= INTSRC_RXDATDONE;
> +             break;
> +     case RXTE:
> +             intsrc |= INTSRC_RXTE;
> +             break;
> +     case RXACK:
> +             intsrc |= INTSRC_RXACK;
> +             break;
> +     default:
> +             /* unsupported irq */
> +             return;
> +     }
> +
> +     dsim_write(dsim, DSIM_INTSRC, intsrc);
> +}
> +
> +static void sec_dsim_irq_init(struct sec_dsim *dsim)
> +{
> +     sec_dsim_irq_unmask(dsim, PLLSTABLE);
> +     sec_dsim_irq_unmask(dsim, SWRSTRELEASE);
> +}
> +
> +static irqreturn_t sec_dsim_irq_handler(int irq, void *data)
> +{
> +     uint32_t intsrc, status;
> +     struct sec_dsim *dsim = data;
> +
> +     intsrc = dsim_read(dsim, DSIM_INTSRC);
> +     status = dsim_read(dsim, DSIM_STATUS);
> +
> +     if (WARN_ON(!intsrc)) {
> +             DRM_DEV_ERROR(dsim->dev, "interrupt is not from dsim\n");
> +             return IRQ_NONE;
> +     }
> +
> +     if (WARN_ON(!(intsrc & INTSRC_MASK))) {
> +             dev_warn(dsim->dev, "unenable irq happens: %#x\n", intsrc);
> +             /* just clear irqs */
> +             dsim_write(dsim, DSIM_INTSRC, intsrc);
> +             return IRQ_NONE;
> +     }
> +
> +     if (intsrc & INTSRC_PLLSTABLE) {
> +             WARN_ON(!(status & STATUS_PLLSTABLE));
> +             sec_dsim_irq_clear(dsim, PLLSTABLE);
> +             complete(&dsim->pll_stable);
> +     }
> +
> +     if (intsrc & INTSRC_SWRSTRELEASE)
> +             sec_dsim_irq_clear(dsim, SWRSTRELEASE);
> +
> +     if (intsrc & INTSRC_SFRPLFIFOEMPTY) {
> +             sec_dsim_irq_clear(dsim, SFRPLFIFOEMPTY);
> +             complete(&dsim->pl_tx_done);
> +     }
> +
> +     if (intsrc & INTSRC_SFRPHFIFOEMPTY) {
> +             sec_dsim_irq_clear(dsim, SFRPHFIFOEMPTY);
> +             complete(&dsim->ph_tx_done);
> +     }
> +
> +     if (WARN_ON(intsrc & INTSRC_LPDRTOUT)) {
> +             sec_dsim_irq_clear(dsim, LPDRTOUT);
> +             dev_warn(dsim->dev, "LP RX timeout\n");
> +     }
> +
> +     if (WARN_ON(intsrc & INTSRC_TATOUT)) {
> +             sec_dsim_irq_clear(dsim, TATOUT);
> +             dev_warn(dsim->dev, "Turns around Acknowledge timeout\n");
> +     }
> +
> +     if (intsrc & INTSRC_RXDATDONE) {
> +             sec_dsim_irq_clear(dsim, RXDATDONE);
> +             complete(&dsim->rx_done);
> +     }
> +
> +     if (intsrc & INTSRC_RXTE) {
> +             sec_dsim_irq_clear(dsim, RXTE);
> +             DRM_DEV_DEBUG(dsim->dev, "TE Rx trigger received\n");
> +     }
> +
> +     if (intsrc & INTSRC_RXACK) {
> +             sec_dsim_irq_clear(dsim, RXACK);
> +             DRM_DEV_DEBUG(dsim->dev, "ACK Rx trigger received\n");
> +     }
> +
> +     return IRQ_HANDLED;
> +}
> +
> +static void sec_dsim_config_cmd_lpm(struct sec_dsim *dsim, bool enable)
> +{
> +     u32 reg;
> +
> +     reg = dsim_read(dsim, DSIM_ESCMODE);
> +
> +     if (enable)
> +             reg |= ESCMODE_CMDLPDT;
> +     else
> +             reg &= ~ESCMODE_CMDLPDT;
> +
> +     dsim_write(dsim, DSIM_ESCMODE, reg);
> +}
> +
> +static void sec_dsim_write_pl_to_sfr_fifo(struct sec_dsim *dsim,
> +                                       const void *payload,
> +                                       size_t length)
> +{
> +     uint32_t pl_data;
> +
> +     if (!length)
> +             return;
> +
> +     while (length >= 4) {
> +             pl_data = get_unaligned_le32(payload);
> +             dsim_write(dsim, DSIM_PAYLOAD, pl_data);
> +             payload += 4;
> +             length -= 4;
> +     }
> +
> +     pl_data = 0;
> +     switch (length) {
> +     case 3:
> +             pl_data |= ((u8 *)payload)[2] << 16;
> +             /* fallthrough */
> +     case 2:
> +             pl_data |= ((u8 *)payload)[1] << 8;
> +             /* fallthrough */
> +     case 1:
> +             pl_data |= ((u8 *)payload)[0];
> +             dsim_write(dsim, DSIM_PAYLOAD, pl_data);
> +             break;
> +     }
> +}
> +
> +static void sec_dsim_write_ph_to_sfr_fifo(struct sec_dsim *dsim,
> +                                            void *header,
> +                                            bool use_lpm)
> +{
> +     u32 reg;
> +
> +     reg = dsim_read(dsim, DSIM_PKTHDR);
> +
> +     reg &= ~PKTHDR_DATA1_MASK;
> +     reg |= PKTHDR_DATA1(((u8 *)header)[2]); /* WC MSB */
> +     reg &= ~PKTHDR_DATA0_MASK;
> +     reg |= PKTHDR_DATA0(((u8 *)header)[1]); /* WC LSB  */
> +     reg &= ~PKTHDR_DI_MASK;
> +     reg |= PKTHDR_DI(((u8 *)header)[0]);    /* Data ID */
> +     dsim_write(dsim, DSIM_PKTHDR, reg);
> +}
> +
> +static int sec_dsim_read_pl_from_sfr_fifo(struct sec_dsim *dsim,
> +                                            void *payload,
> +                                            size_t length)
> +{
> +     uint8_t data_type;
> +     uint16_t word_count = 0;
> +     uint32_t reg, ph, pl;
> +
> +     reg = dsim_read(dsim, DSIM_FIFOCTRL);
> +
> +     if (WARN_ON(reg & FIFOCTRL_EMPTYRX))
> +             return -EINVAL;
> +
> +     ph = dsim_read(dsim, DSIM_RXFIFO);
> +     data_type = PKTHDR_DT_GET(ph);
> +     switch (data_type) {
> +     case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> +             DRM_DEV_ERROR(dsim->dev,
> +                           "peripheral report error: (0-7)%lx, (8-15)%lx\n",
> +                           PKTHDR_DATA0_GET(ph), PKTHDR_DATA1_GET(ph));
> +             return -EPROTO;
> +     case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> +     case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> +             if (!WARN_ON(length < 2)) {
> +                     ((u8 *)payload)[1] = PKTHDR_DATA1_GET(ph);
> +                     word_count++;
> +             }
> +             /* fall through */
> +     case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> +     case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> +             ((u8 *)payload)[0] = PKTHDR_DATA0_GET(ph);
> +             word_count++;
> +             length = word_count;
> +             break;
> +     case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE:
> +     case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE:
> +             word_count = PKTHDR_WC_GET(ph);
> +             if (word_count > length) {
> +                     DRM_DEV_ERROR(dsim->dev, "invalid receive buffer 
> length\n");
> +                     return -EINVAL;
> +             }
> +
> +             length = word_count;
> +
> +             while (word_count >= 4) {
> +                     pl = dsim_read(dsim, DSIM_RXFIFO);
> +                     ((u8 *)payload)[0] = pl & 0xff;
> +                     ((u8 *)payload)[1] = (pl >> 8)  & 0xff;
> +                     ((u8 *)payload)[2] = (pl >> 16) & 0xff;
> +                     ((u8 *)payload)[3] = (pl >> 24) & 0xff;
> +                     payload += 4;
> +                     word_count -= 4;
> +             }
> +
> +             if (word_count > 0) {
> +                     pl = dsim_read(dsim, DSIM_RXFIFO);
> +
> +                     switch (word_count) {
> +                     case 3:
> +                             ((u8 *)payload)[2] = (pl >> 16) & 0xff;
> +                             /* fall through */
> +                     case 2:
> +                             ((u8 *)payload)[1] = (pl >> 8) & 0xff;
> +                             /* fall through */
> +                     case 1:
> +                             ((u8 *)payload)[0] = pl & 0xff;
> +                             break;
> +                     }
> +             }
> +
> +             break;
> +     default:
> +             return -EINVAL;
> +     }
> +
> +     return length;
> +}
> +
> +static ssize_t sec_dsim_host_transfer(struct mipi_dsi_host *host,
> +                                        const struct mipi_dsi_msg *msg)
> +{
> +     int ret;
> +     bool use_lpm;
> +     struct mipi_dsi_packet packet;
> +     struct sec_dsim *dsim = host_to_dsim(host);
> +
> +     if ((msg->rx_buf && !msg->rx_len) || (msg->rx_len && !msg->rx_buf))
> +             return -EINVAL;
> +
> +     ret = mipi_dsi_create_packet(&packet, msg);
> +     if (ret) {
> +             DRM_DEV_ERROR(dsim->dev, "failed to create dsi packet: %d\n", 
> ret);
> +             return ret;
> +     }
> +
> +     /* need to read data from peripheral */
> +     if (unlikely(msg->rx_buf))
> +             reinit_completion(&dsim->rx_done);
> +
> +     /* config LPM for CMD TX */
> +     use_lpm = msg->flags & MIPI_DSI_MSG_USE_LPM ? true : false;
> +     sec_dsim_config_cmd_lpm(dsim, use_lpm);
> +
> +     if (packet.payload_length) {            /* Long Packet case */
> +             reinit_completion(&dsim->pl_tx_done);
> +
> +             /* write packet payload */
> +             sec_dsim_write_pl_to_sfr_fifo(dsim,
> +                                                packet.payload,
> +                                                packet.payload_length);
> +
> +             /* write packet header */
> +             sec_dsim_write_ph_to_sfr_fifo(dsim,
> +                                                packet.header,
> +                                                use_lpm);
> +
> +             ret = wait_for_completion_timeout(&dsim->ph_tx_done,
> +                                               MIPI_FIFO_TIMEOUT);
> +             if (!ret) {
> +                     DRM_DEV_ERROR(dsim->dev, "wait payload tx done time 
> out\n");
> +                     return -EBUSY;
> +             }
> +     } else {
> +             reinit_completion(&dsim->ph_tx_done);
> +
> +             /* write packet header */
> +             sec_dsim_write_ph_to_sfr_fifo(dsim,
> +                                                packet.header,
> +                                                use_lpm);
> +
> +             ret = wait_for_completion_timeout(&dsim->ph_tx_done,
> +                                               MIPI_FIFO_TIMEOUT);
> +             if (!ret) {
> +                     DRM_DEV_ERROR(dsim->dev, "wait pkthdr tx done time 
> out\n");
> +                     return -EBUSY;
> +             }
> +     }
> +
> +     /* read packet payload */
> +     if (unlikely(msg->rx_buf)) {
> +             ret = wait_for_completion_timeout(&dsim->rx_done,
> +                                               MIPI_FIFO_TIMEOUT);
> +             if (!ret) {
> +                     DRM_DEV_ERROR(dsim->dev, "wait rx done time out\n");
> +                     return -EBUSY;
> +             }
> +
> +             ret = sec_dsim_read_pl_from_sfr_fifo(dsim,
> +                                                       msg->rx_buf,
> +                                                       msg->rx_len);
> +             if (ret < 0)
> +                     return ret;
> +     }
> +
> +     return 0;
> +}
> +
> +static int sec_dsim_host_attach(struct mipi_dsi_host *host,
> +                             struct mipi_dsi_device *device)
> +{
> +     struct sec_dsim *dsim = host_to_dsim(host);
> +
> +     dsim->lanes      = device->lanes;
> +     dsim->channel    = device->channel;
> +     dsim->format     = device->format;
> +     dsim->mode_flags = device->mode_flags;
> +
> +     return 0;
> +}
> +
> +static const struct mipi_dsi_host_ops sec_dsim_host_ops = {
> +     .attach   = sec_dsim_host_attach,
> +     .transfer = sec_dsim_host_transfer,
> +};
> +
> +static void sec_dsim_video_mode(struct sec_dsim *dsim)
> +{
> +     struct drm_display_mode *mode = &dsim->mode;
> +     unsigned int bpp = mipi_dsi_pixel_format_to_bpp(dsim->format);
> +     const struct dsim_hblank_par *hpar = NULL;
> +     unsigned int hfp, hbp, hsa, vfp, vbp, vsa;
> +     unsigned int hfp_wc, hbp_wc, hsa_wc, wc;
> +     unsigned int reg;
> +
> +     hfp = mode->hsync_start - mode->hdisplay;
> +     hbp = mode->htotal - mode->hsync_end;
> +     hsa = mode->hsync_end - mode->hsync_start;
> +     vfp = mode->vsync_start - mode->vdisplay;
> +     vbp = mode->vtotal - mode->vsync_end;
> +     vsa = mode->vsync_end - mode->vsync_start;
> +
> +     if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
> +             hpar = sec_dsim_get_hblank_par(dsim);
> +             if (!hpar)
> +                     DRM_DEV_DEBUG(dsim->dev,
> +                                   "No pre-exist hpar can be used\n");
> +     }
> +
> +     /* vertical porch */
> +     reg = dsim_read(dsim, DSIM_MVPORCH);
> +
> +     reg &= ~MVPORCH_MAINVBP_MASK;
> +     reg |= MVPORCH_MAINVBP(vbp);
> +     reg &= ~MVPORCH_STABLEVFP_MASK;
> +     reg |= MVPORCH_STABLEVFP(vfp);
> +     reg &= ~MVPORCH_CMDALLOW_MASK;
> +     reg |= MVPORCH_CMDALLOW(0x0);
> +     dsim_write(dsim, DSIM_MVPORCH, reg);
> +
> +     if (!hpar) {
> +             wc = DIV_ROUND_UP(hfp * (bpp >> 3), dsim->lanes);
> +             hfp_wc = wc > DSIM_HFP_PKT_OVERHEAD ?
> +                      wc - DSIM_HFP_PKT_OVERHEAD : hfp;
> +             wc = DIV_ROUND_UP(hbp * (bpp >> 3), dsim->lanes);
> +             hbp_wc = wc > DSIM_HBP_PKT_OVERHEAD ?
> +                      wc - DSIM_HBP_PKT_OVERHEAD : hbp;
> +     } else {
> +             hfp_wc = hpar->hfp_wc;
> +             hbp_wc = hpar->hbp_wc;
> +     }
> +
> +     /* horizontal porch */
> +     reg = dsim_read(dsim, DSIM_MHPORCH);
> +
> +     reg &= ~MVPORCH_MAINHBP_MASK;
> +     reg |= MVPORCH_MAINHBP(hbp_wc);
> +     reg &= ~MVPORCH_MAINHFP_MASK;
> +     reg |= MVPORCH_MAINHFP(hfp_wc);
> +     dsim_write(dsim, DSIM_MHPORCH, reg);
> +
> +     if (!hpar) {
> +             wc = DIV_ROUND_UP(hsa * (bpp >> 3), dsim->lanes);
> +             hsa_wc = wc > DSIM_HSA_PKT_OVERHEAD ?
> +                      wc - DSIM_HSA_PKT_OVERHEAD : hsa;
> +     } else {
> +             hsa_wc = hpar->hsa_wc;
> +     }
> +
> +     /* sync area */
> +     reg = dsim_read(dsim, DSIM_MSYNC);
> +
> +     reg &= ~MVPORCH_MAINHSA_MASK;
> +     reg |= MVPORCH_MAINHSA(hsa_wc);
> +     reg &= ~MVPORCH_MAINVSA_MASK;
> +     reg |= MVPORCH_MAINVSA(vsa);
> +     dsim_write(dsim, DSIM_MSYNC, reg);
> +}
> +
> +static void sec_dsim_display_mode(struct sec_dsim *dsim)
> +{
> +     struct drm_display_mode *mode = &dsim->mode;
> +     u32 reg;
> +
> +     if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO)
> +             sec_dsim_video_mode(dsim);
> +
> +     /* image resolution */
> +     reg = dsim_read(dsim, DSIM_MDRESOL);
> +
> +     reg &= ~MVPORCH_MAINHRESOL_MASK;
> +     reg |= MVPORCH_MAINHRESOL(mode->hdisplay);
> +     reg &= ~MVPORCH_MAINVRESOL_MASK;
> +     reg |= MVPORCH_MAINVRESOL(mode->vdisplay);
> +
> +     dsim_write(dsim, DSIM_MDRESOL, reg);
> +}
> +
> +static void sec_dsim_config_bridge(struct sec_dsim *dsim)
> +{
> +     const struct sec_dsim_plat_data *pdata = dsim->pdata;
> +     u32 reg;
> +
> +     reg = dsim_read(dsim, DSIM_CONFIG);
> +
> +     if (dsim->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> +             reg |= CONFIG_NON_CONTINUOUS_CLOCK_LANE;
> +             reg |= CONFIG_CLKLANE_STOP_START;
> +     }
> +
> +     if (!(dsim->mode_flags & MIPI_DSI_MODE_VSYNC_FLUSH))
> +             reg |= CONFIG_MFLUSH_VS;
> +
> +     /* disable EoT packets in HS mode */
> +     if (!(dsim->mode_flags & MIPI_DSI_MODE_EOT_PACKET))
> +             reg |= CONFIG_EOT_R03;
> +
> +     if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +             reg |= CONFIG_VIDEOMODE;
> +
> +             if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
> +                     reg |= CONFIG_BURSTMODE;
> +
> +             else if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE)
> +                     reg |= CONFIG_SYNCINFORM;
> +
> +             if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_AUTO_VERT)
> +                     reg |= CONFIG_AUTOMODE;
> +
> +             if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSE)
> +                     reg |= CONFIG_HSEDISABLEMODE;
> +
> +             if (!(dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HFP))
> +                     reg |= CONFIG_HFPDISABLEMODE;
> +
> +             if (!(dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HBP))
> +                     reg |= CONFIG_HBPDISABLEMODE;
> +
> +             if (!(dsim->mode_flags & MIPI_DSI_MODE_VIDEO_HSA))
> +                     reg |= CONFIG_HSADISABLEMODE;
> +     }
> +
> +     /* pixel format */
> +     reg &= ~CONFIG_MAINPIXFORMAT_MASK;
> +     if (dsim->mode_flags & MIPI_DSI_MODE_VIDEO) {
> +             switch (dsim->format) {
> +             case MIPI_DSI_FMT_RGB565:
> +                     reg |= CONFIG_MAINPIXFORMAT(0x4);
> +                     break;
> +             case MIPI_DSI_FMT_RGB666_PACKED:
> +                     reg |= CONFIG_MAINPIXFORMAT(0x5);
> +                     break;
> +             case MIPI_DSI_FMT_RGB666:
> +                     reg |= CONFIG_MAINPIXFORMAT(0x6);
> +                     break;
> +             case MIPI_DSI_FMT_RGB888:
> +                     reg |= CONFIG_MAINPIXFORMAT(0x7);
> +                     break;
> +             default:
> +                     reg |= CONFIG_MAINPIXFORMAT(0x7);
> +                     break;
> +             }
> +     }
> +
> +     /* number of data lanes */
> +     reg &= ~CONFIG_NUMOFDATLANE_MASK;
> +     reg |= CONFIG_NUMOFDATLANE(dsim->lanes - 1);
> +
> +     /* enable data, clock lane */
> +     reg &= ~CONFIG_LANEEN_MASK;
> +     reg |= CONFIG_LANEEN(BIT(dsim->lanes) - 1);
> +     reg |= CONFIG_CLKLANEEN;
> +
> +     dsim_write(dsim, DSIM_CONFIG, reg);
> +
> +     /* escape mode */
> +     reg = dsim_read(dsim, DSIM_ESCMODE);
> +
> +     reg &= ~ESCMODE_STOPSTATE_CN_MASK;
> +     reg |= ESCMODE_STOPSTATE_CN(pdata->esc_stop_state_cnt);
> +     dsim_write(dsim, DSIM_ESCMODE, reg);
> +
> +     /* timeout */
> +     reg = dsim_read(dsim, DSIM_TIMEOUT);
> +
> +     reg &= ~TIMEOUT_LPDRTOUT_MASK;
> +     reg |= TIMEOUT_LPDRTOUT(0xffff);
> +     reg &= ~TIMEOUT_BTAOUT_MASK;
> +     reg |= TIMEOUT_BTAOUT(0xff);
> +     dsim_write(dsim, DSIM_TIMEOUT, reg);
> +}
> +
> +#ifndef MHZ
> +#define MHZ  (1000*1000)
> +#endif
> +
> +static unsigned long sec_dsim_pll_find_pms(struct sec_dsim *dsim,
> +                                        unsigned long fin,
> +                                        unsigned long fout,
> +                                        u8 *p, u16 *m, u8 *s)
> +{
> +     const struct sec_dsim_plat_data *pdata = dsim->pdata;
> +     unsigned long best_freq = 0;
> +     u32 min_delta = 0xffffffff;
> +     u8 p_min, p_max;
> +     u8 _p, best_p;
> +     u16 _m, best_m;
> +     u8 _s, best_s;
> +
> +     p_min = DIV_ROUND_UP(fin, (12 * MHZ));
> +     p_max = fin / (6 * MHZ);
> +
> +     for (_p = p_min; _p <= p_max; ++_p) {
> +             for (_s = 0; _s <= 5; ++_s) {
> +                     u64 tmp;
> +                     u32 delta;
> +
> +                     tmp = (u64)fout * (_p << _s);
> +                     do_div(tmp, fin);
> +                     _m = tmp;
> +                     if (_m < 41 || _m > 125)
> +                             continue;
> +
> +                     tmp = (u64)_m * fin;
> +                     do_div(tmp, _p);
> +                     if (tmp < 500 * MHZ || tmp > pdata->max_freq_hz * MHZ)
> +                             continue;
> +
> +                     tmp = (u64)_m * fin;
> +                     do_div(tmp, _p << _s);
> +
> +                     delta = abs(fout - tmp);
> +                     if (delta < min_delta) {
> +                             best_p = _p;
> +                             best_m = _m;
> +                             best_s = _s;
> +                             min_delta = delta;
> +                             best_freq = tmp;
> +                     }
> +             }
> +     }
> +
> +     if (best_freq) {
> +             *p = best_p;
> +             *m = best_m;
> +             *s = best_s;
> +     }
> +
> +     return best_freq;
> +}
> +
> +static unsigned long sec_dsim_set_pll(struct sec_dsim *dsim)
> +{
> +     unsigned long fin, fout, freq;
> +     u8 p, s;
> +     u16 m;
> +     u32 reg;
> +
> +     fin = dsim->pll_clk_hz;
> +     freq = dsim->burst_clk_hz;
> +     fout = sec_dsim_pll_find_pms(dsim, fin, freq, &p, &m, &s);
> +     if (!fout) {
> +             DRM_DEV_ERROR(dsim->dev,
> +                           "failed to find PLL PMS for requested 
> frequency\n");
> +             return 0;
> +     }
> +     DRM_DEV_DEBUG(dsim->dev,
> +                   "PLL freq %lu, (p %d, m %d, s %d)\n", fout, p, m, s);
> +
> +     reg = PLLCTRL_PLLEN | PLLCTRL_PMS_P(p) | PLLCTRL_PMS_M(m) | 
> PLLCTRL_PMS_S(s);
> +
> +     dsim_write(dsim, DSIM_PLLCTRL, reg);
> +
> +     regmap_read_poll_timeout(dsim->regmap, DSIM_STATUS, reg,
> +                              reg & STATUS_PLLSTABLE, 0, 1000);
> +     return fout;
> +}
> +
> +static int sec_dsim_enable_clock(struct sec_dsim *dsim)
> +{
> +     const struct sec_dsim_plat_data *pdata = dsim->pdata;
> +     unsigned long hs_clk, byte_clk, esc_clk;
> +     unsigned long esc_div;
> +     u32 reg;
> +
> +     /* pll timer */
> +     dsim_write(dsim, DSIM_PLLTMR, pdata->pll_timer);
> +
> +     /* pll control */
> +     hs_clk = sec_dsim_set_pll(dsim);
> +     if (!hs_clk) {
> +             DRM_DEV_ERROR(dsim->dev, "failed to configure DSI PLL\n");
> +             return -EFAULT;
> +     }
> +
> +     byte_clk = hs_clk / 8;
> +     esc_div = DIV_ROUND_UP(byte_clk, dsim->esc_clk_hz);
> +     esc_clk = byte_clk / esc_div;
> +
> +     if (esc_clk > 20 * MHZ) {
> +             ++esc_div;
> +             esc_clk = byte_clk / esc_div;
> +     }
> +
> +     DRM_DEV_DEBUG(dsim->dev,
> +                   "PLL: hs_clk = %lu, byte_clk = %lu, esc_clk = %lu, 
> esc_div = %lu\n",
> +                   hs_clk, byte_clk, esc_clk, esc_div);
> +
> +     /* clk control */
> +     reg = dsim_read(dsim, DSIM_CLKCTRL);
> +
> +     reg |= CLKCTRL_TXREQUESTHSCLK;
> +     reg |= CLKCTRL_ESCCLKEN;
> +     reg &= ~CLKCTRL_PLLBYPASS;
> +     reg &= ~CLKCTRL_BYTECLKSRC_MASK;
> +     reg |= CLKCTRL_BYTECLKEN;
> +     reg &= ~CLKCTRL_LANEESCDATAEN_MASK;
> +     reg |= CLKCTRL_LANEESCDATAEN(BIT(dsim->lanes) - 1);
> +     reg |= CLKCTRL_LANEESCCLKEN;
> +     reg &= ~CLKCTRL_ESCPRESCALER_MASK;
> +     reg |= CLKCTRL_ESCPRESCALER(esc_div);
> +
> +     dsim_write(dsim, DSIM_CLKCTRL, reg);
> +
> +     return 0;
> +}
> +
> +static void sec_dsim_fifo_enable(struct sec_dsim *dsim, bool enable)
> +{
> +     u32 reg;
> +
> +     reg = dsim_read(dsim, DSIM_FIFOCTRL);
> +
> +     reg &= ~FIFOCTRL_INIT_MASK;
> +     dsim_write(dsim, DSIM_FIFOCTRL, reg);
> +     udelay(500);
> +
> +     if (!enable)
> +             return;
> +
> +     reg |= FIFOCTRL_NINITRX |
> +            FIFOCTRL_NINITSFR |
> +            FIFOCTRL_NINITI80 |
> +            FIFOCTRL_NINITSUB |
> +            FIFOCTRL_NINITMAIN;
> +     dsim_write(dsim, DSIM_FIFOCTRL, reg);
> +     udelay(500);
> +}
> +
> +static void sec_dsim_set_display(struct sec_dsim *dsim, bool enable)
> +{
> +     u32 reg;
> +
> +     reg = dsim_read(dsim, DSIM_MDRESOL);
> +
> +     if (enable)
> +             reg |= MDRESOL_MAINSTANDBY;
> +     else
> +             reg &= ~MDRESOL_MAINSTANDBY;
> +     dsim_write(dsim, DSIM_MDRESOL, reg);
> +}
> +
> +static void sec_dsim_bridge_enable(struct drm_bridge *bridge)
> +{
> +     struct sec_dsim *dsim = bridge_to_dsim(bridge);
> +     int ret;
> +
> +     /* enable bridge clocks */
> +     clk_prepare_enable(dsim->clk_bus);
> +     clk_prepare_enable(dsim->clk_phy_ref);
> +
> +     /* initialize the irq */
> +     sec_dsim_irq_init(dsim);
> +
> +     /* configure the bridge */
> +     sec_dsim_config_bridge(dsim);
> +
> +     /* enable fifo control */
> +     sec_dsim_fifo_enable(dsim, true);
> +
> +     /* configure the display mode */
> +     sec_dsim_display_mode(dsim);
> +
> +     /* config dsim pll */
> +     ret = sec_dsim_enable_clock(dsim);
> +     if (ret) {
> +             DRM_DEV_ERROR(dsim->dev, "failed to enable clock: %d\n", ret);
> +             return;
> +     }
> +
> +     /* power on the dphy */
> +     ret = phy_init(dsim->phy);
> +     if (ret) {
> +             DRM_DEV_ERROR(dsim->dev, "failed to init phy %d\n", ret);
> +             return;
> +     }
> +
> +     /* power on the dphy */
> +     ret = phy_power_on(dsim->phy);
> +     if (ret) {
> +             DRM_DEV_ERROR(dsim->dev, "failed to enable phy %d\n", ret);
> +             return;
> +     }
> +
> +     /* enable data transfer */
> +     sec_dsim_set_display(dsim, true);
> +}
> +
> +static void sec_dsim_disable_clock(struct sec_dsim *dsim)
> +{
> +     u32 reg;
> +
> +     /* clk control */
> +     reg = dsim_read(dsim, DSIM_CLKCTRL);
> +
> +     reg &= ~CLKCTRL_TXREQUESTHSCLK;
> +     reg &= ~CLKCTRL_BYTECLKEN;
> +     reg &= ~CLKCTRL_ESCCLKEN;
> +     reg &= ~CLKCTRL_LANEESCDATAEN_MASK;
> +     reg &= ~CLKCTRL_LANEESCCLKEN;
> +     dsim_write(dsim, DSIM_CLKCTRL, reg);
> +
> +     /* pll control */
> +     reg  = dsim_read(dsim, DSIM_PLLCTRL);
> +
> +     reg &= ~PLLCTRL_PLLEN;
> +     dsim_write(dsim, DSIM_PLLCTRL, reg);
> +}
> +
> +static void sec_dsim_bridge_disable(struct drm_bridge *bridge)
> +{
> +     struct sec_dsim *dsim = bridge_to_dsim(bridge);
> +
> +     /* disable data transfer */
> +     sec_dsim_set_display(dsim, false);
> +
> +     /* disable bridge clocks */
> +     sec_dsim_disable_clock(dsim);
> +
> +     /* disable fifo control */
> +     sec_dsim_fifo_enable(dsim, false);
> +
> +     /* power off the phy */
> +     phy_power_off(dsim->phy);
> +
> +     /* exit the phy */
> +     phy_exit(dsim->phy);
> +
> +     /* disable bridge clock */
> +     clk_disable_unprepare(dsim->clk_phy_ref);
> +     clk_disable_unprepare(dsim->clk_bus);
> +}
> +
> +static bool sec_dsim_bridge_mode_fixup(struct drm_bridge *bridge,
> +                                    const struct drm_display_mode *mode,
> +                                    struct drm_display_mode *adjusted_mode)
> +{
> +     adjusted_mode->flags |= (DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC);
> +     adjusted_mode->flags &= ~(DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
> +
> +     return true;
> +}
> +
> +static void sec_dsim_bridge_mode_set(struct drm_bridge *bridge,
> +                                  const struct drm_display_mode *mode,
> +                                  const struct drm_display_mode 
> *adjusted_mode)
> +{
> +     struct sec_dsim *dsim = bridge_to_dsim(bridge);
> +
> +     drm_mode_copy(&dsim->mode, adjusted_mode);
> +}
> +
> +static int sec_dsim_bridge_attach(struct drm_bridge *bridge,
> +                               enum drm_bridge_attach_flags flags)
> +{
> +     struct sec_dsim *dsim = bridge_to_dsim(bridge);
> +     struct drm_bridge *panel_bridge;
> +     struct drm_panel *panel;
> +     int ret;
> +
> +     ret = drm_of_find_panel_or_bridge(dsim->dev->of_node, 1, 0, &panel,
> +                                       &panel_bridge);
> +     if (ret)
> +             return ret;
> +
> +     if (panel) {
> +             panel_bridge = drm_panel_bridge_add(panel);
> +             if (IS_ERR(panel_bridge))
> +                     return PTR_ERR(panel_bridge);
> +     }
> +     dsim->panel_bridge = panel_bridge;
> +
> +     if (!dsim->panel_bridge)
> +             return -EPROBE_DEFER;
> +
> +     return drm_bridge_attach(bridge->encoder, dsim->panel_bridge, bridge,
> +                              flags);
> +}
> +
> +static void sec_dsim_bridge_detach(struct drm_bridge *bridge)
> +{
> +     struct sec_dsim *dsim = bridge_to_dsim(bridge);
> +
> +     drm_of_panel_bridge_remove(dsim->dev->of_node, 1, 0);
> +}
> +
> +static const struct drm_bridge_funcs sec_dsim_bridge_funcs = {
> +     .enable         = sec_dsim_bridge_enable,
> +     .disable        = sec_dsim_bridge_disable,
> +     .mode_set       = sec_dsim_bridge_mode_set,
> +     .mode_fixup     = sec_dsim_bridge_mode_fixup,
> +     .attach         = sec_dsim_bridge_attach,
> +     .detach         = sec_dsim_bridge_detach,
> +};
> +
> +static const struct drm_bridge_timings sec_dsim_bridge_timings = {
> +     .input_bus_flags = DRM_BUS_FLAG_DE_LOW,
> +};
> +
> +static const struct sec_dsim_plat_data imx8mm_mipi_dsim_plat_data = {
> +     .version                = 0x1060200,
> +     .pll_timer              = 500,
> +     .max_freq_hz            = 2100,
> +     .esc_stop_state_cnt     = 0xf,
> +};
> +
> +static const struct of_device_id sec_dsim_dt_ids[] = {
> +     {
> +             .compatible = "fsl,imx8mm-sec-dsim",
> +             .data = &imx8mm_mipi_dsim_plat_data,
> +     },
> +     { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, sec_dsim_dt_ids);
> +
> +static int sec_dsim_parse_dt(struct sec_dsim *dsim)
> +{
> +     struct platform_device *pdev = to_platform_device(dsim->dev);
> +     struct device *dev = dsim->dev;
> +     struct device_node *node = dev->of_node;
> +     struct clk *clk;
> +     void __iomem *base;
> +     u32 value;
> +     int irq;
> +     int ret;
> +
> +     base = devm_platform_ioremap_resource(pdev, 0);
> +     if (IS_ERR(base))
> +             return PTR_ERR(base);
> +
> +     dsim->regmap = devm_regmap_init_mmio(dev, base, 
> &sec_dsim_regmap_config);
> +     if (IS_ERR(dsim->regmap)) {
> +             ret = PTR_ERR(dsim->regmap);
> +             DRM_DEV_ERROR(dev, "failed to create sec dsim regmap: %d\n", 
> ret);
> +             return ret;
> +     }
> +
> +     dsim->phy = devm_phy_get(dev, "dphy");
> +     if (IS_ERR(dsim->phy)) {
> +             DRM_DEV_ERROR(dev, "failed to get dsim phy\n");
> +             return PTR_ERR(dsim->phy);
> +     }
> +
> +     clk = devm_clk_get(dev, "bus");
> +     if (IS_ERR(clk)) {
> +             ret = PTR_ERR(clk);
> +             DRM_DEV_ERROR(dev, "failed to get bus clock: %d\n", ret);
> +             return ret;
> +     }
> +     dsim->clk_bus = clk;
> +
> +     clk = devm_clk_get(dev, "phy_ref");
> +     if (IS_ERR(clk)) {
> +             ret = PTR_ERR(clk);
> +             DRM_DEV_ERROR(dev, "failed to get phy_ref clock: %d\n", ret);
> +             return ret;
> +     }
> +     dsim->clk_phy_ref = clk;
> +
> +     irq = platform_get_irq(pdev, 0);
> +     if (irq < 0)
> +             return -ENODEV;
> +
> +     ret = devm_request_irq(dev, irq, sec_dsim_irq_handler, 0, 
> dev_name(dev), dsim);
> +     if (ret) {
> +             DRM_DEV_ERROR(dev, "failed to request dsim irq: %d\n", ret);
> +             return ret;
> +     }
> +
> +     if (!of_property_read_u32(node, "samsung,pll-clock-frequency", &value))
> +             dsim->pll_clk_hz = value;
> +
> +     if (!of_property_read_u32(node, "samsung,burst-clock-frequency", 
> &value))
> +             dsim->burst_clk_hz = value;
> +
> +     if (!of_property_read_u32(node, "samsung,esc-clock-frequency", &value))
> +             dsim->esc_clk_hz = value;
> +
> +     init_completion(&dsim->pll_stable);
> +     init_completion(&dsim->ph_tx_done);
> +     init_completion(&dsim->pl_tx_done);
> +     init_completion(&dsim->rx_done);
> +
> +     return 0;
> +}
> +
> +static int sec_dsim_probe(struct platform_device *pdev)
> +{
> +     struct device *dev = &pdev->dev;
> +     const struct of_device_id *of_id;
> +     struct sec_dsim *dsim;
> +     int version;
> +     int ret;
> +
> +     dsim = devm_kzalloc(dev, sizeof(*dsim), GFP_KERNEL);
> +     if (!dsim) {
> +             DRM_DEV_ERROR(dev, "failed to allocate dsim\n");
> +             return -ENOMEM;
> +     }
> +
> +     of_id = of_match_device(sec_dsim_dt_ids, dev);
> +     if (!of_id)
> +             return -ENODEV;
> +
> +     dsim->pdata = of_id->data;
> +     dsim->dev = dev;
> +
> +     ret = sec_dsim_parse_dt(dsim);
> +     if (ret) {
> +             DRM_DEV_ERROR(dev, "failed to parse dt: %d\n", ret);
> +             return ret;
> +     }
> +
> +     version = dsim_read(dsim, DSIM_VERSION);
> +     WARN_ON(version != dsim->pdata->version);
> +     DRM_DEV_INFO(dev, "DSIM version number is %#x\n", version);
> +
> +     dsim->host.ops = &sec_dsim_host_ops;
> +     dsim->host.dev = dsim->dev;
> +
> +     ret = mipi_dsi_host_register(&dsim->host);
> +     if (ret) {
> +             DRM_DEV_ERROR(dev, "failed to register mipi dsi host: %d\n", 
> ret);
> +             return ret;
> +     }
> +
> +     dsim->bridge.driver_private = dsim;
> +     dsim->bridge.funcs = &sec_dsim_bridge_funcs;
> +     dsim->bridge.of_node = dev->of_node;
> +     dsim->bridge.timings = &sec_dsim_bridge_timings;
> +
> +     dev_set_drvdata(dev, dsim);
> +
> +     drm_bridge_add(&dsim->bridge);
> +
> +     return 0;
> +}
> +
> +static int sec_dsim_remove(struct platform_device *pdev)
> +{
> +     struct sec_dsim *dsim = platform_get_drvdata(pdev);
> +
> +     mipi_dsi_host_unregister(&dsim->host);
> +     drm_bridge_remove(&dsim->bridge);
> +
> +     return 0;
> +}
> +
> +struct platform_driver sec_dsim_driver = {
> +     .probe    = sec_dsim_probe,
> +     .remove   = sec_dsim_remove,
> +     .driver   = {
> +             .name = DRIVER_NAME,
> +             .of_match_table = sec_dsim_dt_ids,
> +     },
> +};
> +
> +module_platform_driver(sec_dsim_driver);
> +
> +MODULE_AUTHOR("Jagan Teki <ja...@amarulasolutions.com>");
> +MODULE_DESCRIPTION("Samsung SEC MIPI DSIM Bridge driver");
> +MODULE_LICENSE("GPL v2");

-- 
Regards,

Laurent Pinchart

Reply via email to