1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) STMicroelectronics SA 2017 4 * 5 * Authors: Philippe Cornu <philippe.cornu@st.com> 6 * Yannick Fertre <yannick.fertre@st.com> 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/iopoll.h> 11 #include <linux/module.h> 12 #include <drm/drmP.h> 13 #include <drm/drm_mipi_dsi.h> 14 #include <drm/bridge/dw_mipi_dsi.h> 15 #include <video/mipi_display.h> 16 17 /* DSI wrapper register & bit definitions */ 18 /* Note: registers are named as in the Reference Manual */ 19 #define DSI_WCFGR 0x0400 /* Wrapper ConFiGuration Reg */ 20 #define WCFGR_DSIM BIT(0) /* DSI Mode */ 21 #define WCFGR_COLMUX GENMASK(3, 1) /* COLor MUltipleXing */ 22 23 #define DSI_WCR 0x0404 /* Wrapper Control Reg */ 24 #define WCR_DSIEN BIT(3) /* DSI ENable */ 25 26 #define DSI_WISR 0x040C /* Wrapper Interrupt and Status Reg */ 27 #define WISR_PLLLS BIT(8) /* PLL Lock Status */ 28 #define WISR_RRS BIT(12) /* Regulator Ready Status */ 29 30 #define DSI_WPCR0 0x0418 /* Wrapper Phy Conf Reg 0 */ 31 #define WPCR0_UIX4 GENMASK(5, 0) /* Unit Interval X 4 */ 32 #define WPCR0_TDDL BIT(16) /* Turn Disable Data Lanes */ 33 34 #define DSI_WRPCR 0x0430 /* Wrapper Regulator & Pll Ctrl Reg */ 35 #define WRPCR_PLLEN BIT(0) /* PLL ENable */ 36 #define WRPCR_NDIV GENMASK(8, 2) /* pll loop DIVision Factor */ 37 #define WRPCR_IDF GENMASK(14, 11) /* pll Input Division Factor */ 38 #define WRPCR_ODF GENMASK(17, 16) /* pll Output Division Factor */ 39 #define WRPCR_REGEN BIT(24) /* REGulator ENable */ 40 #define WRPCR_BGREN BIT(28) /* BandGap Reference ENable */ 41 #define IDF_MIN 1 42 #define IDF_MAX 7 43 #define NDIV_MIN 10 44 #define NDIV_MAX 125 45 #define ODF_MIN 1 46 #define ODF_MAX 8 47 48 /* dsi color format coding according to the datasheet */ 49 enum dsi_color { 50 DSI_RGB565_CONF1, 51 DSI_RGB565_CONF2, 52 DSI_RGB565_CONF3, 53 DSI_RGB666_CONF1, 54 DSI_RGB666_CONF2, 55 DSI_RGB888, 56 }; 57 58 #define LANE_MIN_KBPS 31250 59 #define LANE_MAX_KBPS 500000 60 61 /* Sleep & timeout for regulator on/off, pll lock/unlock & fifo empty */ 62 #define SLEEP_US 1000 63 #define TIMEOUT_US 200000 64 65 struct dw_mipi_dsi_stm { 66 void __iomem *base; 67 struct clk *pllref_clk; 68 }; 69 70 static inline void dsi_write(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 val) 71 { 72 writel(val, dsi->base + reg); 73 } 74 75 static inline u32 dsi_read(struct dw_mipi_dsi_stm *dsi, u32 reg) 76 { 77 return readl(dsi->base + reg); 78 } 79 80 static inline void dsi_set(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask) 81 { 82 dsi_write(dsi, reg, dsi_read(dsi, reg) | mask); 83 } 84 85 static inline void dsi_clear(struct dw_mipi_dsi_stm *dsi, u32 reg, u32 mask) 86 { 87 dsi_write(dsi, reg, dsi_read(dsi, reg) & ~mask); 88 } 89 90 static inline void dsi_update_bits(struct dw_mipi_dsi_stm *dsi, u32 reg, 91 u32 mask, u32 val) 92 { 93 dsi_write(dsi, reg, (dsi_read(dsi, reg) & ~mask) | val); 94 } 95 96 static enum dsi_color dsi_color_from_mipi(enum mipi_dsi_pixel_format fmt) 97 { 98 switch (fmt) { 99 case MIPI_DSI_FMT_RGB888: 100 return DSI_RGB888; 101 case MIPI_DSI_FMT_RGB666: 102 return DSI_RGB666_CONF2; 103 case MIPI_DSI_FMT_RGB666_PACKED: 104 return DSI_RGB666_CONF1; 105 case MIPI_DSI_FMT_RGB565: 106 return DSI_RGB565_CONF1; 107 default: 108 DRM_DEBUG_DRIVER("MIPI color invalid, so we use rgb888\n"); 109 } 110 return DSI_RGB888; 111 } 112 113 static int dsi_pll_get_clkout_khz(int clkin_khz, int idf, int ndiv, int odf) 114 { 115 int divisor = idf * odf; 116 117 /* prevent from division by 0 */ 118 if (!divisor) 119 return 0; 120 121 return DIV_ROUND_CLOSEST(clkin_khz * ndiv, divisor); 122 } 123 124 static int dsi_pll_get_params(int clkin_khz, int clkout_khz, 125 int *idf, int *ndiv, int *odf) 126 { 127 int i, o, n, n_min, n_max; 128 int fvco_min, fvco_max, delta, best_delta; /* all in khz */ 129 130 /* Early checks preventing division by 0 & odd results */ 131 if (clkin_khz <= 0 || clkout_khz <= 0) 132 return -EINVAL; 133 134 fvco_min = LANE_MIN_KBPS * 2 * ODF_MAX; 135 fvco_max = LANE_MAX_KBPS * 2 * ODF_MIN; 136 137 best_delta = 1000000; /* big started value (1000000khz) */ 138 139 for (i = IDF_MIN; i <= IDF_MAX; i++) { 140 /* Compute ndiv range according to Fvco */ 141 n_min = ((fvco_min * i) / (2 * clkin_khz)) + 1; 142 n_max = (fvco_max * i) / (2 * clkin_khz); 143 144 /* No need to continue idf loop if we reach ndiv max */ 145 if (n_min >= NDIV_MAX) 146 break; 147 148 /* Clamp ndiv to valid values */ 149 if (n_min < NDIV_MIN) 150 n_min = NDIV_MIN; 151 if (n_max > NDIV_MAX) 152 n_max = NDIV_MAX; 153 154 for (o = ODF_MIN; o <= ODF_MAX; o *= 2) { 155 n = DIV_ROUND_CLOSEST(i * o * clkout_khz, clkin_khz); 156 /* Check ndiv according to vco range */ 157 if (n < n_min || n > n_max) 158 continue; 159 /* Check if new delta is better & saves parameters */ 160 delta = dsi_pll_get_clkout_khz(clkin_khz, i, n, o) - 161 clkout_khz; 162 if (delta < 0) 163 delta = -delta; 164 if (delta < best_delta) { 165 *idf = i; 166 *ndiv = n; 167 *odf = o; 168 best_delta = delta; 169 } 170 /* fast return in case of "perfect result" */ 171 if (!delta) 172 return 0; 173 } 174 } 175 176 return 0; 177 } 178 179 static int dw_mipi_dsi_phy_init(void *priv_data) 180 { 181 struct dw_mipi_dsi_stm *dsi = priv_data; 182 u32 val; 183 int ret; 184 185 /* Enable the regulator */ 186 dsi_set(dsi, DSI_WRPCR, WRPCR_REGEN | WRPCR_BGREN); 187 ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_RRS, 188 SLEEP_US, TIMEOUT_US); 189 if (ret) 190 DRM_DEBUG_DRIVER("!TIMEOUT! waiting REGU, let's continue\n"); 191 192 /* Enable the DSI PLL & wait for its lock */ 193 dsi_set(dsi, DSI_WRPCR, WRPCR_PLLEN); 194 ret = readl_poll_timeout(dsi->base + DSI_WISR, val, val & WISR_PLLLS, 195 SLEEP_US, TIMEOUT_US); 196 if (ret) 197 DRM_DEBUG_DRIVER("!TIMEOUT! waiting PLL, let's continue\n"); 198 199 /* Enable the DSI wrapper */ 200 dsi_set(dsi, DSI_WCR, WCR_DSIEN); 201 202 return 0; 203 } 204 205 static int 206 dw_mipi_dsi_get_lane_mbps(void *priv_data, struct drm_display_mode *mode, 207 unsigned long mode_flags, u32 lanes, u32 format, 208 unsigned int *lane_mbps) 209 { 210 struct dw_mipi_dsi_stm *dsi = priv_data; 211 unsigned int idf, ndiv, odf, pll_in_khz, pll_out_khz; 212 int ret, bpp; 213 u32 val; 214 215 pll_in_khz = (unsigned int)(clk_get_rate(dsi->pllref_clk) / 1000); 216 217 /* Compute requested pll out */ 218 bpp = mipi_dsi_pixel_format_to_bpp(format); 219 pll_out_khz = mode->clock * bpp / lanes; 220 /* Add 20% to pll out to be higher than pixel bw (burst mode only) */ 221 pll_out_khz = (pll_out_khz * 12) / 10; 222 if (pll_out_khz > LANE_MAX_KBPS) { 223 pll_out_khz = LANE_MAX_KBPS; 224 DRM_WARN("Warning max phy mbps is used\n"); 225 } 226 if (pll_out_khz < LANE_MIN_KBPS) { 227 pll_out_khz = LANE_MIN_KBPS; 228 DRM_WARN("Warning min phy mbps is used\n"); 229 } 230 231 /* Compute best pll parameters */ 232 idf = 0; 233 ndiv = 0; 234 odf = 0; 235 ret = dsi_pll_get_params(pll_in_khz, pll_out_khz, &idf, &ndiv, &odf); 236 if (ret) 237 DRM_WARN("Warning dsi_pll_get_params(): bad params\n"); 238 239 /* Get the adjusted pll out value */ 240 pll_out_khz = dsi_pll_get_clkout_khz(pll_in_khz, idf, ndiv, odf); 241 242 /* Set the PLL division factors */ 243 dsi_update_bits(dsi, DSI_WRPCR, WRPCR_NDIV | WRPCR_IDF | WRPCR_ODF, 244 (ndiv << 2) | (idf << 11) | ((ffs(odf) - 1) << 16)); 245 246 /* Compute uix4 & set the bit period in high-speed mode */ 247 val = 4000000 / pll_out_khz; 248 dsi_update_bits(dsi, DSI_WPCR0, WPCR0_UIX4, val); 249 250 /* Select video mode by resetting DSIM bit */ 251 dsi_clear(dsi, DSI_WCFGR, WCFGR_DSIM); 252 253 /* Select the color coding */ 254 dsi_update_bits(dsi, DSI_WCFGR, WCFGR_COLMUX, 255 dsi_color_from_mipi(format) << 1); 256 257 *lane_mbps = pll_out_khz / 1000; 258 259 DRM_DEBUG_DRIVER("pll_in %ukHz pll_out %ukHz lane_mbps %uMHz\n", 260 pll_in_khz, pll_out_khz, *lane_mbps); 261 262 return 0; 263 } 264 265 static const struct dw_mipi_dsi_phy_ops dw_mipi_dsi_stm_phy_ops = { 266 .init = dw_mipi_dsi_phy_init, 267 .get_lane_mbps = dw_mipi_dsi_get_lane_mbps, 268 }; 269 270 static struct dw_mipi_dsi_plat_data dw_mipi_dsi_stm_plat_data = { 271 .max_data_lanes = 2, 272 .phy_ops = &dw_mipi_dsi_stm_phy_ops, 273 }; 274 275 static const struct of_device_id dw_mipi_dsi_stm_dt_ids[] = { 276 { .compatible = "st,stm32-dsi", .data = &dw_mipi_dsi_stm_plat_data, }, 277 { }, 278 }; 279 MODULE_DEVICE_TABLE(of, dw_mipi_dsi_stm_dt_ids); 280 281 static int dw_mipi_dsi_stm_probe(struct platform_device *pdev) 282 { 283 struct device *dev = &pdev->dev; 284 struct dw_mipi_dsi_stm *dsi; 285 struct resource *res; 286 int ret; 287 288 dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL); 289 if (!dsi) 290 return -ENOMEM; 291 292 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 293 dsi->base = devm_ioremap_resource(dev, res); 294 if (IS_ERR(dsi->base)) { 295 DRM_ERROR("Unable to get dsi registers\n"); 296 return PTR_ERR(dsi->base); 297 } 298 299 dsi->pllref_clk = devm_clk_get(dev, "ref"); 300 if (IS_ERR(dsi->pllref_clk)) { 301 ret = PTR_ERR(dsi->pllref_clk); 302 dev_err(dev, "Unable to get pll reference clock: %d\n", ret); 303 return ret; 304 } 305 306 ret = clk_prepare_enable(dsi->pllref_clk); 307 if (ret) { 308 dev_err(dev, "%s: Failed to enable pllref_clk\n", __func__); 309 return ret; 310 } 311 312 dw_mipi_dsi_stm_plat_data.base = dsi->base; 313 dw_mipi_dsi_stm_plat_data.priv_data = dsi; 314 315 ret = dw_mipi_dsi_probe(pdev, &dw_mipi_dsi_stm_plat_data); 316 if (ret) { 317 DRM_ERROR("Failed to initialize mipi dsi host\n"); 318 clk_disable_unprepare(dsi->pllref_clk); 319 } 320 321 return ret; 322 } 323 324 static int dw_mipi_dsi_stm_remove(struct platform_device *pdev) 325 { 326 struct dw_mipi_dsi_stm *dsi = dw_mipi_dsi_stm_plat_data.priv_data; 327 328 clk_disable_unprepare(dsi->pllref_clk); 329 dw_mipi_dsi_remove(pdev); 330 331 return 0; 332 } 333 334 static struct platform_driver dw_mipi_dsi_stm_driver = { 335 .probe = dw_mipi_dsi_stm_probe, 336 .remove = dw_mipi_dsi_stm_remove, 337 .driver = { 338 .of_match_table = dw_mipi_dsi_stm_dt_ids, 339 .name = "stm32-display-dsi", 340 }, 341 }; 342 343 module_platform_driver(dw_mipi_dsi_stm_driver); 344 345 MODULE_AUTHOR("Philippe Cornu <philippe.cornu@st.com>"); 346 MODULE_AUTHOR("Yannick Fertre <yannick.fertre@st.com>"); 347 MODULE_DESCRIPTION("STMicroelectronics DW MIPI DSI host controller driver"); 348 MODULE_LICENSE("GPL v2"); 349