summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.c93
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.h6
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.c4
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.c52
-rw-r--r--drivers/gpu/drm/msm/dp/dp_reg.h3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_utils.c23
-rw-r--r--drivers/gpu/drm/msm/dp/dp_utils.h14
7 files changed, 195 insertions, 0 deletions
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c
index e3c9bca01a2a..0bdab9bd21b9 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.c
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.c
@@ -892,6 +892,99 @@ int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog)
return 0;
}
+static void dp_catalog_panel_send_vsc_sdp(struct dp_catalog *dp_catalog, struct dp_sdp *vsc_sdp)
+{
+ struct dp_catalog_private *catalog;
+ u32 header[2];
+ u32 val;
+ int i;
+
+ catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
+
+ dp_utils_pack_sdp_header(&vsc_sdp->sdp_header, header);
+
+ dp_write_link(catalog, MMSS_DP_GENERIC0_0, header[0]);
+ dp_write_link(catalog, 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));
+ dp_write_link(catalog, MMSS_DP_GENERIC0_2 + i, val);
+ }
+}
+
+static void dp_catalog_panel_update_sdp(struct dp_catalog *dp_catalog)
+{
+ struct dp_catalog_private *catalog;
+ u32 hw_revision;
+
+ catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
+
+ hw_revision = dp_catalog_hw_revision(dp_catalog);
+ if (hw_revision < DP_HW_VERSION_1_2 && hw_revision >= DP_HW_VERSION_1_0) {
+ dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x01);
+ dp_write_link(catalog, MMSS_DP_SDP_CFG3, 0x00);
+ }
+}
+
+void dp_catalog_panel_enable_vsc_sdp(struct dp_catalog *dp_catalog, struct dp_sdp *vsc_sdp)
+{
+ struct dp_catalog_private *catalog;
+ u32 cfg, cfg2, misc;
+
+ catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
+
+ cfg = dp_read_link(catalog, MMSS_DP_SDP_CFG);
+ cfg2 = dp_read_link(catalog, MMSS_DP_SDP_CFG2);
+ misc = dp_read_link(catalog, REG_DP_MISC1_MISC0);
+
+ cfg |= GEN0_SDP_EN;
+ dp_write_link(catalog, MMSS_DP_SDP_CFG, cfg);
+
+ cfg2 |= GENERIC0_SDPSIZE_VALID;
+ dp_write_link(catalog, MMSS_DP_SDP_CFG2, cfg2);
+
+ dp_catalog_panel_send_vsc_sdp(dp_catalog, vsc_sdp);
+
+ /* indicates presence of VSC (BIT(6) of MISC1) */
+ misc |= DP_MISC1_VSC_SDP;
+
+ drm_dbg_dp(catalog->drm_dev, "vsc sdp enable=1\n");
+
+ pr_debug("misc settings = 0x%x\n", misc);
+ dp_write_link(catalog, REG_DP_MISC1_MISC0, misc);
+
+ dp_catalog_panel_update_sdp(dp_catalog);
+}
+
+void dp_catalog_panel_disable_vsc_sdp(struct dp_catalog *dp_catalog)
+{
+ struct dp_catalog_private *catalog;
+ u32 cfg, cfg2, misc;
+
+ catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
+
+ cfg = dp_read_link(catalog, MMSS_DP_SDP_CFG);
+ cfg2 = dp_read_link(catalog, MMSS_DP_SDP_CFG2);
+ misc = dp_read_link(catalog, REG_DP_MISC1_MISC0);
+
+ cfg &= ~GEN0_SDP_EN;
+ dp_write_link(catalog, MMSS_DP_SDP_CFG, cfg);
+
+ cfg2 &= ~GENERIC0_SDPSIZE_VALID;
+ dp_write_link(catalog, MMSS_DP_SDP_CFG2, cfg2);
+
+ /* switch back to MSA */
+ misc &= ~DP_MISC1_VSC_SDP;
+
+ drm_dbg_dp(catalog->drm_dev, "vsc sdp enable=0\n");
+
+ pr_debug("misc settings = 0x%x\n", misc);
+ dp_write_link(catalog, REG_DP_MISC1_MISC0, misc);
+
+ dp_catalog_panel_update_sdp(dp_catalog);
+}
+
void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
struct drm_display_mode *drm_mode)
{
diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h
index 2af961e93382..a9c70054d4a4 100644
--- a/drivers/gpu/drm/msm/dp/dp_catalog.h
+++ b/drivers/gpu/drm/msm/dp/dp_catalog.h
@@ -8,6 +8,7 @@
#include <drm/drm_modes.h>
+#include "dp_utils.h"
#include "disp/msm_disp_snapshot.h"
/* interrupts */
@@ -29,6 +30,9 @@
#define DP_AUX_CFG_MAX_VALUE_CNT 3
+#define DP_HW_VERSION_1_0 0x10000000
+#define DP_HW_VERSION_1_2 0x10020000
+
/* PHY AUX config registers */
enum dp_phy_aux_config_type {
PHY_AUX_CFG0,
@@ -120,6 +124,8 @@ u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog);
/* DP Panel APIs */
int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog);
+void dp_catalog_panel_enable_vsc_sdp(struct dp_catalog *dp_catalog, struct dp_sdp *vsc_sdp);
+void dp_catalog_panel_disable_vsc_sdp(struct dp_catalog *dp_catalog);
void dp_catalog_dump_regs(struct dp_catalog *dp_catalog);
void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
struct drm_display_mode *drm_mode);
diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c
index f861d32d7cfa..24787aa738cc 100644
--- a/drivers/gpu/drm/msm/dp/dp_ctrl.c
+++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c
@@ -2048,6 +2048,8 @@ void dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl)
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
phy = ctrl->phy;
+ dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
+
/* set dongle to D3 (power off) mode */
dp_link_psm_config(ctrl->link, &ctrl->panel->link_info, true);
@@ -2100,6 +2102,8 @@ void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
phy = ctrl->phy;
+ dp_catalog_panel_disable_vsc_sdp(ctrl->catalog);
+
dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false);
dp_catalog_ctrl_reset(ctrl->catalog);
diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c
index c6e90c99bcc8..8e7069453952 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_utils.h"
#include <drm/drm_connector.h>
#include <drm/drm_edid.h>
@@ -288,6 +289,53 @@ void dp_panel_tpg_config(struct dp_panel *dp_panel, bool enable)
dp_catalog_panel_tpg_enable(catalog, &panel->dp_panel.dp_mode.drm_mode);
}
+static int dp_panel_setup_vsc_sdp_yuv_420(struct dp_panel *dp_panel)
+{
+ struct dp_catalog *catalog;
+ struct dp_panel_private *panel;
+ struct dp_display_mode *dp_mode;
+ struct drm_dp_vsc_sdp vsc_sdp_data;
+ struct dp_sdp vsc_sdp;
+ ssize_t len;
+
+ if (!dp_panel) {
+ DRM_ERROR("invalid input\n");
+ return -EINVAL;
+ }
+
+ panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
+ catalog = panel->catalog;
+ dp_mode = &dp_panel->dp_mode;
+
+ memset(&vsc_sdp_data, 0, sizeof(vsc_sdp_data));
+
+ /* VSC SDP header as per table 2-118 of DP 1.4 specification */
+ vsc_sdp_data.sdp_type = DP_SDP_VSC;
+ vsc_sdp_data.revision = 0x05;
+ vsc_sdp_data.length = 0x13;
+
+ /* VSC SDP Payload for DB16 */
+ vsc_sdp_data.pixelformat = DP_PIXELFORMAT_YUV420;
+ vsc_sdp_data.colorimetry = DP_COLORIMETRY_DEFAULT;
+
+ /* VSC SDP Payload for DB17 */
+ vsc_sdp_data.bpc = dp_mode->bpp / 3;
+ vsc_sdp_data.dynamic_range = DP_DYNAMIC_RANGE_CTA;
+
+ /* VSC SDP Payload for DB18 */
+ vsc_sdp_data.content_type = DP_CONTENT_TYPE_GRAPHICS;
+
+ len = drm_dp_vsc_sdp_pack(&vsc_sdp_data, &vsc_sdp);
+ if (len < 0) {
+ DRM_ERROR("unable to pack vsc sdp\n");
+ return len;
+ }
+
+ dp_catalog_panel_enable_vsc_sdp(catalog, &vsc_sdp);
+
+ return 0;
+}
+
void dp_panel_dump_regs(struct dp_panel *dp_panel)
{
struct dp_catalog *catalog;
@@ -351,6 +399,10 @@ int dp_panel_timing_cfg(struct dp_panel *dp_panel)
catalog->dp_active = data;
dp_catalog_panel_timing_cfg(catalog);
+
+ if (dp_panel->dp_mode.out_fmt_is_yuv_420)
+ dp_panel_setup_vsc_sdp_yuv_420(dp_panel);
+
panel->panel_on = true;
return 0;
diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h
index 78785ed4b40c..aa9f6c3e4dde 100644
--- a/drivers/gpu/drm/msm/dp/dp_reg.h
+++ b/drivers/gpu/drm/msm/dp/dp_reg.h
@@ -142,6 +142,7 @@
#define DP_MISC0_SYNCHRONOUS_CLK (0x00000001)
#define DP_MISC0_COLORIMETRY_CFG_SHIFT (0x00000001)
#define DP_MISC0_TEST_BITS_DEPTH_SHIFT (0x00000005)
+#define DP_MISC1_VSC_SDP (0x00004000)
#define DP_MISC0_COLORIMERY_CFG_LEGACY_RGB (0)
#define DP_MISC0_COLORIMERY_CFG_CEA_RGB (0x04)
@@ -204,9 +205,11 @@
#define MMSS_DP_AUDIO_CTRL_RESET (0x00000214)
#define MMSS_DP_SDP_CFG (0x00000228)
+#define GEN0_SDP_EN (0x00020000)
#define MMSS_DP_SDP_CFG2 (0x0000022C)
#define MMSS_DP_AUDIO_TIMESTAMP_0 (0x00000230)
#define MMSS_DP_AUDIO_TIMESTAMP_1 (0x00000234)
+#define GENERIC0_SDPSIZE_VALID (0x00010000)
#define MMSS_DP_AUDIO_STREAM_0 (0x00000240)
#define MMSS_DP_AUDIO_STREAM_1 (0x00000244)
diff --git a/drivers/gpu/drm/msm/dp/dp_utils.c b/drivers/gpu/drm/msm/dp/dp_utils.c
index 3a44fe738c00..da9207caf72d 100644
--- a/drivers/gpu/drm/msm/dp/dp_utils.c
+++ b/drivers/gpu/drm/msm/dp/dp_utils.c
@@ -7,6 +7,8 @@
#include "dp_utils.h"
+#define DP_SDP_HEADER_SIZE 8
+
u8 dp_utils_get_g0_value(u8 data)
{
u8 c[4];
@@ -71,3 +73,24 @@ u8 dp_utils_calculate_parity(u32 data)
return parity_byte;
}
+
+ssize_t dp_utils_pack_sdp_header(struct dp_sdp_header *sdp_header, u32 *header_buff)
+{
+ size_t length;
+
+ length = sizeof(header_buff);
+ if (length < DP_SDP_HEADER_SIZE)
+ return -ENOSPC;
+
+ header_buff[0] = FIELD_PREP(HEADER_0_MASK, sdp_header->HB0) |
+ FIELD_PREP(PARITY_0_MASK, dp_utils_calculate_parity(sdp_header->HB0)) |
+ FIELD_PREP(HEADER_1_MASK, sdp_header->HB1) |
+ FIELD_PREP(PARITY_1_MASK, dp_utils_calculate_parity(sdp_header->HB1));
+
+ header_buff[1] = FIELD_PREP(HEADER_2_MASK, sdp_header->HB2) |
+ FIELD_PREP(PARITY_2_MASK, dp_utils_calculate_parity(sdp_header->HB2)) |
+ FIELD_PREP(HEADER_3_MASK, sdp_header->HB3) |
+ FIELD_PREP(PARITY_3_MASK, dp_utils_calculate_parity(sdp_header->HB3));
+
+ return length;
+}
diff --git a/drivers/gpu/drm/msm/dp/dp_utils.h b/drivers/gpu/drm/msm/dp/dp_utils.h
index 5a505cbf3432..7c056d9798dc 100644
--- a/drivers/gpu/drm/msm/dp/dp_utils.h
+++ b/drivers/gpu/drm/msm/dp/dp_utils.h
@@ -6,6 +6,10 @@
#ifndef _DP_UTILS_H_
#define _DP_UTILS_H_
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <drm/display/drm_dp_helper.h>
+
#define HEADER_BYTE_0_BIT 0
#define PARITY_BYTE_0_BIT 8
#define HEADER_BYTE_1_BIT 16
@@ -15,8 +19,18 @@
#define HEADER_BYTE_3_BIT 16
#define PARITY_BYTE_3_BIT 24
+#define HEADER_0_MASK GENMASK(7, 0)
+#define PARITY_0_MASK GENMASK(15, 8)
+#define HEADER_1_MASK GENMASK(23, 16)
+#define PARITY_1_MASK GENMASK(31, 24)
+#define HEADER_2_MASK GENMASK(7, 0)
+#define PARITY_2_MASK GENMASK(15, 8)
+#define HEADER_3_MASK GENMASK(23, 16)
+#define PARITY_3_MASK GENMASK(31, 24)
+
u8 dp_utils_get_g0_value(u8 data);
u8 dp_utils_get_g1_value(u8 data);
u8 dp_utils_calculate_parity(u32 data);
+ssize_t dp_utils_pack_sdp_header(struct dp_sdp_header *sdp_header, u32 *header_buff);
#endif /* _DP_UTILS_H_ */