/*
 * Copyright (c) 2023 bytes at work AG
 * Copyright (c) 2020 Teslabs Engineering S.L.
 * based on dsi_mcux.c
 * Copyright (c) 2022, NXP
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define DT_DRV_COMPAT st_stm32_mipi_dsi

#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/sys/printk.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/mipi_dsi.h>
#include <zephyr/drivers/reset.h>
#include <zephyr/logging/log.h>

LOG_MODULE_REGISTER(dsi_stm32, CONFIG_MIPI_DSI_LOG_LEVEL);

#if defined(CONFIG_STM32_LTDC_ARGB8888)
#define STM32_DSI_INIT_PIXEL_FORMAT	DSI_RGB888
#elif defined(CONFIG_STM32_LTDC_RGB888)
#define STM32_DSI_INIT_PIXEL_FORMAT	DSI_RGB888
#elif defined(CONFIG_STM32_LTDC_RGB565)
#define STM32_DSI_INIT_PIXEL_FORMAT	DSI_RGB565
#else
#error "Invalid LTDC pixel format chosen"
#endif /* CONFIG_STM32_LTDC_ARGB8888 */

#define MAX_TX_ESC_CLK_KHZ 20000
#define MAX_TX_ESC_CLK_DIV 8

struct mipi_dsi_stm32_config {
	const struct device *rcc;
	const struct reset_dt_spec reset;
	struct stm32_pclken dsi_clk;
	struct stm32_pclken ref_clk;
	struct stm32_pclken pix_clk;
	uint32_t data_lanes;
	uint32_t active_errors;
	uint32_t lp_rx_filter_freq;
	int test_pattern;
};

struct mipi_dsi_stm32_data {
	DSI_HandleTypeDef hdsi;
	DSI_HOST_TimeoutTypeDef *host_timeouts;
	DSI_PHY_TimerTypeDef *phy_timings;
	DSI_VidCfgTypeDef vid_cfg;
	DSI_PLLInitTypeDef pll_init;
	uint32_t lane_clk_khz;
	uint32_t pixel_clk_khz;
};

