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