162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2017
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Authors: Philippe Cornu <philippe.cornu@st.com>
662306a36Sopenharmony_ci *          Yannick Fertre <yannick.fertre@st.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/clk.h>
1062306a36Sopenharmony_ci#include <linux/iopoll.h>
1162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/platform_device.h>
1462306a36Sopenharmony_ci#include <linux/regulator/consumer.h>
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <video/mipi_display.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <drm/bridge/dw_mipi_dsi.h>
1962306a36Sopenharmony_ci#include <drm/drm_mipi_dsi.h>
2062306a36Sopenharmony_ci#include <drm/drm_print.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define HWVER_130			0x31333000	/* IP version 1.30 */
2362306a36Sopenharmony_ci#define HWVER_131			0x31333100	/* IP version 1.31 */
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/* DSI digital registers & bit definitions */
2662306a36Sopenharmony_ci#define DSI_VERSION			0x00
2762306a36Sopenharmony_ci#define VERSION				GENMASK(31, 8)
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci/* DSI wrapper registers & bit definitions */
3062306a36Sopenharmony_ci/* Note: registers are named as in the Reference Manual */
3162306a36Sopenharmony_ci#define DSI_WCFGR	0x0400		/* Wrapper ConFiGuration Reg */
3262306a36Sopenharmony_ci#define WCFGR_DSIM	BIT(0)		/* DSI Mode */
3362306a36Sopenharmony_ci#define WCFGR_COLMUX	GENMASK(3, 1)	/* COLor MUltipleXing */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#define DSI_WCR		0x0404		/* Wrapper Control Reg */
3662306a36Sopenharmony_ci#define WCR_DSIEN	BIT(3)		/* DSI ENable */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define DSI_WISR	0x040C		/* Wrapper Interrupt and Status Reg */
3962306a36Sopenharmony_ci#define WISR_PLLLS	BIT(8)		/* PLL Lock Status */
4062306a36Sopenharmony_ci#define WISR_RRS	BIT(12)		/* Regulator Ready Status */
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci#define DSI_WPCR0	0x0418		/* Wrapper Phy Conf Reg 0 */
4362306a36Sopenharmony_ci#define WPCR0_UIX4	GENMASK(5, 0)	/* Unit Interval X 4 */
4462306a36Sopenharmony_ci#define WPCR0_TDDL	BIT(16)		/* Turn Disable Data Lanes */
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define DSI_WRPCR	0x0430		/* Wrapper Regulator & Pll Ctrl Reg */
4762306a36Sopenharmony_ci#define WRPCR_PLLEN	BIT(0)		/* PLL ENable */
4862306a36Sopenharmony_ci#define WRPCR_NDIV	GENMASK(8, 2)	/* pll loop DIVision Factor */
4962306a36Sopenharmony_ci#define WRPCR_IDF	GENMASK(14, 11)	/* pll Input Division Factor */
5062306a36Sopenharmony_ci#define WRPCR_ODF	GENMASK(17, 16)	/* pll Output Division Factor */
5162306a36Sopenharmony_ci#define WRPCR_REGEN	BIT(24)		/* REGulator ENable */
5262306a36Sopenharmony_ci#define WRPCR_BGREN	BIT(28)		/* BandGap Reference ENable */
5362306a36Sopenharmony_ci#define IDF_MIN		1
5462306a36Sopenharmony_ci#define IDF_MAX		7
5562306a36Sopenharmony_ci#define NDIV_MIN	10
5662306a36Sopenharmony_ci#define NDIV_MAX	125
5762306a36Sopenharmony_ci#define ODF_MIN		1
5862306a36Sopenharmony_ci#define ODF_MAX		8
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci/* dsi color format coding according to the datasheet */
6162306a36Sopenharmony_cienum dsi_color {
6262306a36Sopenharmony_ci	DSI_RGB565_CONF1,
6362306a36Sopenharmony_ci	DSI_RGB565_CONF2,
6462306a36Sopenharmony_ci	DSI_RGB565_CONF3,
6562306a36Sopenharmony_ci	DSI_RGB666_CONF1,
6662306a36Sopenharmony_ci	DSI_RGB666_CONF2,
6762306a36Sopenharmony_ci	DSI_RGB888,
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci#define LANE_MIN_KBPS	31250
7162306a36Sopenharmony_ci#define LANE_MAX_KBPS	500000
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* Sleep & timeout for regulator on/off, pll lock/unlock & fifo empty */
7462306a36Sopenharmony_ci#define SLEEP_US	1000
7562306a36Sopenharmony_ci#define TIMEOUT_US	200000
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistruct dw_mipi_dsi_stm {
7862306a36Sopenharmony_ci	void __iomem *base;
7962306a36Sopenharmony_ci	struct clk *pllref_clk;
8062306a36Sopenharmony_ci	struct dw_mipi_dsi *dsi;
8162306a36Sopenharmony_ci	u32 hw_version;
8262306a36Sopenharmony_ci	int lane_min_kbps;
8362306a36Sopenharmony_ci	int lane_max_kbps;
8462306a36Sopenharmony_ci	struct regulator *vdd_supply;
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	writel(val, dsi->base + reg);
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic inline u32 dsi_read(struct dw_mipi_dsi_stm *dsi, u32 reg)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return readl(dsi->base + reg);
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic inline void dsi_set(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	dsi_write(dsi, reg, dsi_read(dsi, reg) | mask);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic inline void dsi_clear(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic inline void dsi_update_bits(struct dw_mipi_dsi_stm *dsi, u32 reg,
10862306a36Sopenharmony_ci				   u32 mask, u32 val)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	switch (fmt) {
11662306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB888:
11762306a36Sopenharmony_ci		return DSI_RGB888;
11862306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB666:
11962306a36Sopenharmony_ci		return DSI_RGB666_CONF2;
12062306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB666_PACKED:
12162306a36Sopenharmony_ci		return DSI_RGB666_CONF1;
12262306a36Sopenharmony_ci	case MIPI_DSI_FMT_RGB565:
12362306a36Sopenharmony_ci		return DSI_RGB565_CONF1;
12462306a36Sopenharmony_ci	default:
12562306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n");
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci	return DSI_RGB888;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	int divisor = idf * odf;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	/* prevent from division by 0 */
13562306a36Sopenharmony_ci	if (!divisor)
13662306a36Sopenharmony_ci		return 0;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int dsi_pll_get_params(struct dw_mipi_dsi_stm *dsi,
14262306a36Sopenharmony_ci			      int clkin_khz, int clkout_khz,
14362306a36Sopenharmony_ci			      int *idf, int *ndiv, int *odf)
14462306a36Sopenharmony_ci{
14562306a36Sopenharmony_ci	int i, o, n, n_min, n_max;
14662306a36Sopenharmony_ci	int fvco_min, fvco_max, delta, best_delta; /* all in khz */
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/* Early checks preventing division by 0 & odd results */
14962306a36Sopenharmony_ci	if (clkin_khz <= 0 || clkout_khz <= 0)
15062306a36Sopenharmony_ci		return -EINVAL;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	fvco_min = dsi->lane_min_kbps * 2 * ODF_MAX;
15362306a36Sopenharmony_ci	fvco_max = dsi->lane_max_kbps * 2 * ODF_MIN;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	best_delta = 1000000; /* big started value (1000000khz) */
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	for (i = IDF_MIN; i <= IDF_MAX; i++) {
15862306a36Sopenharmony_ci		/* Compute ndiv range according to Fvco */
15962306a36Sopenharmony_ci		n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1;
16062306a36Sopenharmony_ci		n_max = (fvco_max * i) / (2 * clkin_khz);
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci		/* No need to continue idf loop if we reach ndiv max */
16362306a36Sopenharmony_ci		if (n_min >= NDIV_MAX)
16462306a36Sopenharmony_ci			break;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci		/* Clamp ndiv to valid values */
16762306a36Sopenharmony_ci		if (n_min < NDIV_MIN)
16862306a36Sopenharmony_ci			n_min = NDIV_MIN;
16962306a36Sopenharmony_ci		if (n_max > NDIV_MAX)
17062306a36Sopenharmony_ci			n_max = NDIV_MAX;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		for (o = ODF_MIN; o <= ODF_MAX; o *= 2) {
17362306a36Sopenharmony_ci			n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz);
17462306a36Sopenharmony_ci			/* Check ndiv according to vco range */
17562306a36Sopenharmony_ci			if (n < n_min || n > n_max)
17662306a36Sopenharmony_ci				continue;
17762306a36Sopenharmony_ci			/* Check if new delta is better & saves parameters */
17862306a36Sopenharmony_ci			delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) -
17962306a36Sopenharmony_ci				clkout_khz;
18062306a36Sopenharmony_ci			if (delta < 0)
18162306a36Sopenharmony_ci				delta = -delta;
18262306a36Sopenharmony_ci			if (delta < best_delta) {
18362306a36Sopenharmony_ci				*idf = i;
18462306a36Sopenharmony_ci				*ndiv = n;
18562306a36Sopenharmony_ci				*odf = o;
18662306a36Sopenharmony_ci				best_delta = delta;
18762306a36Sopenharmony_ci			}
18862306a36Sopenharmony_ci			/* fast return in case of "perfect result" */
18962306a36Sopenharmony_ci			if (!delta)
19062306a36Sopenharmony_ci				return 0;
19162306a36Sopenharmony_ci		}
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int dw_mipi_dsi_phy_init(void *priv_data)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = priv_data;
20062306a36Sopenharmony_ci	u32 val;
20162306a36Sopenharmony_ci	int ret;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	/* Enable the regulator */
20462306a36Sopenharmony_ci	dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN);
20562306a36Sopenharmony_ci	ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS,
20662306a36Sopenharmony_ci				 SLEEP_US, TIMEOUT_US);
20762306a36Sopenharmony_ci	if (ret)
20862306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n");
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	/* Enable the DSI PLL & wait for its lock */
21162306a36Sopenharmony_ci	dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN);
21262306a36Sopenharmony_ci	ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS,
21362306a36Sopenharmony_ci				 SLEEP_US, TIMEOUT_US);
21462306a36Sopenharmony_ci	if (ret)
21562306a36Sopenharmony_ci		DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n");
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void dw_mipi_dsi_phy_power_on(void *priv_data)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = priv_data;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* Enable the DSI wrapper */
22762306a36Sopenharmony_ci	dsi_set(dsi, DSI_WCR, WCR_DSIEN);
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic void dw_mipi_dsi_phy_power_off(void *priv_data)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = priv_data;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	/* Disable the DSI wrapper */
23762306a36Sopenharmony_ci	dsi_clear(dsi, DSI_WCR, WCR_DSIEN);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic int
24162306a36Sopenharmony_cidw_mipi_dsi_get_lane_mbps(void *priv_data, const struct drm_display_mode *mode,
24262306a36Sopenharmony_ci			  unsigned long mode_flags, u32 lanes, u32 format,
24362306a36Sopenharmony_ci			  unsigned int *lane_mbps)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = priv_data;
24662306a36Sopenharmony_ci	unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
24762306a36Sopenharmony_ci	int ret, bpp;
24862306a36Sopenharmony_ci	u32 val;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000);
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	/* Compute requested pll out */
25362306a36Sopenharmony_ci	bpp = mipi_dsi_pixel_format_to_bpp(format);
25462306a36Sopenharmony_ci	pll_out_khz = mode->clock * bpp / lanes;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	/* Add 20% to pll out to be higher than pixel bw (burst mode only) */
25762306a36Sopenharmony_ci	if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST)
25862306a36Sopenharmony_ci		pll_out_khz = (pll_out_khz * 12) / 10;
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	if (pll_out_khz > dsi->lane_max_kbps) {
26162306a36Sopenharmony_ci		pll_out_khz = dsi->lane_max_kbps;
26262306a36Sopenharmony_ci		DRM_WARN("Warning max phy mbps is used\n");
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci	if (pll_out_khz < dsi->lane_min_kbps) {
26562306a36Sopenharmony_ci		pll_out_khz = dsi->lane_min_kbps;
26662306a36Sopenharmony_ci		DRM_WARN("Warning min phy mbps is used\n");
26762306a36Sopenharmony_ci	}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	/* Compute best pll parameters */
27062306a36Sopenharmony_ci	idf = 0;
27162306a36Sopenharmony_ci	ndiv = 0;
27262306a36Sopenharmony_ci	odf = 0;
27362306a36Sopenharmony_ci	ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz,
27462306a36Sopenharmony_ci				 &idf, &ndiv, &odf);
27562306a36Sopenharmony_ci	if (ret)
27662306a36Sopenharmony_ci		DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* Get the adjusted pll out value */
27962306a36Sopenharmony_ci	pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	/* Set the PLL division factors */
28262306a36Sopenharmony_ci	dsi_update_bits(dsi, DSI_WRPCR,	WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF,
28362306a36Sopenharmony_ci			(ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16));
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	/* Compute uix4 & set the bit period in high-speed mode */
28662306a36Sopenharmony_ci	val = 4000000 / pll_out_khz;
28762306a36Sopenharmony_ci	dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Select video mode by resetting DSIM bit */
29062306a36Sopenharmony_ci	dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM);
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* Select the color coding */
29362306a36Sopenharmony_ci	dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX,
29462306a36Sopenharmony_ci			dsi_color_from_mipi(format) << 1);
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	*lane_mbps = pll_out_khz / 1000;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n",
29962306a36Sopenharmony_ci			 pll_in_khz, pll_out_khz, *lane_mbps);
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return 0;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci#define DSI_PHY_DELAY(fp, vp, mbps) DIV_ROUND_UP((fp) * (mbps) + 1000 * (vp), 8000)
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_cistatic int
30762306a36Sopenharmony_cidw_mipi_dsi_phy_get_timing(void *priv_data, unsigned int lane_mbps,
30862306a36Sopenharmony_ci			   struct dw_mipi_dsi_dphy_timing *timing)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	/*
31162306a36Sopenharmony_ci	 * From STM32MP157 datasheet, valid for STM32F469, STM32F7x9, STM32H747
31262306a36Sopenharmony_ci	 * phy_clkhs2lp_time = (272+136*UI)/(8*UI)
31362306a36Sopenharmony_ci	 * phy_clklp2hs_time = (512+40*UI)/(8*UI)
31462306a36Sopenharmony_ci	 * phy_hs2lp_time = (192+64*UI)/(8*UI)
31562306a36Sopenharmony_ci	 * phy_lp2hs_time = (256+32*UI)/(8*UI)
31662306a36Sopenharmony_ci	 */
31762306a36Sopenharmony_ci	timing->clk_hs2lp = DSI_PHY_DELAY(272, 136, lane_mbps);
31862306a36Sopenharmony_ci	timing->clk_lp2hs = DSI_PHY_DELAY(512, 40, lane_mbps);
31962306a36Sopenharmony_ci	timing->data_hs2lp = DSI_PHY_DELAY(192, 64, lane_mbps);
32062306a36Sopenharmony_ci	timing->data_lp2hs = DSI_PHY_DELAY(256, 32, lane_mbps);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return 0;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci#define CLK_TOLERANCE_HZ 50
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_cistatic enum drm_mode_status
32862306a36Sopenharmony_cidw_mipi_dsi_stm_mode_valid(void *priv_data,
32962306a36Sopenharmony_ci			   const struct drm_display_mode *mode,
33062306a36Sopenharmony_ci			   unsigned long mode_flags, u32 lanes, u32 format)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = priv_data;
33362306a36Sopenharmony_ci	unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz;
33462306a36Sopenharmony_ci	int ret, bpp;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	bpp = mipi_dsi_pixel_format_to_bpp(format);
33762306a36Sopenharmony_ci	if (bpp < 0)
33862306a36Sopenharmony_ci		return MODE_BAD;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	/* Compute requested pll out */
34162306a36Sopenharmony_ci	pll_out_khz = mode->clock * bpp / lanes;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	if (pll_out_khz > dsi->lane_max_kbps)
34462306a36Sopenharmony_ci		return MODE_CLOCK_HIGH;
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) {
34762306a36Sopenharmony_ci		/* Add 20% to pll out to be higher than pixel bw */
34862306a36Sopenharmony_ci		pll_out_khz = (pll_out_khz * 12) / 10;
34962306a36Sopenharmony_ci	} else {
35062306a36Sopenharmony_ci		if (pll_out_khz < dsi->lane_min_kbps)
35162306a36Sopenharmony_ci			return MODE_CLOCK_LOW;
35262306a36Sopenharmony_ci	}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_ci	/* Compute best pll parameters */
35562306a36Sopenharmony_ci	idf = 0;
35662306a36Sopenharmony_ci	ndiv = 0;
35762306a36Sopenharmony_ci	odf = 0;
35862306a36Sopenharmony_ci	pll_in_khz = clk_get_rate(dsi->pllref_clk) / 1000;
35962306a36Sopenharmony_ci	ret = dsi_pll_get_params(dsi, pll_in_khz, pll_out_khz, &idf, &ndiv, &odf);
36062306a36Sopenharmony_ci	if (ret) {
36162306a36Sopenharmony_ci		DRM_WARN("Warning dsi_pll_get_params(): bad params\n");
36262306a36Sopenharmony_ci		return MODE_ERROR;
36362306a36Sopenharmony_ci	}
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	if (!(mode_flags & MIPI_DSI_MODE_VIDEO_BURST)) {
36662306a36Sopenharmony_ci		unsigned int px_clock_hz, target_px_clock_hz, lane_mbps;
36762306a36Sopenharmony_ci		int dsi_short_packet_size_px, hfp, hsync, hbp, delay_to_lp;
36862306a36Sopenharmony_ci		struct dw_mipi_dsi_dphy_timing dphy_timing;
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci		/* Get the adjusted pll out value */
37162306a36Sopenharmony_ci		pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci		px_clock_hz = DIV_ROUND_CLOSEST_ULL(1000ULL * pll_out_khz * lanes, bpp);
37462306a36Sopenharmony_ci		target_px_clock_hz = mode->clock * 1000;
37562306a36Sopenharmony_ci		/*
37662306a36Sopenharmony_ci		 * Filter modes according to the clock value, particularly useful for
37762306a36Sopenharmony_ci		 * hdmi modes that require precise pixel clocks.
37862306a36Sopenharmony_ci		 */
37962306a36Sopenharmony_ci		if (px_clock_hz < target_px_clock_hz - CLK_TOLERANCE_HZ ||
38062306a36Sopenharmony_ci		    px_clock_hz > target_px_clock_hz + CLK_TOLERANCE_HZ)
38162306a36Sopenharmony_ci			return MODE_CLOCK_RANGE;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci		/* sync packets are codes as DSI short packets (4 bytes) */
38462306a36Sopenharmony_ci		dsi_short_packet_size_px = DIV_ROUND_UP(4 * BITS_PER_BYTE, bpp);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci		hfp = mode->hsync_start - mode->hdisplay;
38762306a36Sopenharmony_ci		hsync = mode->hsync_end - mode->hsync_start;
38862306a36Sopenharmony_ci		hbp = mode->htotal - mode->hsync_end;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci		/* hsync must be longer than 4 bytes HSS packets */
39162306a36Sopenharmony_ci		if (hsync < dsi_short_packet_size_px)
39262306a36Sopenharmony_ci			return MODE_HSYNC_NARROW;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci		if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) {
39562306a36Sopenharmony_ci			/* HBP must be longer than 4 bytes HSE packets */
39662306a36Sopenharmony_ci			if (hbp < dsi_short_packet_size_px)
39762306a36Sopenharmony_ci				return MODE_HSYNC_NARROW;
39862306a36Sopenharmony_ci			hbp -= dsi_short_packet_size_px;
39962306a36Sopenharmony_ci		} else {
40062306a36Sopenharmony_ci			/* With sync events HBP extends in the hsync */
40162306a36Sopenharmony_ci			hbp += hsync - dsi_short_packet_size_px;
40262306a36Sopenharmony_ci		}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci		lane_mbps = pll_out_khz / 1000;
40562306a36Sopenharmony_ci		ret = dw_mipi_dsi_phy_get_timing(priv_data, lane_mbps, &dphy_timing);
40662306a36Sopenharmony_ci		if (ret)
40762306a36Sopenharmony_ci			return MODE_ERROR;
40862306a36Sopenharmony_ci		/*
40962306a36Sopenharmony_ci		 * In non-burst mode DSI has to enter in LP during HFP
41062306a36Sopenharmony_ci		 * (horizontal front porch) or HBP (horizontal back porch) to
41162306a36Sopenharmony_ci		 * resync with LTDC pixel clock.
41262306a36Sopenharmony_ci		 */
41362306a36Sopenharmony_ci		delay_to_lp = DIV_ROUND_UP((dphy_timing.data_hs2lp + dphy_timing.data_lp2hs) *
41462306a36Sopenharmony_ci					   lanes * BITS_PER_BYTE, bpp);
41562306a36Sopenharmony_ci		if (hfp < delay_to_lp && hbp < delay_to_lp)
41662306a36Sopenharmony_ci			return MODE_HSYNC;
41762306a36Sopenharmony_ci	}
41862306a36Sopenharmony_ci
41962306a36Sopenharmony_ci	return MODE_OK;
42062306a36Sopenharmony_ci}
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_cistatic const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = {
42362306a36Sopenharmony_ci	.init = dw_mipi_dsi_phy_init,
42462306a36Sopenharmony_ci	.power_on = dw_mipi_dsi_phy_power_on,
42562306a36Sopenharmony_ci	.power_off = dw_mipi_dsi_phy_power_off,
42662306a36Sopenharmony_ci	.get_lane_mbps = dw_mipi_dsi_get_lane_mbps,
42762306a36Sopenharmony_ci	.get_timing = dw_mipi_dsi_phy_get_timing,
42862306a36Sopenharmony_ci};
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_cistatic struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = {
43162306a36Sopenharmony_ci	.max_data_lanes = 2,
43262306a36Sopenharmony_ci	.mode_valid = dw_mipi_dsi_stm_mode_valid,
43362306a36Sopenharmony_ci	.phy_ops = &dw_mipi_dsi_stm_phy_ops,
43462306a36Sopenharmony_ci};
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_cistatic const struct of_device_id dw_mipi_dsi_stm_dt_ids[] = {
43762306a36Sopenharmony_ci	{ .compatible = "st,stm32-dsi", .data = &dw_mipi_dsi_stm_plat_data, },
43862306a36Sopenharmony_ci	{ },
43962306a36Sopenharmony_ci};
44062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw_mipi_dsi_stm_dt_ids);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic int dw_mipi_dsi_stm_probe(struct platform_device *pdev)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct device *dev = &pdev->dev;
44562306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi;
44662306a36Sopenharmony_ci	struct clk *pclk;
44762306a36Sopenharmony_ci	int ret;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
45062306a36Sopenharmony_ci	if (!dsi)
45162306a36Sopenharmony_ci		return -ENOMEM;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	dsi->base = devm_platform_ioremap_resource(pdev, 0);
45462306a36Sopenharmony_ci	if (IS_ERR(dsi->base)) {
45562306a36Sopenharmony_ci		ret = PTR_ERR(dsi->base);
45662306a36Sopenharmony_ci		DRM_ERROR("Unable to get dsi registers %d\n", ret);
45762306a36Sopenharmony_ci		return ret;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	dsi->vdd_supply = devm_regulator_get(dev, "phy-dsi");
46162306a36Sopenharmony_ci	if (IS_ERR(dsi->vdd_supply)) {
46262306a36Sopenharmony_ci		ret = PTR_ERR(dsi->vdd_supply);
46362306a36Sopenharmony_ci		dev_err_probe(dev, ret, "Failed to request regulator\n");
46462306a36Sopenharmony_ci		return ret;
46562306a36Sopenharmony_ci	}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	ret = regulator_enable(dsi->vdd_supply);
46862306a36Sopenharmony_ci	if (ret) {
46962306a36Sopenharmony_ci		DRM_ERROR("Failed to enable regulator: %d\n", ret);
47062306a36Sopenharmony_ci		return ret;
47162306a36Sopenharmony_ci	}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci	dsi->pllref_clk = devm_clk_get(dev, "ref");
47462306a36Sopenharmony_ci	if (IS_ERR(dsi->pllref_clk)) {
47562306a36Sopenharmony_ci		ret = PTR_ERR(dsi->pllref_clk);
47662306a36Sopenharmony_ci		dev_err_probe(dev, ret, "Unable to get pll reference clock\n");
47762306a36Sopenharmony_ci		goto err_clk_get;
47862306a36Sopenharmony_ci	}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ret = clk_prepare_enable(dsi->pllref_clk);
48162306a36Sopenharmony_ci	if (ret) {
48262306a36Sopenharmony_ci		DRM_ERROR("Failed to enable pllref_clk: %d\n", ret);
48362306a36Sopenharmony_ci		goto err_clk_get;
48462306a36Sopenharmony_ci	}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	pclk = devm_clk_get(dev, "pclk");
48762306a36Sopenharmony_ci	if (IS_ERR(pclk)) {
48862306a36Sopenharmony_ci		ret = PTR_ERR(pclk);
48962306a36Sopenharmony_ci		DRM_ERROR("Unable to get peripheral clock: %d\n", ret);
49062306a36Sopenharmony_ci		goto err_dsi_probe;
49162306a36Sopenharmony_ci	}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	ret = clk_prepare_enable(pclk);
49462306a36Sopenharmony_ci	if (ret) {
49562306a36Sopenharmony_ci		DRM_ERROR("%s: Failed to enable peripheral clk\n", __func__);
49662306a36Sopenharmony_ci		goto err_dsi_probe;
49762306a36Sopenharmony_ci	}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	dsi->hw_version = dsi_read(dsi, DSI_VERSION) & VERSION;
50062306a36Sopenharmony_ci	clk_disable_unprepare(pclk);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	if (dsi->hw_version != HWVER_130 && dsi->hw_version != HWVER_131) {
50362306a36Sopenharmony_ci		ret = -ENODEV;
50462306a36Sopenharmony_ci		DRM_ERROR("bad dsi hardware version\n");
50562306a36Sopenharmony_ci		goto err_dsi_probe;
50662306a36Sopenharmony_ci	}
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_ci	/* set lane capabilities according to hw version */
50962306a36Sopenharmony_ci	dsi->lane_min_kbps = LANE_MIN_KBPS;
51062306a36Sopenharmony_ci	dsi->lane_max_kbps = LANE_MAX_KBPS;
51162306a36Sopenharmony_ci	if (dsi->hw_version == HWVER_131) {
51262306a36Sopenharmony_ci		dsi->lane_min_kbps *= 2;
51362306a36Sopenharmony_ci		dsi->lane_max_kbps *= 2;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	dw_mipi_dsi_stm_plat_data.base = dsi->base;
51762306a36Sopenharmony_ci	dw_mipi_dsi_stm_plat_data.priv_data = dsi;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	platform_set_drvdata(pdev, dsi);
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	dsi->dsi = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data);
52262306a36Sopenharmony_ci	if (IS_ERR(dsi->dsi)) {
52362306a36Sopenharmony_ci		ret = PTR_ERR(dsi->dsi);
52462306a36Sopenharmony_ci		dev_err_probe(dev, ret, "Failed to initialize mipi dsi host\n");
52562306a36Sopenharmony_ci		goto err_dsi_probe;
52662306a36Sopenharmony_ci	}
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return 0;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_cierr_dsi_probe:
53162306a36Sopenharmony_ci	clk_disable_unprepare(dsi->pllref_clk);
53262306a36Sopenharmony_cierr_clk_get:
53362306a36Sopenharmony_ci	regulator_disable(dsi->vdd_supply);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	return ret;
53662306a36Sopenharmony_ci}
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_cistatic void dw_mipi_dsi_stm_remove(struct platform_device *pdev)
53962306a36Sopenharmony_ci{
54062306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = platform_get_drvdata(pdev);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	dw_mipi_dsi_remove(dsi->dsi);
54362306a36Sopenharmony_ci	clk_disable_unprepare(dsi->pllref_clk);
54462306a36Sopenharmony_ci	regulator_disable(dsi->vdd_supply);
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_cistatic int __maybe_unused dw_mipi_dsi_stm_suspend(struct device *dev)
54862306a36Sopenharmony_ci{
54962306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	clk_disable_unprepare(dsi->pllref_clk);
55462306a36Sopenharmony_ci	regulator_disable(dsi->vdd_supply);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return 0;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic int __maybe_unused dw_mipi_dsi_stm_resume(struct device *dev)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data;
56262306a36Sopenharmony_ci	int ret;
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_ci	DRM_DEBUG_DRIVER("\n");
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_ci	ret = regulator_enable(dsi->vdd_supply);
56762306a36Sopenharmony_ci	if (ret) {
56862306a36Sopenharmony_ci		DRM_ERROR("Failed to enable regulator: %d\n", ret);
56962306a36Sopenharmony_ci		return ret;
57062306a36Sopenharmony_ci	}
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	ret = clk_prepare_enable(dsi->pllref_clk);
57362306a36Sopenharmony_ci	if (ret) {
57462306a36Sopenharmony_ci		regulator_disable(dsi->vdd_supply);
57562306a36Sopenharmony_ci		DRM_ERROR("Failed to enable pllref_clk: %d\n", ret);
57662306a36Sopenharmony_ci		return ret;
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci}
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic const struct dev_pm_ops dw_mipi_dsi_stm_pm_ops = {
58362306a36Sopenharmony_ci	SET_SYSTEM_SLEEP_PM_OPS(dw_mipi_dsi_stm_suspend,
58462306a36Sopenharmony_ci				dw_mipi_dsi_stm_resume)
58562306a36Sopenharmony_ci};
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_cistatic struct platform_driver dw_mipi_dsi_stm_driver = {
58862306a36Sopenharmony_ci	.probe		= dw_mipi_dsi_stm_probe,
58962306a36Sopenharmony_ci	.remove_new	= dw_mipi_dsi_stm_remove,
59062306a36Sopenharmony_ci	.driver		= {
59162306a36Sopenharmony_ci		.of_match_table = dw_mipi_dsi_stm_dt_ids,
59262306a36Sopenharmony_ci		.name	= "stm32-display-dsi",
59362306a36Sopenharmony_ci		.pm = &dw_mipi_dsi_stm_pm_ops,
59462306a36Sopenharmony_ci	},
59562306a36Sopenharmony_ci};
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cimodule_platform_driver(dw_mipi_dsi_stm_driver);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ciMODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>");
60062306a36Sopenharmony_ciMODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>");
60162306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics DW MIPI DSI host controller driver");
60262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
603