static void mipi_dsi_stm32_log_config(const struct device *dev)
{
	const struct mipi_dsi_stm32_config *config = dev->config;
	struct mipi_dsi_stm32_data *data = dev->data;

	LOG_DBG("DISPLAY: pix %d kHz, lane %d kHz", data->pixel_clk_khz, data->lane_clk_khz);
	LOG_DBG("HAL_DSI_Init setup:");
	LOG_DBG("  AutomaticClockLaneControl 0x%x", data->hdsi.Init.AutomaticClockLaneControl);
	LOG_DBG("  TXEscapeCkdiv %u", data->hdsi.Init.TXEscapeCkdiv);
	LOG_DBG("  NumberOfLanes %u", data->hdsi.Init.NumberOfLanes);
	LOG_DBG("  PLLNDIV %u", data->pll_init.PLLNDIV);
	LOG_DBG("  PLLIDF %u", data->pll_init.PLLIDF);
	LOG_DBG("  PLLODF %u", data->pll_init.PLLODF);

	LOG_DBG("HAL_DSI_ConfigVideoMode setup:");
	LOG_DBG("  VirtualChannelID %u", data->vid_cfg.VirtualChannelID);
	LOG_DBG("  ColorCoding 0x%x", data->vid_cfg.ColorCoding);
	LOG_DBG("  LooselyPacked 0x%x", data->vid_cfg.LooselyPacked);
	LOG_DBG("  Mode 0x%x", data->vid_cfg.Mode);
	LOG_DBG("  PacketSize %u", data->vid_cfg.PacketSize);
	LOG_DBG("  NumberOfChunks %u", data->vid_cfg.NumberOfChunks);
	LOG_DBG("  NullPacketSize %u", data->vid_cfg.NullPacketSize);
	LOG_DBG("  HSPolarity 0x%x", data->vid_cfg.HSPolarity);
	LOG_DBG("  VSPolarity 0x%x", data->vid_cfg.VSPolarity);
	LOG_DBG("  DEPolarity 0x%x", data->vid_cfg.DEPolarity);
	LOG_DBG("  HorizontalSyncActive %u", data->vid_cfg.HorizontalSyncActive);
	LOG_DBG("  HorizontalBackPorch %u", data->vid_cfg.HorizontalBackPorch);
	LOG_DBG("  HorizontalLine %u", data->vid_cfg.HorizontalLine);
	LOG_DBG("  VerticalSyncActive %u", data->vid_cfg.VerticalSyncActive);
	LOG_DBG("  VerticalBackPorch %u", data->vid_cfg.VerticalBackPorch);
	LOG_DBG("  VerticalFrontPorch %u", data->vid_cfg.VerticalFrontPorch);
	LOG_DBG("  VerticalActive %u", data->vid_cfg.VerticalActive);
	LOG_DBG("  LPCommandEnable 0x%x", data->vid_cfg.LPCommandEnable);
	LOG_DBG("  LPLargestPacketSize %u", data->vid_cfg.LPLargestPacketSize);
	LOG_DBG("  LPVACTLargestPacketSize %u", data->vid_cfg.LPVACTLargestPacketSize);
	LOG_DBG("  LPHorizontalFrontPorchEnable 0x%x", data->vid_cfg.LPHorizontalFrontPorchEnable);
	LOG_DBG("  LPHorizontalBackPorchEnable 0x%x", data->vid_cfg.LPHorizontalBackPorchEnable);
	LOG_DBG("  LPVerticalActiveEnable 0x%x", data->vid_cfg.LPVerticalActiveEnable);
	LOG_DBG("  LPVerticalFrontPorchEnable 0x%x", data->vid_cfg.LPVerticalFrontPorchEnable);
	LOG_DBG("  LPVerticalBackPorchEnable 0x%x", data->vid_cfg.LPVerticalBackPorchEnable);
	LOG_DBG("  LPVerticalSyncActiveEnable 0x%x", data->vid_cfg.LPVerticalSyncActiveEnable);
	LOG_DBG("  FrameBTAAcknowledgeEnable 0x%x", data->vid_cfg.FrameBTAAcknowledgeEnable);

	if (config->active_errors) {
		LOG_DBG("HAL_DSI_ConfigErrorMonitor: 0x%x", config->active_errors);
	}

	if (config->lp_rx_filter_freq) {
		LOG_DBG("HAL_DSI_SetLowPowerRXFilter: %d", config->lp_rx_filter_freq);
	}

	if (data->host_timeouts) {
		DSI_HOST_TimeoutTypeDef *ht = data->host_timeouts;

		LOG_DBG("HAL_DSI_ConfigHostTimeouts:");
		LOG_DBG("  TimeoutCkdiv %u", ht->TimeoutCkdiv);
		LOG_DBG("  HighSpeedTransmissionTimeout %u", ht->HighSpeedTransmissionTimeout);
		LOG_DBG("  LowPowerReceptionTimeout %u", ht->LowPowerReceptionTimeout);
		LOG_DBG("  HighSpeedReadTimeout %u", ht->HighSpeedReadTimeout);
		LOG_DBG("  LowPowerReadTimeout %u", ht->LowPowerReadTimeout);
		LOG_DBG("  HighSpeedWriteTimeout %u", ht->HighSpeedWriteTimeout);
		LOG_DBG("  HighSpeedWritePrespMode %u", ht->HighSpeedWritePrespMode);
		LOG_DBG("  LowPowerWriteTimeout %u", ht->LowPowerWriteTimeout);
		LOG_DBG("  BTATimeout %u", ht->BTATimeout);
	}

	if (data->phy_timings) {
		LOG_DBG("HAL_DSI_ConfigPhyTimer:");
		LOG_DBG("  ClockLaneHS2LPTime %u", data->phy_timings->ClockLaneHS2LPTime);
		LOG_DBG("  ClockLaneLP2HSTime %u", data->phy_timings->ClockLaneLP2HSTime);
		LOG_DBG("  DataLaneHS2LPTime %u", data->phy_timings->DataLaneHS2LPTime);
		LOG_DBG("  DataLaneLP2HSTime %u", data->phy_timings->DataLaneLP2HSTime);
		LOG_DBG("  DataLaneMaxReadTime %u", data->phy_timings->DataLaneMaxReadTime);
		LOG_DBG("  StopWaitTime %u", data->phy_timings->StopWaitTime);
	}
}

