197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2a689554bSHai Li /* 3a689554bSHai Li * Copyright (c) 2015, The Linux Foundation. All rights reserved. 4a689554bSHai Li */ 5a689554bSHai Li 6a689554bSHai Li #include <linux/clk.h> 7a689554bSHai Li #include <linux/delay.h> 8feea39a8SSam Ravnborg #include <linux/dma-mapping.h> 9a689554bSHai Li #include <linux/err.h> 10964a0754SBrian Norris #include <linux/gpio/consumer.h> 11a689554bSHai Li #include <linux/interrupt.h> 12feea39a8SSam Ravnborg #include <linux/mfd/syscon.h> 13a689554bSHai Li #include <linux/of_device.h> 14feea39a8SSam Ravnborg #include <linux/of_graph.h> 15a689554bSHai Li #include <linux/of_irq.h> 16ab8909b0SHai Li #include <linux/pinctrl/consumer.h> 1732d3e0feSRajendra Nayak #include <linux/pm_opp.h> 18feea39a8SSam Ravnborg #include <linux/regmap.h> 19a689554bSHai Li #include <linux/regulator/consumer.h> 20a689554bSHai Li #include <linux/spinlock.h> 21feea39a8SSam Ravnborg 22a689554bSHai Li #include <video/mipi_display.h> 23a689554bSHai Li 24a689554bSHai Li #include "dsi.h" 25a689554bSHai Li #include "dsi.xml.h" 260c7df47fSArchit Taneja #include "sfpb.xml.h" 27d248b61fSHai Li #include "dsi_cfg.h" 28f59f62d5SRob Clark #include "msm_kms.h" 298f642378SRob Clark #include "msm_gem.h" 305ac17838SJonathan Marek #include "phy/dsi_phy.h" 31a689554bSHai Li 3278e31c42SJeffrey Hugo #define DSI_RESET_TOGGLE_DELAY_MS 20 3378e31c42SJeffrey Hugo 34a689554bSHai Li static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) 35a689554bSHai Li { 36a689554bSHai Li u32 ver; 37a689554bSHai Li 38a689554bSHai Li if (!major || !minor) 39a689554bSHai Li return -EINVAL; 40a689554bSHai Li 41648d5063SArchit Taneja /* 42648d5063SArchit Taneja * From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0 43a689554bSHai Li * makes all other registers 4-byte shifted down. 44648d5063SArchit Taneja * 45648d5063SArchit Taneja * In order to identify between DSI6G(v3) and beyond, and DSIv2 and 46648d5063SArchit Taneja * older, we read the DSI_VERSION register without any shift(offset 47648d5063SArchit Taneja * 0x1f0). In the case of DSIv2, this hast to be a non-zero value. In 48648d5063SArchit Taneja * the case of DSI6G, this has to be zero (the offset points to a 49648d5063SArchit Taneja * scratch register which we never touch) 50a689554bSHai Li */ 51648d5063SArchit Taneja 52a689554bSHai Li ver = msm_readl(base + REG_DSI_VERSION); 53648d5063SArchit Taneja if (ver) { 54648d5063SArchit Taneja /* older dsi host, there is no register shift */ 55a689554bSHai Li ver = FIELD(ver, DSI_VERSION_MAJOR); 56a689554bSHai Li if (ver <= MSM_DSI_VER_MAJOR_V2) { 57a689554bSHai Li /* old versions */ 58a689554bSHai Li *major = ver; 59a689554bSHai Li *minor = 0; 60a689554bSHai Li return 0; 61a689554bSHai Li } else { 62a689554bSHai Li return -EINVAL; 63a689554bSHai Li } 64a689554bSHai Li } else { 65648d5063SArchit Taneja /* 66648d5063SArchit Taneja * newer host, offset 0 has 6G_HW_VERSION, the rest of the 67648d5063SArchit Taneja * registers are shifted down, read DSI_VERSION again with 68648d5063SArchit Taneja * the shifted offset 69648d5063SArchit Taneja */ 70a689554bSHai Li ver = msm_readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION); 71a689554bSHai Li ver = FIELD(ver, DSI_VERSION_MAJOR); 72a689554bSHai Li if (ver == MSM_DSI_VER_MAJOR_6G) { 73a689554bSHai Li /* 6G version */ 74a689554bSHai Li *major = ver; 75648d5063SArchit Taneja *minor = msm_readl(base + REG_DSI_6G_HW_VERSION); 76a689554bSHai Li return 0; 77a689554bSHai Li } else { 78a689554bSHai Li return -EINVAL; 79a689554bSHai Li } 80a689554bSHai Li } 81a689554bSHai Li } 82a689554bSHai Li 83a689554bSHai Li #define DSI_ERR_STATE_ACK 0x0000 84a689554bSHai Li #define DSI_ERR_STATE_TIMEOUT 0x0001 85a689554bSHai Li #define DSI_ERR_STATE_DLN0_PHY 0x0002 86a689554bSHai Li #define DSI_ERR_STATE_FIFO 0x0004 87a689554bSHai Li #define DSI_ERR_STATE_MDP_FIFO_UNDERFLOW 0x0008 88a689554bSHai Li #define DSI_ERR_STATE_INTERLEAVE_OP_CONTENTION 0x0010 89a689554bSHai Li #define DSI_ERR_STATE_PLL_UNLOCKED 0x0020 90a689554bSHai Li 91a689554bSHai Li #define DSI_CLK_CTRL_ENABLE_CLKS \ 92a689554bSHai Li (DSI_CLK_CTRL_AHBS_HCLK_ON | DSI_CLK_CTRL_AHBM_SCLK_ON | \ 93a689554bSHai Li DSI_CLK_CTRL_PCLK_ON | DSI_CLK_CTRL_DSICLK_ON | \ 94a689554bSHai Li DSI_CLK_CTRL_BYTECLK_ON | DSI_CLK_CTRL_ESCCLK_ON | \ 95a689554bSHai Li DSI_CLK_CTRL_FORCE_ON_DYN_AHBM_HCLK) 96a689554bSHai Li 97a689554bSHai Li struct msm_dsi_host { 98a689554bSHai Li struct mipi_dsi_host base; 99a689554bSHai Li 100a689554bSHai Li struct platform_device *pdev; 101a689554bSHai Li struct drm_device *dev; 102a689554bSHai Li 103a689554bSHai Li int id; 104a689554bSHai Li 105a689554bSHai Li void __iomem *ctrl_base; 106bac2c6a6SDmitry Baryshkov phys_addr_t ctrl_size; 107ec31abf6SHai Li struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; 1086e0eb52eSArchit Taneja 1096e0eb52eSArchit Taneja struct clk *bus_clks[DSI_BUS_CLK_MAX]; 1106e0eb52eSArchit Taneja 111a689554bSHai Li struct clk *byte_clk; 112a689554bSHai Li struct clk *esc_clk; 113a689554bSHai Li struct clk *pixel_clk; 1149d32c498SHai Li struct clk *byte_clk_src; 1159d32c498SHai Li struct clk *pixel_clk_src; 116c1d97083SArchit Taneja struct clk *byte_intf_clk; 1179d32c498SHai Li 118a689554bSHai Li u32 byte_clk_rate; 119ed9976a0SChandan Uddaraju u32 pixel_clk_rate; 1204bfa9748SArchit Taneja u32 esc_clk_rate; 1214bfa9748SArchit Taneja 1224bfa9748SArchit Taneja /* DSI v2 specific clocks */ 1234bfa9748SArchit Taneja struct clk *src_clk; 1244bfa9748SArchit Taneja struct clk *esc_clk_src; 1254bfa9748SArchit Taneja struct clk *dsi_clk_src; 1264bfa9748SArchit Taneja 1274bfa9748SArchit Taneja u32 src_clk_rate; 128a689554bSHai Li 129a689554bSHai Li struct gpio_desc *disp_en_gpio; 130a689554bSHai Li struct gpio_desc *te_gpio; 131a689554bSHai Li 132d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd; 133a689554bSHai Li 134a689554bSHai Li struct completion dma_comp; 135a689554bSHai Li struct completion video_comp; 136a689554bSHai Li struct mutex dev_mutex; 137a689554bSHai Li struct mutex cmd_mutex; 138a689554bSHai Li spinlock_t intr_lock; /* Protect interrupt ctrl register */ 139a689554bSHai Li 140a689554bSHai Li u32 err_work_state; 141a689554bSHai Li struct work_struct err_work; 1428d23ea40SArchit Taneja struct work_struct hpd_work; 143a689554bSHai Li struct workqueue_struct *workqueue; 144a689554bSHai Li 1454ff9d4cbSArchit Taneja /* DSI 6G TX buffer*/ 146a689554bSHai Li struct drm_gem_object *tx_gem_obj; 1474ff9d4cbSArchit Taneja 1484ff9d4cbSArchit Taneja /* DSI v2 TX buffer */ 1494ff9d4cbSArchit Taneja void *tx_buf; 1504ff9d4cbSArchit Taneja dma_addr_t tx_buf_paddr; 1514ff9d4cbSArchit Taneja 1524ff9d4cbSArchit Taneja int tx_size; 1534ff9d4cbSArchit Taneja 154a689554bSHai Li u8 *rx_buf; 155a689554bSHai Li 1560c7df47fSArchit Taneja struct regmap *sfpb; 1570c7df47fSArchit Taneja 158a689554bSHai Li struct drm_display_mode *mode; 159a689554bSHai Li 160a9ddac9cSArchit Taneja /* connected device info */ 161a9ddac9cSArchit Taneja struct device_node *device_node; 162a689554bSHai Li unsigned int channel; 163a689554bSHai Li unsigned int lanes; 164a689554bSHai Li enum mipi_dsi_pixel_format format; 165a689554bSHai Li unsigned long mode_flags; 166a689554bSHai Li 16726f7d1f4SArchit Taneja /* lane data parsed via DT */ 16826f7d1f4SArchit Taneja int dlane_swap; 16926f7d1f4SArchit Taneja int num_data_lanes; 17026f7d1f4SArchit Taneja 1715ac17838SJonathan Marek /* from phy DT */ 1725ac17838SJonathan Marek bool cphy_mode; 1735ac17838SJonathan Marek 174a689554bSHai Li u32 dma_cmd_ctrl_restore; 175a689554bSHai Li 176a689554bSHai Li bool registered; 177a689554bSHai Li bool power_on; 1789c5638d7SAbhinav Kumar bool enabled; 179a689554bSHai Li int irq; 180a689554bSHai Li }; 181a689554bSHai Li 182a689554bSHai Li static u32 dsi_get_bpp(const enum mipi_dsi_pixel_format fmt) 183a689554bSHai Li { 184a689554bSHai Li switch (fmt) { 185a689554bSHai Li case MIPI_DSI_FMT_RGB565: return 16; 186a689554bSHai Li case MIPI_DSI_FMT_RGB666_PACKED: return 18; 187a689554bSHai Li case MIPI_DSI_FMT_RGB666: 188a689554bSHai Li case MIPI_DSI_FMT_RGB888: 189a689554bSHai Li default: return 24; 190a689554bSHai Li } 191a689554bSHai Li } 192a689554bSHai Li 193a689554bSHai Li static inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) 194a689554bSHai Li { 195d248b61fSHai Li return msm_readl(msm_host->ctrl_base + reg); 196a689554bSHai Li } 197a689554bSHai Li static inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) 198a689554bSHai Li { 199d248b61fSHai Li msm_writel(data, msm_host->ctrl_base + reg); 200a689554bSHai Li } 201a689554bSHai Li 202a689554bSHai Li static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host); 203a689554bSHai Li static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host); 204a689554bSHai Li 205d248b61fSHai Li static const struct msm_dsi_cfg_handler *dsi_get_config( 206d248b61fSHai Li struct msm_dsi_host *msm_host) 207a689554bSHai Li { 208d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd = NULL; 20931c92767SArchit Taneja struct device *dev = &msm_host->pdev->dev; 21031c92767SArchit Taneja struct clk *ahb_clk; 211d248b61fSHai Li int ret; 212a689554bSHai Li u32 major = 0, minor = 0; 213a689554bSHai Li 21429a1157cSArchit Taneja ahb_clk = msm_clk_get(msm_host->pdev, "iface"); 21531c92767SArchit Taneja if (IS_ERR(ahb_clk)) { 21631c92767SArchit Taneja pr_err("%s: cannot get interface clock\n", __func__); 217b93cc4b2SDmitry Baryshkov goto exit; 21831c92767SArchit Taneja } 21931c92767SArchit Taneja 220f6be1121SArchit Taneja pm_runtime_get_sync(dev); 221f6be1121SArchit Taneja 22231c92767SArchit Taneja ret = clk_prepare_enable(ahb_clk); 223a689554bSHai Li if (ret) { 224a689554bSHai Li pr_err("%s: unable to enable ahb_clk\n", __func__); 225b93cc4b2SDmitry Baryshkov goto runtime_put; 226a689554bSHai Li } 227a689554bSHai Li 228a689554bSHai Li ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); 229a689554bSHai Li if (ret) { 230a689554bSHai Li pr_err("%s: Invalid version\n", __func__); 231d248b61fSHai Li goto disable_clks; 232a689554bSHai Li } 233a689554bSHai Li 234d248b61fSHai Li cfg_hnd = msm_dsi_cfg_get(major, minor); 235a689554bSHai Li 236d248b61fSHai Li DBG("%s: Version %x:%x\n", __func__, major, minor); 237d248b61fSHai Li 238d248b61fSHai Li disable_clks: 23931c92767SArchit Taneja clk_disable_unprepare(ahb_clk); 240b93cc4b2SDmitry Baryshkov runtime_put: 241a18a0ea0SArchit Taneja pm_runtime_put_sync(dev); 242d248b61fSHai Li exit: 243d248b61fSHai Li return cfg_hnd; 244a689554bSHai Li } 245a689554bSHai Li 246a689554bSHai Li static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) 247a689554bSHai Li { 248a689554bSHai Li return container_of(host, struct msm_dsi_host, base); 249a689554bSHai Li } 250a689554bSHai Li 251a689554bSHai Li static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) 252a689554bSHai Li { 253a689554bSHai Li struct regulator_bulk_data *s = msm_host->supplies; 254d248b61fSHai Li const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; 255d248b61fSHai Li int num = msm_host->cfg_hnd->cfg->reg_cfg.num; 256a689554bSHai Li int i; 257a689554bSHai Li 258a689554bSHai Li DBG(""); 259a689554bSHai Li for (i = num - 1; i >= 0; i--) 260a689554bSHai Li if (regs[i].disable_load >= 0) 2612c33ce00SDave Airlie regulator_set_load(s[i].consumer, 262a689554bSHai Li regs[i].disable_load); 263a689554bSHai Li 264a689554bSHai Li regulator_bulk_disable(num, s); 265a689554bSHai Li } 266a689554bSHai Li 267a689554bSHai Li static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host) 268a689554bSHai Li { 269a689554bSHai Li struct regulator_bulk_data *s = msm_host->supplies; 270d248b61fSHai Li const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; 271d248b61fSHai Li int num = msm_host->cfg_hnd->cfg->reg_cfg.num; 272a689554bSHai Li int ret, i; 273a689554bSHai Li 274a689554bSHai Li DBG(""); 275a689554bSHai Li for (i = 0; i < num; i++) { 276a689554bSHai Li if (regs[i].enable_load >= 0) { 2772c33ce00SDave Airlie ret = regulator_set_load(s[i].consumer, 278a689554bSHai Li regs[i].enable_load); 279a689554bSHai Li if (ret < 0) { 280a689554bSHai Li pr_err("regulator %d set op mode failed, %d\n", 281a689554bSHai Li i, ret); 282a689554bSHai Li goto fail; 283a689554bSHai Li } 284a689554bSHai Li } 285a689554bSHai Li } 286a689554bSHai Li 287a689554bSHai Li ret = regulator_bulk_enable(num, s); 288a689554bSHai Li if (ret < 0) { 289a689554bSHai Li pr_err("regulator enable failed, %d\n", ret); 290a689554bSHai Li goto fail; 291a689554bSHai Li } 292a689554bSHai Li 293a689554bSHai Li return 0; 294a689554bSHai Li 295a689554bSHai Li fail: 296a689554bSHai Li for (i--; i >= 0; i--) 2972c33ce00SDave Airlie regulator_set_load(s[i].consumer, regs[i].disable_load); 298a689554bSHai Li return ret; 299a689554bSHai Li } 300a689554bSHai Li 301a689554bSHai Li static int dsi_regulator_init(struct msm_dsi_host *msm_host) 302a689554bSHai Li { 303a689554bSHai Li struct regulator_bulk_data *s = msm_host->supplies; 304d248b61fSHai Li const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; 305d248b61fSHai Li int num = msm_host->cfg_hnd->cfg->reg_cfg.num; 306a689554bSHai Li int i, ret; 307a689554bSHai Li 308a689554bSHai Li for (i = 0; i < num; i++) 309a689554bSHai Li s[i].supply = regs[i].name; 310a689554bSHai Li 311a689554bSHai Li ret = devm_regulator_bulk_get(&msm_host->pdev->dev, num, s); 312a689554bSHai Li if (ret < 0) { 313a689554bSHai Li pr_err("%s: failed to init regulator, ret=%d\n", 314a689554bSHai Li __func__, ret); 315a689554bSHai Li return ret; 316a689554bSHai Li } 317a689554bSHai Li 318a689554bSHai Li return 0; 319a689554bSHai Li } 320a689554bSHai Li 321c4d8cfe5SSibi Sankar int dsi_clk_init_v2(struct msm_dsi_host *msm_host) 322c4d8cfe5SSibi Sankar { 323c4d8cfe5SSibi Sankar struct platform_device *pdev = msm_host->pdev; 324c4d8cfe5SSibi Sankar int ret = 0; 325c4d8cfe5SSibi Sankar 326c4d8cfe5SSibi Sankar msm_host->src_clk = msm_clk_get(pdev, "src"); 327c4d8cfe5SSibi Sankar 328c4d8cfe5SSibi Sankar if (IS_ERR(msm_host->src_clk)) { 329c4d8cfe5SSibi Sankar ret = PTR_ERR(msm_host->src_clk); 330c4d8cfe5SSibi Sankar pr_err("%s: can't find src clock. ret=%d\n", 331c4d8cfe5SSibi Sankar __func__, ret); 332c4d8cfe5SSibi Sankar msm_host->src_clk = NULL; 333c4d8cfe5SSibi Sankar return ret; 334c4d8cfe5SSibi Sankar } 335c4d8cfe5SSibi Sankar 336c4d8cfe5SSibi Sankar msm_host->esc_clk_src = clk_get_parent(msm_host->esc_clk); 337c4d8cfe5SSibi Sankar if (!msm_host->esc_clk_src) { 338c4d8cfe5SSibi Sankar ret = -ENODEV; 339c4d8cfe5SSibi Sankar pr_err("%s: can't get esc clock parent. ret=%d\n", 340c4d8cfe5SSibi Sankar __func__, ret); 341c4d8cfe5SSibi Sankar return ret; 342c4d8cfe5SSibi Sankar } 343c4d8cfe5SSibi Sankar 344c4d8cfe5SSibi Sankar msm_host->dsi_clk_src = clk_get_parent(msm_host->src_clk); 345c4d8cfe5SSibi Sankar if (!msm_host->dsi_clk_src) { 346c4d8cfe5SSibi Sankar ret = -ENODEV; 347c4d8cfe5SSibi Sankar pr_err("%s: can't get src clock parent. ret=%d\n", 348c4d8cfe5SSibi Sankar __func__, ret); 349c4d8cfe5SSibi Sankar } 350c4d8cfe5SSibi Sankar 351c4d8cfe5SSibi Sankar return ret; 352c4d8cfe5SSibi Sankar } 353c4d8cfe5SSibi Sankar 354c4d8cfe5SSibi Sankar int dsi_clk_init_6g_v2(struct msm_dsi_host *msm_host) 355c4d8cfe5SSibi Sankar { 356c4d8cfe5SSibi Sankar struct platform_device *pdev = msm_host->pdev; 357c4d8cfe5SSibi Sankar int ret = 0; 358c4d8cfe5SSibi Sankar 359c4d8cfe5SSibi Sankar msm_host->byte_intf_clk = msm_clk_get(pdev, "byte_intf"); 360c4d8cfe5SSibi Sankar if (IS_ERR(msm_host->byte_intf_clk)) { 361c4d8cfe5SSibi Sankar ret = PTR_ERR(msm_host->byte_intf_clk); 362c4d8cfe5SSibi Sankar pr_err("%s: can't find byte_intf clock. ret=%d\n", 363c4d8cfe5SSibi Sankar __func__, ret); 364c4d8cfe5SSibi Sankar } 365c4d8cfe5SSibi Sankar 366c4d8cfe5SSibi Sankar return ret; 367c4d8cfe5SSibi Sankar } 368c4d8cfe5SSibi Sankar 369a689554bSHai Li static int dsi_clk_init(struct msm_dsi_host *msm_host) 370a689554bSHai Li { 371db9a3750SRob Clark struct platform_device *pdev = msm_host->pdev; 3724bfa9748SArchit Taneja const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 3734bfa9748SArchit Taneja const struct msm_dsi_config *cfg = cfg_hnd->cfg; 3746e0eb52eSArchit Taneja int i, ret = 0; 375a689554bSHai Li 3766e0eb52eSArchit Taneja /* get bus clocks */ 3776e0eb52eSArchit Taneja for (i = 0; i < cfg->num_bus_clks; i++) { 378db9a3750SRob Clark msm_host->bus_clks[i] = msm_clk_get(pdev, 3796e0eb52eSArchit Taneja cfg->bus_clk_names[i]); 3806e0eb52eSArchit Taneja if (IS_ERR(msm_host->bus_clks[i])) { 3816e0eb52eSArchit Taneja ret = PTR_ERR(msm_host->bus_clks[i]); 382db9a3750SRob Clark pr_err("%s: Unable to get %s clock, ret = %d\n", 3836e0eb52eSArchit Taneja __func__, cfg->bus_clk_names[i], ret); 384a689554bSHai Li goto exit; 385a689554bSHai Li } 386a689554bSHai Li } 387a689554bSHai Li 3886e0eb52eSArchit Taneja /* get link and source clocks */ 389db9a3750SRob Clark msm_host->byte_clk = msm_clk_get(pdev, "byte"); 390a689554bSHai Li if (IS_ERR(msm_host->byte_clk)) { 391a689554bSHai Li ret = PTR_ERR(msm_host->byte_clk); 392db9a3750SRob Clark pr_err("%s: can't find dsi_byte clock. ret=%d\n", 393a689554bSHai Li __func__, ret); 394a689554bSHai Li msm_host->byte_clk = NULL; 395a689554bSHai Li goto exit; 396a689554bSHai Li } 397a689554bSHai Li 398db9a3750SRob Clark msm_host->pixel_clk = msm_clk_get(pdev, "pixel"); 399a689554bSHai Li if (IS_ERR(msm_host->pixel_clk)) { 400a689554bSHai Li ret = PTR_ERR(msm_host->pixel_clk); 401db9a3750SRob Clark pr_err("%s: can't find dsi_pixel clock. ret=%d\n", 402a689554bSHai Li __func__, ret); 403a689554bSHai Li msm_host->pixel_clk = NULL; 404a689554bSHai Li goto exit; 405a689554bSHai Li } 406a689554bSHai Li 407db9a3750SRob Clark msm_host->esc_clk = msm_clk_get(pdev, "core"); 408a689554bSHai Li if (IS_ERR(msm_host->esc_clk)) { 409a689554bSHai Li ret = PTR_ERR(msm_host->esc_clk); 410db9a3750SRob Clark pr_err("%s: can't find dsi_esc clock. ret=%d\n", 411a689554bSHai Li __func__, ret); 412a689554bSHai Li msm_host->esc_clk = NULL; 413a689554bSHai Li goto exit; 414a689554bSHai Li } 415a689554bSHai Li 416e6c4c78cSArchit Taneja msm_host->byte_clk_src = clk_get_parent(msm_host->byte_clk); 4175fb9b797SSean Paul if (IS_ERR(msm_host->byte_clk_src)) { 4185fb9b797SSean Paul ret = PTR_ERR(msm_host->byte_clk_src); 419db9a3750SRob Clark pr_err("%s: can't find byte_clk clock. ret=%d\n", __func__, ret); 4209d32c498SHai Li goto exit; 4219d32c498SHai Li } 4229d32c498SHai Li 423e6c4c78cSArchit Taneja msm_host->pixel_clk_src = clk_get_parent(msm_host->pixel_clk); 4245fb9b797SSean Paul if (IS_ERR(msm_host->pixel_clk_src)) { 4255fb9b797SSean Paul ret = PTR_ERR(msm_host->pixel_clk_src); 426db9a3750SRob Clark pr_err("%s: can't find pixel_clk clock. ret=%d\n", __func__, ret); 4274bfa9748SArchit Taneja goto exit; 4289d32c498SHai Li } 4299d32c498SHai Li 4308f7ca540SSibi Sankar if (cfg_hnd->ops->clk_init_ver) 4318f7ca540SSibi Sankar ret = cfg_hnd->ops->clk_init_ver(msm_host); 432a689554bSHai Li exit: 433a689554bSHai Li return ret; 434a689554bSHai Li } 435a689554bSHai Li 436a689554bSHai Li static int dsi_bus_clk_enable(struct msm_dsi_host *msm_host) 437a689554bSHai Li { 4386e0eb52eSArchit Taneja const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 4396e0eb52eSArchit Taneja int i, ret; 440a689554bSHai Li 441a689554bSHai Li DBG("id=%d", msm_host->id); 442a689554bSHai Li 4436e0eb52eSArchit Taneja for (i = 0; i < cfg->num_bus_clks; i++) { 4446e0eb52eSArchit Taneja ret = clk_prepare_enable(msm_host->bus_clks[i]); 445a689554bSHai Li if (ret) { 4466e0eb52eSArchit Taneja pr_err("%s: failed to enable bus clock %d ret %d\n", 4476e0eb52eSArchit Taneja __func__, i, ret); 4486e0eb52eSArchit Taneja goto err; 449a689554bSHai Li } 450a689554bSHai Li } 451a689554bSHai Li 452a689554bSHai Li return 0; 4536e0eb52eSArchit Taneja err: 4546e0eb52eSArchit Taneja for (; i > 0; i--) 4556e0eb52eSArchit Taneja clk_disable_unprepare(msm_host->bus_clks[i]); 456a689554bSHai Li 457a689554bSHai Li return ret; 458a689554bSHai Li } 459a689554bSHai Li 460a689554bSHai Li static void dsi_bus_clk_disable(struct msm_dsi_host *msm_host) 461a689554bSHai Li { 4626e0eb52eSArchit Taneja const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 4636e0eb52eSArchit Taneja int i; 4646e0eb52eSArchit Taneja 465a689554bSHai Li DBG(""); 4666e0eb52eSArchit Taneja 4676e0eb52eSArchit Taneja for (i = cfg->num_bus_clks - 1; i >= 0; i--) 4686e0eb52eSArchit Taneja clk_disable_unprepare(msm_host->bus_clks[i]); 469a689554bSHai Li } 470a689554bSHai Li 471f54ca1a0SArchit Taneja int msm_dsi_runtime_suspend(struct device *dev) 472f54ca1a0SArchit Taneja { 473f54ca1a0SArchit Taneja struct platform_device *pdev = to_platform_device(dev); 474f54ca1a0SArchit Taneja struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); 475f54ca1a0SArchit Taneja struct mipi_dsi_host *host = msm_dsi->host; 476f54ca1a0SArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 477f54ca1a0SArchit Taneja 478f54ca1a0SArchit Taneja if (!msm_host->cfg_hnd) 479f54ca1a0SArchit Taneja return 0; 480f54ca1a0SArchit Taneja 481f54ca1a0SArchit Taneja dsi_bus_clk_disable(msm_host); 482f54ca1a0SArchit Taneja 483f54ca1a0SArchit Taneja return 0; 484f54ca1a0SArchit Taneja } 485f54ca1a0SArchit Taneja 486f54ca1a0SArchit Taneja int msm_dsi_runtime_resume(struct device *dev) 487f54ca1a0SArchit Taneja { 488f54ca1a0SArchit Taneja struct platform_device *pdev = to_platform_device(dev); 489f54ca1a0SArchit Taneja struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); 490f54ca1a0SArchit Taneja struct mipi_dsi_host *host = msm_dsi->host; 491f54ca1a0SArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 492f54ca1a0SArchit Taneja 493f54ca1a0SArchit Taneja if (!msm_host->cfg_hnd) 494f54ca1a0SArchit Taneja return 0; 495f54ca1a0SArchit Taneja 496f54ca1a0SArchit Taneja return dsi_bus_clk_enable(msm_host); 497f54ca1a0SArchit Taneja } 498f54ca1a0SArchit Taneja 4996b16f05aSRob Clark int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host) 500a689554bSHai Li { 5015ac17838SJonathan Marek u32 byte_intf_rate; 502a689554bSHai Li int ret; 503a689554bSHai Li 504a689554bSHai Li DBG("Set clk rates: pclk=%d, byteclk=%d", 505a689554bSHai Li msm_host->mode->clock, msm_host->byte_clk_rate); 506a689554bSHai Li 50732d3e0feSRajendra Nayak ret = dev_pm_opp_set_rate(&msm_host->pdev->dev, 50832d3e0feSRajendra Nayak msm_host->byte_clk_rate); 509a689554bSHai Li if (ret) { 51032d3e0feSRajendra Nayak pr_err("%s: dev_pm_opp_set_rate failed %d\n", __func__, ret); 5116b16f05aSRob Clark return ret; 512a689554bSHai Li } 513a689554bSHai Li 514ed9976a0SChandan Uddaraju ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); 515a689554bSHai Li if (ret) { 516a689554bSHai Li pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); 5176b16f05aSRob Clark return ret; 518a689554bSHai Li } 519a689554bSHai Li 520c1d97083SArchit Taneja if (msm_host->byte_intf_clk) { 5215ac17838SJonathan Marek /* For CPHY, byte_intf_clk is same as byte_clk */ 5225ac17838SJonathan Marek if (msm_host->cphy_mode) 5235ac17838SJonathan Marek byte_intf_rate = msm_host->byte_clk_rate; 5245ac17838SJonathan Marek else 5255ac17838SJonathan Marek byte_intf_rate = msm_host->byte_clk_rate / 2; 5265ac17838SJonathan Marek 5275ac17838SJonathan Marek ret = clk_set_rate(msm_host->byte_intf_clk, byte_intf_rate); 528c1d97083SArchit Taneja if (ret) { 529c1d97083SArchit Taneja pr_err("%s: Failed to set rate byte intf clk, %d\n", 530c1d97083SArchit Taneja __func__, ret); 5316b16f05aSRob Clark return ret; 532c1d97083SArchit Taneja } 533c1d97083SArchit Taneja } 534c1d97083SArchit Taneja 5356b16f05aSRob Clark return 0; 5366b16f05aSRob Clark } 5376b16f05aSRob Clark 5386b16f05aSRob Clark 5396b16f05aSRob Clark int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) 5406b16f05aSRob Clark { 5416b16f05aSRob Clark int ret; 5426b16f05aSRob Clark 543a689554bSHai Li ret = clk_prepare_enable(msm_host->esc_clk); 544a689554bSHai Li if (ret) { 545a689554bSHai Li pr_err("%s: Failed to enable dsi esc clk\n", __func__); 546a689554bSHai Li goto error; 547a689554bSHai Li } 548a689554bSHai Li 549a689554bSHai Li ret = clk_prepare_enable(msm_host->byte_clk); 550a689554bSHai Li if (ret) { 551a689554bSHai Li pr_err("%s: Failed to enable dsi byte clk\n", __func__); 552a689554bSHai Li goto byte_clk_err; 553a689554bSHai Li } 554a689554bSHai Li 555a689554bSHai Li ret = clk_prepare_enable(msm_host->pixel_clk); 556a689554bSHai Li if (ret) { 557a689554bSHai Li pr_err("%s: Failed to enable dsi pixel clk\n", __func__); 558a689554bSHai Li goto pixel_clk_err; 559a689554bSHai Li } 560a689554bSHai Li 561c1d97083SArchit Taneja if (msm_host->byte_intf_clk) { 562c1d97083SArchit Taneja ret = clk_prepare_enable(msm_host->byte_intf_clk); 563c1d97083SArchit Taneja if (ret) { 564c1d97083SArchit Taneja pr_err("%s: Failed to enable byte intf clk\n", 565c1d97083SArchit Taneja __func__); 566c1d97083SArchit Taneja goto byte_intf_clk_err; 567c1d97083SArchit Taneja } 568c1d97083SArchit Taneja } 569c1d97083SArchit Taneja 570a689554bSHai Li return 0; 571a689554bSHai Li 572c1d97083SArchit Taneja byte_intf_clk_err: 573c1d97083SArchit Taneja clk_disable_unprepare(msm_host->pixel_clk); 574a689554bSHai Li pixel_clk_err: 575a689554bSHai Li clk_disable_unprepare(msm_host->byte_clk); 576a689554bSHai Li byte_clk_err: 577a689554bSHai Li clk_disable_unprepare(msm_host->esc_clk); 578a689554bSHai Li error: 579a689554bSHai Li return ret; 580a689554bSHai Li } 581a689554bSHai Li 5826b16f05aSRob Clark int dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host) 5834bfa9748SArchit Taneja { 5844bfa9748SArchit Taneja int ret; 5854bfa9748SArchit Taneja 5864bfa9748SArchit Taneja DBG("Set clk rates: pclk=%d, byteclk=%d, esc_clk=%d, dsi_src_clk=%d", 5874bfa9748SArchit Taneja msm_host->mode->clock, msm_host->byte_clk_rate, 5884bfa9748SArchit Taneja msm_host->esc_clk_rate, msm_host->src_clk_rate); 5894bfa9748SArchit Taneja 5904bfa9748SArchit Taneja ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate); 5914bfa9748SArchit Taneja if (ret) { 5924bfa9748SArchit Taneja pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret); 5936b16f05aSRob Clark return ret; 5944bfa9748SArchit Taneja } 5954bfa9748SArchit Taneja 5964bfa9748SArchit Taneja ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate); 5974bfa9748SArchit Taneja if (ret) { 5984bfa9748SArchit Taneja pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret); 5996b16f05aSRob Clark return ret; 6004bfa9748SArchit Taneja } 6014bfa9748SArchit Taneja 6024bfa9748SArchit Taneja ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate); 6034bfa9748SArchit Taneja if (ret) { 6044bfa9748SArchit Taneja pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret); 6056b16f05aSRob Clark return ret; 6064bfa9748SArchit Taneja } 6074bfa9748SArchit Taneja 608ed9976a0SChandan Uddaraju ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); 6094bfa9748SArchit Taneja if (ret) { 6104bfa9748SArchit Taneja pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); 6116b16f05aSRob Clark return ret; 6124bfa9748SArchit Taneja } 6134bfa9748SArchit Taneja 6146b16f05aSRob Clark return 0; 6156b16f05aSRob Clark } 6166b16f05aSRob Clark 6176b16f05aSRob Clark int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) 6186b16f05aSRob Clark { 6196b16f05aSRob Clark int ret; 6206b16f05aSRob Clark 6214bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->byte_clk); 6224bfa9748SArchit Taneja if (ret) { 6234bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi byte clk\n", __func__); 6244bfa9748SArchit Taneja goto error; 6254bfa9748SArchit Taneja } 6264bfa9748SArchit Taneja 6274bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->esc_clk); 6284bfa9748SArchit Taneja if (ret) { 6294bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi esc clk\n", __func__); 6304bfa9748SArchit Taneja goto esc_clk_err; 6314bfa9748SArchit Taneja } 6324bfa9748SArchit Taneja 6334bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->src_clk); 6344bfa9748SArchit Taneja if (ret) { 6354bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi src clk\n", __func__); 6364bfa9748SArchit Taneja goto src_clk_err; 6374bfa9748SArchit Taneja } 6384bfa9748SArchit Taneja 6394bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->pixel_clk); 6404bfa9748SArchit Taneja if (ret) { 6414bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi pixel clk\n", __func__); 6424bfa9748SArchit Taneja goto pixel_clk_err; 6434bfa9748SArchit Taneja } 6444bfa9748SArchit Taneja 6454bfa9748SArchit Taneja return 0; 6464bfa9748SArchit Taneja 6474bfa9748SArchit Taneja pixel_clk_err: 6484bfa9748SArchit Taneja clk_disable_unprepare(msm_host->src_clk); 6494bfa9748SArchit Taneja src_clk_err: 6504bfa9748SArchit Taneja clk_disable_unprepare(msm_host->esc_clk); 6514bfa9748SArchit Taneja esc_clk_err: 6524bfa9748SArchit Taneja clk_disable_unprepare(msm_host->byte_clk); 6534bfa9748SArchit Taneja error: 6544bfa9748SArchit Taneja return ret; 6554bfa9748SArchit Taneja } 6564bfa9748SArchit Taneja 657c4d8cfe5SSibi Sankar void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host) 658c4d8cfe5SSibi Sankar { 65932d3e0feSRajendra Nayak /* Drop the performance state vote */ 66032d3e0feSRajendra Nayak dev_pm_opp_set_rate(&msm_host->pdev->dev, 0); 661c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->esc_clk); 662c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->pixel_clk); 663c4d8cfe5SSibi Sankar if (msm_host->byte_intf_clk) 664c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->byte_intf_clk); 665c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->byte_clk); 666c4d8cfe5SSibi Sankar } 667c4d8cfe5SSibi Sankar 668c4d8cfe5SSibi Sankar void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host) 669c4d8cfe5SSibi Sankar { 670c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->pixel_clk); 671c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->src_clk); 672c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->esc_clk); 673c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->byte_clk); 674c4d8cfe5SSibi Sankar } 675c4d8cfe5SSibi Sankar 6766183606dSDmitry Baryshkov static u32 dsi_get_pclk_rate(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 677c4d8cfe5SSibi Sankar { 678c4d8cfe5SSibi Sankar struct drm_display_mode *mode = msm_host->mode; 679c4d8cfe5SSibi Sankar u32 pclk_rate; 680c4d8cfe5SSibi Sankar 681c4d8cfe5SSibi Sankar pclk_rate = mode->clock * 1000; 682ed9976a0SChandan Uddaraju 683ed9976a0SChandan Uddaraju /* 6846183606dSDmitry Baryshkov * For bonded DSI mode, the current DRM mode has the complete width of the 685ed9976a0SChandan Uddaraju * panel. Since, the complete panel is driven by two DSI controllers, 686ed9976a0SChandan Uddaraju * the clock rates have to be split between the two dsi controllers. 687ed9976a0SChandan Uddaraju * Adjust the byte and pixel clock rates for each dsi host accordingly. 688ed9976a0SChandan Uddaraju */ 6896183606dSDmitry Baryshkov if (is_bonded_dsi) 690ed9976a0SChandan Uddaraju pclk_rate /= 2; 691ed9976a0SChandan Uddaraju 692a6bcddbcSSean Paul return pclk_rate; 693a6bcddbcSSean Paul } 694a6bcddbcSSean Paul 6956183606dSDmitry Baryshkov static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 696a6bcddbcSSean Paul { 697a6bcddbcSSean Paul u8 lanes = msm_host->lanes; 698a6bcddbcSSean Paul u32 bpp = dsi_get_bpp(msm_host->format); 6996183606dSDmitry Baryshkov u32 pclk_rate = dsi_get_pclk_rate(msm_host, is_bonded_dsi); 700a6bcddbcSSean Paul u64 pclk_bpp = (u64)pclk_rate * bpp; 701a6bcddbcSSean Paul 702a6bcddbcSSean Paul if (lanes == 0) { 703c4d8cfe5SSibi Sankar pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); 704a6bcddbcSSean Paul lanes = 1; 705c4d8cfe5SSibi Sankar } 706c4d8cfe5SSibi Sankar 7075ac17838SJonathan Marek /* CPHY "byte_clk" is in units of 16 bits */ 7085ac17838SJonathan Marek if (msm_host->cphy_mode) 7095ac17838SJonathan Marek do_div(pclk_bpp, (16 * lanes)); 7105ac17838SJonathan Marek else 7112d0b10fcSAbhinav Kumar do_div(pclk_bpp, (8 * lanes)); 712a6bcddbcSSean Paul 713ed9976a0SChandan Uddaraju msm_host->pixel_clk_rate = pclk_rate; 7142d0b10fcSAbhinav Kumar msm_host->byte_clk_rate = pclk_bpp; 715c4d8cfe5SSibi Sankar 716ed9976a0SChandan Uddaraju DBG("pclk=%d, bclk=%d", msm_host->pixel_clk_rate, 717ed9976a0SChandan Uddaraju msm_host->byte_clk_rate); 718c4d8cfe5SSibi Sankar 719a6bcddbcSSean Paul } 720a6bcddbcSSean Paul 7216183606dSDmitry Baryshkov int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 722a6bcddbcSSean Paul { 723a6bcddbcSSean Paul if (!msm_host->mode) { 724a6bcddbcSSean Paul pr_err("%s: mode not set\n", __func__); 725a6bcddbcSSean Paul return -EINVAL; 726a6bcddbcSSean Paul } 727a6bcddbcSSean Paul 7286183606dSDmitry Baryshkov dsi_calc_pclk(msm_host, is_bonded_dsi); 729a6bcddbcSSean Paul msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); 730a6bcddbcSSean Paul return 0; 731a6bcddbcSSean Paul } 732a6bcddbcSSean Paul 7336183606dSDmitry Baryshkov int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 734a6bcddbcSSean Paul { 735a6bcddbcSSean Paul u32 bpp = dsi_get_bpp(msm_host->format); 736a6bcddbcSSean Paul u64 pclk_bpp; 737a6bcddbcSSean Paul unsigned int esc_mhz, esc_div; 738a6bcddbcSSean Paul unsigned long byte_mhz; 739a6bcddbcSSean Paul 7406183606dSDmitry Baryshkov dsi_calc_pclk(msm_host, is_bonded_dsi); 741a6bcddbcSSean Paul 7426183606dSDmitry Baryshkov pclk_bpp = (u64)dsi_get_pclk_rate(msm_host, is_bonded_dsi) * bpp; 743a6bcddbcSSean Paul do_div(pclk_bpp, 8); 744a6bcddbcSSean Paul msm_host->src_clk_rate = pclk_bpp; 745c4d8cfe5SSibi Sankar 746c4d8cfe5SSibi Sankar /* 747c4d8cfe5SSibi Sankar * esc clock is byte clock followed by a 4 bit divider, 748c4d8cfe5SSibi Sankar * we need to find an escape clock frequency within the 749c4d8cfe5SSibi Sankar * mipi DSI spec range within the maximum divider limit 750c4d8cfe5SSibi Sankar * We iterate here between an escape clock frequencey 751c4d8cfe5SSibi Sankar * between 20 Mhz to 5 Mhz and pick up the first one 752c4d8cfe5SSibi Sankar * that can be supported by our divider 753c4d8cfe5SSibi Sankar */ 754c4d8cfe5SSibi Sankar 755c4d8cfe5SSibi Sankar byte_mhz = msm_host->byte_clk_rate / 1000000; 756c4d8cfe5SSibi Sankar 757c4d8cfe5SSibi Sankar for (esc_mhz = 20; esc_mhz >= 5; esc_mhz--) { 758c4d8cfe5SSibi Sankar esc_div = DIV_ROUND_UP(byte_mhz, esc_mhz); 759c4d8cfe5SSibi Sankar 760c4d8cfe5SSibi Sankar /* 761c4d8cfe5SSibi Sankar * TODO: Ideally, we shouldn't know what sort of divider 762c4d8cfe5SSibi Sankar * is available in mmss_cc, we're just assuming that 763c4d8cfe5SSibi Sankar * it'll always be a 4 bit divider. Need to come up with 764c4d8cfe5SSibi Sankar * a better way here. 765c4d8cfe5SSibi Sankar */ 766c4d8cfe5SSibi Sankar if (esc_div >= 1 && esc_div <= 16) 767c4d8cfe5SSibi Sankar break; 768c4d8cfe5SSibi Sankar } 769c4d8cfe5SSibi Sankar 770c4d8cfe5SSibi Sankar if (esc_mhz < 5) 771c4d8cfe5SSibi Sankar return -EINVAL; 772c4d8cfe5SSibi Sankar 773c4d8cfe5SSibi Sankar msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div; 774c4d8cfe5SSibi Sankar 775c4d8cfe5SSibi Sankar DBG("esc=%d, src=%d", msm_host->esc_clk_rate, 776c4d8cfe5SSibi Sankar msm_host->src_clk_rate); 777c4d8cfe5SSibi Sankar 778c4d8cfe5SSibi Sankar return 0; 779c4d8cfe5SSibi Sankar } 780c4d8cfe5SSibi Sankar 781a689554bSHai Li static void dsi_intr_ctrl(struct msm_dsi_host *msm_host, u32 mask, int enable) 782a689554bSHai Li { 783a689554bSHai Li u32 intr; 784a689554bSHai Li unsigned long flags; 785a689554bSHai Li 786a689554bSHai Li spin_lock_irqsave(&msm_host->intr_lock, flags); 787a689554bSHai Li intr = dsi_read(msm_host, REG_DSI_INTR_CTRL); 788a689554bSHai Li 789a689554bSHai Li if (enable) 790a689554bSHai Li intr |= mask; 791a689554bSHai Li else 792a689554bSHai Li intr &= ~mask; 793a689554bSHai Li 794a689554bSHai Li DBG("intr=%x enable=%d", intr, enable); 795a689554bSHai Li 796a689554bSHai Li dsi_write(msm_host, REG_DSI_INTR_CTRL, intr); 797a689554bSHai Li spin_unlock_irqrestore(&msm_host->intr_lock, flags); 798a689554bSHai Li } 799a689554bSHai Li 800a689554bSHai Li static inline enum dsi_traffic_mode dsi_get_traffic_mode(const u32 mode_flags) 801a689554bSHai Li { 802a689554bSHai Li if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 803a689554bSHai Li return BURST_MODE; 804a689554bSHai Li else if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) 805a689554bSHai Li return NON_BURST_SYNCH_PULSE; 806a689554bSHai Li 807a689554bSHai Li return NON_BURST_SYNCH_EVENT; 808a689554bSHai Li } 809a689554bSHai Li 810a689554bSHai Li static inline enum dsi_vid_dst_format dsi_get_vid_fmt( 811a689554bSHai Li const enum mipi_dsi_pixel_format mipi_fmt) 812a689554bSHai Li { 813a689554bSHai Li switch (mipi_fmt) { 814a689554bSHai Li case MIPI_DSI_FMT_RGB888: return VID_DST_FORMAT_RGB888; 815a689554bSHai Li case MIPI_DSI_FMT_RGB666: return VID_DST_FORMAT_RGB666_LOOSE; 816a689554bSHai Li case MIPI_DSI_FMT_RGB666_PACKED: return VID_DST_FORMAT_RGB666; 817a689554bSHai Li case MIPI_DSI_FMT_RGB565: return VID_DST_FORMAT_RGB565; 818a689554bSHai Li default: return VID_DST_FORMAT_RGB888; 819a689554bSHai Li } 820a689554bSHai Li } 821a689554bSHai Li 822a689554bSHai Li static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( 823a689554bSHai Li const enum mipi_dsi_pixel_format mipi_fmt) 824a689554bSHai Li { 825a689554bSHai Li switch (mipi_fmt) { 826a689554bSHai Li case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888; 827a689554bSHai Li case MIPI_DSI_FMT_RGB666_PACKED: 828cf606fe3SStefan Agner case MIPI_DSI_FMT_RGB666: return CMD_DST_FORMAT_RGB666; 829a689554bSHai Li case MIPI_DSI_FMT_RGB565: return CMD_DST_FORMAT_RGB565; 830a689554bSHai Li default: return CMD_DST_FORMAT_RGB888; 831a689554bSHai Li } 832a689554bSHai Li } 833a689554bSHai Li 834a689554bSHai Li static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, 835858c595aSDmitry Baryshkov struct msm_dsi_phy_shared_timings *phy_shared_timings, struct msm_dsi_phy *phy) 836a689554bSHai Li { 837a689554bSHai Li u32 flags = msm_host->mode_flags; 838a689554bSHai Li enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; 839d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 840e3ff6881SHarigovindan P u32 data = 0, lane_ctrl = 0; 841a689554bSHai Li 842a689554bSHai Li if (!enable) { 843a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, 0); 844a689554bSHai Li return; 845a689554bSHai Li } 846a689554bSHai Li 847a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO) { 848a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO_HSE) 849a689554bSHai Li data |= DSI_VID_CFG0_PULSE_MODE_HSA_HE; 8500f3b68b6SNicolas Boichat if (flags & MIPI_DSI_MODE_VIDEO_NO_HFP) 851a689554bSHai Li data |= DSI_VID_CFG0_HFP_POWER_STOP; 8520f3b68b6SNicolas Boichat if (flags & MIPI_DSI_MODE_VIDEO_NO_HBP) 853a689554bSHai Li data |= DSI_VID_CFG0_HBP_POWER_STOP; 8540f3b68b6SNicolas Boichat if (flags & MIPI_DSI_MODE_VIDEO_NO_HSA) 855a689554bSHai Li data |= DSI_VID_CFG0_HSA_POWER_STOP; 856a689554bSHai Li /* Always set low power stop mode for BLLP 857a689554bSHai Li * to let command engine send packets 858a689554bSHai Li */ 859a689554bSHai Li data |= DSI_VID_CFG0_EOF_BLLP_POWER_STOP | 860a689554bSHai Li DSI_VID_CFG0_BLLP_POWER_STOP; 861a689554bSHai Li data |= DSI_VID_CFG0_TRAFFIC_MODE(dsi_get_traffic_mode(flags)); 862a689554bSHai Li data |= DSI_VID_CFG0_DST_FORMAT(dsi_get_vid_fmt(mipi_fmt)); 863a689554bSHai Li data |= DSI_VID_CFG0_VIRT_CHANNEL(msm_host->channel); 864a689554bSHai Li dsi_write(msm_host, REG_DSI_VID_CFG0, data); 865a689554bSHai Li 866a689554bSHai Li /* Do not swap RGB colors */ 867a689554bSHai Li data = DSI_VID_CFG1_RGB_SWAP(SWAP_RGB); 868a689554bSHai Li dsi_write(msm_host, REG_DSI_VID_CFG1, 0); 869a689554bSHai Li } else { 870a689554bSHai Li /* Do not swap RGB colors */ 871a689554bSHai Li data = DSI_CMD_CFG0_RGB_SWAP(SWAP_RGB); 872a689554bSHai Li data |= DSI_CMD_CFG0_DST_FORMAT(dsi_get_cmd_fmt(mipi_fmt)); 873a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_CFG0, data); 874a689554bSHai Li 875a689554bSHai Li data = DSI_CMD_CFG1_WR_MEM_START(MIPI_DCS_WRITE_MEMORY_START) | 876a689554bSHai Li DSI_CMD_CFG1_WR_MEM_CONTINUE( 877a689554bSHai Li MIPI_DCS_WRITE_MEMORY_CONTINUE); 878a689554bSHai Li /* Always insert DCS command */ 879a689554bSHai Li data |= DSI_CMD_CFG1_INSERT_DCS_COMMAND; 880a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_CFG1, data); 881a689554bSHai Li } 882a689554bSHai Li 883a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, 884a689554bSHai Li DSI_CMD_DMA_CTRL_FROM_FRAME_BUFFER | 885a689554bSHai Li DSI_CMD_DMA_CTRL_LOW_POWER); 886a689554bSHai Li 887a689554bSHai Li data = 0; 888a689554bSHai Li /* Always assume dedicated TE pin */ 889a689554bSHai Li data |= DSI_TRIG_CTRL_TE; 890a689554bSHai Li data |= DSI_TRIG_CTRL_MDP_TRIGGER(TRIGGER_NONE); 891a689554bSHai Li data |= DSI_TRIG_CTRL_DMA_TRIGGER(TRIGGER_SW); 892a689554bSHai Li data |= DSI_TRIG_CTRL_STREAM(msm_host->channel); 893d248b61fSHai Li if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 894d248b61fSHai Li (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) 895a689554bSHai Li data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME; 896a689554bSHai Li dsi_write(msm_host, REG_DSI_TRIG_CTRL, data); 897a689554bSHai Li 898dceac340SHai Li data = DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(phy_shared_timings->clk_post) | 899dceac340SHai Li DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(phy_shared_timings->clk_pre); 900a689554bSHai Li dsi_write(msm_host, REG_DSI_CLKOUT_TIMING_CTRL, data); 901a689554bSHai Li 902dceac340SHai Li if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 903dceac340SHai Li (cfg_hnd->minor > MSM_DSI_6G_VER_MINOR_V1_0) && 904dceac340SHai Li phy_shared_timings->clk_pre_inc_by_2) 905dceac340SHai Li dsi_write(msm_host, REG_DSI_T_CLK_PRE_EXTEND, 906dceac340SHai Li DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK); 907dceac340SHai Li 908a689554bSHai Li data = 0; 9090f3b68b6SNicolas Boichat if (!(flags & MIPI_DSI_MODE_NO_EOT_PACKET)) 910a689554bSHai Li data |= DSI_EOT_PACKET_CTRL_TX_EOT_APPEND; 911a689554bSHai Li dsi_write(msm_host, REG_DSI_EOT_PACKET_CTRL, data); 912a689554bSHai Li 913a689554bSHai Li /* allow only ack-err-status to generate interrupt */ 914a689554bSHai Li dsi_write(msm_host, REG_DSI_ERR_INT_MASK0, 0x13ff3fe0); 915a689554bSHai Li 916a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 1); 917a689554bSHai Li 918a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 919a689554bSHai Li 920a689554bSHai Li data = DSI_CTRL_CLK_EN; 921a689554bSHai Li 922a689554bSHai Li DBG("lane number=%d", msm_host->lanes); 92326f7d1f4SArchit Taneja data |= ((DSI_CTRL_LANE0 << msm_host->lanes) - DSI_CTRL_LANE0); 92426f7d1f4SArchit Taneja 925a689554bSHai Li dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, 92626f7d1f4SArchit Taneja DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(msm_host->dlane_swap)); 92765c5e542SArchit Taneja 928e3ff6881SHarigovindan P if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) { 929e3ff6881SHarigovindan P lane_ctrl = dsi_read(msm_host, REG_DSI_LANE_CTRL); 930858c595aSDmitry Baryshkov 931858c595aSDmitry Baryshkov if (msm_dsi_phy_set_continuous_clock(phy, enable)) 932858c595aSDmitry Baryshkov lane_ctrl &= ~DSI_LANE_CTRL_HS_REQ_SEL_PHY; 933858c595aSDmitry Baryshkov 93465c5e542SArchit Taneja dsi_write(msm_host, REG_DSI_LANE_CTRL, 935e3ff6881SHarigovindan P lane_ctrl | DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST); 936e3ff6881SHarigovindan P } 93765c5e542SArchit Taneja 938a689554bSHai Li data |= DSI_CTRL_ENABLE; 939a689554bSHai Li 940a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, data); 9415ac17838SJonathan Marek 9425ac17838SJonathan Marek if (msm_host->cphy_mode) 9435ac17838SJonathan Marek dsi_write(msm_host, REG_DSI_CPHY_MODE_CTRL, BIT(0)); 944a689554bSHai Li } 945a689554bSHai Li 9466183606dSDmitry Baryshkov static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 947a689554bSHai Li { 948a689554bSHai Li struct drm_display_mode *mode = msm_host->mode; 949a689554bSHai Li u32 hs_start = 0, vs_start = 0; /* take sync start as 0 */ 950a689554bSHai Li u32 h_total = mode->htotal; 951a689554bSHai Li u32 v_total = mode->vtotal; 952a689554bSHai Li u32 hs_end = mode->hsync_end - mode->hsync_start; 953a689554bSHai Li u32 vs_end = mode->vsync_end - mode->vsync_start; 954a689554bSHai Li u32 ha_start = h_total - mode->hsync_start; 955a689554bSHai Li u32 ha_end = ha_start + mode->hdisplay; 956a689554bSHai Li u32 va_start = v_total - mode->vsync_start; 957a689554bSHai Li u32 va_end = va_start + mode->vdisplay; 958ed9976a0SChandan Uddaraju u32 hdisplay = mode->hdisplay; 959a689554bSHai Li u32 wc; 960a689554bSHai Li 961a689554bSHai Li DBG(""); 962a689554bSHai Li 963ed9976a0SChandan Uddaraju /* 9646183606dSDmitry Baryshkov * For bonded DSI mode, the current DRM mode has 965ed9976a0SChandan Uddaraju * the complete width of the panel. Since, the complete 966ed9976a0SChandan Uddaraju * panel is driven by two DSI controllers, the horizontal 967ed9976a0SChandan Uddaraju * timings have to be split between the two dsi controllers. 968ed9976a0SChandan Uddaraju * Adjust the DSI host timing values accordingly. 969ed9976a0SChandan Uddaraju */ 9706183606dSDmitry Baryshkov if (is_bonded_dsi) { 971ed9976a0SChandan Uddaraju h_total /= 2; 972ed9976a0SChandan Uddaraju hs_end /= 2; 973ed9976a0SChandan Uddaraju ha_start /= 2; 974ed9976a0SChandan Uddaraju ha_end /= 2; 975ed9976a0SChandan Uddaraju hdisplay /= 2; 976ed9976a0SChandan Uddaraju } 977ed9976a0SChandan Uddaraju 978a689554bSHai Li if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) { 979a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_H, 980a689554bSHai Li DSI_ACTIVE_H_START(ha_start) | 981a689554bSHai Li DSI_ACTIVE_H_END(ha_end)); 982a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_V, 983a689554bSHai Li DSI_ACTIVE_V_START(va_start) | 984a689554bSHai Li DSI_ACTIVE_V_END(va_end)); 985a689554bSHai Li dsi_write(msm_host, REG_DSI_TOTAL, 986a689554bSHai Li DSI_TOTAL_H_TOTAL(h_total - 1) | 987a689554bSHai Li DSI_TOTAL_V_TOTAL(v_total - 1)); 988a689554bSHai Li 989a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_HSYNC, 990a689554bSHai Li DSI_ACTIVE_HSYNC_START(hs_start) | 991a689554bSHai Li DSI_ACTIVE_HSYNC_END(hs_end)); 992a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_VSYNC_HPOS, 0); 993a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_VSYNC_VPOS, 994a689554bSHai Li DSI_ACTIVE_VSYNC_VPOS_START(vs_start) | 995a689554bSHai Li DSI_ACTIVE_VSYNC_VPOS_END(vs_end)); 996a689554bSHai Li } else { /* command mode */ 997a689554bSHai Li /* image data and 1 byte write_memory_start cmd */ 998ed9976a0SChandan Uddaraju wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; 999a689554bSHai Li 1000c28c82e9SRob Clark dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, 1001c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | 1002c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_CTRL_VIRTUAL_CHANNEL( 1003a689554bSHai Li msm_host->channel) | 1004c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_CTRL_DATA_TYPE( 1005a689554bSHai Li MIPI_DSI_DCS_LONG_WRITE)); 1006a689554bSHai Li 1007c28c82e9SRob Clark dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_TOTAL, 1008c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_TOTAL_H_TOTAL(hdisplay) | 1009c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_TOTAL_V_TOTAL(mode->vdisplay)); 1010a689554bSHai Li } 1011a689554bSHai Li } 1012a689554bSHai Li 1013a689554bSHai Li static void dsi_sw_reset(struct msm_dsi_host *msm_host) 1014a689554bSHai Li { 1015a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 1016a689554bSHai Li wmb(); /* clocks need to be enabled before reset */ 1017a689554bSHai Li 1018a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 1); 101978e31c42SJeffrey Hugo msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ 1020a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 0); 1021a689554bSHai Li } 1022a689554bSHai Li 1023a689554bSHai Li static void dsi_op_mode_config(struct msm_dsi_host *msm_host, 1024a689554bSHai Li bool video_mode, bool enable) 1025a689554bSHai Li { 1026a689554bSHai Li u32 dsi_ctrl; 1027a689554bSHai Li 1028a689554bSHai Li dsi_ctrl = dsi_read(msm_host, REG_DSI_CTRL); 1029a689554bSHai Li 1030a689554bSHai Li if (!enable) { 1031a689554bSHai Li dsi_ctrl &= ~(DSI_CTRL_ENABLE | DSI_CTRL_VID_MODE_EN | 1032a689554bSHai Li DSI_CTRL_CMD_MODE_EN); 1033a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_MDP_DONE | 1034a689554bSHai Li DSI_IRQ_MASK_VIDEO_DONE, 0); 1035a689554bSHai Li } else { 1036a689554bSHai Li if (video_mode) { 1037a689554bSHai Li dsi_ctrl |= DSI_CTRL_VID_MODE_EN; 1038a689554bSHai Li } else { /* command mode */ 1039a689554bSHai Li dsi_ctrl |= DSI_CTRL_CMD_MODE_EN; 1040a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_MDP_DONE, 1); 1041a689554bSHai Li } 1042a689554bSHai Li dsi_ctrl |= DSI_CTRL_ENABLE; 1043a689554bSHai Li } 1044a689554bSHai Li 1045a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, dsi_ctrl); 1046a689554bSHai Li } 1047a689554bSHai Li 1048a689554bSHai Li static void dsi_set_tx_power_mode(int mode, struct msm_dsi_host *msm_host) 1049a689554bSHai Li { 1050a689554bSHai Li u32 data; 1051a689554bSHai Li 1052a689554bSHai Li data = dsi_read(msm_host, REG_DSI_CMD_DMA_CTRL); 1053a689554bSHai Li 1054a689554bSHai Li if (mode == 0) 1055a689554bSHai Li data &= ~DSI_CMD_DMA_CTRL_LOW_POWER; 1056a689554bSHai Li else 1057a689554bSHai Li data |= DSI_CMD_DMA_CTRL_LOW_POWER; 1058a689554bSHai Li 1059a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, data); 1060a689554bSHai Li } 1061a689554bSHai Li 1062a689554bSHai Li static void dsi_wait4video_done(struct msm_dsi_host *msm_host) 1063a689554bSHai Li { 106479ebc86cSAbhinav Kumar u32 ret = 0; 106579ebc86cSAbhinav Kumar struct device *dev = &msm_host->pdev->dev; 106679ebc86cSAbhinav Kumar 1067a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 1); 1068a689554bSHai Li 1069a689554bSHai Li reinit_completion(&msm_host->video_comp); 1070a689554bSHai Li 107179ebc86cSAbhinav Kumar ret = wait_for_completion_timeout(&msm_host->video_comp, 1072a689554bSHai Li msecs_to_jiffies(70)); 1073a689554bSHai Li 10749a4a153bSNicholas Mc Guire if (ret == 0) 10756a41da17SMamta Shukla DRM_DEV_ERROR(dev, "wait for video done timed out\n"); 107679ebc86cSAbhinav Kumar 1077a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 0); 1078a689554bSHai Li } 1079a689554bSHai Li 1080a689554bSHai Li static void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host) 1081a689554bSHai Li { 1082a689554bSHai Li if (!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) 1083a689554bSHai Li return; 1084a689554bSHai Li 10859c5638d7SAbhinav Kumar if (msm_host->power_on && msm_host->enabled) { 1086a689554bSHai Li dsi_wait4video_done(msm_host); 1087a689554bSHai Li /* delay 4 ms to skip BLLP */ 1088a689554bSHai Li usleep_range(2000, 4000); 1089a689554bSHai Li } 1090a689554bSHai Li } 1091a689554bSHai Li 1092c4d8cfe5SSibi Sankar int dsi_tx_buf_alloc_6g(struct msm_dsi_host *msm_host, int size) 1093c4d8cfe5SSibi Sankar { 1094c4d8cfe5SSibi Sankar struct drm_device *dev = msm_host->dev; 1095c4d8cfe5SSibi Sankar struct msm_drm_private *priv = dev->dev_private; 1096c4d8cfe5SSibi Sankar uint64_t iova; 1097c4d8cfe5SSibi Sankar u8 *data; 1098c4d8cfe5SSibi Sankar 1099a5fc7aa9SJonathan Marek data = msm_gem_kernel_new(dev, size, MSM_BO_WC, 1100c4d8cfe5SSibi Sankar priv->kms->aspace, 1101c4d8cfe5SSibi Sankar &msm_host->tx_gem_obj, &iova); 1102c4d8cfe5SSibi Sankar 1103c4d8cfe5SSibi Sankar if (IS_ERR(data)) { 1104c4d8cfe5SSibi Sankar msm_host->tx_gem_obj = NULL; 1105c4d8cfe5SSibi Sankar return PTR_ERR(data); 1106c4d8cfe5SSibi Sankar } 1107c4d8cfe5SSibi Sankar 11080815d774SJordan Crouse msm_gem_object_set_name(msm_host->tx_gem_obj, "tx_gem"); 11090815d774SJordan Crouse 1110c4d8cfe5SSibi Sankar msm_host->tx_size = msm_host->tx_gem_obj->size; 1111c4d8cfe5SSibi Sankar 1112c4d8cfe5SSibi Sankar return 0; 1113c4d8cfe5SSibi Sankar } 1114c4d8cfe5SSibi Sankar 1115c4d8cfe5SSibi Sankar int dsi_tx_buf_alloc_v2(struct msm_dsi_host *msm_host, int size) 1116c4d8cfe5SSibi Sankar { 1117c4d8cfe5SSibi Sankar struct drm_device *dev = msm_host->dev; 1118c4d8cfe5SSibi Sankar 1119c4d8cfe5SSibi Sankar msm_host->tx_buf = dma_alloc_coherent(dev->dev, size, 1120c4d8cfe5SSibi Sankar &msm_host->tx_buf_paddr, GFP_KERNEL); 1121c4d8cfe5SSibi Sankar if (!msm_host->tx_buf) 1122c4d8cfe5SSibi Sankar return -ENOMEM; 1123c4d8cfe5SSibi Sankar 1124c4d8cfe5SSibi Sankar msm_host->tx_size = size; 1125c4d8cfe5SSibi Sankar 1126c4d8cfe5SSibi Sankar return 0; 1127c4d8cfe5SSibi Sankar } 1128c4d8cfe5SSibi Sankar 1129a689554bSHai Li static void dsi_tx_buf_free(struct msm_dsi_host *msm_host) 1130a689554bSHai Li { 1131a689554bSHai Li struct drm_device *dev = msm_host->dev; 113274d3a3a7SSean Paul struct msm_drm_private *priv; 1133a689554bSHai Li 113474d3a3a7SSean Paul /* 113574d3a3a7SSean Paul * This is possible if we're tearing down before we've had a chance to 113674d3a3a7SSean Paul * fully initialize. A very real possibility if our probe is deferred, 113774d3a3a7SSean Paul * in which case we'll hit msm_dsi_host_destroy() without having run 113874d3a3a7SSean Paul * through the dsi_tx_buf_alloc(). 113974d3a3a7SSean Paul */ 114074d3a3a7SSean Paul if (!dev) 114174d3a3a7SSean Paul return; 114274d3a3a7SSean Paul 114374d3a3a7SSean Paul priv = dev->dev_private; 1144a689554bSHai Li if (msm_host->tx_gem_obj) { 11457ad0e8cfSJordan Crouse msm_gem_unpin_iova(msm_host->tx_gem_obj, priv->kms->aspace); 1146f7d33950SEmil Velikov drm_gem_object_put(msm_host->tx_gem_obj); 1147a689554bSHai Li msm_host->tx_gem_obj = NULL; 1148a689554bSHai Li } 11494ff9d4cbSArchit Taneja 11504ff9d4cbSArchit Taneja if (msm_host->tx_buf) 11514ff9d4cbSArchit Taneja dma_free_coherent(dev->dev, msm_host->tx_size, msm_host->tx_buf, 11524ff9d4cbSArchit Taneja msm_host->tx_buf_paddr); 1153a689554bSHai Li } 1154a689554bSHai Li 1155c4d8cfe5SSibi Sankar void *dsi_tx_buf_get_6g(struct msm_dsi_host *msm_host) 1156c4d8cfe5SSibi Sankar { 1157c4d8cfe5SSibi Sankar return msm_gem_get_vaddr(msm_host->tx_gem_obj); 1158c4d8cfe5SSibi Sankar } 1159c4d8cfe5SSibi Sankar 1160c4d8cfe5SSibi Sankar void *dsi_tx_buf_get_v2(struct msm_dsi_host *msm_host) 1161c4d8cfe5SSibi Sankar { 1162c4d8cfe5SSibi Sankar return msm_host->tx_buf; 1163c4d8cfe5SSibi Sankar } 1164c4d8cfe5SSibi Sankar 1165c4d8cfe5SSibi Sankar void dsi_tx_buf_put_6g(struct msm_dsi_host *msm_host) 1166c4d8cfe5SSibi Sankar { 1167c4d8cfe5SSibi Sankar msm_gem_put_vaddr(msm_host->tx_gem_obj); 1168c4d8cfe5SSibi Sankar } 1169c4d8cfe5SSibi Sankar 1170a689554bSHai Li /* 1171a689554bSHai Li * prepare cmd buffer to be txed 1172a689554bSHai Li */ 11734ff9d4cbSArchit Taneja static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, 1174a689554bSHai Li const struct mipi_dsi_msg *msg) 1175a689554bSHai Li { 11764ff9d4cbSArchit Taneja const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 1177a689554bSHai Li struct mipi_dsi_packet packet; 1178a689554bSHai Li int len; 1179a689554bSHai Li int ret; 1180a689554bSHai Li u8 *data; 1181a689554bSHai Li 1182a689554bSHai Li ret = mipi_dsi_create_packet(&packet, msg); 1183a689554bSHai Li if (ret) { 1184a689554bSHai Li pr_err("%s: create packet failed, %d\n", __func__, ret); 1185a689554bSHai Li return ret; 1186a689554bSHai Li } 1187a689554bSHai Li len = (packet.size + 3) & (~0x3); 1188a689554bSHai Li 11894ff9d4cbSArchit Taneja if (len > msm_host->tx_size) { 1190a689554bSHai Li pr_err("%s: packet size is too big\n", __func__); 1191a689554bSHai Li return -EINVAL; 1192a689554bSHai Li } 1193a689554bSHai Li 11948f7ca540SSibi Sankar data = cfg_hnd->ops->tx_buf_get(msm_host); 1195a689554bSHai Li if (IS_ERR(data)) { 1196a689554bSHai Li ret = PTR_ERR(data); 1197a689554bSHai Li pr_err("%s: get vaddr failed, %d\n", __func__, ret); 1198a689554bSHai Li return ret; 1199a689554bSHai Li } 1200a689554bSHai Li 1201a689554bSHai Li /* MSM specific command format in memory */ 1202a689554bSHai Li data[0] = packet.header[1]; 1203a689554bSHai Li data[1] = packet.header[2]; 1204a689554bSHai Li data[2] = packet.header[0]; 1205a689554bSHai Li data[3] = BIT(7); /* Last packet */ 1206a689554bSHai Li if (mipi_dsi_packet_format_is_long(msg->type)) 1207a689554bSHai Li data[3] |= BIT(6); 1208a689554bSHai Li if (msg->rx_buf && msg->rx_len) 1209a689554bSHai Li data[3] |= BIT(5); 1210a689554bSHai Li 1211a689554bSHai Li /* Long packet */ 1212a689554bSHai Li if (packet.payload && packet.payload_length) 1213a689554bSHai Li memcpy(data + 4, packet.payload, packet.payload_length); 1214a689554bSHai Li 1215a689554bSHai Li /* Append 0xff to the end */ 1216a689554bSHai Li if (packet.size < len) 1217a689554bSHai Li memset(data + packet.size, 0xff, len - packet.size); 1218a689554bSHai Li 12198f7ca540SSibi Sankar if (cfg_hnd->ops->tx_buf_put) 12208f7ca540SSibi Sankar cfg_hnd->ops->tx_buf_put(msm_host); 122118f23049SRob Clark 1222a689554bSHai Li return len; 1223a689554bSHai Li } 1224a689554bSHai Li 1225a689554bSHai Li /* 1226a689554bSHai Li * dsi_short_read1_resp: 1 parameter 1227a689554bSHai Li */ 1228a689554bSHai Li static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) 1229a689554bSHai Li { 1230a689554bSHai Li u8 *data = msg->rx_buf; 1231a689554bSHai Li if (data && (msg->rx_len >= 1)) { 1232a689554bSHai Li *data = buf[1]; /* strip out dcs type */ 1233a689554bSHai Li return 1; 1234a689554bSHai Li } else { 1235981371f3SStephane Viau pr_err("%s: read data does not match with rx_buf len %zu\n", 1236a689554bSHai Li __func__, msg->rx_len); 1237a689554bSHai Li return -EINVAL; 1238a689554bSHai Li } 1239a689554bSHai Li } 1240a689554bSHai Li 1241a689554bSHai Li /* 1242a689554bSHai Li * dsi_short_read2_resp: 2 parameter 1243a689554bSHai Li */ 1244a689554bSHai Li static int dsi_short_read2_resp(u8 *buf, const struct mipi_dsi_msg *msg) 1245a689554bSHai Li { 1246a689554bSHai Li u8 *data = msg->rx_buf; 1247a689554bSHai Li if (data && (msg->rx_len >= 2)) { 1248a689554bSHai Li data[0] = buf[1]; /* strip out dcs type */ 1249a689554bSHai Li data[1] = buf[2]; 1250a689554bSHai Li return 2; 1251a689554bSHai Li } else { 1252981371f3SStephane Viau pr_err("%s: read data does not match with rx_buf len %zu\n", 1253a689554bSHai Li __func__, msg->rx_len); 1254a689554bSHai Li return -EINVAL; 1255a689554bSHai Li } 1256a689554bSHai Li } 1257a689554bSHai Li 1258a689554bSHai Li static int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg) 1259a689554bSHai Li { 1260a689554bSHai Li /* strip out 4 byte dcs header */ 1261a689554bSHai Li if (msg->rx_buf && msg->rx_len) 1262a689554bSHai Li memcpy(msg->rx_buf, buf + 4, msg->rx_len); 1263a689554bSHai Li 1264a689554bSHai Li return msg->rx_len; 1265a689554bSHai Li } 1266a689554bSHai Li 1267c4d8cfe5SSibi Sankar int dsi_dma_base_get_6g(struct msm_dsi_host *msm_host, uint64_t *dma_base) 1268c4d8cfe5SSibi Sankar { 1269c4d8cfe5SSibi Sankar struct drm_device *dev = msm_host->dev; 1270c4d8cfe5SSibi Sankar struct msm_drm_private *priv = dev->dev_private; 1271c4d8cfe5SSibi Sankar 1272c4d8cfe5SSibi Sankar if (!dma_base) 1273c4d8cfe5SSibi Sankar return -EINVAL; 1274c4d8cfe5SSibi Sankar 12759fe041f6SJordan Crouse return msm_gem_get_and_pin_iova(msm_host->tx_gem_obj, 1276c4d8cfe5SSibi Sankar priv->kms->aspace, dma_base); 1277c4d8cfe5SSibi Sankar } 1278c4d8cfe5SSibi Sankar 1279c4d8cfe5SSibi Sankar int dsi_dma_base_get_v2(struct msm_dsi_host *msm_host, uint64_t *dma_base) 1280c4d8cfe5SSibi Sankar { 1281c4d8cfe5SSibi Sankar if (!dma_base) 1282c4d8cfe5SSibi Sankar return -EINVAL; 1283c4d8cfe5SSibi Sankar 1284c4d8cfe5SSibi Sankar *dma_base = msm_host->tx_buf_paddr; 1285c4d8cfe5SSibi Sankar return 0; 1286c4d8cfe5SSibi Sankar } 1287c4d8cfe5SSibi Sankar 1288a689554bSHai Li static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) 1289a689554bSHai Li { 12904ff9d4cbSArchit Taneja const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 1291a689554bSHai Li int ret; 129278babc16SRob Clark uint64_t dma_base; 1293a689554bSHai Li bool triggered; 1294a689554bSHai Li 12958f7ca540SSibi Sankar ret = cfg_hnd->ops->dma_base_get(msm_host, &dma_base); 1296a689554bSHai Li if (ret) { 1297a689554bSHai Li pr_err("%s: failed to get iova: %d\n", __func__, ret); 1298a689554bSHai Li return ret; 1299a689554bSHai Li } 1300a689554bSHai Li 1301a689554bSHai Li reinit_completion(&msm_host->dma_comp); 1302a689554bSHai Li 1303a689554bSHai Li dsi_wait4video_eng_busy(msm_host); 1304a689554bSHai Li 1305a689554bSHai Li triggered = msm_dsi_manager_cmd_xfer_trigger( 13064ff9d4cbSArchit Taneja msm_host->id, dma_base, len); 1307a689554bSHai Li if (triggered) { 1308a689554bSHai Li ret = wait_for_completion_timeout(&msm_host->dma_comp, 1309a689554bSHai Li msecs_to_jiffies(200)); 1310a689554bSHai Li DBG("ret=%d", ret); 1311a689554bSHai Li if (ret == 0) 1312a689554bSHai Li ret = -ETIMEDOUT; 1313a689554bSHai Li else 1314a689554bSHai Li ret = len; 1315a689554bSHai Li } else 1316a689554bSHai Li ret = len; 1317a689554bSHai Li 1318a689554bSHai Li return ret; 1319a689554bSHai Li } 1320a689554bSHai Li 1321a689554bSHai Li static int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host, 1322a689554bSHai Li u8 *buf, int rx_byte, int pkt_size) 1323a689554bSHai Li { 13242e3cc607Szhengbin u32 *temp, data; 1325a689554bSHai Li int i, j = 0, cnt; 1326a689554bSHai Li u32 read_cnt; 1327a689554bSHai Li u8 reg[16]; 1328a689554bSHai Li int repeated_bytes = 0; 1329a689554bSHai Li int buf_offset = buf - msm_host->rx_buf; 1330a689554bSHai Li 1331a689554bSHai Li temp = (u32 *)reg; 1332a689554bSHai Li cnt = (rx_byte + 3) >> 2; 1333a689554bSHai Li if (cnt > 4) 1334a689554bSHai Li cnt = 4; /* 4 x 32 bits registers only */ 1335a689554bSHai Li 1336ec1936ebSHai Li if (rx_byte == 4) 1337ec1936ebSHai Li read_cnt = 4; 1338ec1936ebSHai Li else 1339ec1936ebSHai Li read_cnt = pkt_size + 6; 1340a689554bSHai Li 1341a689554bSHai Li /* 1342a689554bSHai Li * In case of multiple reads from the panel, after the first read, there 1343a689554bSHai Li * is possibility that there are some bytes in the payload repeating in 1344a689554bSHai Li * the RDBK_DATA registers. Since we read all the parameters from the 1345a689554bSHai Li * panel right from the first byte for every pass. We need to skip the 1346a689554bSHai Li * repeating bytes and then append the new parameters to the rx buffer. 1347a689554bSHai Li */ 1348a689554bSHai Li if (read_cnt > 16) { 1349a689554bSHai Li int bytes_shifted; 1350a689554bSHai Li /* Any data more than 16 bytes will be shifted out. 1351a689554bSHai Li * The temp read buffer should already contain these bytes. 1352a689554bSHai Li * The remaining bytes in read buffer are the repeated bytes. 1353a689554bSHai Li */ 1354a689554bSHai Li bytes_shifted = read_cnt - 16; 1355a689554bSHai Li repeated_bytes = buf_offset - bytes_shifted; 1356a689554bSHai Li } 1357a689554bSHai Li 1358a689554bSHai Li for (i = cnt - 1; i >= 0; i--) { 1359a689554bSHai Li data = dsi_read(msm_host, REG_DSI_RDBK_DATA(i)); 1360a689554bSHai Li *temp++ = ntohl(data); /* to host byte order */ 1361a689554bSHai Li DBG("data = 0x%x and ntohl(data) = 0x%x", data, ntohl(data)); 1362a689554bSHai Li } 1363a689554bSHai Li 1364a689554bSHai Li for (i = repeated_bytes; i < 16; i++) 1365a689554bSHai Li buf[j++] = reg[i]; 1366a689554bSHai Li 1367a689554bSHai Li return j; 1368a689554bSHai Li } 1369a689554bSHai Li 1370a689554bSHai Li static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, 1371a689554bSHai Li const struct mipi_dsi_msg *msg) 1372a689554bSHai Li { 1373a689554bSHai Li int len, ret; 1374a689554bSHai Li int bllp_len = msm_host->mode->hdisplay * 1375a689554bSHai Li dsi_get_bpp(msm_host->format) / 8; 1376a689554bSHai Li 13774ff9d4cbSArchit Taneja len = dsi_cmd_dma_add(msm_host, msg); 1378a689554bSHai Li if (!len) { 1379a689554bSHai Li pr_err("%s: failed to add cmd type = 0x%x\n", 1380a689554bSHai Li __func__, msg->type); 1381a689554bSHai Li return -EINVAL; 1382a689554bSHai Li } 1383a689554bSHai Li 1384a689554bSHai Li /* for video mode, do not send cmds more than 1385a689554bSHai Li * one pixel line, since it only transmit it 1386a689554bSHai Li * during BLLP. 1387a689554bSHai Li */ 1388a689554bSHai Li /* TODO: if the command is sent in LP mode, the bit rate is only 1389a689554bSHai Li * half of esc clk rate. In this case, if the video is already 1390a689554bSHai Li * actively streaming, we need to check more carefully if the 1391a689554bSHai Li * command can be fit into one BLLP. 1392a689554bSHai Li */ 1393a689554bSHai Li if ((msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) && (len > bllp_len)) { 1394a689554bSHai Li pr_err("%s: cmd cannot fit into BLLP period, len=%d\n", 1395a689554bSHai Li __func__, len); 1396a689554bSHai Li return -EINVAL; 1397a689554bSHai Li } 1398a689554bSHai Li 1399a689554bSHai Li ret = dsi_cmd_dma_tx(msm_host, len); 1400a689554bSHai Li if (ret < len) { 1401a689554bSHai Li pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d\n", 1402a689554bSHai Li __func__, msg->type, (*(u8 *)(msg->tx_buf)), len); 1403a689554bSHai Li return -ECOMM; 1404a689554bSHai Li } 1405a689554bSHai Li 1406a689554bSHai Li return len; 1407a689554bSHai Li } 1408a689554bSHai Li 1409a689554bSHai Li static void dsi_sw_reset_restore(struct msm_dsi_host *msm_host) 1410a689554bSHai Li { 1411a689554bSHai Li u32 data0, data1; 1412a689554bSHai Li 1413a689554bSHai Li data0 = dsi_read(msm_host, REG_DSI_CTRL); 1414a689554bSHai Li data1 = data0; 1415a689554bSHai Li data1 &= ~DSI_CTRL_ENABLE; 1416a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, data1); 1417a689554bSHai Li /* 1418a689554bSHai Li * dsi controller need to be disabled before 1419a689554bSHai Li * clocks turned on 1420a689554bSHai Li */ 1421a689554bSHai Li wmb(); 1422a689554bSHai Li 1423a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 1424a689554bSHai Li wmb(); /* make sure clocks enabled */ 1425a689554bSHai Li 1426a689554bSHai Li /* dsi controller can only be reset while clocks are running */ 1427a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 1); 142878e31c42SJeffrey Hugo msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ 1429a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 0); 1430a689554bSHai Li wmb(); /* controller out of reset */ 1431a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, data0); 1432a689554bSHai Li wmb(); /* make sure dsi controller enabled again */ 1433a689554bSHai Li } 1434a689554bSHai Li 14358d23ea40SArchit Taneja static void dsi_hpd_worker(struct work_struct *work) 14368d23ea40SArchit Taneja { 14378d23ea40SArchit Taneja struct msm_dsi_host *msm_host = 14388d23ea40SArchit Taneja container_of(work, struct msm_dsi_host, hpd_work); 14398d23ea40SArchit Taneja 14408d23ea40SArchit Taneja drm_helper_hpd_irq_event(msm_host->dev); 14418d23ea40SArchit Taneja } 14428d23ea40SArchit Taneja 1443a689554bSHai Li static void dsi_err_worker(struct work_struct *work) 1444a689554bSHai Li { 1445a689554bSHai Li struct msm_dsi_host *msm_host = 1446a689554bSHai Li container_of(work, struct msm_dsi_host, err_work); 1447a689554bSHai Li u32 status = msm_host->err_work_state; 1448a689554bSHai Li 1449ff431fa4SRob Clark pr_err_ratelimited("%s: status=%x\n", __func__, status); 1450a689554bSHai Li if (status & DSI_ERR_STATE_MDP_FIFO_UNDERFLOW) 1451a689554bSHai Li dsi_sw_reset_restore(msm_host); 1452a689554bSHai Li 1453a689554bSHai Li /* It is safe to clear here because error irq is disabled. */ 1454a689554bSHai Li msm_host->err_work_state = 0; 1455a689554bSHai Li 1456a689554bSHai Li /* enable dsi error interrupt */ 1457a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 1); 1458a689554bSHai Li } 1459a689554bSHai Li 1460a689554bSHai Li static void dsi_ack_err_status(struct msm_dsi_host *msm_host) 1461a689554bSHai Li { 1462a689554bSHai Li u32 status; 1463a689554bSHai Li 1464a689554bSHai Li status = dsi_read(msm_host, REG_DSI_ACK_ERR_STATUS); 1465a689554bSHai Li 1466a689554bSHai Li if (status) { 1467a689554bSHai Li dsi_write(msm_host, REG_DSI_ACK_ERR_STATUS, status); 1468a689554bSHai Li /* Writing of an extra 0 needed to clear error bits */ 1469a689554bSHai Li dsi_write(msm_host, REG_DSI_ACK_ERR_STATUS, 0); 1470a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_ACK; 1471a689554bSHai Li } 1472a689554bSHai Li } 1473a689554bSHai Li 1474a689554bSHai Li static void dsi_timeout_status(struct msm_dsi_host *msm_host) 1475a689554bSHai Li { 1476a689554bSHai Li u32 status; 1477a689554bSHai Li 1478a689554bSHai Li status = dsi_read(msm_host, REG_DSI_TIMEOUT_STATUS); 1479a689554bSHai Li 1480a689554bSHai Li if (status) { 1481a689554bSHai Li dsi_write(msm_host, REG_DSI_TIMEOUT_STATUS, status); 1482a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_TIMEOUT; 1483a689554bSHai Li } 1484a689554bSHai Li } 1485a689554bSHai Li 1486a689554bSHai Li static void dsi_dln0_phy_err(struct msm_dsi_host *msm_host) 1487a689554bSHai Li { 1488a689554bSHai Li u32 status; 1489a689554bSHai Li 1490a689554bSHai Li status = dsi_read(msm_host, REG_DSI_DLN0_PHY_ERR); 1491a689554bSHai Li 149201199361SArchit Taneja if (status & (DSI_DLN0_PHY_ERR_DLN0_ERR_ESC | 149301199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC | 149401199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL | 149501199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 | 149601199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1)) { 1497a689554bSHai Li dsi_write(msm_host, REG_DSI_DLN0_PHY_ERR, status); 1498a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_DLN0_PHY; 1499a689554bSHai Li } 1500a689554bSHai Li } 1501a689554bSHai Li 1502a689554bSHai Li static void dsi_fifo_status(struct msm_dsi_host *msm_host) 1503a689554bSHai Li { 1504a689554bSHai Li u32 status; 1505a689554bSHai Li 1506a689554bSHai Li status = dsi_read(msm_host, REG_DSI_FIFO_STATUS); 1507a689554bSHai Li 1508a689554bSHai Li /* fifo underflow, overflow */ 1509a689554bSHai Li if (status) { 1510a689554bSHai Li dsi_write(msm_host, REG_DSI_FIFO_STATUS, status); 1511a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_FIFO; 1512a689554bSHai Li if (status & DSI_FIFO_STATUS_CMD_MDP_FIFO_UNDERFLOW) 1513a689554bSHai Li msm_host->err_work_state |= 1514a689554bSHai Li DSI_ERR_STATE_MDP_FIFO_UNDERFLOW; 1515a689554bSHai Li } 1516a689554bSHai Li } 1517a689554bSHai Li 1518a689554bSHai Li static void dsi_status(struct msm_dsi_host *msm_host) 1519a689554bSHai Li { 1520a689554bSHai Li u32 status; 1521a689554bSHai Li 1522a689554bSHai Li status = dsi_read(msm_host, REG_DSI_STATUS0); 1523a689554bSHai Li 1524a689554bSHai Li if (status & DSI_STATUS0_INTERLEAVE_OP_CONTENTION) { 1525a689554bSHai Li dsi_write(msm_host, REG_DSI_STATUS0, status); 1526a689554bSHai Li msm_host->err_work_state |= 1527a689554bSHai Li DSI_ERR_STATE_INTERLEAVE_OP_CONTENTION; 1528a689554bSHai Li } 1529a689554bSHai Li } 1530a689554bSHai Li 1531a689554bSHai Li static void dsi_clk_status(struct msm_dsi_host *msm_host) 1532a689554bSHai Li { 1533a689554bSHai Li u32 status; 1534a689554bSHai Li 1535a689554bSHai Li status = dsi_read(msm_host, REG_DSI_CLK_STATUS); 1536a689554bSHai Li 1537a689554bSHai Li if (status & DSI_CLK_STATUS_PLL_UNLOCKED) { 1538a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_STATUS, status); 1539a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_PLL_UNLOCKED; 1540a689554bSHai Li } 1541a689554bSHai Li } 1542a689554bSHai Li 1543a689554bSHai Li static void dsi_error(struct msm_dsi_host *msm_host) 1544a689554bSHai Li { 1545a689554bSHai Li /* disable dsi error interrupt */ 1546a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 0); 1547a689554bSHai Li 1548a689554bSHai Li dsi_clk_status(msm_host); 1549a689554bSHai Li dsi_fifo_status(msm_host); 1550a689554bSHai Li dsi_ack_err_status(msm_host); 1551a689554bSHai Li dsi_timeout_status(msm_host); 1552a689554bSHai Li dsi_status(msm_host); 1553a689554bSHai Li dsi_dln0_phy_err(msm_host); 1554a689554bSHai Li 1555a689554bSHai Li queue_work(msm_host->workqueue, &msm_host->err_work); 1556a689554bSHai Li } 1557a689554bSHai Li 1558a689554bSHai Li static irqreturn_t dsi_host_irq(int irq, void *ptr) 1559a689554bSHai Li { 1560a689554bSHai Li struct msm_dsi_host *msm_host = ptr; 1561a689554bSHai Li u32 isr; 1562a689554bSHai Li unsigned long flags; 1563a689554bSHai Li 1564a689554bSHai Li if (!msm_host->ctrl_base) 1565a689554bSHai Li return IRQ_HANDLED; 1566a689554bSHai Li 1567a689554bSHai Li spin_lock_irqsave(&msm_host->intr_lock, flags); 1568a689554bSHai Li isr = dsi_read(msm_host, REG_DSI_INTR_CTRL); 1569a689554bSHai Li dsi_write(msm_host, REG_DSI_INTR_CTRL, isr); 1570a689554bSHai Li spin_unlock_irqrestore(&msm_host->intr_lock, flags); 1571a689554bSHai Li 1572a689554bSHai Li DBG("isr=0x%x, id=%d", isr, msm_host->id); 1573a689554bSHai Li 1574a689554bSHai Li if (isr & DSI_IRQ_ERROR) 1575a689554bSHai Li dsi_error(msm_host); 1576a689554bSHai Li 1577a689554bSHai Li if (isr & DSI_IRQ_VIDEO_DONE) 1578a689554bSHai Li complete(&msm_host->video_comp); 1579a689554bSHai Li 1580a689554bSHai Li if (isr & DSI_IRQ_CMD_DMA_DONE) 1581a689554bSHai Li complete(&msm_host->dma_comp); 1582a689554bSHai Li 1583a689554bSHai Li return IRQ_HANDLED; 1584a689554bSHai Li } 1585a689554bSHai Li 1586a689554bSHai Li static int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host, 1587a689554bSHai Li struct device *panel_device) 1588a689554bSHai Li { 15899590e69dSUwe Kleine-König msm_host->disp_en_gpio = devm_gpiod_get_optional(panel_device, 15909590e69dSUwe Kleine-König "disp-enable", 15919590e69dSUwe Kleine-König GPIOD_OUT_LOW); 1592a689554bSHai Li if (IS_ERR(msm_host->disp_en_gpio)) { 1593a689554bSHai Li DBG("cannot get disp-enable-gpios %ld", 1594a689554bSHai Li PTR_ERR(msm_host->disp_en_gpio)); 15959590e69dSUwe Kleine-König return PTR_ERR(msm_host->disp_en_gpio); 1596a689554bSHai Li } 1597a689554bSHai Li 159860d05cb4SArchit Taneja msm_host->te_gpio = devm_gpiod_get_optional(panel_device, "disp-te", 159960d05cb4SArchit Taneja GPIOD_IN); 1600a689554bSHai Li if (IS_ERR(msm_host->te_gpio)) { 1601a689554bSHai Li DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio)); 16029590e69dSUwe Kleine-König return PTR_ERR(msm_host->te_gpio); 1603a689554bSHai Li } 1604a689554bSHai Li 1605a689554bSHai Li return 0; 1606a689554bSHai Li } 1607a689554bSHai Li 1608a689554bSHai Li static int dsi_host_attach(struct mipi_dsi_host *host, 1609a689554bSHai Li struct mipi_dsi_device *dsi) 1610a689554bSHai Li { 1611a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1612a689554bSHai Li int ret; 1613a689554bSHai Li 161426f7d1f4SArchit Taneja if (dsi->lanes > msm_host->num_data_lanes) 161526f7d1f4SArchit Taneja return -EINVAL; 161626f7d1f4SArchit Taneja 1617a689554bSHai Li msm_host->channel = dsi->channel; 1618a689554bSHai Li msm_host->lanes = dsi->lanes; 1619a689554bSHai Li msm_host->format = dsi->format; 1620a689554bSHai Li msm_host->mode_flags = dsi->mode_flags; 1621a689554bSHai Li 1622a689554bSHai Li /* Some gpios defined in panel DT need to be controlled by host */ 1623a689554bSHai Li ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); 1624a689554bSHai Li if (ret) 1625a689554bSHai Li return ret; 1626a689554bSHai Li 1627*8f59ee9aSRob Clark ret = dsi_dev_attach(msm_host->pdev); 1628*8f59ee9aSRob Clark if (ret) 1629*8f59ee9aSRob Clark return ret; 1630*8f59ee9aSRob Clark 1631a689554bSHai Li DBG("id=%d", msm_host->id); 1632a689554bSHai Li if (msm_host->dev) 16338d23ea40SArchit Taneja queue_work(msm_host->workqueue, &msm_host->hpd_work); 1634a689554bSHai Li 1635a689554bSHai Li return 0; 1636a689554bSHai Li } 1637a689554bSHai Li 1638a689554bSHai Li static int dsi_host_detach(struct mipi_dsi_host *host, 1639a689554bSHai Li struct mipi_dsi_device *dsi) 1640a689554bSHai Li { 1641a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1642a689554bSHai Li 1643*8f59ee9aSRob Clark dsi_dev_detach(msm_host->pdev); 1644*8f59ee9aSRob Clark 1645a9ddac9cSArchit Taneja msm_host->device_node = NULL; 1646a689554bSHai Li 1647a689554bSHai Li DBG("id=%d", msm_host->id); 1648a689554bSHai Li if (msm_host->dev) 16498d23ea40SArchit Taneja queue_work(msm_host->workqueue, &msm_host->hpd_work); 1650a689554bSHai Li 1651a689554bSHai Li return 0; 1652a689554bSHai Li } 1653a689554bSHai Li 1654a689554bSHai Li static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, 1655a689554bSHai Li const struct mipi_dsi_msg *msg) 1656a689554bSHai Li { 1657a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1658a689554bSHai Li int ret; 1659a689554bSHai Li 1660a689554bSHai Li if (!msg || !msm_host->power_on) 1661a689554bSHai Li return -EINVAL; 1662a689554bSHai Li 1663a689554bSHai Li mutex_lock(&msm_host->cmd_mutex); 1664a689554bSHai Li ret = msm_dsi_manager_cmd_xfer(msm_host->id, msg); 1665a689554bSHai Li mutex_unlock(&msm_host->cmd_mutex); 1666a689554bSHai Li 1667a689554bSHai Li return ret; 1668a689554bSHai Li } 1669a689554bSHai Li 16708b6947a8SRikard Falkeborn static const struct mipi_dsi_host_ops dsi_host_ops = { 1671a689554bSHai Li .attach = dsi_host_attach, 1672a689554bSHai Li .detach = dsi_host_detach, 1673a689554bSHai Li .transfer = dsi_host_transfer, 1674a689554bSHai Li }; 1675a689554bSHai Li 167626f7d1f4SArchit Taneja /* 167726f7d1f4SArchit Taneja * List of supported physical to logical lane mappings. 167826f7d1f4SArchit Taneja * For example, the 2nd entry represents the following mapping: 167926f7d1f4SArchit Taneja * 168026f7d1f4SArchit Taneja * "3012": Logic 3->Phys 0; Logic 0->Phys 1; Logic 1->Phys 2; Logic 2->Phys 3; 168126f7d1f4SArchit Taneja */ 168226f7d1f4SArchit Taneja static const int supported_data_lane_swaps[][4] = { 168326f7d1f4SArchit Taneja { 0, 1, 2, 3 }, 168426f7d1f4SArchit Taneja { 3, 0, 1, 2 }, 168526f7d1f4SArchit Taneja { 2, 3, 0, 1 }, 168626f7d1f4SArchit Taneja { 1, 2, 3, 0 }, 168726f7d1f4SArchit Taneja { 0, 3, 2, 1 }, 168826f7d1f4SArchit Taneja { 1, 0, 3, 2 }, 168926f7d1f4SArchit Taneja { 2, 1, 0, 3 }, 169026f7d1f4SArchit Taneja { 3, 2, 1, 0 }, 169126f7d1f4SArchit Taneja }; 169226f7d1f4SArchit Taneja 169326f7d1f4SArchit Taneja static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, 169426f7d1f4SArchit Taneja struct device_node *ep) 169526f7d1f4SArchit Taneja { 169626f7d1f4SArchit Taneja struct device *dev = &msm_host->pdev->dev; 169726f7d1f4SArchit Taneja struct property *prop; 169826f7d1f4SArchit Taneja u32 lane_map[4]; 169926f7d1f4SArchit Taneja int ret, i, len, num_lanes; 170026f7d1f4SArchit Taneja 170160282ceaSArchit Taneja prop = of_find_property(ep, "data-lanes", &len); 170226f7d1f4SArchit Taneja if (!prop) { 17036a41da17SMamta Shukla DRM_DEV_DEBUG(dev, 1704a1b1a4f7SArchit Taneja "failed to find data lane mapping, using default\n"); 1705a1b1a4f7SArchit Taneja return 0; 170626f7d1f4SArchit Taneja } 170726f7d1f4SArchit Taneja 170826f7d1f4SArchit Taneja num_lanes = len / sizeof(u32); 170926f7d1f4SArchit Taneja 171026f7d1f4SArchit Taneja if (num_lanes < 1 || num_lanes > 4) { 17116a41da17SMamta Shukla DRM_DEV_ERROR(dev, "bad number of data lanes\n"); 171226f7d1f4SArchit Taneja return -EINVAL; 171326f7d1f4SArchit Taneja } 171426f7d1f4SArchit Taneja 171526f7d1f4SArchit Taneja msm_host->num_data_lanes = num_lanes; 171626f7d1f4SArchit Taneja 171760282ceaSArchit Taneja ret = of_property_read_u32_array(ep, "data-lanes", lane_map, 171826f7d1f4SArchit Taneja num_lanes); 171926f7d1f4SArchit Taneja if (ret) { 17206a41da17SMamta Shukla DRM_DEV_ERROR(dev, "failed to read lane data\n"); 172126f7d1f4SArchit Taneja return ret; 172226f7d1f4SArchit Taneja } 172326f7d1f4SArchit Taneja 172426f7d1f4SArchit Taneja /* 172526f7d1f4SArchit Taneja * compare DT specified physical-logical lane mappings with the ones 172626f7d1f4SArchit Taneja * supported by hardware 172726f7d1f4SArchit Taneja */ 172826f7d1f4SArchit Taneja for (i = 0; i < ARRAY_SIZE(supported_data_lane_swaps); i++) { 172926f7d1f4SArchit Taneja const int *swap = supported_data_lane_swaps[i]; 173026f7d1f4SArchit Taneja int j; 173126f7d1f4SArchit Taneja 173260282ceaSArchit Taneja /* 173360282ceaSArchit Taneja * the data-lanes array we get from DT has a logical->physical 173460282ceaSArchit Taneja * mapping. The "data lane swap" register field represents 173560282ceaSArchit Taneja * supported configurations in a physical->logical mapping. 173660282ceaSArchit Taneja * Translate the DT mapping to what we understand and find a 173760282ceaSArchit Taneja * configuration that works. 173860282ceaSArchit Taneja */ 173926f7d1f4SArchit Taneja for (j = 0; j < num_lanes; j++) { 174060282ceaSArchit Taneja if (lane_map[j] < 0 || lane_map[j] > 3) 17416a41da17SMamta Shukla DRM_DEV_ERROR(dev, "bad physical lane entry %u\n", 174260282ceaSArchit Taneja lane_map[j]); 174360282ceaSArchit Taneja 174460282ceaSArchit Taneja if (swap[lane_map[j]] != j) 174526f7d1f4SArchit Taneja break; 174626f7d1f4SArchit Taneja } 174726f7d1f4SArchit Taneja 174826f7d1f4SArchit Taneja if (j == num_lanes) { 174926f7d1f4SArchit Taneja msm_host->dlane_swap = i; 175026f7d1f4SArchit Taneja return 0; 175126f7d1f4SArchit Taneja } 175226f7d1f4SArchit Taneja } 175326f7d1f4SArchit Taneja 175426f7d1f4SArchit Taneja return -EINVAL; 175526f7d1f4SArchit Taneja } 175626f7d1f4SArchit Taneja 1757f7009d26SArchit Taneja static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) 1758f7009d26SArchit Taneja { 1759f7009d26SArchit Taneja struct device *dev = &msm_host->pdev->dev; 1760f7009d26SArchit Taneja struct device_node *np = dev->of_node; 1761a9ddac9cSArchit Taneja struct device_node *endpoint, *device_node; 1762a1b1a4f7SArchit Taneja int ret = 0; 1763f7009d26SArchit Taneja 1764f7009d26SArchit Taneja /* 1765b9ac76f6SArchit Taneja * Get the endpoint of the output port of the DSI host. In our case, 1766b9ac76f6SArchit Taneja * this is mapped to port number with reg = 1. Don't return an error if 1767b9ac76f6SArchit Taneja * the remote endpoint isn't defined. It's possible that there is 1768b9ac76f6SArchit Taneja * nothing connected to the dsi output. 1769f7009d26SArchit Taneja */ 1770b9ac76f6SArchit Taneja endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); 1771f7009d26SArchit Taneja if (!endpoint) { 17726a41da17SMamta Shukla DRM_DEV_DEBUG(dev, "%s: no endpoint\n", __func__); 1773f7009d26SArchit Taneja return 0; 1774f7009d26SArchit Taneja } 1775f7009d26SArchit Taneja 177626f7d1f4SArchit Taneja ret = dsi_host_parse_lane_data(msm_host, endpoint); 177726f7d1f4SArchit Taneja if (ret) { 17786a41da17SMamta Shukla DRM_DEV_ERROR(dev, "%s: invalid lane configuration %d\n", 177926f7d1f4SArchit Taneja __func__, ret); 1780feb085ecSSean Paul ret = -EINVAL; 178126f7d1f4SArchit Taneja goto err; 178226f7d1f4SArchit Taneja } 178326f7d1f4SArchit Taneja 1784f7009d26SArchit Taneja /* Get panel node from the output port's endpoint data */ 178586418f90SRob Herring device_node = of_graph_get_remote_node(np, 1, 0); 1786a9ddac9cSArchit Taneja if (!device_node) { 17876a41da17SMamta Shukla DRM_DEV_DEBUG(dev, "%s: no valid device\n", __func__); 1788feb085ecSSean Paul ret = -ENODEV; 178926f7d1f4SArchit Taneja goto err; 1790f7009d26SArchit Taneja } 1791f7009d26SArchit Taneja 1792a9ddac9cSArchit Taneja msm_host->device_node = device_node; 1793f7009d26SArchit Taneja 17940c7df47fSArchit Taneja if (of_property_read_bool(np, "syscon-sfpb")) { 17950c7df47fSArchit Taneja msm_host->sfpb = syscon_regmap_lookup_by_phandle(np, 17960c7df47fSArchit Taneja "syscon-sfpb"); 17970c7df47fSArchit Taneja if (IS_ERR(msm_host->sfpb)) { 17986a41da17SMamta Shukla DRM_DEV_ERROR(dev, "%s: failed to get sfpb regmap\n", 17990c7df47fSArchit Taneja __func__); 180026f7d1f4SArchit Taneja ret = PTR_ERR(msm_host->sfpb); 18010c7df47fSArchit Taneja } 18020c7df47fSArchit Taneja } 18030c7df47fSArchit Taneja 180426f7d1f4SArchit Taneja of_node_put(device_node); 180526f7d1f4SArchit Taneja 180626f7d1f4SArchit Taneja err: 180726f7d1f4SArchit Taneja of_node_put(endpoint); 180826f7d1f4SArchit Taneja 180926f7d1f4SArchit Taneja return ret; 1810f7009d26SArchit Taneja } 1811f7009d26SArchit Taneja 181232280d66SArchit Taneja static int dsi_host_get_id(struct msm_dsi_host *msm_host) 181332280d66SArchit Taneja { 181432280d66SArchit Taneja struct platform_device *pdev = msm_host->pdev; 181532280d66SArchit Taneja const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 181632280d66SArchit Taneja struct resource *res; 181732280d66SArchit Taneja int i; 181832280d66SArchit Taneja 181932280d66SArchit Taneja res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_ctrl"); 182032280d66SArchit Taneja if (!res) 182132280d66SArchit Taneja return -EINVAL; 182232280d66SArchit Taneja 182332280d66SArchit Taneja for (i = 0; i < cfg->num_dsi; i++) { 182432280d66SArchit Taneja if (cfg->io_start[i] == res->start) 182532280d66SArchit Taneja return i; 182632280d66SArchit Taneja } 182732280d66SArchit Taneja 182832280d66SArchit Taneja return -EINVAL; 182932280d66SArchit Taneja } 183032280d66SArchit Taneja 1831a689554bSHai Li int msm_dsi_host_init(struct msm_dsi *msm_dsi) 1832a689554bSHai Li { 1833a689554bSHai Li struct msm_dsi_host *msm_host = NULL; 1834a689554bSHai Li struct platform_device *pdev = msm_dsi->pdev; 1835a689554bSHai Li int ret; 1836a689554bSHai Li 1837a689554bSHai Li msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); 1838a689554bSHai Li if (!msm_host) { 1839a689554bSHai Li ret = -ENOMEM; 1840a689554bSHai Li goto fail; 1841a689554bSHai Li } 1842a689554bSHai Li 1843f7009d26SArchit Taneja msm_host->pdev = pdev; 1844f54ca1a0SArchit Taneja msm_dsi->host = &msm_host->base; 1845f7009d26SArchit Taneja 1846f7009d26SArchit Taneja ret = dsi_host_parse_dt(msm_host); 1847a689554bSHai Li if (ret) { 1848f7009d26SArchit Taneja pr_err("%s: failed to parse dt\n", __func__); 1849a689554bSHai Li goto fail; 1850a689554bSHai Li } 1851a689554bSHai Li 1852bac2c6a6SDmitry Baryshkov msm_host->ctrl_base = msm_ioremap_size(pdev, "dsi_ctrl", "DSI CTRL", &msm_host->ctrl_size); 1853a689554bSHai Li if (IS_ERR(msm_host->ctrl_base)) { 1854a689554bSHai Li pr_err("%s: unable to map Dsi ctrl base\n", __func__); 1855a689554bSHai Li ret = PTR_ERR(msm_host->ctrl_base); 1856a689554bSHai Li goto fail; 1857a689554bSHai Li } 1858a689554bSHai Li 1859f6be1121SArchit Taneja pm_runtime_enable(&pdev->dev); 1860f6be1121SArchit Taneja 1861d248b61fSHai Li msm_host->cfg_hnd = dsi_get_config(msm_host); 1862d248b61fSHai Li if (!msm_host->cfg_hnd) { 1863a689554bSHai Li ret = -EINVAL; 1864a689554bSHai Li pr_err("%s: get config failed\n", __func__); 1865a689554bSHai Li goto fail; 1866a689554bSHai Li } 1867a689554bSHai Li 186832280d66SArchit Taneja msm_host->id = dsi_host_get_id(msm_host); 186932280d66SArchit Taneja if (msm_host->id < 0) { 187032280d66SArchit Taneja ret = msm_host->id; 187132280d66SArchit Taneja pr_err("%s: unable to identify DSI host index\n", __func__); 187232280d66SArchit Taneja goto fail; 187332280d66SArchit Taneja } 187432280d66SArchit Taneja 1875d248b61fSHai Li /* fixup base address by io offset */ 1876d248b61fSHai Li msm_host->ctrl_base += msm_host->cfg_hnd->cfg->io_offset; 1877d248b61fSHai Li 1878a689554bSHai Li ret = dsi_regulator_init(msm_host); 1879a689554bSHai Li if (ret) { 1880a689554bSHai Li pr_err("%s: regulator init failed\n", __func__); 1881a689554bSHai Li goto fail; 1882a689554bSHai Li } 1883a689554bSHai Li 188431c92767SArchit Taneja ret = dsi_clk_init(msm_host); 188531c92767SArchit Taneja if (ret) { 188631c92767SArchit Taneja pr_err("%s: unable to initialize dsi clks\n", __func__); 188731c92767SArchit Taneja goto fail; 188831c92767SArchit Taneja } 188931c92767SArchit Taneja 1890a689554bSHai Li msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); 1891a689554bSHai Li if (!msm_host->rx_buf) { 1892cd57b48aSWei Yongjun ret = -ENOMEM; 1893a689554bSHai Li pr_err("%s: alloc rx temp buf failed\n", __func__); 1894a689554bSHai Li goto fail; 1895a689554bSHai Li } 1896a689554bSHai Li 189711120e93SYangtao Li ret = devm_pm_opp_set_clkname(&pdev->dev, "byte"); 189811120e93SYangtao Li if (ret) 189911120e93SYangtao Li return ret; 190032d3e0feSRajendra Nayak /* OPP table is optional */ 190111120e93SYangtao Li ret = devm_pm_opp_of_add_table(&pdev->dev); 19026400a8e8SViresh Kumar if (ret && ret != -ENODEV) { 190332d3e0feSRajendra Nayak dev_err(&pdev->dev, "invalid OPP table in device tree\n"); 190432d3e0feSRajendra Nayak return ret; 190532d3e0feSRajendra Nayak } 190632d3e0feSRajendra Nayak 1907a689554bSHai Li init_completion(&msm_host->dma_comp); 1908a689554bSHai Li init_completion(&msm_host->video_comp); 1909a689554bSHai Li mutex_init(&msm_host->dev_mutex); 1910a689554bSHai Li mutex_init(&msm_host->cmd_mutex); 1911a689554bSHai Li spin_lock_init(&msm_host->intr_lock); 1912a689554bSHai Li 1913a689554bSHai Li /* setup workqueue */ 1914a689554bSHai Li msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); 1915a689554bSHai Li INIT_WORK(&msm_host->err_work, dsi_err_worker); 19168d23ea40SArchit Taneja INIT_WORK(&msm_host->hpd_work, dsi_hpd_worker); 1917a689554bSHai Li 1918a689554bSHai Li msm_dsi->id = msm_host->id; 1919a689554bSHai Li 1920a689554bSHai Li DBG("Dsi Host %d initialized", msm_host->id); 1921a689554bSHai Li return 0; 1922a689554bSHai Li 1923a689554bSHai Li fail: 1924a689554bSHai Li return ret; 1925a689554bSHai Li } 1926a689554bSHai Li 1927a689554bSHai Li void msm_dsi_host_destroy(struct mipi_dsi_host *host) 1928a689554bSHai Li { 1929a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1930a689554bSHai Li 1931a689554bSHai Li DBG(""); 1932a689554bSHai Li dsi_tx_buf_free(msm_host); 1933a689554bSHai Li if (msm_host->workqueue) { 1934a689554bSHai Li flush_workqueue(msm_host->workqueue); 1935a689554bSHai Li destroy_workqueue(msm_host->workqueue); 1936a689554bSHai Li msm_host->workqueue = NULL; 1937a689554bSHai Li } 1938a689554bSHai Li 1939a689554bSHai Li mutex_destroy(&msm_host->cmd_mutex); 1940a689554bSHai Li mutex_destroy(&msm_host->dev_mutex); 1941f6be1121SArchit Taneja 1942f6be1121SArchit Taneja pm_runtime_disable(&msm_host->pdev->dev); 1943a689554bSHai Li } 1944a689554bSHai Li 1945a689554bSHai Li int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, 1946a689554bSHai Li struct drm_device *dev) 1947a689554bSHai Li { 1948a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 19498f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 1950a689554bSHai Li struct platform_device *pdev = msm_host->pdev; 1951a689554bSHai Li int ret; 1952a689554bSHai Li 1953a689554bSHai Li msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 1954a689554bSHai Li if (msm_host->irq < 0) { 1955a689554bSHai Li ret = msm_host->irq; 19566a41da17SMamta Shukla DRM_DEV_ERROR(dev->dev, "failed to get irq: %d\n", ret); 1957a689554bSHai Li return ret; 1958a689554bSHai Li } 1959a689554bSHai Li 1960a689554bSHai Li ret = devm_request_irq(&pdev->dev, msm_host->irq, 1961a689554bSHai Li dsi_host_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 1962a689554bSHai Li "dsi_isr", msm_host); 1963a689554bSHai Li if (ret < 0) { 19646a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "failed to request IRQ%u: %d\n", 1965a689554bSHai Li msm_host->irq, ret); 1966a689554bSHai Li return ret; 1967a689554bSHai Li } 1968a689554bSHai Li 1969a689554bSHai Li msm_host->dev = dev; 19708f7ca540SSibi Sankar ret = cfg_hnd->ops->tx_buf_alloc(msm_host, SZ_4K); 1971a689554bSHai Li if (ret) { 1972a689554bSHai Li pr_err("%s: alloc tx gem obj failed, %d\n", __func__, ret); 1973a689554bSHai Li return ret; 1974a689554bSHai Li } 1975a689554bSHai Li 1976a689554bSHai Li return 0; 1977a689554bSHai Li } 1978a689554bSHai Li 1979*8f59ee9aSRob Clark int msm_dsi_host_register(struct mipi_dsi_host *host) 1980a689554bSHai Li { 1981a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1982a689554bSHai Li int ret; 1983a689554bSHai Li 1984a689554bSHai Li /* Register mipi dsi host */ 1985a689554bSHai Li if (!msm_host->registered) { 1986a689554bSHai Li host->dev = &msm_host->pdev->dev; 1987a689554bSHai Li host->ops = &dsi_host_ops; 1988a689554bSHai Li ret = mipi_dsi_host_register(host); 1989a689554bSHai Li if (ret) 1990a689554bSHai Li return ret; 1991a689554bSHai Li 1992a689554bSHai Li msm_host->registered = true; 1993a689554bSHai Li } 1994a689554bSHai Li 1995a689554bSHai Li return 0; 1996a689554bSHai Li } 1997a689554bSHai Li 1998a689554bSHai Li void msm_dsi_host_unregister(struct mipi_dsi_host *host) 1999a689554bSHai Li { 2000a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2001a689554bSHai Li 2002a689554bSHai Li if (msm_host->registered) { 2003a689554bSHai Li mipi_dsi_host_unregister(host); 2004a689554bSHai Li host->dev = NULL; 2005a689554bSHai Li host->ops = NULL; 2006a689554bSHai Li msm_host->registered = false; 2007a689554bSHai Li } 2008a689554bSHai Li } 2009a689554bSHai Li 2010a689554bSHai Li int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, 2011a689554bSHai Li const struct mipi_dsi_msg *msg) 2012a689554bSHai Li { 2013a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 20148f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2015a689554bSHai Li 2016a689554bSHai Li /* TODO: make sure dsi_cmd_mdp is idle. 2017a689554bSHai Li * Since DSI6G v1.2.0, we can set DSI_TRIG_CTRL.BLOCK_DMA_WITHIN_FRAME 2018a689554bSHai Li * to ask H/W to wait until cmd mdp is idle. S/W wait is not needed. 2019a689554bSHai Li * How to handle the old versions? Wait for mdp cmd done? 2020a689554bSHai Li */ 2021a689554bSHai Li 2022a689554bSHai Li /* 2023a689554bSHai Li * mdss interrupt is generated in mdp core clock domain 2024a689554bSHai Li * mdp clock need to be enabled to receive dsi interrupt 2025a689554bSHai Li */ 2026f6be1121SArchit Taneja pm_runtime_get_sync(&msm_host->pdev->dev); 20276b16f05aSRob Clark cfg_hnd->ops->link_clk_set_rate(msm_host); 20288f7ca540SSibi Sankar cfg_hnd->ops->link_clk_enable(msm_host); 2029a689554bSHai Li 2030a689554bSHai Li /* TODO: vote for bus bandwidth */ 2031a689554bSHai Li 2032a689554bSHai Li if (!(msg->flags & MIPI_DSI_MSG_USE_LPM)) 2033a689554bSHai Li dsi_set_tx_power_mode(0, msm_host); 2034a689554bSHai Li 2035a689554bSHai Li msm_host->dma_cmd_ctrl_restore = dsi_read(msm_host, REG_DSI_CTRL); 2036a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, 2037a689554bSHai Li msm_host->dma_cmd_ctrl_restore | 2038a689554bSHai Li DSI_CTRL_CMD_MODE_EN | 2039a689554bSHai Li DSI_CTRL_ENABLE); 2040a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_DMA_DONE, 1); 2041a689554bSHai Li 2042a689554bSHai Li return 0; 2043a689554bSHai Li } 2044a689554bSHai Li 2045a689554bSHai Li void msm_dsi_host_xfer_restore(struct mipi_dsi_host *host, 2046a689554bSHai Li const struct mipi_dsi_msg *msg) 2047a689554bSHai Li { 2048a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 20498f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2050a689554bSHai Li 2051a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_DMA_DONE, 0); 2052a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, msm_host->dma_cmd_ctrl_restore); 2053a689554bSHai Li 2054a689554bSHai Li if (!(msg->flags & MIPI_DSI_MSG_USE_LPM)) 2055a689554bSHai Li dsi_set_tx_power_mode(1, msm_host); 2056a689554bSHai Li 2057a689554bSHai Li /* TODO: unvote for bus bandwidth */ 2058a689554bSHai Li 20598f7ca540SSibi Sankar cfg_hnd->ops->link_clk_disable(msm_host); 2060f6be1121SArchit Taneja pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2061a689554bSHai Li } 2062a689554bSHai Li 2063a689554bSHai Li int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host, 2064a689554bSHai Li const struct mipi_dsi_msg *msg) 2065a689554bSHai Li { 2066a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2067a689554bSHai Li 2068a689554bSHai Li return dsi_cmds2buf_tx(msm_host, msg); 2069a689554bSHai Li } 2070a689554bSHai Li 2071a689554bSHai Li int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, 2072a689554bSHai Li const struct mipi_dsi_msg *msg) 2073a689554bSHai Li { 2074a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2075d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2076a689554bSHai Li int data_byte, rx_byte, dlen, end; 2077a689554bSHai Li int short_response, diff, pkt_size, ret = 0; 2078a689554bSHai Li char cmd; 2079a689554bSHai Li int rlen = msg->rx_len; 2080a689554bSHai Li u8 *buf; 2081a689554bSHai Li 2082a689554bSHai Li if (rlen <= 2) { 2083a689554bSHai Li short_response = 1; 2084a689554bSHai Li pkt_size = rlen; 2085a689554bSHai Li rx_byte = 4; 2086a689554bSHai Li } else { 2087a689554bSHai Li short_response = 0; 2088a689554bSHai Li data_byte = 10; /* first read */ 2089a689554bSHai Li if (rlen < data_byte) 2090a689554bSHai Li pkt_size = rlen; 2091a689554bSHai Li else 2092a689554bSHai Li pkt_size = data_byte; 2093a689554bSHai Li rx_byte = data_byte + 6; /* 4 header + 2 crc */ 2094a689554bSHai Li } 2095a689554bSHai Li 2096a689554bSHai Li buf = msm_host->rx_buf; 2097a689554bSHai Li end = 0; 2098a689554bSHai Li while (!end) { 2099a689554bSHai Li u8 tx[2] = {pkt_size & 0xff, pkt_size >> 8}; 2100a689554bSHai Li struct mipi_dsi_msg max_pkt_size_msg = { 2101a689554bSHai Li .channel = msg->channel, 2102a689554bSHai Li .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, 2103a689554bSHai Li .tx_len = 2, 2104a689554bSHai Li .tx_buf = tx, 2105a689554bSHai Li }; 2106a689554bSHai Li 2107a689554bSHai Li DBG("rlen=%d pkt_size=%d rx_byte=%d", 2108a689554bSHai Li rlen, pkt_size, rx_byte); 2109a689554bSHai Li 2110a689554bSHai Li ret = dsi_cmds2buf_tx(msm_host, &max_pkt_size_msg); 2111a689554bSHai Li if (ret < 2) { 2112a689554bSHai Li pr_err("%s: Set max pkt size failed, %d\n", 2113a689554bSHai Li __func__, ret); 2114a689554bSHai Li return -EINVAL; 2115a689554bSHai Li } 2116a689554bSHai Li 2117d248b61fSHai Li if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 2118d248b61fSHai Li (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { 2119a689554bSHai Li /* Clear the RDBK_DATA registers */ 2120a689554bSHai Li dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 2121a689554bSHai Li DSI_RDBK_DATA_CTRL_CLR); 2122a689554bSHai Li wmb(); /* make sure the RDBK registers are cleared */ 2123a689554bSHai Li dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 0); 2124a689554bSHai Li wmb(); /* release cleared status before transfer */ 2125a689554bSHai Li } 2126a689554bSHai Li 2127a689554bSHai Li ret = dsi_cmds2buf_tx(msm_host, msg); 2128a689554bSHai Li if (ret < msg->tx_len) { 2129a689554bSHai Li pr_err("%s: Read cmd Tx failed, %d\n", __func__, ret); 2130a689554bSHai Li return ret; 2131a689554bSHai Li } 2132a689554bSHai Li 2133a689554bSHai Li /* 2134a689554bSHai Li * once cmd_dma_done interrupt received, 2135a689554bSHai Li * return data from client is ready and stored 2136a689554bSHai Li * at RDBK_DATA register already 2137a689554bSHai Li * since rx fifo is 16 bytes, dcs header is kept at first loop, 2138a689554bSHai Li * after that dcs header lost during shift into registers 2139a689554bSHai Li */ 2140a689554bSHai Li dlen = dsi_cmd_dma_rx(msm_host, buf, rx_byte, pkt_size); 2141a689554bSHai Li 2142a689554bSHai Li if (dlen <= 0) 2143a689554bSHai Li return 0; 2144a689554bSHai Li 2145a689554bSHai Li if (short_response) 2146a689554bSHai Li break; 2147a689554bSHai Li 2148a689554bSHai Li if (rlen <= data_byte) { 2149a689554bSHai Li diff = data_byte - rlen; 2150a689554bSHai Li end = 1; 2151a689554bSHai Li } else { 2152a689554bSHai Li diff = 0; 2153a689554bSHai Li rlen -= data_byte; 2154a689554bSHai Li } 2155a689554bSHai Li 2156a689554bSHai Li if (!end) { 2157a689554bSHai Li dlen -= 2; /* 2 crc */ 2158a689554bSHai Li dlen -= diff; 2159a689554bSHai Li buf += dlen; /* next start position */ 2160a689554bSHai Li data_byte = 14; /* NOT first read */ 2161a689554bSHai Li if (rlen < data_byte) 2162a689554bSHai Li pkt_size += rlen; 2163a689554bSHai Li else 2164a689554bSHai Li pkt_size += data_byte; 2165a689554bSHai Li DBG("buf=%p dlen=%d diff=%d", buf, dlen, diff); 2166a689554bSHai Li } 2167a689554bSHai Li } 2168a689554bSHai Li 2169a689554bSHai Li /* 2170a689554bSHai Li * For single Long read, if the requested rlen < 10, 2171a689554bSHai Li * we need to shift the start position of rx 2172a689554bSHai Li * data buffer to skip the bytes which are not 2173a689554bSHai Li * updated. 2174a689554bSHai Li */ 2175a689554bSHai Li if (pkt_size < 10 && !short_response) 2176a689554bSHai Li buf = msm_host->rx_buf + (10 - rlen); 2177a689554bSHai Li else 2178a689554bSHai Li buf = msm_host->rx_buf; 2179a689554bSHai Li 2180a689554bSHai Li cmd = buf[0]; 2181a689554bSHai Li switch (cmd) { 2182a689554bSHai Li case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: 2183a689554bSHai Li pr_err("%s: rx ACK_ERR_PACLAGE\n", __func__); 2184a689554bSHai Li ret = 0; 2185651ad3f5SHai Li break; 2186a689554bSHai Li case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: 2187a689554bSHai Li case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: 2188a689554bSHai Li ret = dsi_short_read1_resp(buf, msg); 2189a689554bSHai Li break; 2190a689554bSHai Li case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: 2191a689554bSHai Li case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: 2192a689554bSHai Li ret = dsi_short_read2_resp(buf, msg); 2193a689554bSHai Li break; 2194a689554bSHai Li case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: 2195a689554bSHai Li case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: 2196a689554bSHai Li ret = dsi_long_read_resp(buf, msg); 2197a689554bSHai Li break; 2198a689554bSHai Li default: 2199a689554bSHai Li pr_warn("%s:Invalid response cmd\n", __func__); 2200a689554bSHai Li ret = 0; 2201a689554bSHai Li } 2202a689554bSHai Li 2203a689554bSHai Li return ret; 2204a689554bSHai Li } 2205a689554bSHai Li 22064ff9d4cbSArchit Taneja void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, 22074ff9d4cbSArchit Taneja u32 len) 2208a689554bSHai Li { 2209a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2210a689554bSHai Li 22114ff9d4cbSArchit Taneja dsi_write(msm_host, REG_DSI_DMA_BASE, dma_base); 2212a689554bSHai Li dsi_write(msm_host, REG_DSI_DMA_LEN, len); 2213a689554bSHai Li dsi_write(msm_host, REG_DSI_TRIG_DMA, 1); 2214a689554bSHai Li 2215a689554bSHai Li /* Make sure trigger happens */ 2216a689554bSHai Li wmb(); 2217a689554bSHai Li } 2218a689554bSHai Li 22199d32c498SHai Li int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, 22205d134596SDmitry Baryshkov struct msm_dsi_phy *src_phy) 22219d32c498SHai Li { 22229d32c498SHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 22239d32c498SHai Li struct clk *byte_clk_provider, *pixel_clk_provider; 22249d32c498SHai Li int ret; 22259d32c498SHai Li 22265ac17838SJonathan Marek msm_host->cphy_mode = src_phy->cphy_mode; 22275ac17838SJonathan Marek 22285d134596SDmitry Baryshkov ret = msm_dsi_phy_get_clk_provider(src_phy, 22299d32c498SHai Li &byte_clk_provider, &pixel_clk_provider); 22309d32c498SHai Li if (ret) { 22319d32c498SHai Li pr_info("%s: can't get provider from pll, don't set parent\n", 22329d32c498SHai Li __func__); 22339d32c498SHai Li return 0; 22349d32c498SHai Li } 22359d32c498SHai Li 22369d32c498SHai Li ret = clk_set_parent(msm_host->byte_clk_src, byte_clk_provider); 22379d32c498SHai Li if (ret) { 22389d32c498SHai Li pr_err("%s: can't set parent to byte_clk_src. ret=%d\n", 22399d32c498SHai Li __func__, ret); 22409d32c498SHai Li goto exit; 22419d32c498SHai Li } 22429d32c498SHai Li 22439d32c498SHai Li ret = clk_set_parent(msm_host->pixel_clk_src, pixel_clk_provider); 22449d32c498SHai Li if (ret) { 22459d32c498SHai Li pr_err("%s: can't set parent to pixel_clk_src. ret=%d\n", 22469d32c498SHai Li __func__, ret); 22479d32c498SHai Li goto exit; 22489d32c498SHai Li } 22499d32c498SHai Li 22508f7ca540SSibi Sankar if (msm_host->dsi_clk_src) { 22514bfa9748SArchit Taneja ret = clk_set_parent(msm_host->dsi_clk_src, pixel_clk_provider); 22524bfa9748SArchit Taneja if (ret) { 22534bfa9748SArchit Taneja pr_err("%s: can't set parent to dsi_clk_src. ret=%d\n", 22544bfa9748SArchit Taneja __func__, ret); 22554bfa9748SArchit Taneja goto exit; 22564bfa9748SArchit Taneja } 22578f7ca540SSibi Sankar } 22584bfa9748SArchit Taneja 22598f7ca540SSibi Sankar if (msm_host->esc_clk_src) { 22604bfa9748SArchit Taneja ret = clk_set_parent(msm_host->esc_clk_src, byte_clk_provider); 22614bfa9748SArchit Taneja if (ret) { 22624bfa9748SArchit Taneja pr_err("%s: can't set parent to esc_clk_src. ret=%d\n", 22634bfa9748SArchit Taneja __func__, ret); 22644bfa9748SArchit Taneja goto exit; 22654bfa9748SArchit Taneja } 22664bfa9748SArchit Taneja } 22674bfa9748SArchit Taneja 22689d32c498SHai Li exit: 22699d32c498SHai Li return ret; 22709d32c498SHai Li } 22719d32c498SHai Li 227234d9545bSArchit Taneja void msm_dsi_host_reset_phy(struct mipi_dsi_host *host) 227334d9545bSArchit Taneja { 227434d9545bSArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 227534d9545bSArchit Taneja 227634d9545bSArchit Taneja DBG(""); 227734d9545bSArchit Taneja dsi_write(msm_host, REG_DSI_PHY_RESET, DSI_PHY_RESET_RESET); 227834d9545bSArchit Taneja /* Make sure fully reset */ 227934d9545bSArchit Taneja wmb(); 228034d9545bSArchit Taneja udelay(1000); 228134d9545bSArchit Taneja dsi_write(msm_host, REG_DSI_PHY_RESET, 0); 228234d9545bSArchit Taneja udelay(100); 228334d9545bSArchit Taneja } 228434d9545bSArchit Taneja 2285b62aa70aSHai Li void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, 2286ed9976a0SChandan Uddaraju struct msm_dsi_phy_clk_request *clk_req, 22876183606dSDmitry Baryshkov bool is_bonded_dsi) 2288b62aa70aSHai Li { 2289b62aa70aSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 22908f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2291d4cea38eSArchit Taneja int ret; 2292d4cea38eSArchit Taneja 22936183606dSDmitry Baryshkov ret = cfg_hnd->ops->calc_clk_rate(msm_host, is_bonded_dsi); 2294d4cea38eSArchit Taneja if (ret) { 2295d4cea38eSArchit Taneja pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); 2296d4cea38eSArchit Taneja return; 2297d4cea38eSArchit Taneja } 2298b62aa70aSHai Li 22995ac17838SJonathan Marek /* CPHY transmits 16 bits over 7 clock cycles 23005ac17838SJonathan Marek * "byte_clk" is in units of 16-bits (see dsi_calc_pclk), 23015ac17838SJonathan Marek * so multiply by 7 to get the "bitclk rate" 23025ac17838SJonathan Marek */ 23035ac17838SJonathan Marek if (msm_host->cphy_mode) 23045ac17838SJonathan Marek clk_req->bitclk_rate = msm_host->byte_clk_rate * 7; 23055ac17838SJonathan Marek else 2306b62aa70aSHai Li clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; 2307b62aa70aSHai Li clk_req->escclk_rate = msm_host->esc_clk_rate; 2308b62aa70aSHai Li } 2309b62aa70aSHai Li 2310a689554bSHai Li int msm_dsi_host_enable(struct mipi_dsi_host *host) 2311a689554bSHai Li { 2312a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2313a689554bSHai Li 2314a689554bSHai Li dsi_op_mode_config(msm_host, 2315a689554bSHai Li !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), true); 2316a689554bSHai Li 2317a689554bSHai Li /* TODO: clock should be turned off for command mode, 2318a689554bSHai Li * and only turned on before MDP START. 2319a689554bSHai Li * This part of code should be enabled once mdp driver support it. 2320a689554bSHai Li */ 2321f54ca1a0SArchit Taneja /* if (msm_panel->mode == MSM_DSI_CMD_MODE) { 2322f54ca1a0SArchit Taneja * dsi_link_clk_disable(msm_host); 2323f54ca1a0SArchit Taneja * pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2324f54ca1a0SArchit Taneja * } 2325f54ca1a0SArchit Taneja */ 23269c5638d7SAbhinav Kumar msm_host->enabled = true; 2327a689554bSHai Li return 0; 2328a689554bSHai Li } 2329a689554bSHai Li 2330a689554bSHai Li int msm_dsi_host_disable(struct mipi_dsi_host *host) 2331a689554bSHai Li { 2332a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2333a689554bSHai Li 23349c5638d7SAbhinav Kumar msm_host->enabled = false; 2335a689554bSHai Li dsi_op_mode_config(msm_host, 2336a689554bSHai Li !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), false); 2337a689554bSHai Li 2338a689554bSHai Li /* Since we have disabled INTF, the video engine won't stop so that 2339a689554bSHai Li * the cmd engine will be blocked. 2340a689554bSHai Li * Reset to disable video engine so that we can send off cmd. 2341a689554bSHai Li */ 2342a689554bSHai Li dsi_sw_reset(msm_host); 2343a689554bSHai Li 2344a689554bSHai Li return 0; 2345a689554bSHai Li } 2346a689554bSHai Li 23470c7df47fSArchit Taneja static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) 23480c7df47fSArchit Taneja { 23490c7df47fSArchit Taneja enum sfpb_ahb_arb_master_port_en en; 23500c7df47fSArchit Taneja 23510c7df47fSArchit Taneja if (!msm_host->sfpb) 23520c7df47fSArchit Taneja return; 23530c7df47fSArchit Taneja 23540c7df47fSArchit Taneja en = enable ? SFPB_MASTER_PORT_ENABLE : SFPB_MASTER_PORT_DISABLE; 23550c7df47fSArchit Taneja 23560c7df47fSArchit Taneja regmap_update_bits(msm_host->sfpb, REG_SFPB_GPREG, 23570c7df47fSArchit Taneja SFPB_GPREG_MASTER_PORT_EN__MASK, 23580c7df47fSArchit Taneja SFPB_GPREG_MASTER_PORT_EN(en)); 23590c7df47fSArchit Taneja } 23600c7df47fSArchit Taneja 2361b62aa70aSHai Li int msm_dsi_host_power_on(struct mipi_dsi_host *host, 2362ed9976a0SChandan Uddaraju struct msm_dsi_phy_shared_timings *phy_shared_timings, 2363858c595aSDmitry Baryshkov bool is_bonded_dsi, struct msm_dsi_phy *phy) 2364a689554bSHai Li { 2365a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 23668f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2367a689554bSHai Li int ret = 0; 2368a689554bSHai Li 2369a689554bSHai Li mutex_lock(&msm_host->dev_mutex); 2370a689554bSHai Li if (msm_host->power_on) { 2371a689554bSHai Li DBG("dsi host already on"); 2372a689554bSHai Li goto unlock_ret; 2373a689554bSHai Li } 2374a689554bSHai Li 23750c7df47fSArchit Taneja msm_dsi_sfpb_config(msm_host, true); 23760c7df47fSArchit Taneja 2377a689554bSHai Li ret = dsi_host_regulator_enable(msm_host); 2378a689554bSHai Li if (ret) { 2379a689554bSHai Li pr_err("%s:Failed to enable vregs.ret=%d\n", 2380a689554bSHai Li __func__, ret); 2381a689554bSHai Li goto unlock_ret; 2382a689554bSHai Li } 2383a689554bSHai Li 2384f6be1121SArchit Taneja pm_runtime_get_sync(&msm_host->pdev->dev); 23856b16f05aSRob Clark ret = cfg_hnd->ops->link_clk_set_rate(msm_host); 23866b16f05aSRob Clark if (!ret) 23878f7ca540SSibi Sankar ret = cfg_hnd->ops->link_clk_enable(msm_host); 2388a689554bSHai Li if (ret) { 2389f54ca1a0SArchit Taneja pr_err("%s: failed to enable link clocks. ret=%d\n", 2390f54ca1a0SArchit Taneja __func__, ret); 2391a689554bSHai Li goto fail_disable_reg; 2392a689554bSHai Li } 2393a689554bSHai Li 2394ab8909b0SHai Li ret = pinctrl_pm_select_default_state(&msm_host->pdev->dev); 2395ab8909b0SHai Li if (ret) { 2396ab8909b0SHai Li pr_err("%s: failed to set pinctrl default state, %d\n", 2397ab8909b0SHai Li __func__, ret); 2398ab8909b0SHai Li goto fail_disable_clk; 2399ab8909b0SHai Li } 2400ab8909b0SHai Li 24016183606dSDmitry Baryshkov dsi_timing_setup(msm_host, is_bonded_dsi); 2402a689554bSHai Li dsi_sw_reset(msm_host); 2403858c595aSDmitry Baryshkov dsi_ctrl_config(msm_host, true, phy_shared_timings, phy); 2404a689554bSHai Li 2405a689554bSHai Li if (msm_host->disp_en_gpio) 2406a689554bSHai Li gpiod_set_value(msm_host->disp_en_gpio, 1); 2407a689554bSHai Li 2408a689554bSHai Li msm_host->power_on = true; 2409a689554bSHai Li mutex_unlock(&msm_host->dev_mutex); 2410a689554bSHai Li 2411a689554bSHai Li return 0; 2412a689554bSHai Li 2413ab8909b0SHai Li fail_disable_clk: 24148f7ca540SSibi Sankar cfg_hnd->ops->link_clk_disable(msm_host); 2415f54ca1a0SArchit Taneja pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2416a689554bSHai Li fail_disable_reg: 2417a689554bSHai Li dsi_host_regulator_disable(msm_host); 2418a689554bSHai Li unlock_ret: 2419a689554bSHai Li mutex_unlock(&msm_host->dev_mutex); 2420a689554bSHai Li return ret; 2421a689554bSHai Li } 2422a689554bSHai Li 2423a689554bSHai Li int msm_dsi_host_power_off(struct mipi_dsi_host *host) 2424a689554bSHai Li { 2425a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 24268f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2427a689554bSHai Li 2428a689554bSHai Li mutex_lock(&msm_host->dev_mutex); 2429a689554bSHai Li if (!msm_host->power_on) { 2430a689554bSHai Li DBG("dsi host already off"); 2431a689554bSHai Li goto unlock_ret; 2432a689554bSHai Li } 2433a689554bSHai Li 2434858c595aSDmitry Baryshkov dsi_ctrl_config(msm_host, false, NULL, NULL); 2435a689554bSHai Li 2436a689554bSHai Li if (msm_host->disp_en_gpio) 2437a689554bSHai Li gpiod_set_value(msm_host->disp_en_gpio, 0); 2438a689554bSHai Li 2439ab8909b0SHai Li pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); 2440ab8909b0SHai Li 24418f7ca540SSibi Sankar cfg_hnd->ops->link_clk_disable(msm_host); 2442f6be1121SArchit Taneja pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2443a689554bSHai Li 2444a689554bSHai Li dsi_host_regulator_disable(msm_host); 2445a689554bSHai Li 24460c7df47fSArchit Taneja msm_dsi_sfpb_config(msm_host, false); 24470c7df47fSArchit Taneja 2448a689554bSHai Li DBG("-"); 2449a689554bSHai Li 2450a689554bSHai Li msm_host->power_on = false; 2451a689554bSHai Li 2452a689554bSHai Li unlock_ret: 2453a689554bSHai Li mutex_unlock(&msm_host->dev_mutex); 2454a689554bSHai Li return 0; 2455a689554bSHai Li } 2456a689554bSHai Li 2457a689554bSHai Li int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, 245863f8f3baSLaurent Pinchart const struct drm_display_mode *mode) 2459a689554bSHai Li { 2460a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2461a689554bSHai Li 2462a689554bSHai Li if (msm_host->mode) { 2463a689554bSHai Li drm_mode_destroy(msm_host->dev, msm_host->mode); 2464a689554bSHai Li msm_host->mode = NULL; 2465a689554bSHai Li } 2466a689554bSHai Li 2467a689554bSHai Li msm_host->mode = drm_mode_duplicate(msm_host->dev, mode); 24682abe1f25SWei Yongjun if (!msm_host->mode) { 2469a689554bSHai Li pr_err("%s: cannot duplicate mode\n", __func__); 24702abe1f25SWei Yongjun return -ENOMEM; 2471a689554bSHai Li } 2472a689554bSHai Li 2473a689554bSHai Li return 0; 2474a689554bSHai Li } 2475a689554bSHai Li 2476e3a91f89SSean Paul struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host) 2477a689554bSHai Li { 2478e3a91f89SSean Paul return of_drm_find_panel(to_msm_dsi_host(host)->device_node); 2479e3a91f89SSean Paul } 2480a689554bSHai Li 2481e3a91f89SSean Paul unsigned long msm_dsi_host_get_mode_flags(struct mipi_dsi_host *host) 2482e3a91f89SSean Paul { 2483e3a91f89SSean Paul return to_msm_dsi_host(host)->mode_flags; 2484a689554bSHai Li } 2485a689554bSHai Li 2486c118e290SArchit Taneja struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host) 2487c118e290SArchit Taneja { 2488c118e290SArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2489c118e290SArchit Taneja 2490c118e290SArchit Taneja return of_drm_find_bridge(msm_host->device_node); 2491c118e290SArchit Taneja } 249298659487SAbhinav Kumar 2493eb9d6c7eSDmitry Baryshkov void msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host) 24949d30a4bcSAbhinav Kumar { 24959d30a4bcSAbhinav Kumar struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 24969d30a4bcSAbhinav Kumar 24979d30a4bcSAbhinav Kumar pm_runtime_get_sync(&msm_host->pdev->dev); 24989d30a4bcSAbhinav Kumar 2499bac2c6a6SDmitry Baryshkov msm_disp_snapshot_add_block(disp_state, msm_host->ctrl_size, 25009d30a4bcSAbhinav Kumar msm_host->ctrl_base, "dsi%d_ctrl", msm_host->id); 25019d30a4bcSAbhinav Kumar 25029d30a4bcSAbhinav Kumar pm_runtime_put_sync(&msm_host->pdev->dev); 25039d30a4bcSAbhinav Kumar } 25045e2a72d4SAbhinav Kumar 25055e2a72d4SAbhinav Kumar static void msm_dsi_host_video_test_pattern_setup(struct msm_dsi_host *msm_host) 25065e2a72d4SAbhinav Kumar { 25075e2a72d4SAbhinav Kumar u32 reg; 25085e2a72d4SAbhinav Kumar 25095e2a72d4SAbhinav Kumar reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); 25105e2a72d4SAbhinav Kumar 25115e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_VIDEO_INIT_VAL, 0xff); 25125e2a72d4SAbhinav Kumar /* draw checkered rectangle pattern */ 25135e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TPG_MAIN_CONTROL, 25145e2a72d4SAbhinav Kumar DSI_TPG_MAIN_CONTROL_CHECKERED_RECTANGLE_PATTERN); 25155e2a72d4SAbhinav Kumar /* use 24-bit RGB test pttern */ 25165e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TPG_VIDEO_CONFIG, 25175e2a72d4SAbhinav Kumar DSI_TPG_VIDEO_CONFIG_BPP(VIDEO_CONFIG_24BPP) | 25185e2a72d4SAbhinav Kumar DSI_TPG_VIDEO_CONFIG_RGB); 25195e2a72d4SAbhinav Kumar 25205e2a72d4SAbhinav Kumar reg |= DSI_TEST_PATTERN_GEN_CTRL_VIDEO_PATTERN_SEL(VID_MDSS_GENERAL_PATTERN); 25215e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, reg); 25225e2a72d4SAbhinav Kumar 25235e2a72d4SAbhinav Kumar DBG("Video test pattern setup done\n"); 25245e2a72d4SAbhinav Kumar } 25255e2a72d4SAbhinav Kumar 25265e2a72d4SAbhinav Kumar static void msm_dsi_host_cmd_test_pattern_setup(struct msm_dsi_host *msm_host) 25275e2a72d4SAbhinav Kumar { 25285e2a72d4SAbhinav Kumar u32 reg; 25295e2a72d4SAbhinav Kumar 25305e2a72d4SAbhinav Kumar reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); 25315e2a72d4SAbhinav Kumar 25325e2a72d4SAbhinav Kumar /* initial value for test pattern */ 25335e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL0, 0xff); 25345e2a72d4SAbhinav Kumar 25355e2a72d4SAbhinav Kumar reg |= DSI_TEST_PATTERN_GEN_CTRL_CMD_MDP_STREAM0_PATTERN_SEL(CMD_MDP_MDSS_GENERAL_PATTERN); 25365e2a72d4SAbhinav Kumar 25375e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, reg); 25385e2a72d4SAbhinav Kumar /* draw checkered rectangle pattern */ 25395e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TPG_MAIN_CONTROL2, 25405e2a72d4SAbhinav Kumar DSI_TPG_MAIN_CONTROL2_CMD_MDP0_CHECKERED_RECTANGLE_PATTERN); 25415e2a72d4SAbhinav Kumar 25425e2a72d4SAbhinav Kumar DBG("Cmd test pattern setup done\n"); 25435e2a72d4SAbhinav Kumar } 25445e2a72d4SAbhinav Kumar 25455e2a72d4SAbhinav Kumar void msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host) 25465e2a72d4SAbhinav Kumar { 25475e2a72d4SAbhinav Kumar struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 25485e2a72d4SAbhinav Kumar bool is_video_mode = !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO); 25495e2a72d4SAbhinav Kumar u32 reg; 25505e2a72d4SAbhinav Kumar 25515e2a72d4SAbhinav Kumar if (is_video_mode) 25525e2a72d4SAbhinav Kumar msm_dsi_host_video_test_pattern_setup(msm_host); 25535e2a72d4SAbhinav Kumar else 25545e2a72d4SAbhinav Kumar msm_dsi_host_cmd_test_pattern_setup(msm_host); 25555e2a72d4SAbhinav Kumar 25565e2a72d4SAbhinav Kumar reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); 25575e2a72d4SAbhinav Kumar /* enable the test pattern generator */ 25585e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, (reg | DSI_TEST_PATTERN_GEN_CTRL_EN)); 25595e2a72d4SAbhinav Kumar 25605e2a72d4SAbhinav Kumar /* for command mode need to trigger one frame from tpg */ 25615e2a72d4SAbhinav Kumar if (!is_video_mode) 25625e2a72d4SAbhinav Kumar dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER, 25635e2a72d4SAbhinav Kumar DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER); 25645e2a72d4SAbhinav Kumar } 2565