| 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Copyright (c) 2024, Fuzhou Rockchip Electronics Co., Ltd |
| 4 | * |
| 5 | * Modified by Heiko Stuebner <heiko.stuebner@cherry.de> |
| 6 | * This generic Synopsys DesignWare MIPI DSI2 host driver is based on the |
| 7 | * Rockchip version from rockchip/dw-mipi-dsi2.c converted to use bridge APIs. |
| 8 | */ |
| 9 | |
| 10 | #include <linux/bitfield.h> |
| 11 | #include <linux/clk.h> |
| 12 | #include <linux/export.h> |
| 13 | #include <linux/iopoll.h> |
| 14 | #include <linux/media-bus-format.h> |
| 15 | #include <linux/module.h> |
| 16 | #include <linux/platform_device.h> |
| 17 | #include <linux/pm_runtime.h> |
| 18 | #include <linux/reset.h> |
| 19 | |
| 20 | #include <video/mipi_display.h> |
| 21 | |
| 22 | #include <drm/bridge/dw_mipi_dsi2.h> |
| 23 | #include <drm/drm_atomic_helper.h> |
| 24 | #include <drm/drm_bridge.h> |
| 25 | #include <drm/drm_mipi_dsi.h> |
| 26 | #include <drm/drm_of.h> |
| 27 | #include <drm/drm_print.h> |
| 28 | |
| 29 | #define DSI2_PWR_UP 0x000c |
| 30 | #define RESET 0 |
| 31 | #define POWER_UP BIT(0) |
| 32 | #define CMD_TX_MODE(x) FIELD_PREP(BIT(24), x) |
| 33 | #define DSI2_SOFT_RESET 0x0010 |
| 34 | #define SYS_RSTN BIT(2) |
| 35 | #define PHY_RSTN BIT(1) |
| 36 | #define IPI_RSTN BIT(0) |
| 37 | #define INT_ST_MAIN 0x0014 |
| 38 | #define DSI2_MODE_CTRL 0x0018 |
| 39 | #define DSI2_MODE_STATUS 0x001c |
| 40 | #define DSI2_CORE_STATUS 0x0020 |
| 41 | #define PRI_RD_DATA_AVAIL BIT(26) |
| 42 | #define PRI_FIFOS_NOT_EMPTY BIT(25) |
| 43 | #define PRI_BUSY BIT(24) |
| 44 | #define CRI_RD_DATA_AVAIL BIT(18) |
| 45 | #define CRT_FIFOS_NOT_EMPTY BIT(17) |
| 46 | #define CRI_BUSY BIT(16) |
| 47 | #define IPI_FIFOS_NOT_EMPTY BIT(9) |
| 48 | #define IPI_BUSY BIT(8) |
| 49 | #define CORE_FIFOS_NOT_EMPTY BIT(1) |
| 50 | #define CORE_BUSY BIT(0) |
| 51 | #define MANUAL_MODE_CFG 0x0024 |
| 52 | #define MANUAL_MODE_EN BIT(0) |
| 53 | #define DSI2_TIMEOUT_HSTX_CFG 0x0048 |
| 54 | #define TO_HSTX(x) FIELD_PREP(GENMASK(15, 0), x) |
| 55 | #define DSI2_TIMEOUT_HSTXRDY_CFG 0x004c |
| 56 | #define TO_HSTXRDY(x) FIELD_PREP(GENMASK(15, 0), x) |
| 57 | #define DSI2_TIMEOUT_LPRX_CFG 0x0050 |
| 58 | #define TO_LPRXRDY(x) FIELD_PREP(GENMASK(15, 0), x) |
| 59 | #define DSI2_TIMEOUT_LPTXRDY_CFG 0x0054 |
| 60 | #define TO_LPTXRDY(x) FIELD_PREP(GENMASK(15, 0), x) |
| 61 | #define DSI2_TIMEOUT_LPTXTRIG_CFG 0x0058 |
| 62 | #define TO_LPTXTRIG(x) FIELD_PREP(GENMASK(15, 0), x) |
| 63 | #define DSI2_TIMEOUT_LPTXULPS_CFG 0x005c |
| 64 | #define TO_LPTXULPS(x) FIELD_PREP(GENMASK(15, 0), x) |
| 65 | #define DSI2_TIMEOUT_BTA_CFG 0x60 |
| 66 | #define TO_BTA(x) FIELD_PREP(GENMASK(15, 0), x) |
| 67 | |
| 68 | #define DSI2_PHY_MODE_CFG 0x0100 |
| 69 | #define PPI_WIDTH(x) FIELD_PREP(GENMASK(9, 8), x) |
| 70 | #define PHY_LANES(x) FIELD_PREP(GENMASK(5, 4), (x) - 1) |
| 71 | #define PHY_TYPE(x) FIELD_PREP(BIT(0), x) |
| 72 | #define DSI2_PHY_CLK_CFG 0X0104 |
| 73 | #define PHY_LPTX_CLK_DIV(x) FIELD_PREP(GENMASK(12, 8), x) |
| 74 | #define CLK_TYPE_MASK BIT(0) |
| 75 | #define NON_CONTINUOUS_CLK BIT(0) |
| 76 | #define CONTINUOUS_CLK 0 |
| 77 | #define DSI2_PHY_LP2HS_MAN_CFG 0x010c |
| 78 | #define PHY_LP2HS_TIME(x) FIELD_PREP(GENMASK(28, 0), x) |
| 79 | #define DSI2_PHY_HS2LP_MAN_CFG 0x0114 |
| 80 | #define PHY_HS2LP_TIME(x) FIELD_PREP(GENMASK(28, 0), x) |
| 81 | #define DSI2_PHY_MAX_RD_T_MAN_CFG 0x011c |
| 82 | #define PHY_MAX_RD_TIME(x) FIELD_PREP(GENMASK(26, 0), x) |
| 83 | #define DSI2_PHY_ESC_CMD_T_MAN_CFG 0x0124 |
| 84 | #define PHY_ESC_CMD_TIME(x) FIELD_PREP(GENMASK(28, 0), x) |
| 85 | #define DSI2_PHY_ESC_BYTE_T_MAN_CFG 0x012c |
| 86 | #define PHY_ESC_BYTE_TIME(x) FIELD_PREP(GENMASK(28, 0), x) |
| 87 | |
| 88 | #define DSI2_PHY_IPI_RATIO_MAN_CFG 0x0134 |
| 89 | #define PHY_IPI_RATIO(x) FIELD_PREP(GENMASK(21, 0), x) |
| 90 | #define DSI2_PHY_SYS_RATIO_MAN_CFG 0x013C |
| 91 | #define PHY_SYS_RATIO(x) FIELD_PREP(GENMASK(16, 0), x) |
| 92 | |
| 93 | #define DSI2_DSI_GENERAL_CFG 0x0200 |
| 94 | #define BTA_EN BIT(1) |
| 95 | #define EOTP_TX_EN BIT(0) |
| 96 | #define DSI2_DSI_VCID_CFG 0x0204 |
| 97 | #define TX_VCID(x) FIELD_PREP(GENMASK(1, 0), x) |
| 98 | #define DSI2_DSI_SCRAMBLING_CFG 0x0208 |
| 99 | #define SCRAMBLING_SEED(x) FIELD_PREP(GENMASK(31, 16), x) |
| 100 | #define SCRAMBLING_EN BIT(0) |
| 101 | #define DSI2_DSI_VID_TX_CFG 0x020c |
| 102 | #define LPDT_DISPLAY_CMD_EN BIT(20) |
| 103 | #define BLK_VFP_HS_EN BIT(14) |
| 104 | #define BLK_VBP_HS_EN BIT(13) |
| 105 | #define BLK_VSA_HS_EN BIT(12) |
| 106 | #define BLK_HFP_HS_EN BIT(6) |
| 107 | #define BLK_HBP_HS_EN BIT(5) |
| 108 | #define BLK_HSA_HS_EN BIT(4) |
| 109 | #define VID_MODE_TYPE(x) FIELD_PREP(GENMASK(1, 0), x) |
| 110 | #define DSI2_CRI_TX_HDR 0x02c0 |
| 111 | #define CMD_TX_MODE(x) FIELD_PREP(BIT(24), x) |
| 112 | #define DSI2_CRI_TX_PLD 0x02c4 |
| 113 | #define DSI2_CRI_RX_HDR 0x02c8 |
| 114 | #define DSI2_CRI_RX_PLD 0x02cc |
| 115 | |
| 116 | #define DSI2_IPI_COLOR_MAN_CFG 0x0300 |
| 117 | #define IPI_DEPTH(x) FIELD_PREP(GENMASK(7, 4), x) |
| 118 | #define IPI_DEPTH_5_6_5_BITS 0x02 |
| 119 | #define IPI_DEPTH_6_BITS 0x03 |
| 120 | #define IPI_DEPTH_8_BITS 0x05 |
| 121 | #define IPI_DEPTH_10_BITS 0x06 |
| 122 | #define IPI_FORMAT(x) FIELD_PREP(GENMASK(3, 0), x) |
| 123 | #define IPI_FORMAT_RGB 0x0 |
| 124 | #define IPI_FORMAT_DSC 0x0b |
| 125 | #define DSI2_IPI_VID_HSA_MAN_CFG 0x0304 |
| 126 | #define VID_HSA_TIME(x) FIELD_PREP(GENMASK(29, 0), x) |
| 127 | #define DSI2_IPI_VID_HBP_MAN_CFG 0x030c |
| 128 | #define VID_HBP_TIME(x) FIELD_PREP(GENMASK(29, 0), x) |
| 129 | #define DSI2_IPI_VID_HACT_MAN_CFG 0x0314 |
| 130 | #define VID_HACT_TIME(x) FIELD_PREP(GENMASK(29, 0), x) |
| 131 | #define DSI2_IPI_VID_HLINE_MAN_CFG 0x031c |
| 132 | #define VID_HLINE_TIME(x) FIELD_PREP(GENMASK(29, 0), x) |
| 133 | #define DSI2_IPI_VID_VSA_MAN_CFG 0x0324 |
| 134 | #define VID_VSA_LINES(x) FIELD_PREP(GENMASK(9, 0), x) |
| 135 | #define DSI2_IPI_VID_VBP_MAN_CFG 0X032C |
| 136 | #define VID_VBP_LINES(x) FIELD_PREP(GENMASK(9, 0), x) |
| 137 | #define DSI2_IPI_VID_VACT_MAN_CFG 0X0334 |
| 138 | #define VID_VACT_LINES(x) FIELD_PREP(GENMASK(13, 0), x) |
| 139 | #define DSI2_IPI_VID_VFP_MAN_CFG 0X033C |
| 140 | #define VID_VFP_LINES(x) FIELD_PREP(GENMASK(9, 0), x) |
| 141 | #define DSI2_IPI_PIX_PKT_CFG 0x0344 |
| 142 | #define MAX_PIX_PKT(x) FIELD_PREP(GENMASK(15, 0), x) |
| 143 | |
| 144 | #define DSI2_INT_ST_PHY 0x0400 |
| 145 | #define DSI2_INT_MASK_PHY 0x0404 |
| 146 | #define DSI2_INT_ST_TO 0x0410 |
| 147 | #define DSI2_INT_MASK_TO 0x0414 |
| 148 | #define DSI2_INT_ST_ACK 0x0420 |
| 149 | #define DSI2_INT_MASK_ACK 0x0424 |
| 150 | #define DSI2_INT_ST_IPI 0x0430 |
| 151 | #define DSI2_INT_MASK_IPI 0x0434 |
| 152 | #define DSI2_INT_ST_FIFO 0x0440 |
| 153 | #define DSI2_INT_MASK_FIFO 0x0444 |
| 154 | #define DSI2_INT_ST_PRI 0x0450 |
| 155 | #define DSI2_INT_MASK_PRI 0x0454 |
| 156 | #define DSI2_INT_ST_CRI 0x0460 |
| 157 | #define DSI2_INT_MASK_CRI 0x0464 |
| 158 | #define DSI2_INT_FORCE_CRI 0x0468 |
| 159 | #define DSI2_MAX_REGISGER DSI2_INT_FORCE_CRI |
| 160 | |
| 161 | #define MODE_STATUS_TIMEOUT_US 10000 |
| 162 | #define CMD_PKT_STATUS_TIMEOUT_US 20000 |
| 163 | |
| 164 | enum vid_mode_type { |
| 165 | VID_MODE_TYPE_NON_BURST_SYNC_PULSES, |
| 166 | VID_MODE_TYPE_NON_BURST_SYNC_EVENTS, |
| 167 | VID_MODE_TYPE_BURST, |
| 168 | }; |
| 169 | |
| 170 | enum mode_ctrl { |
| 171 | IDLE_MODE, |
| 172 | AUTOCALC_MODE, |
| 173 | COMMAND_MODE, |
| 174 | VIDEO_MODE, |
| 175 | DATA_STREAM_MODE, |
| 176 | VIDEO_TEST_MODE, |
| 177 | DATA_STREAM_TEST_MODE, |
| 178 | }; |
| 179 | |
| 180 | enum ppi_width { |
| 181 | PPI_WIDTH_8_BITS, |
| 182 | PPI_WIDTH_16_BITS, |
| 183 | PPI_WIDTH_32_BITS, |
| 184 | }; |
| 185 | |
| 186 | struct { |
| 187 | u8 ; |
| 188 | u8 ; |
| 189 | u8 ; |
| 190 | }; |
| 191 | |
| 192 | struct dw_mipi_dsi2 { |
| 193 | struct drm_bridge bridge; |
| 194 | struct mipi_dsi_host dsi_host; |
| 195 | struct drm_bridge *panel_bridge; |
| 196 | struct device *dev; |
| 197 | struct regmap *regmap; |
| 198 | struct clk *pclk; |
| 199 | struct clk *sys_clk; |
| 200 | |
| 201 | unsigned int lane_mbps; /* per lane */ |
| 202 | u32 channel; |
| 203 | u32 lanes; |
| 204 | u32 format; |
| 205 | unsigned long mode_flags; |
| 206 | |
| 207 | struct drm_display_mode mode; |
| 208 | const struct dw_mipi_dsi2_plat_data *plat_data; |
| 209 | }; |
| 210 | |
| 211 | static inline struct dw_mipi_dsi2 *host_to_dsi2(struct mipi_dsi_host *host) |
| 212 | { |
| 213 | return container_of(host, struct dw_mipi_dsi2, dsi_host); |
| 214 | } |
| 215 | |
| 216 | static inline struct dw_mipi_dsi2 *bridge_to_dsi2(struct drm_bridge *bridge) |
| 217 | { |
| 218 | return container_of(bridge, struct dw_mipi_dsi2, bridge); |
| 219 | } |
| 220 | |
| 221 | static int cri_fifos_wait_avail(struct dw_mipi_dsi2 *dsi2) |
| 222 | { |
| 223 | u32 sts, mask; |
| 224 | int ret; |
| 225 | |
| 226 | mask = CRI_BUSY | CRT_FIFOS_NOT_EMPTY; |
| 227 | ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_CORE_STATUS, sts, |
| 228 | !(sts & mask), 0, CMD_PKT_STATUS_TIMEOUT_US); |
| 229 | if (ret < 0) { |
| 230 | dev_err(dsi2->dev, "command interface is busy\n" ); |
| 231 | return ret; |
| 232 | } |
| 233 | |
| 234 | return 0; |
| 235 | } |
| 236 | |
| 237 | static void dw_mipi_dsi2_set_vid_mode(struct dw_mipi_dsi2 *dsi2) |
| 238 | { |
| 239 | u32 val = 0, mode; |
| 240 | int ret; |
| 241 | |
| 242 | if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HFP) |
| 243 | val |= BLK_HFP_HS_EN; |
| 244 | |
| 245 | if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HBP) |
| 246 | val |= BLK_HBP_HS_EN; |
| 247 | |
| 248 | if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_NO_HSA) |
| 249 | val |= BLK_HSA_HS_EN; |
| 250 | |
| 251 | if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_BURST) |
| 252 | val |= VID_MODE_TYPE_BURST; |
| 253 | else if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) |
| 254 | val |= VID_MODE_TYPE_NON_BURST_SYNC_PULSES; |
| 255 | else |
| 256 | val |= VID_MODE_TYPE_NON_BURST_SYNC_EVENTS; |
| 257 | |
| 258 | regmap_write(map: dsi2->regmap, DSI2_DSI_VID_TX_CFG, val); |
| 259 | |
| 260 | regmap_write(map: dsi2->regmap, DSI2_MODE_CTRL, val: VIDEO_MODE); |
| 261 | ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_MODE_STATUS, |
| 262 | mode, mode & VIDEO_MODE, |
| 263 | 1000, MODE_STATUS_TIMEOUT_US); |
| 264 | if (ret < 0) |
| 265 | dev_err(dsi2->dev, "failed to enter video mode\n" ); |
| 266 | } |
| 267 | |
| 268 | static void dw_mipi_dsi2_set_data_stream_mode(struct dw_mipi_dsi2 *dsi2) |
| 269 | { |
| 270 | u32 mode; |
| 271 | int ret; |
| 272 | |
| 273 | regmap_write(map: dsi2->regmap, DSI2_MODE_CTRL, val: DATA_STREAM_MODE); |
| 274 | ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_MODE_STATUS, |
| 275 | mode, mode & DATA_STREAM_MODE, |
| 276 | 1000, MODE_STATUS_TIMEOUT_US); |
| 277 | if (ret < 0) |
| 278 | dev_err(dsi2->dev, "failed to enter data stream mode\n" ); |
| 279 | } |
| 280 | |
| 281 | static void dw_mipi_dsi2_set_cmd_mode(struct dw_mipi_dsi2 *dsi2) |
| 282 | { |
| 283 | u32 mode; |
| 284 | int ret; |
| 285 | |
| 286 | regmap_write(map: dsi2->regmap, DSI2_MODE_CTRL, val: COMMAND_MODE); |
| 287 | ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_MODE_STATUS, |
| 288 | mode, mode & COMMAND_MODE, |
| 289 | 1000, MODE_STATUS_TIMEOUT_US); |
| 290 | if (ret < 0) |
| 291 | dev_err(dsi2->dev, "failed to enter data stream mode\n" ); |
| 292 | } |
| 293 | |
| 294 | static void dw_mipi_dsi2_host_softrst(struct dw_mipi_dsi2 *dsi2) |
| 295 | { |
| 296 | regmap_write(map: dsi2->regmap, DSI2_SOFT_RESET, val: 0x0); |
| 297 | usleep_range(min: 50, max: 100); |
| 298 | regmap_write(map: dsi2->regmap, DSI2_SOFT_RESET, |
| 299 | SYS_RSTN | PHY_RSTN | IPI_RSTN); |
| 300 | } |
| 301 | |
| 302 | static void dw_mipi_dsi2_phy_clk_mode_cfg(struct dw_mipi_dsi2 *dsi2) |
| 303 | { |
| 304 | u32 sys_clk, esc_clk_div; |
| 305 | u32 val = 0; |
| 306 | |
| 307 | /* |
| 308 | * clk_type should be NON_CONTINUOUS_CLK before |
| 309 | * initial deskew calibration be sent. |
| 310 | */ |
| 311 | val |= NON_CONTINUOUS_CLK; |
| 312 | |
| 313 | /* The maximum value of the escape clock frequency is 20MHz */ |
| 314 | sys_clk = clk_get_rate(clk: dsi2->sys_clk) / USEC_PER_SEC; |
| 315 | esc_clk_div = DIV_ROUND_UP(sys_clk, 20 * 2); |
| 316 | val |= PHY_LPTX_CLK_DIV(esc_clk_div); |
| 317 | |
| 318 | regmap_write(map: dsi2->regmap, DSI2_PHY_CLK_CFG, val); |
| 319 | } |
| 320 | |
| 321 | static void dw_mipi_dsi2_phy_ratio_cfg(struct dw_mipi_dsi2 *dsi2) |
| 322 | { |
| 323 | struct drm_display_mode *mode = &dsi2->mode; |
| 324 | u64 sys_clk = clk_get_rate(clk: dsi2->sys_clk); |
| 325 | u64 pixel_clk, ipi_clk, phy_hsclk; |
| 326 | u64 tmp; |
| 327 | |
| 328 | /* |
| 329 | * in DPHY mode, the phy_hstx_clk is exactly 1/16 the Lane high-speed |
| 330 | * data rate; In CPHY mode, the phy_hstx_clk is exactly 1/7 the trio |
| 331 | * high speed symbol rate. |
| 332 | */ |
| 333 | phy_hsclk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16); |
| 334 | |
| 335 | /* IPI_RATIO_MAN_CFG = PHY_HSTX_CLK / IPI_CLK */ |
| 336 | pixel_clk = mode->crtc_clock * MSEC_PER_SEC; |
| 337 | ipi_clk = pixel_clk / 4; |
| 338 | |
| 339 | tmp = DIV_ROUND_CLOSEST_ULL(phy_hsclk << 16, ipi_clk); |
| 340 | regmap_write(map: dsi2->regmap, DSI2_PHY_IPI_RATIO_MAN_CFG, |
| 341 | PHY_IPI_RATIO(tmp)); |
| 342 | |
| 343 | /* |
| 344 | * SYS_RATIO_MAN_CFG = MIPI_DCPHY_HSCLK_Freq / MIPI_DCPHY_HSCLK_Freq |
| 345 | */ |
| 346 | tmp = DIV_ROUND_CLOSEST_ULL(phy_hsclk << 16, sys_clk); |
| 347 | regmap_write(map: dsi2->regmap, DSI2_PHY_SYS_RATIO_MAN_CFG, |
| 348 | PHY_SYS_RATIO(tmp)); |
| 349 | } |
| 350 | |
| 351 | static void dw_mipi_dsi2_lp2hs_or_hs2lp_cfg(struct dw_mipi_dsi2 *dsi2) |
| 352 | { |
| 353 | const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; |
| 354 | struct dw_mipi_dsi2_phy_timing timing; |
| 355 | int ret; |
| 356 | |
| 357 | ret = phy_ops->get_timing(dsi2->plat_data->priv_data, |
| 358 | dsi2->lane_mbps, &timing); |
| 359 | if (ret) |
| 360 | dev_err(dsi2->dev, "Retrieving phy timings failed\n" ); |
| 361 | |
| 362 | regmap_write(map: dsi2->regmap, DSI2_PHY_LP2HS_MAN_CFG, PHY_LP2HS_TIME(timing.data_lp2hs)); |
| 363 | regmap_write(map: dsi2->regmap, DSI2_PHY_HS2LP_MAN_CFG, PHY_HS2LP_TIME(timing.data_hs2lp)); |
| 364 | } |
| 365 | |
| 366 | static void dw_mipi_dsi2_phy_init(struct dw_mipi_dsi2 *dsi2) |
| 367 | { |
| 368 | const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; |
| 369 | struct dw_mipi_dsi2_phy_iface iface; |
| 370 | u32 val = 0; |
| 371 | |
| 372 | phy_ops->get_interface(dsi2->plat_data->priv_data, &iface); |
| 373 | |
| 374 | switch (iface.ppi_width) { |
| 375 | case 8: |
| 376 | val |= PPI_WIDTH(PPI_WIDTH_8_BITS); |
| 377 | break; |
| 378 | case 16: |
| 379 | val |= PPI_WIDTH(PPI_WIDTH_16_BITS); |
| 380 | break; |
| 381 | case 32: |
| 382 | val |= PPI_WIDTH(PPI_WIDTH_32_BITS); |
| 383 | break; |
| 384 | default: |
| 385 | /* Caught in probe */ |
| 386 | break; |
| 387 | } |
| 388 | |
| 389 | val |= PHY_LANES(dsi2->lanes); |
| 390 | val |= PHY_TYPE(DW_MIPI_DSI2_DPHY); |
| 391 | regmap_write(map: dsi2->regmap, DSI2_PHY_MODE_CFG, val); |
| 392 | |
| 393 | dw_mipi_dsi2_phy_clk_mode_cfg(dsi2); |
| 394 | dw_mipi_dsi2_phy_ratio_cfg(dsi2); |
| 395 | dw_mipi_dsi2_lp2hs_or_hs2lp_cfg(dsi2); |
| 396 | |
| 397 | /* phy configuration 8 - 10 */ |
| 398 | } |
| 399 | |
| 400 | static void dw_mipi_dsi2_tx_option_set(struct dw_mipi_dsi2 *dsi2) |
| 401 | { |
| 402 | u32 val; |
| 403 | |
| 404 | val = BTA_EN | EOTP_TX_EN; |
| 405 | |
| 406 | if (dsi2->mode_flags & MIPI_DSI_MODE_NO_EOT_PACKET) |
| 407 | val &= ~EOTP_TX_EN; |
| 408 | |
| 409 | regmap_write(map: dsi2->regmap, DSI2_DSI_GENERAL_CFG, val); |
| 410 | regmap_write(map: dsi2->regmap, DSI2_DSI_VCID_CFG, TX_VCID(dsi2->channel)); |
| 411 | } |
| 412 | |
| 413 | static void dw_mipi_dsi2_ipi_color_coding_cfg(struct dw_mipi_dsi2 *dsi2) |
| 414 | { |
| 415 | u32 val, color_depth; |
| 416 | |
| 417 | switch (dsi2->format) { |
| 418 | case MIPI_DSI_FMT_RGB666: |
| 419 | case MIPI_DSI_FMT_RGB666_PACKED: |
| 420 | color_depth = IPI_DEPTH_6_BITS; |
| 421 | break; |
| 422 | case MIPI_DSI_FMT_RGB565: |
| 423 | color_depth = IPI_DEPTH_5_6_5_BITS; |
| 424 | break; |
| 425 | case MIPI_DSI_FMT_RGB888: |
| 426 | default: |
| 427 | color_depth = IPI_DEPTH_8_BITS; |
| 428 | break; |
| 429 | } |
| 430 | |
| 431 | val = IPI_DEPTH(color_depth) | |
| 432 | IPI_FORMAT(IPI_FORMAT_RGB); |
| 433 | regmap_write(map: dsi2->regmap, DSI2_IPI_COLOR_MAN_CFG, val); |
| 434 | } |
| 435 | |
| 436 | static void dw_mipi_dsi2_vertical_timing_config(struct dw_mipi_dsi2 *dsi2, |
| 437 | const struct drm_display_mode *mode) |
| 438 | { |
| 439 | u32 vactive, vsa, vfp, vbp; |
| 440 | |
| 441 | vactive = mode->vdisplay; |
| 442 | vsa = mode->vsync_end - mode->vsync_start; |
| 443 | vfp = mode->vsync_start - mode->vdisplay; |
| 444 | vbp = mode->vtotal - mode->vsync_end; |
| 445 | |
| 446 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_VSA_MAN_CFG, VID_VSA_LINES(vsa)); |
| 447 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_VBP_MAN_CFG, VID_VBP_LINES(vbp)); |
| 448 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_VACT_MAN_CFG, VID_VACT_LINES(vactive)); |
| 449 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_VFP_MAN_CFG, VID_VFP_LINES(vfp)); |
| 450 | } |
| 451 | |
| 452 | static void dw_mipi_dsi2_ipi_set(struct dw_mipi_dsi2 *dsi2) |
| 453 | { |
| 454 | struct drm_display_mode *mode = &dsi2->mode; |
| 455 | u32 hline, hsa, hbp, hact; |
| 456 | u64 hline_time, hsa_time, hbp_time, hact_time, tmp; |
| 457 | u64 pixel_clk, phy_hs_clk; |
| 458 | u16 val; |
| 459 | |
| 460 | val = mode->hdisplay; |
| 461 | |
| 462 | regmap_write(map: dsi2->regmap, DSI2_IPI_PIX_PKT_CFG, MAX_PIX_PKT(val)); |
| 463 | |
| 464 | dw_mipi_dsi2_ipi_color_coding_cfg(dsi2); |
| 465 | |
| 466 | /* |
| 467 | * if the controller is intended to operate in data stream mode, |
| 468 | * no more steps are required. |
| 469 | */ |
| 470 | if (!(dsi2->mode_flags & MIPI_DSI_MODE_VIDEO)) |
| 471 | return; |
| 472 | |
| 473 | hact = mode->hdisplay; |
| 474 | hsa = mode->hsync_end - mode->hsync_start; |
| 475 | hbp = mode->htotal - mode->hsync_end; |
| 476 | hline = mode->htotal; |
| 477 | |
| 478 | pixel_clk = mode->crtc_clock * MSEC_PER_SEC; |
| 479 | |
| 480 | phy_hs_clk = DIV_ROUND_CLOSEST_ULL(dsi2->lane_mbps * USEC_PER_SEC, 16); |
| 481 | |
| 482 | tmp = hsa * phy_hs_clk; |
| 483 | hsa_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); |
| 484 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_HSA_MAN_CFG, VID_HSA_TIME(hsa_time)); |
| 485 | |
| 486 | tmp = hbp * phy_hs_clk; |
| 487 | hbp_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); |
| 488 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_HBP_MAN_CFG, VID_HBP_TIME(hbp_time)); |
| 489 | |
| 490 | tmp = hact * phy_hs_clk; |
| 491 | hact_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); |
| 492 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_HACT_MAN_CFG, VID_HACT_TIME(hact_time)); |
| 493 | |
| 494 | tmp = hline * phy_hs_clk; |
| 495 | hline_time = DIV_ROUND_CLOSEST_ULL(tmp << 16, pixel_clk); |
| 496 | regmap_write(map: dsi2->regmap, DSI2_IPI_VID_HLINE_MAN_CFG, VID_HLINE_TIME(hline_time)); |
| 497 | |
| 498 | dw_mipi_dsi2_vertical_timing_config(dsi2, mode); |
| 499 | } |
| 500 | |
| 501 | static void |
| 502 | dw_mipi_dsi2_work_mode(struct dw_mipi_dsi2 *dsi2, u32 mode) |
| 503 | { |
| 504 | /* |
| 505 | * select controller work in Manual mode |
| 506 | * Manual: MANUAL_MODE_EN |
| 507 | * Automatic: 0 |
| 508 | */ |
| 509 | regmap_write(map: dsi2->regmap, MANUAL_MODE_CFG, val: mode); |
| 510 | } |
| 511 | |
| 512 | static int dw_mipi_dsi2_host_attach(struct mipi_dsi_host *host, |
| 513 | struct mipi_dsi_device *device) |
| 514 | { |
| 515 | struct dw_mipi_dsi2 *dsi2 = host_to_dsi2(host); |
| 516 | const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; |
| 517 | struct drm_bridge *bridge; |
| 518 | int ret; |
| 519 | |
| 520 | if (device->lanes > dsi2->plat_data->max_data_lanes) { |
| 521 | dev_err(dsi2->dev, "the number of data lanes(%u) is too many\n" , |
| 522 | device->lanes); |
| 523 | return -EINVAL; |
| 524 | } |
| 525 | |
| 526 | dsi2->lanes = device->lanes; |
| 527 | dsi2->channel = device->channel; |
| 528 | dsi2->format = device->format; |
| 529 | dsi2->mode_flags = device->mode_flags; |
| 530 | |
| 531 | bridge = devm_drm_of_get_bridge(dev: dsi2->dev, node: dsi2->dev->of_node, port: 1, endpoint: 0); |
| 532 | if (IS_ERR(ptr: bridge)) |
| 533 | return PTR_ERR(ptr: bridge); |
| 534 | |
| 535 | bridge->pre_enable_prev_first = true; |
| 536 | dsi2->panel_bridge = bridge; |
| 537 | |
| 538 | drm_bridge_add(bridge: &dsi2->bridge); |
| 539 | |
| 540 | if (pdata->host_ops && pdata->host_ops->attach) { |
| 541 | ret = pdata->host_ops->attach(pdata->priv_data, device); |
| 542 | if (ret < 0) |
| 543 | return ret; |
| 544 | } |
| 545 | |
| 546 | return 0; |
| 547 | } |
| 548 | |
| 549 | static int dw_mipi_dsi2_host_detach(struct mipi_dsi_host *host, |
| 550 | struct mipi_dsi_device *device) |
| 551 | { |
| 552 | struct dw_mipi_dsi2 *dsi2 = host_to_dsi2(host); |
| 553 | const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; |
| 554 | int ret; |
| 555 | |
| 556 | if (pdata->host_ops && pdata->host_ops->detach) { |
| 557 | ret = pdata->host_ops->detach(pdata->priv_data, device); |
| 558 | if (ret < 0) |
| 559 | return ret; |
| 560 | } |
| 561 | |
| 562 | drm_bridge_remove(bridge: &dsi2->bridge); |
| 563 | |
| 564 | drm_of_panel_bridge_remove(np: host->dev->of_node, port: 1, endpoint: 0); |
| 565 | |
| 566 | return 0; |
| 567 | } |
| 568 | |
| 569 | static int dw_mipi_dsi2_gen_pkt_hdr_write(struct dw_mipi_dsi2 *dsi2, |
| 570 | u32 hdr_val, bool lpm) |
| 571 | { |
| 572 | int ret; |
| 573 | |
| 574 | regmap_write(map: dsi2->regmap, DSI2_CRI_TX_HDR, val: hdr_val | CMD_TX_MODE(lpm)); |
| 575 | |
| 576 | ret = cri_fifos_wait_avail(dsi2); |
| 577 | if (ret) { |
| 578 | dev_err(dsi2->dev, "failed to write command header\n" ); |
| 579 | return ret; |
| 580 | } |
| 581 | |
| 582 | return 0; |
| 583 | } |
| 584 | |
| 585 | static int dw_mipi_dsi2_write(struct dw_mipi_dsi2 *dsi2, |
| 586 | const struct mipi_dsi_packet *packet, bool lpm) |
| 587 | { |
| 588 | const u8 *tx_buf = packet->payload; |
| 589 | int len = packet->payload_length, pld_data_bytes = sizeof(u32); |
| 590 | __le32 word; |
| 591 | |
| 592 | /* Send payload */ |
| 593 | while (len) { |
| 594 | if (len < pld_data_bytes) { |
| 595 | word = 0; |
| 596 | memcpy(&word, tx_buf, len); |
| 597 | regmap_write(map: dsi2->regmap, DSI2_CRI_TX_PLD, le32_to_cpu(word)); |
| 598 | len = 0; |
| 599 | } else { |
| 600 | memcpy(&word, tx_buf, pld_data_bytes); |
| 601 | regmap_write(map: dsi2->regmap, DSI2_CRI_TX_PLD, le32_to_cpu(word)); |
| 602 | tx_buf += pld_data_bytes; |
| 603 | len -= pld_data_bytes; |
| 604 | } |
| 605 | } |
| 606 | |
| 607 | word = 0; |
| 608 | memcpy(&word, packet->header, sizeof(packet->header)); |
| 609 | return dw_mipi_dsi2_gen_pkt_hdr_write(dsi2, le32_to_cpu(word), lpm); |
| 610 | } |
| 611 | |
| 612 | static int dw_mipi_dsi2_read(struct dw_mipi_dsi2 *dsi2, |
| 613 | const struct mipi_dsi_msg *msg) |
| 614 | { |
| 615 | u8 *payload = msg->rx_buf; |
| 616 | int i, j, ret, len = msg->rx_len; |
| 617 | u8 data_type; |
| 618 | u16 wc; |
| 619 | u32 val; |
| 620 | |
| 621 | ret = regmap_read_poll_timeout(dsi2->regmap, DSI2_CORE_STATUS, |
| 622 | val, val & CRI_RD_DATA_AVAIL, |
| 623 | 100, CMD_PKT_STATUS_TIMEOUT_US); |
| 624 | if (ret) { |
| 625 | dev_err(dsi2->dev, "CRI has no available read data\n" ); |
| 626 | return ret; |
| 627 | } |
| 628 | |
| 629 | regmap_read(map: dsi2->regmap, DSI2_CRI_RX_HDR, val: &val); |
| 630 | data_type = val & 0x3f; |
| 631 | |
| 632 | if (mipi_dsi_packet_format_is_short(type: data_type)) { |
| 633 | for (i = 0; i < len && i < 2; i++) |
| 634 | payload[i] = (val >> (8 * (i + 1))) & 0xff; |
| 635 | |
| 636 | return 0; |
| 637 | } |
| 638 | |
| 639 | wc = (val >> 8) & 0xffff; |
| 640 | /* Receive payload */ |
| 641 | for (i = 0; i < len && i < wc; i += 4) { |
| 642 | regmap_read(map: dsi2->regmap, DSI2_CRI_RX_PLD, val: &val); |
| 643 | for (j = 0; j < 4 && j + i < len && j + i < wc; j++) |
| 644 | payload[i + j] = val >> (8 * j); |
| 645 | } |
| 646 | |
| 647 | return 0; |
| 648 | } |
| 649 | |
| 650 | static ssize_t dw_mipi_dsi2_host_transfer(struct mipi_dsi_host *host, |
| 651 | const struct mipi_dsi_msg *msg) |
| 652 | { |
| 653 | struct dw_mipi_dsi2 *dsi2 = host_to_dsi2(host); |
| 654 | bool lpm = msg->flags & MIPI_DSI_MSG_USE_LPM; |
| 655 | struct mipi_dsi_packet packet; |
| 656 | int ret, nb_bytes; |
| 657 | |
| 658 | regmap_update_bits(map: dsi2->regmap, DSI2_DSI_VID_TX_CFG, |
| 659 | LPDT_DISPLAY_CMD_EN, |
| 660 | val: lpm ? LPDT_DISPLAY_CMD_EN : 0); |
| 661 | |
| 662 | /* create a packet to the DSI protocol */ |
| 663 | ret = mipi_dsi_create_packet(packet: &packet, msg); |
| 664 | if (ret) { |
| 665 | dev_err(dsi2->dev, "failed to create packet: %d\n" , ret); |
| 666 | return ret; |
| 667 | } |
| 668 | |
| 669 | ret = cri_fifos_wait_avail(dsi2); |
| 670 | if (ret) |
| 671 | return ret; |
| 672 | |
| 673 | ret = dw_mipi_dsi2_write(dsi2, packet: &packet, lpm); |
| 674 | if (ret) |
| 675 | return ret; |
| 676 | |
| 677 | if (msg->rx_buf && msg->rx_len) { |
| 678 | ret = dw_mipi_dsi2_read(dsi2, msg); |
| 679 | if (ret < 0) |
| 680 | return ret; |
| 681 | nb_bytes = msg->rx_len; |
| 682 | } else { |
| 683 | nb_bytes = packet.size; |
| 684 | } |
| 685 | |
| 686 | return nb_bytes; |
| 687 | } |
| 688 | |
| 689 | static const struct mipi_dsi_host_ops dw_mipi_dsi2_host_ops = { |
| 690 | .attach = dw_mipi_dsi2_host_attach, |
| 691 | .detach = dw_mipi_dsi2_host_detach, |
| 692 | .transfer = dw_mipi_dsi2_host_transfer, |
| 693 | }; |
| 694 | |
| 695 | static u32 * |
| 696 | dw_mipi_dsi2_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge, |
| 697 | struct drm_bridge_state *bridge_state, |
| 698 | struct drm_crtc_state *crtc_state, |
| 699 | struct drm_connector_state *conn_state, |
| 700 | u32 output_fmt, |
| 701 | unsigned int *num_input_fmts) |
| 702 | { |
| 703 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 704 | const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; |
| 705 | u32 *input_fmts; |
| 706 | |
| 707 | if (pdata->get_input_bus_fmts) |
| 708 | return pdata->get_input_bus_fmts(pdata->priv_data, |
| 709 | bridge, bridge_state, |
| 710 | crtc_state, conn_state, |
| 711 | output_fmt, num_input_fmts); |
| 712 | |
| 713 | /* Fall back to MEDIA_BUS_FMT_FIXED as the only input format. */ |
| 714 | input_fmts = kmalloc(sizeof(*input_fmts), GFP_KERNEL); |
| 715 | if (!input_fmts) |
| 716 | return NULL; |
| 717 | input_fmts[0] = MEDIA_BUS_FMT_FIXED; |
| 718 | *num_input_fmts = 1; |
| 719 | |
| 720 | return input_fmts; |
| 721 | } |
| 722 | |
| 723 | static int dw_mipi_dsi2_bridge_atomic_check(struct drm_bridge *bridge, |
| 724 | struct drm_bridge_state *bridge_state, |
| 725 | struct drm_crtc_state *crtc_state, |
| 726 | struct drm_connector_state *conn_state) |
| 727 | { |
| 728 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 729 | const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; |
| 730 | bool ret; |
| 731 | |
| 732 | bridge_state->input_bus_cfg.flags = |
| 733 | DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE; |
| 734 | |
| 735 | if (pdata->mode_fixup) { |
| 736 | ret = pdata->mode_fixup(pdata->priv_data, &crtc_state->mode, |
| 737 | &crtc_state->adjusted_mode); |
| 738 | if (!ret) { |
| 739 | DRM_DEBUG_DRIVER("failed to fixup mode " DRM_MODE_FMT "\n" , |
| 740 | DRM_MODE_ARG(&crtc_state->mode)); |
| 741 | return -EINVAL; |
| 742 | } |
| 743 | } |
| 744 | |
| 745 | return 0; |
| 746 | } |
| 747 | |
| 748 | static void dw_mipi_dsi2_bridge_post_atomic_disable(struct drm_bridge *bridge, |
| 749 | struct drm_atomic_state *state) |
| 750 | { |
| 751 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 752 | const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; |
| 753 | |
| 754 | regmap_write(map: dsi2->regmap, DSI2_IPI_PIX_PKT_CFG, val: 0); |
| 755 | |
| 756 | /* |
| 757 | * Switch to command mode before panel-bridge post_disable & |
| 758 | * panel unprepare. |
| 759 | * Note: panel-bridge disable & panel disable has been called |
| 760 | * before by the drm framework. |
| 761 | */ |
| 762 | dw_mipi_dsi2_set_cmd_mode(dsi2); |
| 763 | |
| 764 | regmap_write(map: dsi2->regmap, DSI2_PWR_UP, RESET); |
| 765 | |
| 766 | if (phy_ops->power_off) |
| 767 | phy_ops->power_off(dsi2->plat_data->priv_data); |
| 768 | |
| 769 | clk_disable_unprepare(clk: dsi2->sys_clk); |
| 770 | clk_disable_unprepare(clk: dsi2->pclk); |
| 771 | pm_runtime_put(dev: dsi2->dev); |
| 772 | } |
| 773 | |
| 774 | static unsigned int dw_mipi_dsi2_get_lanes(struct dw_mipi_dsi2 *dsi2) |
| 775 | { |
| 776 | /* single-dsi, so no other instance to consider */ |
| 777 | return dsi2->lanes; |
| 778 | } |
| 779 | |
| 780 | static void dw_mipi_dsi2_mode_set(struct dw_mipi_dsi2 *dsi2, |
| 781 | const struct drm_display_mode *adjusted_mode) |
| 782 | { |
| 783 | const struct dw_mipi_dsi2_phy_ops *phy_ops = dsi2->plat_data->phy_ops; |
| 784 | void *priv_data = dsi2->plat_data->priv_data; |
| 785 | u32 lanes = dw_mipi_dsi2_get_lanes(dsi2); |
| 786 | int ret; |
| 787 | |
| 788 | clk_prepare_enable(clk: dsi2->pclk); |
| 789 | clk_prepare_enable(clk: dsi2->sys_clk); |
| 790 | |
| 791 | ret = phy_ops->get_lane_mbps(priv_data, adjusted_mode, dsi2->mode_flags, |
| 792 | lanes, dsi2->format, &dsi2->lane_mbps); |
| 793 | if (ret) |
| 794 | DRM_DEBUG_DRIVER("Phy get_lane_mbps() failed\n" ); |
| 795 | |
| 796 | pm_runtime_get_sync(dev: dsi2->dev); |
| 797 | |
| 798 | dw_mipi_dsi2_host_softrst(dsi2); |
| 799 | regmap_write(map: dsi2->regmap, DSI2_PWR_UP, RESET); |
| 800 | |
| 801 | dw_mipi_dsi2_work_mode(dsi2, MANUAL_MODE_EN); |
| 802 | dw_mipi_dsi2_phy_init(dsi2); |
| 803 | |
| 804 | if (phy_ops->power_on) |
| 805 | phy_ops->power_on(dsi2->plat_data->priv_data); |
| 806 | |
| 807 | dw_mipi_dsi2_tx_option_set(dsi2); |
| 808 | |
| 809 | /* |
| 810 | * initial deskew calibration is send after phy_power_on, |
| 811 | * then we can configure clk_type. |
| 812 | */ |
| 813 | |
| 814 | regmap_update_bits(map: dsi2->regmap, DSI2_PHY_CLK_CFG, CLK_TYPE_MASK, |
| 815 | val: dsi2->mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS ? NON_CONTINUOUS_CLK : |
| 816 | CONTINUOUS_CLK); |
| 817 | |
| 818 | regmap_write(map: dsi2->regmap, DSI2_PWR_UP, POWER_UP); |
| 819 | dw_mipi_dsi2_set_cmd_mode(dsi2); |
| 820 | |
| 821 | dw_mipi_dsi2_ipi_set(dsi2); |
| 822 | } |
| 823 | |
| 824 | static void dw_mipi_dsi2_bridge_atomic_pre_enable(struct drm_bridge *bridge, |
| 825 | struct drm_atomic_state *state) |
| 826 | { |
| 827 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 828 | |
| 829 | /* Power up the dsi ctl into a command mode */ |
| 830 | dw_mipi_dsi2_mode_set(dsi2, adjusted_mode: &dsi2->mode); |
| 831 | } |
| 832 | |
| 833 | static void dw_mipi_dsi2_bridge_mode_set(struct drm_bridge *bridge, |
| 834 | const struct drm_display_mode *mode, |
| 835 | const struct drm_display_mode *adjusted_mode) |
| 836 | { |
| 837 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 838 | |
| 839 | /* Store the display mode for later use in pre_enable callback */ |
| 840 | drm_mode_copy(dst: &dsi2->mode, src: adjusted_mode); |
| 841 | } |
| 842 | |
| 843 | static void dw_mipi_dsi2_bridge_atomic_enable(struct drm_bridge *bridge, |
| 844 | struct drm_atomic_state *state) |
| 845 | { |
| 846 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 847 | |
| 848 | /* Switch to video mode for panel-bridge enable & panel enable */ |
| 849 | if (dsi2->mode_flags & MIPI_DSI_MODE_VIDEO) |
| 850 | dw_mipi_dsi2_set_vid_mode(dsi2); |
| 851 | else |
| 852 | dw_mipi_dsi2_set_data_stream_mode(dsi2); |
| 853 | } |
| 854 | |
| 855 | static enum drm_mode_status |
| 856 | dw_mipi_dsi2_bridge_mode_valid(struct drm_bridge *bridge, |
| 857 | const struct drm_display_info *info, |
| 858 | const struct drm_display_mode *mode) |
| 859 | { |
| 860 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 861 | const struct dw_mipi_dsi2_plat_data *pdata = dsi2->plat_data; |
| 862 | enum drm_mode_status mode_status = MODE_OK; |
| 863 | |
| 864 | if (pdata->mode_valid) |
| 865 | mode_status = pdata->mode_valid(pdata->priv_data, mode, |
| 866 | dsi2->mode_flags, |
| 867 | dw_mipi_dsi2_get_lanes(dsi2), |
| 868 | dsi2->format); |
| 869 | |
| 870 | return mode_status; |
| 871 | } |
| 872 | |
| 873 | static int dw_mipi_dsi2_bridge_attach(struct drm_bridge *bridge, |
| 874 | struct drm_encoder *encoder, |
| 875 | enum drm_bridge_attach_flags flags) |
| 876 | { |
| 877 | struct dw_mipi_dsi2 *dsi2 = bridge_to_dsi2(bridge); |
| 878 | |
| 879 | /* Set the encoder type as caller does not know it */ |
| 880 | encoder->encoder_type = DRM_MODE_ENCODER_DSI; |
| 881 | |
| 882 | /* Attach the panel-bridge to the dsi bridge */ |
| 883 | return drm_bridge_attach(encoder, bridge: dsi2->panel_bridge, previous: bridge, |
| 884 | flags); |
| 885 | } |
| 886 | |
| 887 | static const struct drm_bridge_funcs dw_mipi_dsi2_bridge_funcs = { |
| 888 | .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state, |
| 889 | .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state, |
| 890 | .atomic_get_input_bus_fmts = dw_mipi_dsi2_bridge_atomic_get_input_bus_fmts, |
| 891 | .atomic_check = dw_mipi_dsi2_bridge_atomic_check, |
| 892 | .atomic_reset = drm_atomic_helper_bridge_reset, |
| 893 | .atomic_pre_enable = dw_mipi_dsi2_bridge_atomic_pre_enable, |
| 894 | .atomic_enable = dw_mipi_dsi2_bridge_atomic_enable, |
| 895 | .atomic_post_disable = dw_mipi_dsi2_bridge_post_atomic_disable, |
| 896 | .mode_set = dw_mipi_dsi2_bridge_mode_set, |
| 897 | .mode_valid = dw_mipi_dsi2_bridge_mode_valid, |
| 898 | .attach = dw_mipi_dsi2_bridge_attach, |
| 899 | }; |
| 900 | |
| 901 | static const struct regmap_config dw_mipi_dsi2_regmap_config = { |
| 902 | .name = "dsi2-host" , |
| 903 | .reg_bits = 32, |
| 904 | .val_bits = 32, |
| 905 | .reg_stride = 4, |
| 906 | .fast_io = true, |
| 907 | }; |
| 908 | |
| 909 | static struct dw_mipi_dsi2 * |
| 910 | __dw_mipi_dsi2_probe(struct platform_device *pdev, |
| 911 | const struct dw_mipi_dsi2_plat_data *plat_data) |
| 912 | { |
| 913 | struct device *dev = &pdev->dev; |
| 914 | struct reset_control *apb_rst; |
| 915 | struct dw_mipi_dsi2 *dsi2; |
| 916 | int ret; |
| 917 | |
| 918 | dsi2 = devm_drm_bridge_alloc(dev, struct dw_mipi_dsi2, bridge, |
| 919 | &dw_mipi_dsi2_bridge_funcs); |
| 920 | if (IS_ERR(ptr: dsi2)) |
| 921 | return ERR_CAST(ptr: dsi2); |
| 922 | |
| 923 | dsi2->dev = dev; |
| 924 | dsi2->plat_data = plat_data; |
| 925 | |
| 926 | if (!plat_data->phy_ops->init || !plat_data->phy_ops->get_lane_mbps || |
| 927 | !plat_data->phy_ops->get_timing) |
| 928 | return dev_err_ptr_probe(dev, -ENODEV, "Phy not properly configured\n" ); |
| 929 | |
| 930 | if (!plat_data->regmap) { |
| 931 | void __iomem *base = devm_platform_ioremap_resource(pdev, index: 0); |
| 932 | |
| 933 | if (IS_ERR(ptr: base)) |
| 934 | return dev_err_cast_probe(dev, base, "failed to registers\n" ); |
| 935 | |
| 936 | dsi2->regmap = devm_regmap_init_mmio(dev, base, |
| 937 | &dw_mipi_dsi2_regmap_config); |
| 938 | if (IS_ERR(ptr: dsi2->regmap)) |
| 939 | return dev_err_cast_probe(dev, dsi2->regmap, "failed to init regmap\n" ); |
| 940 | } else { |
| 941 | dsi2->regmap = plat_data->regmap; |
| 942 | } |
| 943 | |
| 944 | dsi2->pclk = devm_clk_get(dev, id: "pclk" ); |
| 945 | if (IS_ERR(ptr: dsi2->pclk)) |
| 946 | return dev_err_cast_probe(dev, dsi2->pclk, "Unable to get pclk\n" ); |
| 947 | |
| 948 | dsi2->sys_clk = devm_clk_get(dev, id: "sys" ); |
| 949 | if (IS_ERR(ptr: dsi2->sys_clk)) |
| 950 | return dev_err_cast_probe(dev, dsi2->sys_clk, "Unable to get sys_clk\n" ); |
| 951 | |
| 952 | /* |
| 953 | * Note that the reset was not defined in the initial device tree, so |
| 954 | * we have to be prepared for it not being found. |
| 955 | */ |
| 956 | apb_rst = devm_reset_control_get_optional_exclusive(dev, id: "apb" ); |
| 957 | if (IS_ERR(ptr: apb_rst)) |
| 958 | return dev_err_cast_probe(dev, apb_rst, "Unable to get reset control\n" ); |
| 959 | |
| 960 | if (apb_rst) { |
| 961 | ret = clk_prepare_enable(clk: dsi2->pclk); |
| 962 | if (ret) { |
| 963 | dev_err(dev, "%s: Failed to enable pclk\n" , __func__); |
| 964 | return ERR_PTR(error: ret); |
| 965 | } |
| 966 | |
| 967 | reset_control_assert(rstc: apb_rst); |
| 968 | usleep_range(min: 10, max: 20); |
| 969 | reset_control_deassert(rstc: apb_rst); |
| 970 | |
| 971 | clk_disable_unprepare(clk: dsi2->pclk); |
| 972 | } |
| 973 | |
| 974 | devm_pm_runtime_enable(dev); |
| 975 | |
| 976 | dsi2->dsi_host.ops = &dw_mipi_dsi2_host_ops; |
| 977 | dsi2->dsi_host.dev = dev; |
| 978 | ret = mipi_dsi_host_register(host: &dsi2->dsi_host); |
| 979 | if (ret) { |
| 980 | dev_err(dev, "Failed to register MIPI host: %d\n" , ret); |
| 981 | pm_runtime_disable(dev); |
| 982 | return ERR_PTR(error: ret); |
| 983 | } |
| 984 | |
| 985 | dsi2->bridge.driver_private = dsi2; |
| 986 | dsi2->bridge.of_node = pdev->dev.of_node; |
| 987 | |
| 988 | return dsi2; |
| 989 | } |
| 990 | |
| 991 | static void __dw_mipi_dsi2_remove(struct dw_mipi_dsi2 *dsi2) |
| 992 | { |
| 993 | mipi_dsi_host_unregister(host: &dsi2->dsi_host); |
| 994 | } |
| 995 | |
| 996 | /* |
| 997 | * Probe/remove API, used to create the bridge instance. |
| 998 | */ |
| 999 | struct dw_mipi_dsi2 * |
| 1000 | dw_mipi_dsi2_probe(struct platform_device *pdev, |
| 1001 | const struct dw_mipi_dsi2_plat_data *plat_data) |
| 1002 | { |
| 1003 | return __dw_mipi_dsi2_probe(pdev, plat_data); |
| 1004 | } |
| 1005 | EXPORT_SYMBOL_GPL(dw_mipi_dsi2_probe); |
| 1006 | |
| 1007 | void dw_mipi_dsi2_remove(struct dw_mipi_dsi2 *dsi2) |
| 1008 | { |
| 1009 | __dw_mipi_dsi2_remove(dsi2); |
| 1010 | } |
| 1011 | EXPORT_SYMBOL_GPL(dw_mipi_dsi2_remove); |
| 1012 | |
| 1013 | /* |
| 1014 | * Bind/unbind API, used from platforms based on the component framework |
| 1015 | * to attach the bridge to an encoder. |
| 1016 | */ |
| 1017 | int dw_mipi_dsi2_bind(struct dw_mipi_dsi2 *dsi2, struct drm_encoder *encoder) |
| 1018 | { |
| 1019 | return drm_bridge_attach(encoder, bridge: &dsi2->bridge, NULL, flags: 0); |
| 1020 | } |
| 1021 | EXPORT_SYMBOL_GPL(dw_mipi_dsi2_bind); |
| 1022 | |
| 1023 | void dw_mipi_dsi2_unbind(struct dw_mipi_dsi2 *dsi2) |
| 1024 | { |
| 1025 | } |
| 1026 | EXPORT_SYMBOL_GPL(dw_mipi_dsi2_unbind); |
| 1027 | |
| 1028 | MODULE_AUTHOR("Guochun Huang <hero.huang@rock-chips.com>" ); |
| 1029 | MODULE_AUTHOR("Heiko Stuebner <heiko.stuebner@cherry.de>" ); |
| 1030 | MODULE_DESCRIPTION("DW MIPI DSI2 host controller driver" ); |
| 1031 | MODULE_LICENSE("GPL" ); |
| 1032 | MODULE_ALIAS("platform:dw-mipi-dsi2" ); |
| 1033 | |