static int mipi_dsi_stm32_host_init(const struct device *dev)
{
	const struct mipi_dsi_stm32_config *config = dev->config;
	struct mipi_dsi_stm32_data *data = dev->data;
	uint32_t hse_clock;
	int ret;

	switch (config->data_lanes) {
	case 1:
		data->hdsi.Init.NumberOfLanes = DSI_ONE_DATA_LANE;
		break;
	case 2:
		data->hdsi.Init.NumberOfLanes = DSI_TWO_DATA_LANES;
		break;
	default:
		LOG_ERR("Number of DSI lanes (%d) not supported!", config->data_lanes);
		return -ENOTSUP;
	}

	ret = clock_control_get_rate(config->rcc, (clock_control_subsys_t)&config->pix_clk,
				     &data->pixel_clk_khz);
	if (ret) {
		LOG_ERR("Get pixel clock failed! (%d)", ret);
		return ret;
	}

	data->pixel_clk_khz /= 1000;
	ret = clock_control_get_rate(config->rcc, (clock_control_subsys_t)&config->ref_clk,
				     &hse_clock);
	if (ret) {
		LOG_ERR("Get HSE clock failed! (%d)", ret);
		return ret;
	}

	/* LANE_BYTE_CLOCK = CLK_IN / PLLIDF * 2 * PLLNDIV / 2 / PLLODF / 8 */
	data->lane_clk_khz = hse_clock / data->pll_init.PLLIDF * 2 * data->pll_init.PLLNDIV / 2 /
			     (1UL << data->pll_init.PLLODF) / 8 / 1000;

	/* stm32x_hal_dsi: The values 0 and 1 stop the TX_ESC clock generation */
	data->hdsi.Init.TXEscapeCkdiv = 0;
	for (int i = 2; i <= MAX_TX_ESC_CLK_DIV; i++) {
		if ((data->lane_clk_khz / i) <= MAX_TX_ESC_CLK_KHZ) {
			data->hdsi.Init.TXEscapeCkdiv = i;
			break;
		}
	}

	if (data->hdsi.Init.TXEscapeCkdiv < 2) {
		LOG_WRN("DSI TX escape clock disabled.");
	}

	ret = HAL_DSI_Init(&data->hdsi, &data->pll_init);
	if (ret != HAL_OK) {
		LOG_ERR("DSI init failed! (%d)", ret);
		return -ret;
	}

	if (data->host_timeouts) {
		ret = HAL_DSI_ConfigHostTimeouts(&data->hdsi, data->host_timeouts);
		if (ret != HAL_OK) {
			LOG_ERR("Set DSI host timeouts failed! (%d)", ret);
			return -ret;
		}
	}

	if (data->phy_timings) {
		ret = HAL_DSI_ConfigPhyTimer(&data->hdsi, data->phy_timings);
		if (ret != HAL_OK) {
			LOG_ERR("Set DSI PHY timings failed! (%d)", ret);
			return -ret;
		}
	}

	ret = HAL_DSI_ConfigFlowControl(&data->hdsi, DSI_FLOW_CONTROL_BTA);
	if (ret != HAL_OK) {
		LOG_ERR("Setup DSI flow control failed! (%d)", ret);
		return -ret;
	}

	if (config->lp_rx_filter_freq) {
		ret = HAL_DSI_SetLowPowerRXFilter(&data->hdsi, config->lp_rx_filter_freq);
		if (ret != HAL_OK) {
			LOG_ERR("Setup DSI LP RX filter failed! (%d)", ret);
			return -ret;
		}
	}

	ret = HAL_DSI_ConfigErrorMonitor(&data->hdsi, config->active_errors);
	if (ret != HAL_OK) {
		LOG_ERR("Setup DSI error monitor failed! (%d)", ret);
		return -ret;
	}

	return 0;
}


