162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2015, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/clk.h> 762306a36Sopenharmony_ci#include <linux/delay.h> 862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 1162306a36Sopenharmony_ci#include <linux/interrupt.h> 1262306a36Sopenharmony_ci#include <linux/mfd/syscon.h> 1362306a36Sopenharmony_ci#include <linux/of.h> 1462306a36Sopenharmony_ci#include <linux/of_graph.h> 1562306a36Sopenharmony_ci#include <linux/of_irq.h> 1662306a36Sopenharmony_ci#include <linux/pinctrl/consumer.h> 1762306a36Sopenharmony_ci#include <linux/pm_opp.h> 1862306a36Sopenharmony_ci#include <linux/regmap.h> 1962306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2062306a36Sopenharmony_ci#include <linux/spinlock.h> 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#include <video/mipi_display.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include <drm/display/drm_dsc_helper.h> 2562306a36Sopenharmony_ci#include <drm/drm_of.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include "dsi.h" 2862306a36Sopenharmony_ci#include "dsi.xml.h" 2962306a36Sopenharmony_ci#include "sfpb.xml.h" 3062306a36Sopenharmony_ci#include "dsi_cfg.h" 3162306a36Sopenharmony_ci#include "msm_dsc_helper.h" 3262306a36Sopenharmony_ci#include "msm_kms.h" 3362306a36Sopenharmony_ci#include "msm_gem.h" 3462306a36Sopenharmony_ci#include "phy/dsi_phy.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define DSI_RESET_TOGGLE_DELAY_MS 20 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic int dsi_get_version(const void __iomem *base, u32 *major, u32 *minor) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci u32 ver; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci if (!major || !minor) 4562306a36Sopenharmony_ci return -EINVAL; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * From DSI6G(v3), addition of a 6G_HW_VERSION register at offset 0 4962306a36Sopenharmony_ci * makes all other registers 4-byte shifted down. 5062306a36Sopenharmony_ci * 5162306a36Sopenharmony_ci * In order to identify between DSI6G(v3) and beyond, and DSIv2 and 5262306a36Sopenharmony_ci * older, we read the DSI_VERSION register without any shift(offset 5362306a36Sopenharmony_ci * 0x1f0). In the case of DSIv2, this hast to be a non-zero value. In 5462306a36Sopenharmony_ci * the case of DSI6G, this has to be zero (the offset points to a 5562306a36Sopenharmony_ci * scratch register which we never touch) 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci ver = msm_readl(base + REG_DSI_VERSION); 5962306a36Sopenharmony_ci if (ver) { 6062306a36Sopenharmony_ci /* older dsi host, there is no register shift */ 6162306a36Sopenharmony_ci ver = FIELD(ver, DSI_VERSION_MAJOR); 6262306a36Sopenharmony_ci if (ver <= MSM_DSI_VER_MAJOR_V2) { 6362306a36Sopenharmony_ci /* old versions */ 6462306a36Sopenharmony_ci *major = ver; 6562306a36Sopenharmony_ci *minor = 0; 6662306a36Sopenharmony_ci return 0; 6762306a36Sopenharmony_ci } else { 6862306a36Sopenharmony_ci return -EINVAL; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci } else { 7162306a36Sopenharmony_ci /* 7262306a36Sopenharmony_ci * newer host, offset 0 has 6G_HW_VERSION, the rest of the 7362306a36Sopenharmony_ci * registers are shifted down, read DSI_VERSION again with 7462306a36Sopenharmony_ci * the shifted offset 7562306a36Sopenharmony_ci */ 7662306a36Sopenharmony_ci ver = msm_readl(base + DSI_6G_REG_SHIFT + REG_DSI_VERSION); 7762306a36Sopenharmony_ci ver = FIELD(ver, DSI_VERSION_MAJOR); 7862306a36Sopenharmony_ci if (ver == MSM_DSI_VER_MAJOR_6G) { 7962306a36Sopenharmony_ci /* 6G version */ 8062306a36Sopenharmony_ci *major = ver; 8162306a36Sopenharmony_ci *minor = msm_readl(base + REG_DSI_6G_HW_VERSION); 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci } else { 8462306a36Sopenharmony_ci return -EINVAL; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci } 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define DSI_ERR_STATE_ACK 0x0000 9062306a36Sopenharmony_ci#define DSI_ERR_STATE_TIMEOUT 0x0001 9162306a36Sopenharmony_ci#define DSI_ERR_STATE_DLN0_PHY 0x0002 9262306a36Sopenharmony_ci#define DSI_ERR_STATE_FIFO 0x0004 9362306a36Sopenharmony_ci#define DSI_ERR_STATE_MDP_FIFO_UNDERFLOW 0x0008 9462306a36Sopenharmony_ci#define DSI_ERR_STATE_INTERLEAVE_OP_CONTENTION 0x0010 9562306a36Sopenharmony_ci#define DSI_ERR_STATE_PLL_UNLOCKED 0x0020 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define DSI_CLK_CTRL_ENABLE_CLKS \ 9862306a36Sopenharmony_ci (DSI_CLK_CTRL_AHBS_HCLK_ON | DSI_CLK_CTRL_AHBM_SCLK_ON | \ 9962306a36Sopenharmony_ci DSI_CLK_CTRL_PCLK_ON | DSI_CLK_CTRL_DSICLK_ON | \ 10062306a36Sopenharmony_ci DSI_CLK_CTRL_BYTECLK_ON | DSI_CLK_CTRL_ESCCLK_ON | \ 10162306a36Sopenharmony_ci DSI_CLK_CTRL_FORCE_ON_DYN_AHBM_HCLK) 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistruct msm_dsi_host { 10462306a36Sopenharmony_ci struct mipi_dsi_host base; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci struct platform_device *pdev; 10762306a36Sopenharmony_ci struct drm_device *dev; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci int id; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci void __iomem *ctrl_base; 11262306a36Sopenharmony_ci phys_addr_t ctrl_size; 11362306a36Sopenharmony_ci struct regulator_bulk_data *supplies; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci int num_bus_clks; 11662306a36Sopenharmony_ci struct clk_bulk_data bus_clks[DSI_BUS_CLK_MAX]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci struct clk *byte_clk; 11962306a36Sopenharmony_ci struct clk *esc_clk; 12062306a36Sopenharmony_ci struct clk *pixel_clk; 12162306a36Sopenharmony_ci struct clk *byte_intf_clk; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci unsigned long byte_clk_rate; 12462306a36Sopenharmony_ci unsigned long byte_intf_clk_rate; 12562306a36Sopenharmony_ci unsigned long pixel_clk_rate; 12662306a36Sopenharmony_ci unsigned long esc_clk_rate; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* DSI v2 specific clocks */ 12962306a36Sopenharmony_ci struct clk *src_clk; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci unsigned long src_clk_rate; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci struct gpio_desc *disp_en_gpio; 13462306a36Sopenharmony_ci struct gpio_desc *te_gpio; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci struct completion dma_comp; 13962306a36Sopenharmony_ci struct completion video_comp; 14062306a36Sopenharmony_ci struct mutex dev_mutex; 14162306a36Sopenharmony_ci struct mutex cmd_mutex; 14262306a36Sopenharmony_ci spinlock_t intr_lock; /* Protect interrupt ctrl register */ 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci u32 err_work_state; 14562306a36Sopenharmony_ci struct work_struct err_work; 14662306a36Sopenharmony_ci struct workqueue_struct *workqueue; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* DSI 6G TX buffer*/ 14962306a36Sopenharmony_ci struct drm_gem_object *tx_gem_obj; 15062306a36Sopenharmony_ci struct msm_gem_address_space *aspace; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci /* DSI v2 TX buffer */ 15362306a36Sopenharmony_ci void *tx_buf; 15462306a36Sopenharmony_ci dma_addr_t tx_buf_paddr; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci int tx_size; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci u8 *rx_buf; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci struct regmap *sfpb; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci struct drm_display_mode *mode; 16362306a36Sopenharmony_ci struct drm_dsc_config *dsc; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci /* connected device info */ 16662306a36Sopenharmony_ci unsigned int channel; 16762306a36Sopenharmony_ci unsigned int lanes; 16862306a36Sopenharmony_ci enum mipi_dsi_pixel_format format; 16962306a36Sopenharmony_ci unsigned long mode_flags; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* lane data parsed via DT */ 17262306a36Sopenharmony_ci int dlane_swap; 17362306a36Sopenharmony_ci int num_data_lanes; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci /* from phy DT */ 17662306a36Sopenharmony_ci bool cphy_mode; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci u32 dma_cmd_ctrl_restore; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci bool registered; 18162306a36Sopenharmony_ci bool power_on; 18262306a36Sopenharmony_ci bool enabled; 18362306a36Sopenharmony_ci int irq; 18462306a36Sopenharmony_ci}; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic u32 dsi_get_bpp(const enum mipi_dsi_pixel_format fmt) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci switch (fmt) { 18962306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB565: return 16; 19062306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666_PACKED: return 18; 19162306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666: 19262306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB888: 19362306a36Sopenharmony_ci default: return 24; 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci} 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_cistatic inline u32 dsi_read(struct msm_dsi_host *msm_host, u32 reg) 19862306a36Sopenharmony_ci{ 19962306a36Sopenharmony_ci return msm_readl(msm_host->ctrl_base + reg); 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_cistatic inline void dsi_write(struct msm_dsi_host *msm_host, u32 reg, u32 data) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci msm_writel(data, msm_host->ctrl_base + reg); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct msm_dsi_cfg_handler *dsi_get_config( 20762306a36Sopenharmony_ci struct msm_dsi_host *msm_host) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = NULL; 21062306a36Sopenharmony_ci struct device *dev = &msm_host->pdev->dev; 21162306a36Sopenharmony_ci struct clk *ahb_clk; 21262306a36Sopenharmony_ci int ret; 21362306a36Sopenharmony_ci u32 major = 0, minor = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ahb_clk = msm_clk_get(msm_host->pdev, "iface"); 21662306a36Sopenharmony_ci if (IS_ERR(ahb_clk)) { 21762306a36Sopenharmony_ci pr_err("%s: cannot get interface clock\n", __func__); 21862306a36Sopenharmony_ci goto exit; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci pm_runtime_get_sync(dev); 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci ret = clk_prepare_enable(ahb_clk); 22462306a36Sopenharmony_ci if (ret) { 22562306a36Sopenharmony_ci pr_err("%s: unable to enable ahb_clk\n", __func__); 22662306a36Sopenharmony_ci goto runtime_put; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci ret = dsi_get_version(msm_host->ctrl_base, &major, &minor); 23062306a36Sopenharmony_ci if (ret) { 23162306a36Sopenharmony_ci pr_err("%s: Invalid version\n", __func__); 23262306a36Sopenharmony_ci goto disable_clks; 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci cfg_hnd = msm_dsi_cfg_get(major, minor); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci DBG("%s: Version %x:%x\n", __func__, major, minor); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cidisable_clks: 24062306a36Sopenharmony_ci clk_disable_unprepare(ahb_clk); 24162306a36Sopenharmony_ciruntime_put: 24262306a36Sopenharmony_ci pm_runtime_put_sync(dev); 24362306a36Sopenharmony_ciexit: 24462306a36Sopenharmony_ci return cfg_hnd; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic inline struct msm_dsi_host *to_msm_dsi_host(struct mipi_dsi_host *host) 24862306a36Sopenharmony_ci{ 24962306a36Sopenharmony_ci return container_of(host, struct msm_dsi_host, base); 25062306a36Sopenharmony_ci} 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciint dsi_clk_init_v2(struct msm_dsi_host *msm_host) 25362306a36Sopenharmony_ci{ 25462306a36Sopenharmony_ci struct platform_device *pdev = msm_host->pdev; 25562306a36Sopenharmony_ci int ret = 0; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci msm_host->src_clk = msm_clk_get(pdev, "src"); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci if (IS_ERR(msm_host->src_clk)) { 26062306a36Sopenharmony_ci ret = PTR_ERR(msm_host->src_clk); 26162306a36Sopenharmony_ci pr_err("%s: can't find src clock. ret=%d\n", 26262306a36Sopenharmony_ci __func__, ret); 26362306a36Sopenharmony_ci msm_host->src_clk = NULL; 26462306a36Sopenharmony_ci return ret; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci return ret; 26862306a36Sopenharmony_ci} 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ciint dsi_clk_init_6g_v2(struct msm_dsi_host *msm_host) 27162306a36Sopenharmony_ci{ 27262306a36Sopenharmony_ci struct platform_device *pdev = msm_host->pdev; 27362306a36Sopenharmony_ci int ret = 0; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci msm_host->byte_intf_clk = msm_clk_get(pdev, "byte_intf"); 27662306a36Sopenharmony_ci if (IS_ERR(msm_host->byte_intf_clk)) { 27762306a36Sopenharmony_ci ret = PTR_ERR(msm_host->byte_intf_clk); 27862306a36Sopenharmony_ci pr_err("%s: can't find byte_intf clock. ret=%d\n", 27962306a36Sopenharmony_ci __func__, ret); 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci return ret; 28362306a36Sopenharmony_ci} 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_cistatic int dsi_clk_init(struct msm_dsi_host *msm_host) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct platform_device *pdev = msm_host->pdev; 28862306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 28962306a36Sopenharmony_ci const struct msm_dsi_config *cfg = cfg_hnd->cfg; 29062306a36Sopenharmony_ci int i, ret = 0; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* get bus clocks */ 29362306a36Sopenharmony_ci for (i = 0; i < cfg->num_bus_clks; i++) 29462306a36Sopenharmony_ci msm_host->bus_clks[i].id = cfg->bus_clk_names[i]; 29562306a36Sopenharmony_ci msm_host->num_bus_clks = cfg->num_bus_clks; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = devm_clk_bulk_get(&pdev->dev, msm_host->num_bus_clks, msm_host->bus_clks); 29862306a36Sopenharmony_ci if (ret < 0) { 29962306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to get clocks, ret = %d\n", ret); 30062306a36Sopenharmony_ci goto exit; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* get link and source clocks */ 30462306a36Sopenharmony_ci msm_host->byte_clk = msm_clk_get(pdev, "byte"); 30562306a36Sopenharmony_ci if (IS_ERR(msm_host->byte_clk)) { 30662306a36Sopenharmony_ci ret = PTR_ERR(msm_host->byte_clk); 30762306a36Sopenharmony_ci pr_err("%s: can't find dsi_byte clock. ret=%d\n", 30862306a36Sopenharmony_ci __func__, ret); 30962306a36Sopenharmony_ci msm_host->byte_clk = NULL; 31062306a36Sopenharmony_ci goto exit; 31162306a36Sopenharmony_ci } 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci msm_host->pixel_clk = msm_clk_get(pdev, "pixel"); 31462306a36Sopenharmony_ci if (IS_ERR(msm_host->pixel_clk)) { 31562306a36Sopenharmony_ci ret = PTR_ERR(msm_host->pixel_clk); 31662306a36Sopenharmony_ci pr_err("%s: can't find dsi_pixel clock. ret=%d\n", 31762306a36Sopenharmony_ci __func__, ret); 31862306a36Sopenharmony_ci msm_host->pixel_clk = NULL; 31962306a36Sopenharmony_ci goto exit; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci msm_host->esc_clk = msm_clk_get(pdev, "core"); 32362306a36Sopenharmony_ci if (IS_ERR(msm_host->esc_clk)) { 32462306a36Sopenharmony_ci ret = PTR_ERR(msm_host->esc_clk); 32562306a36Sopenharmony_ci pr_err("%s: can't find dsi_esc clock. ret=%d\n", 32662306a36Sopenharmony_ci __func__, ret); 32762306a36Sopenharmony_ci msm_host->esc_clk = NULL; 32862306a36Sopenharmony_ci goto exit; 32962306a36Sopenharmony_ci } 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci if (cfg_hnd->ops->clk_init_ver) 33262306a36Sopenharmony_ci ret = cfg_hnd->ops->clk_init_ver(msm_host); 33362306a36Sopenharmony_ciexit: 33462306a36Sopenharmony_ci return ret; 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ciint msm_dsi_runtime_suspend(struct device *dev) 33862306a36Sopenharmony_ci{ 33962306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 34062306a36Sopenharmony_ci struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); 34162306a36Sopenharmony_ci struct mipi_dsi_host *host = msm_dsi->host; 34262306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (!msm_host->cfg_hnd) 34562306a36Sopenharmony_ci return 0; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci clk_bulk_disable_unprepare(msm_host->num_bus_clks, msm_host->bus_clks); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ciint msm_dsi_runtime_resume(struct device *dev) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 35562306a36Sopenharmony_ci struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); 35662306a36Sopenharmony_ci struct mipi_dsi_host *host = msm_dsi->host; 35762306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci if (!msm_host->cfg_hnd) 36062306a36Sopenharmony_ci return 0; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return clk_bulk_prepare_enable(msm_host->num_bus_clks, msm_host->bus_clks); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ciint dsi_link_clk_set_rate_6g(struct msm_dsi_host *msm_host) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci int ret; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci DBG("Set clk rates: pclk=%d, byteclk=%lu", 37062306a36Sopenharmony_ci msm_host->mode->clock, msm_host->byte_clk_rate); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci ret = dev_pm_opp_set_rate(&msm_host->pdev->dev, 37362306a36Sopenharmony_ci msm_host->byte_clk_rate); 37462306a36Sopenharmony_ci if (ret) { 37562306a36Sopenharmony_ci pr_err("%s: dev_pm_opp_set_rate failed %d\n", __func__, ret); 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); 38062306a36Sopenharmony_ci if (ret) { 38162306a36Sopenharmony_ci pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); 38262306a36Sopenharmony_ci return ret; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (msm_host->byte_intf_clk) { 38662306a36Sopenharmony_ci ret = clk_set_rate(msm_host->byte_intf_clk, msm_host->byte_intf_clk_rate); 38762306a36Sopenharmony_ci if (ret) { 38862306a36Sopenharmony_ci pr_err("%s: Failed to set rate byte intf clk, %d\n", 38962306a36Sopenharmony_ci __func__, ret); 39062306a36Sopenharmony_ci return ret; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return 0; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ciint dsi_link_clk_enable_6g(struct msm_dsi_host *msm_host) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci int ret; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->esc_clk); 40362306a36Sopenharmony_ci if (ret) { 40462306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi esc clk\n", __func__); 40562306a36Sopenharmony_ci goto error; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->byte_clk); 40962306a36Sopenharmony_ci if (ret) { 41062306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi byte clk\n", __func__); 41162306a36Sopenharmony_ci goto byte_clk_err; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->pixel_clk); 41562306a36Sopenharmony_ci if (ret) { 41662306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi pixel clk\n", __func__); 41762306a36Sopenharmony_ci goto pixel_clk_err; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->byte_intf_clk); 42162306a36Sopenharmony_ci if (ret) { 42262306a36Sopenharmony_ci pr_err("%s: Failed to enable byte intf clk\n", 42362306a36Sopenharmony_ci __func__); 42462306a36Sopenharmony_ci goto byte_intf_clk_err; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cibyte_intf_clk_err: 43062306a36Sopenharmony_ci clk_disable_unprepare(msm_host->pixel_clk); 43162306a36Sopenharmony_cipixel_clk_err: 43262306a36Sopenharmony_ci clk_disable_unprepare(msm_host->byte_clk); 43362306a36Sopenharmony_cibyte_clk_err: 43462306a36Sopenharmony_ci clk_disable_unprepare(msm_host->esc_clk); 43562306a36Sopenharmony_cierror: 43662306a36Sopenharmony_ci return ret; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ciint dsi_link_clk_set_rate_v2(struct msm_dsi_host *msm_host) 44062306a36Sopenharmony_ci{ 44162306a36Sopenharmony_ci int ret; 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci DBG("Set clk rates: pclk=%d, byteclk=%lu, esc_clk=%lu, dsi_src_clk=%lu", 44462306a36Sopenharmony_ci msm_host->mode->clock, msm_host->byte_clk_rate, 44562306a36Sopenharmony_ci msm_host->esc_clk_rate, msm_host->src_clk_rate); 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci ret = clk_set_rate(msm_host->byte_clk, msm_host->byte_clk_rate); 44862306a36Sopenharmony_ci if (ret) { 44962306a36Sopenharmony_ci pr_err("%s: Failed to set rate byte clk, %d\n", __func__, ret); 45062306a36Sopenharmony_ci return ret; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci ret = clk_set_rate(msm_host->esc_clk, msm_host->esc_clk_rate); 45462306a36Sopenharmony_ci if (ret) { 45562306a36Sopenharmony_ci pr_err("%s: Failed to set rate esc clk, %d\n", __func__, ret); 45662306a36Sopenharmony_ci return ret; 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci ret = clk_set_rate(msm_host->src_clk, msm_host->src_clk_rate); 46062306a36Sopenharmony_ci if (ret) { 46162306a36Sopenharmony_ci pr_err("%s: Failed to set rate src clk, %d\n", __func__, ret); 46262306a36Sopenharmony_ci return ret; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci ret = clk_set_rate(msm_host->pixel_clk, msm_host->pixel_clk_rate); 46662306a36Sopenharmony_ci if (ret) { 46762306a36Sopenharmony_ci pr_err("%s: Failed to set rate pixel clk, %d\n", __func__, ret); 46862306a36Sopenharmony_ci return ret; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ciint dsi_link_clk_enable_v2(struct msm_dsi_host *msm_host) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci int ret; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->byte_clk); 47962306a36Sopenharmony_ci if (ret) { 48062306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi byte clk\n", __func__); 48162306a36Sopenharmony_ci goto error; 48262306a36Sopenharmony_ci } 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->esc_clk); 48562306a36Sopenharmony_ci if (ret) { 48662306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi esc clk\n", __func__); 48762306a36Sopenharmony_ci goto esc_clk_err; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->src_clk); 49162306a36Sopenharmony_ci if (ret) { 49262306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi src clk\n", __func__); 49362306a36Sopenharmony_ci goto src_clk_err; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci ret = clk_prepare_enable(msm_host->pixel_clk); 49762306a36Sopenharmony_ci if (ret) { 49862306a36Sopenharmony_ci pr_err("%s: Failed to enable dsi pixel clk\n", __func__); 49962306a36Sopenharmony_ci goto pixel_clk_err; 50062306a36Sopenharmony_ci } 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci return 0; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cipixel_clk_err: 50562306a36Sopenharmony_ci clk_disable_unprepare(msm_host->src_clk); 50662306a36Sopenharmony_cisrc_clk_err: 50762306a36Sopenharmony_ci clk_disable_unprepare(msm_host->esc_clk); 50862306a36Sopenharmony_ciesc_clk_err: 50962306a36Sopenharmony_ci clk_disable_unprepare(msm_host->byte_clk); 51062306a36Sopenharmony_cierror: 51162306a36Sopenharmony_ci return ret; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_civoid dsi_link_clk_disable_6g(struct msm_dsi_host *msm_host) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci /* Drop the performance state vote */ 51762306a36Sopenharmony_ci dev_pm_opp_set_rate(&msm_host->pdev->dev, 0); 51862306a36Sopenharmony_ci clk_disable_unprepare(msm_host->esc_clk); 51962306a36Sopenharmony_ci clk_disable_unprepare(msm_host->pixel_clk); 52062306a36Sopenharmony_ci clk_disable_unprepare(msm_host->byte_intf_clk); 52162306a36Sopenharmony_ci clk_disable_unprepare(msm_host->byte_clk); 52262306a36Sopenharmony_ci} 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_civoid dsi_link_clk_disable_v2(struct msm_dsi_host *msm_host) 52562306a36Sopenharmony_ci{ 52662306a36Sopenharmony_ci clk_disable_unprepare(msm_host->pixel_clk); 52762306a36Sopenharmony_ci clk_disable_unprepare(msm_host->src_clk); 52862306a36Sopenharmony_ci clk_disable_unprepare(msm_host->esc_clk); 52962306a36Sopenharmony_ci clk_disable_unprepare(msm_host->byte_clk); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic unsigned long dsi_adjust_pclk_for_compression(const struct drm_display_mode *mode, 53362306a36Sopenharmony_ci const struct drm_dsc_config *dsc) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci int new_hdisplay = DIV_ROUND_UP(mode->hdisplay * drm_dsc_get_bpp_int(dsc), 53662306a36Sopenharmony_ci dsc->bits_per_component * 3); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci int new_htotal = mode->htotal - mode->hdisplay + new_hdisplay; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return new_htotal * mode->vtotal * drm_mode_vrefresh(mode); 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic unsigned long dsi_get_pclk_rate(const struct drm_display_mode *mode, 54462306a36Sopenharmony_ci const struct drm_dsc_config *dsc, bool is_bonded_dsi) 54562306a36Sopenharmony_ci{ 54662306a36Sopenharmony_ci unsigned long pclk_rate; 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci pclk_rate = mode->clock * 1000; 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci if (dsc) 55162306a36Sopenharmony_ci pclk_rate = dsi_adjust_pclk_for_compression(mode, dsc); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* 55462306a36Sopenharmony_ci * For bonded DSI mode, the current DRM mode has the complete width of the 55562306a36Sopenharmony_ci * panel. Since, the complete panel is driven by two DSI controllers, 55662306a36Sopenharmony_ci * the clock rates have to be split between the two dsi controllers. 55762306a36Sopenharmony_ci * Adjust the byte and pixel clock rates for each dsi host accordingly. 55862306a36Sopenharmony_ci */ 55962306a36Sopenharmony_ci if (is_bonded_dsi) 56062306a36Sopenharmony_ci pclk_rate /= 2; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return pclk_rate; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ciunsigned long dsi_byte_clk_get_rate(struct mipi_dsi_host *host, bool is_bonded_dsi, 56662306a36Sopenharmony_ci const struct drm_display_mode *mode) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 56962306a36Sopenharmony_ci u8 lanes = msm_host->lanes; 57062306a36Sopenharmony_ci u32 bpp = dsi_get_bpp(msm_host->format); 57162306a36Sopenharmony_ci unsigned long pclk_rate = dsi_get_pclk_rate(mode, msm_host->dsc, is_bonded_dsi); 57262306a36Sopenharmony_ci unsigned long pclk_bpp; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if (lanes == 0) { 57562306a36Sopenharmony_ci pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); 57662306a36Sopenharmony_ci lanes = 1; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* CPHY "byte_clk" is in units of 16 bits */ 58062306a36Sopenharmony_ci if (msm_host->cphy_mode) 58162306a36Sopenharmony_ci pclk_bpp = mult_frac(pclk_rate, bpp, 16 * lanes); 58262306a36Sopenharmony_ci else 58362306a36Sopenharmony_ci pclk_bpp = mult_frac(pclk_rate, bpp, 8 * lanes); 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci return pclk_bpp; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void dsi_calc_pclk(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 58962306a36Sopenharmony_ci{ 59062306a36Sopenharmony_ci msm_host->pixel_clk_rate = dsi_get_pclk_rate(msm_host->mode, msm_host->dsc, is_bonded_dsi); 59162306a36Sopenharmony_ci msm_host->byte_clk_rate = dsi_byte_clk_get_rate(&msm_host->base, is_bonded_dsi, 59262306a36Sopenharmony_ci msm_host->mode); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci DBG("pclk=%lu, bclk=%lu", msm_host->pixel_clk_rate, 59562306a36Sopenharmony_ci msm_host->byte_clk_rate); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ciint dsi_calc_clk_rate_6g(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci if (!msm_host->mode) { 60262306a36Sopenharmony_ci pr_err("%s: mode not set\n", __func__); 60362306a36Sopenharmony_ci return -EINVAL; 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci dsi_calc_pclk(msm_host, is_bonded_dsi); 60762306a36Sopenharmony_ci msm_host->esc_clk_rate = clk_get_rate(msm_host->esc_clk); 60862306a36Sopenharmony_ci return 0; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ciint dsi_calc_clk_rate_v2(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 61262306a36Sopenharmony_ci{ 61362306a36Sopenharmony_ci u32 bpp = dsi_get_bpp(msm_host->format); 61462306a36Sopenharmony_ci unsigned int esc_mhz, esc_div; 61562306a36Sopenharmony_ci unsigned long byte_mhz; 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci dsi_calc_pclk(msm_host, is_bonded_dsi); 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci msm_host->src_clk_rate = mult_frac(msm_host->pixel_clk_rate, bpp, 8); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* 62262306a36Sopenharmony_ci * esc clock is byte clock followed by a 4 bit divider, 62362306a36Sopenharmony_ci * we need to find an escape clock frequency within the 62462306a36Sopenharmony_ci * mipi DSI spec range within the maximum divider limit 62562306a36Sopenharmony_ci * We iterate here between an escape clock frequencey 62662306a36Sopenharmony_ci * between 20 Mhz to 5 Mhz and pick up the first one 62762306a36Sopenharmony_ci * that can be supported by our divider 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci byte_mhz = msm_host->byte_clk_rate / 1000000; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci for (esc_mhz = 20; esc_mhz >= 5; esc_mhz--) { 63362306a36Sopenharmony_ci esc_div = DIV_ROUND_UP(byte_mhz, esc_mhz); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci /* 63662306a36Sopenharmony_ci * TODO: Ideally, we shouldn't know what sort of divider 63762306a36Sopenharmony_ci * is available in mmss_cc, we're just assuming that 63862306a36Sopenharmony_ci * it'll always be a 4 bit divider. Need to come up with 63962306a36Sopenharmony_ci * a better way here. 64062306a36Sopenharmony_ci */ 64162306a36Sopenharmony_ci if (esc_div >= 1 && esc_div <= 16) 64262306a36Sopenharmony_ci break; 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci if (esc_mhz < 5) 64662306a36Sopenharmony_ci return -EINVAL; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci msm_host->esc_clk_rate = msm_host->byte_clk_rate / esc_div; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci DBG("esc=%lu, src=%lu", msm_host->esc_clk_rate, 65162306a36Sopenharmony_ci msm_host->src_clk_rate); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci return 0; 65462306a36Sopenharmony_ci} 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_cistatic void dsi_intr_ctrl(struct msm_dsi_host *msm_host, u32 mask, int enable) 65762306a36Sopenharmony_ci{ 65862306a36Sopenharmony_ci u32 intr; 65962306a36Sopenharmony_ci unsigned long flags; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci spin_lock_irqsave(&msm_host->intr_lock, flags); 66262306a36Sopenharmony_ci intr = dsi_read(msm_host, REG_DSI_INTR_CTRL); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci if (enable) 66562306a36Sopenharmony_ci intr |= mask; 66662306a36Sopenharmony_ci else 66762306a36Sopenharmony_ci intr &= ~mask; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci DBG("intr=%x enable=%d", intr, enable); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_INTR_CTRL, intr); 67262306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_host->intr_lock, flags); 67362306a36Sopenharmony_ci} 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_cistatic inline enum dsi_traffic_mode dsi_get_traffic_mode(const u32 mode_flags) 67662306a36Sopenharmony_ci{ 67762306a36Sopenharmony_ci if (mode_flags & MIPI_DSI_MODE_VIDEO_BURST) 67862306a36Sopenharmony_ci return BURST_MODE; 67962306a36Sopenharmony_ci else if (mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) 68062306a36Sopenharmony_ci return NON_BURST_SYNCH_PULSE; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci return NON_BURST_SYNCH_EVENT; 68362306a36Sopenharmony_ci} 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_cistatic inline enum dsi_vid_dst_format dsi_get_vid_fmt( 68662306a36Sopenharmony_ci const enum mipi_dsi_pixel_format mipi_fmt) 68762306a36Sopenharmony_ci{ 68862306a36Sopenharmony_ci switch (mipi_fmt) { 68962306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB888: return VID_DST_FORMAT_RGB888; 69062306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666: return VID_DST_FORMAT_RGB666_LOOSE; 69162306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666_PACKED: return VID_DST_FORMAT_RGB666; 69262306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB565: return VID_DST_FORMAT_RGB565; 69362306a36Sopenharmony_ci default: return VID_DST_FORMAT_RGB888; 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_cistatic inline enum dsi_cmd_dst_format dsi_get_cmd_fmt( 69862306a36Sopenharmony_ci const enum mipi_dsi_pixel_format mipi_fmt) 69962306a36Sopenharmony_ci{ 70062306a36Sopenharmony_ci switch (mipi_fmt) { 70162306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB888: return CMD_DST_FORMAT_RGB888; 70262306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666_PACKED: 70362306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB666: return CMD_DST_FORMAT_RGB666; 70462306a36Sopenharmony_ci case MIPI_DSI_FMT_RGB565: return CMD_DST_FORMAT_RGB565; 70562306a36Sopenharmony_ci default: return CMD_DST_FORMAT_RGB888; 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic void dsi_ctrl_disable(struct msm_dsi_host *msm_host) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, 0); 71262306a36Sopenharmony_ci} 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_cistatic void dsi_ctrl_enable(struct msm_dsi_host *msm_host, 71562306a36Sopenharmony_ci struct msm_dsi_phy_shared_timings *phy_shared_timings, struct msm_dsi_phy *phy) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci u32 flags = msm_host->mode_flags; 71862306a36Sopenharmony_ci enum mipi_dsi_pixel_format mipi_fmt = msm_host->format; 71962306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 72062306a36Sopenharmony_ci u32 data = 0, lane_ctrl = 0; 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci if (flags & MIPI_DSI_MODE_VIDEO) { 72362306a36Sopenharmony_ci if (flags & MIPI_DSI_MODE_VIDEO_HSE) 72462306a36Sopenharmony_ci data |= DSI_VID_CFG0_PULSE_MODE_HSA_HE; 72562306a36Sopenharmony_ci if (flags & MIPI_DSI_MODE_VIDEO_NO_HFP) 72662306a36Sopenharmony_ci data |= DSI_VID_CFG0_HFP_POWER_STOP; 72762306a36Sopenharmony_ci if (flags & MIPI_DSI_MODE_VIDEO_NO_HBP) 72862306a36Sopenharmony_ci data |= DSI_VID_CFG0_HBP_POWER_STOP; 72962306a36Sopenharmony_ci if (flags & MIPI_DSI_MODE_VIDEO_NO_HSA) 73062306a36Sopenharmony_ci data |= DSI_VID_CFG0_HSA_POWER_STOP; 73162306a36Sopenharmony_ci /* Always set low power stop mode for BLLP 73262306a36Sopenharmony_ci * to let command engine send packets 73362306a36Sopenharmony_ci */ 73462306a36Sopenharmony_ci data |= DSI_VID_CFG0_EOF_BLLP_POWER_STOP | 73562306a36Sopenharmony_ci DSI_VID_CFG0_BLLP_POWER_STOP; 73662306a36Sopenharmony_ci data |= DSI_VID_CFG0_TRAFFIC_MODE(dsi_get_traffic_mode(flags)); 73762306a36Sopenharmony_ci data |= DSI_VID_CFG0_DST_FORMAT(dsi_get_vid_fmt(mipi_fmt)); 73862306a36Sopenharmony_ci data |= DSI_VID_CFG0_VIRT_CHANNEL(msm_host->channel); 73962306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_VID_CFG0, data); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Do not swap RGB colors */ 74262306a36Sopenharmony_ci data = DSI_VID_CFG1_RGB_SWAP(SWAP_RGB); 74362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_VID_CFG1, 0); 74462306a36Sopenharmony_ci } else { 74562306a36Sopenharmony_ci /* Do not swap RGB colors */ 74662306a36Sopenharmony_ci data = DSI_CMD_CFG0_RGB_SWAP(SWAP_RGB); 74762306a36Sopenharmony_ci data |= DSI_CMD_CFG0_DST_FORMAT(dsi_get_cmd_fmt(mipi_fmt)); 74862306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_CFG0, data); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci data = DSI_CMD_CFG1_WR_MEM_START(MIPI_DCS_WRITE_MEMORY_START) | 75162306a36Sopenharmony_ci DSI_CMD_CFG1_WR_MEM_CONTINUE( 75262306a36Sopenharmony_ci MIPI_DCS_WRITE_MEMORY_CONTINUE); 75362306a36Sopenharmony_ci /* Always insert DCS command */ 75462306a36Sopenharmony_ci data |= DSI_CMD_CFG1_INSERT_DCS_COMMAND; 75562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_CFG1, data); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (msm_host->cfg_hnd->major == MSM_DSI_VER_MAJOR_6G && 75862306a36Sopenharmony_ci msm_host->cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_3) { 75962306a36Sopenharmony_ci data = dsi_read(msm_host, REG_DSI_CMD_MODE_MDP_CTRL2); 76062306a36Sopenharmony_ci data |= DSI_CMD_MODE_MDP_CTRL2_BURST_MODE; 76162306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_MODE_MDP_CTRL2, data); 76262306a36Sopenharmony_ci } 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, 76662306a36Sopenharmony_ci DSI_CMD_DMA_CTRL_FROM_FRAME_BUFFER | 76762306a36Sopenharmony_ci DSI_CMD_DMA_CTRL_LOW_POWER); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci data = 0; 77062306a36Sopenharmony_ci /* Always assume dedicated TE pin */ 77162306a36Sopenharmony_ci data |= DSI_TRIG_CTRL_TE; 77262306a36Sopenharmony_ci data |= DSI_TRIG_CTRL_MDP_TRIGGER(TRIGGER_NONE); 77362306a36Sopenharmony_ci data |= DSI_TRIG_CTRL_DMA_TRIGGER(TRIGGER_SW); 77462306a36Sopenharmony_ci data |= DSI_TRIG_CTRL_STREAM(msm_host->channel); 77562306a36Sopenharmony_ci if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 77662306a36Sopenharmony_ci (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_2)) 77762306a36Sopenharmony_ci data |= DSI_TRIG_CTRL_BLOCK_DMA_WITHIN_FRAME; 77862306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TRIG_CTRL, data); 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci data = DSI_CLKOUT_TIMING_CTRL_T_CLK_POST(phy_shared_timings->clk_post) | 78162306a36Sopenharmony_ci DSI_CLKOUT_TIMING_CTRL_T_CLK_PRE(phy_shared_timings->clk_pre); 78262306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CLKOUT_TIMING_CTRL, data); 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 78562306a36Sopenharmony_ci (cfg_hnd->minor > MSM_DSI_6G_VER_MINOR_V1_0) && 78662306a36Sopenharmony_ci phy_shared_timings->clk_pre_inc_by_2) 78762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_T_CLK_PRE_EXTEND, 78862306a36Sopenharmony_ci DSI_T_CLK_PRE_EXTEND_INC_BY_2_BYTECLK); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci data = 0; 79162306a36Sopenharmony_ci if (!(flags & MIPI_DSI_MODE_NO_EOT_PACKET)) 79262306a36Sopenharmony_ci data |= DSI_EOT_PACKET_CTRL_TX_EOT_APPEND; 79362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_EOT_PACKET_CTRL, data); 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci /* allow only ack-err-status to generate interrupt */ 79662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ERR_INT_MASK0, 0x13ff3fe0); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 1); 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci data = DSI_CTRL_CLK_EN; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci DBG("lane number=%d", msm_host->lanes); 80562306a36Sopenharmony_ci data |= ((DSI_CTRL_LANE0 << msm_host->lanes) - DSI_CTRL_LANE0); 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_LANE_SWAP_CTRL, 80862306a36Sopenharmony_ci DSI_LANE_SWAP_CTRL_DLN_SWAP_SEL(msm_host->dlane_swap)); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (!(flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) { 81162306a36Sopenharmony_ci lane_ctrl = dsi_read(msm_host, REG_DSI_LANE_CTRL); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (msm_dsi_phy_set_continuous_clock(phy, true)) 81462306a36Sopenharmony_ci lane_ctrl &= ~DSI_LANE_CTRL_HS_REQ_SEL_PHY; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_LANE_CTRL, 81762306a36Sopenharmony_ci lane_ctrl | DSI_LANE_CTRL_CLKLN_HS_FORCE_REQUEST); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci data |= DSI_CTRL_ENABLE; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, data); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci if (msm_host->cphy_mode) 82562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CPHY_MODE_CTRL, BIT(0)); 82662306a36Sopenharmony_ci} 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void dsi_update_dsc_timing(struct msm_dsi_host *msm_host, bool is_cmd_mode, u32 hdisplay) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct drm_dsc_config *dsc = msm_host->dsc; 83162306a36Sopenharmony_ci u32 reg, reg_ctrl, reg_ctrl2; 83262306a36Sopenharmony_ci u32 slice_per_intf, total_bytes_per_intf; 83362306a36Sopenharmony_ci u32 pkt_per_line; 83462306a36Sopenharmony_ci u32 eol_byte_num; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci /* first calculate dsc parameters and then program 83762306a36Sopenharmony_ci * compress mode registers 83862306a36Sopenharmony_ci */ 83962306a36Sopenharmony_ci slice_per_intf = msm_dsc_get_slices_per_intf(dsc, hdisplay); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci total_bytes_per_intf = dsc->slice_chunk_size * slice_per_intf; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci eol_byte_num = total_bytes_per_intf % 3; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci /* 84662306a36Sopenharmony_ci * Typically, pkt_per_line = slice_per_intf * slice_per_pkt. 84762306a36Sopenharmony_ci * 84862306a36Sopenharmony_ci * Since the current driver only supports slice_per_pkt = 1, 84962306a36Sopenharmony_ci * pkt_per_line will be equal to slice per intf for now. 85062306a36Sopenharmony_ci */ 85162306a36Sopenharmony_ci pkt_per_line = slice_per_intf; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (is_cmd_mode) /* packet data type */ 85462306a36Sopenharmony_ci reg = DSI_COMMAND_COMPRESSION_MODE_CTRL_STREAM0_DATATYPE(MIPI_DSI_DCS_LONG_WRITE); 85562306a36Sopenharmony_ci else 85662306a36Sopenharmony_ci reg = DSI_VIDEO_COMPRESSION_MODE_CTRL_DATATYPE(MIPI_DSI_COMPRESSED_PIXEL_STREAM); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* DSI_VIDEO_COMPRESSION_MODE & DSI_COMMAND_COMPRESSION_MODE 85962306a36Sopenharmony_ci * registers have similar offsets, so for below common code use 86062306a36Sopenharmony_ci * DSI_VIDEO_COMPRESSION_MODE_XXXX for setting bits 86162306a36Sopenharmony_ci */ 86262306a36Sopenharmony_ci reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_PKT_PER_LINE(pkt_per_line >> 1); 86362306a36Sopenharmony_ci reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EOL_BYTE_NUM(eol_byte_num); 86462306a36Sopenharmony_ci reg |= DSI_VIDEO_COMPRESSION_MODE_CTRL_EN; 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci if (is_cmd_mode) { 86762306a36Sopenharmony_ci reg_ctrl = dsi_read(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL); 86862306a36Sopenharmony_ci reg_ctrl2 = dsi_read(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2); 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci reg_ctrl &= ~0xffff; 87162306a36Sopenharmony_ci reg_ctrl |= reg; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_ci reg_ctrl2 &= ~DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH__MASK; 87462306a36Sopenharmony_ci reg_ctrl2 |= DSI_COMMAND_COMPRESSION_MODE_CTRL2_STREAM0_SLICE_WIDTH(dsc->slice_chunk_size); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL, reg_ctrl); 87762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_COMMAND_COMPRESSION_MODE_CTRL2, reg_ctrl2); 87862306a36Sopenharmony_ci } else { 87962306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_VIDEO_COMPRESSION_MODE_CTRL, reg); 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci} 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic void dsi_timing_setup(struct msm_dsi_host *msm_host, bool is_bonded_dsi) 88462306a36Sopenharmony_ci{ 88562306a36Sopenharmony_ci struct drm_display_mode *mode = msm_host->mode; 88662306a36Sopenharmony_ci u32 hs_start = 0, vs_start = 0; /* take sync start as 0 */ 88762306a36Sopenharmony_ci u32 h_total = mode->htotal; 88862306a36Sopenharmony_ci u32 v_total = mode->vtotal; 88962306a36Sopenharmony_ci u32 hs_end = mode->hsync_end - mode->hsync_start; 89062306a36Sopenharmony_ci u32 vs_end = mode->vsync_end - mode->vsync_start; 89162306a36Sopenharmony_ci u32 ha_start = h_total - mode->hsync_start; 89262306a36Sopenharmony_ci u32 ha_end = ha_start + mode->hdisplay; 89362306a36Sopenharmony_ci u32 va_start = v_total - mode->vsync_start; 89462306a36Sopenharmony_ci u32 va_end = va_start + mode->vdisplay; 89562306a36Sopenharmony_ci u32 hdisplay = mode->hdisplay; 89662306a36Sopenharmony_ci u32 wc; 89762306a36Sopenharmony_ci int ret; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci DBG(""); 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_ci /* 90262306a36Sopenharmony_ci * For bonded DSI mode, the current DRM mode has 90362306a36Sopenharmony_ci * the complete width of the panel. Since, the complete 90462306a36Sopenharmony_ci * panel is driven by two DSI controllers, the horizontal 90562306a36Sopenharmony_ci * timings have to be split between the two dsi controllers. 90662306a36Sopenharmony_ci * Adjust the DSI host timing values accordingly. 90762306a36Sopenharmony_ci */ 90862306a36Sopenharmony_ci if (is_bonded_dsi) { 90962306a36Sopenharmony_ci h_total /= 2; 91062306a36Sopenharmony_ci hs_end /= 2; 91162306a36Sopenharmony_ci ha_start /= 2; 91262306a36Sopenharmony_ci ha_end /= 2; 91362306a36Sopenharmony_ci hdisplay /= 2; 91462306a36Sopenharmony_ci } 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci if (msm_host->dsc) { 91762306a36Sopenharmony_ci struct drm_dsc_config *dsc = msm_host->dsc; 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci /* update dsc params with timing params */ 92062306a36Sopenharmony_ci if (!dsc || !mode->hdisplay || !mode->vdisplay) { 92162306a36Sopenharmony_ci pr_err("DSI: invalid input: pic_width: %d pic_height: %d\n", 92262306a36Sopenharmony_ci mode->hdisplay, mode->vdisplay); 92362306a36Sopenharmony_ci return; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci dsc->pic_width = mode->hdisplay; 92762306a36Sopenharmony_ci dsc->pic_height = mode->vdisplay; 92862306a36Sopenharmony_ci DBG("Mode %dx%d\n", dsc->pic_width, dsc->pic_height); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci /* we do the calculations for dsc parameters here so that 93162306a36Sopenharmony_ci * panel can use these parameters 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_ci ret = dsi_populate_dsc_params(msm_host, dsc); 93462306a36Sopenharmony_ci if (ret) 93562306a36Sopenharmony_ci return; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci /* Divide the display by 3 but keep back/font porch and 93862306a36Sopenharmony_ci * pulse width same 93962306a36Sopenharmony_ci */ 94062306a36Sopenharmony_ci h_total -= hdisplay; 94162306a36Sopenharmony_ci hdisplay = DIV_ROUND_UP(msm_dsc_get_bytes_per_line(msm_host->dsc), 3); 94262306a36Sopenharmony_ci h_total += hdisplay; 94362306a36Sopenharmony_ci ha_end = ha_start + hdisplay; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci if (msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) { 94762306a36Sopenharmony_ci if (msm_host->dsc) 94862306a36Sopenharmony_ci dsi_update_dsc_timing(msm_host, false, mode->hdisplay); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACTIVE_H, 95162306a36Sopenharmony_ci DSI_ACTIVE_H_START(ha_start) | 95262306a36Sopenharmony_ci DSI_ACTIVE_H_END(ha_end)); 95362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACTIVE_V, 95462306a36Sopenharmony_ci DSI_ACTIVE_V_START(va_start) | 95562306a36Sopenharmony_ci DSI_ACTIVE_V_END(va_end)); 95662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TOTAL, 95762306a36Sopenharmony_ci DSI_TOTAL_H_TOTAL(h_total - 1) | 95862306a36Sopenharmony_ci DSI_TOTAL_V_TOTAL(v_total - 1)); 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACTIVE_HSYNC, 96162306a36Sopenharmony_ci DSI_ACTIVE_HSYNC_START(hs_start) | 96262306a36Sopenharmony_ci DSI_ACTIVE_HSYNC_END(hs_end)); 96362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACTIVE_VSYNC_HPOS, 0); 96462306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACTIVE_VSYNC_VPOS, 96562306a36Sopenharmony_ci DSI_ACTIVE_VSYNC_VPOS_START(vs_start) | 96662306a36Sopenharmony_ci DSI_ACTIVE_VSYNC_VPOS_END(vs_end)); 96762306a36Sopenharmony_ci } else { /* command mode */ 96862306a36Sopenharmony_ci if (msm_host->dsc) 96962306a36Sopenharmony_ci dsi_update_dsc_timing(msm_host, true, mode->hdisplay); 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci /* image data and 1 byte write_memory_start cmd */ 97262306a36Sopenharmony_ci if (!msm_host->dsc) 97362306a36Sopenharmony_ci wc = hdisplay * dsi_get_bpp(msm_host->format) / 8 + 1; 97462306a36Sopenharmony_ci else 97562306a36Sopenharmony_ci /* 97662306a36Sopenharmony_ci * When DSC is enabled, WC = slice_chunk_size * slice_per_pkt + 1. 97762306a36Sopenharmony_ci * Currently, the driver only supports default value of slice_per_pkt = 1 97862306a36Sopenharmony_ci * 97962306a36Sopenharmony_ci * TODO: Expand mipi_dsi_device struct to hold slice_per_pkt info 98062306a36Sopenharmony_ci * and adjust DSC math to account for slice_per_pkt. 98162306a36Sopenharmony_ci */ 98262306a36Sopenharmony_ci wc = msm_host->dsc->slice_chunk_size + 1; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_CTRL, 98562306a36Sopenharmony_ci DSI_CMD_MDP_STREAM0_CTRL_WORD_COUNT(wc) | 98662306a36Sopenharmony_ci DSI_CMD_MDP_STREAM0_CTRL_VIRTUAL_CHANNEL( 98762306a36Sopenharmony_ci msm_host->channel) | 98862306a36Sopenharmony_ci DSI_CMD_MDP_STREAM0_CTRL_DATA_TYPE( 98962306a36Sopenharmony_ci MIPI_DSI_DCS_LONG_WRITE)); 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_MDP_STREAM0_TOTAL, 99262306a36Sopenharmony_ci DSI_CMD_MDP_STREAM0_TOTAL_H_TOTAL(hdisplay) | 99362306a36Sopenharmony_ci DSI_CMD_MDP_STREAM0_TOTAL_V_TOTAL(mode->vdisplay)); 99462306a36Sopenharmony_ci } 99562306a36Sopenharmony_ci} 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_cistatic void dsi_sw_reset(struct msm_dsi_host *msm_host) 99862306a36Sopenharmony_ci{ 99962306a36Sopenharmony_ci u32 ctrl; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci ctrl = dsi_read(msm_host, REG_DSI_CTRL); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (ctrl & DSI_CTRL_ENABLE) { 100462306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, ctrl & ~DSI_CTRL_ENABLE); 100562306a36Sopenharmony_ci /* 100662306a36Sopenharmony_ci * dsi controller need to be disabled before 100762306a36Sopenharmony_ci * clocks turned on 100862306a36Sopenharmony_ci */ 100962306a36Sopenharmony_ci wmb(); 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CLK_CTRL, DSI_CLK_CTRL_ENABLE_CLKS); 101362306a36Sopenharmony_ci wmb(); /* clocks need to be enabled before reset */ 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci /* dsi controller can only be reset while clocks are running */ 101662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_RESET, 1); 101762306a36Sopenharmony_ci msleep(DSI_RESET_TOGGLE_DELAY_MS); /* make sure reset happen */ 101862306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_RESET, 0); 101962306a36Sopenharmony_ci wmb(); /* controller out of reset */ 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (ctrl & DSI_CTRL_ENABLE) { 102262306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, ctrl); 102362306a36Sopenharmony_ci wmb(); /* make sure dsi controller enabled again */ 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_cistatic void dsi_op_mode_config(struct msm_dsi_host *msm_host, 102862306a36Sopenharmony_ci bool video_mode, bool enable) 102962306a36Sopenharmony_ci{ 103062306a36Sopenharmony_ci u32 dsi_ctrl; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci dsi_ctrl = dsi_read(msm_host, REG_DSI_CTRL); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (!enable) { 103562306a36Sopenharmony_ci dsi_ctrl &= ~(DSI_CTRL_ENABLE | DSI_CTRL_VID_MODE_EN | 103662306a36Sopenharmony_ci DSI_CTRL_CMD_MODE_EN); 103762306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_MDP_DONE | 103862306a36Sopenharmony_ci DSI_IRQ_MASK_VIDEO_DONE, 0); 103962306a36Sopenharmony_ci } else { 104062306a36Sopenharmony_ci if (video_mode) { 104162306a36Sopenharmony_ci dsi_ctrl |= DSI_CTRL_VID_MODE_EN; 104262306a36Sopenharmony_ci } else { /* command mode */ 104362306a36Sopenharmony_ci dsi_ctrl |= DSI_CTRL_CMD_MODE_EN; 104462306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_MDP_DONE, 1); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci dsi_ctrl |= DSI_CTRL_ENABLE; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, dsi_ctrl); 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_cistatic void dsi_set_tx_power_mode(int mode, struct msm_dsi_host *msm_host) 105362306a36Sopenharmony_ci{ 105462306a36Sopenharmony_ci u32 data; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci data = dsi_read(msm_host, REG_DSI_CMD_DMA_CTRL); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (mode == 0) 105962306a36Sopenharmony_ci data &= ~DSI_CMD_DMA_CTRL_LOW_POWER; 106062306a36Sopenharmony_ci else 106162306a36Sopenharmony_ci data |= DSI_CMD_DMA_CTRL_LOW_POWER; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CMD_DMA_CTRL, data); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic void dsi_wait4video_done(struct msm_dsi_host *msm_host) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci u32 ret = 0; 106962306a36Sopenharmony_ci struct device *dev = &msm_host->pdev->dev; 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 1); 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci reinit_completion(&msm_host->video_comp); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci ret = wait_for_completion_timeout(&msm_host->video_comp, 107662306a36Sopenharmony_ci msecs_to_jiffies(70)); 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci if (ret == 0) 107962306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "wait for video done timed out\n"); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_VIDEO_DONE, 0); 108262306a36Sopenharmony_ci} 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_cistatic void dsi_wait4video_eng_busy(struct msm_dsi_host *msm_host) 108562306a36Sopenharmony_ci{ 108662306a36Sopenharmony_ci u32 data; 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO)) 108962306a36Sopenharmony_ci return; 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci data = dsi_read(msm_host, REG_DSI_STATUS0); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci /* if video mode engine is not busy, its because 109462306a36Sopenharmony_ci * either timing engine was not turned on or the 109562306a36Sopenharmony_ci * DSI controller has finished transmitting the video 109662306a36Sopenharmony_ci * data already, so no need to wait in those cases 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_ci if (!(data & DSI_STATUS0_VIDEO_MODE_ENGINE_BUSY)) 109962306a36Sopenharmony_ci return; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci if (msm_host->power_on && msm_host->enabled) { 110262306a36Sopenharmony_ci dsi_wait4video_done(msm_host); 110362306a36Sopenharmony_ci /* delay 4 ms to skip BLLP */ 110462306a36Sopenharmony_ci usleep_range(2000, 4000); 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci} 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ciint dsi_tx_buf_alloc_6g(struct msm_dsi_host *msm_host, int size) 110962306a36Sopenharmony_ci{ 111062306a36Sopenharmony_ci struct drm_device *dev = msm_host->dev; 111162306a36Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 111262306a36Sopenharmony_ci uint64_t iova; 111362306a36Sopenharmony_ci u8 *data; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci msm_host->aspace = msm_gem_address_space_get(priv->kms->aspace); 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci data = msm_gem_kernel_new(dev, size, MSM_BO_WC, 111862306a36Sopenharmony_ci msm_host->aspace, 111962306a36Sopenharmony_ci &msm_host->tx_gem_obj, &iova); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (IS_ERR(data)) { 112262306a36Sopenharmony_ci msm_host->tx_gem_obj = NULL; 112362306a36Sopenharmony_ci return PTR_ERR(data); 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci msm_gem_object_set_name(msm_host->tx_gem_obj, "tx_gem"); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci msm_host->tx_size = msm_host->tx_gem_obj->size; 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci return 0; 113162306a36Sopenharmony_ci} 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ciint dsi_tx_buf_alloc_v2(struct msm_dsi_host *msm_host, int size) 113462306a36Sopenharmony_ci{ 113562306a36Sopenharmony_ci struct drm_device *dev = msm_host->dev; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci msm_host->tx_buf = dma_alloc_coherent(dev->dev, size, 113862306a36Sopenharmony_ci &msm_host->tx_buf_paddr, GFP_KERNEL); 113962306a36Sopenharmony_ci if (!msm_host->tx_buf) 114062306a36Sopenharmony_ci return -ENOMEM; 114162306a36Sopenharmony_ci 114262306a36Sopenharmony_ci msm_host->tx_size = size; 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_ci return 0; 114562306a36Sopenharmony_ci} 114662306a36Sopenharmony_ci 114762306a36Sopenharmony_civoid msm_dsi_tx_buf_free(struct mipi_dsi_host *host) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 115062306a36Sopenharmony_ci struct drm_device *dev = msm_host->dev; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* 115362306a36Sopenharmony_ci * This is possible if we're tearing down before we've had a chance to 115462306a36Sopenharmony_ci * fully initialize. A very real possibility if our probe is deferred, 115562306a36Sopenharmony_ci * in which case we'll hit msm_dsi_host_destroy() without having run 115662306a36Sopenharmony_ci * through the dsi_tx_buf_alloc(). 115762306a36Sopenharmony_ci */ 115862306a36Sopenharmony_ci if (!dev) 115962306a36Sopenharmony_ci return; 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci if (msm_host->tx_gem_obj) { 116262306a36Sopenharmony_ci msm_gem_kernel_put(msm_host->tx_gem_obj, msm_host->aspace); 116362306a36Sopenharmony_ci msm_gem_address_space_put(msm_host->aspace); 116462306a36Sopenharmony_ci msm_host->tx_gem_obj = NULL; 116562306a36Sopenharmony_ci msm_host->aspace = NULL; 116662306a36Sopenharmony_ci } 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci if (msm_host->tx_buf) 116962306a36Sopenharmony_ci dma_free_coherent(dev->dev, msm_host->tx_size, msm_host->tx_buf, 117062306a36Sopenharmony_ci msm_host->tx_buf_paddr); 117162306a36Sopenharmony_ci} 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_civoid *dsi_tx_buf_get_6g(struct msm_dsi_host *msm_host) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci return msm_gem_get_vaddr(msm_host->tx_gem_obj); 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_civoid *dsi_tx_buf_get_v2(struct msm_dsi_host *msm_host) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci return msm_host->tx_buf; 118162306a36Sopenharmony_ci} 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_civoid dsi_tx_buf_put_6g(struct msm_dsi_host *msm_host) 118462306a36Sopenharmony_ci{ 118562306a36Sopenharmony_ci msm_gem_put_vaddr(msm_host->tx_gem_obj); 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci/* 118962306a36Sopenharmony_ci * prepare cmd buffer to be txed 119062306a36Sopenharmony_ci */ 119162306a36Sopenharmony_cistatic int dsi_cmd_dma_add(struct msm_dsi_host *msm_host, 119262306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 119362306a36Sopenharmony_ci{ 119462306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 119562306a36Sopenharmony_ci struct mipi_dsi_packet packet; 119662306a36Sopenharmony_ci int len; 119762306a36Sopenharmony_ci int ret; 119862306a36Sopenharmony_ci u8 *data; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci ret = mipi_dsi_create_packet(&packet, msg); 120162306a36Sopenharmony_ci if (ret) { 120262306a36Sopenharmony_ci pr_err("%s: create packet failed, %d\n", __func__, ret); 120362306a36Sopenharmony_ci return ret; 120462306a36Sopenharmony_ci } 120562306a36Sopenharmony_ci len = (packet.size + 3) & (~0x3); 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (len > msm_host->tx_size) { 120862306a36Sopenharmony_ci pr_err("%s: packet size is too big\n", __func__); 120962306a36Sopenharmony_ci return -EINVAL; 121062306a36Sopenharmony_ci } 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci data = cfg_hnd->ops->tx_buf_get(msm_host); 121362306a36Sopenharmony_ci if (IS_ERR(data)) { 121462306a36Sopenharmony_ci ret = PTR_ERR(data); 121562306a36Sopenharmony_ci pr_err("%s: get vaddr failed, %d\n", __func__, ret); 121662306a36Sopenharmony_ci return ret; 121762306a36Sopenharmony_ci } 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci /* MSM specific command format in memory */ 122062306a36Sopenharmony_ci data[0] = packet.header[1]; 122162306a36Sopenharmony_ci data[1] = packet.header[2]; 122262306a36Sopenharmony_ci data[2] = packet.header[0]; 122362306a36Sopenharmony_ci data[3] = BIT(7); /* Last packet */ 122462306a36Sopenharmony_ci if (mipi_dsi_packet_format_is_long(msg->type)) 122562306a36Sopenharmony_ci data[3] |= BIT(6); 122662306a36Sopenharmony_ci if (msg->rx_buf && msg->rx_len) 122762306a36Sopenharmony_ci data[3] |= BIT(5); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* Long packet */ 123062306a36Sopenharmony_ci if (packet.payload && packet.payload_length) 123162306a36Sopenharmony_ci memcpy(data + 4, packet.payload, packet.payload_length); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* Append 0xff to the end */ 123462306a36Sopenharmony_ci if (packet.size < len) 123562306a36Sopenharmony_ci memset(data + packet.size, 0xff, len - packet.size); 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci if (cfg_hnd->ops->tx_buf_put) 123862306a36Sopenharmony_ci cfg_hnd->ops->tx_buf_put(msm_host); 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_ci return len; 124162306a36Sopenharmony_ci} 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci/* 124462306a36Sopenharmony_ci * dsi_short_read1_resp: 1 parameter 124562306a36Sopenharmony_ci */ 124662306a36Sopenharmony_cistatic int dsi_short_read1_resp(u8 *buf, const struct mipi_dsi_msg *msg) 124762306a36Sopenharmony_ci{ 124862306a36Sopenharmony_ci u8 *data = msg->rx_buf; 124962306a36Sopenharmony_ci if (data && (msg->rx_len >= 1)) { 125062306a36Sopenharmony_ci *data = buf[1]; /* strip out dcs type */ 125162306a36Sopenharmony_ci return 1; 125262306a36Sopenharmony_ci } else { 125362306a36Sopenharmony_ci pr_err("%s: read data does not match with rx_buf len %zu\n", 125462306a36Sopenharmony_ci __func__, msg->rx_len); 125562306a36Sopenharmony_ci return -EINVAL; 125662306a36Sopenharmony_ci } 125762306a36Sopenharmony_ci} 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci/* 126062306a36Sopenharmony_ci * dsi_short_read2_resp: 2 parameter 126162306a36Sopenharmony_ci */ 126262306a36Sopenharmony_cistatic int dsi_short_read2_resp(u8 *buf, const struct mipi_dsi_msg *msg) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci u8 *data = msg->rx_buf; 126562306a36Sopenharmony_ci if (data && (msg->rx_len >= 2)) { 126662306a36Sopenharmony_ci data[0] = buf[1]; /* strip out dcs type */ 126762306a36Sopenharmony_ci data[1] = buf[2]; 126862306a36Sopenharmony_ci return 2; 126962306a36Sopenharmony_ci } else { 127062306a36Sopenharmony_ci pr_err("%s: read data does not match with rx_buf len %zu\n", 127162306a36Sopenharmony_ci __func__, msg->rx_len); 127262306a36Sopenharmony_ci return -EINVAL; 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci} 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_cistatic int dsi_long_read_resp(u8 *buf, const struct mipi_dsi_msg *msg) 127762306a36Sopenharmony_ci{ 127862306a36Sopenharmony_ci /* strip out 4 byte dcs header */ 127962306a36Sopenharmony_ci if (msg->rx_buf && msg->rx_len) 128062306a36Sopenharmony_ci memcpy(msg->rx_buf, buf + 4, msg->rx_len); 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci return msg->rx_len; 128362306a36Sopenharmony_ci} 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ciint dsi_dma_base_get_6g(struct msm_dsi_host *msm_host, uint64_t *dma_base) 128662306a36Sopenharmony_ci{ 128762306a36Sopenharmony_ci struct drm_device *dev = msm_host->dev; 128862306a36Sopenharmony_ci struct msm_drm_private *priv = dev->dev_private; 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci if (!dma_base) 129162306a36Sopenharmony_ci return -EINVAL; 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci return msm_gem_get_and_pin_iova(msm_host->tx_gem_obj, 129462306a36Sopenharmony_ci priv->kms->aspace, dma_base); 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ciint dsi_dma_base_get_v2(struct msm_dsi_host *msm_host, uint64_t *dma_base) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci if (!dma_base) 130062306a36Sopenharmony_ci return -EINVAL; 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci *dma_base = msm_host->tx_buf_paddr; 130362306a36Sopenharmony_ci return 0; 130462306a36Sopenharmony_ci} 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_cistatic int dsi_cmd_dma_tx(struct msm_dsi_host *msm_host, int len) 130762306a36Sopenharmony_ci{ 130862306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 130962306a36Sopenharmony_ci int ret; 131062306a36Sopenharmony_ci uint64_t dma_base; 131162306a36Sopenharmony_ci bool triggered; 131262306a36Sopenharmony_ci 131362306a36Sopenharmony_ci ret = cfg_hnd->ops->dma_base_get(msm_host, &dma_base); 131462306a36Sopenharmony_ci if (ret) { 131562306a36Sopenharmony_ci pr_err("%s: failed to get iova: %d\n", __func__, ret); 131662306a36Sopenharmony_ci return ret; 131762306a36Sopenharmony_ci } 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci reinit_completion(&msm_host->dma_comp); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci dsi_wait4video_eng_busy(msm_host); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci triggered = msm_dsi_manager_cmd_xfer_trigger( 132462306a36Sopenharmony_ci msm_host->id, dma_base, len); 132562306a36Sopenharmony_ci if (triggered) { 132662306a36Sopenharmony_ci ret = wait_for_completion_timeout(&msm_host->dma_comp, 132762306a36Sopenharmony_ci msecs_to_jiffies(200)); 132862306a36Sopenharmony_ci DBG("ret=%d", ret); 132962306a36Sopenharmony_ci if (ret == 0) 133062306a36Sopenharmony_ci ret = -ETIMEDOUT; 133162306a36Sopenharmony_ci else 133262306a36Sopenharmony_ci ret = len; 133362306a36Sopenharmony_ci } else 133462306a36Sopenharmony_ci ret = len; 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci return ret; 133762306a36Sopenharmony_ci} 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_cistatic int dsi_cmd_dma_rx(struct msm_dsi_host *msm_host, 134062306a36Sopenharmony_ci u8 *buf, int rx_byte, int pkt_size) 134162306a36Sopenharmony_ci{ 134262306a36Sopenharmony_ci u32 *temp, data; 134362306a36Sopenharmony_ci int i, j = 0, cnt; 134462306a36Sopenharmony_ci u32 read_cnt; 134562306a36Sopenharmony_ci u8 reg[16]; 134662306a36Sopenharmony_ci int repeated_bytes = 0; 134762306a36Sopenharmony_ci int buf_offset = buf - msm_host->rx_buf; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci temp = (u32 *)reg; 135062306a36Sopenharmony_ci cnt = (rx_byte + 3) >> 2; 135162306a36Sopenharmony_ci if (cnt > 4) 135262306a36Sopenharmony_ci cnt = 4; /* 4 x 32 bits registers only */ 135362306a36Sopenharmony_ci 135462306a36Sopenharmony_ci if (rx_byte == 4) 135562306a36Sopenharmony_ci read_cnt = 4; 135662306a36Sopenharmony_ci else 135762306a36Sopenharmony_ci read_cnt = pkt_size + 6; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci /* 136062306a36Sopenharmony_ci * In case of multiple reads from the panel, after the first read, there 136162306a36Sopenharmony_ci * is possibility that there are some bytes in the payload repeating in 136262306a36Sopenharmony_ci * the RDBK_DATA registers. Since we read all the parameters from the 136362306a36Sopenharmony_ci * panel right from the first byte for every pass. We need to skip the 136462306a36Sopenharmony_ci * repeating bytes and then append the new parameters to the rx buffer. 136562306a36Sopenharmony_ci */ 136662306a36Sopenharmony_ci if (read_cnt > 16) { 136762306a36Sopenharmony_ci int bytes_shifted; 136862306a36Sopenharmony_ci /* Any data more than 16 bytes will be shifted out. 136962306a36Sopenharmony_ci * The temp read buffer should already contain these bytes. 137062306a36Sopenharmony_ci * The remaining bytes in read buffer are the repeated bytes. 137162306a36Sopenharmony_ci */ 137262306a36Sopenharmony_ci bytes_shifted = read_cnt - 16; 137362306a36Sopenharmony_ci repeated_bytes = buf_offset - bytes_shifted; 137462306a36Sopenharmony_ci } 137562306a36Sopenharmony_ci 137662306a36Sopenharmony_ci for (i = cnt - 1; i >= 0; i--) { 137762306a36Sopenharmony_ci data = dsi_read(msm_host, REG_DSI_RDBK_DATA(i)); 137862306a36Sopenharmony_ci *temp++ = ntohl(data); /* to host byte order */ 137962306a36Sopenharmony_ci DBG("data = 0x%x and ntohl(data) = 0x%x", data, ntohl(data)); 138062306a36Sopenharmony_ci } 138162306a36Sopenharmony_ci 138262306a36Sopenharmony_ci for (i = repeated_bytes; i < 16; i++) 138362306a36Sopenharmony_ci buf[j++] = reg[i]; 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci return j; 138662306a36Sopenharmony_ci} 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_cistatic int dsi_cmds2buf_tx(struct msm_dsi_host *msm_host, 138962306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci int len, ret; 139262306a36Sopenharmony_ci int bllp_len = msm_host->mode->hdisplay * 139362306a36Sopenharmony_ci dsi_get_bpp(msm_host->format) / 8; 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci len = dsi_cmd_dma_add(msm_host, msg); 139662306a36Sopenharmony_ci if (len < 0) { 139762306a36Sopenharmony_ci pr_err("%s: failed to add cmd type = 0x%x\n", 139862306a36Sopenharmony_ci __func__, msg->type); 139962306a36Sopenharmony_ci return len; 140062306a36Sopenharmony_ci } 140162306a36Sopenharmony_ci 140262306a36Sopenharmony_ci /* for video mode, do not send cmds more than 140362306a36Sopenharmony_ci * one pixel line, since it only transmit it 140462306a36Sopenharmony_ci * during BLLP. 140562306a36Sopenharmony_ci */ 140662306a36Sopenharmony_ci /* TODO: if the command is sent in LP mode, the bit rate is only 140762306a36Sopenharmony_ci * half of esc clk rate. In this case, if the video is already 140862306a36Sopenharmony_ci * actively streaming, we need to check more carefully if the 140962306a36Sopenharmony_ci * command can be fit into one BLLP. 141062306a36Sopenharmony_ci */ 141162306a36Sopenharmony_ci if ((msm_host->mode_flags & MIPI_DSI_MODE_VIDEO) && (len > bllp_len)) { 141262306a36Sopenharmony_ci pr_err("%s: cmd cannot fit into BLLP period, len=%d\n", 141362306a36Sopenharmony_ci __func__, len); 141462306a36Sopenharmony_ci return -EINVAL; 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci ret = dsi_cmd_dma_tx(msm_host, len); 141862306a36Sopenharmony_ci if (ret < 0) { 141962306a36Sopenharmony_ci pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, len=%d, ret=%d\n", 142062306a36Sopenharmony_ci __func__, msg->type, (*(u8 *)(msg->tx_buf)), len, ret); 142162306a36Sopenharmony_ci return ret; 142262306a36Sopenharmony_ci } else if (ret < len) { 142362306a36Sopenharmony_ci pr_err("%s: cmd dma tx failed, type=0x%x, data0=0x%x, ret=%d len=%d\n", 142462306a36Sopenharmony_ci __func__, msg->type, (*(u8 *)(msg->tx_buf)), ret, len); 142562306a36Sopenharmony_ci return -EIO; 142662306a36Sopenharmony_ci } 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci return len; 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic void dsi_err_worker(struct work_struct *work) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct msm_dsi_host *msm_host = 143462306a36Sopenharmony_ci container_of(work, struct msm_dsi_host, err_work); 143562306a36Sopenharmony_ci u32 status = msm_host->err_work_state; 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci pr_err_ratelimited("%s: status=%x\n", __func__, status); 143862306a36Sopenharmony_ci if (status & DSI_ERR_STATE_MDP_FIFO_UNDERFLOW) 143962306a36Sopenharmony_ci dsi_sw_reset(msm_host); 144062306a36Sopenharmony_ci 144162306a36Sopenharmony_ci /* It is safe to clear here because error irq is disabled. */ 144262306a36Sopenharmony_ci msm_host->err_work_state = 0; 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci /* enable dsi error interrupt */ 144562306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 1); 144662306a36Sopenharmony_ci} 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_cistatic void dsi_ack_err_status(struct msm_dsi_host *msm_host) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci u32 status; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci status = dsi_read(msm_host, REG_DSI_ACK_ERR_STATUS); 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci if (status) { 145562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACK_ERR_STATUS, status); 145662306a36Sopenharmony_ci /* Writing of an extra 0 needed to clear error bits */ 145762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_ACK_ERR_STATUS, 0); 145862306a36Sopenharmony_ci msm_host->err_work_state |= DSI_ERR_STATE_ACK; 145962306a36Sopenharmony_ci } 146062306a36Sopenharmony_ci} 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_cistatic void dsi_timeout_status(struct msm_dsi_host *msm_host) 146362306a36Sopenharmony_ci{ 146462306a36Sopenharmony_ci u32 status; 146562306a36Sopenharmony_ci 146662306a36Sopenharmony_ci status = dsi_read(msm_host, REG_DSI_TIMEOUT_STATUS); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci if (status) { 146962306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TIMEOUT_STATUS, status); 147062306a36Sopenharmony_ci msm_host->err_work_state |= DSI_ERR_STATE_TIMEOUT; 147162306a36Sopenharmony_ci } 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_cistatic void dsi_dln0_phy_err(struct msm_dsi_host *msm_host) 147562306a36Sopenharmony_ci{ 147662306a36Sopenharmony_ci u32 status; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci status = dsi_read(msm_host, REG_DSI_DLN0_PHY_ERR); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci if (status & (DSI_DLN0_PHY_ERR_DLN0_ERR_ESC | 148162306a36Sopenharmony_ci DSI_DLN0_PHY_ERR_DLN0_ERR_SYNC_ESC | 148262306a36Sopenharmony_ci DSI_DLN0_PHY_ERR_DLN0_ERR_CONTROL | 148362306a36Sopenharmony_ci DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP0 | 148462306a36Sopenharmony_ci DSI_DLN0_PHY_ERR_DLN0_ERR_CONTENTION_LP1)) { 148562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_DLN0_PHY_ERR, status); 148662306a36Sopenharmony_ci msm_host->err_work_state |= DSI_ERR_STATE_DLN0_PHY; 148762306a36Sopenharmony_ci } 148862306a36Sopenharmony_ci} 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_cistatic void dsi_fifo_status(struct msm_dsi_host *msm_host) 149162306a36Sopenharmony_ci{ 149262306a36Sopenharmony_ci u32 status; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci status = dsi_read(msm_host, REG_DSI_FIFO_STATUS); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* fifo underflow, overflow */ 149762306a36Sopenharmony_ci if (status) { 149862306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_FIFO_STATUS, status); 149962306a36Sopenharmony_ci msm_host->err_work_state |= DSI_ERR_STATE_FIFO; 150062306a36Sopenharmony_ci if (status & DSI_FIFO_STATUS_CMD_MDP_FIFO_UNDERFLOW) 150162306a36Sopenharmony_ci msm_host->err_work_state |= 150262306a36Sopenharmony_ci DSI_ERR_STATE_MDP_FIFO_UNDERFLOW; 150362306a36Sopenharmony_ci } 150462306a36Sopenharmony_ci} 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_cistatic void dsi_status(struct msm_dsi_host *msm_host) 150762306a36Sopenharmony_ci{ 150862306a36Sopenharmony_ci u32 status; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci status = dsi_read(msm_host, REG_DSI_STATUS0); 151162306a36Sopenharmony_ci 151262306a36Sopenharmony_ci if (status & DSI_STATUS0_INTERLEAVE_OP_CONTENTION) { 151362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_STATUS0, status); 151462306a36Sopenharmony_ci msm_host->err_work_state |= 151562306a36Sopenharmony_ci DSI_ERR_STATE_INTERLEAVE_OP_CONTENTION; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci} 151862306a36Sopenharmony_ci 151962306a36Sopenharmony_cistatic void dsi_clk_status(struct msm_dsi_host *msm_host) 152062306a36Sopenharmony_ci{ 152162306a36Sopenharmony_ci u32 status; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci status = dsi_read(msm_host, REG_DSI_CLK_STATUS); 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci if (status & DSI_CLK_STATUS_PLL_UNLOCKED) { 152662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CLK_STATUS, status); 152762306a36Sopenharmony_ci msm_host->err_work_state |= DSI_ERR_STATE_PLL_UNLOCKED; 152862306a36Sopenharmony_ci } 152962306a36Sopenharmony_ci} 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_cistatic void dsi_error(struct msm_dsi_host *msm_host) 153262306a36Sopenharmony_ci{ 153362306a36Sopenharmony_ci /* disable dsi error interrupt */ 153462306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_ERROR, 0); 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci dsi_clk_status(msm_host); 153762306a36Sopenharmony_ci dsi_fifo_status(msm_host); 153862306a36Sopenharmony_ci dsi_ack_err_status(msm_host); 153962306a36Sopenharmony_ci dsi_timeout_status(msm_host); 154062306a36Sopenharmony_ci dsi_status(msm_host); 154162306a36Sopenharmony_ci dsi_dln0_phy_err(msm_host); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci queue_work(msm_host->workqueue, &msm_host->err_work); 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic irqreturn_t dsi_host_irq(int irq, void *ptr) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct msm_dsi_host *msm_host = ptr; 154962306a36Sopenharmony_ci u32 isr; 155062306a36Sopenharmony_ci unsigned long flags; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci if (!msm_host->ctrl_base) 155362306a36Sopenharmony_ci return IRQ_HANDLED; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci spin_lock_irqsave(&msm_host->intr_lock, flags); 155662306a36Sopenharmony_ci isr = dsi_read(msm_host, REG_DSI_INTR_CTRL); 155762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_INTR_CTRL, isr); 155862306a36Sopenharmony_ci spin_unlock_irqrestore(&msm_host->intr_lock, flags); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci DBG("isr=0x%x, id=%d", isr, msm_host->id); 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci if (isr & DSI_IRQ_ERROR) 156362306a36Sopenharmony_ci dsi_error(msm_host); 156462306a36Sopenharmony_ci 156562306a36Sopenharmony_ci if (isr & DSI_IRQ_VIDEO_DONE) 156662306a36Sopenharmony_ci complete(&msm_host->video_comp); 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci if (isr & DSI_IRQ_CMD_DMA_DONE) 156962306a36Sopenharmony_ci complete(&msm_host->dma_comp); 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci return IRQ_HANDLED; 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_cistatic int dsi_host_init_panel_gpios(struct msm_dsi_host *msm_host, 157562306a36Sopenharmony_ci struct device *panel_device) 157662306a36Sopenharmony_ci{ 157762306a36Sopenharmony_ci msm_host->disp_en_gpio = devm_gpiod_get_optional(panel_device, 157862306a36Sopenharmony_ci "disp-enable", 157962306a36Sopenharmony_ci GPIOD_OUT_LOW); 158062306a36Sopenharmony_ci if (IS_ERR(msm_host->disp_en_gpio)) { 158162306a36Sopenharmony_ci DBG("cannot get disp-enable-gpios %ld", 158262306a36Sopenharmony_ci PTR_ERR(msm_host->disp_en_gpio)); 158362306a36Sopenharmony_ci return PTR_ERR(msm_host->disp_en_gpio); 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci msm_host->te_gpio = devm_gpiod_get_optional(panel_device, "disp-te", 158762306a36Sopenharmony_ci GPIOD_IN); 158862306a36Sopenharmony_ci if (IS_ERR(msm_host->te_gpio)) { 158962306a36Sopenharmony_ci DBG("cannot get disp-te-gpios %ld", PTR_ERR(msm_host->te_gpio)); 159062306a36Sopenharmony_ci return PTR_ERR(msm_host->te_gpio); 159162306a36Sopenharmony_ci } 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci return 0; 159462306a36Sopenharmony_ci} 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic int dsi_host_attach(struct mipi_dsi_host *host, 159762306a36Sopenharmony_ci struct mipi_dsi_device *dsi) 159862306a36Sopenharmony_ci{ 159962306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 160062306a36Sopenharmony_ci int ret; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci if (dsi->lanes > msm_host->num_data_lanes) 160362306a36Sopenharmony_ci return -EINVAL; 160462306a36Sopenharmony_ci 160562306a36Sopenharmony_ci msm_host->channel = dsi->channel; 160662306a36Sopenharmony_ci msm_host->lanes = dsi->lanes; 160762306a36Sopenharmony_ci msm_host->format = dsi->format; 160862306a36Sopenharmony_ci msm_host->mode_flags = dsi->mode_flags; 160962306a36Sopenharmony_ci if (dsi->dsc) 161062306a36Sopenharmony_ci msm_host->dsc = dsi->dsc; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci /* Some gpios defined in panel DT need to be controlled by host */ 161362306a36Sopenharmony_ci ret = dsi_host_init_panel_gpios(msm_host, &dsi->dev); 161462306a36Sopenharmony_ci if (ret) 161562306a36Sopenharmony_ci return ret; 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci ret = dsi_dev_attach(msm_host->pdev); 161862306a36Sopenharmony_ci if (ret) 161962306a36Sopenharmony_ci return ret; 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_ci DBG("id=%d", msm_host->id); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci return 0; 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic int dsi_host_detach(struct mipi_dsi_host *host, 162762306a36Sopenharmony_ci struct mipi_dsi_device *dsi) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci dsi_dev_detach(msm_host->pdev); 163262306a36Sopenharmony_ci 163362306a36Sopenharmony_ci DBG("id=%d", msm_host->id); 163462306a36Sopenharmony_ci 163562306a36Sopenharmony_ci return 0; 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cistatic ssize_t dsi_host_transfer(struct mipi_dsi_host *host, 163962306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 164262306a36Sopenharmony_ci int ret; 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_ci if (!msg || !msm_host->power_on) 164562306a36Sopenharmony_ci return -EINVAL; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci mutex_lock(&msm_host->cmd_mutex); 164862306a36Sopenharmony_ci ret = msm_dsi_manager_cmd_xfer(msm_host->id, msg); 164962306a36Sopenharmony_ci mutex_unlock(&msm_host->cmd_mutex); 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci return ret; 165262306a36Sopenharmony_ci} 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_cistatic const struct mipi_dsi_host_ops dsi_host_ops = { 165562306a36Sopenharmony_ci .attach = dsi_host_attach, 165662306a36Sopenharmony_ci .detach = dsi_host_detach, 165762306a36Sopenharmony_ci .transfer = dsi_host_transfer, 165862306a36Sopenharmony_ci}; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci/* 166162306a36Sopenharmony_ci * List of supported physical to logical lane mappings. 166262306a36Sopenharmony_ci * For example, the 2nd entry represents the following mapping: 166362306a36Sopenharmony_ci * 166462306a36Sopenharmony_ci * "3012": Logic 3->Phys 0; Logic 0->Phys 1; Logic 1->Phys 2; Logic 2->Phys 3; 166562306a36Sopenharmony_ci */ 166662306a36Sopenharmony_cistatic const int supported_data_lane_swaps[][4] = { 166762306a36Sopenharmony_ci { 0, 1, 2, 3 }, 166862306a36Sopenharmony_ci { 3, 0, 1, 2 }, 166962306a36Sopenharmony_ci { 2, 3, 0, 1 }, 167062306a36Sopenharmony_ci { 1, 2, 3, 0 }, 167162306a36Sopenharmony_ci { 0, 3, 2, 1 }, 167262306a36Sopenharmony_ci { 1, 0, 3, 2 }, 167362306a36Sopenharmony_ci { 2, 1, 0, 3 }, 167462306a36Sopenharmony_ci { 3, 2, 1, 0 }, 167562306a36Sopenharmony_ci}; 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_cistatic int dsi_host_parse_lane_data(struct msm_dsi_host *msm_host, 167862306a36Sopenharmony_ci struct device_node *ep) 167962306a36Sopenharmony_ci{ 168062306a36Sopenharmony_ci struct device *dev = &msm_host->pdev->dev; 168162306a36Sopenharmony_ci struct property *prop; 168262306a36Sopenharmony_ci u32 lane_map[4]; 168362306a36Sopenharmony_ci int ret, i, len, num_lanes; 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci prop = of_find_property(ep, "data-lanes", &len); 168662306a36Sopenharmony_ci if (!prop) { 168762306a36Sopenharmony_ci DRM_DEV_DEBUG(dev, 168862306a36Sopenharmony_ci "failed to find data lane mapping, using default\n"); 168962306a36Sopenharmony_ci /* Set the number of date lanes to 4 by default. */ 169062306a36Sopenharmony_ci msm_host->num_data_lanes = 4; 169162306a36Sopenharmony_ci return 0; 169262306a36Sopenharmony_ci } 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci num_lanes = drm_of_get_data_lanes_count(ep, 1, 4); 169562306a36Sopenharmony_ci if (num_lanes < 0) { 169662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "bad number of data lanes\n"); 169762306a36Sopenharmony_ci return num_lanes; 169862306a36Sopenharmony_ci } 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci msm_host->num_data_lanes = num_lanes; 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci ret = of_property_read_u32_array(ep, "data-lanes", lane_map, 170362306a36Sopenharmony_ci num_lanes); 170462306a36Sopenharmony_ci if (ret) { 170562306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "failed to read lane data\n"); 170662306a36Sopenharmony_ci return ret; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci /* 171062306a36Sopenharmony_ci * compare DT specified physical-logical lane mappings with the ones 171162306a36Sopenharmony_ci * supported by hardware 171262306a36Sopenharmony_ci */ 171362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(supported_data_lane_swaps); i++) { 171462306a36Sopenharmony_ci const int *swap = supported_data_lane_swaps[i]; 171562306a36Sopenharmony_ci int j; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci /* 171862306a36Sopenharmony_ci * the data-lanes array we get from DT has a logical->physical 171962306a36Sopenharmony_ci * mapping. The "data lane swap" register field represents 172062306a36Sopenharmony_ci * supported configurations in a physical->logical mapping. 172162306a36Sopenharmony_ci * Translate the DT mapping to what we understand and find a 172262306a36Sopenharmony_ci * configuration that works. 172362306a36Sopenharmony_ci */ 172462306a36Sopenharmony_ci for (j = 0; j < num_lanes; j++) { 172562306a36Sopenharmony_ci if (lane_map[j] < 0 || lane_map[j] > 3) 172662306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "bad physical lane entry %u\n", 172762306a36Sopenharmony_ci lane_map[j]); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci if (swap[lane_map[j]] != j) 173062306a36Sopenharmony_ci break; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ci if (j == num_lanes) { 173462306a36Sopenharmony_ci msm_host->dlane_swap = i; 173562306a36Sopenharmony_ci return 0; 173662306a36Sopenharmony_ci } 173762306a36Sopenharmony_ci } 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_ci return -EINVAL; 174062306a36Sopenharmony_ci} 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_cistatic int dsi_populate_dsc_params(struct msm_dsi_host *msm_host, struct drm_dsc_config *dsc) 174362306a36Sopenharmony_ci{ 174462306a36Sopenharmony_ci int ret; 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci if (dsc->bits_per_pixel & 0xf) { 174762306a36Sopenharmony_ci DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support fractional bits_per_pixel\n"); 174862306a36Sopenharmony_ci return -EINVAL; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci if (dsc->bits_per_component != 8) { 175262306a36Sopenharmony_ci DRM_DEV_ERROR(&msm_host->pdev->dev, "DSI does not support bits_per_component != 8 yet\n"); 175362306a36Sopenharmony_ci return -EOPNOTSUPP; 175462306a36Sopenharmony_ci } 175562306a36Sopenharmony_ci 175662306a36Sopenharmony_ci dsc->simple_422 = 0; 175762306a36Sopenharmony_ci dsc->convert_rgb = 1; 175862306a36Sopenharmony_ci dsc->vbr_enable = 0; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci drm_dsc_set_const_params(dsc); 176162306a36Sopenharmony_ci drm_dsc_set_rc_buf_thresh(dsc); 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci /* handle only bpp = bpc = 8, pre-SCR panels */ 176462306a36Sopenharmony_ci ret = drm_dsc_setup_rc_params(dsc, DRM_DSC_1_1_PRE_SCR); 176562306a36Sopenharmony_ci if (ret) { 176662306a36Sopenharmony_ci DRM_DEV_ERROR(&msm_host->pdev->dev, "could not find DSC RC parameters\n"); 176762306a36Sopenharmony_ci return ret; 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci dsc->initial_scale_value = drm_dsc_initial_scale_value(dsc); 177162306a36Sopenharmony_ci dsc->line_buf_depth = dsc->bits_per_component + 1; 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci return drm_dsc_compute_rc_parameters(dsc); 177462306a36Sopenharmony_ci} 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_cistatic int dsi_host_parse_dt(struct msm_dsi_host *msm_host) 177762306a36Sopenharmony_ci{ 177862306a36Sopenharmony_ci struct device *dev = &msm_host->pdev->dev; 177962306a36Sopenharmony_ci struct device_node *np = dev->of_node; 178062306a36Sopenharmony_ci struct device_node *endpoint; 178162306a36Sopenharmony_ci int ret = 0; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci /* 178462306a36Sopenharmony_ci * Get the endpoint of the output port of the DSI host. In our case, 178562306a36Sopenharmony_ci * this is mapped to port number with reg = 1. Don't return an error if 178662306a36Sopenharmony_ci * the remote endpoint isn't defined. It's possible that there is 178762306a36Sopenharmony_ci * nothing connected to the dsi output. 178862306a36Sopenharmony_ci */ 178962306a36Sopenharmony_ci endpoint = of_graph_get_endpoint_by_regs(np, 1, -1); 179062306a36Sopenharmony_ci if (!endpoint) { 179162306a36Sopenharmony_ci DRM_DEV_DEBUG(dev, "%s: no endpoint\n", __func__); 179262306a36Sopenharmony_ci return 0; 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci ret = dsi_host_parse_lane_data(msm_host, endpoint); 179662306a36Sopenharmony_ci if (ret) { 179762306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "%s: invalid lane configuration %d\n", 179862306a36Sopenharmony_ci __func__, ret); 179962306a36Sopenharmony_ci ret = -EINVAL; 180062306a36Sopenharmony_ci goto err; 180162306a36Sopenharmony_ci } 180262306a36Sopenharmony_ci 180362306a36Sopenharmony_ci if (of_property_read_bool(np, "syscon-sfpb")) { 180462306a36Sopenharmony_ci msm_host->sfpb = syscon_regmap_lookup_by_phandle(np, 180562306a36Sopenharmony_ci "syscon-sfpb"); 180662306a36Sopenharmony_ci if (IS_ERR(msm_host->sfpb)) { 180762306a36Sopenharmony_ci DRM_DEV_ERROR(dev, "%s: failed to get sfpb regmap\n", 180862306a36Sopenharmony_ci __func__); 180962306a36Sopenharmony_ci ret = PTR_ERR(msm_host->sfpb); 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci } 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_cierr: 181462306a36Sopenharmony_ci of_node_put(endpoint); 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci return ret; 181762306a36Sopenharmony_ci} 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_cistatic int dsi_host_get_id(struct msm_dsi_host *msm_host) 182062306a36Sopenharmony_ci{ 182162306a36Sopenharmony_ci struct platform_device *pdev = msm_host->pdev; 182262306a36Sopenharmony_ci const struct msm_dsi_config *cfg = msm_host->cfg_hnd->cfg; 182362306a36Sopenharmony_ci struct resource *res; 182462306a36Sopenharmony_ci int i, j; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dsi_ctrl"); 182762306a36Sopenharmony_ci if (!res) 182862306a36Sopenharmony_ci return -EINVAL; 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci for (i = 0; i < VARIANTS_MAX; i++) 183162306a36Sopenharmony_ci for (j = 0; j < DSI_MAX; j++) 183262306a36Sopenharmony_ci if (cfg->io_start[i][j] == res->start) 183362306a36Sopenharmony_ci return j; 183462306a36Sopenharmony_ci 183562306a36Sopenharmony_ci return -EINVAL; 183662306a36Sopenharmony_ci} 183762306a36Sopenharmony_ci 183862306a36Sopenharmony_ciint msm_dsi_host_init(struct msm_dsi *msm_dsi) 183962306a36Sopenharmony_ci{ 184062306a36Sopenharmony_ci struct msm_dsi_host *msm_host = NULL; 184162306a36Sopenharmony_ci struct platform_device *pdev = msm_dsi->pdev; 184262306a36Sopenharmony_ci const struct msm_dsi_config *cfg; 184362306a36Sopenharmony_ci int ret; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci msm_host = devm_kzalloc(&pdev->dev, sizeof(*msm_host), GFP_KERNEL); 184662306a36Sopenharmony_ci if (!msm_host) { 184762306a36Sopenharmony_ci return -ENOMEM; 184862306a36Sopenharmony_ci } 184962306a36Sopenharmony_ci 185062306a36Sopenharmony_ci msm_host->pdev = pdev; 185162306a36Sopenharmony_ci msm_dsi->host = &msm_host->base; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci ret = dsi_host_parse_dt(msm_host); 185462306a36Sopenharmony_ci if (ret) { 185562306a36Sopenharmony_ci pr_err("%s: failed to parse dt\n", __func__); 185662306a36Sopenharmony_ci return ret; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci msm_host->ctrl_base = msm_ioremap_size(pdev, "dsi_ctrl", &msm_host->ctrl_size); 186062306a36Sopenharmony_ci if (IS_ERR(msm_host->ctrl_base)) { 186162306a36Sopenharmony_ci pr_err("%s: unable to map Dsi ctrl base\n", __func__); 186262306a36Sopenharmony_ci return PTR_ERR(msm_host->ctrl_base); 186362306a36Sopenharmony_ci } 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci pm_runtime_enable(&pdev->dev); 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci msm_host->cfg_hnd = dsi_get_config(msm_host); 186862306a36Sopenharmony_ci if (!msm_host->cfg_hnd) { 186962306a36Sopenharmony_ci pr_err("%s: get config failed\n", __func__); 187062306a36Sopenharmony_ci return -EINVAL; 187162306a36Sopenharmony_ci } 187262306a36Sopenharmony_ci cfg = msm_host->cfg_hnd->cfg; 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_ci msm_host->id = dsi_host_get_id(msm_host); 187562306a36Sopenharmony_ci if (msm_host->id < 0) { 187662306a36Sopenharmony_ci pr_err("%s: unable to identify DSI host index\n", __func__); 187762306a36Sopenharmony_ci return msm_host->id; 187862306a36Sopenharmony_ci } 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci /* fixup base address by io offset */ 188162306a36Sopenharmony_ci msm_host->ctrl_base += cfg->io_offset; 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_ci ret = devm_regulator_bulk_get_const(&pdev->dev, cfg->num_regulators, 188462306a36Sopenharmony_ci cfg->regulator_data, 188562306a36Sopenharmony_ci &msm_host->supplies); 188662306a36Sopenharmony_ci if (ret) 188762306a36Sopenharmony_ci return ret; 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci ret = dsi_clk_init(msm_host); 189062306a36Sopenharmony_ci if (ret) { 189162306a36Sopenharmony_ci pr_err("%s: unable to initialize dsi clks\n", __func__); 189262306a36Sopenharmony_ci return ret; 189362306a36Sopenharmony_ci } 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci msm_host->rx_buf = devm_kzalloc(&pdev->dev, SZ_4K, GFP_KERNEL); 189662306a36Sopenharmony_ci if (!msm_host->rx_buf) { 189762306a36Sopenharmony_ci pr_err("%s: alloc rx temp buf failed\n", __func__); 189862306a36Sopenharmony_ci return -ENOMEM; 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci ret = devm_pm_opp_set_clkname(&pdev->dev, "byte"); 190262306a36Sopenharmony_ci if (ret) 190362306a36Sopenharmony_ci return ret; 190462306a36Sopenharmony_ci /* OPP table is optional */ 190562306a36Sopenharmony_ci ret = devm_pm_opp_of_add_table(&pdev->dev); 190662306a36Sopenharmony_ci if (ret && ret != -ENODEV) { 190762306a36Sopenharmony_ci dev_err(&pdev->dev, "invalid OPP table in device tree\n"); 190862306a36Sopenharmony_ci return ret; 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci msm_host->irq = irq_of_parse_and_map(pdev->dev.of_node, 0); 191262306a36Sopenharmony_ci if (!msm_host->irq) { 191362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to get irq\n"); 191462306a36Sopenharmony_ci return -EINVAL; 191562306a36Sopenharmony_ci } 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci /* do not autoenable, will be enabled later */ 191862306a36Sopenharmony_ci ret = devm_request_irq(&pdev->dev, msm_host->irq, dsi_host_irq, 191962306a36Sopenharmony_ci IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN, 192062306a36Sopenharmony_ci "dsi_isr", msm_host); 192162306a36Sopenharmony_ci if (ret < 0) { 192262306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request IRQ%u: %d\n", 192362306a36Sopenharmony_ci msm_host->irq, ret); 192462306a36Sopenharmony_ci return ret; 192562306a36Sopenharmony_ci } 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci init_completion(&msm_host->dma_comp); 192862306a36Sopenharmony_ci init_completion(&msm_host->video_comp); 192962306a36Sopenharmony_ci mutex_init(&msm_host->dev_mutex); 193062306a36Sopenharmony_ci mutex_init(&msm_host->cmd_mutex); 193162306a36Sopenharmony_ci spin_lock_init(&msm_host->intr_lock); 193262306a36Sopenharmony_ci 193362306a36Sopenharmony_ci /* setup workqueue */ 193462306a36Sopenharmony_ci msm_host->workqueue = alloc_ordered_workqueue("dsi_drm_work", 0); 193562306a36Sopenharmony_ci if (!msm_host->workqueue) 193662306a36Sopenharmony_ci return -ENOMEM; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci INIT_WORK(&msm_host->err_work, dsi_err_worker); 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci msm_dsi->id = msm_host->id; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci DBG("Dsi Host %d initialized", msm_host->id); 194362306a36Sopenharmony_ci return 0; 194462306a36Sopenharmony_ci} 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_civoid msm_dsi_host_destroy(struct mipi_dsi_host *host) 194762306a36Sopenharmony_ci{ 194862306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci DBG(""); 195162306a36Sopenharmony_ci if (msm_host->workqueue) { 195262306a36Sopenharmony_ci destroy_workqueue(msm_host->workqueue); 195362306a36Sopenharmony_ci msm_host->workqueue = NULL; 195462306a36Sopenharmony_ci } 195562306a36Sopenharmony_ci 195662306a36Sopenharmony_ci mutex_destroy(&msm_host->cmd_mutex); 195762306a36Sopenharmony_ci mutex_destroy(&msm_host->dev_mutex); 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci pm_runtime_disable(&msm_host->pdev->dev); 196062306a36Sopenharmony_ci} 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ciint msm_dsi_host_modeset_init(struct mipi_dsi_host *host, 196362306a36Sopenharmony_ci struct drm_device *dev) 196462306a36Sopenharmony_ci{ 196562306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 196662306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 196762306a36Sopenharmony_ci int ret; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci msm_host->dev = dev; 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci ret = cfg_hnd->ops->tx_buf_alloc(msm_host, SZ_4K); 197262306a36Sopenharmony_ci if (ret) { 197362306a36Sopenharmony_ci pr_err("%s: alloc tx gem obj failed, %d\n", __func__, ret); 197462306a36Sopenharmony_ci return ret; 197562306a36Sopenharmony_ci } 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci return 0; 197862306a36Sopenharmony_ci} 197962306a36Sopenharmony_ci 198062306a36Sopenharmony_ciint msm_dsi_host_register(struct mipi_dsi_host *host) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 198362306a36Sopenharmony_ci int ret; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci /* Register mipi dsi host */ 198662306a36Sopenharmony_ci if (!msm_host->registered) { 198762306a36Sopenharmony_ci host->dev = &msm_host->pdev->dev; 198862306a36Sopenharmony_ci host->ops = &dsi_host_ops; 198962306a36Sopenharmony_ci ret = mipi_dsi_host_register(host); 199062306a36Sopenharmony_ci if (ret) 199162306a36Sopenharmony_ci return ret; 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci msm_host->registered = true; 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci return 0; 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_civoid msm_dsi_host_unregister(struct mipi_dsi_host *host) 200062306a36Sopenharmony_ci{ 200162306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci if (msm_host->registered) { 200462306a36Sopenharmony_ci mipi_dsi_host_unregister(host); 200562306a36Sopenharmony_ci host->dev = NULL; 200662306a36Sopenharmony_ci host->ops = NULL; 200762306a36Sopenharmony_ci msm_host->registered = false; 200862306a36Sopenharmony_ci } 200962306a36Sopenharmony_ci} 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ciint msm_dsi_host_xfer_prepare(struct mipi_dsi_host *host, 201262306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 201362306a36Sopenharmony_ci{ 201462306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 201562306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_ci /* TODO: make sure dsi_cmd_mdp is idle. 201862306a36Sopenharmony_ci * Since DSI6G v1.2.0, we can set DSI_TRIG_CTRL.BLOCK_DMA_WITHIN_FRAME 201962306a36Sopenharmony_ci * to ask H/W to wait until cmd mdp is idle. S/W wait is not needed. 202062306a36Sopenharmony_ci * How to handle the old versions? Wait for mdp cmd done? 202162306a36Sopenharmony_ci */ 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci /* 202462306a36Sopenharmony_ci * mdss interrupt is generated in mdp core clock domain 202562306a36Sopenharmony_ci * mdp clock need to be enabled to receive dsi interrupt 202662306a36Sopenharmony_ci */ 202762306a36Sopenharmony_ci pm_runtime_get_sync(&msm_host->pdev->dev); 202862306a36Sopenharmony_ci cfg_hnd->ops->link_clk_set_rate(msm_host); 202962306a36Sopenharmony_ci cfg_hnd->ops->link_clk_enable(msm_host); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci /* TODO: vote for bus bandwidth */ 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci if (!(msg->flags & MIPI_DSI_MSG_USE_LPM)) 203462306a36Sopenharmony_ci dsi_set_tx_power_mode(0, msm_host); 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci msm_host->dma_cmd_ctrl_restore = dsi_read(msm_host, REG_DSI_CTRL); 203762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, 203862306a36Sopenharmony_ci msm_host->dma_cmd_ctrl_restore | 203962306a36Sopenharmony_ci DSI_CTRL_CMD_MODE_EN | 204062306a36Sopenharmony_ci DSI_CTRL_ENABLE); 204162306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_DMA_DONE, 1); 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci return 0; 204462306a36Sopenharmony_ci} 204562306a36Sopenharmony_ci 204662306a36Sopenharmony_civoid msm_dsi_host_xfer_restore(struct mipi_dsi_host *host, 204762306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 205062306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci dsi_intr_ctrl(msm_host, DSI_IRQ_MASK_CMD_DMA_DONE, 0); 205362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_CTRL, msm_host->dma_cmd_ctrl_restore); 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_ci if (!(msg->flags & MIPI_DSI_MSG_USE_LPM)) 205662306a36Sopenharmony_ci dsi_set_tx_power_mode(1, msm_host); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* TODO: unvote for bus bandwidth */ 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci cfg_hnd->ops->link_clk_disable(msm_host); 206162306a36Sopenharmony_ci pm_runtime_put(&msm_host->pdev->dev); 206262306a36Sopenharmony_ci} 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ciint msm_dsi_host_cmd_tx(struct mipi_dsi_host *host, 206562306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 206662306a36Sopenharmony_ci{ 206762306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci return dsi_cmds2buf_tx(msm_host, msg); 207062306a36Sopenharmony_ci} 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ciint msm_dsi_host_cmd_rx(struct mipi_dsi_host *host, 207362306a36Sopenharmony_ci const struct mipi_dsi_msg *msg) 207462306a36Sopenharmony_ci{ 207562306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 207662306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 207762306a36Sopenharmony_ci int data_byte, rx_byte, dlen, end; 207862306a36Sopenharmony_ci int short_response, diff, pkt_size, ret = 0; 207962306a36Sopenharmony_ci char cmd; 208062306a36Sopenharmony_ci int rlen = msg->rx_len; 208162306a36Sopenharmony_ci u8 *buf; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci if (rlen <= 2) { 208462306a36Sopenharmony_ci short_response = 1; 208562306a36Sopenharmony_ci pkt_size = rlen; 208662306a36Sopenharmony_ci rx_byte = 4; 208762306a36Sopenharmony_ci } else { 208862306a36Sopenharmony_ci short_response = 0; 208962306a36Sopenharmony_ci data_byte = 10; /* first read */ 209062306a36Sopenharmony_ci if (rlen < data_byte) 209162306a36Sopenharmony_ci pkt_size = rlen; 209262306a36Sopenharmony_ci else 209362306a36Sopenharmony_ci pkt_size = data_byte; 209462306a36Sopenharmony_ci rx_byte = data_byte + 6; /* 4 header + 2 crc */ 209562306a36Sopenharmony_ci } 209662306a36Sopenharmony_ci 209762306a36Sopenharmony_ci buf = msm_host->rx_buf; 209862306a36Sopenharmony_ci end = 0; 209962306a36Sopenharmony_ci while (!end) { 210062306a36Sopenharmony_ci u8 tx[2] = {pkt_size & 0xff, pkt_size >> 8}; 210162306a36Sopenharmony_ci struct mipi_dsi_msg max_pkt_size_msg = { 210262306a36Sopenharmony_ci .channel = msg->channel, 210362306a36Sopenharmony_ci .type = MIPI_DSI_SET_MAXIMUM_RETURN_PACKET_SIZE, 210462306a36Sopenharmony_ci .tx_len = 2, 210562306a36Sopenharmony_ci .tx_buf = tx, 210662306a36Sopenharmony_ci }; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci DBG("rlen=%d pkt_size=%d rx_byte=%d", 210962306a36Sopenharmony_ci rlen, pkt_size, rx_byte); 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci ret = dsi_cmds2buf_tx(msm_host, &max_pkt_size_msg); 211262306a36Sopenharmony_ci if (ret < 2) { 211362306a36Sopenharmony_ci pr_err("%s: Set max pkt size failed, %d\n", 211462306a36Sopenharmony_ci __func__, ret); 211562306a36Sopenharmony_ci return -EINVAL; 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci if ((cfg_hnd->major == MSM_DSI_VER_MAJOR_6G) && 211962306a36Sopenharmony_ci (cfg_hnd->minor >= MSM_DSI_6G_VER_MINOR_V1_1)) { 212062306a36Sopenharmony_ci /* Clear the RDBK_DATA registers */ 212162306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 212262306a36Sopenharmony_ci DSI_RDBK_DATA_CTRL_CLR); 212362306a36Sopenharmony_ci wmb(); /* make sure the RDBK registers are cleared */ 212462306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_RDBK_DATA_CTRL, 0); 212562306a36Sopenharmony_ci wmb(); /* release cleared status before transfer */ 212662306a36Sopenharmony_ci } 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_ci ret = dsi_cmds2buf_tx(msm_host, msg); 212962306a36Sopenharmony_ci if (ret < 0) { 213062306a36Sopenharmony_ci pr_err("%s: Read cmd Tx failed, %d\n", __func__, ret); 213162306a36Sopenharmony_ci return ret; 213262306a36Sopenharmony_ci } else if (ret < msg->tx_len) { 213362306a36Sopenharmony_ci pr_err("%s: Read cmd Tx failed, too short: %d\n", __func__, ret); 213462306a36Sopenharmony_ci return -ECOMM; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci 213762306a36Sopenharmony_ci /* 213862306a36Sopenharmony_ci * once cmd_dma_done interrupt received, 213962306a36Sopenharmony_ci * return data from client is ready and stored 214062306a36Sopenharmony_ci * at RDBK_DATA register already 214162306a36Sopenharmony_ci * since rx fifo is 16 bytes, dcs header is kept at first loop, 214262306a36Sopenharmony_ci * after that dcs header lost during shift into registers 214362306a36Sopenharmony_ci */ 214462306a36Sopenharmony_ci dlen = dsi_cmd_dma_rx(msm_host, buf, rx_byte, pkt_size); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (dlen <= 0) 214762306a36Sopenharmony_ci return 0; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci if (short_response) 215062306a36Sopenharmony_ci break; 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci if (rlen <= data_byte) { 215362306a36Sopenharmony_ci diff = data_byte - rlen; 215462306a36Sopenharmony_ci end = 1; 215562306a36Sopenharmony_ci } else { 215662306a36Sopenharmony_ci diff = 0; 215762306a36Sopenharmony_ci rlen -= data_byte; 215862306a36Sopenharmony_ci } 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci if (!end) { 216162306a36Sopenharmony_ci dlen -= 2; /* 2 crc */ 216262306a36Sopenharmony_ci dlen -= diff; 216362306a36Sopenharmony_ci buf += dlen; /* next start position */ 216462306a36Sopenharmony_ci data_byte = 14; /* NOT first read */ 216562306a36Sopenharmony_ci if (rlen < data_byte) 216662306a36Sopenharmony_ci pkt_size += rlen; 216762306a36Sopenharmony_ci else 216862306a36Sopenharmony_ci pkt_size += data_byte; 216962306a36Sopenharmony_ci DBG("buf=%p dlen=%d diff=%d", buf, dlen, diff); 217062306a36Sopenharmony_ci } 217162306a36Sopenharmony_ci } 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci /* 217462306a36Sopenharmony_ci * For single Long read, if the requested rlen < 10, 217562306a36Sopenharmony_ci * we need to shift the start position of rx 217662306a36Sopenharmony_ci * data buffer to skip the bytes which are not 217762306a36Sopenharmony_ci * updated. 217862306a36Sopenharmony_ci */ 217962306a36Sopenharmony_ci if (pkt_size < 10 && !short_response) 218062306a36Sopenharmony_ci buf = msm_host->rx_buf + (10 - rlen); 218162306a36Sopenharmony_ci else 218262306a36Sopenharmony_ci buf = msm_host->rx_buf; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci cmd = buf[0]; 218562306a36Sopenharmony_ci switch (cmd) { 218662306a36Sopenharmony_ci case MIPI_DSI_RX_ACKNOWLEDGE_AND_ERROR_REPORT: 218762306a36Sopenharmony_ci pr_err("%s: rx ACK_ERR_PACLAGE\n", __func__); 218862306a36Sopenharmony_ci ret = 0; 218962306a36Sopenharmony_ci break; 219062306a36Sopenharmony_ci case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_1BYTE: 219162306a36Sopenharmony_ci case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_1BYTE: 219262306a36Sopenharmony_ci ret = dsi_short_read1_resp(buf, msg); 219362306a36Sopenharmony_ci break; 219462306a36Sopenharmony_ci case MIPI_DSI_RX_GENERIC_SHORT_READ_RESPONSE_2BYTE: 219562306a36Sopenharmony_ci case MIPI_DSI_RX_DCS_SHORT_READ_RESPONSE_2BYTE: 219662306a36Sopenharmony_ci ret = dsi_short_read2_resp(buf, msg); 219762306a36Sopenharmony_ci break; 219862306a36Sopenharmony_ci case MIPI_DSI_RX_GENERIC_LONG_READ_RESPONSE: 219962306a36Sopenharmony_ci case MIPI_DSI_RX_DCS_LONG_READ_RESPONSE: 220062306a36Sopenharmony_ci ret = dsi_long_read_resp(buf, msg); 220162306a36Sopenharmony_ci break; 220262306a36Sopenharmony_ci default: 220362306a36Sopenharmony_ci pr_warn("%s:Invalid response cmd\n", __func__); 220462306a36Sopenharmony_ci ret = 0; 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci 220762306a36Sopenharmony_ci return ret; 220862306a36Sopenharmony_ci} 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_civoid msm_dsi_host_cmd_xfer_commit(struct mipi_dsi_host *host, u32 dma_base, 221162306a36Sopenharmony_ci u32 len) 221262306a36Sopenharmony_ci{ 221362306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_DMA_BASE, dma_base); 221662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_DMA_LEN, len); 221762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TRIG_DMA, 1); 221862306a36Sopenharmony_ci 221962306a36Sopenharmony_ci /* Make sure trigger happens */ 222062306a36Sopenharmony_ci wmb(); 222162306a36Sopenharmony_ci} 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_civoid msm_dsi_host_set_phy_mode(struct mipi_dsi_host *host, 222462306a36Sopenharmony_ci struct msm_dsi_phy *src_phy) 222562306a36Sopenharmony_ci{ 222662306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_ci msm_host->cphy_mode = src_phy->cphy_mode; 222962306a36Sopenharmony_ci} 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_civoid msm_dsi_host_reset_phy(struct mipi_dsi_host *host) 223262306a36Sopenharmony_ci{ 223362306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci DBG(""); 223662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_PHY_RESET, DSI_PHY_RESET_RESET); 223762306a36Sopenharmony_ci /* Make sure fully reset */ 223862306a36Sopenharmony_ci wmb(); 223962306a36Sopenharmony_ci udelay(1000); 224062306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_PHY_RESET, 0); 224162306a36Sopenharmony_ci udelay(100); 224262306a36Sopenharmony_ci} 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_civoid msm_dsi_host_get_phy_clk_req(struct mipi_dsi_host *host, 224562306a36Sopenharmony_ci struct msm_dsi_phy_clk_request *clk_req, 224662306a36Sopenharmony_ci bool is_bonded_dsi) 224762306a36Sopenharmony_ci{ 224862306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 224962306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 225062306a36Sopenharmony_ci int ret; 225162306a36Sopenharmony_ci 225262306a36Sopenharmony_ci ret = cfg_hnd->ops->calc_clk_rate(msm_host, is_bonded_dsi); 225362306a36Sopenharmony_ci if (ret) { 225462306a36Sopenharmony_ci pr_err("%s: unable to calc clk rate, %d\n", __func__, ret); 225562306a36Sopenharmony_ci return; 225662306a36Sopenharmony_ci } 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci /* CPHY transmits 16 bits over 7 clock cycles 225962306a36Sopenharmony_ci * "byte_clk" is in units of 16-bits (see dsi_calc_pclk), 226062306a36Sopenharmony_ci * so multiply by 7 to get the "bitclk rate" 226162306a36Sopenharmony_ci */ 226262306a36Sopenharmony_ci if (msm_host->cphy_mode) 226362306a36Sopenharmony_ci clk_req->bitclk_rate = msm_host->byte_clk_rate * 7; 226462306a36Sopenharmony_ci else 226562306a36Sopenharmony_ci clk_req->bitclk_rate = msm_host->byte_clk_rate * 8; 226662306a36Sopenharmony_ci clk_req->escclk_rate = msm_host->esc_clk_rate; 226762306a36Sopenharmony_ci} 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_civoid msm_dsi_host_enable_irq(struct mipi_dsi_host *host) 227062306a36Sopenharmony_ci{ 227162306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci enable_irq(msm_host->irq); 227462306a36Sopenharmony_ci} 227562306a36Sopenharmony_ci 227662306a36Sopenharmony_civoid msm_dsi_host_disable_irq(struct mipi_dsi_host *host) 227762306a36Sopenharmony_ci{ 227862306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 227962306a36Sopenharmony_ci 228062306a36Sopenharmony_ci disable_irq(msm_host->irq); 228162306a36Sopenharmony_ci} 228262306a36Sopenharmony_ci 228362306a36Sopenharmony_ciint msm_dsi_host_enable(struct mipi_dsi_host *host) 228462306a36Sopenharmony_ci{ 228562306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci dsi_op_mode_config(msm_host, 228862306a36Sopenharmony_ci !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), true); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci /* TODO: clock should be turned off for command mode, 229162306a36Sopenharmony_ci * and only turned on before MDP START. 229262306a36Sopenharmony_ci * This part of code should be enabled once mdp driver support it. 229362306a36Sopenharmony_ci */ 229462306a36Sopenharmony_ci /* if (msm_panel->mode == MSM_DSI_CMD_MODE) { 229562306a36Sopenharmony_ci * dsi_link_clk_disable(msm_host); 229662306a36Sopenharmony_ci * pm_runtime_put(&msm_host->pdev->dev); 229762306a36Sopenharmony_ci * } 229862306a36Sopenharmony_ci */ 229962306a36Sopenharmony_ci msm_host->enabled = true; 230062306a36Sopenharmony_ci return 0; 230162306a36Sopenharmony_ci} 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ciint msm_dsi_host_disable(struct mipi_dsi_host *host) 230462306a36Sopenharmony_ci{ 230562306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci msm_host->enabled = false; 230862306a36Sopenharmony_ci dsi_op_mode_config(msm_host, 230962306a36Sopenharmony_ci !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO), false); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci /* Since we have disabled INTF, the video engine won't stop so that 231262306a36Sopenharmony_ci * the cmd engine will be blocked. 231362306a36Sopenharmony_ci * Reset to disable video engine so that we can send off cmd. 231462306a36Sopenharmony_ci */ 231562306a36Sopenharmony_ci dsi_sw_reset(msm_host); 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci return 0; 231862306a36Sopenharmony_ci} 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_cistatic void msm_dsi_sfpb_config(struct msm_dsi_host *msm_host, bool enable) 232162306a36Sopenharmony_ci{ 232262306a36Sopenharmony_ci enum sfpb_ahb_arb_master_port_en en; 232362306a36Sopenharmony_ci 232462306a36Sopenharmony_ci if (!msm_host->sfpb) 232562306a36Sopenharmony_ci return; 232662306a36Sopenharmony_ci 232762306a36Sopenharmony_ci en = enable ? SFPB_MASTER_PORT_ENABLE : SFPB_MASTER_PORT_DISABLE; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci regmap_update_bits(msm_host->sfpb, REG_SFPB_GPREG, 233062306a36Sopenharmony_ci SFPB_GPREG_MASTER_PORT_EN__MASK, 233162306a36Sopenharmony_ci SFPB_GPREG_MASTER_PORT_EN(en)); 233262306a36Sopenharmony_ci} 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ciint msm_dsi_host_power_on(struct mipi_dsi_host *host, 233562306a36Sopenharmony_ci struct msm_dsi_phy_shared_timings *phy_shared_timings, 233662306a36Sopenharmony_ci bool is_bonded_dsi, struct msm_dsi_phy *phy) 233762306a36Sopenharmony_ci{ 233862306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 233962306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 234062306a36Sopenharmony_ci int ret = 0; 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci mutex_lock(&msm_host->dev_mutex); 234362306a36Sopenharmony_ci if (msm_host->power_on) { 234462306a36Sopenharmony_ci DBG("dsi host already on"); 234562306a36Sopenharmony_ci goto unlock_ret; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci msm_host->byte_intf_clk_rate = msm_host->byte_clk_rate; 234962306a36Sopenharmony_ci if (phy_shared_timings->byte_intf_clk_div_2) 235062306a36Sopenharmony_ci msm_host->byte_intf_clk_rate /= 2; 235162306a36Sopenharmony_ci 235262306a36Sopenharmony_ci msm_dsi_sfpb_config(msm_host, true); 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci ret = regulator_bulk_enable(msm_host->cfg_hnd->cfg->num_regulators, 235562306a36Sopenharmony_ci msm_host->supplies); 235662306a36Sopenharmony_ci if (ret) { 235762306a36Sopenharmony_ci pr_err("%s:Failed to enable vregs.ret=%d\n", 235862306a36Sopenharmony_ci __func__, ret); 235962306a36Sopenharmony_ci goto unlock_ret; 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci pm_runtime_get_sync(&msm_host->pdev->dev); 236362306a36Sopenharmony_ci ret = cfg_hnd->ops->link_clk_set_rate(msm_host); 236462306a36Sopenharmony_ci if (!ret) 236562306a36Sopenharmony_ci ret = cfg_hnd->ops->link_clk_enable(msm_host); 236662306a36Sopenharmony_ci if (ret) { 236762306a36Sopenharmony_ci pr_err("%s: failed to enable link clocks. ret=%d\n", 236862306a36Sopenharmony_ci __func__, ret); 236962306a36Sopenharmony_ci goto fail_disable_reg; 237062306a36Sopenharmony_ci } 237162306a36Sopenharmony_ci 237262306a36Sopenharmony_ci ret = pinctrl_pm_select_default_state(&msm_host->pdev->dev); 237362306a36Sopenharmony_ci if (ret) { 237462306a36Sopenharmony_ci pr_err("%s: failed to set pinctrl default state, %d\n", 237562306a36Sopenharmony_ci __func__, ret); 237662306a36Sopenharmony_ci goto fail_disable_clk; 237762306a36Sopenharmony_ci } 237862306a36Sopenharmony_ci 237962306a36Sopenharmony_ci dsi_timing_setup(msm_host, is_bonded_dsi); 238062306a36Sopenharmony_ci dsi_sw_reset(msm_host); 238162306a36Sopenharmony_ci dsi_ctrl_enable(msm_host, phy_shared_timings, phy); 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci if (msm_host->disp_en_gpio) 238462306a36Sopenharmony_ci gpiod_set_value(msm_host->disp_en_gpio, 1); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci msm_host->power_on = true; 238762306a36Sopenharmony_ci mutex_unlock(&msm_host->dev_mutex); 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci return 0; 239062306a36Sopenharmony_ci 239162306a36Sopenharmony_cifail_disable_clk: 239262306a36Sopenharmony_ci cfg_hnd->ops->link_clk_disable(msm_host); 239362306a36Sopenharmony_ci pm_runtime_put(&msm_host->pdev->dev); 239462306a36Sopenharmony_cifail_disable_reg: 239562306a36Sopenharmony_ci regulator_bulk_disable(msm_host->cfg_hnd->cfg->num_regulators, 239662306a36Sopenharmony_ci msm_host->supplies); 239762306a36Sopenharmony_ciunlock_ret: 239862306a36Sopenharmony_ci mutex_unlock(&msm_host->dev_mutex); 239962306a36Sopenharmony_ci return ret; 240062306a36Sopenharmony_ci} 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ciint msm_dsi_host_power_off(struct mipi_dsi_host *host) 240362306a36Sopenharmony_ci{ 240462306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 240562306a36Sopenharmony_ci const struct msm_dsi_cfg_handler *cfg_hnd = msm_host->cfg_hnd; 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci mutex_lock(&msm_host->dev_mutex); 240862306a36Sopenharmony_ci if (!msm_host->power_on) { 240962306a36Sopenharmony_ci DBG("dsi host already off"); 241062306a36Sopenharmony_ci goto unlock_ret; 241162306a36Sopenharmony_ci } 241262306a36Sopenharmony_ci 241362306a36Sopenharmony_ci dsi_ctrl_disable(msm_host); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci if (msm_host->disp_en_gpio) 241662306a36Sopenharmony_ci gpiod_set_value(msm_host->disp_en_gpio, 0); 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci pinctrl_pm_select_sleep_state(&msm_host->pdev->dev); 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci cfg_hnd->ops->link_clk_disable(msm_host); 242162306a36Sopenharmony_ci pm_runtime_put(&msm_host->pdev->dev); 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci regulator_bulk_disable(msm_host->cfg_hnd->cfg->num_regulators, 242462306a36Sopenharmony_ci msm_host->supplies); 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci msm_dsi_sfpb_config(msm_host, false); 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci DBG("-"); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci msm_host->power_on = false; 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ciunlock_ret: 243362306a36Sopenharmony_ci mutex_unlock(&msm_host->dev_mutex); 243462306a36Sopenharmony_ci return 0; 243562306a36Sopenharmony_ci} 243662306a36Sopenharmony_ci 243762306a36Sopenharmony_ciint msm_dsi_host_set_display_mode(struct mipi_dsi_host *host, 243862306a36Sopenharmony_ci const struct drm_display_mode *mode) 243962306a36Sopenharmony_ci{ 244062306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci if (msm_host->mode) { 244362306a36Sopenharmony_ci drm_mode_destroy(msm_host->dev, msm_host->mode); 244462306a36Sopenharmony_ci msm_host->mode = NULL; 244562306a36Sopenharmony_ci } 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci msm_host->mode = drm_mode_duplicate(msm_host->dev, mode); 244862306a36Sopenharmony_ci if (!msm_host->mode) { 244962306a36Sopenharmony_ci pr_err("%s: cannot duplicate mode\n", __func__); 245062306a36Sopenharmony_ci return -ENOMEM; 245162306a36Sopenharmony_ci } 245262306a36Sopenharmony_ci 245362306a36Sopenharmony_ci return 0; 245462306a36Sopenharmony_ci} 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_cienum drm_mode_status msm_dsi_host_check_dsc(struct mipi_dsi_host *host, 245762306a36Sopenharmony_ci const struct drm_display_mode *mode) 245862306a36Sopenharmony_ci{ 245962306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 246062306a36Sopenharmony_ci struct drm_dsc_config *dsc = msm_host->dsc; 246162306a36Sopenharmony_ci int pic_width = mode->hdisplay; 246262306a36Sopenharmony_ci int pic_height = mode->vdisplay; 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci if (!msm_host->dsc) 246562306a36Sopenharmony_ci return MODE_OK; 246662306a36Sopenharmony_ci 246762306a36Sopenharmony_ci if (pic_width % dsc->slice_width) { 246862306a36Sopenharmony_ci pr_err("DSI: pic_width %d has to be multiple of slice %d\n", 246962306a36Sopenharmony_ci pic_width, dsc->slice_width); 247062306a36Sopenharmony_ci return MODE_H_ILLEGAL; 247162306a36Sopenharmony_ci } 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci if (pic_height % dsc->slice_height) { 247462306a36Sopenharmony_ci pr_err("DSI: pic_height %d has to be multiple of slice %d\n", 247562306a36Sopenharmony_ci pic_height, dsc->slice_height); 247662306a36Sopenharmony_ci return MODE_V_ILLEGAL; 247762306a36Sopenharmony_ci } 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci return MODE_OK; 248062306a36Sopenharmony_ci} 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ciunsigned long msm_dsi_host_get_mode_flags(struct mipi_dsi_host *host) 248362306a36Sopenharmony_ci{ 248462306a36Sopenharmony_ci return to_msm_dsi_host(host)->mode_flags; 248562306a36Sopenharmony_ci} 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_civoid msm_dsi_host_snapshot(struct msm_disp_state *disp_state, struct mipi_dsi_host *host) 248862306a36Sopenharmony_ci{ 248962306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 249062306a36Sopenharmony_ci 249162306a36Sopenharmony_ci pm_runtime_get_sync(&msm_host->pdev->dev); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci msm_disp_snapshot_add_block(disp_state, msm_host->ctrl_size, 249462306a36Sopenharmony_ci msm_host->ctrl_base, "dsi%d_ctrl", msm_host->id); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci pm_runtime_put_sync(&msm_host->pdev->dev); 249762306a36Sopenharmony_ci} 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_cistatic void msm_dsi_host_video_test_pattern_setup(struct msm_dsi_host *msm_host) 250062306a36Sopenharmony_ci{ 250162306a36Sopenharmony_ci u32 reg; 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_VIDEO_INIT_VAL, 0xff); 250662306a36Sopenharmony_ci /* draw checkered rectangle pattern */ 250762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TPG_MAIN_CONTROL, 250862306a36Sopenharmony_ci DSI_TPG_MAIN_CONTROL_CHECKERED_RECTANGLE_PATTERN); 250962306a36Sopenharmony_ci /* use 24-bit RGB test pttern */ 251062306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TPG_VIDEO_CONFIG, 251162306a36Sopenharmony_ci DSI_TPG_VIDEO_CONFIG_BPP(VIDEO_CONFIG_24BPP) | 251262306a36Sopenharmony_ci DSI_TPG_VIDEO_CONFIG_RGB); 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci reg |= DSI_TEST_PATTERN_GEN_CTRL_VIDEO_PATTERN_SEL(VID_MDSS_GENERAL_PATTERN); 251562306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, reg); 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci DBG("Video test pattern setup done\n"); 251862306a36Sopenharmony_ci} 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_cistatic void msm_dsi_host_cmd_test_pattern_setup(struct msm_dsi_host *msm_host) 252162306a36Sopenharmony_ci{ 252262306a36Sopenharmony_ci u32 reg; 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci /* initial value for test pattern */ 252762306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_MDP_INIT_VAL0, 0xff); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci reg |= DSI_TEST_PATTERN_GEN_CTRL_CMD_MDP_STREAM0_PATTERN_SEL(CMD_MDP_MDSS_GENERAL_PATTERN); 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, reg); 253262306a36Sopenharmony_ci /* draw checkered rectangle pattern */ 253362306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TPG_MAIN_CONTROL2, 253462306a36Sopenharmony_ci DSI_TPG_MAIN_CONTROL2_CMD_MDP0_CHECKERED_RECTANGLE_PATTERN); 253562306a36Sopenharmony_ci 253662306a36Sopenharmony_ci DBG("Cmd test pattern setup done\n"); 253762306a36Sopenharmony_ci} 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_civoid msm_dsi_host_test_pattern_en(struct mipi_dsi_host *host) 254062306a36Sopenharmony_ci{ 254162306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 254262306a36Sopenharmony_ci bool is_video_mode = !!(msm_host->mode_flags & MIPI_DSI_MODE_VIDEO); 254362306a36Sopenharmony_ci u32 reg; 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci if (is_video_mode) 254662306a36Sopenharmony_ci msm_dsi_host_video_test_pattern_setup(msm_host); 254762306a36Sopenharmony_ci else 254862306a36Sopenharmony_ci msm_dsi_host_cmd_test_pattern_setup(msm_host); 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci reg = dsi_read(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL); 255162306a36Sopenharmony_ci /* enable the test pattern generator */ 255262306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CTRL, (reg | DSI_TEST_PATTERN_GEN_CTRL_EN)); 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci /* for command mode need to trigger one frame from tpg */ 255562306a36Sopenharmony_ci if (!is_video_mode) 255662306a36Sopenharmony_ci dsi_write(msm_host, REG_DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER, 255762306a36Sopenharmony_ci DSI_TEST_PATTERN_GEN_CMD_STREAM0_TRIGGER_SW_TRIGGER); 255862306a36Sopenharmony_ci} 255962306a36Sopenharmony_ci 256062306a36Sopenharmony_cistruct drm_dsc_config *msm_dsi_host_get_dsc_config(struct mipi_dsi_host *host) 256162306a36Sopenharmony_ci{ 256262306a36Sopenharmony_ci struct msm_dsi_host *msm_host = to_msm_dsi_host(host); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci return msm_host->dsc; 256562306a36Sopenharmony_ci} 2566