diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_panel.c')
-rw-r--r-- | drivers/gpu/drm/msm/dp/dp_panel.c | 270 |
1 files changed, 250 insertions, 20 deletions
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 92415bf8aa16..15b7f6c7146e 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -4,6 +4,7 @@ */ #include "dp_panel.h" +#include "dp_reg.h" #include "dp_utils.h" #include <drm/drm_connector.h> @@ -11,6 +12,10 @@ #include <drm/drm_of.h> #include <drm/drm_print.h> +#include <linux/io.h> + +#define DP_INTF_CONFIG_DATABUS_WIDEN BIT(4) + #define DP_MAX_NUM_DP_LANES 4 #define DP_LINK_RATE_HBR2 540000 /* kbytes */ @@ -20,10 +25,46 @@ struct msm_dp_panel_private { struct msm_dp_panel msm_dp_panel; struct drm_dp_aux *aux; struct msm_dp_link *link; - struct msm_dp_catalog *catalog; + void __iomem *link_base; + void __iomem *p0_base; bool panel_on; }; +static inline u32 msm_dp_read_link(struct msm_dp_panel_private *panel, u32 offset) +{ + return readl_relaxed(panel->link_base + offset); +} + +static inline void msm_dp_write_link(struct msm_dp_panel_private *panel, + u32 offset, u32 data) +{ + /* + * To make sure link reg writes happens before any other operation, + * this function uses writel() instread of writel_relaxed() + */ + writel(data, panel->link_base + offset); +} + +static inline void msm_dp_write_p0(struct msm_dp_panel_private *panel, + u32 offset, u32 data) +{ + /* + * To make sure interface reg writes happens before any other operation, + * this function uses writel() instread of writel_relaxed() + */ + writel(data, panel->p0_base + offset); +} + +static inline u32 msm_dp_read_p0(struct msm_dp_panel_private *panel, + u32 offset) +{ + /* + * To make sure interface reg writes happens before any other operation, + * this function uses writel() instread of writel_relaxed() + */ + return readl_relaxed(panel->p0_base + offset); +} + static void msm_dp_panel_read_psr_cap(struct msm_dp_panel_private *panel) { ssize_t rlen; @@ -47,7 +88,7 @@ static void msm_dp_panel_read_psr_cap(struct msm_dp_panel_private *panel) static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) { - int rc; + int rc, max_lttpr_lanes, max_lttpr_rate; struct msm_dp_panel_private *panel; struct msm_dp_link_info *link_info; u8 *dpcd, major, minor; @@ -75,6 +116,16 @@ static int msm_dp_panel_read_dpcd(struct msm_dp_panel *msm_dp_panel) if (link_info->rate > msm_dp_panel->max_dp_link_rate) link_info->rate = msm_dp_panel->max_dp_link_rate; + /* Limit data lanes from LTTPR capabilities, if any */ + max_lttpr_lanes = drm_dp_lttpr_max_lane_count(panel->link->lttpr_common_caps); + if (max_lttpr_lanes && max_lttpr_lanes < link_info->num_lanes) + link_info->num_lanes = max_lttpr_lanes; + + /* Limit link rate from LTTPR capabilities, if any */ + max_lttpr_rate = drm_dp_lttpr_max_link_rate(panel->link->lttpr_common_caps); + if (max_lttpr_rate && max_lttpr_rate < link_info->rate) + link_info->rate = max_lttpr_rate; + drm_dbg_dp(panel->drm_dev, "version: %d.%d\n", major, minor); drm_dbg_dp(panel->drm_dev, "link_rate=%d\n", link_info->rate); drm_dbg_dp(panel->drm_dev, "lane_count=%d\n", link_info->num_lanes); @@ -162,7 +213,7 @@ int msm_dp_panel_read_sink_caps(struct msm_dp_panel *msm_dp_panel, if (!msm_dp_panel->drm_edid) { DRM_ERROR("panel edid read failed\n"); /* check edid read fail is due to unplug */ - if (!msm_dp_catalog_link_is_connected(panel->catalog)) { + if (!msm_dp_aux_is_link_connected(panel->aux)) { rc = -ETIMEDOUT; goto end; } @@ -242,9 +293,85 @@ void msm_dp_panel_handle_sink_request(struct msm_dp_panel *msm_dp_panel) } } +static void msm_dp_panel_tpg_enable(struct msm_dp_panel *msm_dp_panel, + struct drm_display_mode *drm_mode) +{ + struct msm_dp_panel_private *panel = + container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); + u32 hsync_period, vsync_period; + u32 display_v_start, display_v_end; + u32 hsync_start_x, hsync_end_x; + u32 v_sync_width; + u32 hsync_ctl; + u32 display_hctl; + + /* TPG config parameters*/ + hsync_period = drm_mode->htotal; + vsync_period = drm_mode->vtotal; + + display_v_start = ((drm_mode->vtotal - drm_mode->vsync_start) * + hsync_period); + display_v_end = ((vsync_period - (drm_mode->vsync_start - + drm_mode->vdisplay)) + * hsync_period) - 1; + + display_v_start += drm_mode->htotal - drm_mode->hsync_start; + display_v_end -= (drm_mode->hsync_start - drm_mode->hdisplay); + + hsync_start_x = drm_mode->htotal - drm_mode->hsync_start; + hsync_end_x = hsync_period - (drm_mode->hsync_start - + drm_mode->hdisplay) - 1; + + v_sync_width = drm_mode->vsync_end - drm_mode->vsync_start; + + hsync_ctl = (hsync_period << 16) | + (drm_mode->hsync_end - drm_mode->hsync_start); + display_hctl = (hsync_end_x << 16) | hsync_start_x; + + + msm_dp_write_p0(panel, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl); + msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period * + hsync_period); + msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width * + hsync_period); + msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl); + msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_HCTL, 0); + msm_dp_write_p0(panel, MMSS_INTF_DISPLAY_V_START_F0, display_v_start); + msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end); + msm_dp_write_p0(panel, MMSS_INTF_DISPLAY_V_START_F1, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_DISPLAY_V_END_F1, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_START_F0, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_END_F0, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_START_F1, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_ACTIVE_V_END_F1, 0); + msm_dp_write_p0(panel, MMSS_DP_INTF_POLARITY_CTL, 0); + + msm_dp_write_p0(panel, MMSS_DP_TPG_MAIN_CONTROL, + DP_TPG_CHECKERED_RECT_PATTERN); + msm_dp_write_p0(panel, MMSS_DP_TPG_VIDEO_CONFIG, + DP_TPG_VIDEO_CONFIG_BPP_8BIT | + DP_TPG_VIDEO_CONFIG_RGB); + msm_dp_write_p0(panel, MMSS_DP_BIST_ENABLE, + DP_BIST_ENABLE_DPBIST_EN); + msm_dp_write_p0(panel, MMSS_DP_TIMING_ENGINE_EN, + DP_TIMING_ENGINE_EN_EN); + drm_dbg_dp(panel->drm_dev, "%s: enabled tpg\n", __func__); +} + +static void msm_dp_panel_tpg_disable(struct msm_dp_panel *msm_dp_panel) +{ + struct msm_dp_panel_private *panel = + container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); + + msm_dp_write_p0(panel, MMSS_DP_TPG_MAIN_CONTROL, 0x0); + msm_dp_write_p0(panel, MMSS_DP_BIST_ENABLE, 0x0); + msm_dp_write_p0(panel, MMSS_DP_TIMING_ENGINE_EN, 0x0); +} + void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable) { - struct msm_dp_catalog *catalog; struct msm_dp_panel_private *panel; if (!msm_dp_panel) { @@ -253,7 +380,6 @@ void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable) } panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); - catalog = panel->catalog; if (!panel->panel_on) { drm_dbg_dp(panel->drm_dev, @@ -262,18 +388,109 @@ void msm_dp_panel_tpg_config(struct msm_dp_panel *msm_dp_panel, bool enable) } if (!enable) { - msm_dp_catalog_panel_tpg_disable(catalog); + msm_dp_panel_tpg_disable(msm_dp_panel); return; } - drm_dbg_dp(panel->drm_dev, "calling catalog tpg_enable\n"); - msm_dp_catalog_panel_tpg_enable(catalog, &panel->msm_dp_panel.msm_dp_mode.drm_mode); + drm_dbg_dp(panel->drm_dev, "calling panel's tpg_enable\n"); + msm_dp_panel_tpg_enable(msm_dp_panel, &panel->msm_dp_panel.msm_dp_mode.drm_mode); +} + +void msm_dp_panel_clear_dsc_dto(struct msm_dp_panel *msm_dp_panel) +{ + struct msm_dp_panel_private *panel = + container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); + + msm_dp_write_p0(panel, MMSS_DP_DSC_DTO, 0x0); +} + +static void msm_dp_panel_send_vsc_sdp(struct msm_dp_panel_private *panel, struct dp_sdp *vsc_sdp) +{ + u32 header[2]; + u32 val; + int i; + + msm_dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header); + + msm_dp_write_link(panel, MMSS_DP_GENERIC0_0, header[0]); + msm_dp_write_link(panel, MMSS_DP_GENERIC0_1, header[1]); + + for (i = 0; i < sizeof(vsc_sdp->db); i += 4) { + val = ((vsc_sdp->db[i]) | (vsc_sdp->db[i + 1] << 8) | (vsc_sdp->db[i + 2] << 16) | + (vsc_sdp->db[i + 3] << 24)); + msm_dp_write_link(panel, MMSS_DP_GENERIC0_2 + i, val); + } +} + +static void msm_dp_panel_update_sdp(struct msm_dp_panel_private *panel) +{ + u32 hw_revision = panel->msm_dp_panel.hw_revision; + + if (hw_revision >= DP_HW_VERSION_1_0 && + hw_revision < DP_HW_VERSION_1_2) { + msm_dp_write_link(panel, MMSS_DP_SDP_CFG3, UPDATE_SDP); + msm_dp_write_link(panel, MMSS_DP_SDP_CFG3, 0x0); + } +} + +void msm_dp_panel_enable_vsc_sdp(struct msm_dp_panel *msm_dp_panel, struct dp_sdp *vsc_sdp) +{ + struct msm_dp_panel_private *panel = + container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); + u32 cfg, cfg2, misc; + + cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG); + cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2); + misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0); + + cfg |= GEN0_SDP_EN; + msm_dp_write_link(panel, MMSS_DP_SDP_CFG, cfg); + + cfg2 |= GENERIC0_SDPSIZE_VALID; + msm_dp_write_link(panel, MMSS_DP_SDP_CFG2, cfg2); + + msm_dp_panel_send_vsc_sdp(panel, vsc_sdp); + + /* indicates presence of VSC (BIT(6) of MISC1) */ + misc |= DP_MISC1_VSC_SDP; + + drm_dbg_dp(panel->drm_dev, "vsc sdp enable=1\n"); + + pr_debug("misc settings = 0x%x\n", misc); + msm_dp_write_link(panel, REG_DP_MISC1_MISC0, misc); + + msm_dp_panel_update_sdp(panel); +} + +void msm_dp_panel_disable_vsc_sdp(struct msm_dp_panel *msm_dp_panel) +{ + struct msm_dp_panel_private *panel = + container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); + u32 cfg, cfg2, misc; + + cfg = msm_dp_read_link(panel, MMSS_DP_SDP_CFG); + cfg2 = msm_dp_read_link(panel, MMSS_DP_SDP_CFG2); + misc = msm_dp_read_link(panel, REG_DP_MISC1_MISC0); + + cfg &= ~GEN0_SDP_EN; + msm_dp_write_link(panel, MMSS_DP_SDP_CFG, cfg); + + cfg2 &= ~GENERIC0_SDPSIZE_VALID; + msm_dp_write_link(panel, MMSS_DP_SDP_CFG2, cfg2); + + /* switch back to MSA */ + misc &= ~DP_MISC1_VSC_SDP; + + drm_dbg_dp(panel->drm_dev, "vsc sdp enable=0\n"); + + pr_debug("misc settings = 0x%x\n", misc); + msm_dp_write_link(panel, REG_DP_MISC1_MISC0, misc); + + msm_dp_panel_update_sdp(panel); } static int msm_dp_panel_setup_vsc_sdp_yuv_420(struct msm_dp_panel *msm_dp_panel) { - struct msm_dp_catalog *catalog; - struct msm_dp_panel_private *panel; struct msm_dp_display_mode *msm_dp_mode; struct drm_dp_vsc_sdp vsc_sdp_data; struct dp_sdp vsc_sdp; @@ -284,8 +501,6 @@ static int msm_dp_panel_setup_vsc_sdp_yuv_420(struct msm_dp_panel *msm_dp_panel) return -EINVAL; } - panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); - catalog = panel->catalog; msm_dp_mode = &msm_dp_panel->msm_dp_mode; memset(&vsc_sdp_data, 0, sizeof(vsc_sdp_data)); @@ -312,24 +527,23 @@ static int msm_dp_panel_setup_vsc_sdp_yuv_420(struct msm_dp_panel *msm_dp_panel) return len; } - msm_dp_catalog_panel_enable_vsc_sdp(catalog, &vsc_sdp); + msm_dp_panel_enable_vsc_sdp(msm_dp_panel, &vsc_sdp); return 0; } -int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel) +int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel, bool wide_bus_en) { u32 data, total_ver, total_hor; - struct msm_dp_catalog *catalog; struct msm_dp_panel_private *panel; struct drm_display_mode *drm_mode; u32 width_blanking; u32 sync_start; u32 msm_dp_active; u32 total; + u32 reg; panel = container_of(msm_dp_panel, struct msm_dp_panel_private, msm_dp_panel); - catalog = panel->catalog; drm_mode = &panel->msm_dp_panel.msm_dp_mode.drm_mode; drm_dbg_dp(panel->drm_dev, "width=%d hporch= %d %d %d\n", @@ -372,7 +586,20 @@ int msm_dp_panel_timing_cfg(struct msm_dp_panel *msm_dp_panel) msm_dp_active = data; - msm_dp_catalog_panel_timing_cfg(catalog, total, sync_start, width_blanking, msm_dp_active); + msm_dp_write_link(panel, REG_DP_TOTAL_HOR_VER, total); + msm_dp_write_link(panel, REG_DP_START_HOR_VER_FROM_SYNC, sync_start); + msm_dp_write_link(panel, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY, width_blanking); + msm_dp_write_link(panel, REG_DP_ACTIVE_HOR_VER, msm_dp_active); + + reg = msm_dp_read_p0(panel, MMSS_DP_INTF_CONFIG); + if (wide_bus_en) + reg |= DP_INTF_CONFIG_DATABUS_WIDEN; + else + reg &= ~DP_INTF_CONFIG_DATABUS_WIDEN; + + drm_dbg_dp(panel->drm_dev, "wide_bus_en=%d reg=%#x\n", wide_bus_en, reg); + + msm_dp_write_p0(panel, MMSS_DP_INTF_CONFIG, reg); if (msm_dp_panel->msm_dp_mode.out_fmt_is_yuv_420) msm_dp_panel_setup_vsc_sdp_yuv_420(msm_dp_panel); @@ -476,13 +703,15 @@ static int msm_dp_panel_parse_dt(struct msm_dp_panel *msm_dp_panel) } struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux, - struct msm_dp_link *link, struct msm_dp_catalog *catalog) + struct msm_dp_link *link, + void __iomem *link_base, + void __iomem *p0_base) { struct msm_dp_panel_private *panel; struct msm_dp_panel *msm_dp_panel; int ret; - if (!dev || !catalog || !aux || !link) { + if (!dev || !aux || !link) { DRM_ERROR("invalid input\n"); return ERR_PTR(-EINVAL); } @@ -493,8 +722,9 @@ struct msm_dp_panel *msm_dp_panel_get(struct device *dev, struct drm_dp_aux *aux panel->dev = dev; panel->aux = aux; - panel->catalog = catalog; panel->link = link; + panel->link_base = link_base; + panel->p0_base = p0_base; msm_dp_panel = &panel->msm_dp_panel; msm_dp_panel->max_bw_code = DP_LINK_BW_8_1; |