static int mipi_dsi_stm32_attach(const struct device *dev, uint8_t channel,
				 const struct mipi_dsi_device *mdev)
{
	const struct mipi_dsi_stm32_config *config = dev->config;
	struct mipi_dsi_stm32_data *data = dev->data;
	DSI_VidCfgTypeDef *vcfg = &data->vid_cfg;
	int ret;

	if (!(mdev->mode_flags & MIPI_DSI_MODE_VIDEO)) {
		LOG_ERR("DSI host supports video mode only!");
		return -ENOTSUP;
	}

	vcfg->VirtualChannelID = channel;
	vcfg->ColorCoding = STM32_DSI_INIT_PIXEL_FORMAT;
	vcfg->LooselyPacked = DSI_LOOSELY_PACKED_DISABLE;

	if (mdev->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
		vcfg->Mode = DSI_VID_MODE_BURST;
	} else if (mdev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
		vcfg->Mode = DSI_VID_MODE_NB_PULSES;
	} else {
		vcfg->Mode = DSI_VID_MODE_NB_EVENTS;
	}

	vcfg->PacketSize = mdev->timings.hactive;
	vcfg->NumberOfChunks = 0;
	vcfg->NullPacketSize = 0xFFFU;

	vcfg->HorizontalSyncActive =
		(mdev->timings.hsync * data->lane_clk_khz) / data->pixel_clk_khz;
	vcfg->HorizontalBackPorch =
		(mdev->timings.hbp * data->lane_clk_khz) / data->pixel_clk_khz;
	vcfg->HorizontalLine =
		((mdev->timings.hactive + mdev->timings.hsync + mdev->timings.hbp +
		  mdev->timings.hfp) * data->lane_clk_khz) / data->pixel_clk_khz;
	vcfg->VerticalSyncActive = mdev->timings.vsync;
	vcfg->VerticalBackPorch = mdev->timings.vbp;
	vcfg->VerticalFrontPorch = mdev->timings.vfp;
	vcfg->VerticalActive = mdev->timings.vactive;

	if (mdev->mode_flags & MIPI_DSI_MODE_LPM) {
		vcfg->LPCommandEnable = DSI_LP_COMMAND_ENABLE;
	} else {
		vcfg->LPCommandEnable = DSI_LP_COMMAND_DISABLE;
	}

	vcfg->LPLargestPacketSize = 4;
	vcfg->LPVACTLargestPacketSize = 4;

	vcfg->LPHorizontalFrontPorchEnable = DSI_LP_HFP_ENABLE;
	vcfg->LPHorizontalBackPorchEnable = DSI_LP_HBP_ENABLE;
	vcfg->LPVerticalActiveEnable = DSI_LP_VACT_ENABLE;
	vcfg->LPVerticalFrontPorchEnable = DSI_LP_VFP_ENABLE;
	vcfg->LPVerticalBackPorchEnable = DSI_LP_VBP_ENABLE;
	vcfg->LPVerticalSyncActiveEnable = DSI_LP_VSYNC_ENABLE;
	vcfg->FrameBTAAcknowledgeEnable = DSI_FBTAA_ENABLE;

	ret = HAL_DSI_ConfigVideoMode(&data->hdsi, vcfg);
	if (ret != HAL_OK) {
		LOG_ERR("Setup DSI video mode failed! (%d)", ret);
		return -ret;
	}

	if (IS_ENABLED(CONFIG_MIPI_DSI_LOG_LEVEL_DBG)) {
		mipi_dsi_stm32_log_config(dev);
	}

	ret = HAL_DSI_Start(&data->hdsi);
	if (ret != HAL_OK) {
		LOG_ERR("Start DSI host failed! (%d)", ret);
		return -ret;
	}

	if (config->test_pattern >= 0) {
		ret = HAL_DSI_PatternGeneratorStart(&data->hdsi, 0, config->test_pattern);
		if (ret != HAL_OK) {
			LOG_ERR("Start DSI pattern generator failed! (%d)", ret);
			return -ret;
		}
	}

	return 0;
}

static ssize_t mipi_dsi_stm32_transfer(const struct device *dev, uint8_t channel,
				       struct mipi_dsi_msg *msg)
{
	struct mipi_dsi_stm32_data *data = dev->data;
	uint32_t param1 = 0;
	uint32_t param2 = 0;
	ssize_t len;
	int ret;

	switch (msg->type) {
	case MIPI_DSI_DCS_READ:
		ret = HAL_DSI_Read(&data->hdsi, channel, (uint8_t *)msg->rx_buf, msg->rx_len,
				   msg->type, msg->cmd, (uint8_t *)msg->rx_buf);
		len =  msg->rx_len;
		break;
	case MIPI_DSI_DCS_SHORT_WRITE:
	case MIPI_DSI_DCS_SHORT_WRITE_PARAM:
		param1 = msg->cmd;
		if (msg->tx_len >= 1U) {
			param2 = ((uint8_t *)msg->tx_buf)[0];
		}

		ret = HAL_DSI_ShortWrite(&data->hdsi, channel, msg->type, param1, param2);
		len = msg->tx_len;
		break;
	case MIPI_DSI_DCS_LONG_WRITE:
		ret = HAL_DSI_LongWrite(&data->hdsi, channel, msg->type, msg->tx_len, msg->cmd,
					(uint8_t *)msg->tx_buf);
		len = msg->tx_len;
		break;
	case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:
	case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:
	case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:
		param1 = ((uint8_t *)msg->tx_buf)[0];
		if (msg->tx_len == 1U) {
			param2 = ((uint8_t *)msg->tx_buf)[1];
		}

		if (msg->tx_len >= 2U) {
			param2 = *(uint16_t *)&((uint8_t *)msg->tx_buf)[1];
		}

		ret = HAL_DSI_ShortWrite(&data->hdsi, channel, msg->type, param1, param2);
		len = msg->tx_len;
		break;
	case MIPI_DSI_GENERIC_LONG_WRITE:
		ret = HAL_DSI_LongWrite(&data->hdsi, channel, msg->type, msg->tx_len,
					((uint8_t *)msg->tx_buf)[0], &((uint8_t *)msg->tx_buf)[1]);
		len = msg->tx_len;
		break;
	default:
		LOG_ERR("Unsupported message type (%d)", msg->type);
		return -ENOTSUP;
	}

