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" 30a689554bSHai Li 3178e31c42SJeffrey Hugo #define DSI_RESET_TOGGLE_DELAY_MS 20 3278e31c42SJeffrey Hugo 33a689554bSHai Li static int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) 34a689554bSHai Li { 35a689554bSHai Li u32 ver; 36a689554bSHai Li 37a689554bSHai Li if (!major || !minor) 38a689554bSHai Li return -EINVAL; 39a689554bSHai Li 40648d5063SArchit Taneja /* 41648d5063SArchit Taneja * From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0 42a689554bSHai Li * makes all other registers 4-byte shifted down. 43648d5063SArchit Taneja * 44648d5063SArchit Taneja * In order to identify between DSI6G(v3) and beyond, and DSIv2 and 45648d5063SArchit Taneja * older, we read the DSI_VERSION register without any shift(offset 46648d5063SArchit Taneja * 0x1f0). In the case of DSIv2, this hast to be a non-zero value. In 47648d5063SArchit Taneja * the case of DSI6G, this has to be zero (the offset points to a 48648d5063SArchit Taneja * scratch register which we never touch) 49a689554bSHai Li */ 50648d5063SArchit Taneja 51a689554bSHai Li ver = msm_readl(base + REG_DSI_VERSION); 52648d5063SArchit Taneja if (ver) { 53648d5063SArchit Taneja /* older dsi host, there is no register shift */ 54a689554bSHai Li ver = FIELD(ver, DSI_VERSION_MAJOR); 55a689554bSHai Li if (ver <= MSM_DSI_VER_MAJOR_V2) { 56a689554bSHai Li /* old versions */ 57a689554bSHai Li *major = ver; 58a689554bSHai Li *minor = 0; 59a689554bSHai Li return 0; 60a689554bSHai Li } else { 61a689554bSHai Li return -EINVAL; 62a689554bSHai Li } 63a689554bSHai Li } else { 64648d5063SArchit Taneja /* 65648d5063SArchit Taneja * newer host, offset 0 has 6G_HW_VERSION, the rest of the 66648d5063SArchit Taneja * registers are shifted down, read DSI_VERSION again with 67648d5063SArchit Taneja * the shifted offset 68648d5063SArchit Taneja */ 69a689554bSHai Li ver = msm_readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION); 70a689554bSHai Li ver = FIELD(ver, DSI_VERSION_MAJOR); 71a689554bSHai Li if (ver == MSM_DSI_VER_MAJOR_6G) { 72a689554bSHai Li /* 6G version */ 73a689554bSHai Li *major = ver; 74648d5063SArchit Taneja *minor = msm_readl(base + REG_DSI_6G_HW_VERSION); 75a689554bSHai Li return 0; 76a689554bSHai Li } else { 77a689554bSHai Li return -EINVAL; 78a689554bSHai Li } 79a689554bSHai Li } 80a689554bSHai Li } 81a689554bSHai Li 82a689554bSHai Li #define DSI_ERR_STATE_ACK 0x0000 83a689554bSHai Li #define DSI_ERR_STATE_TIMEOUT 0x0001 84a689554bSHai Li #define DSI_ERR_STATE_DLN0_PHY 0x0002 85a689554bSHai Li #define DSI_ERR_STATE_FIFO 0x0004 86a689554bSHai Li #define DSI_ERR_STATE_MDP_FIFO_UNDERFLOW 0x0008 87a689554bSHai Li #define DSI_ERR_STATE_INTERLEAVE_OP_CONTENTION 0x0010 88a689554bSHai Li #define DSI_ERR_STATE_PLL_UNLOCKED 0x0020 89a689554bSHai Li 90a689554bSHai Li #define DSI_CLK_CTRL_ENABLE_CLKS \ 91a689554bSHai Li (DSI_CLK_CTRL_AHBS_HCLK_ON | DSI_CLK_CTRL_AHBM_SCLK_ON | \ 92a689554bSHai Li DSI_CLK_CTRL_PCLK_ON | DSI_CLK_CTRL_DSICLK_ON | \ 93a689554bSHai Li DSI_CLK_CTRL_BYTECLK_ON | DSI_CLK_CTRL_ESCCLK_ON | \ 94a689554bSHai Li DSI_CLK_CTRL_FORCE_ON_DYN_AHBM_HCLK) 95a689554bSHai Li 96a689554bSHai Li struct msm_dsi_host { 97a689554bSHai Li struct mipi_dsi_host base; 98a689554bSHai Li 99a689554bSHai Li struct platform_device *pdev; 100a689554bSHai Li struct drm_device *dev; 101a689554bSHai Li 102a689554bSHai Li int id; 103a689554bSHai Li 104a689554bSHai Li void __iomem *ctrl_base; 105ec31abf6SHai Li struct regulator_bulk_data supplies[DSI_DEV_REGULATOR_MAX]; 1066e0eb52eSArchit Taneja 1076e0eb52eSArchit Taneja struct clk *bus_clks[DSI_BUS_CLK_MAX]; 1086e0eb52eSArchit Taneja 109a689554bSHai Li struct clk *byte_clk; 110a689554bSHai Li struct clk *esc_clk; 111a689554bSHai Li struct clk *pixel_clk; 1129d32c498SHai Li struct clk *byte_clk_src; 1139d32c498SHai Li struct clk *pixel_clk_src; 114c1d97083SArchit Taneja struct clk *byte_intf_clk; 1159d32c498SHai Li 11632d3e0feSRajendra Nayak struct opp_table *opp_table; 11732d3e0feSRajendra Nayak 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 171a689554bSHai Li u32 dma_cmd_ctrl_restore; 172a689554bSHai Li 173a689554bSHai Li bool registered; 174a689554bSHai Li bool power_on; 1759c5638d7SAbhinav Kumar bool enabled; 176a689554bSHai Li int irq; 177a689554bSHai Li }; 178a689554bSHai Li 179a689554bSHai Li static u32 dsi_get_bpp(const enum mipi_dsi_pixel_format fmt) 180a689554bSHai Li { 181a689554bSHai Li switch (fmt) { 182a689554bSHai Li case MIPI_DSI_FMT_RGB565: return 16; 183a689554bSHai Li case MIPI_DSI_FMT_RGB666_PACKED: return 18; 184a689554bSHai Li case MIPI_DSI_FMT_RGB666: 185a689554bSHai Li case MIPI_DSI_FMT_RGB888: 186a689554bSHai Li default: return 24; 187a689554bSHai Li } 188a689554bSHai Li } 189a689554bSHai Li 190a689554bSHai Li static inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) 191a689554bSHai Li { 192d248b61fSHai Li return msm_readl(msm_host->ctrl_base + reg); 193a689554bSHai Li } 194a689554bSHai Li static inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) 195a689554bSHai Li { 196d248b61fSHai Li msm_writel(data, msm_host->ctrl_base + reg); 197a689554bSHai Li } 198a689554bSHai Li 199a689554bSHai Li static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host); 200a689554bSHai Li static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host); 201a689554bSHai Li 202d248b61fSHai Li static const struct msm_dsi_cfg_handler *dsi_get_config( 203d248b61fSHai Li struct msm_dsi_host *msm_host) 204a689554bSHai Li { 205d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd = NULL; 20631c92767SArchit Taneja struct device *dev = &msm_host->pdev->dev; 207a689554bSHai Li struct regulator *gdsc_reg; 20831c92767SArchit Taneja struct clk *ahb_clk; 209d248b61fSHai Li int ret; 210a689554bSHai Li u32 major = 0, minor = 0; 211a689554bSHai Li 21231c92767SArchit Taneja gdsc_reg = regulator_get(dev, "gdsc"); 213bdc80de2SFabian Frederick if (IS_ERR(gdsc_reg)) { 214a689554bSHai Li pr_err("%s: cannot get gdsc\n", __func__); 215d248b61fSHai Li goto exit; 216a689554bSHai Li } 21731c92767SArchit Taneja 21829a1157cSArchit Taneja ahb_clk = msm_clk_get(msm_host->pdev, "iface"); 21931c92767SArchit Taneja if (IS_ERR(ahb_clk)) { 22031c92767SArchit Taneja pr_err("%s: cannot get interface clock\n", __func__); 22131c92767SArchit Taneja goto put_gdsc; 22231c92767SArchit Taneja } 22331c92767SArchit Taneja 224f6be1121SArchit Taneja pm_runtime_get_sync(dev); 225f6be1121SArchit Taneja 226a689554bSHai Li ret = regulator_enable(gdsc_reg); 227a689554bSHai Li if (ret) { 228a689554bSHai Li pr_err("%s: unable to enable gdsc\n", __func__); 22929a1157cSArchit Taneja goto put_gdsc; 230a689554bSHai Li } 23131c92767SArchit Taneja 23231c92767SArchit Taneja ret = clk_prepare_enable(ahb_clk); 233a689554bSHai Li if (ret) { 234a689554bSHai Li pr_err("%s: unable to enable ahb_clk\n", __func__); 235d248b61fSHai Li goto disable_gdsc; 236a689554bSHai Li } 237a689554bSHai Li 238a689554bSHai Li ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); 239a689554bSHai Li if (ret) { 240a689554bSHai Li pr_err("%s: Invalid version\n", __func__); 241d248b61fSHai Li goto disable_clks; 242a689554bSHai Li } 243a689554bSHai Li 244d248b61fSHai Li cfg_hnd = msm_dsi_cfg_get(major, minor); 245a689554bSHai Li 246d248b61fSHai Li DBG("%s: Version %x:%x\n", __func__, major, minor); 247d248b61fSHai Li 248d248b61fSHai Li disable_clks: 24931c92767SArchit Taneja clk_disable_unprepare(ahb_clk); 250d248b61fSHai Li disable_gdsc: 251d248b61fSHai Li regulator_disable(gdsc_reg); 252a18a0ea0SArchit Taneja pm_runtime_put_sync(dev); 253d248b61fSHai Li put_gdsc: 254d248b61fSHai Li regulator_put(gdsc_reg); 255d248b61fSHai Li exit: 256d248b61fSHai Li return cfg_hnd; 257a689554bSHai Li } 258a689554bSHai Li 259a689554bSHai Li static inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) 260a689554bSHai Li { 261a689554bSHai Li return container_of(host, struct msm_dsi_host, base); 262a689554bSHai Li } 263a689554bSHai Li 264a689554bSHai Li static void dsi_host_regulator_disable(struct msm_dsi_host *msm_host) 265a689554bSHai Li { 266a689554bSHai Li struct regulator_bulk_data *s = msm_host->supplies; 267d248b61fSHai Li const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; 268d248b61fSHai Li int num = msm_host->cfg_hnd->cfg->reg_cfg.num; 269a689554bSHai Li int i; 270a689554bSHai Li 271a689554bSHai Li DBG(""); 272a689554bSHai Li for (i = num - 1; i >= 0; i--) 273a689554bSHai Li if (regs[i].disable_load >= 0) 2742c33ce00SDave Airlie regulator_set_load(s[i].consumer, 275a689554bSHai Li regs[i].disable_load); 276a689554bSHai Li 277a689554bSHai Li regulator_bulk_disable(num, s); 278a689554bSHai Li } 279a689554bSHai Li 280a689554bSHai Li static int dsi_host_regulator_enable(struct msm_dsi_host *msm_host) 281a689554bSHai Li { 282a689554bSHai Li struct regulator_bulk_data *s = msm_host->supplies; 283d248b61fSHai Li const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; 284d248b61fSHai Li int num = msm_host->cfg_hnd->cfg->reg_cfg.num; 285a689554bSHai Li int ret, i; 286a689554bSHai Li 287a689554bSHai Li DBG(""); 288a689554bSHai Li for (i = 0; i < num; i++) { 289a689554bSHai Li if (regs[i].enable_load >= 0) { 2902c33ce00SDave Airlie ret = regulator_set_load(s[i].consumer, 291a689554bSHai Li regs[i].enable_load); 292a689554bSHai Li if (ret < 0) { 293a689554bSHai Li pr_err("regulator %d set op mode failed, %d\n", 294a689554bSHai Li i, ret); 295a689554bSHai Li goto fail; 296a689554bSHai Li } 297a689554bSHai Li } 298a689554bSHai Li } 299a689554bSHai Li 300a689554bSHai Li ret = regulator_bulk_enable(num, s); 301a689554bSHai Li if (ret < 0) { 302a689554bSHai Li pr_err("regulator enable failed, %d\n", ret); 303a689554bSHai Li goto fail; 304a689554bSHai Li } 305a689554bSHai Li 306a689554bSHai Li return 0; 307a689554bSHai Li 308a689554bSHai Li fail: 309a689554bSHai Li for (i--; i >= 0; i--) 3102c33ce00SDave Airlie regulator_set_load(s[i].consumer, regs[i].disable_load); 311a689554bSHai Li return ret; 312a689554bSHai Li } 313a689554bSHai Li 314a689554bSHai Li static int dsi_regulator_init(struct msm_dsi_host *msm_host) 315a689554bSHai Li { 316a689554bSHai Li struct regulator_bulk_data *s = msm_host->supplies; 317d248b61fSHai Li const struct dsi_reg_entry *regs = msm_host->cfg_hnd->cfg->reg_cfg.regs; 318d248b61fSHai Li int num = msm_host->cfg_hnd->cfg->reg_cfg.num; 319a689554bSHai Li int i, ret; 320a689554bSHai Li 321a689554bSHai Li for (i = 0; i < num; i++) 322a689554bSHai Li s[i].supply = regs[i].name; 323a689554bSHai Li 324a689554bSHai Li ret = devm_regulator_bulk_get(&msm_host->pdev->dev, num, s); 325a689554bSHai Li if (ret < 0) { 326a689554bSHai Li pr_err("%s: failed to init regulator, ret=%d\n", 327a689554bSHai Li __func__, ret); 328a689554bSHai Li return ret; 329a689554bSHai Li } 330a689554bSHai Li 331a689554bSHai Li return 0; 332a689554bSHai Li } 333a689554bSHai Li 334c4d8cfe5SSibi Sankar int dsi_clk_init_v2(struct msm_dsi_host *msm_host) 335c4d8cfe5SSibi Sankar { 336c4d8cfe5SSibi Sankar struct platform_device *pdev = msm_host->pdev; 337c4d8cfe5SSibi Sankar int ret = 0; 338c4d8cfe5SSibi Sankar 339c4d8cfe5SSibi Sankar msm_host->src_clk = msm_clk_get(pdev, "src"); 340c4d8cfe5SSibi Sankar 341c4d8cfe5SSibi Sankar if (IS_ERR(msm_host->src_clk)) { 342c4d8cfe5SSibi Sankar ret = PTR_ERR(msm_host->src_clk); 343c4d8cfe5SSibi Sankar pr_err("%s: can't find src clock. ret=%d\n", 344c4d8cfe5SSibi Sankar __func__, ret); 345c4d8cfe5SSibi Sankar msm_host->src_clk = NULL; 346c4d8cfe5SSibi Sankar return ret; 347c4d8cfe5SSibi Sankar } 348c4d8cfe5SSibi Sankar 349c4d8cfe5SSibi Sankar msm_host->esc_clk_src = clk_get_parent(msm_host->esc_clk); 350c4d8cfe5SSibi Sankar if (!msm_host->esc_clk_src) { 351c4d8cfe5SSibi Sankar ret = -ENODEV; 352c4d8cfe5SSibi Sankar pr_err("%s: can't get esc clock parent. ret=%d\n", 353c4d8cfe5SSibi Sankar __func__, ret); 354c4d8cfe5SSibi Sankar return ret; 355c4d8cfe5SSibi Sankar } 356c4d8cfe5SSibi Sankar 357c4d8cfe5SSibi Sankar msm_host->dsi_clk_src = clk_get_parent(msm_host->src_clk); 358c4d8cfe5SSibi Sankar if (!msm_host->dsi_clk_src) { 359c4d8cfe5SSibi Sankar ret = -ENODEV; 360c4d8cfe5SSibi Sankar pr_err("%s: can't get src clock parent. ret=%d\n", 361c4d8cfe5SSibi Sankar __func__, ret); 362c4d8cfe5SSibi Sankar } 363c4d8cfe5SSibi Sankar 364c4d8cfe5SSibi Sankar return ret; 365c4d8cfe5SSibi Sankar } 366c4d8cfe5SSibi Sankar 367c4d8cfe5SSibi Sankar int dsi_clk_init_6g_v2(struct msm_dsi_host *msm_host) 368c4d8cfe5SSibi Sankar { 369c4d8cfe5SSibi Sankar struct platform_device *pdev = msm_host->pdev; 370c4d8cfe5SSibi Sankar int ret = 0; 371c4d8cfe5SSibi Sankar 372c4d8cfe5SSibi Sankar msm_host->byte_intf_clk = msm_clk_get(pdev, "byte_intf"); 373c4d8cfe5SSibi Sankar if (IS_ERR(msm_host->byte_intf_clk)) { 374c4d8cfe5SSibi Sankar ret = PTR_ERR(msm_host->byte_intf_clk); 375c4d8cfe5SSibi Sankar pr_err("%s: can't find byte_intf clock. ret=%d\n", 376c4d8cfe5SSibi Sankar __func__, ret); 377c4d8cfe5SSibi Sankar } 378c4d8cfe5SSibi Sankar 379c4d8cfe5SSibi Sankar return ret; 380c4d8cfe5SSibi Sankar } 381c4d8cfe5SSibi Sankar 382a689554bSHai Li static int dsi_clk_init(struct msm_dsi_host *msm_host) 383a689554bSHai Li { 384db9a3750SRob Clark struct platform_device *pdev = msm_host->pdev; 3854bfa9748SArchit Taneja const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 3864bfa9748SArchit Taneja const struct msm_dsi_config *cfg = cfg_hnd->cfg; 3876e0eb52eSArchit Taneja int i, ret = 0; 388a689554bSHai Li 3896e0eb52eSArchit Taneja /* get bus clocks */ 3906e0eb52eSArchit Taneja for (i = 0; i < cfg->num_bus_clks; i++) { 391db9a3750SRob Clark msm_host->bus_clks[i] = msm_clk_get(pdev, 3926e0eb52eSArchit Taneja cfg->bus_clk_names[i]); 3936e0eb52eSArchit Taneja if (IS_ERR(msm_host->bus_clks[i])) { 3946e0eb52eSArchit Taneja ret = PTR_ERR(msm_host->bus_clks[i]); 395db9a3750SRob Clark pr_err("%s: Unable to get %s clock, ret = %d\n", 3966e0eb52eSArchit Taneja __func__, cfg->bus_clk_names[i], ret); 397a689554bSHai Li goto exit; 398a689554bSHai Li } 399a689554bSHai Li } 400a689554bSHai Li 4016e0eb52eSArchit Taneja /* get link and source clocks */ 402db9a3750SRob Clark msm_host->byte_clk = msm_clk_get(pdev, "byte"); 403a689554bSHai Li if (IS_ERR(msm_host->byte_clk)) { 404a689554bSHai Li ret = PTR_ERR(msm_host->byte_clk); 405db9a3750SRob Clark pr_err("%s: can't find dsi_byte clock. ret=%d\n", 406a689554bSHai Li __func__, ret); 407a689554bSHai Li msm_host->byte_clk = NULL; 408a689554bSHai Li goto exit; 409a689554bSHai Li } 410a689554bSHai Li 411db9a3750SRob Clark msm_host->pixel_clk = msm_clk_get(pdev, "pixel"); 412a689554bSHai Li if (IS_ERR(msm_host->pixel_clk)) { 413a689554bSHai Li ret = PTR_ERR(msm_host->pixel_clk); 414db9a3750SRob Clark pr_err("%s: can't find dsi_pixel clock. ret=%d\n", 415a689554bSHai Li __func__, ret); 416a689554bSHai Li msm_host->pixel_clk = NULL; 417a689554bSHai Li goto exit; 418a689554bSHai Li } 419a689554bSHai Li 420db9a3750SRob Clark msm_host->esc_clk = msm_clk_get(pdev, "core"); 421a689554bSHai Li if (IS_ERR(msm_host->esc_clk)) { 422a689554bSHai Li ret = PTR_ERR(msm_host->esc_clk); 423db9a3750SRob Clark pr_err("%s: can't find dsi_esc clock. ret=%d\n", 424a689554bSHai Li __func__, ret); 425a689554bSHai Li msm_host->esc_clk = NULL; 426a689554bSHai Li goto exit; 427a689554bSHai Li } 428a689554bSHai Li 429e6c4c78cSArchit Taneja msm_host->byte_clk_src = clk_get_parent(msm_host->byte_clk); 4305fb9b797SSean Paul if (IS_ERR(msm_host->byte_clk_src)) { 4315fb9b797SSean Paul ret = PTR_ERR(msm_host->byte_clk_src); 432db9a3750SRob Clark pr_err("%s: can't find byte_clk clock. ret=%d\n", __func__, ret); 4339d32c498SHai Li goto exit; 4349d32c498SHai Li } 4359d32c498SHai Li 436e6c4c78cSArchit Taneja msm_host->pixel_clk_src = clk_get_parent(msm_host->pixel_clk); 4375fb9b797SSean Paul if (IS_ERR(msm_host->pixel_clk_src)) { 4385fb9b797SSean Paul ret = PTR_ERR(msm_host->pixel_clk_src); 439db9a3750SRob Clark pr_err("%s: can't find pixel_clk clock. ret=%d\n", __func__, ret); 4404bfa9748SArchit Taneja goto exit; 4419d32c498SHai Li } 4429d32c498SHai Li 4438f7ca540SSibi Sankar if (cfg_hnd->ops->clk_init_ver) 4448f7ca540SSibi Sankar ret = cfg_hnd->ops->clk_init_ver(msm_host); 445a689554bSHai Li exit: 446a689554bSHai Li return ret; 447a689554bSHai Li } 448a689554bSHai Li 449a689554bSHai Li static int dsi_bus_clk_enable(struct msm_dsi_host *msm_host) 450a689554bSHai Li { 4516e0eb52eSArchit Taneja const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 4526e0eb52eSArchit Taneja int i, ret; 453a689554bSHai Li 454a689554bSHai Li DBG("id=%d", msm_host->id); 455a689554bSHai Li 4566e0eb52eSArchit Taneja for (i = 0; i < cfg->num_bus_clks; i++) { 4576e0eb52eSArchit Taneja ret = clk_prepare_enable(msm_host->bus_clks[i]); 458a689554bSHai Li if (ret) { 4596e0eb52eSArchit Taneja pr_err("%s: failed to enable bus clock %d ret %d\n", 4606e0eb52eSArchit Taneja __func__, i, ret); 4616e0eb52eSArchit Taneja goto err; 462a689554bSHai Li } 463a689554bSHai Li } 464a689554bSHai Li 465a689554bSHai Li return 0; 4666e0eb52eSArchit Taneja err: 4676e0eb52eSArchit Taneja for (; i > 0; i--) 4686e0eb52eSArchit Taneja clk_disable_unprepare(msm_host->bus_clks[i]); 469a689554bSHai Li 470a689554bSHai Li return ret; 471a689554bSHai Li } 472a689554bSHai Li 473a689554bSHai Li static void dsi_bus_clk_disable(struct msm_dsi_host *msm_host) 474a689554bSHai Li { 4756e0eb52eSArchit Taneja const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 4766e0eb52eSArchit Taneja int i; 4776e0eb52eSArchit Taneja 478a689554bSHai Li DBG(""); 4796e0eb52eSArchit Taneja 4806e0eb52eSArchit Taneja for (i = cfg->num_bus_clks - 1; i >= 0; i--) 4816e0eb52eSArchit Taneja clk_disable_unprepare(msm_host->bus_clks[i]); 482a689554bSHai Li } 483a689554bSHai Li 484f54ca1a0SArchit Taneja int msm_dsi_runtime_suspend(struct device *dev) 485f54ca1a0SArchit Taneja { 486f54ca1a0SArchit Taneja struct platform_device *pdev = to_platform_device(dev); 487f54ca1a0SArchit Taneja struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); 488f54ca1a0SArchit Taneja struct mipi_dsi_host *host = msm_dsi->host; 489f54ca1a0SArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 490f54ca1a0SArchit Taneja 491f54ca1a0SArchit Taneja if (!msm_host->cfg_hnd) 492f54ca1a0SArchit Taneja return 0; 493f54ca1a0SArchit Taneja 494f54ca1a0SArchit Taneja dsi_bus_clk_disable(msm_host); 495f54ca1a0SArchit Taneja 496f54ca1a0SArchit Taneja return 0; 497f54ca1a0SArchit Taneja } 498f54ca1a0SArchit Taneja 499f54ca1a0SArchit Taneja int msm_dsi_runtime_resume(struct device *dev) 500f54ca1a0SArchit Taneja { 501f54ca1a0SArchit Taneja struct platform_device *pdev = to_platform_device(dev); 502f54ca1a0SArchit Taneja struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); 503f54ca1a0SArchit Taneja struct mipi_dsi_host *host = msm_dsi->host; 504f54ca1a0SArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 505f54ca1a0SArchit Taneja 506f54ca1a0SArchit Taneja if (!msm_host->cfg_hnd) 507f54ca1a0SArchit Taneja return 0; 508f54ca1a0SArchit Taneja 509f54ca1a0SArchit Taneja return dsi_bus_clk_enable(msm_host); 510f54ca1a0SArchit Taneja } 511f54ca1a0SArchit Taneja 5126b16f05aSRob Clark int dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host) 513a689554bSHai Li { 514a689554bSHai Li int ret; 515a689554bSHai Li 516a689554bSHai Li DBG("Set clk rates: pclk=%d, byteclk=%d", 517a689554bSHai Li msm_host->mode->clock, msm_host->byte_clk_rate); 518a689554bSHai Li 51932d3e0feSRajendra Nayak ret = dev_pm_opp_set_rate(&msm_host->pdev->dev, 52032d3e0feSRajendra Nayak msm_host->byte_clk_rate); 521a689554bSHai Li if (ret) { 52232d3e0feSRajendra Nayak pr_err("%s: dev_pm_opp_set_rate failed %d\n", __func__, ret); 5236b16f05aSRob Clark return ret; 524a689554bSHai Li } 525a689554bSHai Li 526ed9976a0SChandan Uddaraju ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); 527a689554bSHai Li if (ret) { 528a689554bSHai Li pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); 5296b16f05aSRob Clark return ret; 530a689554bSHai Li } 531a689554bSHai Li 532c1d97083SArchit Taneja if (msm_host->byte_intf_clk) { 533c1d97083SArchit Taneja ret = clk_set_rate(msm_host->byte_intf_clk, 534c1d97083SArchit Taneja msm_host->byte_clk_rate / 2); 535c1d97083SArchit Taneja if (ret) { 536c1d97083SArchit Taneja pr_err("%s: Failed to set rate byte intf clk, %d\n", 537c1d97083SArchit Taneja __func__, ret); 5386b16f05aSRob Clark return ret; 539c1d97083SArchit Taneja } 540c1d97083SArchit Taneja } 541c1d97083SArchit Taneja 5426b16f05aSRob Clark return 0; 5436b16f05aSRob Clark } 5446b16f05aSRob Clark 5456b16f05aSRob Clark 5466b16f05aSRob Clark int dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) 5476b16f05aSRob Clark { 5486b16f05aSRob Clark int ret; 5496b16f05aSRob Clark 550a689554bSHai Li ret = clk_prepare_enable(msm_host->esc_clk); 551a689554bSHai Li if (ret) { 552a689554bSHai Li pr_err("%s: Failed to enable dsi esc clk\n", __func__); 553a689554bSHai Li goto error; 554a689554bSHai Li } 555a689554bSHai Li 556a689554bSHai Li ret = clk_prepare_enable(msm_host->byte_clk); 557a689554bSHai Li if (ret) { 558a689554bSHai Li pr_err("%s: Failed to enable dsi byte clk\n", __func__); 559a689554bSHai Li goto byte_clk_err; 560a689554bSHai Li } 561a689554bSHai Li 562a689554bSHai Li ret = clk_prepare_enable(msm_host->pixel_clk); 563a689554bSHai Li if (ret) { 564a689554bSHai Li pr_err("%s: Failed to enable dsi pixel clk\n", __func__); 565a689554bSHai Li goto pixel_clk_err; 566a689554bSHai Li } 567a689554bSHai Li 568c1d97083SArchit Taneja if (msm_host->byte_intf_clk) { 569c1d97083SArchit Taneja ret = clk_prepare_enable(msm_host->byte_intf_clk); 570c1d97083SArchit Taneja if (ret) { 571c1d97083SArchit Taneja pr_err("%s: Failed to enable byte intf clk\n", 572c1d97083SArchit Taneja __func__); 573c1d97083SArchit Taneja goto byte_intf_clk_err; 574c1d97083SArchit Taneja } 575c1d97083SArchit Taneja } 576c1d97083SArchit Taneja 577a689554bSHai Li return 0; 578a689554bSHai Li 579c1d97083SArchit Taneja byte_intf_clk_err: 580c1d97083SArchit Taneja clk_disable_unprepare(msm_host->pixel_clk); 581a689554bSHai Li pixel_clk_err: 582a689554bSHai Li clk_disable_unprepare(msm_host->byte_clk); 583a689554bSHai Li byte_clk_err: 584a689554bSHai Li clk_disable_unprepare(msm_host->esc_clk); 585a689554bSHai Li error: 586a689554bSHai Li return ret; 587a689554bSHai Li } 588a689554bSHai Li 5896b16f05aSRob Clark int dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host) 5904bfa9748SArchit Taneja { 5914bfa9748SArchit Taneja int ret; 5924bfa9748SArchit Taneja 5934bfa9748SArchit Taneja DBG("Set clk rates: pclk=%d, byteclk=%d, esc_clk=%d, dsi_src_clk=%d", 5944bfa9748SArchit Taneja msm_host->mode->clock, msm_host->byte_clk_rate, 5954bfa9748SArchit Taneja msm_host->esc_clk_rate, msm_host->src_clk_rate); 5964bfa9748SArchit Taneja 5974bfa9748SArchit Taneja ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate); 5984bfa9748SArchit Taneja if (ret) { 5994bfa9748SArchit Taneja pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret); 6006b16f05aSRob Clark return ret; 6014bfa9748SArchit Taneja } 6024bfa9748SArchit Taneja 6034bfa9748SArchit Taneja ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate); 6044bfa9748SArchit Taneja if (ret) { 6054bfa9748SArchit Taneja pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret); 6066b16f05aSRob Clark return ret; 6074bfa9748SArchit Taneja } 6084bfa9748SArchit Taneja 6094bfa9748SArchit Taneja ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate); 6104bfa9748SArchit Taneja if (ret) { 6114bfa9748SArchit Taneja pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret); 6126b16f05aSRob Clark return ret; 6134bfa9748SArchit Taneja } 6144bfa9748SArchit Taneja 615ed9976a0SChandan Uddaraju ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); 6164bfa9748SArchit Taneja if (ret) { 6174bfa9748SArchit Taneja pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); 6186b16f05aSRob Clark return ret; 6194bfa9748SArchit Taneja } 6204bfa9748SArchit Taneja 6216b16f05aSRob Clark return 0; 6226b16f05aSRob Clark } 6236b16f05aSRob Clark 6246b16f05aSRob Clark int dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) 6256b16f05aSRob Clark { 6266b16f05aSRob Clark int ret; 6276b16f05aSRob Clark 6284bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->byte_clk); 6294bfa9748SArchit Taneja if (ret) { 6304bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi byte clk\n", __func__); 6314bfa9748SArchit Taneja goto error; 6324bfa9748SArchit Taneja } 6334bfa9748SArchit Taneja 6344bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->esc_clk); 6354bfa9748SArchit Taneja if (ret) { 6364bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi esc clk\n", __func__); 6374bfa9748SArchit Taneja goto esc_clk_err; 6384bfa9748SArchit Taneja } 6394bfa9748SArchit Taneja 6404bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->src_clk); 6414bfa9748SArchit Taneja if (ret) { 6424bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi src clk\n", __func__); 6434bfa9748SArchit Taneja goto src_clk_err; 6444bfa9748SArchit Taneja } 6454bfa9748SArchit Taneja 6464bfa9748SArchit Taneja ret = clk_prepare_enable(msm_host->pixel_clk); 6474bfa9748SArchit Taneja if (ret) { 6484bfa9748SArchit Taneja pr_err("%s: Failed to enable dsi pixel clk\n", __func__); 6494bfa9748SArchit Taneja goto pixel_clk_err; 6504bfa9748SArchit Taneja } 6514bfa9748SArchit Taneja 6524bfa9748SArchit Taneja return 0; 6534bfa9748SArchit Taneja 6544bfa9748SArchit Taneja pixel_clk_err: 6554bfa9748SArchit Taneja clk_disable_unprepare(msm_host->src_clk); 6564bfa9748SArchit Taneja src_clk_err: 6574bfa9748SArchit Taneja clk_disable_unprepare(msm_host->esc_clk); 6584bfa9748SArchit Taneja esc_clk_err: 6594bfa9748SArchit Taneja clk_disable_unprepare(msm_host->byte_clk); 6604bfa9748SArchit Taneja error: 6614bfa9748SArchit Taneja return ret; 6624bfa9748SArchit Taneja } 6634bfa9748SArchit Taneja 664c4d8cfe5SSibi Sankar void dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host) 665c4d8cfe5SSibi Sankar { 66632d3e0feSRajendra Nayak /* Drop the performance state vote */ 66732d3e0feSRajendra Nayak dev_pm_opp_set_rate(&msm_host->pdev->dev, 0); 668c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->esc_clk); 669c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->pixel_clk); 670c4d8cfe5SSibi Sankar if (msm_host->byte_intf_clk) 671c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->byte_intf_clk); 672c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->byte_clk); 673c4d8cfe5SSibi Sankar } 674c4d8cfe5SSibi Sankar 675c4d8cfe5SSibi Sankar void dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host) 676c4d8cfe5SSibi Sankar { 677c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->pixel_clk); 678c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->src_clk); 679c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->esc_clk); 680c4d8cfe5SSibi Sankar clk_disable_unprepare(msm_host->byte_clk); 681c4d8cfe5SSibi Sankar } 682c4d8cfe5SSibi Sankar 683a6bcddbcSSean Paul static u32 dsi_get_pclk_rate(struct msm_dsi_host *msm_host, bool is_dual_dsi) 684c4d8cfe5SSibi Sankar { 685c4d8cfe5SSibi Sankar struct drm_display_mode *mode = msm_host->mode; 686c4d8cfe5SSibi Sankar u32 pclk_rate; 687c4d8cfe5SSibi Sankar 688c4d8cfe5SSibi Sankar pclk_rate = mode->clock * 1000; 689ed9976a0SChandan Uddaraju 690ed9976a0SChandan Uddaraju /* 691ed9976a0SChandan Uddaraju * For dual DSI mode, the current DRM mode has the complete width of the 692ed9976a0SChandan Uddaraju * panel. Since, the complete panel is driven by two DSI controllers, 693ed9976a0SChandan Uddaraju * the clock rates have to be split between the two dsi controllers. 694ed9976a0SChandan Uddaraju * Adjust the byte and pixel clock rates for each dsi host accordingly. 695ed9976a0SChandan Uddaraju */ 696ed9976a0SChandan Uddaraju if (is_dual_dsi) 697ed9976a0SChandan Uddaraju pclk_rate /= 2; 698ed9976a0SChandan Uddaraju 699a6bcddbcSSean Paul return pclk_rate; 700a6bcddbcSSean Paul } 701a6bcddbcSSean Paul 702a6bcddbcSSean Paul static void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_dual_dsi) 703a6bcddbcSSean Paul { 704a6bcddbcSSean Paul u8 lanes = msm_host->lanes; 705a6bcddbcSSean Paul u32 bpp = dsi_get_bpp(msm_host->format); 706a6bcddbcSSean Paul u32 pclk_rate = dsi_get_pclk_rate(msm_host, is_dual_dsi); 707a6bcddbcSSean Paul u64 pclk_bpp = (u64)pclk_rate * bpp; 708a6bcddbcSSean Paul 709a6bcddbcSSean Paul if (lanes == 0) { 710c4d8cfe5SSibi Sankar pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); 711a6bcddbcSSean Paul lanes = 1; 712c4d8cfe5SSibi Sankar } 713c4d8cfe5SSibi Sankar 7142d0b10fcSAbhinav Kumar do_div(pclk_bpp, (8 * lanes)); 715a6bcddbcSSean Paul 716ed9976a0SChandan Uddaraju msm_host->pixel_clk_rate = pclk_rate; 7172d0b10fcSAbhinav Kumar msm_host->byte_clk_rate = pclk_bpp; 718c4d8cfe5SSibi Sankar 719ed9976a0SChandan Uddaraju DBG("pclk=%d, bclk=%d", msm_host->pixel_clk_rate, 720ed9976a0SChandan Uddaraju msm_host->byte_clk_rate); 721c4d8cfe5SSibi Sankar 722a6bcddbcSSean Paul } 723a6bcddbcSSean Paul 724a6bcddbcSSean Paul int dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_dual_dsi) 725a6bcddbcSSean Paul { 726a6bcddbcSSean Paul if (!msm_host->mode) { 727a6bcddbcSSean Paul pr_err("%s: mode not set\n", __func__); 728a6bcddbcSSean Paul return -EINVAL; 729a6bcddbcSSean Paul } 730a6bcddbcSSean Paul 731a6bcddbcSSean Paul dsi_calc_pclk(msm_host, is_dual_dsi); 732a6bcddbcSSean Paul msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); 733a6bcddbcSSean Paul return 0; 734a6bcddbcSSean Paul } 735a6bcddbcSSean Paul 736a6bcddbcSSean Paul int dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_dual_dsi) 737a6bcddbcSSean Paul { 738a6bcddbcSSean Paul u32 bpp = dsi_get_bpp(msm_host->format); 739a6bcddbcSSean Paul u64 pclk_bpp; 740a6bcddbcSSean Paul unsigned int esc_mhz, esc_div; 741a6bcddbcSSean Paul unsigned long byte_mhz; 742a6bcddbcSSean Paul 743a6bcddbcSSean Paul dsi_calc_pclk(msm_host, is_dual_dsi); 744a6bcddbcSSean Paul 745a6bcddbcSSean Paul pclk_bpp = (u64)dsi_get_pclk_rate(msm_host, is_dual_dsi) * bpp; 746a6bcddbcSSean Paul do_div(pclk_bpp, 8); 747a6bcddbcSSean Paul msm_host->src_clk_rate = pclk_bpp; 748c4d8cfe5SSibi Sankar 749c4d8cfe5SSibi Sankar /* 750c4d8cfe5SSibi Sankar * esc clock is byte clock followed by a 4 bit divider, 751c4d8cfe5SSibi Sankar * we need to find an escape clock frequency within the 752c4d8cfe5SSibi Sankar * mipi DSI spec range within the maximum divider limit 753c4d8cfe5SSibi Sankar * We iterate here between an escape clock frequencey 754c4d8cfe5SSibi Sankar * between 20 Mhz to 5 Mhz and pick up the first one 755c4d8cfe5SSibi Sankar * that can be supported by our divider 756c4d8cfe5SSibi Sankar */ 757c4d8cfe5SSibi Sankar 758c4d8cfe5SSibi Sankar byte_mhz = msm_host->byte_clk_rate / 1000000; 759c4d8cfe5SSibi Sankar 760c4d8cfe5SSibi Sankar for (esc_mhz = 20; esc_mhz >= 5; esc_mhz--) { 761c4d8cfe5SSibi Sankar esc_div = DIV_ROUND_UP(byte_mhz, esc_mhz); 762c4d8cfe5SSibi Sankar 763c4d8cfe5SSibi Sankar /* 764c4d8cfe5SSibi Sankar * TODO: Ideally, we shouldn't know what sort of divider 765c4d8cfe5SSibi Sankar * is available in mmss_cc, we're just assuming that 766c4d8cfe5SSibi Sankar * it'll always be a 4 bit divider. Need to come up with 767c4d8cfe5SSibi Sankar * a better way here. 768c4d8cfe5SSibi Sankar */ 769c4d8cfe5SSibi Sankar if (esc_div >= 1 && esc_div <= 16) 770c4d8cfe5SSibi Sankar break; 771c4d8cfe5SSibi Sankar } 772c4d8cfe5SSibi Sankar 773c4d8cfe5SSibi Sankar if (esc_mhz < 5) 774c4d8cfe5SSibi Sankar return -EINVAL; 775c4d8cfe5SSibi Sankar 776c4d8cfe5SSibi Sankar msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div; 777c4d8cfe5SSibi Sankar 778c4d8cfe5SSibi Sankar DBG("esc=%d, src=%d", msm_host->esc_clk_rate, 779c4d8cfe5SSibi Sankar msm_host->src_clk_rate); 780c4d8cfe5SSibi Sankar 781c4d8cfe5SSibi Sankar return 0; 782c4d8cfe5SSibi Sankar } 783c4d8cfe5SSibi Sankar 784a689554bSHai Li static void dsi_intr_ctrl(struct msm_dsi_host *msm_host, u32 mask, int enable) 785a689554bSHai Li { 786a689554bSHai Li u32 intr; 787a689554bSHai Li unsigned long flags; 788a689554bSHai Li 789a689554bSHai Li spin_lock_irqsave(&msm_host->intr_lock, flags); 790a689554bSHai Li intr = dsi_read(msm_host, REG_DSI_INTR_CTRL); 791a689554bSHai Li 792a689554bSHai Li if (enable) 793a689554bSHai Li intr |= mask; 794a689554bSHai Li else 795a689554bSHai Li intr &= ~mask; 796a689554bSHai Li 797a689554bSHai Li DBG("intr=%x enable=%d", intr, enable); 798a689554bSHai Li 799a689554bSHai Li dsi_write(msm_host, REG_DSI_INTR_CTRL, intr); 800a689554bSHai Li spin_unlock_irqrestore(&msm_host->intr_lock, flags); 801a689554bSHai Li } 802a689554bSHai Li 803a689554bSHai Li static inline enum dsi_traffic_mode dsi_get_traffic_mode(const u32 mode_flags) 804a689554bSHai Li { 805a689554bSHai Li if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 806a689554bSHai Li return BURST_MODE; 807a689554bSHai Li else if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) 808a689554bSHai Li return NON_BURST_SYNCH_PULSE; 809a689554bSHai Li 810a689554bSHai Li return NON_BURST_SYNCH_EVENT; 811a689554bSHai Li } 812a689554bSHai Li 813a689554bSHai Li static inline enum dsi_vid_dst_format dsi_get_vid_fmt( 814a689554bSHai Li const enum mipi_dsi_pixel_format mipi_fmt) 815a689554bSHai Li { 816a689554bSHai Li switch (mipi_fmt) { 817a689554bSHai Li case MIPI_DSI_FMT_RGB888: return VID_DST_FORMAT_RGB888; 818a689554bSHai Li case MIPI_DSI_FMT_RGB666: return VID_DST_FORMAT_RGB666_LOOSE; 819a689554bSHai Li case MIPI_DSI_FMT_RGB666_PACKED: return VID_DST_FORMAT_RGB666; 820a689554bSHai Li case MIPI_DSI_FMT_RGB565: return VID_DST_FORMAT_RGB565; 821a689554bSHai Li default: return VID_DST_FORMAT_RGB888; 822a689554bSHai Li } 823a689554bSHai Li } 824a689554bSHai Li 825a689554bSHai Li static inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( 826a689554bSHai Li const enum mipi_dsi_pixel_format mipi_fmt) 827a689554bSHai Li { 828a689554bSHai Li switch (mipi_fmt) { 829a689554bSHai Li case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888; 830a689554bSHai Li case MIPI_DSI_FMT_RGB666_PACKED: 831cf606fe3SStefan Agner case MIPI_DSI_FMT_RGB666: return CMD_DST_FORMAT_RGB666; 832a689554bSHai Li case MIPI_DSI_FMT_RGB565: return CMD_DST_FORMAT_RGB565; 833a689554bSHai Li default: return CMD_DST_FORMAT_RGB888; 834a689554bSHai Li } 835a689554bSHai Li } 836a689554bSHai Li 837a689554bSHai Li static void dsi_ctrl_config(struct msm_dsi_host *msm_host, bool enable, 838dceac340SHai Li struct msm_dsi_phy_shared_timings *phy_shared_timings) 839a689554bSHai Li { 840a689554bSHai Li u32 flags = msm_host->mode_flags; 841a689554bSHai Li enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; 842d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 843e3ff6881SHarigovindan P u32 data = 0, lane_ctrl = 0; 844a689554bSHai Li 845a689554bSHai Li if (!enable) { 846a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, 0); 847a689554bSHai Li return; 848a689554bSHai Li } 849a689554bSHai Li 850a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO) { 851a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO_HSE) 852a689554bSHai Li data |= DSI_VID_CFG0_PULSE_MODE_HSA_HE; 853a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO_HFP) 854a689554bSHai Li data |= DSI_VID_CFG0_HFP_POWER_STOP; 855a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO_HBP) 856a689554bSHai Li data |= DSI_VID_CFG0_HBP_POWER_STOP; 857a689554bSHai Li if (flags & MIPI_DSI_MODE_VIDEO_HSA) 858a689554bSHai Li data |= DSI_VID_CFG0_HSA_POWER_STOP; 859a689554bSHai Li /* Always set low power stop mode for BLLP 860a689554bSHai Li * to let command engine send packets 861a689554bSHai Li */ 862a689554bSHai Li data |= DSI_VID_CFG0_EOF_BLLP_POWER_STOP | 863a689554bSHai Li DSI_VID_CFG0_BLLP_POWER_STOP; 864a689554bSHai Li data |= DSI_VID_CFG0_TRAFFIC_MODE(dsi_get_traffic_mode(flags)); 865a689554bSHai Li data |= DSI_VID_CFG0_DST_FORMAT(dsi_get_vid_fmt(mipi_fmt)); 866a689554bSHai Li data |= DSI_VID_CFG0_VIRT_CHANNEL(msm_host->channel); 867a689554bSHai Li dsi_write(msm_host, REG_DSI_VID_CFG0, data); 868a689554bSHai Li 869a689554bSHai Li /* Do not swap RGB colors */ 870a689554bSHai Li data = DSI_VID_CFG1_RGB_SWAP(SWAP_RGB); 871a689554bSHai Li dsi_write(msm_host, REG_DSI_VID_CFG1, 0); 872a689554bSHai Li } else { 873a689554bSHai Li /* Do not swap RGB colors */ 874a689554bSHai Li data = DSI_CMD_CFG0_RGB_SWAP(SWAP_RGB); 875a689554bSHai Li data |= DSI_CMD_CFG0_DST_FORMAT(dsi_get_cmd_fmt(mipi_fmt)); 876a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_CFG0, data); 877a689554bSHai Li 878a689554bSHai Li data = DSI_CMD_CFG1_WR_MEM_START(MIPI_DCS_WRITE_MEMORY_START) | 879a689554bSHai Li DSI_CMD_CFG1_WR_MEM_CONTINUE( 880a689554bSHai Li MIPI_DCS_WRITE_MEMORY_CONTINUE); 881a689554bSHai Li /* Always insert DCS command */ 882a689554bSHai Li data |= DSI_CMD_CFG1_INSERT_DCS_COMMAND; 883a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_CFG1, data); 884a689554bSHai Li } 885a689554bSHai Li 886a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, 887a689554bSHai Li DSI_CMD_DMA_CTRL_FROM_FRAME_BUFFER | 888a689554bSHai Li DSI_CMD_DMA_CTRL_LOW_POWER); 889a689554bSHai Li 890a689554bSHai Li data = 0; 891a689554bSHai Li /* Always assume dedicated TE pin */ 892a689554bSHai Li data |= DSI_TRIG_CTRL_TE; 893a689554bSHai Li data |= DSI_TRIG_CTRL_MDP_TRIGGER(TRIGGER_NONE); 894a689554bSHai Li data |= DSI_TRIG_CTRL_DMA_TRIGGER(TRIGGER_SW); 895a689554bSHai Li data |= DSI_TRIG_CTRL_STREAM(msm_host->channel); 896d248b61fSHai Li if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 897d248b61fSHai Li (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) 898a689554bSHai Li data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME; 899a689554bSHai Li dsi_write(msm_host, REG_DSI_TRIG_CTRL, data); 900a689554bSHai Li 901dceac340SHai Li data = DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(phy_shared_timings->clk_post) | 902dceac340SHai Li DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(phy_shared_timings->clk_pre); 903a689554bSHai Li dsi_write(msm_host, REG_DSI_CLKOUT_TIMING_CTRL, data); 904a689554bSHai Li 905dceac340SHai Li if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 906dceac340SHai Li (cfg_hnd->minor > MSM_DSI_6G_VER_MINOR_V1_0) && 907dceac340SHai Li phy_shared_timings->clk_pre_inc_by_2) 908dceac340SHai Li dsi_write(msm_host, REG_DSI_T_CLK_PRE_EXTEND, 909dceac340SHai Li DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK); 910dceac340SHai Li 911a689554bSHai Li data = 0; 912a689554bSHai Li if (!(flags & MIPI_DSI_MODE_EOT_PACKET)) 913a689554bSHai Li data |= DSI_EOT_PACKET_CTRL_TX_EOT_APPEND; 914a689554bSHai Li dsi_write(msm_host, REG_DSI_EOT_PACKET_CTRL, data); 915a689554bSHai Li 916a689554bSHai Li /* allow only ack-err-status to generate interrupt */ 917a689554bSHai Li dsi_write(msm_host, REG_DSI_ERR_INT_MASK0, 0x13ff3fe0); 918a689554bSHai Li 919a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 1); 920a689554bSHai Li 921a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 922a689554bSHai Li 923a689554bSHai Li data = DSI_CTRL_CLK_EN; 924a689554bSHai Li 925a689554bSHai Li DBG("lane number=%d", msm_host->lanes); 92626f7d1f4SArchit Taneja data |= ((DSI_CTRL_LANE0 << msm_host->lanes) - DSI_CTRL_LANE0); 92726f7d1f4SArchit Taneja 928a689554bSHai Li dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, 92926f7d1f4SArchit Taneja DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(msm_host->dlane_swap)); 93065c5e542SArchit Taneja 931e3ff6881SHarigovindan P if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) { 932e3ff6881SHarigovindan P lane_ctrl = dsi_read(msm_host, REG_DSI_LANE_CTRL); 93365c5e542SArchit Taneja dsi_write(msm_host, REG_DSI_LANE_CTRL, 934e3ff6881SHarigovindan P lane_ctrl | DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST); 935e3ff6881SHarigovindan P } 93665c5e542SArchit Taneja 937a689554bSHai Li data |= DSI_CTRL_ENABLE; 938a689554bSHai Li 939a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, data); 940a689554bSHai Li } 941a689554bSHai Li 942ed9976a0SChandan Uddaraju static void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_dual_dsi) 943a689554bSHai Li { 944a689554bSHai Li struct drm_display_mode *mode = msm_host->mode; 945a689554bSHai Li u32 hs_start = 0, vs_start = 0; /* take sync start as 0 */ 946a689554bSHai Li u32 h_total = mode->htotal; 947a689554bSHai Li u32 v_total = mode->vtotal; 948a689554bSHai Li u32 hs_end = mode->hsync_end - mode->hsync_start; 949a689554bSHai Li u32 vs_end = mode->vsync_end - mode->vsync_start; 950a689554bSHai Li u32 ha_start = h_total - mode->hsync_start; 951a689554bSHai Li u32 ha_end = ha_start + mode->hdisplay; 952a689554bSHai Li u32 va_start = v_total - mode->vsync_start; 953a689554bSHai Li u32 va_end = va_start + mode->vdisplay; 954ed9976a0SChandan Uddaraju u32 hdisplay = mode->hdisplay; 955a689554bSHai Li u32 wc; 956a689554bSHai Li 957a689554bSHai Li DBG(""); 958a689554bSHai Li 959ed9976a0SChandan Uddaraju /* 960ed9976a0SChandan Uddaraju * For dual DSI mode, the current DRM mode has 961ed9976a0SChandan Uddaraju * the complete width of the panel. Since, the complete 962ed9976a0SChandan Uddaraju * panel is driven by two DSI controllers, the horizontal 963ed9976a0SChandan Uddaraju * timings have to be split between the two dsi controllers. 964ed9976a0SChandan Uddaraju * Adjust the DSI host timing values accordingly. 965ed9976a0SChandan Uddaraju */ 966ed9976a0SChandan Uddaraju if (is_dual_dsi) { 967ed9976a0SChandan Uddaraju h_total /= 2; 968ed9976a0SChandan Uddaraju hs_end /= 2; 969ed9976a0SChandan Uddaraju ha_start /= 2; 970ed9976a0SChandan Uddaraju ha_end /= 2; 971ed9976a0SChandan Uddaraju hdisplay /= 2; 972ed9976a0SChandan Uddaraju } 973ed9976a0SChandan Uddaraju 974a689554bSHai Li if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) { 975a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_H, 976a689554bSHai Li DSI_ACTIVE_H_START(ha_start) | 977a689554bSHai Li DSI_ACTIVE_H_END(ha_end)); 978a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_V, 979a689554bSHai Li DSI_ACTIVE_V_START(va_start) | 980a689554bSHai Li DSI_ACTIVE_V_END(va_end)); 981a689554bSHai Li dsi_write(msm_host, REG_DSI_TOTAL, 982a689554bSHai Li DSI_TOTAL_H_TOTAL(h_total - 1) | 983a689554bSHai Li DSI_TOTAL_V_TOTAL(v_total - 1)); 984a689554bSHai Li 985a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_HSYNC, 986a689554bSHai Li DSI_ACTIVE_HSYNC_START(hs_start) | 987a689554bSHai Li DSI_ACTIVE_HSYNC_END(hs_end)); 988a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_VSYNC_HPOS, 0); 989a689554bSHai Li dsi_write(msm_host, REG_DSI_ACTIVE_VSYNC_VPOS, 990a689554bSHai Li DSI_ACTIVE_VSYNC_VPOS_START(vs_start) | 991a689554bSHai Li DSI_ACTIVE_VSYNC_VPOS_END(vs_end)); 992a689554bSHai Li } else { /* command mode */ 993a689554bSHai Li /* image data and 1 byte write_memory_start cmd */ 994ed9976a0SChandan Uddaraju wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; 995a689554bSHai Li 996c28c82e9SRob Clark dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, 997c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | 998c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_CTRL_VIRTUAL_CHANNEL( 999a689554bSHai Li msm_host->channel) | 1000c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_CTRL_DATA_TYPE( 1001a689554bSHai Li MIPI_DSI_DCS_LONG_WRITE)); 1002a689554bSHai Li 1003c28c82e9SRob Clark dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_TOTAL, 1004c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_TOTAL_H_TOTAL(hdisplay) | 1005c28c82e9SRob Clark DSI_CMD_MDP_STREAM0_TOTAL_V_TOTAL(mode->vdisplay)); 1006a689554bSHai Li } 1007a689554bSHai Li } 1008a689554bSHai Li 1009a689554bSHai Li static void dsi_sw_reset(struct msm_dsi_host *msm_host) 1010a689554bSHai Li { 1011a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 1012a689554bSHai Li wmb(); /* clocks need to be enabled before reset */ 1013a689554bSHai Li 1014a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 1); 101578e31c42SJeffrey Hugo msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ 1016a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 0); 1017a689554bSHai Li } 1018a689554bSHai Li 1019a689554bSHai Li static void dsi_op_mode_config(struct msm_dsi_host *msm_host, 1020a689554bSHai Li bool video_mode, bool enable) 1021a689554bSHai Li { 1022a689554bSHai Li u32 dsi_ctrl; 1023a689554bSHai Li 1024a689554bSHai Li dsi_ctrl = dsi_read(msm_host, REG_DSI_CTRL); 1025a689554bSHai Li 1026a689554bSHai Li if (!enable) { 1027a689554bSHai Li dsi_ctrl &= ~(DSI_CTRL_ENABLE | DSI_CTRL_VID_MODE_EN | 1028a689554bSHai Li DSI_CTRL_CMD_MODE_EN); 1029a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_MDP_DONE | 1030a689554bSHai Li DSI_IRQ_MASK_VIDEO_DONE, 0); 1031a689554bSHai Li } else { 1032a689554bSHai Li if (video_mode) { 1033a689554bSHai Li dsi_ctrl |= DSI_CTRL_VID_MODE_EN; 1034a689554bSHai Li } else { /* command mode */ 1035a689554bSHai Li dsi_ctrl |= DSI_CTRL_CMD_MODE_EN; 1036a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_MDP_DONE, 1); 1037a689554bSHai Li } 1038a689554bSHai Li dsi_ctrl |= DSI_CTRL_ENABLE; 1039a689554bSHai Li } 1040a689554bSHai Li 1041a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, dsi_ctrl); 1042a689554bSHai Li } 1043a689554bSHai Li 1044a689554bSHai Li static void dsi_set_tx_power_mode(int mode, struct msm_dsi_host *msm_host) 1045a689554bSHai Li { 1046a689554bSHai Li u32 data; 1047a689554bSHai Li 1048a689554bSHai Li data = dsi_read(msm_host, REG_DSI_CMD_DMA_CTRL); 1049a689554bSHai Li 1050a689554bSHai Li if (mode == 0) 1051a689554bSHai Li data &= ~DSI_CMD_DMA_CTRL_LOW_POWER; 1052a689554bSHai Li else 1053a689554bSHai Li data |= DSI_CMD_DMA_CTRL_LOW_POWER; 1054a689554bSHai Li 1055a689554bSHai Li dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, data); 1056a689554bSHai Li } 1057a689554bSHai Li 1058a689554bSHai Li static void dsi_wait4video_done(struct msm_dsi_host *msm_host) 1059a689554bSHai Li { 106079ebc86cSAbhinav Kumar u32 ret = 0; 106179ebc86cSAbhinav Kumar struct device *dev = &msm_host->pdev->dev; 106279ebc86cSAbhinav Kumar 1063a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 1); 1064a689554bSHai Li 1065a689554bSHai Li reinit_completion(&msm_host->video_comp); 1066a689554bSHai Li 106779ebc86cSAbhinav Kumar ret = wait_for_completion_timeout(&msm_host->video_comp, 1068a689554bSHai Li msecs_to_jiffies(70)); 1069a689554bSHai Li 10709a4a153bSNicholas Mc Guire if (ret == 0) 10716a41da17SMamta Shukla DRM_DEV_ERROR(dev, "wait for video done timed out\n"); 107279ebc86cSAbhinav Kumar 1073a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 0); 1074a689554bSHai Li } 1075a689554bSHai Li 1076a689554bSHai Li static void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host) 1077a689554bSHai Li { 1078a689554bSHai Li if (!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) 1079a689554bSHai Li return; 1080a689554bSHai Li 10819c5638d7SAbhinav Kumar if (msm_host->power_on && msm_host->enabled) { 1082a689554bSHai Li dsi_wait4video_done(msm_host); 1083a689554bSHai Li /* delay 4 ms to skip BLLP */ 1084a689554bSHai Li usleep_range(2000, 4000); 1085a689554bSHai Li } 1086a689554bSHai Li } 1087a689554bSHai Li 1088c4d8cfe5SSibi Sankar int dsi_tx_buf_alloc_6g(struct msm_dsi_host *msm_host, int size) 1089c4d8cfe5SSibi Sankar { 1090c4d8cfe5SSibi Sankar struct drm_device *dev = msm_host->dev; 1091c4d8cfe5SSibi Sankar struct msm_drm_private *priv = dev->dev_private; 1092c4d8cfe5SSibi Sankar uint64_t iova; 1093c4d8cfe5SSibi Sankar u8 *data; 1094c4d8cfe5SSibi Sankar 1095c4d8cfe5SSibi Sankar data = msm_gem_kernel_new(dev, size, MSM_BO_UNCACHED, 1096c4d8cfe5SSibi Sankar priv->kms->aspace, 1097c4d8cfe5SSibi Sankar &msm_host->tx_gem_obj, &iova); 1098c4d8cfe5SSibi Sankar 1099c4d8cfe5SSibi Sankar if (IS_ERR(data)) { 1100c4d8cfe5SSibi Sankar msm_host->tx_gem_obj = NULL; 1101c4d8cfe5SSibi Sankar return PTR_ERR(data); 1102c4d8cfe5SSibi Sankar } 1103c4d8cfe5SSibi Sankar 11040815d774SJordan Crouse msm_gem_object_set_name(msm_host->tx_gem_obj, "tx_gem"); 11050815d774SJordan Crouse 1106c4d8cfe5SSibi Sankar msm_host->tx_size = msm_host->tx_gem_obj->size; 1107c4d8cfe5SSibi Sankar 1108c4d8cfe5SSibi Sankar return 0; 1109c4d8cfe5SSibi Sankar } 1110c4d8cfe5SSibi Sankar 1111c4d8cfe5SSibi Sankar int dsi_tx_buf_alloc_v2(struct msm_dsi_host *msm_host, int size) 1112c4d8cfe5SSibi Sankar { 1113c4d8cfe5SSibi Sankar struct drm_device *dev = msm_host->dev; 1114c4d8cfe5SSibi Sankar 1115c4d8cfe5SSibi Sankar msm_host->tx_buf = dma_alloc_coherent(dev->dev, size, 1116c4d8cfe5SSibi Sankar &msm_host->tx_buf_paddr, GFP_KERNEL); 1117c4d8cfe5SSibi Sankar if (!msm_host->tx_buf) 1118c4d8cfe5SSibi Sankar return -ENOMEM; 1119c4d8cfe5SSibi Sankar 1120c4d8cfe5SSibi Sankar msm_host->tx_size = size; 1121c4d8cfe5SSibi Sankar 1122c4d8cfe5SSibi Sankar return 0; 1123c4d8cfe5SSibi Sankar } 1124c4d8cfe5SSibi Sankar 1125a689554bSHai Li static void dsi_tx_buf_free(struct msm_dsi_host *msm_host) 1126a689554bSHai Li { 1127a689554bSHai Li struct drm_device *dev = msm_host->dev; 112874d3a3a7SSean Paul struct msm_drm_private *priv; 1129a689554bSHai Li 113074d3a3a7SSean Paul /* 113174d3a3a7SSean Paul * This is possible if we're tearing down before we've had a chance to 113274d3a3a7SSean Paul * fully initialize. A very real possibility if our probe is deferred, 113374d3a3a7SSean Paul * in which case we'll hit msm_dsi_host_destroy() without having run 113474d3a3a7SSean Paul * through the dsi_tx_buf_alloc(). 113574d3a3a7SSean Paul */ 113674d3a3a7SSean Paul if (!dev) 113774d3a3a7SSean Paul return; 113874d3a3a7SSean Paul 113974d3a3a7SSean Paul priv = dev->dev_private; 1140a689554bSHai Li if (msm_host->tx_gem_obj) { 11417ad0e8cfSJordan Crouse msm_gem_unpin_iova(msm_host->tx_gem_obj, priv->kms->aspace); 1142f7d33950SEmil Velikov drm_gem_object_put(msm_host->tx_gem_obj); 1143a689554bSHai Li msm_host->tx_gem_obj = NULL; 1144a689554bSHai Li } 11454ff9d4cbSArchit Taneja 11464ff9d4cbSArchit Taneja if (msm_host->tx_buf) 11474ff9d4cbSArchit Taneja dma_free_coherent(dev->dev, msm_host->tx_size, msm_host->tx_buf, 11484ff9d4cbSArchit Taneja msm_host->tx_buf_paddr); 1149a689554bSHai Li } 1150a689554bSHai Li 1151c4d8cfe5SSibi Sankar void *dsi_tx_buf_get_6g(struct msm_dsi_host *msm_host) 1152c4d8cfe5SSibi Sankar { 1153c4d8cfe5SSibi Sankar return msm_gem_get_vaddr(msm_host->tx_gem_obj); 1154c4d8cfe5SSibi Sankar } 1155c4d8cfe5SSibi Sankar 1156c4d8cfe5SSibi Sankar void *dsi_tx_buf_get_v2(struct msm_dsi_host *msm_host) 1157c4d8cfe5SSibi Sankar { 1158c4d8cfe5SSibi Sankar return msm_host->tx_buf; 1159c4d8cfe5SSibi Sankar } 1160c4d8cfe5SSibi Sankar 1161c4d8cfe5SSibi Sankar void dsi_tx_buf_put_6g(struct msm_dsi_host *msm_host) 1162c4d8cfe5SSibi Sankar { 1163c4d8cfe5SSibi Sankar msm_gem_put_vaddr(msm_host->tx_gem_obj); 1164c4d8cfe5SSibi Sankar } 1165c4d8cfe5SSibi Sankar 1166a689554bSHai Li /* 1167a689554bSHai Li * prepare cmd buffer to be txed 1168a689554bSHai Li */ 11694ff9d4cbSArchit Taneja static int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, 1170a689554bSHai Li const struct mipi_dsi_msg *msg) 1171a689554bSHai Li { 11724ff9d4cbSArchit Taneja const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 1173a689554bSHai Li struct mipi_dsi_packet packet; 1174a689554bSHai Li int len; 1175a689554bSHai Li int ret; 1176a689554bSHai Li u8 *data; 1177a689554bSHai Li 1178a689554bSHai Li ret = mipi_dsi_create_packet(&packet, msg); 1179a689554bSHai Li if (ret) { 1180a689554bSHai Li pr_err("%s: create packet failed, %d\n", __func__, ret); 1181a689554bSHai Li return ret; 1182a689554bSHai Li } 1183a689554bSHai Li len = (packet.size + 3) & (~0x3); 1184a689554bSHai Li 11854ff9d4cbSArchit Taneja if (len > msm_host->tx_size) { 1186a689554bSHai Li pr_err("%s: packet size is too big\n", __func__); 1187a689554bSHai Li return -EINVAL; 1188a689554bSHai Li } 1189a689554bSHai Li 11908f7ca540SSibi Sankar data = cfg_hnd->ops->tx_buf_get(msm_host); 1191a689554bSHai Li if (IS_ERR(data)) { 1192a689554bSHai Li ret = PTR_ERR(data); 1193a689554bSHai Li pr_err("%s: get vaddr failed, %d\n", __func__, ret); 1194a689554bSHai Li return ret; 1195a689554bSHai Li } 1196a689554bSHai Li 1197a689554bSHai Li /* MSM specific command format in memory */ 1198a689554bSHai Li data[0] = packet.header[1]; 1199a689554bSHai Li data[1] = packet.header[2]; 1200a689554bSHai Li data[2] = packet.header[0]; 1201a689554bSHai Li data[3] = BIT(7); /* Last packet */ 1202a689554bSHai Li if (mipi_dsi_packet_format_is_long(msg->type)) 1203a689554bSHai Li data[3] |= BIT(6); 1204a689554bSHai Li if (msg->rx_buf && msg->rx_len) 1205a689554bSHai Li data[3] |= BIT(5); 1206a689554bSHai Li 1207a689554bSHai Li /* Long packet */ 1208a689554bSHai Li if (packet.payload && packet.payload_length) 1209a689554bSHai Li memcpy(data + 4, packet.payload, packet.payload_length); 1210a689554bSHai Li 1211a689554bSHai Li /* Append 0xff to the end */ 1212a689554bSHai Li if (packet.size < len) 1213a689554bSHai Li memset(data + packet.size, 0xff, len - packet.size); 1214a689554bSHai Li 12158f7ca540SSibi Sankar if (cfg_hnd->ops->tx_buf_put) 12168f7ca540SSibi Sankar cfg_hnd->ops->tx_buf_put(msm_host); 121718f23049SRob Clark 1218a689554bSHai Li return len; 1219a689554bSHai Li } 1220a689554bSHai Li 1221a689554bSHai Li /* 1222a689554bSHai Li * dsi_short_read1_resp: 1 parameter 1223a689554bSHai Li */ 1224a689554bSHai Li static int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) 1225a689554bSHai Li { 1226a689554bSHai Li u8 *data = msg->rx_buf; 1227a689554bSHai Li if (data && (msg->rx_len >= 1)) { 1228a689554bSHai Li *data = buf[1]; /* strip out dcs type */ 1229a689554bSHai Li return 1; 1230a689554bSHai Li } else { 1231981371f3SStephane Viau pr_err("%s: read data does not match with rx_buf len %zu\n", 1232a689554bSHai Li __func__, msg->rx_len); 1233a689554bSHai Li return -EINVAL; 1234a689554bSHai Li } 1235a689554bSHai Li } 1236a689554bSHai Li 1237a689554bSHai Li /* 1238a689554bSHai Li * dsi_short_read2_resp: 2 parameter 1239a689554bSHai Li */ 1240a689554bSHai Li static int dsi_short_read2_resp(u8 *buf, const struct mipi_dsi_msg *msg) 1241a689554bSHai Li { 1242a689554bSHai Li u8 *data = msg->rx_buf; 1243a689554bSHai Li if (data && (msg->rx_len >= 2)) { 1244a689554bSHai Li data[0] = buf[1]; /* strip out dcs type */ 1245a689554bSHai Li data[1] = buf[2]; 1246a689554bSHai Li return 2; 1247a689554bSHai Li } else { 1248981371f3SStephane Viau pr_err("%s: read data does not match with rx_buf len %zu\n", 1249a689554bSHai Li __func__, msg->rx_len); 1250a689554bSHai Li return -EINVAL; 1251a689554bSHai Li } 1252a689554bSHai Li } 1253a689554bSHai Li 1254a689554bSHai Li static int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg) 1255a689554bSHai Li { 1256a689554bSHai Li /* strip out 4 byte dcs header */ 1257a689554bSHai Li if (msg->rx_buf && msg->rx_len) 1258a689554bSHai Li memcpy(msg->rx_buf, buf + 4, msg->rx_len); 1259a689554bSHai Li 1260a689554bSHai Li return msg->rx_len; 1261a689554bSHai Li } 1262a689554bSHai Li 1263c4d8cfe5SSibi Sankar int dsi_dma_base_get_6g(struct msm_dsi_host *msm_host, uint64_t *dma_base) 1264c4d8cfe5SSibi Sankar { 1265c4d8cfe5SSibi Sankar struct drm_device *dev = msm_host->dev; 1266c4d8cfe5SSibi Sankar struct msm_drm_private *priv = dev->dev_private; 1267c4d8cfe5SSibi Sankar 1268c4d8cfe5SSibi Sankar if (!dma_base) 1269c4d8cfe5SSibi Sankar return -EINVAL; 1270c4d8cfe5SSibi Sankar 12719fe041f6SJordan Crouse return msm_gem_get_and_pin_iova(msm_host->tx_gem_obj, 1272c4d8cfe5SSibi Sankar priv->kms->aspace, dma_base); 1273c4d8cfe5SSibi Sankar } 1274c4d8cfe5SSibi Sankar 1275c4d8cfe5SSibi Sankar int dsi_dma_base_get_v2(struct msm_dsi_host *msm_host, uint64_t *dma_base) 1276c4d8cfe5SSibi Sankar { 1277c4d8cfe5SSibi Sankar if (!dma_base) 1278c4d8cfe5SSibi Sankar return -EINVAL; 1279c4d8cfe5SSibi Sankar 1280c4d8cfe5SSibi Sankar *dma_base = msm_host->tx_buf_paddr; 1281c4d8cfe5SSibi Sankar return 0; 1282c4d8cfe5SSibi Sankar } 1283c4d8cfe5SSibi Sankar 1284a689554bSHai Li static int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) 1285a689554bSHai Li { 12864ff9d4cbSArchit Taneja const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 1287a689554bSHai Li int ret; 128878babc16SRob Clark uint64_t dma_base; 1289a689554bSHai Li bool triggered; 1290a689554bSHai Li 12918f7ca540SSibi Sankar ret = cfg_hnd->ops->dma_base_get(msm_host, &dma_base); 1292a689554bSHai Li if (ret) { 1293a689554bSHai Li pr_err("%s: failed to get iova: %d\n", __func__, ret); 1294a689554bSHai Li return ret; 1295a689554bSHai Li } 1296a689554bSHai Li 1297a689554bSHai Li reinit_completion(&msm_host->dma_comp); 1298a689554bSHai Li 1299a689554bSHai Li dsi_wait4video_eng_busy(msm_host); 1300a689554bSHai Li 1301a689554bSHai Li triggered = msm_dsi_manager_cmd_xfer_trigger( 13024ff9d4cbSArchit Taneja msm_host->id, dma_base, len); 1303a689554bSHai Li if (triggered) { 1304a689554bSHai Li ret = wait_for_completion_timeout(&msm_host->dma_comp, 1305a689554bSHai Li msecs_to_jiffies(200)); 1306a689554bSHai Li DBG("ret=%d", ret); 1307a689554bSHai Li if (ret == 0) 1308a689554bSHai Li ret = -ETIMEDOUT; 1309a689554bSHai Li else 1310a689554bSHai Li ret = len; 1311a689554bSHai Li } else 1312a689554bSHai Li ret = len; 1313a689554bSHai Li 1314a689554bSHai Li return ret; 1315a689554bSHai Li } 1316a689554bSHai Li 1317a689554bSHai Li static int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host, 1318a689554bSHai Li u8 *buf, int rx_byte, int pkt_size) 1319a689554bSHai Li { 13202e3cc607Szhengbin u32 *temp, data; 1321a689554bSHai Li int i, j = 0, cnt; 1322a689554bSHai Li u32 read_cnt; 1323a689554bSHai Li u8 reg[16]; 1324a689554bSHai Li int repeated_bytes = 0; 1325a689554bSHai Li int buf_offset = buf - msm_host->rx_buf; 1326a689554bSHai Li 1327a689554bSHai Li temp = (u32 *)reg; 1328a689554bSHai Li cnt = (rx_byte + 3) >> 2; 1329a689554bSHai Li if (cnt > 4) 1330a689554bSHai Li cnt = 4; /* 4 x 32 bits registers only */ 1331a689554bSHai Li 1332ec1936ebSHai Li if (rx_byte == 4) 1333ec1936ebSHai Li read_cnt = 4; 1334ec1936ebSHai Li else 1335ec1936ebSHai Li read_cnt = pkt_size + 6; 1336a689554bSHai Li 1337a689554bSHai Li /* 1338a689554bSHai Li * In case of multiple reads from the panel, after the first read, there 1339a689554bSHai Li * is possibility that there are some bytes in the payload repeating in 1340a689554bSHai Li * the RDBK_DATA registers. Since we read all the parameters from the 1341a689554bSHai Li * panel right from the first byte for every pass. We need to skip the 1342a689554bSHai Li * repeating bytes and then append the new parameters to the rx buffer. 1343a689554bSHai Li */ 1344a689554bSHai Li if (read_cnt > 16) { 1345a689554bSHai Li int bytes_shifted; 1346a689554bSHai Li /* Any data more than 16 bytes will be shifted out. 1347a689554bSHai Li * The temp read buffer should already contain these bytes. 1348a689554bSHai Li * The remaining bytes in read buffer are the repeated bytes. 1349a689554bSHai Li */ 1350a689554bSHai Li bytes_shifted = read_cnt - 16; 1351a689554bSHai Li repeated_bytes = buf_offset - bytes_shifted; 1352a689554bSHai Li } 1353a689554bSHai Li 1354a689554bSHai Li for (i = cnt - 1; i >= 0; i--) { 1355a689554bSHai Li data = dsi_read(msm_host, REG_DSI_RDBK_DATA(i)); 1356a689554bSHai Li *temp++ = ntohl(data); /* to host byte order */ 1357a689554bSHai Li DBG("data = 0x%x and ntohl(data) = 0x%x", data, ntohl(data)); 1358a689554bSHai Li } 1359a689554bSHai Li 1360a689554bSHai Li for (i = repeated_bytes; i < 16; i++) 1361a689554bSHai Li buf[j++] = reg[i]; 1362a689554bSHai Li 1363a689554bSHai Li return j; 1364a689554bSHai Li } 1365a689554bSHai Li 1366a689554bSHai Li static int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, 1367a689554bSHai Li const struct mipi_dsi_msg *msg) 1368a689554bSHai Li { 1369a689554bSHai Li int len, ret; 1370a689554bSHai Li int bllp_len = msm_host->mode->hdisplay * 1371a689554bSHai Li dsi_get_bpp(msm_host->format) / 8; 1372a689554bSHai Li 13734ff9d4cbSArchit Taneja len = dsi_cmd_dma_add(msm_host, msg); 1374a689554bSHai Li if (!len) { 1375a689554bSHai Li pr_err("%s: failed to add cmd type = 0x%x\n", 1376a689554bSHai Li __func__, msg->type); 1377a689554bSHai Li return -EINVAL; 1378a689554bSHai Li } 1379a689554bSHai Li 1380a689554bSHai Li /* for video mode, do not send cmds more than 1381a689554bSHai Li * one pixel line, since it only transmit it 1382a689554bSHai Li * during BLLP. 1383a689554bSHai Li */ 1384a689554bSHai Li /* TODO: if the command is sent in LP mode, the bit rate is only 1385a689554bSHai Li * half of esc clk rate. In this case, if the video is already 1386a689554bSHai Li * actively streaming, we need to check more carefully if the 1387a689554bSHai Li * command can be fit into one BLLP. 1388a689554bSHai Li */ 1389a689554bSHai Li if ((msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) && (len > bllp_len)) { 1390a689554bSHai Li pr_err("%s: cmd cannot fit into BLLP period, len=%d\n", 1391a689554bSHai Li __func__, len); 1392a689554bSHai Li return -EINVAL; 1393a689554bSHai Li } 1394a689554bSHai Li 1395a689554bSHai Li ret = dsi_cmd_dma_tx(msm_host, len); 1396a689554bSHai Li if (ret < len) { 1397a689554bSHai Li pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d\n", 1398a689554bSHai Li __func__, msg->type, (*(u8 *)(msg->tx_buf)), len); 1399a689554bSHai Li return -ECOMM; 1400a689554bSHai Li } 1401a689554bSHai Li 1402a689554bSHai Li return len; 1403a689554bSHai Li } 1404a689554bSHai Li 1405a689554bSHai Li static void dsi_sw_reset_restore(struct msm_dsi_host *msm_host) 1406a689554bSHai Li { 1407a689554bSHai Li u32 data0, data1; 1408a689554bSHai Li 1409a689554bSHai Li data0 = dsi_read(msm_host, REG_DSI_CTRL); 1410a689554bSHai Li data1 = data0; 1411a689554bSHai Li data1 &= ~DSI_CTRL_ENABLE; 1412a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, data1); 1413a689554bSHai Li /* 1414a689554bSHai Li * dsi controller need to be disabled before 1415a689554bSHai Li * clocks turned on 1416a689554bSHai Li */ 1417a689554bSHai Li wmb(); 1418a689554bSHai Li 1419a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 1420a689554bSHai Li wmb(); /* make sure clocks enabled */ 1421a689554bSHai Li 1422a689554bSHai Li /* dsi controller can only be reset while clocks are running */ 1423a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 1); 142478e31c42SJeffrey Hugo msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ 1425a689554bSHai Li dsi_write(msm_host, REG_DSI_RESET, 0); 1426a689554bSHai Li wmb(); /* controller out of reset */ 1427a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, data0); 1428a689554bSHai Li wmb(); /* make sure dsi controller enabled again */ 1429a689554bSHai Li } 1430a689554bSHai Li 14318d23ea40SArchit Taneja static void dsi_hpd_worker(struct work_struct *work) 14328d23ea40SArchit Taneja { 14338d23ea40SArchit Taneja struct msm_dsi_host *msm_host = 14348d23ea40SArchit Taneja container_of(work, struct msm_dsi_host, hpd_work); 14358d23ea40SArchit Taneja 14368d23ea40SArchit Taneja drm_helper_hpd_irq_event(msm_host->dev); 14378d23ea40SArchit Taneja } 14388d23ea40SArchit Taneja 1439a689554bSHai Li static void dsi_err_worker(struct work_struct *work) 1440a689554bSHai Li { 1441a689554bSHai Li struct msm_dsi_host *msm_host = 1442a689554bSHai Li container_of(work, struct msm_dsi_host, err_work); 1443a689554bSHai Li u32 status = msm_host->err_work_state; 1444a689554bSHai Li 1445ff431fa4SRob Clark pr_err_ratelimited("%s: status=%x\n", __func__, status); 1446a689554bSHai Li if (status & DSI_ERR_STATE_MDP_FIFO_UNDERFLOW) 1447a689554bSHai Li dsi_sw_reset_restore(msm_host); 1448a689554bSHai Li 1449a689554bSHai Li /* It is safe to clear here because error irq is disabled. */ 1450a689554bSHai Li msm_host->err_work_state = 0; 1451a689554bSHai Li 1452a689554bSHai Li /* enable dsi error interrupt */ 1453a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 1); 1454a689554bSHai Li } 1455a689554bSHai Li 1456a689554bSHai Li static void dsi_ack_err_status(struct msm_dsi_host *msm_host) 1457a689554bSHai Li { 1458a689554bSHai Li u32 status; 1459a689554bSHai Li 1460a689554bSHai Li status = dsi_read(msm_host, REG_DSI_ACK_ERR_STATUS); 1461a689554bSHai Li 1462a689554bSHai Li if (status) { 1463a689554bSHai Li dsi_write(msm_host, REG_DSI_ACK_ERR_STATUS, status); 1464a689554bSHai Li /* Writing of an extra 0 needed to clear error bits */ 1465a689554bSHai Li dsi_write(msm_host, REG_DSI_ACK_ERR_STATUS, 0); 1466a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_ACK; 1467a689554bSHai Li } 1468a689554bSHai Li } 1469a689554bSHai Li 1470a689554bSHai Li static void dsi_timeout_status(struct msm_dsi_host *msm_host) 1471a689554bSHai Li { 1472a689554bSHai Li u32 status; 1473a689554bSHai Li 1474a689554bSHai Li status = dsi_read(msm_host, REG_DSI_TIMEOUT_STATUS); 1475a689554bSHai Li 1476a689554bSHai Li if (status) { 1477a689554bSHai Li dsi_write(msm_host, REG_DSI_TIMEOUT_STATUS, status); 1478a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_TIMEOUT; 1479a689554bSHai Li } 1480a689554bSHai Li } 1481a689554bSHai Li 1482a689554bSHai Li static void dsi_dln0_phy_err(struct msm_dsi_host *msm_host) 1483a689554bSHai Li { 1484a689554bSHai Li u32 status; 1485a689554bSHai Li 1486a689554bSHai Li status = dsi_read(msm_host, REG_DSI_DLN0_PHY_ERR); 1487a689554bSHai Li 148801199361SArchit Taneja if (status & (DSI_DLN0_PHY_ERR_DLN0_ERR_ESC | 148901199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC | 149001199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL | 149101199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 | 149201199361SArchit Taneja DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1)) { 1493a689554bSHai Li dsi_write(msm_host, REG_DSI_DLN0_PHY_ERR, status); 1494a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_DLN0_PHY; 1495a689554bSHai Li } 1496a689554bSHai Li } 1497a689554bSHai Li 1498a689554bSHai Li static void dsi_fifo_status(struct msm_dsi_host *msm_host) 1499a689554bSHai Li { 1500a689554bSHai Li u32 status; 1501a689554bSHai Li 1502a689554bSHai Li status = dsi_read(msm_host, REG_DSI_FIFO_STATUS); 1503a689554bSHai Li 1504a689554bSHai Li /* fifo underflow, overflow */ 1505a689554bSHai Li if (status) { 1506a689554bSHai Li dsi_write(msm_host, REG_DSI_FIFO_STATUS, status); 1507a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_FIFO; 1508a689554bSHai Li if (status & DSI_FIFO_STATUS_CMD_MDP_FIFO_UNDERFLOW) 1509a689554bSHai Li msm_host->err_work_state |= 1510a689554bSHai Li DSI_ERR_STATE_MDP_FIFO_UNDERFLOW; 1511a689554bSHai Li } 1512a689554bSHai Li } 1513a689554bSHai Li 1514a689554bSHai Li static void dsi_status(struct msm_dsi_host *msm_host) 1515a689554bSHai Li { 1516a689554bSHai Li u32 status; 1517a689554bSHai Li 1518a689554bSHai Li status = dsi_read(msm_host, REG_DSI_STATUS0); 1519a689554bSHai Li 1520a689554bSHai Li if (status & DSI_STATUS0_INTERLEAVE_OP_CONTENTION) { 1521a689554bSHai Li dsi_write(msm_host, REG_DSI_STATUS0, status); 1522a689554bSHai Li msm_host->err_work_state |= 1523a689554bSHai Li DSI_ERR_STATE_INTERLEAVE_OP_CONTENTION; 1524a689554bSHai Li } 1525a689554bSHai Li } 1526a689554bSHai Li 1527a689554bSHai Li static void dsi_clk_status(struct msm_dsi_host *msm_host) 1528a689554bSHai Li { 1529a689554bSHai Li u32 status; 1530a689554bSHai Li 1531a689554bSHai Li status = dsi_read(msm_host, REG_DSI_CLK_STATUS); 1532a689554bSHai Li 1533a689554bSHai Li if (status & DSI_CLK_STATUS_PLL_UNLOCKED) { 1534a689554bSHai Li dsi_write(msm_host, REG_DSI_CLK_STATUS, status); 1535a689554bSHai Li msm_host->err_work_state |= DSI_ERR_STATE_PLL_UNLOCKED; 1536a689554bSHai Li } 1537a689554bSHai Li } 1538a689554bSHai Li 1539a689554bSHai Li static void dsi_error(struct msm_dsi_host *msm_host) 1540a689554bSHai Li { 1541a689554bSHai Li /* disable dsi error interrupt */ 1542a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 0); 1543a689554bSHai Li 1544a689554bSHai Li dsi_clk_status(msm_host); 1545a689554bSHai Li dsi_fifo_status(msm_host); 1546a689554bSHai Li dsi_ack_err_status(msm_host); 1547a689554bSHai Li dsi_timeout_status(msm_host); 1548a689554bSHai Li dsi_status(msm_host); 1549a689554bSHai Li dsi_dln0_phy_err(msm_host); 1550a689554bSHai Li 1551a689554bSHai Li queue_work(msm_host->workqueue, &msm_host->err_work); 1552a689554bSHai Li } 1553a689554bSHai Li 1554a689554bSHai Li static irqreturn_t dsi_host_irq(int irq, void *ptr) 1555a689554bSHai Li { 1556a689554bSHai Li struct msm_dsi_host *msm_host = ptr; 1557a689554bSHai Li u32 isr; 1558a689554bSHai Li unsigned long flags; 1559a689554bSHai Li 1560a689554bSHai Li if (!msm_host->ctrl_base) 1561a689554bSHai Li return IRQ_HANDLED; 1562a689554bSHai Li 1563a689554bSHai Li spin_lock_irqsave(&msm_host->intr_lock, flags); 1564a689554bSHai Li isr = dsi_read(msm_host, REG_DSI_INTR_CTRL); 1565a689554bSHai Li dsi_write(msm_host, REG_DSI_INTR_CTRL, isr); 1566a689554bSHai Li spin_unlock_irqrestore(&msm_host->intr_lock, flags); 1567a689554bSHai Li 1568a689554bSHai Li DBG("isr=0x%x, id=%d", isr, msm_host->id); 1569a689554bSHai Li 1570a689554bSHai Li if (isr & DSI_IRQ_ERROR) 1571a689554bSHai Li dsi_error(msm_host); 1572a689554bSHai Li 1573a689554bSHai Li if (isr & DSI_IRQ_VIDEO_DONE) 1574a689554bSHai Li complete(&msm_host->video_comp); 1575a689554bSHai Li 1576a689554bSHai Li if (isr & DSI_IRQ_CMD_DMA_DONE) 1577a689554bSHai Li complete(&msm_host->dma_comp); 1578a689554bSHai Li 1579a689554bSHai Li return IRQ_HANDLED; 1580a689554bSHai Li } 1581a689554bSHai Li 1582a689554bSHai Li static int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host, 1583a689554bSHai Li struct device *panel_device) 1584a689554bSHai Li { 15859590e69dSUwe Kleine-König msm_host->disp_en_gpio = devm_gpiod_get_optional(panel_device, 15869590e69dSUwe Kleine-König "disp-enable", 15879590e69dSUwe Kleine-König GPIOD_OUT_LOW); 1588a689554bSHai Li if (IS_ERR(msm_host->disp_en_gpio)) { 1589a689554bSHai Li DBG("cannot get disp-enable-gpios %ld", 1590a689554bSHai Li PTR_ERR(msm_host->disp_en_gpio)); 15919590e69dSUwe Kleine-König return PTR_ERR(msm_host->disp_en_gpio); 1592a689554bSHai Li } 1593a689554bSHai Li 159460d05cb4SArchit Taneja msm_host->te_gpio = devm_gpiod_get_optional(panel_device, "disp-te", 159560d05cb4SArchit Taneja GPIOD_IN); 1596a689554bSHai Li if (IS_ERR(msm_host->te_gpio)) { 1597a689554bSHai Li DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio)); 15989590e69dSUwe Kleine-König return PTR_ERR(msm_host->te_gpio); 1599a689554bSHai Li } 1600a689554bSHai Li 1601a689554bSHai Li return 0; 1602a689554bSHai Li } 1603a689554bSHai Li 1604a689554bSHai Li static int dsi_host_attach(struct mipi_dsi_host *host, 1605a689554bSHai Li struct mipi_dsi_device *dsi) 1606a689554bSHai Li { 1607a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1608a689554bSHai Li int ret; 1609a689554bSHai Li 161026f7d1f4SArchit Taneja if (dsi->lanes > msm_host->num_data_lanes) 161126f7d1f4SArchit Taneja return -EINVAL; 161226f7d1f4SArchit Taneja 1613a689554bSHai Li msm_host->channel = dsi->channel; 1614a689554bSHai Li msm_host->lanes = dsi->lanes; 1615a689554bSHai Li msm_host->format = dsi->format; 1616a689554bSHai Li msm_host->mode_flags = dsi->mode_flags; 1617a689554bSHai Li 1618a689554bSHai Li /* Some gpios defined in panel DT need to be controlled by host */ 1619a689554bSHai Li ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); 1620a689554bSHai Li if (ret) 1621a689554bSHai Li return ret; 1622a689554bSHai Li 1623a689554bSHai Li DBG("id=%d", msm_host->id); 1624a689554bSHai Li if (msm_host->dev) 16258d23ea40SArchit Taneja queue_work(msm_host->workqueue, &msm_host->hpd_work); 1626a689554bSHai Li 1627a689554bSHai Li return 0; 1628a689554bSHai Li } 1629a689554bSHai Li 1630a689554bSHai Li static int dsi_host_detach(struct mipi_dsi_host *host, 1631a689554bSHai Li struct mipi_dsi_device *dsi) 1632a689554bSHai Li { 1633a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1634a689554bSHai Li 1635a9ddac9cSArchit Taneja msm_host->device_node = NULL; 1636a689554bSHai Li 1637a689554bSHai Li DBG("id=%d", msm_host->id); 1638a689554bSHai Li if (msm_host->dev) 16398d23ea40SArchit Taneja queue_work(msm_host->workqueue, &msm_host->hpd_work); 1640a689554bSHai Li 1641a689554bSHai Li return 0; 1642a689554bSHai Li } 1643a689554bSHai Li 1644a689554bSHai Li static ssize_t dsi_host_transfer(struct mipi_dsi_host *host, 1645a689554bSHai Li const struct mipi_dsi_msg *msg) 1646a689554bSHai Li { 1647a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1648a689554bSHai Li int ret; 1649a689554bSHai Li 1650a689554bSHai Li if (!msg || !msm_host->power_on) 1651a689554bSHai Li return -EINVAL; 1652a689554bSHai Li 1653a689554bSHai Li mutex_lock(&msm_host->cmd_mutex); 1654a689554bSHai Li ret = msm_dsi_manager_cmd_xfer(msm_host->id, msg); 1655a689554bSHai Li mutex_unlock(&msm_host->cmd_mutex); 1656a689554bSHai Li 1657a689554bSHai Li return ret; 1658a689554bSHai Li } 1659a689554bSHai Li 16608b6947a8SRikard Falkeborn static const struct mipi_dsi_host_ops dsi_host_ops = { 1661a689554bSHai Li .attach = dsi_host_attach, 1662a689554bSHai Li .detach = dsi_host_detach, 1663a689554bSHai Li .transfer = dsi_host_transfer, 1664a689554bSHai Li }; 1665a689554bSHai Li 166626f7d1f4SArchit Taneja /* 166726f7d1f4SArchit Taneja * List of supported physical to logical lane mappings. 166826f7d1f4SArchit Taneja * For example, the 2nd entry represents the following mapping: 166926f7d1f4SArchit Taneja * 167026f7d1f4SArchit Taneja * "3012": Logic 3->Phys 0; Logic 0->Phys 1; Logic 1->Phys 2; Logic 2->Phys 3; 167126f7d1f4SArchit Taneja */ 167226f7d1f4SArchit Taneja static const int supported_data_lane_swaps[][4] = { 167326f7d1f4SArchit Taneja { 0, 1, 2, 3 }, 167426f7d1f4SArchit Taneja { 3, 0, 1, 2 }, 167526f7d1f4SArchit Taneja { 2, 3, 0, 1 }, 167626f7d1f4SArchit Taneja { 1, 2, 3, 0 }, 167726f7d1f4SArchit Taneja { 0, 3, 2, 1 }, 167826f7d1f4SArchit Taneja { 1, 0, 3, 2 }, 167926f7d1f4SArchit Taneja { 2, 1, 0, 3 }, 168026f7d1f4SArchit Taneja { 3, 2, 1, 0 }, 168126f7d1f4SArchit Taneja }; 168226f7d1f4SArchit Taneja 168326f7d1f4SArchit Taneja static int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, 168426f7d1f4SArchit Taneja struct device_node *ep) 168526f7d1f4SArchit Taneja { 168626f7d1f4SArchit Taneja struct device *dev = &msm_host->pdev->dev; 168726f7d1f4SArchit Taneja struct property *prop; 168826f7d1f4SArchit Taneja u32 lane_map[4]; 168926f7d1f4SArchit Taneja int ret, i, len, num_lanes; 169026f7d1f4SArchit Taneja 169160282ceaSArchit Taneja prop = of_find_property(ep, "data-lanes", &len); 169226f7d1f4SArchit Taneja if (!prop) { 16936a41da17SMamta Shukla DRM_DEV_DEBUG(dev, 1694a1b1a4f7SArchit Taneja "failed to find data lane mapping, using default\n"); 1695a1b1a4f7SArchit Taneja return 0; 169626f7d1f4SArchit Taneja } 169726f7d1f4SArchit Taneja 169826f7d1f4SArchit Taneja num_lanes = len / sizeof(u32); 169926f7d1f4SArchit Taneja 170026f7d1f4SArchit Taneja if (num_lanes < 1 || num_lanes > 4) { 17016a41da17SMamta Shukla DRM_DEV_ERROR(dev, "bad number of data lanes\n"); 170226f7d1f4SArchit Taneja return -EINVAL; 170326f7d1f4SArchit Taneja } 170426f7d1f4SArchit Taneja 170526f7d1f4SArchit Taneja msm_host->num_data_lanes = num_lanes; 170626f7d1f4SArchit Taneja 170760282ceaSArchit Taneja ret = of_property_read_u32_array(ep, "data-lanes", lane_map, 170826f7d1f4SArchit Taneja num_lanes); 170926f7d1f4SArchit Taneja if (ret) { 17106a41da17SMamta Shukla DRM_DEV_ERROR(dev, "failed to read lane data\n"); 171126f7d1f4SArchit Taneja return ret; 171226f7d1f4SArchit Taneja } 171326f7d1f4SArchit Taneja 171426f7d1f4SArchit Taneja /* 171526f7d1f4SArchit Taneja * compare DT specified physical-logical lane mappings with the ones 171626f7d1f4SArchit Taneja * supported by hardware 171726f7d1f4SArchit Taneja */ 171826f7d1f4SArchit Taneja for (i = 0; i < ARRAY_SIZE(supported_data_lane_swaps); i++) { 171926f7d1f4SArchit Taneja const int *swap = supported_data_lane_swaps[i]; 172026f7d1f4SArchit Taneja int j; 172126f7d1f4SArchit Taneja 172260282ceaSArchit Taneja /* 172360282ceaSArchit Taneja * the data-lanes array we get from DT has a logical->physical 172460282ceaSArchit Taneja * mapping. The "data lane swap" register field represents 172560282ceaSArchit Taneja * supported configurations in a physical->logical mapping. 172660282ceaSArchit Taneja * Translate the DT mapping to what we understand and find a 172760282ceaSArchit Taneja * configuration that works. 172860282ceaSArchit Taneja */ 172926f7d1f4SArchit Taneja for (j = 0; j < num_lanes; j++) { 173060282ceaSArchit Taneja if (lane_map[j] < 0 || lane_map[j] > 3) 17316a41da17SMamta Shukla DRM_DEV_ERROR(dev, "bad physical lane entry %u\n", 173260282ceaSArchit Taneja lane_map[j]); 173360282ceaSArchit Taneja 173460282ceaSArchit Taneja if (swap[lane_map[j]] != j) 173526f7d1f4SArchit Taneja break; 173626f7d1f4SArchit Taneja } 173726f7d1f4SArchit Taneja 173826f7d1f4SArchit Taneja if (j == num_lanes) { 173926f7d1f4SArchit Taneja msm_host->dlane_swap = i; 174026f7d1f4SArchit Taneja return 0; 174126f7d1f4SArchit Taneja } 174226f7d1f4SArchit Taneja } 174326f7d1f4SArchit Taneja 174426f7d1f4SArchit Taneja return -EINVAL; 174526f7d1f4SArchit Taneja } 174626f7d1f4SArchit Taneja 1747f7009d26SArchit Taneja static int dsi_host_parse_dt(struct msm_dsi_host *msm_host) 1748f7009d26SArchit Taneja { 1749f7009d26SArchit Taneja struct device *dev = &msm_host->pdev->dev; 1750f7009d26SArchit Taneja struct device_node *np = dev->of_node; 1751a9ddac9cSArchit Taneja struct device_node *endpoint, *device_node; 1752a1b1a4f7SArchit Taneja int ret = 0; 1753f7009d26SArchit Taneja 1754f7009d26SArchit Taneja /* 1755b9ac76f6SArchit Taneja * Get the endpoint of the output port of the DSI host. In our case, 1756b9ac76f6SArchit Taneja * this is mapped to port number with reg = 1. Don't return an error if 1757b9ac76f6SArchit Taneja * the remote endpoint isn't defined. It's possible that there is 1758b9ac76f6SArchit Taneja * nothing connected to the dsi output. 1759f7009d26SArchit Taneja */ 1760b9ac76f6SArchit Taneja endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); 1761f7009d26SArchit Taneja if (!endpoint) { 17626a41da17SMamta Shukla DRM_DEV_DEBUG(dev, "%s: no endpoint\n", __func__); 1763f7009d26SArchit Taneja return 0; 1764f7009d26SArchit Taneja } 1765f7009d26SArchit Taneja 176626f7d1f4SArchit Taneja ret = dsi_host_parse_lane_data(msm_host, endpoint); 176726f7d1f4SArchit Taneja if (ret) { 17686a41da17SMamta Shukla DRM_DEV_ERROR(dev, "%s: invalid lane configuration %d\n", 176926f7d1f4SArchit Taneja __func__, ret); 1770feb085ecSSean Paul ret = -EINVAL; 177126f7d1f4SArchit Taneja goto err; 177226f7d1f4SArchit Taneja } 177326f7d1f4SArchit Taneja 1774f7009d26SArchit Taneja /* Get panel node from the output port's endpoint data */ 177586418f90SRob Herring device_node = of_graph_get_remote_node(np, 1, 0); 1776a9ddac9cSArchit Taneja if (!device_node) { 17776a41da17SMamta Shukla DRM_DEV_DEBUG(dev, "%s: no valid device\n", __func__); 1778feb085ecSSean Paul ret = -ENODEV; 177926f7d1f4SArchit Taneja goto err; 1780f7009d26SArchit Taneja } 1781f7009d26SArchit Taneja 1782a9ddac9cSArchit Taneja msm_host->device_node = device_node; 1783f7009d26SArchit Taneja 17840c7df47fSArchit Taneja if (of_property_read_bool(np, "syscon-sfpb")) { 17850c7df47fSArchit Taneja msm_host->sfpb = syscon_regmap_lookup_by_phandle(np, 17860c7df47fSArchit Taneja "syscon-sfpb"); 17870c7df47fSArchit Taneja if (IS_ERR(msm_host->sfpb)) { 17886a41da17SMamta Shukla DRM_DEV_ERROR(dev, "%s: failed to get sfpb regmap\n", 17890c7df47fSArchit Taneja __func__); 179026f7d1f4SArchit Taneja ret = PTR_ERR(msm_host->sfpb); 17910c7df47fSArchit Taneja } 17920c7df47fSArchit Taneja } 17930c7df47fSArchit Taneja 179426f7d1f4SArchit Taneja of_node_put(device_node); 179526f7d1f4SArchit Taneja 179626f7d1f4SArchit Taneja err: 179726f7d1f4SArchit Taneja of_node_put(endpoint); 179826f7d1f4SArchit Taneja 179926f7d1f4SArchit Taneja return ret; 1800f7009d26SArchit Taneja } 1801f7009d26SArchit Taneja 180232280d66SArchit Taneja static int dsi_host_get_id(struct msm_dsi_host *msm_host) 180332280d66SArchit Taneja { 180432280d66SArchit Taneja struct platform_device *pdev = msm_host->pdev; 180532280d66SArchit Taneja const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 180632280d66SArchit Taneja struct resource *res; 180732280d66SArchit Taneja int i; 180832280d66SArchit Taneja 180932280d66SArchit Taneja res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_ctrl"); 181032280d66SArchit Taneja if (!res) 181132280d66SArchit Taneja return -EINVAL; 181232280d66SArchit Taneja 181332280d66SArchit Taneja for (i = 0; i < cfg->num_dsi; i++) { 181432280d66SArchit Taneja if (cfg->io_start[i] == res->start) 181532280d66SArchit Taneja return i; 181632280d66SArchit Taneja } 181732280d66SArchit Taneja 181832280d66SArchit Taneja return -EINVAL; 181932280d66SArchit Taneja } 182032280d66SArchit Taneja 1821a689554bSHai Li int msm_dsi_host_init(struct msm_dsi *msm_dsi) 1822a689554bSHai Li { 1823a689554bSHai Li struct msm_dsi_host *msm_host = NULL; 1824a689554bSHai Li struct platform_device *pdev = msm_dsi->pdev; 1825a689554bSHai Li int ret; 1826a689554bSHai Li 1827a689554bSHai Li msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); 1828a689554bSHai Li if (!msm_host) { 1829a689554bSHai Li ret = -ENOMEM; 1830a689554bSHai Li goto fail; 1831a689554bSHai Li } 1832a689554bSHai Li 1833f7009d26SArchit Taneja msm_host->pdev = pdev; 1834f54ca1a0SArchit Taneja msm_dsi->host = &msm_host->base; 1835f7009d26SArchit Taneja 1836f7009d26SArchit Taneja ret = dsi_host_parse_dt(msm_host); 1837a689554bSHai Li if (ret) { 1838f7009d26SArchit Taneja pr_err("%s: failed to parse dt\n", __func__); 1839a689554bSHai Li goto fail; 1840a689554bSHai Li } 1841a689554bSHai Li 1842a689554bSHai Li msm_host->ctrl_base = msm_ioremap(pdev, "dsi_ctrl", "DSI CTRL"); 1843a689554bSHai Li if (IS_ERR(msm_host->ctrl_base)) { 1844a689554bSHai Li pr_err("%s: unable to map Dsi ctrl base\n", __func__); 1845a689554bSHai Li ret = PTR_ERR(msm_host->ctrl_base); 1846a689554bSHai Li goto fail; 1847a689554bSHai Li } 1848a689554bSHai Li 1849f6be1121SArchit Taneja pm_runtime_enable(&pdev->dev); 1850f6be1121SArchit Taneja 1851d248b61fSHai Li msm_host->cfg_hnd = dsi_get_config(msm_host); 1852d248b61fSHai Li if (!msm_host->cfg_hnd) { 1853a689554bSHai Li ret = -EINVAL; 1854a689554bSHai Li pr_err("%s: get config failed\n", __func__); 1855a689554bSHai Li goto fail; 1856a689554bSHai Li } 1857a689554bSHai Li 185832280d66SArchit Taneja msm_host->id = dsi_host_get_id(msm_host); 185932280d66SArchit Taneja if (msm_host->id < 0) { 186032280d66SArchit Taneja ret = msm_host->id; 186132280d66SArchit Taneja pr_err("%s: unable to identify DSI host index\n", __func__); 186232280d66SArchit Taneja goto fail; 186332280d66SArchit Taneja } 186432280d66SArchit Taneja 1865d248b61fSHai Li /* fixup base address by io offset */ 1866d248b61fSHai Li msm_host->ctrl_base += msm_host->cfg_hnd->cfg->io_offset; 1867d248b61fSHai Li 1868a689554bSHai Li ret = dsi_regulator_init(msm_host); 1869a689554bSHai Li if (ret) { 1870a689554bSHai Li pr_err("%s: regulator init failed\n", __func__); 1871a689554bSHai Li goto fail; 1872a689554bSHai Li } 1873a689554bSHai Li 187431c92767SArchit Taneja ret = dsi_clk_init(msm_host); 187531c92767SArchit Taneja if (ret) { 187631c92767SArchit Taneja pr_err("%s: unable to initialize dsi clks\n", __func__); 187731c92767SArchit Taneja goto fail; 187831c92767SArchit Taneja } 187931c92767SArchit Taneja 1880a689554bSHai Li msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); 1881a689554bSHai Li if (!msm_host->rx_buf) { 1882cd57b48aSWei Yongjun ret = -ENOMEM; 1883a689554bSHai Li pr_err("%s: alloc rx temp buf failed\n", __func__); 1884a689554bSHai Li goto fail; 1885a689554bSHai Li } 1886a689554bSHai Li 188732d3e0feSRajendra Nayak msm_host->opp_table = dev_pm_opp_set_clkname(&pdev->dev, "byte"); 188832d3e0feSRajendra Nayak if (IS_ERR(msm_host->opp_table)) 188932d3e0feSRajendra Nayak return PTR_ERR(msm_host->opp_table); 189032d3e0feSRajendra Nayak /* OPP table is optional */ 189132d3e0feSRajendra Nayak ret = dev_pm_opp_of_add_table(&pdev->dev); 18926400a8e8SViresh Kumar if (ret && ret != -ENODEV) { 189332d3e0feSRajendra Nayak dev_err(&pdev->dev, "invalid OPP table in device tree\n"); 189432d3e0feSRajendra Nayak dev_pm_opp_put_clkname(msm_host->opp_table); 189532d3e0feSRajendra Nayak return ret; 189632d3e0feSRajendra Nayak } 189732d3e0feSRajendra Nayak 1898a689554bSHai Li init_completion(&msm_host->dma_comp); 1899a689554bSHai Li init_completion(&msm_host->video_comp); 1900a689554bSHai Li mutex_init(&msm_host->dev_mutex); 1901a689554bSHai Li mutex_init(&msm_host->cmd_mutex); 1902a689554bSHai Li spin_lock_init(&msm_host->intr_lock); 1903a689554bSHai Li 1904a689554bSHai Li /* setup workqueue */ 1905a689554bSHai Li msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); 1906a689554bSHai Li INIT_WORK(&msm_host->err_work, dsi_err_worker); 19078d23ea40SArchit Taneja INIT_WORK(&msm_host->hpd_work, dsi_hpd_worker); 1908a689554bSHai Li 1909a689554bSHai Li msm_dsi->id = msm_host->id; 1910a689554bSHai Li 1911a689554bSHai Li DBG("Dsi Host %d initialized", msm_host->id); 1912a689554bSHai Li return 0; 1913a689554bSHai Li 1914a689554bSHai Li fail: 1915a689554bSHai Li return ret; 1916a689554bSHai Li } 1917a689554bSHai Li 1918a689554bSHai Li void msm_dsi_host_destroy(struct mipi_dsi_host *host) 1919a689554bSHai Li { 1920a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1921a689554bSHai Li 1922a689554bSHai Li DBG(""); 1923a689554bSHai Li dsi_tx_buf_free(msm_host); 1924a689554bSHai Li if (msm_host->workqueue) { 1925a689554bSHai Li flush_workqueue(msm_host->workqueue); 1926a689554bSHai Li destroy_workqueue(msm_host->workqueue); 1927a689554bSHai Li msm_host->workqueue = NULL; 1928a689554bSHai Li } 1929a689554bSHai Li 1930a689554bSHai Li mutex_destroy(&msm_host->cmd_mutex); 1931a689554bSHai Li mutex_destroy(&msm_host->dev_mutex); 1932f6be1121SArchit Taneja 193332d3e0feSRajendra Nayak dev_pm_opp_of_remove_table(&msm_host->pdev->dev); 193432d3e0feSRajendra Nayak dev_pm_opp_put_clkname(msm_host->opp_table); 1935f6be1121SArchit Taneja pm_runtime_disable(&msm_host->pdev->dev); 1936a689554bSHai Li } 1937a689554bSHai Li 1938a689554bSHai Li int msm_dsi_host_modeset_init(struct mipi_dsi_host *host, 1939a689554bSHai Li struct drm_device *dev) 1940a689554bSHai Li { 1941a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 19428f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 1943a689554bSHai Li struct platform_device *pdev = msm_host->pdev; 1944a689554bSHai Li int ret; 1945a689554bSHai Li 1946a689554bSHai Li msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 1947a689554bSHai Li if (msm_host->irq < 0) { 1948a689554bSHai Li ret = msm_host->irq; 19496a41da17SMamta Shukla DRM_DEV_ERROR(dev->dev, "failed to get irq: %d\n", ret); 1950a689554bSHai Li return ret; 1951a689554bSHai Li } 1952a689554bSHai Li 1953a689554bSHai Li ret = devm_request_irq(&pdev->dev, msm_host->irq, 1954a689554bSHai Li dsi_host_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 1955a689554bSHai Li "dsi_isr", msm_host); 1956a689554bSHai Li if (ret < 0) { 19576a41da17SMamta Shukla DRM_DEV_ERROR(&pdev->dev, "failed to request IRQ%u: %d\n", 1958a689554bSHai Li msm_host->irq, ret); 1959a689554bSHai Li return ret; 1960a689554bSHai Li } 1961a689554bSHai Li 1962a689554bSHai Li msm_host->dev = dev; 19638f7ca540SSibi Sankar ret = cfg_hnd->ops->tx_buf_alloc(msm_host, SZ_4K); 1964a689554bSHai Li if (ret) { 1965a689554bSHai Li pr_err("%s: alloc tx gem obj failed, %d\n", __func__, ret); 1966a689554bSHai Li return ret; 1967a689554bSHai Li } 1968a689554bSHai Li 1969a689554bSHai Li return 0; 1970a689554bSHai Li } 1971a689554bSHai Li 1972a689554bSHai Li int msm_dsi_host_register(struct mipi_dsi_host *host, bool check_defer) 1973a689554bSHai Li { 1974a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 1975a689554bSHai Li int ret; 1976a689554bSHai Li 1977a689554bSHai Li /* Register mipi dsi host */ 1978a689554bSHai Li if (!msm_host->registered) { 1979a689554bSHai Li host->dev = &msm_host->pdev->dev; 1980a689554bSHai Li host->ops = &dsi_host_ops; 1981a689554bSHai Li ret = mipi_dsi_host_register(host); 1982a689554bSHai Li if (ret) 1983a689554bSHai Li return ret; 1984a689554bSHai Li 1985a689554bSHai Li msm_host->registered = true; 1986a689554bSHai Li 1987a689554bSHai Li /* If the panel driver has not been probed after host register, 1988a689554bSHai Li * we should defer the host's probe. 1989a689554bSHai Li * It makes sure panel is connected when fbcon detects 1990a689554bSHai Li * connector status and gets the proper display mode to 1991a689554bSHai Li * create framebuffer. 1992f7009d26SArchit Taneja * Don't try to defer if there is nothing connected to the dsi 1993f7009d26SArchit Taneja * output 1994a689554bSHai Li */ 1995a9ddac9cSArchit Taneja if (check_defer && msm_host->device_node) { 19965fa8e4a2SBoris Brezillon if (IS_ERR(of_drm_find_panel(msm_host->device_node))) 1997c118e290SArchit Taneja if (!of_drm_find_bridge(msm_host->device_node)) 1998a689554bSHai Li return -EPROBE_DEFER; 1999a689554bSHai Li } 2000a689554bSHai Li } 2001a689554bSHai Li 2002a689554bSHai Li return 0; 2003a689554bSHai Li } 2004a689554bSHai Li 2005a689554bSHai Li void msm_dsi_host_unregister(struct mipi_dsi_host *host) 2006a689554bSHai Li { 2007a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2008a689554bSHai Li 2009a689554bSHai Li if (msm_host->registered) { 2010a689554bSHai Li mipi_dsi_host_unregister(host); 2011a689554bSHai Li host->dev = NULL; 2012a689554bSHai Li host->ops = NULL; 2013a689554bSHai Li msm_host->registered = false; 2014a689554bSHai Li } 2015a689554bSHai Li } 2016a689554bSHai Li 2017a689554bSHai Li int msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, 2018a689554bSHai Li const struct mipi_dsi_msg *msg) 2019a689554bSHai Li { 2020a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 20218f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2022a689554bSHai Li 2023a689554bSHai Li /* TODO: make sure dsi_cmd_mdp is idle. 2024a689554bSHai Li * Since DSI6G v1.2.0, we can set DSI_TRIG_CTRL.BLOCK_DMA_WITHIN_FRAME 2025a689554bSHai Li * to ask H/W to wait until cmd mdp is idle. S/W wait is not needed. 2026a689554bSHai Li * How to handle the old versions? Wait for mdp cmd done? 2027a689554bSHai Li */ 2028a689554bSHai Li 2029a689554bSHai Li /* 2030a689554bSHai Li * mdss interrupt is generated in mdp core clock domain 2031a689554bSHai Li * mdp clock need to be enabled to receive dsi interrupt 2032a689554bSHai Li */ 2033f6be1121SArchit Taneja pm_runtime_get_sync(&msm_host->pdev->dev); 20346b16f05aSRob Clark cfg_hnd->ops->link_clk_set_rate(msm_host); 20358f7ca540SSibi Sankar cfg_hnd->ops->link_clk_enable(msm_host); 2036a689554bSHai Li 2037a689554bSHai Li /* TODO: vote for bus bandwidth */ 2038a689554bSHai Li 2039a689554bSHai Li if (!(msg->flags & MIPI_DSI_MSG_USE_LPM)) 2040a689554bSHai Li dsi_set_tx_power_mode(0, msm_host); 2041a689554bSHai Li 2042a689554bSHai Li msm_host->dma_cmd_ctrl_restore = dsi_read(msm_host, REG_DSI_CTRL); 2043a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, 2044a689554bSHai Li msm_host->dma_cmd_ctrl_restore | 2045a689554bSHai Li DSI_CTRL_CMD_MODE_EN | 2046a689554bSHai Li DSI_CTRL_ENABLE); 2047a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_DMA_DONE, 1); 2048a689554bSHai Li 2049a689554bSHai Li return 0; 2050a689554bSHai Li } 2051a689554bSHai Li 2052a689554bSHai Li void msm_dsi_host_xfer_restore(struct mipi_dsi_host *host, 2053a689554bSHai Li const struct mipi_dsi_msg *msg) 2054a689554bSHai Li { 2055a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 20568f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2057a689554bSHai Li 2058a689554bSHai Li dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_DMA_DONE, 0); 2059a689554bSHai Li dsi_write(msm_host, REG_DSI_CTRL, msm_host->dma_cmd_ctrl_restore); 2060a689554bSHai Li 2061a689554bSHai Li if (!(msg->flags & MIPI_DSI_MSG_USE_LPM)) 2062a689554bSHai Li dsi_set_tx_power_mode(1, msm_host); 2063a689554bSHai Li 2064a689554bSHai Li /* TODO: unvote for bus bandwidth */ 2065a689554bSHai Li 20668f7ca540SSibi Sankar cfg_hnd->ops->link_clk_disable(msm_host); 2067f6be1121SArchit Taneja pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2068a689554bSHai Li } 2069a689554bSHai Li 2070a689554bSHai Li int msm_dsi_host_cmd_tx(struct mipi_dsi_host *host, 2071a689554bSHai Li const struct mipi_dsi_msg *msg) 2072a689554bSHai Li { 2073a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2074a689554bSHai Li 2075a689554bSHai Li return dsi_cmds2buf_tx(msm_host, msg); 2076a689554bSHai Li } 2077a689554bSHai Li 2078a689554bSHai Li int msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, 2079a689554bSHai Li const struct mipi_dsi_msg *msg) 2080a689554bSHai Li { 2081a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2082d248b61fSHai Li const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2083a689554bSHai Li int data_byte, rx_byte, dlen, end; 2084a689554bSHai Li int short_response, diff, pkt_size, ret = 0; 2085a689554bSHai Li char cmd; 2086a689554bSHai Li int rlen = msg->rx_len; 2087a689554bSHai Li u8 *buf; 2088a689554bSHai Li 2089a689554bSHai Li if (rlen <= 2) { 2090a689554bSHai Li short_response = 1; 2091a689554bSHai Li pkt_size = rlen; 2092a689554bSHai Li rx_byte = 4; 2093a689554bSHai Li } else { 2094a689554bSHai Li short_response = 0; 2095a689554bSHai Li data_byte = 10; /* first read */ 2096a689554bSHai Li if (rlen < data_byte) 2097a689554bSHai Li pkt_size = rlen; 2098a689554bSHai Li else 2099a689554bSHai Li pkt_size = data_byte; 2100a689554bSHai Li rx_byte = data_byte + 6; /* 4 header + 2 crc */ 2101a689554bSHai Li } 2102a689554bSHai Li 2103a689554bSHai Li buf = msm_host->rx_buf; 2104a689554bSHai Li end = 0; 2105a689554bSHai Li while (!end) { 2106a689554bSHai Li u8 tx[2] = {pkt_size & 0xff, pkt_size >> 8}; 2107a689554bSHai Li struct mipi_dsi_msg max_pkt_size_msg = { 2108a689554bSHai Li .channel = msg->channel, 2109a689554bSHai Li .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, 2110a689554bSHai Li .tx_len = 2, 2111a689554bSHai Li .tx_buf = tx, 2112a689554bSHai Li }; 2113a689554bSHai Li 2114a689554bSHai Li DBG("rlen=%d pkt_size=%d rx_byte=%d", 2115a689554bSHai Li rlen, pkt_size, rx_byte); 2116a689554bSHai Li 2117a689554bSHai Li ret = dsi_cmds2buf_tx(msm_host, &max_pkt_size_msg); 2118a689554bSHai Li if (ret < 2) { 2119a689554bSHai Li pr_err("%s: Set max pkt size failed, %d\n", 2120a689554bSHai Li __func__, ret); 2121a689554bSHai Li return -EINVAL; 2122a689554bSHai Li } 2123a689554bSHai Li 2124d248b61fSHai Li if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 2125d248b61fSHai Li (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { 2126a689554bSHai Li /* Clear the RDBK_DATA registers */ 2127a689554bSHai Li dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 2128a689554bSHai Li DSI_RDBK_DATA_CTRL_CLR); 2129a689554bSHai Li wmb(); /* make sure the RDBK registers are cleared */ 2130a689554bSHai Li dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 0); 2131a689554bSHai Li wmb(); /* release cleared status before transfer */ 2132a689554bSHai Li } 2133a689554bSHai Li 2134a689554bSHai Li ret = dsi_cmds2buf_tx(msm_host, msg); 2135a689554bSHai Li if (ret < msg->tx_len) { 2136a689554bSHai Li pr_err("%s: Read cmd Tx failed, %d\n", __func__, ret); 2137a689554bSHai Li return ret; 2138a689554bSHai Li } 2139a689554bSHai Li 2140a689554bSHai Li /* 2141a689554bSHai Li * once cmd_dma_done interrupt received, 2142a689554bSHai Li * return data from client is ready and stored 2143a689554bSHai Li * at RDBK_DATA register already 2144a689554bSHai Li * since rx fifo is 16 bytes, dcs header is kept at first loop, 2145a689554bSHai Li * after that dcs header lost during shift into registers 2146a689554bSHai Li */ 2147a689554bSHai Li dlen = dsi_cmd_dma_rx(msm_host, buf, rx_byte, pkt_size); 2148a689554bSHai Li 2149a689554bSHai Li if (dlen <= 0) 2150a689554bSHai Li return 0; 2151a689554bSHai Li 2152a689554bSHai Li if (short_response) 2153a689554bSHai Li break; 2154a689554bSHai Li 2155a689554bSHai Li if (rlen <= data_byte) { 2156a689554bSHai Li diff = data_byte - rlen; 2157a689554bSHai Li end = 1; 2158a689554bSHai Li } else { 2159a689554bSHai Li diff = 0; 2160a689554bSHai Li rlen -= data_byte; 2161a689554bSHai Li } 2162a689554bSHai Li 2163a689554bSHai Li if (!end) { 2164a689554bSHai Li dlen -= 2; /* 2 crc */ 2165a689554bSHai Li dlen -= diff; 2166a689554bSHai Li buf += dlen; /* next start position */ 2167a689554bSHai Li data_byte = 14; /* NOT first read */ 2168a689554bSHai Li if (rlen < data_byte) 2169a689554bSHai Li pkt_size += rlen; 2170a689554bSHai Li else 2171a689554bSHai Li pkt_size += data_byte; 2172a689554bSHai Li DBG("buf=%p dlen=%d diff=%d", buf, dlen, diff); 2173a689554bSHai Li } 2174a689554bSHai Li } 2175a689554bSHai Li 2176a689554bSHai Li /* 2177a689554bSHai Li * For single Long read, if the requested rlen < 10, 2178a689554bSHai Li * we need to shift the start position of rx 2179a689554bSHai Li * data buffer to skip the bytes which are not 2180a689554bSHai Li * updated. 2181a689554bSHai Li */ 2182a689554bSHai Li if (pkt_size < 10 && !short_response) 2183a689554bSHai Li buf = msm_host->rx_buf + (10 - rlen); 2184a689554bSHai Li else 2185a689554bSHai Li buf = msm_host->rx_buf; 2186a689554bSHai Li 2187a689554bSHai Li cmd = buf[0]; 2188a689554bSHai Li switch (cmd) { 2189a689554bSHai Li case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: 2190a689554bSHai Li pr_err("%s: rx ACK_ERR_PACLAGE\n", __func__); 2191a689554bSHai Li ret = 0; 2192651ad3f5SHai Li break; 2193a689554bSHai Li case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: 2194a689554bSHai Li case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: 2195a689554bSHai Li ret = dsi_short_read1_resp(buf, msg); 2196a689554bSHai Li break; 2197a689554bSHai Li case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: 2198a689554bSHai Li case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: 2199a689554bSHai Li ret = dsi_short_read2_resp(buf, msg); 2200a689554bSHai Li break; 2201a689554bSHai Li case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: 2202a689554bSHai Li case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: 2203a689554bSHai Li ret = dsi_long_read_resp(buf, msg); 2204a689554bSHai Li break; 2205a689554bSHai Li default: 2206a689554bSHai Li pr_warn("%s:Invalid response cmd\n", __func__); 2207a689554bSHai Li ret = 0; 2208a689554bSHai Li } 2209a689554bSHai Li 2210a689554bSHai Li return ret; 2211a689554bSHai Li } 2212a689554bSHai Li 22134ff9d4cbSArchit Taneja void msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, 22144ff9d4cbSArchit Taneja u32 len) 2215a689554bSHai Li { 2216a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2217a689554bSHai Li 22184ff9d4cbSArchit Taneja dsi_write(msm_host, REG_DSI_DMA_BASE, dma_base); 2219a689554bSHai Li dsi_write(msm_host, REG_DSI_DMA_LEN, len); 2220a689554bSHai Li dsi_write(msm_host, REG_DSI_TRIG_DMA, 1); 2221a689554bSHai Li 2222a689554bSHai Li /* Make sure trigger happens */ 2223a689554bSHai Li wmb(); 2224a689554bSHai Li } 2225a689554bSHai Li 22269d32c498SHai Li int msm_dsi_host_set_src_pll(struct mipi_dsi_host *host, 22275d134596SDmitry Baryshkov struct msm_dsi_phy *src_phy) 22289d32c498SHai Li { 22299d32c498SHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 22309d32c498SHai Li struct clk *byte_clk_provider, *pixel_clk_provider; 22319d32c498SHai Li int ret; 22329d32c498SHai Li 22335d134596SDmitry Baryshkov ret = msm_dsi_phy_get_clk_provider(src_phy, 22349d32c498SHai Li &byte_clk_provider, &pixel_clk_provider); 22359d32c498SHai Li if (ret) { 22369d32c498SHai Li pr_info("%s: can't get provider from pll, don't set parent\n", 22379d32c498SHai Li __func__); 22389d32c498SHai Li return 0; 22399d32c498SHai Li } 22409d32c498SHai Li 22419d32c498SHai Li ret = clk_set_parent(msm_host->byte_clk_src, byte_clk_provider); 22429d32c498SHai Li if (ret) { 22439d32c498SHai Li pr_err("%s: can't set parent to byte_clk_src. ret=%d\n", 22449d32c498SHai Li __func__, ret); 22459d32c498SHai Li goto exit; 22469d32c498SHai Li } 22479d32c498SHai Li 22489d32c498SHai Li ret = clk_set_parent(msm_host->pixel_clk_src, pixel_clk_provider); 22499d32c498SHai Li if (ret) { 22509d32c498SHai Li pr_err("%s: can't set parent to pixel_clk_src. ret=%d\n", 22519d32c498SHai Li __func__, ret); 22529d32c498SHai Li goto exit; 22539d32c498SHai Li } 22549d32c498SHai Li 22558f7ca540SSibi Sankar if (msm_host->dsi_clk_src) { 22564bfa9748SArchit Taneja ret = clk_set_parent(msm_host->dsi_clk_src, pixel_clk_provider); 22574bfa9748SArchit Taneja if (ret) { 22584bfa9748SArchit Taneja pr_err("%s: can't set parent to dsi_clk_src. ret=%d\n", 22594bfa9748SArchit Taneja __func__, ret); 22604bfa9748SArchit Taneja goto exit; 22614bfa9748SArchit Taneja } 22628f7ca540SSibi Sankar } 22634bfa9748SArchit Taneja 22648f7ca540SSibi Sankar if (msm_host->esc_clk_src) { 22654bfa9748SArchit Taneja ret = clk_set_parent(msm_host->esc_clk_src, byte_clk_provider); 22664bfa9748SArchit Taneja if (ret) { 22674bfa9748SArchit Taneja pr_err("%s: can't set parent to esc_clk_src. ret=%d\n", 22684bfa9748SArchit Taneja __func__, ret); 22694bfa9748SArchit Taneja goto exit; 22704bfa9748SArchit Taneja } 22714bfa9748SArchit Taneja } 22724bfa9748SArchit Taneja 22739d32c498SHai Li exit: 22749d32c498SHai Li return ret; 22759d32c498SHai Li } 22769d32c498SHai Li 227734d9545bSArchit Taneja void msm_dsi_host_reset_phy(struct mipi_dsi_host *host) 227834d9545bSArchit Taneja { 227934d9545bSArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 228034d9545bSArchit Taneja 228134d9545bSArchit Taneja DBG(""); 228234d9545bSArchit Taneja dsi_write(msm_host, REG_DSI_PHY_RESET, DSI_PHY_RESET_RESET); 228334d9545bSArchit Taneja /* Make sure fully reset */ 228434d9545bSArchit Taneja wmb(); 228534d9545bSArchit Taneja udelay(1000); 228634d9545bSArchit Taneja dsi_write(msm_host, REG_DSI_PHY_RESET, 0); 228734d9545bSArchit Taneja udelay(100); 228834d9545bSArchit Taneja } 228934d9545bSArchit Taneja 2290b62aa70aSHai Li void msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, 2291ed9976a0SChandan Uddaraju struct msm_dsi_phy_clk_request *clk_req, 2292ed9976a0SChandan Uddaraju bool is_dual_dsi) 2293b62aa70aSHai Li { 2294b62aa70aSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 22958f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2296d4cea38eSArchit Taneja int ret; 2297d4cea38eSArchit Taneja 2298ed9976a0SChandan Uddaraju ret = cfg_hnd->ops->calc_clk_rate(msm_host, is_dual_dsi); 2299d4cea38eSArchit Taneja if (ret) { 2300d4cea38eSArchit Taneja pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); 2301d4cea38eSArchit Taneja return; 2302d4cea38eSArchit Taneja } 2303b62aa70aSHai Li 2304b62aa70aSHai Li clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; 2305b62aa70aSHai Li clk_req->escclk_rate = msm_host->esc_clk_rate; 2306b62aa70aSHai Li } 2307b62aa70aSHai Li 2308a689554bSHai Li int msm_dsi_host_enable(struct mipi_dsi_host *host) 2309a689554bSHai Li { 2310a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2311a689554bSHai Li 2312a689554bSHai Li dsi_op_mode_config(msm_host, 2313a689554bSHai Li !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), true); 2314a689554bSHai Li 2315a689554bSHai Li /* TODO: clock should be turned off for command mode, 2316a689554bSHai Li * and only turned on before MDP START. 2317a689554bSHai Li * This part of code should be enabled once mdp driver support it. 2318a689554bSHai Li */ 2319f54ca1a0SArchit Taneja /* if (msm_panel->mode == MSM_DSI_CMD_MODE) { 2320f54ca1a0SArchit Taneja * dsi_link_clk_disable(msm_host); 2321f54ca1a0SArchit Taneja * pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2322f54ca1a0SArchit Taneja * } 2323f54ca1a0SArchit Taneja */ 23249c5638d7SAbhinav Kumar msm_host->enabled = true; 2325a689554bSHai Li return 0; 2326a689554bSHai Li } 2327a689554bSHai Li 2328a689554bSHai Li int msm_dsi_host_disable(struct mipi_dsi_host *host) 2329a689554bSHai Li { 2330a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2331a689554bSHai Li 23329c5638d7SAbhinav Kumar msm_host->enabled = false; 2333a689554bSHai Li dsi_op_mode_config(msm_host, 2334a689554bSHai Li !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), false); 2335a689554bSHai Li 2336a689554bSHai Li /* Since we have disabled INTF, the video engine won't stop so that 2337a689554bSHai Li * the cmd engine will be blocked. 2338a689554bSHai Li * Reset to disable video engine so that we can send off cmd. 2339a689554bSHai Li */ 2340a689554bSHai Li dsi_sw_reset(msm_host); 2341a689554bSHai Li 2342a689554bSHai Li return 0; 2343a689554bSHai Li } 2344a689554bSHai Li 23450c7df47fSArchit Taneja static void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) 23460c7df47fSArchit Taneja { 23470c7df47fSArchit Taneja enum sfpb_ahb_arb_master_port_en en; 23480c7df47fSArchit Taneja 23490c7df47fSArchit Taneja if (!msm_host->sfpb) 23500c7df47fSArchit Taneja return; 23510c7df47fSArchit Taneja 23520c7df47fSArchit Taneja en = enable ? SFPB_MASTER_PORT_ENABLE : SFPB_MASTER_PORT_DISABLE; 23530c7df47fSArchit Taneja 23540c7df47fSArchit Taneja regmap_update_bits(msm_host->sfpb, REG_SFPB_GPREG, 23550c7df47fSArchit Taneja SFPB_GPREG_MASTER_PORT_EN__MASK, 23560c7df47fSArchit Taneja SFPB_GPREG_MASTER_PORT_EN(en)); 23570c7df47fSArchit Taneja } 23580c7df47fSArchit Taneja 2359b62aa70aSHai Li int msm_dsi_host_power_on(struct mipi_dsi_host *host, 2360ed9976a0SChandan Uddaraju struct msm_dsi_phy_shared_timings *phy_shared_timings, 2361ed9976a0SChandan Uddaraju bool is_dual_dsi) 2362a689554bSHai Li { 2363a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 23648f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2365a689554bSHai Li int ret = 0; 2366a689554bSHai Li 2367a689554bSHai Li mutex_lock(&msm_host->dev_mutex); 2368a689554bSHai Li if (msm_host->power_on) { 2369a689554bSHai Li DBG("dsi host already on"); 2370a689554bSHai Li goto unlock_ret; 2371a689554bSHai Li } 2372a689554bSHai Li 23730c7df47fSArchit Taneja msm_dsi_sfpb_config(msm_host, true); 23740c7df47fSArchit Taneja 2375a689554bSHai Li ret = dsi_host_regulator_enable(msm_host); 2376a689554bSHai Li if (ret) { 2377a689554bSHai Li pr_err("%s:Failed to enable vregs.ret=%d\n", 2378a689554bSHai Li __func__, ret); 2379a689554bSHai Li goto unlock_ret; 2380a689554bSHai Li } 2381a689554bSHai Li 2382f6be1121SArchit Taneja pm_runtime_get_sync(&msm_host->pdev->dev); 23836b16f05aSRob Clark ret = cfg_hnd->ops->link_clk_set_rate(msm_host); 23846b16f05aSRob Clark if (!ret) 23858f7ca540SSibi Sankar ret = cfg_hnd->ops->link_clk_enable(msm_host); 2386a689554bSHai Li if (ret) { 2387f54ca1a0SArchit Taneja pr_err("%s: failed to enable link clocks. ret=%d\n", 2388f54ca1a0SArchit Taneja __func__, ret); 2389a689554bSHai Li goto fail_disable_reg; 2390a689554bSHai Li } 2391a689554bSHai Li 2392ab8909b0SHai Li ret = pinctrl_pm_select_default_state(&msm_host->pdev->dev); 2393ab8909b0SHai Li if (ret) { 2394ab8909b0SHai Li pr_err("%s: failed to set pinctrl default state, %d\n", 2395ab8909b0SHai Li __func__, ret); 2396ab8909b0SHai Li goto fail_disable_clk; 2397ab8909b0SHai Li } 2398ab8909b0SHai Li 2399ed9976a0SChandan Uddaraju dsi_timing_setup(msm_host, is_dual_dsi); 2400a689554bSHai Li dsi_sw_reset(msm_host); 2401b62aa70aSHai Li dsi_ctrl_config(msm_host, true, phy_shared_timings); 2402a689554bSHai Li 2403a689554bSHai Li if (msm_host->disp_en_gpio) 2404a689554bSHai Li gpiod_set_value(msm_host->disp_en_gpio, 1); 2405a689554bSHai Li 2406a689554bSHai Li msm_host->power_on = true; 2407a689554bSHai Li mutex_unlock(&msm_host->dev_mutex); 2408a689554bSHai Li 2409a689554bSHai Li return 0; 2410a689554bSHai Li 2411ab8909b0SHai Li fail_disable_clk: 24128f7ca540SSibi Sankar cfg_hnd->ops->link_clk_disable(msm_host); 2413f54ca1a0SArchit Taneja pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2414a689554bSHai Li fail_disable_reg: 2415a689554bSHai Li dsi_host_regulator_disable(msm_host); 2416a689554bSHai Li unlock_ret: 2417a689554bSHai Li mutex_unlock(&msm_host->dev_mutex); 2418a689554bSHai Li return ret; 2419a689554bSHai Li } 2420a689554bSHai Li 2421a689554bSHai Li int msm_dsi_host_power_off(struct mipi_dsi_host *host) 2422a689554bSHai Li { 2423a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 24248f7ca540SSibi Sankar const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 2425a689554bSHai Li 2426a689554bSHai Li mutex_lock(&msm_host->dev_mutex); 2427a689554bSHai Li if (!msm_host->power_on) { 2428a689554bSHai Li DBG("dsi host already off"); 2429a689554bSHai Li goto unlock_ret; 2430a689554bSHai Li } 2431a689554bSHai Li 2432dceac340SHai Li dsi_ctrl_config(msm_host, false, NULL); 2433a689554bSHai Li 2434a689554bSHai Li if (msm_host->disp_en_gpio) 2435a689554bSHai Li gpiod_set_value(msm_host->disp_en_gpio, 0); 2436a689554bSHai Li 2437ab8909b0SHai Li pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); 2438ab8909b0SHai Li 24398f7ca540SSibi Sankar cfg_hnd->ops->link_clk_disable(msm_host); 2440f6be1121SArchit Taneja pm_runtime_put_autosuspend(&msm_host->pdev->dev); 2441a689554bSHai Li 2442a689554bSHai Li dsi_host_regulator_disable(msm_host); 2443a689554bSHai Li 24440c7df47fSArchit Taneja msm_dsi_sfpb_config(msm_host, false); 24450c7df47fSArchit Taneja 2446a689554bSHai Li DBG("-"); 2447a689554bSHai Li 2448a689554bSHai Li msm_host->power_on = false; 2449a689554bSHai Li 2450a689554bSHai Li unlock_ret: 2451a689554bSHai Li mutex_unlock(&msm_host->dev_mutex); 2452a689554bSHai Li return 0; 2453a689554bSHai Li } 2454a689554bSHai Li 2455a689554bSHai Li int msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, 245663f8f3baSLaurent Pinchart const struct drm_display_mode *mode) 2457a689554bSHai Li { 2458a689554bSHai Li struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2459a689554bSHai Li 2460a689554bSHai Li if (msm_host->mode) { 2461a689554bSHai Li drm_mode_destroy(msm_host->dev, msm_host->mode); 2462a689554bSHai Li msm_host->mode = NULL; 2463a689554bSHai Li } 2464a689554bSHai Li 2465a689554bSHai Li msm_host->mode = drm_mode_duplicate(msm_host->dev, mode); 24662abe1f25SWei Yongjun if (!msm_host->mode) { 2467a689554bSHai Li pr_err("%s: cannot duplicate mode\n", __func__); 24682abe1f25SWei Yongjun return -ENOMEM; 2469a689554bSHai Li } 2470a689554bSHai Li 2471a689554bSHai Li return 0; 2472a689554bSHai Li } 2473a689554bSHai Li 2474e3a91f89SSean Paul struct drm_panel *msm_dsi_host_get_panel(struct mipi_dsi_host *host) 2475a689554bSHai Li { 2476e3a91f89SSean Paul return of_drm_find_panel(to_msm_dsi_host(host)->device_node); 2477e3a91f89SSean Paul } 2478a689554bSHai Li 2479e3a91f89SSean Paul unsigned long msm_dsi_host_get_mode_flags(struct mipi_dsi_host *host) 2480e3a91f89SSean Paul { 2481e3a91f89SSean Paul return to_msm_dsi_host(host)->mode_flags; 2482a689554bSHai Li } 2483a689554bSHai Li 2484c118e290SArchit Taneja struct drm_bridge *msm_dsi_host_get_bridge(struct mipi_dsi_host *host) 2485c118e290SArchit Taneja { 2486c118e290SArchit Taneja struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 2487c118e290SArchit Taneja 2488c118e290SArchit Taneja return of_drm_find_bridge(msm_host->device_node); 2489c118e290SArchit Taneja } 2490*98659487SAbhinav Kumar 2491