Re: [EXT] [PATCH v8 2/2] drm/bridge: Add NWL MIPI DSI host controller support
From: Robert Chiras
Date: Tue Dec 03 2019 - 04:50:28 EST
Hi Guido,
Since your last revision sent, I've done more tests here and found a
few more improvements that could be added to this driver.
See inline.
On Lu, 2019-12-02 at 20:35 +0100, Guido GÃnther wrote:
> Caution: EXT Email
>
> This adds initial support for the NWL MIPI DSI Host controller found
> on
> i.MX8 SoCs.
>
> It adds support for the i.MX8MQ but the same IP can be found on
> e.g. the i.MX8QXP.
>
> It has been tested on the Librem 5 devkit using mxsfb.
>
> Signed-off-by: Guido GÃnther <agx@xxxxxxxxxxx>
> Co-developed-by: Robert Chiras <robert.chiras@xxxxxxx>
> Signed-off-by: Robert Chiras <robert.chiras@xxxxxxx>
> Tested-by: Robert Chiras <robert.chiras@xxxxxxx>
> ---
> Âdrivers/gpu/drm/bridge/KconfigÂÂÂ|ÂÂÂ16 +
> Âdrivers/gpu/drm/bridge/MakefileÂÂ|ÂÂÂÂ3 +
> Âdrivers/gpu/drm/bridge/nwl-dsi.c | 1230
> ++++++++++++++++++++++++++++++
> Âdrivers/gpu/drm/bridge/nwl-dsi.h |ÂÂ144 ++++
> Â4 files changed, 1393 insertions(+)
> Âcreate mode 100644 drivers/gpu/drm/bridge/nwl-dsi.c
> Âcreate mode 100644 drivers/gpu/drm/bridge/nwl-dsi.h
>
> diff --git a/drivers/gpu/drm/bridge/Kconfig
> b/drivers/gpu/drm/bridge/Kconfig
> index 34362976cd6f..6fb534f55d22 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -65,6 +65,22 @@ config DRM_MEGACHIPS_STDPXXXX_GE_B850V3_FW
> ÂÂÂÂÂÂÂÂÂÂÂto DP++. This is used with the i.MX6 imx-ldb
> ÂÂÂÂÂÂÂÂÂÂÂdriver. You are likely to say N here.
>
> +config DRM_NWL_MIPI_DSI
> +ÂÂÂÂÂÂÂtristate "Northwest Logic MIPI DSI Host 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 MULTIPLEXER
> +ÂÂÂÂÂÂÂselect REGMAP_MMIO
> +ÂÂÂÂÂÂÂhelp
> +ÂÂÂÂÂÂÂÂÂThis enables the Northwest Logic MIPI DSI Host controller
> as
> +ÂÂÂÂÂÂÂÂÂfor example found on NXP's i.MX8 Processors.
> +
> Âconfig DRM_NXP_PTN3460
> ÂÂÂÂÂÂÂÂtristate "NXP PTN3460 DP/LVDS bridge"
> ÂÂÂÂÂÂÂÂdepends on OF
> diff --git a/drivers/gpu/drm/bridge/Makefile
> b/drivers/gpu/drm/bridge/Makefile
> index 4934fcf5a6f8..c3f3a43e9b8f 100644
> --- a/drivers/gpu/drm/bridge/Makefile
> +++ b/drivers/gpu/drm/bridge/Makefile
> @@ -16,4 +16,7 @@ obj-$(CONFIG_DRM_ANALOGIX_DP) += analogix/
> Âobj-$(CONFIG_DRM_I2C_ADV7511) += adv7511/
> Âobj-$(CONFIG_DRM_TI_SN65DSI86) += ti-sn65dsi86.o
> Âobj-$(CONFIG_DRM_TI_TFP410) += ti-tfp410.o
> +obj-$(CONFIG_DRM_NWL_MIPI_DSI) += nwl-dsi.o
> Âobj-y += synopsys/
> +
> +header-test-y += nwl-dsi.h
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi.c
> b/drivers/gpu/drm/bridge/nwl-dsi.c
> new file mode 100644
> index 000000000000..023191894fe4
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi.c
> @@ -0,0 +1,1230 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * i.MX8 NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +
> +#include <linux/bitfield.h>
> +#include <linux/clk.h>
> +#include <linux/irq.h>
> +#include <linux/math64.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/module.h>
> +#include <linux/mux/consumer.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/phy/phy.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/sys_soc.h>
> +#include <linux/time64.h>
> +
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_mipi_dsi.h>
> +#include <drm/drm_of.h>
> +#include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +
> +#include <video/mipi_display.h>
> +#include <video/videomode.h>
> +
> +#include "nwl-dsi.h"
> +
> +#define DRV_NAME "nwl-dsi"
> +
> +/* i.MX8 NWL quirks */
> +/* i.MX8MQ errata E11418 */
> +#define E11418_HS_MODE_QUIRKÂÂÂBIT(0)
> +
> +#define NWL_DSI_MIPI_FIFO_TIMEOUT msecs_to_jiffies(500)
> +
> +enum transfer_direction {
> +ÂÂÂÂÂÂÂDSI_PACKET_SEND,
> +ÂÂÂÂÂÂÂDSI_PACKET_RECEIVE,
> +};
> +
> +/* Possible platform specific clocks */
> +#define NWL_DSI_CLK_COREÂÂÂÂÂÂÂÂÂÂÂ"core"
> +#define NWL_DSI_MAX_PLATFORM_CLOCKS 1
> +
> +#define NWL_DSI_ENDPOINT_LCDIF 0
> +#define NWL_DSI_ENDPOINT_DCSS 1
> +
> +struct nwl_dsi_plat_clk_config {
> +ÂÂÂÂÂÂÂconst char *id;
> +ÂÂÂÂÂÂÂstruct clk *clk;
> +ÂÂÂÂÂÂÂbool present;
> +};
> +
> +struct nwl_dsi_transfer {
> +ÂÂÂÂÂÂÂconst struct mipi_dsi_msg *msg;
> +ÂÂÂÂÂÂÂstruct mipi_dsi_packet packet;
> +ÂÂÂÂÂÂÂstruct completion completed;
> +
> +ÂÂÂÂÂÂÂint status; /* status of transmission */
> +ÂÂÂÂÂÂÂenum transfer_direction direction;
> +ÂÂÂÂÂÂÂbool need_bta;
> +ÂÂÂÂÂÂÂu8 cmd;
> +ÂÂÂÂÂÂÂu16 rx_word_count;
> +ÂÂÂÂÂÂÂsize_t tx_len; /* in bytes */
> +ÂÂÂÂÂÂÂsize_t rx_len; /* in bytes */
> +};
> +
> +struct nwl_dsi {
> +ÂÂÂÂÂÂÂstruct drm_bridge bridge;
> +ÂÂÂÂÂÂÂstruct mipi_dsi_host dsi_host;
> +ÂÂÂÂÂÂÂstruct drm_bridge *panel_bridge;
> +ÂÂÂÂÂÂÂstruct device *dev;
> +ÂÂÂÂÂÂÂstruct phy *phy;
> +ÂÂÂÂÂÂÂunion phy_configure_opts phy_cfg;
> +ÂÂÂÂÂÂÂunsigned int quirks;
> +
> +ÂÂÂÂÂÂÂstruct regmap *regmap;
> +ÂÂÂÂÂÂÂint irq;
> +ÂÂÂÂÂÂÂstruct reset_control *rstc;
> +ÂÂÂÂÂÂÂstruct mux_control *mux;
> +
> +ÂÂÂÂÂÂÂ/* DSI clocks */
> +ÂÂÂÂÂÂÂstruct clk *phy_ref_clk;
> +ÂÂÂÂÂÂÂstruct clk *rx_esc_clk;
> +ÂÂÂÂÂÂÂstruct clk *tx_esc_clk;
> +ÂÂÂÂÂÂÂ/* Platform dependent clocks */
> +ÂÂÂÂÂÂÂstruct nwl_dsi_plat_clk_config
> clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
> +
> +ÂÂÂÂÂÂÂ/* dsi lanes */
> +ÂÂÂÂÂÂÂu32 lanes;
> +ÂÂÂÂÂÂÂenum mipi_dsi_pixel_format format;
> +ÂÂÂÂÂÂÂstruct drm_display_mode mode;
> +ÂÂÂÂÂÂÂunsigned long dsi_mode_flags;
> +ÂÂÂÂÂÂÂint error;
> +
> +ÂÂÂÂÂÂÂstruct nwl_dsi_transfer *xfer;
> +
> +ÂÂÂÂÂÂÂconst struct nwl_dsi_platform_data *pdata;
> +};
> +
> +/* Platform specific hooks to enable other SoCs like the i.MX8QM */
> +struct nwl_dsi_platform_data {
> +ÂÂÂÂÂÂÂint (*poweron)(struct nwl_dsi *dsi);
> +ÂÂÂÂÂÂÂint (*poweroff)(struct nwl_dsi *dsi);
> +ÂÂÂÂÂÂÂint (*select_input)(struct nwl_dsi *dsi);
> +ÂÂÂÂÂÂÂint (*deselect_input)(struct nwl_dsi *dsi);
> +ÂÂÂÂÂÂÂstruct nwl_dsi_plat_clk_config
> clk_config[NWL_DSI_MAX_PLATFORM_CLOCKS];
> +};
> +
> +static const struct regmap_config nwl_dsi_regmap_config = {
> +ÂÂÂÂÂÂÂ.reg_bits = 16,
> +ÂÂÂÂÂÂÂ.val_bits = 32,
> +ÂÂÂÂÂÂÂ.reg_stride = 4,
> +ÂÂÂÂÂÂÂ.max_register = NWL_DSI_IRQ_MASK2,
> +ÂÂÂÂÂÂÂ.name = DRV_NAME,
> +};
> +
> +static inline struct nwl_dsi *bridge_to_dsi(struct drm_bridge
> *bridge)
> +{
> +ÂÂÂÂÂÂÂreturn container_of(bridge, struct nwl_dsi, bridge);
> +}
> +
> +static int nwl_dsi_clear_error(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂint ret = dsi->error;
> +
> +ÂÂÂÂÂÂÂdsi->error = 0;
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static void nwl_dsi_write(struct nwl_dsi *dsi, unsigned int reg, u32
> val)
> +{
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂif (dsi->error)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn;
> +
> +ÂÂÂÂÂÂÂret = regmap_write(dsi->regmap, reg, val);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ"Failed to write NWL DSI reg 0x%x:
> %d\n", reg,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->error = ret;
> +ÂÂÂÂÂÂÂ}
> +}
> +
> +static u32 nwl_dsi_read(struct nwl_dsi *dsi, u32 reg)
> +{
> +ÂÂÂÂÂÂÂunsigned int val;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂif (dsi->error)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn 0;
> +
> +ÂÂÂÂÂÂÂret = regmap_read(dsi->regmap, reg, &val);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to read NWL DSI reg
> 0x%x: %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreg, ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->error = ret;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂreturn val;
> +}
> +
> +static int nwl_dsi_get_dpi_pixel_format(enum mipi_dsi_pixel_format
> format)
> +{
> +ÂÂÂÂÂÂÂswitch (format) {
> +ÂÂÂÂÂÂÂcase MIPI_DSI_FMT_RGB565:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn NWL_DSI_PIXEL_FORMAT_16;
> +ÂÂÂÂÂÂÂcase MIPI_DSI_FMT_RGB666:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn NWL_DSI_PIXEL_FORMAT_18L;
> +ÂÂÂÂÂÂÂcase MIPI_DSI_FMT_RGB666_PACKED:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn NWL_DSI_PIXEL_FORMAT_18;
> +ÂÂÂÂÂÂÂcase MIPI_DSI_FMT_RGB888:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn NWL_DSI_PIXEL_FORMAT_24;
> +ÂÂÂÂÂÂÂdefault:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
> +ÂÂÂÂÂÂÂ}
> +}
> +
> +/*
> + * ps2bc - Picoseconds to byte clock cycles
> + */
> +static u32 ps2bc(struct nwl_dsi *dsi, unsigned long long ps)
> +{
> +ÂÂÂÂÂÂÂu32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> +ÂÂÂÂÂÂÂreturn DIV64_U64_ROUND_UP(ps * dsi->mode.clock * bpp,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->lanes * 8 * NSEC_PER_SEC);
> +}
> +
> +/*
> + * ui2bc - UI time periods to byte clock cycles
> + */
> +static u32 ui2bc(struct nwl_dsi *dsi, unsigned long long ui)
> +{
> +ÂÂÂÂÂÂÂu32 bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> +ÂÂÂÂÂÂÂreturn DIV64_U64_ROUND_UP(ui * dsi->lanes,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->mode.clock * 1000 * bpp);
> +}
> +
> +/*
> + * us2bc - micro seconds to lp clock cycles
> + */
> +static u32 us2lp(u32 lp_clk_rate, unsigned long us)
> +{
> +ÂÂÂÂÂÂÂreturn DIV_ROUND_UP(us * lp_clk_rate, USEC_PER_SEC);
> +}
> +
> +static int nwl_dsi_config_host(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂu32 cycles;
> +ÂÂÂÂÂÂÂstruct phy_configure_opts_mipi_dphy *cfg = &dsi-
> >phy_cfg.mipi_dphy;
> +
> +ÂÂÂÂÂÂÂif (dsi->lanes < 1 || dsi->lanes > 4)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
> +
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "DSI Lanes %d\n", dsi->lanes);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_NUM_LANES, dsi->lanes - 1);
> +
> +ÂÂÂÂÂÂÂif (dsi->dsi_mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK,
> 0x01);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP,
> 0x01);
> +ÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_NONCONTINUOUS_CLK,
> 0x00);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_AUTOINSERT_EOTP,
> 0x00);
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂ/* values in byte clock cycles */
> +ÂÂÂÂÂÂÂcycles = ui2bc(dsi, cfg->clk_pre);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_pre: 0x%x\n", cycles);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_T_PRE, cycles);
> +ÂÂÂÂÂÂÂcycles = ps2bc(dsi, cfg->lpx + cfg->clk_prepare + cfg-
> >clk_zero);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap (pre): 0x%x\n",
> cycles);
> +ÂÂÂÂÂÂÂcycles += ui2bc(dsi, cfg->clk_pre);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_t_post: 0x%x\n", cycles);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_T_POST, cycles);
> +ÂÂÂÂÂÂÂcycles = ps2bc(dsi, cfg->hs_exit);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_tx_gap: 0x%x\n", cycles);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_TX_GAP, cycles);
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTP, 0x01);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_HTX_TO_COUNT, 0x00);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_LRX_H_TO_COUNT, 0x00);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_BTA_H_TO_COUNT, 0x00);
> +ÂÂÂÂÂÂÂ/* In LP clock cycles */
> +ÂÂÂÂÂÂÂcycles = us2lp(cfg->lp_clk_rate, cfg->wakeup);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "cfg_twakeup: 0x%x\n",
> cycles);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_CFG_TWAKEUP, cycles);
> +
> +ÂÂÂÂÂÂÂreturn nwl_dsi_clear_error(dsi);
> +}
> +
> +static int nwl_dsi_config_dpi(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂu32 mode;
> +ÂÂÂÂÂÂÂint color_format;
> +ÂÂÂÂÂÂÂbool burst_mode;
> +ÂÂÂÂÂÂÂint hfront_porch, hback_porch, vfront_porch, vback_porch;
> +ÂÂÂÂÂÂÂint hsync_len, vsync_len;
> +
> +ÂÂÂÂÂÂÂhfront_porch = dsi->mode.hsync_start - dsi->mode.hdisplay;
> +ÂÂÂÂÂÂÂhsync_len = dsi->mode.hsync_end - dsi->mode.hsync_start;
> +ÂÂÂÂÂÂÂhback_porch = dsi->mode.htotal - dsi->mode.hsync_end;
> +
> +ÂÂÂÂÂÂÂvfront_porch = dsi->mode.vsync_start - dsi->mode.vdisplay;
> +ÂÂÂÂÂÂÂvsync_len = dsi->mode.vsync_end - dsi->mode.vsync_start;
> +ÂÂÂÂÂÂÂvback_porch = dsi->mode.vtotal - dsi->mode.vsync_end;
> +
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "hfront_porch = %d\n",
> hfront_porch);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "hback_porch = %d\n",
> hback_porch);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "hsync_len = %d\n",
> hsync_len);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "hdisplay = %d\n", dsi-
> >mode.hdisplay);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "vfront_porch = %d\n",
> vfront_porch);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "vback_porch = %d\n",
> vback_porch);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "vsync_len = %d\n",
> vsync_len);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "vactive = %d\n", dsi-
> >mode.vdisplay);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "clock = %d kHz\n", dsi-
> >mode.clock);
> +
> +ÂÂÂÂÂÂÂcolor_format = nwl_dsi_get_dpi_pixel_format(dsi->format);
> +ÂÂÂÂÂÂÂif (color_format < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Invalid color format
> 0x%x\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->format);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn color_format;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "pixel fmt = %d\n", dsi-
> >format);
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_INTERFACE_COLOR_CODING,
> NWL_DSI_DPI_24_BIT);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_PIXEL_FORMAT, color_format);
> +ÂÂÂÂÂÂÂ/*
> +ÂÂÂÂÂÂÂÂ* Adjusting input polarity based on the video mode results
> in
> +ÂÂÂÂÂÂÂÂ* a black screen so always pick active low:
> +ÂÂÂÂÂÂÂÂ*/
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VSYNC_POLARITY,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_VSYNC_POLARITY_ACTIVE_LOW);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_HSYNC_POLARITY,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_HSYNC_POLARITY_ACTIVE_LOW);
> +
> +ÂÂÂÂÂÂÂburst_mode = (dsi->dsi_mode_flags &
> MIPI_DSI_MODE_VIDEO_BURST) &&
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ!(dsi->dsi_mode_flags &
> MIPI_DSI_MODE_VIDEO_SYNC_PULSE);
> +
> +ÂÂÂÂÂÂÂif (burst_mode) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE,
> NWL_DSI_VM_BURST_MODE);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
> 256);
> +ÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂmode = ((dsi->dsi_mode_flags &
> MIPI_DSI_MODE_VIDEO_SYNC_PULSE) ?
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSE
> S :
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_E
> VENTS);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VIDEO_MODE, mode);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_PIXEL_FIFO_SEND_LEVEL,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->mode.hdisplay);
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_HFP, hfront_porch);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_HBP, hback_porch);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_HSA, hsync_len);
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_ENABLE_MULT_PKTS, 0x0);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_BLLP_MODE, 0x1);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_USE_NULL_PKT_BLLP, 0x0);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VC, 0x0);
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_PIXEL_PAYLOAD_SIZE, dsi-
> >mode.hdisplay);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VACTIVE, dsi->mode.vdisplay - 1);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VBP, vback_porch);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_VFP, vfront_porch);
> +
> +ÂÂÂÂÂÂÂreturn nwl_dsi_clear_error(dsi);
> +}
> +
> +static int nwl_dsi_init_interrupts(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂu32 irq_enable;
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, 0xffffffff);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_IRQ_MASK2, 0x7);
> +
> +ÂÂÂÂÂÂÂirq_enable = ~(u32)(NWL_DSI_TX_PKT_DONE_MASK |
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_RX_PKT_HDR_RCVD_MASK |
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_TX_FIFO_OVFLW_MASK |
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_HS_TX_TIMEOUT_MASK);
> +
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_IRQ_MASK, irq_enable);
> +
> +ÂÂÂÂÂÂÂreturn nwl_dsi_clear_error(dsi);
> +}
> +
> +static int nwl_dsi_host_attach(struct mipi_dsi_host *dsi_host,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂstruct mipi_dsi_device *device)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi,
> dsi_host);
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +ÂÂÂÂÂÂÂstruct drm_bridge *bridge;
> +ÂÂÂÂÂÂÂstruct drm_panel *panel;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂDRM_DEV_INFO(dev, "lanes=%u, format=0x%x flags=0x%lx\n",
> device->lanes,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdevice->format, device->mode_flags);
> +
> +ÂÂÂÂÂÂÂif (device->lanes < 1 || device->lanes > 4)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
> +
> +ÂÂÂÂÂÂÂdsi->lanes = device->lanes;
> +ÂÂÂÂÂÂÂdsi->format = device->format;
> +ÂÂÂÂÂÂÂdsi->dsi_mode_flags = device->mode_flags;
> +
> +ÂÂÂÂÂÂÂret = drm_of_find_panel_or_bridge(dsi->dev->of_node, 1, 0,
> &panel,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ&bridge);
> +ÂÂÂÂÂÂÂif (ret)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +
> +ÂÂÂÂÂÂÂif (panel) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂbridge = drm_panel_bridge_add(panel);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (IS_ERR(bridge))
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn PTR_ERR(bridge);
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂdsi->panel_bridge = bridge;
> +ÂÂÂÂÂÂÂdrm_bridge_add(&dsi->bridge);
This works only with a panel, but not with a bridge. For example,
adv7511 bridge, will cal dsi_host_attach in it's drm_bridge_attach
callback. Since you add our bridge only here, the bridge_attach from
adv7511 will never be called so this callback will never be called.
So, I'd suggest to call drm_bridge_add in probe, and
moveÂdrm_of_find_panel_or_bridge in our bridge_attach. Basically, you
need to separate the bridge_attach operations from the dsi_host_attach
operations.
> +
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static int nwl_dsi_host_detach(struct mipi_dsi_host *dsi_host,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂstruct mipi_dsi_device *device)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi,
> dsi_host);
> +
> +ÂÂÂÂÂÂÂdrm_of_panel_bridge_remove(dsi->dev->of_node, 1, 0);
> +ÂÂÂÂÂÂÂdrm_bridge_remove(&dsi->bridge);
Also, these removes should be done in our bridge_remove. These are not
related to the dsi_device.
> +
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static bool nwl_dsi_read_packet(struct nwl_dsi *dsi, u32 status)
> +{
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +ÂÂÂÂÂÂÂstruct nwl_dsi_transfer *xfer = dsi->xfer;
> +ÂÂÂÂÂÂÂint err;
> +ÂÂÂÂÂÂÂu8 *payload = xfer->msg->rx_buf;
> +ÂÂÂÂÂÂÂu32 val;
> +ÂÂÂÂÂÂÂu16 word_count;
> +ÂÂÂÂÂÂÂu8 channel;
> +ÂÂÂÂÂÂÂu8 data_type;
> +
> +ÂÂÂÂÂÂÂxfer->status = 0;
> +
> +ÂÂÂÂÂÂÂif (xfer->rx_word_count == 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (!(status & NWL_DSI_RX_PKT_HDR_RCVD))
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn false;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Get the RX header and parse it */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval = nwl_dsi_read(dsi, NWL_DSI_RX_PKT_HEADER);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂerr = nwl_dsi_clear_error(dsi);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (err)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = err;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂword_count = NWL_DSI_WC(val);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂchannel = NWL_DSI_RX_VC(val);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdata_type = NWL_DSI_RX_DT(val);
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (channel != xfer->msg->channel) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ"[%02X] Channel mismatch (%u !=
> %u)\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->cmd, channel, xfer->msg-
> >channel);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = -EINVAL;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn true;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂswitch (data_type) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (xfer->msg->rx_len > 1) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* read second byte */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[1] = word_count >> 8;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ++xfer->rx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (xfer->msg->rx_len > 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* read first byte */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[0] = word_count & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ++xfer->rx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = xfer->rx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn true;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂword_count &= 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "[%02X] DSI error report:
> 0x%02x\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->cmd, word_count);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = -EPROTO;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn true;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (word_count > xfer->msg->rx_len) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdev,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ"[%02X] Receive buffer too small: %zu
> (< %u)\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->cmd, xfer->msg->rx_len,
> word_count);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = -EINVAL;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn true;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->rx_word_count = word_count;
> +ÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Set word_count from previous header read */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂword_count = xfer->rx_word_count;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂ/* If RX payload is not yet received, wait for it */
> +ÂÂÂÂÂÂÂif (!(status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD))
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn false;
> +
> +ÂÂÂÂÂÂÂ/* Read the RX payload */
> +ÂÂÂÂÂÂÂwhile (word_count >= 4) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[0] = (val >> 0) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[1] = (val >> 8) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[2] = (val >> 16) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[3] = (val >> 24) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload += 4;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->rx_len += 4;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂword_count -= 4;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂif (word_count > 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval = nwl_dsi_read(dsi, NWL_DSI_RX_PAYLOAD);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂswitch (word_count) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase 3:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[2] = (val >> 16) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ++xfer->rx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase 2:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[1] = (val >> 8) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ++xfer->rx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcase 1:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload[0] = (val >> 0) & 0xff;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ++xfer->rx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂbreak;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂxfer->status = xfer->rx_len;
> +ÂÂÂÂÂÂÂerr = nwl_dsi_clear_error(dsi);
> +ÂÂÂÂÂÂÂif (err)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = err;
> +
> +ÂÂÂÂÂÂÂreturn true;
> +}
> +
> +static void nwl_dsi_finish_transmission(struct nwl_dsi *dsi, u32
> status)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi_transfer *xfer = dsi->xfer;
> +ÂÂÂÂÂÂÂbool end_packet = false;
> +
> +ÂÂÂÂÂÂÂif (!xfer)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn;
> +
> +ÂÂÂÂÂÂÂif (xfer->direction == DSI_PACKET_SEND &&
> +ÂÂÂÂÂÂÂÂÂÂÂstatus & NWL_DSI_TX_PKT_DONE) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->status = xfer->tx_len;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂend_packet = true;
> +ÂÂÂÂÂÂÂ} else if (status & NWL_DSI_DPHY_DIRECTION &&
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ((status & (NWL_DSI_RX_PKT_HDR_RCVD |
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)))) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂend_packet = nwl_dsi_read_packet(dsi, status);
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂif (end_packet)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcomplete(&xfer->completed);
> +}
> +
> +static void nwl_dsi_begin_transmission(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi_transfer *xfer = dsi->xfer;
> +ÂÂÂÂÂÂÂstruct mipi_dsi_packet *pkt = &xfer->packet;
> +ÂÂÂÂÂÂÂconst u8 *payload;
> +ÂÂÂÂÂÂÂsize_t length;
> +ÂÂÂÂÂÂÂu16 word_count;
> +ÂÂÂÂÂÂÂu8 hs_mode;
> +ÂÂÂÂÂÂÂu32 val;
> +ÂÂÂÂÂÂÂu32 hs_workaround = 0;
> +
> +ÂÂÂÂÂÂÂ/* Send the payload, if any */
> +ÂÂÂÂÂÂÂlength = pkt->payload_length;
> +ÂÂÂÂÂÂÂpayload = pkt->payload;
> +
> +ÂÂÂÂÂÂÂwhile (length >= 4) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval = *(u32 *)payload;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂhs_workaround |= !(val & 0xFFFF00);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂpayload += 4;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂlength -= 4;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂ/* Send the rest of the payload */
> +ÂÂÂÂÂÂÂval = 0;
> +ÂÂÂÂÂÂÂswitch (length) {
> +ÂÂÂÂÂÂÂcase 3:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval |= payload[2] << 16;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂcase 2:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval |= payload[1] << 8;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂhs_workaround |= !(val & 0xFFFF00);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ/* Fall through */
> +ÂÂÂÂÂÂÂcase 1:
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂval |= payload[0];
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_TX_PAYLOAD, val);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂbreak;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂxfer->tx_len = pkt->payload_length;
> +
> +ÂÂÂÂÂÂÂ/*
> +ÂÂÂÂÂÂÂÂ* Send the header
> +ÂÂÂÂÂÂÂÂ* header[0] = Virtual Channel + Data Type
> +ÂÂÂÂÂÂÂÂ* header[1] = Word Count LSB (LP) or first param (SP)
> +ÂÂÂÂÂÂÂÂ* header[2] = Word Count MSB (LP) or second param (SP)
> +ÂÂÂÂÂÂÂÂ*/
> +ÂÂÂÂÂÂÂword_count = pkt->header[1] | (pkt->header[2] << 8);
> +ÂÂÂÂÂÂÂif (hs_workaround && (dsi->quirks & E11418_HS_MODE_QUIRK)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ"Using hs mode workaround for
> cmd 0x%x\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer->cmd);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂhs_mode = 1;
> +ÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂhs_mode = (xfer->msg->flags & MIPI_DSI_MSG_USE_LPM) ?
> 0 : 1;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂval = NWL_DSI_WC(word_count) | NWL_DSI_TX_VC(xfer->msg-
> >channel) |
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_TX_DT(xfer->msg->type) |
> NWL_DSI_HS_SEL(hs_mode) |
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_BTA_TX(xfer->need_bta);
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_PKT_CONTROL, val);
> +
> +ÂÂÂÂÂÂÂ/* Send packet command */
> +ÂÂÂÂÂÂÂnwl_dsi_write(dsi, NWL_DSI_SEND_PACKET, 0x1);
> +}
> +
> +static ssize_t nwl_dsi_host_transfer(struct mipi_dsi_host *dsi_host,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂconst struct mipi_dsi_msg *msg)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = container_of(dsi_host, struct nwl_dsi,
> dsi_host);
> +ÂÂÂÂÂÂÂstruct nwl_dsi_transfer xfer;
> +ÂÂÂÂÂÂÂssize_t ret = 0;
> +
> +ÂÂÂÂÂÂÂ/* Create packet to be sent */
> +ÂÂÂÂÂÂÂdsi->xfer = &xfer;
> +ÂÂÂÂÂÂÂret = mipi_dsi_create_packet(&xfer.packet, msg);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->xfer = NULL;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂif ((msg->type & MIPI_DSI_GENERIC_READ_REQUEST_0_PARAM ||
> +ÂÂÂÂÂÂÂÂÂÂÂÂmsg->type & MIPI_DSI_GENERIC_READ_REQUEST_1_PARAM ||
> +ÂÂÂÂÂÂÂÂÂÂÂÂmsg->type & MIPI_DSI_GENERIC_READ_REQUEST_2_PARAM ||
> +ÂÂÂÂÂÂÂÂÂÂÂÂmsg->type & MIPI_DSI_DCS_READ) &&
> +ÂÂÂÂÂÂÂÂÂÂÂmsg->rx_len > 0 && msg->rx_buf != NULL)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer.direction = DSI_PACKET_RECEIVE;
> +ÂÂÂÂÂÂÂelse
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer.direction = DSI_PACKET_SEND;
> +
> +ÂÂÂÂÂÂÂxfer.need_bta = (xfer.direction == DSI_PACKET_RECEIVE);
> +ÂÂÂÂÂÂÂxfer.need_bta |= (msg->flags & MIPI_DSI_MSG_REQ_ACK) ? 1 : 0;
> +ÂÂÂÂÂÂÂxfer.msg = msg;
> +ÂÂÂÂÂÂÂxfer.status = -ETIMEDOUT;
> +ÂÂÂÂÂÂÂxfer.rx_word_count = 0;
> +ÂÂÂÂÂÂÂxfer.rx_len = 0;
> +ÂÂÂÂÂÂÂxfer.cmd = 0x00;
> +ÂÂÂÂÂÂÂif (msg->tx_len > 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer.cmd = ((u8 *)(msg->tx_buf))[0];
> +ÂÂÂÂÂÂÂinit_completion(&xfer.completed);
> +
> +ÂÂÂÂÂÂÂret = clk_prepare_enable(dsi->rx_esc_clk);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to enable rx_esc clk:
> %zd\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled rx_esc clk @%lu
> Hz\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk_get_rate(dsi->rx_esc_clk));
> +
> +ÂÂÂÂÂÂÂ/* Initiate the DSI packet transmision */
> +ÂÂÂÂÂÂÂnwl_dsi_begin_transmission(dsi);
> +
> +ÂÂÂÂÂÂÂif (!wait_for_completion_timeout(&xfer.completed,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_MIPI_FIFO_TIMEOUT))
> {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi_host->dev, "[%02X] DSI transfer
> timed out\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂxfer.cmd);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = -ETIMEDOUT;
> +ÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = xfer.status;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂclk_disable_unprepare(dsi->rx_esc_clk);
> +
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static const struct mipi_dsi_host_ops nwl_dsi_host_ops = {
> +ÂÂÂÂÂÂÂ.attach = nwl_dsi_host_attach,
> +ÂÂÂÂÂÂÂ.detach = nwl_dsi_host_detach,
> +ÂÂÂÂÂÂÂ.transfer = nwl_dsi_host_transfer,
> +};
> +
> +static irqreturn_t nwl_dsi_irq_handler(int irq, void *data)
> +{
> +ÂÂÂÂÂÂÂu32 irq_status;
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = data;
> +
> +ÂÂÂÂÂÂÂirq_status = nwl_dsi_read(dsi, NWL_DSI_IRQ_STATUS);
> +
> +ÂÂÂÂÂÂÂif (irq_status & NWL_DSI_TX_FIFO_OVFLW)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR_RATELIMITED(dsi->dev, "tx fifo
> overflow\n");
> +
> +ÂÂÂÂÂÂÂif (irq_status & NWL_DSI_HS_TX_TIMEOUT)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR_RATELIMITED(dsi->dev, "HS tx
> timeout\n");
> +
> +ÂÂÂÂÂÂÂif (irq_status & NWL_DSI_TX_PKT_DONE ||
> +ÂÂÂÂÂÂÂÂÂÂÂirq_status & NWL_DSI_RX_PKT_HDR_RCVD ||
> +ÂÂÂÂÂÂÂÂÂÂÂirq_status & NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂnwl_dsi_finish_transmission(dsi, irq_status);
> +
> +ÂÂÂÂÂÂÂreturn IRQ_HANDLED;
> +}
> +
> +static int nwl_dsi_enable(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +ÂÂÂÂÂÂÂunion phy_configure_opts *phy_cfg = &dsi->phy_cfg;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂif (!dsi->lanes) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Need DSI lanes: %d\n", dsi-
> >lanes);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂret = phy_init(dsi->phy);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to init DSI phy: %d\n",
> ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂret = phy_configure(dsi->phy, phy_cfg);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to configure DSI phy:
> %d\n", ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂgoto uninit_phy;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂret = clk_prepare_enable(dsi->tx_esc_clk);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to enable tx_esc clk:
> %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂgoto uninit_phy;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "Enabled tx_esc clk @%lu
> Hz\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk_get_rate(dsi->tx_esc_clk));
> +
> +ÂÂÂÂÂÂÂret = nwl_dsi_config_host(dsi);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to set up DSI: %d", ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂgoto disable_clock;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂret = nwl_dsi_config_dpi(dsi);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to set up DPI: %d", ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂgoto disable_clock;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂret = phy_power_on(dsi->phy);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to power on DPHY (%d)\n",
> ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂgoto disable_clock;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂret = nwl_dsi_init_interrupts(dsi);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂgoto power_off_phy;
> +
> +ÂÂÂÂÂÂÂreturn ret;
> +
> +power_off_phy:
> +ÂÂÂÂÂÂÂphy_power_off(dsi->phy);
> +disable_clock:
> +ÂÂÂÂÂÂÂclk_disable_unprepare(dsi->tx_esc_clk);
> +uninit_phy:
> +ÂÂÂÂÂÂÂphy_exit(dsi->phy);
> +
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static int nwl_dsi_disable(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dev, "Disabling clocks and phy\n");
> +
> +ÂÂÂÂÂÂÂphy_power_off(dsi->phy);
> +ÂÂÂÂÂÂÂphy_exit(dsi->phy);
> +
> +ÂÂÂÂÂÂÂ/* Disabling the clock before the phy breaks enabling dsi
> again */
> +ÂÂÂÂÂÂÂclk_disable_unprepare(dsi->tx_esc_clk);
> +
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static int nwl_dsi_set_platform_clocks(struct nwl_dsi *dsi, bool
> enable)
> +{
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +ÂÂÂÂÂÂÂconst char *id;
> +ÂÂÂÂÂÂÂstruct clk *clk;
> +ÂÂÂÂÂÂÂsize_t i;
> +ÂÂÂÂÂÂÂunsigned long rate;
> +ÂÂÂÂÂÂÂint ret, result = 0;
> +
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dev, "%s platform clocks\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂenable ? "enabling" : "disabling");
> +ÂÂÂÂÂÂÂfor (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (!dsi->clk_config[i].present)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcontinue;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂid = dsi->clk_config[i].id;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk = dsi->clk_config[i].clk;
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (enable) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = clk_prepare_enable(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ"Failed to enable %s
> clk: %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂid, ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂresult = result ?: ret;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂrate = clk_get_rate(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dev, "Enabled %s clk
> @%lu Hz\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂid, rate);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk_disable_unprepare(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dev, "Disabled %s
> clk\n", id);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂreturn result;
> +}
> +
> +static int nwl_dsi_plat_enable(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂif (dsi->pdata->select_input) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = dsi->pdata->select_input(dsi);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
Since the select_input always reads the dtb (and at runtime this node
never changes anyway) I think it's best to use the select_input at
probe since it is only needed once and also drop the deselect_input.
> +
> +ÂÂÂÂÂÂÂret = nwl_dsi_set_platform_clocks(dsi, true);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +
> +ÂÂÂÂÂÂÂret = dsi->pdata->poweron(dsi);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to power on DSI: %d\n",
> ret);
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static void nwl_dsi_plat_disable(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂdsi->pdata->poweroff(dsi);
> +ÂÂÂÂÂÂÂnwl_dsi_set_platform_clocks(dsi, false);
> +ÂÂÂÂÂÂÂif (dsi->pdata->deselect_input)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->pdata->deselect_input(dsi);
> +}
> +
> +static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> +ÂÂÂÂÂÂÂnwl_dsi_disable(dsi);
> +ÂÂÂÂÂÂÂnwl_dsi_plat_disable(dsi);
> +ÂÂÂÂÂÂÂpm_runtime_put(dsi->dev);
> +}
> +
> +static int nwl_dsi_get_dphy_params(struct nwl_dsi *dsi,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂconst struct drm_display_mode
> *mode,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂunion phy_configure_opts
> *phy_opts)
> +{
> +ÂÂÂÂÂÂÂunsigned long rate;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂif (dsi->lanes < 1 || dsi->lanes > 4)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
> +
> +ÂÂÂÂÂÂÂ/*
> +ÂÂÂÂÂÂÂÂ* So far the DPHY spec minimal timings work for both mixel
> +ÂÂÂÂÂÂÂÂ* dphy and nwl dsi host
> +ÂÂÂÂÂÂÂÂ*/
> +ÂÂÂÂÂÂÂret = phy_mipi_dphy_get_default_config(
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂmode->crtc_clock * 1000,
Why are you using crtc_clock here? I this this should be used only by
the CRTC component. I'd suggest to use mode->clock here, since it is
about the output clock of the DSI, not the input clock on the DPI
interface, that is coming from CRTC.
Â
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂmipi_dsi_pixel_format_to_bpp(dsi->format), dsi-
> >lanes,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ&phy_opts->mipi_dphy);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +
> +ÂÂÂÂÂÂÂrate = clk_get_rate(dsi->tx_esc_clk);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "LP clk is @%lu Hz\n", rate);
> +ÂÂÂÂÂÂÂphy_opts->mipi_dphy.lp_clk_rate = rate;
> +
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂconst struct drm_display_mode
> *mode,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂstruct drm_display_mode
> *adjusted_mode)
> +{
> +ÂÂÂÂÂÂÂ/* At least LCDIF + NWL needs active high sync */
> +ÂÂÂÂÂÂÂadjusted_mode->flags |= (DRM_MODE_FLAG_PHSYNC |
> DRM_MODE_FLAG_PVSYNC);
> +ÂÂÂÂÂÂÂadjusted_mode->flags &= ~(DRM_MODE_FLAG_NHSYNC |
> DRM_MODE_FLAG_NVSYNC);
> +
> +ÂÂÂÂÂÂÂreturn true;
> +}
> +
> +static enum drm_mode_status
> +nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂconst struct drm_display_mode *mode)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +ÂÂÂÂÂÂÂint bpp = mipi_dsi_pixel_format_to_bpp(dsi->format);
> +
> +ÂÂÂÂÂÂÂif (mode->clock * bpp > 15000000 * dsi->lanes)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn MODE_CLOCK_HIGH;
> +
> +ÂÂÂÂÂÂÂif (mode->clock * bpp < 80000 * dsi->lanes)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn MODE_CLOCK_LOW;
> +
> +ÂÂÂÂÂÂÂreturn MODE_OK;
> +}
> +
> +static void
> +nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂconst struct drm_display_mode *mode,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂconst struct drm_display_mode *adjusted_mode)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +ÂÂÂÂÂÂÂstruct device *dev = dsi->dev;
> +ÂÂÂÂÂÂÂunion phy_configure_opts new_cfg;
> +ÂÂÂÂÂÂÂunsigned long phy_ref_rate;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂret = nwl_dsi_get_dphy_params(dsi, adjusted_mode, &new_cfg);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn;
> +
> +ÂÂÂÂÂÂÂ/*
> +ÂÂÂÂÂÂÂÂ* If hs clock is unchanged, we're all good - all parameters
> are
> +ÂÂÂÂÂÂÂÂ* derived from it atm.
> +ÂÂÂÂÂÂÂÂ*/
> +ÂÂÂÂÂÂÂif (new_cfg.mipi_dphy.hs_clk_rate == dsi-
> >phy_cfg.mipi_dphy.hs_clk_rate)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn;
> +
> +ÂÂÂÂÂÂÂphy_ref_rate = clk_get_rate(dsi->phy_ref_clk);
> +ÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dev, "PHY at ref rate: %lu\n",
> phy_ref_rate);
> +ÂÂÂÂÂÂÂ/* Save the new desired phy config */
> +ÂÂÂÂÂÂÂmemcpy(&dsi->phy_cfg, &new_cfg, sizeof(new_cfg));
> +
> +ÂÂÂÂÂÂÂmemcpy(&dsi->mode, adjusted_mode, sizeof(dsi->mode));
> +ÂÂÂÂÂÂÂdrm_mode_debug_printmodeline(adjusted_mode);
> +}
> +
> +static void nwl_dsi_bridge_pre_enable(struct drm_bridge *bridge)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> +ÂÂÂÂÂÂÂpm_runtime_get_sync(dsi->dev);
> +ÂÂÂÂÂÂÂnwl_dsi_plat_enable(dsi);
> +ÂÂÂÂÂÂÂnwl_dsi_enable(dsi);
> +}
> +
> +static int nwl_dsi_bridge_attach(struct drm_bridge *bridge)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = bridge_to_dsi(bridge);
> +
> +ÂÂÂÂÂÂÂreturn drm_bridge_attach(bridge->encoder, dsi->panel_bridge,
> bridge);
> +}
> +
> +static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
> +ÂÂÂÂÂÂÂ.pre_enable = nwl_dsi_bridge_pre_enable,
> +ÂÂÂÂÂÂÂ.disableÂÂÂÂ= nwl_dsi_bridge_disable,
> +ÂÂÂÂÂÂÂ.mode_fixup = nwl_dsi_bridge_mode_fixup,
> +ÂÂÂÂÂÂÂ.mode_setÂÂÂ= nwl_dsi_bridge_mode_set,
> +ÂÂÂÂÂÂÂ.mode_valid = nwl_dsi_bridge_mode_valid,
> +ÂÂÂÂÂÂÂ.attachÂÂÂÂÂ= nwl_dsi_bridge_attach,
> +};
> +
> +static int nwl_dsi_parse_dt(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂstruct platform_device *pdev = to_platform_device(dsi->dev);
> +ÂÂÂÂÂÂÂstruct clk *clk;
> +ÂÂÂÂÂÂÂconst char *clk_id;
> +ÂÂÂÂÂÂÂvoid __iomem *base;
> +ÂÂÂÂÂÂÂint i, ret;
> +
> +ÂÂÂÂÂÂÂdsi->phy = devm_phy_get(dsi->dev, "dphy");
> +ÂÂÂÂÂÂÂif (IS_ERR(dsi->phy)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(dsi->phy);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (ret != -EPROBE_DEFER)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Could not get PHY:
> %d\n", ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂ/* Platform dependent clocks */
> +ÂÂÂÂÂÂÂmemcpy(dsi->clk_config, dsi->pdata->clk_config,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂsizeof(dsi->pdata->clk_config));
> +
> +ÂÂÂÂÂÂÂfor (i = 0; i < ARRAY_SIZE(dsi->pdata->clk_config); i++) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (!dsi->clk_config[i].present)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂcontinue;
> +
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk_id = dsi->clk_config[i].id;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk = devm_clk_get(dsi->dev, clk_id);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (IS_ERR(clk)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get %s
> clock: %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk_id, ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_DEBUG_DRIVER(dsi->dev, "Setup clk %s (rate:
> %lu)\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂclk_id, clk_get_rate(clk));
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->clk_config[i].clk = clk;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂ/* DSI clocks */
> +ÂÂÂÂÂÂÂclk = devm_clk_get(dsi->dev, "phy_ref");
> +ÂÂÂÂÂÂÂif (IS_ERR(clk)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get phy_ref clock:
> %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂdsi->phy_ref_clk = clk;
> +
> +ÂÂÂÂÂÂÂclk = devm_clk_get(dsi->dev, "rx_esc");
> +ÂÂÂÂÂÂÂif (IS_ERR(clk)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get rx_esc clock:
> %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂdsi->rx_esc_clk = clk;
> +
> +ÂÂÂÂÂÂÂclk = devm_clk_get(dsi->dev, "tx_esc");
> +ÂÂÂÂÂÂÂif (IS_ERR(clk)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(clk);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get tx_esc clock:
> %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂdsi->tx_esc_clk = clk;
> +
> +ÂÂÂÂÂÂÂdsi->mux = devm_mux_control_get(dsi->dev, NULL);
> +ÂÂÂÂÂÂÂif (IS_ERR(dsi->mux)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(dsi->mux);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (ret != -EPROBE_DEFER)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get mux:
> %d\n", ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂbase = devm_platform_ioremap_resource(pdev, 0);
> +ÂÂÂÂÂÂÂif (IS_ERR(base))
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn PTR_ERR(base);
> +
> +ÂÂÂÂÂÂÂdsi->regmap =
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdevm_regmap_init_mmio(dsi->dev, base,
> &nwl_dsi_regmap_config);
> +ÂÂÂÂÂÂÂif (IS_ERR(dsi->regmap)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(dsi->regmap);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to create NWL DSI
> regmap: %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂdsi->irq = platform_get_irq(pdev, 0);
> +ÂÂÂÂÂÂÂif (dsi->irq < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get device IRQ:
> %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->irq);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn dsi->irq;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂdsi->rstc = devm_reset_control_array_get(dsi->dev, false,
> true);
> +ÂÂÂÂÂÂÂif (IS_ERR(dsi->rstc)) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = PTR_ERR(dsi->rstc);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (ret != -EPROBE_DEFER)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to get
> resets: %d\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
Since you dropped the reset quirk (which is a good idea, but it needs a
backup), I would like to tell you about our findings. So, as you know,
DSi can receive input from DCSS or LCDIF (this is done through a mux).
Now, the reset of the DSI block is not handled directly through the
System Reset Controller (as initially I though). The signals coming
from SRC are going into a reset_sync (Reset Synchronyzer block),
because the DSI block needs the reset signals to beÂsynchronous de-
asserted.
Recently, we discovered the bug that was not allowing us to correctly
reset the DSI and also why that quirk was initially used.
Normally, the output pixel-clock that was coming from that mux should
be used as a clock for the reset_sync, but due to a design issue in the
core, the LCDIF pixel-clock was used to drive this reset_sync.
So, since HW bug here which can be managed in SW, what we did is to add
the LCDIF pixel-clock to this driver (in dts it can be added as an
external clock needed for reset_sync). I know it is a nasty external
depencency, but this is the part in SW that fixes the issue in HW.
Now: the LCDIF pixel-clock needs bo be on for the whole duration of the
DSI operation, This means that LCDIF should be enabled before de-
asserting the reset signals, then disabled after we are done with DSI
and assert the reset signals back.
Next, I want to detail you the sequence of the operations that needs to
be done, since while debugging this issue we received the correct
sequence from Northwest Logic representatives. This sequence is an
absolute requirement for a proper initialization and I will detail it
below:
1. De-assert 'pclk' reset (this is needed to have acces to the DSI
registers)
2. Configure DSI Host
3. Configure DHPY
4. Enable DPHY
5. De-assert 'esc' and 'byte' resets (these are needed for Host TX
operations)
6. Send DSI commands (if the DSI peripheral needs configuration)
7. De-assert 'dpi' reset (de-asserting this reset, will enable the DPI
to start fetching data from it's input and start streaming DSI data)
You will see that I left the 'dpi' reset at the end, so that the 'DSI
peripheral initialization' step will be clean. DSI lanes needs to be
clean during this phase and not be "poluted" by any pixel data that DPI
thinks it needs to stream. On my side, I tried to separate the above
steps by implementing steps 1-5 in bridge_pre_enable, hoping that the
step 6 will be automatically run in panel enable phase, and finally
have step 7 implemented in bridge_enable. Unfortunatelly, the order of
calls for enable is exactly the oposite way as for pre_enable, which
makes impossible to have the step 7 executed AFTER step 6.
In bridge_pre_enable, the pre_enable for the bridge chain is executed
first, then our pre_enable is executed.
In bridge_enable, our enable is executed first, then the enable for
brigde chain is executed. This means that step 7 will end up executed
before step 6, causing the 'DSI lane polution' I was talking above.
As of now, I didn't manage to find a way to fix this, but I will keep
investigating. If you have a good ideea for this it woule be great.
So, with all the above details, I think you will need to treat each
reset individually, instead of treating them as an array.
> +
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static int imx8mq_dsi_select_input(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂstruct device_node *remote;
> +ÂÂÂÂÂÂÂu32 use_dcss = 1;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂremote = of_graph_get_remote_node(dsi->dev->of_node, 0,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_ENDPOINT_LCDIF);
> +ÂÂÂÂÂÂÂif (remote) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂuse_dcss = 0;
> +ÂÂÂÂÂÂÂ} else {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂremote = of_graph_get_remote_node(dsi->dev->of_node,
> 0,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂNWL_DSI_ENDPOINT_DC
> SS);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (!remote) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ"No valid input endpoint
> found\n");
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂDRM_DEV_INFO(dsi->dev, "Using %s as input source\n",
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ(use_dcss) ? "DCSS" : "LCDIF");
> +ÂÂÂÂÂÂÂret = mux_control_try_select(dsi->mux, use_dcss);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to select input:
> %d\n", ret);
> +
> +ÂÂÂÂÂÂÂof_node_put(remote);
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static int imx8mq_dsi_deselect_input(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂret = mux_control_deselect(dsi->mux);
> +ÂÂÂÂÂÂÂif (ret < 0)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dsi->dev, "Failed to deselect input:
> %d\n", ret);
> +
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +
> +static int imx8mq_dsi_poweron(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂint ret = 0;
> +
> +ÂÂÂÂÂÂÂ/* otherwise the display stays blank */
> +ÂÂÂÂÂÂÂusleep_range(200, 300);
If you implement the part with LCDIF clock detailed above, you can drop
this sleep. Also, poweron should be replaced by various reset callbacks
(ex: pclk_reset, mipi_reset, dpi_reset).
> +
> +ÂÂÂÂÂÂÂif (dsi->rstc)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = reset_control_deassert(dsi->rstc);
> +
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static int imx8mq_dsi_poweroff(struct nwl_dsi *dsi)
> +{
> +ÂÂÂÂÂÂÂint ret = 0;
> +
> +ÂÂÂÂÂÂÂif (dsi->rstc)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = reset_control_assert(dsi->rstc);
> +ÂÂÂÂÂÂÂreturn ret;
> +}
> +
> +static const struct drm_bridge_timings nwl_dsi_timings = {
> +ÂÂÂÂÂÂÂ.input_bus_flags = DRM_BUS_FLAG_DE_LOW,
> +};
> +
> +static const struct nwl_dsi_platform_data imx8mq_dev = {
> +ÂÂÂÂÂÂÂ.poweron = &imx8mq_dsi_poweron,
> +ÂÂÂÂÂÂÂ.poweroff = &imx8mq_dsi_poweroff,
> +ÂÂÂÂÂÂÂ.select_input = &imx8mq_dsi_select_input,
> +ÂÂÂÂÂÂÂ.deselect_input = &imx8mq_dsi_deselect_input,
> +ÂÂÂÂÂÂÂ.clk_config = {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ{ .id = NWL_DSI_CLK_CORE, .present = true },
> +ÂÂÂÂÂÂÂ},
> +};
> +
> +static const struct of_device_id nwl_dsi_dt_ids[] = {
> +ÂÂÂÂÂÂÂ{ .compatible = "fsl,imx8mq-nwl-dsi", .data = &imx8mq_dev, },
> +ÂÂÂÂÂÂÂ{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, nwl_dsi_dt_ids);
> +
> +static const struct soc_device_attribute nwl_dsi_quirks_match[] = {
> +ÂÂÂÂÂÂÂ{ .soc_id = "i.MX8MQ", .revision = "2.0",
> +ÂÂÂÂÂÂÂÂÂ.data = (void *)E11418_HS_MODE_QUIRK },
> +ÂÂÂÂÂÂÂ{ /* sentinel. */ },
> +};
> +
> +static int nwl_dsi_probe(struct platform_device *pdev)
> +{
> +ÂÂÂÂÂÂÂstruct device *dev = &pdev->dev;
> +ÂÂÂÂÂÂÂconst struct of_device_id *of_id =
> of_match_device(nwl_dsi_dt_ids, dev);
> +ÂÂÂÂÂÂÂconst struct nwl_dsi_platform_data *pdata = of_id->data;
> +ÂÂÂÂÂÂÂconst struct soc_device_attribute *attr;
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi;
> +ÂÂÂÂÂÂÂint ret;
> +
> +ÂÂÂÂÂÂÂdsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
> +ÂÂÂÂÂÂÂif (!dsi)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -ENOMEM;
> +
> +ÂÂÂÂÂÂÂdsi->dev = dev;
> +ÂÂÂÂÂÂÂdsi->pdata = pdata;
> +
> +ÂÂÂÂÂÂÂret = nwl_dsi_parse_dt(dsi);
> +ÂÂÂÂÂÂÂif (ret)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +
> +ÂÂÂÂÂÂÂret = devm_request_irq(dev, dsi->irq, nwl_dsi_irq_handler, 0,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdev_name(dev), dsi);
> +ÂÂÂÂÂÂÂif (ret < 0) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to request IRQ %d: %d\n",
> dsi->irq,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂdsi->dsi_host.ops = &nwl_dsi_host_ops;
> +ÂÂÂÂÂÂÂdsi->dsi_host.dev = dev;
> +ÂÂÂÂÂÂÂret = mipi_dsi_host_register(&dsi->dsi_host);
> +ÂÂÂÂÂÂÂif (ret) {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂDRM_DEV_ERROR(dev, "Failed to register MIPI host:
> %d\n", ret);
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
> +ÂÂÂÂÂÂÂ}
> +
> +ÂÂÂÂÂÂÂattr = soc_device_match(nwl_dsi_quirks_match);
> +ÂÂÂÂÂÂÂif (attr)
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdsi->quirks = (uintptr_t)attr->data;
> +
> +ÂÂÂÂÂÂÂdsi->bridge.driver_private = dsi;
> +ÂÂÂÂÂÂÂdsi->bridge.funcs = &nwl_dsi_bridge_funcs;
> +ÂÂÂÂÂÂÂdsi->bridge.of_node = dev->of_node;
> +ÂÂÂÂÂÂÂdsi->bridge.timings = &nwl_dsi_timings;
> +
> +ÂÂÂÂÂÂÂdev_set_drvdata(dev, dsi);
> +ÂÂÂÂÂÂÂpm_runtime_enable(dev);
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static int nwl_dsi_remove(struct platform_device *pdev)
> +{
> +ÂÂÂÂÂÂÂstruct nwl_dsi *dsi = platform_get_drvdata(pdev);
> +
> +ÂÂÂÂÂÂÂmipi_dsi_host_unregister(&dsi->dsi_host);
> +ÂÂÂÂÂÂÂpm_runtime_disable(&pdev->dev);
> +ÂÂÂÂÂÂÂreturn 0;
> +}
> +
> +static struct platform_driver nwl_dsi_driver = {
> +ÂÂÂÂÂÂÂ.probeÂÂÂÂÂÂÂÂÂÂ= nwl_dsi_probe,
> +ÂÂÂÂÂÂÂ.removeÂÂÂÂÂÂÂÂÂ= nwl_dsi_remove,
> +ÂÂÂÂÂÂÂ.driverÂÂÂÂÂÂÂÂÂ= {
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ.of_match_table = nwl_dsi_dt_ids,
> +ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ.nameÂÂÂ= DRV_NAME,
> +ÂÂÂÂÂÂÂ},
> +};
> +
> +module_platform_driver(nwl_dsi_driver);
> +
> +MODULE_AUTHOR("NXP Semiconductor");
> +MODULE_AUTHOR("Purism SPC");
> +MODULE_DESCRIPTION("Northwest Logic MIPI-DSI driver");
> +MODULE_LICENSE("GPL"); /* GPLv2 or later */
> diff --git a/drivers/gpu/drm/bridge/nwl-dsi.h
> b/drivers/gpu/drm/bridge/nwl-dsi.h
> new file mode 100644
> index 000000000000..a247a8a11c7c
> --- /dev/null
> +++ b/drivers/gpu/drm/bridge/nwl-dsi.h
> @@ -0,0 +1,144 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * NWL MIPI DSI host driver
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2019 Purism SPC
> + */
> +#ifndef __NWL_DSI_H__
> +#define __NWL_DSI_H__
> +
> +/* DSI HOST registers */
> +#define NWL_DSI_CFG_NUM_LANESÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x0
> +#define NWL_DSI_CFG_NONCONTINUOUS_CLKÂÂÂÂÂÂÂÂÂÂ0x4
> +#define NWL_DSI_CFG_T_PREÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x8
> +#define NWL_DSI_CFG_T_POSTÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0xc
> +#define NWL_DSI_CFG_TX_GAPÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x10
> +#define NWL_DSI_CFG_AUTOINSERT_EOTPÂÂÂÂÂÂÂÂÂÂÂÂ0x14
> +#define NWL_DSI_CFG_EXTRA_CMDS_AFTER_EOTPÂÂÂÂÂÂ0x18
> +#define NWL_DSI_CFG_HTX_TO_COUNTÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x1c
> +#define NWL_DSI_CFG_LRX_H_TO_COUNTÂÂÂÂÂÂÂÂÂÂÂÂÂ0x20
> +#define NWL_DSI_CFG_BTA_H_TO_COUNTÂÂÂÂÂÂÂÂÂÂÂÂÂ0x24
> +#define NWL_DSI_CFG_TWAKEUPÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x28
> +#define NWL_DSI_CFG_STATUS_OUTÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x2c
> +#define NWL_DSI_RX_ERROR_STATUSÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x30
> +
> +/* DSI DPI registers */
> +#define NWL_DSI_PIXEL_PAYLOAD_SIZEÂÂÂÂÂÂÂÂÂÂÂÂÂ0x200
> +#define NWL_DSI_PIXEL_FIFO_SEND_LEVELÂÂÂÂÂÂÂÂÂÂ0x204
> +#define NWL_DSI_INTERFACE_COLOR_CODINGÂÂÂÂÂÂÂÂÂ0x208
> +#define NWL_DSI_PIXEL_FORMATÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x20c
> +#define NWL_DSI_VSYNC_POLARITYÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x210
> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_LOWÂÂÂÂÂÂ0
> +#define NWL_DSI_VSYNC_POLARITY_ACTIVE_HIGHÂÂÂÂÂBIT(1)
> +
> +#define NWL_DSI_HSYNC_POLARITYÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x214
> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_LOWÂÂÂÂÂÂ0
> +#define NWL_DSI_HSYNC_POLARITY_ACTIVE_HIGHÂÂÂÂÂBIT(1)
> +
> +#define NWL_DSI_VIDEO_MODEÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x218
> +#define NWL_DSI_HFPÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x21c
> +#define NWL_DSI_HBPÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x220
> +#define NWL_DSI_HSAÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x224
> +#define NWL_DSI_ENABLE_MULT_PKTSÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x228
> +#define NWL_DSI_VBPÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x22c
> +#define NWL_DSI_VFPÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x230
> +#define NWL_DSI_BLLP_MODEÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x234
> +#define NWL_DSI_USE_NULL_PKT_BLLPÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x238
> +#define NWL_DSI_VACTIVEÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x23c
> +#define NWL_DSI_VCÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x240
> +
> +/* DSI APB PKT control */
> +#define NWL_DSI_TX_PAYLOADÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x280
> +#define NWL_DSI_PKT_CONTROLÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x284
> +#define NWL_DSI_SEND_PACKETÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x288
> +#define NWL_DSI_PKT_STATUSÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x28c
> +#define NWL_DSI_PKT_FIFO_WR_LEVELÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x290
> +#define NWL_DSI_PKT_FIFO_RD_LEVELÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x294
> +#define NWL_DSI_RX_PAYLOADÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x298
> +#define NWL_DSI_RX_PKT_HEADERÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x29c
> +
> +/* DSI IRQ handling */
> +#define NWL_DSI_IRQ_STATUSÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x2a0
> +#define NWL_DSI_SM_NOT_IDLEÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(0)
> +#define NWL_DSI_TX_PKT_DONEÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(1)
> +#define NWL_DSI_DPHY_DIRECTIONÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(2)
> +#define NWL_DSI_TX_FIFO_OVFLWÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(3)
> +#define NWL_DSI_TX_FIFO_UDFLWÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(4)
> +#define NWL_DSI_RX_FIFO_OVFLWÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(5)
> +#define NWL_DSI_RX_FIFO_UDFLWÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(6)
> +#define NWL_DSI_RX_PKT_HDR_RCVDÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(7)
> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVDÂÂÂÂÂÂÂBIT(8)
> +#define NWL_DSI_BTA_TIMEOUTÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(29)
> +#define NWL_DSI_LP_RX_TIMEOUTÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(30)
> +#define NWL_DSI_HS_TX_TIMEOUTÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(31)
> +
> +#define NWL_DSI_IRQ_STATUS2ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x2a4
> +#define NWL_DSI_SINGLE_BIT_ECC_ERRÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(0)
> +#define NWL_DSI_MULTI_BIT_ECC_ERRÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(1)
> +#define NWL_DSI_CRC_ERRÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(2)
> +
> +#define NWL_DSI_IRQ_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x2a8
> +#define NWL_DSI_SM_NOT_IDLE_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(0)
> +#define NWL_DSI_TX_PKT_DONE_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(1)
> +#define NWL_DSI_DPHY_DIRECTION_MASKÂÂÂÂÂÂÂÂÂÂÂÂBIT(2)
> +#define NWL_DSI_TX_FIFO_OVFLW_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(3)
> +#define NWL_DSI_TX_FIFO_UDFLW_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(4)
> +#define NWL_DSI_RX_FIFO_OVFLW_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(5)
> +#define NWL_DSI_RX_FIFO_UDFLW_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(6)
> +#define NWL_DSI_RX_PKT_HDR_RCVD_MASKÂÂÂÂÂÂÂÂÂÂÂBIT(7)
> +#define NWL_DSI_RX_PKT_PAYLOAD_DATA_RCVD_MASKÂÂBIT(8)
> +#define NWL_DSI_BTA_TIMEOUT_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(29)
> +#define NWL_DSI_LP_RX_TIMEOUT_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(30)
> +#define NWL_DSI_HS_TX_TIMEOUT_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(31)
> +
> +#define NWL_DSI_IRQ_MASK2ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ0x2ac
> +#define NWL_DSI_SINGLE_BIT_ECC_ERR_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(0)
> +#define NWL_DSI_MULTI_BIT_ECC_ERR_MASKÂÂÂÂÂÂÂÂÂBIT(1)
> +#define NWL_DSI_CRC_ERR_MASKÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(2)
> +
> +/*
> + * PKT_CONTROL format:
> + * [15: 0] - word count
> + * [17:16] - virtual channel
> + * [23:18] - data type
> + * [24]ÂÂÂÂÂÂÂÂÂÂÂ- LP or HS select (0 - LP, 1 - HS)
> + * [25]ÂÂÂÂÂÂÂÂÂÂÂ- perform BTA after packet is sent
> + * [26]ÂÂÂÂÂÂÂÂÂÂÂ- perform BTA only, no packet tx
> + */
> +#define NWL_DSI_WC(x)ÂÂÂÂÂÂÂÂÂÂFIELD_PREP(GENMASK(15, 0), (x))
> +#define NWL_DSI_TX_VC(x)ÂÂÂÂÂÂÂFIELD_PREP(GENMASK(17, 16), (x))
> +#define NWL_DSI_TX_DT(x)ÂÂÂÂÂÂÂFIELD_PREP(GENMASK(23, 18), (x))
> +#define NWL_DSI_HS_SEL(x)ÂÂÂÂÂÂFIELD_PREP(GENMASK(24, 24), (x))
> +#define NWL_DSI_BTA_TX(x)ÂÂÂÂÂÂFIELD_PREP(GENMASK(25, 25), (x))
> +#define NWL_DSI_BTA_NO_TX(x)ÂÂÂFIELD_PREP(GENMASK(26, 26), (x))
> +
> +/*
> + * RX_PKT_HEADER format:
> + * [15: 0] - word count
> + * [21:16] - data type
> + * [23:22] - virtual channel
> + */
> +#define NWL_DSI_RX_DT(x)ÂÂÂÂÂÂÂFIELD_GET(GENMASK(21, 16), (x))
> +#define NWL_DSI_RX_VC(x)ÂÂÂÂÂÂÂFIELD_GET(GENMASK(23, 22), (x))
> +
> +/* DSI Video mode */
> +#define NWL_DSI_VM_BURST_MODE_WITH_SYNC_PULSESÂÂÂÂÂÂÂÂÂ0
> +#define NWL_DSI_VM_NON_BURST_MODE_WITH_SYNC_EVENTSÂÂÂÂÂBIT(0)
> +#define NWL_DSI_VM_BURST_MODEÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂBIT(1)
> +
> +/* * DPI color coding */
> +#define NWL_DSI_DPI_16_BIT_565_PACKEDÂÂ0
> +#define NWL_DSI_DPI_16_BIT_565_ALIGNED 1
> +#define NWL_DSI_DPI_16_BIT_565_SHIFTED 2
> +#define NWL_DSI_DPI_18_BIT_PACKEDÂÂÂÂÂÂ3
> +#define NWL_DSI_DPI_18_BIT_ALIGNEDÂÂÂÂÂ4
> +#define NWL_DSI_DPI_24_BITÂÂÂÂÂÂÂÂÂÂÂÂÂ5
> +
> +/* * DPI Pixel format */
> +#define NWL_DSI_PIXEL_FORMAT_16ÂÂ0
> +#define NWL_DSI_PIXEL_FORMAT_18ÂÂBIT(0)
> +#define NWL_DSI_PIXEL_FORMAT_18L BIT(1)
> +#define NWL_DSI_PIXEL_FORMAT_24ÂÂ(BIT(0) | BIT(1))
> +
> +#endif /* __NWL_DSI_H__ */
> --
> 2.23.0
>
>
I know it's a lot of information here and, since I already have that
implemented on my side, do you want me to send the next revision, or do
you want to implement it on your own?
Best regards,
Robert