	if (IS_ENABLED(CONFIG_MIPI_DSI_LOG_LEVEL_DBG)) {
		char tmp[64];

		if (msg->type == MIPI_DSI_DCS_READ) {
			snprintk(tmp, sizeof(tmp), "RX: ch %3d, reg 0x%02x, len %2d",
				 channel, msg->cmd, msg->rx_len);
			LOG_HEXDUMP_DBG(msg->rx_buf, msg->rx_len, tmp);
		} else {
			snprintk(tmp, sizeof(tmp), "TX: ch %3d, reg 0x%02x, len %2d",
				 channel, msg->cmd, msg->tx_len);
			LOG_HEXDUMP_DBG(msg->tx_buf, msg->tx_len, tmp);
		}
	}

	if (ret != HAL_OK) {
		LOG_ERR("Transfer failed! (%d)", ret);
		return -EIO;
	}

	return len;
}

static struct mipi_dsi_driver_api dsi_stm32_api = {
	.attach = mipi_dsi_stm32_attach,
	.transfer = mipi_dsi_stm32_transfer,
};

static int mipi_dsi_stm32_init(const struct device *dev)
{
	const struct mipi_dsi_stm32_config *config = dev->config;
	int ret;

	if (!device_is_ready(config->rcc)) {
		LOG_ERR("clock control device not ready");
		return -ENODEV;
	}

	ret = clock_control_on(config->rcc, (clock_control_subsys_t)&config->dsi_clk);
	if (ret < 0) {
		LOG_ERR("Enable DSI peripheral clock failed! (%d)", ret);
		return ret;
	}

	(void)reset_line_toggle_dt(&config->reset);

	ret = mipi_dsi_stm32_host_init(dev);
	if (ret) {
		LOG_ERR("Setup DSI host failed! (%d)", ret);
		return ret;
	}

	return 0;
}

#define CHILD_GET_DATA_LANES(child) DT_PROP(child, data_lanes)

