// SPDX-License-Identifier: GPL-2.0+
/*
* i.MX8 NWL MIPI DSI host driver
*
* Copyright (C) 2017 NXP
* Copyright (C) 2020 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_bridge.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
#include <drm/drm_print.h>
#include <video/mipi_display.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,
};
#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;
/*
* The DSI host controller needs this reset sequence according to NWL:
* 1. Deassert pclk reset to get access to DSI regs
* 2. Configure DSI Host and DPHY and enable DPHY
* 3. Deassert ESC and BYTE resets to allow host TX operations)
* 4. Send DSI cmds to configure peripheral (handled by panel drv)
* 5. Deassert DPI reset so DPI receives pixels and starts sending
* DSI data
*
* TODO: Since panel_bridges do their DSI setup in enable we
* currently have 4. and 5. swapped.
*/
struct reset_control *rst_byte;
struct reset_control *rst_esc;
struct reset_control *rst_dpi;
struct reset_control *rst_pclk;
struct mux_control *mux;
/* DSI clocks */
struct clk *phy_ref_clk;
struct clk *rx_esc_clk;
struct clk *tx_esc_clk;
struct clk *core_clk;
/*
* hardware bug: the i.MX8MQ needs this clock on during reset
* even when not using LCDIF.
*/
struct clk *lcdif_clk;
/* 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;
};
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 *