#define STM32_MIPI_DSI_DEVICE(inst)								\
	COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, host_timeouts),					\
		(static DSI_HOST_TimeoutTypeDef host_timeouts_##inst = {			\
			.TimeoutCkdiv = DT_INST_PROP_BY_IDX(inst, host_timeouts, 0),		\
			.HighSpeedTransmissionTimeout =						\
				DT_INST_PROP_BY_IDX(inst, host_timeouts, 1),			\
			.LowPowerReceptionTimeout =						\
				DT_INST_PROP_BY_IDX(inst, host_timeouts, 2),			\
			.HighSpeedReadTimeout = DT_INST_PROP_BY_IDX(inst, host_timeouts, 3),	\
			.LowPowerReadTimeout = DT_INST_PROP_BY_IDX(inst, host_timeouts, 4),	\
			.HighSpeedWriteTimeout = DT_INST_PROP_BY_IDX(inst, host_timeouts, 5),	\
			.HighSpeedWritePrespMode = DT_INST_PROP_BY_IDX(inst, host_timeouts, 6),	\
			.LowPowerWriteTimeout = DT_INST_PROP_BY_IDX(inst, host_timeouts, 7),	\
			.BTATimeout = DT_INST_PROP_BY_IDX(inst, host_timeouts, 8)		\
		}), ());									\
	COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, phy_timings),					\
		(static DSI_PHY_TimerTypeDef phy_timings_##inst = {				\
			.ClockLaneHS2LPTime = DT_INST_PROP_BY_IDX(inst, phy_timings, 0),	\
			.ClockLaneLP2HSTime = DT_INST_PROP_BY_IDX(inst, phy_timings, 1),	\
			.DataLaneHS2LPTime = DT_INST_PROP_BY_IDX(inst, phy_timings, 2),		\
			.DataLaneLP2HSTime = DT_INST_PROP_BY_IDX(inst, phy_timings, 3),		\
			.DataLaneMaxReadTime = DT_INST_PROP_BY_IDX(inst, phy_timings, 4),	\
			.StopWaitTime = DT_INST_PROP_BY_IDX(inst, phy_timings, 5)		\
		}), ());									\
	/* Only child data-lanes property at index 0 is taken into account */			\
	static const uint32_t data_lanes_##inst[] = {						\
		DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP_VARGS(inst, DT_PROP_BY_IDX, (,),		\
							    data_lanes, 0)			\
	};											\
	static const struct mipi_dsi_stm32_config stm32_dsi_config_##inst = {			\
		.rcc = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE),					\
		.reset = RESET_DT_SPEC_INST_GET(inst),						\
		.dsi_clk = {									\
			.enr = DT_INST_CLOCKS_CELL_BY_NAME(inst, dsiclk, bits),			\
			.bus = DT_INST_CLOCKS_CELL_BY_NAME(inst, dsiclk, bus),			\
		},										\
		.ref_clk = {									\
			.enr = DT_INST_CLOCKS_CELL_BY_NAME(inst, refclk, bits),			\
			.bus = DT_INST_CLOCKS_CELL_BY_NAME(inst, refclk, bus),			\
		},										\
		.pix_clk = {									\
			.enr = DT_INST_CLOCKS_CELL_BY_NAME(inst, pixelclk, bits),		\
			.bus = DT_INST_CLOCKS_CELL_BY_NAME(inst, pixelclk, bus),		\
		},										\
		/* Use only one (the first) display configuration for DSI HOST configuration */	\
		.data_lanes = data_lanes_##inst[0],						\
		.active_errors = DT_INST_PROP_OR(inst, active_errors, HAL_DSI_ERROR_NONE),	\
		.lp_rx_filter_freq = DT_INST_PROP_OR(inst, lp_rx_filter, 0),			\
		.test_pattern = DT_INST_PROP_OR(inst, test_pattern, -1),			\
	};											\
	static struct mipi_dsi_stm32_data stm32_dsi_data_##inst = {				\
		.hdsi = {									\
			.Instance = (DSI_TypeDef *)DT_INST_REG_ADDR(inst),			\
			.Init = {								\
				.AutomaticClockLaneControl =					\
					DT_INST_PROP(inst, non_continuous) ?			\
						DSI_AUTO_CLK_LANE_CTRL_ENABLE :			\
						DSI_AUTO_CLK_LANE_CTRL_DISABLE,			\
			},									\
		},										\
		.host_timeouts = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, host_timeouts),	\
					     (&host_timeouts_##inst), (NULL)),			\
		.phy_timings = COND_CODE_1(DT_INST_NODE_HAS_PROP(inst, phy_timings),		\
					   (&phy_timings_##inst), (NULL)),			\
		.vid_cfg = {									\
			.HSPolarity = DT_INST_PROP(inst, hs_active_high) ?			\
				      DSI_HSYNC_ACTIVE_HIGH : DSI_HSYNC_ACTIVE_LOW,		\
			.VSPolarity = DT_INST_PROP(inst, vs_active_high) ?			\
				      DSI_VSYNC_ACTIVE_HIGH : DSI_VSYNC_ACTIVE_LOW,		\
			.DEPolarity = DT_INST_PROP(inst, de_active_high) ?			\
				      DSI_DATA_ENABLE_ACTIVE_HIGH : DSI_DATA_ENABLE_ACTIVE_LOW,	\
		},										\
		.pll_init = {									\
			.PLLNDIV = DT_INST_PROP(inst, pll_ndiv),				\
			.PLLIDF = DT_INST_PROP(inst, pll_idf),					\
			.PLLODF = DT_INST_PROP(inst, pll_odf),					\
		},										\
	};											\
	DEVICE_DT_INST_DEFINE(inst, &mipi_dsi_stm32_init, NULL,					\
			      &stm32_dsi_data_##inst, &stm32_dsi_config_##inst,			\
			      POST_KERNEL, CONFIG_MIPI_DSI_INIT_PRIORITY, &dsi_stm32_api);

DT_INST_FOREACH_STATUS_OKAY(STM32_MIPI_DSI_DEVICE)
