diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dp')
22 files changed, 879 insertions, 1156 deletions
diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c b/drivers/gpu/drm/msm/dp/dp_audio.c index 4a2e479723a8..7634e4b74208 100644 --- a/drivers/gpu/drm/msm/dp/dp_audio.c +++ b/drivers/gpu/drm/msm/dp/dp_audio.c @@ -15,13 +15,7 @@ #include "dp_audio.h" #include "dp_panel.h" #include "dp_display.h" - -#define HEADER_BYTE_2_BIT 0 -#define PARITY_BYTE_2_BIT 8 -#define HEADER_BYTE_1_BIT 16 -#define PARITY_BYTE_1_BIT 24 -#define HEADER_BYTE_3_BIT 16 -#define PARITY_BYTE_3_BIT 24 +#include "dp_utils.h" struct dp_audio_private { struct platform_device *audio_pdev; @@ -36,71 +30,6 @@ struct dp_audio_private { struct dp_audio dp_audio; }; -static u8 dp_audio_get_g0_value(u8 data) -{ - u8 c[4]; - u8 g[4]; - u8 ret_data = 0; - u8 i; - - for (i = 0; i < 4; i++) - c[i] = (data >> i) & 0x01; - - g[0] = c[3]; - g[1] = c[0] ^ c[3]; - g[2] = c[1]; - g[3] = c[2]; - - for (i = 0; i < 4; i++) - ret_data = ((g[i] & 0x01) << i) | ret_data; - - return ret_data; -} - -static u8 dp_audio_get_g1_value(u8 data) -{ - u8 c[4]; - u8 g[4]; - u8 ret_data = 0; - u8 i; - - for (i = 0; i < 4; i++) - c[i] = (data >> i) & 0x01; - - g[0] = c[0] ^ c[3]; - g[1] = c[0] ^ c[1] ^ c[3]; - g[2] = c[1] ^ c[2]; - g[3] = c[2] ^ c[3]; - - for (i = 0; i < 4; i++) - ret_data = ((g[i] & 0x01) << i) | ret_data; - - return ret_data; -} - -static u8 dp_audio_calculate_parity(u32 data) -{ - u8 x0 = 0; - u8 x1 = 0; - u8 ci = 0; - u8 iData = 0; - u8 i = 0; - u8 parity_byte; - u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2; - - for (i = 0; i < num_byte; i++) { - iData = (data >> i*4) & 0xF; - - ci = iData ^ x1; - x1 = x0 ^ dp_audio_get_g1_value(ci); - x0 = dp_audio_get_g0_value(ci); - } - - parity_byte = x1 | (x0 << 4); - - return parity_byte; -} - static u32 dp_audio_get_header(struct dp_catalog *catalog, enum dp_catalog_audio_sdp_type sdp, enum dp_catalog_audio_header_type header) @@ -134,7 +63,7 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_1); new_value = 0x02; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_1_BIT) | (parity_byte << PARITY_BYTE_1_BIT)); drm_dbg_dp(audio->drm_dev, @@ -147,7 +76,7 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio) value = dp_audio_get_header(catalog, DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_2); new_value = value; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_2_BIT) | (parity_byte << PARITY_BYTE_2_BIT)); drm_dbg_dp(audio->drm_dev, @@ -162,7 +91,7 @@ static void dp_audio_stream_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_STREAM, DP_AUDIO_SDP_HEADER_3); new_value = audio->channels - 1; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_3_BIT) | (parity_byte << PARITY_BYTE_3_BIT)); drm_dbg_dp(audio->drm_dev, @@ -184,7 +113,7 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_1); new_value = 0x1; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_1_BIT) | (parity_byte << PARITY_BYTE_1_BIT)); drm_dbg_dp(audio->drm_dev, @@ -198,7 +127,7 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_2); new_value = 0x17; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_2_BIT) | (parity_byte << PARITY_BYTE_2_BIT)); drm_dbg_dp(audio->drm_dev, @@ -212,7 +141,7 @@ static void dp_audio_timestamp_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_TIMESTAMP, DP_AUDIO_SDP_HEADER_3); new_value = (0x0 | (0x11 << 2)); - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_3_BIT) | (parity_byte << PARITY_BYTE_3_BIT)); drm_dbg_dp(audio->drm_dev, @@ -233,7 +162,7 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_1); new_value = 0x84; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_1_BIT) | (parity_byte << PARITY_BYTE_1_BIT)); drm_dbg_dp(audio->drm_dev, @@ -247,7 +176,7 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_2); new_value = 0x1b; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_2_BIT) | (parity_byte << PARITY_BYTE_2_BIT)); drm_dbg_dp(audio->drm_dev, @@ -261,7 +190,7 @@ static void dp_audio_infoframe_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_INFOFRAME, DP_AUDIO_SDP_HEADER_3); new_value = (0x0 | (0x11 << 2)); - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_3_BIT) | (parity_byte << PARITY_BYTE_3_BIT)); drm_dbg_dp(audio->drm_dev, @@ -282,7 +211,7 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_1); new_value = 0x05; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_1_BIT) | (parity_byte << PARITY_BYTE_1_BIT)); drm_dbg_dp(audio->drm_dev, @@ -296,7 +225,7 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_2); new_value = 0x0F; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_2_BIT) | (parity_byte << PARITY_BYTE_2_BIT)); drm_dbg_dp(audio->drm_dev, @@ -310,7 +239,7 @@ static void dp_audio_copy_management_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_COPYMANAGEMENT, DP_AUDIO_SDP_HEADER_3); new_value = 0x0; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_3_BIT) | (parity_byte << PARITY_BYTE_3_BIT)); drm_dbg_dp(audio->drm_dev, @@ -331,7 +260,7 @@ static void dp_audio_isrc_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_1); new_value = 0x06; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_1_BIT) | (parity_byte << PARITY_BYTE_1_BIT)); drm_dbg_dp(audio->drm_dev, @@ -345,7 +274,7 @@ static void dp_audio_isrc_sdp(struct dp_audio_private *audio) DP_AUDIO_SDP_ISRC, DP_AUDIO_SDP_HEADER_2); new_value = 0x0F; - parity_byte = dp_audio_calculate_parity(new_value); + parity_byte = dp_utils_calculate_parity(new_value); value |= ((new_value << HEADER_BYTE_2_BIT) | (parity_byte << PARITY_BYTE_2_BIT)); drm_dbg_dp(audio->drm_dev, diff --git a/drivers/gpu/drm/msm/dp/dp_aux.c b/drivers/gpu/drm/msm/dp/dp_aux.c index 03f4951c49f4..adbd5a367395 100644 --- a/drivers/gpu/drm/msm/dp/dp_aux.c +++ b/drivers/gpu/drm/msm/dp/dp_aux.c @@ -4,6 +4,7 @@ */ #include <linux/delay.h> +#include <linux/phy/phy.h> #include <drm/drm_print.h> #include "dp_reg.h" @@ -23,6 +24,8 @@ struct dp_aux_private { struct device *dev; struct dp_catalog *catalog; + struct phy *phy; + struct mutex mutex; struct completion comp; @@ -336,7 +339,7 @@ static ssize_t dp_aux_transfer(struct drm_dp_aux *dp_aux, if (aux->native) { aux->retry_cnt++; if (!(aux->retry_cnt % MAX_AUX_RETRIES)) - dp_catalog_aux_update_cfg(aux->catalog); + phy_calibrate(aux->phy); } /* reset aux if link is in connected state */ if (dp_catalog_link_is_connected(aux->catalog)) @@ -439,7 +442,7 @@ void dp_aux_reconfig(struct drm_dp_aux *dp_aux) aux = container_of(dp_aux, struct dp_aux_private, dp_aux); - dp_catalog_aux_update_cfg(aux->catalog); + phy_calibrate(aux->phy); dp_catalog_aux_reset(aux->catalog); } @@ -517,6 +520,7 @@ static int dp_wait_hpd_asserted(struct drm_dp_aux *dp_aux, } struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog, + struct phy *phy, bool is_edp) { struct dp_aux_private *aux; @@ -537,6 +541,7 @@ struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog, aux->dev = dev; aux->catalog = catalog; + aux->phy = phy; aux->retry_cnt = 0; /* diff --git a/drivers/gpu/drm/msm/dp/dp_aux.h b/drivers/gpu/drm/msm/dp/dp_aux.h index 511305da4f66..f47d591c1f54 100644 --- a/drivers/gpu/drm/msm/dp/dp_aux.h +++ b/drivers/gpu/drm/msm/dp/dp_aux.h @@ -16,7 +16,9 @@ void dp_aux_init(struct drm_dp_aux *dp_aux); void dp_aux_deinit(struct drm_dp_aux *dp_aux); void dp_aux_reconfig(struct drm_dp_aux *dp_aux); +struct phy; struct drm_dp_aux *dp_aux_get(struct device *dev, struct dp_catalog *catalog, + struct phy *phy, bool is_edp); void dp_aux_put(struct drm_dp_aux *aux); diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.c b/drivers/gpu/drm/msm/dp/dp_catalog.c index 5142aeb705a4..3e7c84cdef47 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.c +++ b/drivers/gpu/drm/msm/dp/dp_catalog.c @@ -7,8 +7,7 @@ #include <linux/delay.h> #include <linux/iopoll.h> -#include <linux/phy/phy.h> -#include <linux/phy/phy-dp.h> +#include <linux/platform_device.h> #include <linux/rational.h> #include <drm/display/drm_dp_helper.h> #include <drm/drm_print.h> @@ -55,10 +54,31 @@ (PSR_UPDATE_MASK | PSR_CAPTURE_MASK | PSR_EXIT_MASK | \ PSR_UPDATE_ERROR_MASK | PSR_WAKE_ERROR_MASK) +#define DP_DEFAULT_AHB_OFFSET 0x0000 +#define DP_DEFAULT_AHB_SIZE 0x0200 +#define DP_DEFAULT_AUX_OFFSET 0x0200 +#define DP_DEFAULT_AUX_SIZE 0x0200 +#define DP_DEFAULT_LINK_OFFSET 0x0400 +#define DP_DEFAULT_LINK_SIZE 0x0C00 +#define DP_DEFAULT_P0_OFFSET 0x1000 +#define DP_DEFAULT_P0_SIZE 0x0400 + +struct dss_io_region { + size_t len; + void __iomem *base; +}; + +struct dss_io_data { + struct dss_io_region ahb; + struct dss_io_region aux; + struct dss_io_region link; + struct dss_io_region p0; +}; + struct dp_catalog_private { struct device *dev; struct drm_device *drm_dev; - struct dp_io *io; + struct dss_io_data io; u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX]; struct dp_catalog dp_catalog; u8 aux_lut_cfg_index[PHY_AUX_CFG_MAX]; @@ -68,7 +88,7 @@ void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *d { struct dp_catalog_private *catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog); - struct dss_io_data *dss = &catalog->io->dp_controller; + struct dss_io_data *dss = &catalog->io; msm_disp_snapshot_add_block(disp_state, dss->ahb.len, dss->ahb.base, "dp_ahb"); msm_disp_snapshot_add_block(disp_state, dss->aux.len, dss->aux.base, "dp_aux"); @@ -78,7 +98,7 @@ void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *d static inline u32 dp_read_aux(struct dp_catalog_private *catalog, u32 offset) { - return readl_relaxed(catalog->io->dp_controller.aux.base + offset); + return readl_relaxed(catalog->io.aux.base + offset); } static inline void dp_write_aux(struct dp_catalog_private *catalog, @@ -88,12 +108,12 @@ static inline void dp_write_aux(struct dp_catalog_private *catalog, * To make sure aux reg writes happens before any other operation, * this function uses writel() instread of writel_relaxed() */ - writel(data, catalog->io->dp_controller.aux.base + offset); + writel(data, catalog->io.aux.base + offset); } static inline u32 dp_read_ahb(const struct dp_catalog_private *catalog, u32 offset) { - return readl_relaxed(catalog->io->dp_controller.ahb.base + offset); + return readl_relaxed(catalog->io.ahb.base + offset); } static inline void dp_write_ahb(struct dp_catalog_private *catalog, @@ -103,7 +123,7 @@ static inline void dp_write_ahb(struct dp_catalog_private *catalog, * To make sure phy reg writes happens before any other operation, * this function uses writel() instread of writel_relaxed() */ - writel(data, catalog->io->dp_controller.ahb.base + offset); + writel(data, catalog->io.ahb.base + offset); } static inline void dp_write_p0(struct dp_catalog_private *catalog, @@ -113,7 +133,7 @@ static inline void dp_write_p0(struct dp_catalog_private *catalog, * To make sure interface reg writes happens before any other operation, * this function uses writel() instread of writel_relaxed() */ - writel(data, catalog->io->dp_controller.p0.base + offset); + writel(data, catalog->io.p0.base + offset); } static inline u32 dp_read_p0(struct dp_catalog_private *catalog, @@ -123,12 +143,12 @@ static inline u32 dp_read_p0(struct dp_catalog_private *catalog, * To make sure interface reg writes happens before any other operation, * this function uses writel() instread of writel_relaxed() */ - return readl_relaxed(catalog->io->dp_controller.p0.base + offset); + return readl_relaxed(catalog->io.p0.base + offset); } static inline u32 dp_read_link(struct dp_catalog_private *catalog, u32 offset) { - return readl_relaxed(catalog->io->dp_controller.link.base + offset); + return readl_relaxed(catalog->io.link.base + offset); } static inline void dp_write_link(struct dp_catalog_private *catalog, @@ -138,7 +158,7 @@ static inline void dp_write_link(struct dp_catalog_private *catalog, * To make sure link reg writes happens before any other operation, * this function uses writel() instread of writel_relaxed() */ - writel(data, catalog->io->dp_controller.link.base + offset); + writel(data, catalog->io.link.base + offset); } /* aux related catalog functions */ @@ -243,16 +263,6 @@ void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable) dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl); } -void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog) -{ - struct dp_catalog_private *catalog = container_of(dp_catalog, - struct dp_catalog_private, dp_catalog); - struct dp_io *dp_io = catalog->io; - struct phy *phy = dp_io->phy; - - phy_calibrate(phy); -} - int dp_catalog_aux_wait_for_hpd_connect_state(struct dp_catalog *dp_catalog) { u32 state; @@ -260,7 +270,7 @@ int dp_catalog_aux_wait_for_hpd_connect_state(struct dp_catalog *dp_catalog) struct dp_catalog_private, dp_catalog); /* poll for hpd connected status every 2ms and timeout after 500ms */ - return readl_poll_timeout(catalog->io->dp_controller.aux.base + + return readl_poll_timeout(catalog->io.aux.base + REG_DP_DP_HPD_INT_STATUS, state, state & DP_DP_HPD_STATE_STATUS_CONNECTED, 2000, 500000); @@ -288,7 +298,7 @@ void dp_catalog_dump_regs(struct dp_catalog *dp_catalog) { struct dp_catalog_private *catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog); - struct dss_io_data *io = &catalog->io->dp_controller; + struct dss_io_data *io = &catalog->io; pr_info("AHB regs\n"); dump_regs(io->ahb.base, io->ahb.len); @@ -440,9 +450,26 @@ void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, dp_write_link(catalog, REG_DP_MISC1_MISC0, misc_val); } +void dp_catalog_setup_peripheral_flush(struct dp_catalog *dp_catalog) +{ + u32 mainlink_ctrl, hw_revision; + struct dp_catalog_private *catalog = container_of(dp_catalog, + struct dp_catalog_private, dp_catalog); + + mainlink_ctrl = dp_read_link(catalog, REG_DP_MAINLINK_CTRL); + + hw_revision = dp_catalog_hw_revision(dp_catalog); + if (hw_revision >= DP_HW_VERSION_1_2) + mainlink_ctrl |= DP_MAINLINK_FLUSH_MODE_SDE_PERIPH_UPDATE; + else + mainlink_ctrl |= DP_MAINLINK_FLUSH_MODE_UPDATE_SDP; + + dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl); +} + void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate, u32 stream_rate_khz, - bool fixed_nvid) + bool fixed_nvid, bool is_ycbcr_420) { u32 pixel_m, pixel_n; u32 mvid, nvid, pixel_div = 0, dispcc_input_rate; @@ -485,6 +512,9 @@ void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, nvid = temp; } + if (is_ycbcr_420) + mvid /= 2; + if (link_rate_hbr2 == rate) nvid *= 2; @@ -512,7 +542,7 @@ int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, bit = BIT(state_bit - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT; /* Poll for mainlink ready status */ - ret = readx_poll_timeout(readl, catalog->io->dp_controller.link.base + + ret = readx_poll_timeout(readl, catalog->io.link.base + REG_DP_MAINLINK_READY, data, data & bit, POLLING_SLEEP_US, POLLING_TIMEOUT_US); @@ -575,7 +605,7 @@ bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog) struct dp_catalog_private, dp_catalog); /* Poll for mainlink ready status */ - ret = readl_poll_timeout(catalog->io->dp_controller.link.base + + ret = readl_poll_timeout(catalog->io.link.base + REG_DP_MAINLINK_READY, data, data & DP_MAINLINK_READY_FOR_VIDEO, POLLING_SLEEP_US, POLLING_TIMEOUT_US); @@ -765,25 +795,6 @@ void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog) dp_write_ahb(catalog, REG_DP_PHY_CTRL, 0x0); } -int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, - u8 v_level, u8 p_level) -{ - struct dp_catalog_private *catalog = container_of(dp_catalog, - struct dp_catalog_private, dp_catalog); - struct dp_io *dp_io = catalog->io; - struct phy *phy = dp_io->phy; - struct phy_configure_opts_dp *opts_dp = &dp_io->phy_opts.dp; - - /* TODO: Update for all lanes instead of just first one */ - opts_dp->voltage[0] = v_level; - opts_dp->pre[0] = p_level; - opts_dp->set_voltages = 1; - phy_configure(phy, &dp_io->phy_opts); - opts_dp->set_voltages = 0; - - return 0; -} - void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog, u32 pattern) { @@ -898,6 +909,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) { @@ -976,21 +1080,84 @@ void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog) dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN, 0x0); } -struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io) +static void __iomem *dp_ioremap(struct platform_device *pdev, int idx, size_t *len) { - struct dp_catalog_private *catalog; + struct resource *res; + void __iomem *base; + + base = devm_platform_get_and_ioremap_resource(pdev, idx, &res); + if (!IS_ERR(base)) + *len = resource_size(res); + + return base; +} + +static int dp_catalog_get_io(struct dp_catalog_private *catalog) +{ + struct platform_device *pdev = to_platform_device(catalog->dev); + struct dss_io_data *dss = &catalog->io; + + dss->ahb.base = dp_ioremap(pdev, 0, &dss->ahb.len); + if (IS_ERR(dss->ahb.base)) + return PTR_ERR(dss->ahb.base); - if (!io) { - DRM_ERROR("invalid input\n"); - return ERR_PTR(-EINVAL); + dss->aux.base = dp_ioremap(pdev, 1, &dss->aux.len); + if (IS_ERR(dss->aux.base)) { + /* + * The initial binding had a single reg, but in order to + * support variation in the sub-region sizes this was split. + * dp_ioremap() will fail with -EINVAL here if only a single + * reg is specified, so fill in the sub-region offsets and + * lengths based on this single region. + */ + if (PTR_ERR(dss->aux.base) == -EINVAL) { + if (dss->ahb.len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) { + DRM_ERROR("legacy memory region not large enough\n"); + return -EINVAL; + } + + dss->ahb.len = DP_DEFAULT_AHB_SIZE; + dss->aux.base = dss->ahb.base + DP_DEFAULT_AUX_OFFSET; + dss->aux.len = DP_DEFAULT_AUX_SIZE; + dss->link.base = dss->ahb.base + DP_DEFAULT_LINK_OFFSET; + dss->link.len = DP_DEFAULT_LINK_SIZE; + dss->p0.base = dss->ahb.base + DP_DEFAULT_P0_OFFSET; + dss->p0.len = DP_DEFAULT_P0_SIZE; + } else { + DRM_ERROR("unable to remap aux region: %pe\n", dss->aux.base); + return PTR_ERR(dss->aux.base); + } + } else { + dss->link.base = dp_ioremap(pdev, 2, &dss->link.len); + if (IS_ERR(dss->link.base)) { + DRM_ERROR("unable to remap link region: %pe\n", dss->link.base); + return PTR_ERR(dss->link.base); + } + + dss->p0.base = dp_ioremap(pdev, 3, &dss->p0.len); + if (IS_ERR(dss->p0.base)) { + DRM_ERROR("unable to remap p0 region: %pe\n", dss->p0.base); + return PTR_ERR(dss->p0.base); + } } + return 0; +} + +struct dp_catalog *dp_catalog_get(struct device *dev) +{ + struct dp_catalog_private *catalog; + int ret; + catalog = devm_kzalloc(dev, sizeof(*catalog), GFP_KERNEL); if (!catalog) return ERR_PTR(-ENOMEM); catalog->dev = dev; - catalog->io = io; + + ret = dp_catalog_get_io(catalog); + if (ret) + return ERR_PTR(ret); return &catalog->dp_catalog; } diff --git a/drivers/gpu/drm/msm/dp/dp_catalog.h b/drivers/gpu/drm/msm/dp/dp_catalog.h index 38786e855b51..75ec290127c7 100644 --- a/drivers/gpu/drm/msm/dp/dp_catalog.h +++ b/drivers/gpu/drm/msm/dp/dp_catalog.h @@ -8,7 +8,7 @@ #include <drm/drm_modes.h> -#include "dp_parser.h" +#include "dp_utils.h" #include "disp/msm_disp_snapshot.h" /* interrupts */ @@ -30,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, @@ -84,7 +87,6 @@ int dp_catalog_aux_clear_trans(struct dp_catalog *dp_catalog, bool read); int dp_catalog_aux_clear_hw_interrupts(struct dp_catalog *dp_catalog); void dp_catalog_aux_reset(struct dp_catalog *dp_catalog); void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable); -void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog); int dp_catalog_aux_wait_for_hpd_connect_state(struct dp_catalog *dp_catalog); u32 dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog); @@ -94,9 +96,10 @@ void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 config); void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog); void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog, bool enable); void dp_catalog_ctrl_psr_mainlink_enable(struct dp_catalog *dp_catalog, bool enable); +void dp_catalog_setup_peripheral_flush(struct dp_catalog *dp_catalog); void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog, u32 cc, u32 tb); void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog, u32 rate, - u32 stream_rate_khz, bool fixed_nvid); + u32 stream_rate_khz, bool fixed_nvid, bool is_ycbcr_420); int dp_catalog_ctrl_set_pattern_state_bit(struct dp_catalog *dp_catalog, u32 pattern); u32 dp_catalog_hw_revision(const struct dp_catalog *dp_catalog); void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog); @@ -111,8 +114,6 @@ void dp_catalog_ctrl_set_psr(struct dp_catalog *dp_catalog, bool enter); u32 dp_catalog_link_is_connected(struct dp_catalog *dp_catalog); u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog); void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog); -int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog, u8 v_level, - u8 p_level); int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog); u32 dp_catalog_ctrl_read_psr_interrupt_status(struct dp_catalog *dp_catalog); void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog, @@ -124,12 +125,14 @@ 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); void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog); -struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io); +struct dp_catalog *dp_catalog_get(struct device *dev); /* DP Audio APIs */ void dp_catalog_audio_get_header(struct dp_catalog *catalog); diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.c b/drivers/gpu/drm/msm/dp/dp_ctrl.c index fb588fde298a..c4dda1faef67 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.c +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.c @@ -76,13 +76,27 @@ struct dp_ctrl_private { struct drm_dp_aux *aux; struct dp_panel *panel; struct dp_link *link; - struct dp_power *power; - struct dp_parser *parser; struct dp_catalog *catalog; + struct phy *phy; + + unsigned int num_core_clks; + struct clk_bulk_data *core_clks; + + unsigned int num_link_clks; + struct clk_bulk_data *link_clks; + + struct clk *pixel_clk; + + union phy_configure_opts phy_opts; + struct completion idle_comp; struct completion psr_op_comp; struct completion video_comp; + + bool core_clks_on; + bool link_clks_on; + bool stream_clks_on; }; static int dp_aux_link_configure(struct drm_dp_aux *aux, @@ -128,6 +142,9 @@ static void dp_ctrl_config_ctrl(struct dp_ctrl_private *ctrl) /* Default-> LSCLK DIV: 1/4 LCLK */ config |= (2 << DP_CONFIGURATION_CTRL_LSCLK_DIV_SHIFT); + if (ctrl->panel->dp_mode.out_fmt_is_yuv_420) + config |= DP_CONFIGURATION_CTRL_RGB_YUV; /* YUV420 */ + /* Scrambler reset enable */ if (drm_dp_alternate_scrambler_reset_cap(dpcd)) config |= DP_CONFIGURATION_CTRL_ASSR; @@ -162,6 +179,7 @@ static void dp_ctrl_configure_source_params(struct dp_ctrl_private *ctrl) dp_catalog_ctrl_lane_mapping(ctrl->catalog); dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, true); + dp_catalog_setup_peripheral_flush(ctrl->catalog); dp_ctrl_config_ctrl(ctrl); @@ -952,7 +970,7 @@ static void dp_ctrl_calc_tu_parameters(struct dp_ctrl_private *ctrl, in.hporch = drm_mode->htotal - drm_mode->hdisplay; in.nlanes = ctrl->link->link_params.num_lanes; in.bpp = ctrl->panel->dp_mode.bpp; - in.pixel_enc = 444; + in.pixel_enc = ctrl->panel->dp_mode.out_fmt_is_yuv_420 ? 420 : 444; in.dsc_en = 0; in.async_en = 0; in.fec_en = 0; @@ -1001,6 +1019,21 @@ static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl) return ret; } +static int dp_ctrl_set_vx_px(struct dp_ctrl_private *ctrl, + u8 v_level, u8 p_level) +{ + union phy_configure_opts *phy_opts = &ctrl->phy_opts; + + /* TODO: Update for all lanes instead of just first one */ + phy_opts->dp.voltage[0] = v_level; + phy_opts->dp.pre[0] = p_level; + phy_opts->dp.set_voltages = 1; + phy_configure(ctrl->phy, phy_opts); + phy_opts->dp.set_voltages = 0; + + return 0; +} + static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl) { struct dp_link *link = ctrl->link; @@ -1013,7 +1046,7 @@ static int dp_ctrl_update_vx_px(struct dp_ctrl_private *ctrl) drm_dbg_dp(ctrl->drm_dev, "voltage level: %d emphasis level: %d\n", voltage_swing_level, pre_emphasis_level); - ret = dp_catalog_ctrl_update_vx_px(ctrl->catalog, + ret = dp_ctrl_set_vx_px(ctrl, voltage_swing_level, pre_emphasis_level); if (ret) @@ -1312,44 +1345,115 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, return ret; } -static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl, - enum dp_pm_type module, char *name, unsigned long rate) +int dp_ctrl_core_clk_enable(struct dp_ctrl *dp_ctrl) { - u32 num = ctrl->parser->mp[module].num_clk; - struct clk_bulk_data *cfg = ctrl->parser->mp[module].clocks; + struct dp_ctrl_private *ctrl; + int ret = 0; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - while (num && strcmp(cfg->id, name)) { - num--; - cfg++; + if (ctrl->core_clks_on) { + drm_dbg_dp(ctrl->drm_dev, "core clks already enabled\n"); + return 0; } - drm_dbg_dp(ctrl->drm_dev, "setting rate=%lu on clk=%s\n", - rate, name); + ret = clk_bulk_prepare_enable(ctrl->num_core_clks, ctrl->core_clks); + if (ret) + return ret; - if (num) - clk_set_rate(cfg->clk, rate); - else - DRM_ERROR("%s clock doesn't exit to set rate %lu\n", - name, rate); + ctrl->core_clks_on = true; + + drm_dbg_dp(ctrl->drm_dev, "enable core clocks \n"); + drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", + ctrl->stream_clks_on ? "on" : "off", + ctrl->link_clks_on ? "on" : "off", + ctrl->core_clks_on ? "on" : "off"); + + return 0; +} + +void dp_ctrl_core_clk_disable(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + clk_bulk_disable_unprepare(ctrl->num_core_clks, ctrl->core_clks); + + ctrl->core_clks_on = false; + + drm_dbg_dp(ctrl->drm_dev, "disable core clocks \n"); + drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", + ctrl->stream_clks_on ? "on" : "off", + ctrl->link_clks_on ? "on" : "off", + ctrl->core_clks_on ? "on" : "off"); +} + +static int dp_ctrl_link_clk_enable(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + int ret = 0; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + if (ctrl->link_clks_on) { + drm_dbg_dp(ctrl->drm_dev, "links clks already enabled\n"); + return 0; + } + + if (!ctrl->core_clks_on) { + drm_dbg_dp(ctrl->drm_dev, "Enable core clks before link clks\n"); + + dp_ctrl_core_clk_enable(dp_ctrl); + } + + ret = clk_bulk_prepare_enable(ctrl->num_link_clks, ctrl->link_clks); + if (ret) + return ret; + + ctrl->link_clks_on = true; + + drm_dbg_dp(ctrl->drm_dev, "enable link clocks\n"); + drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", + ctrl->stream_clks_on ? "on" : "off", + ctrl->link_clks_on ? "on" : "off", + ctrl->core_clks_on ? "on" : "off"); + + return 0; +} + +static void dp_ctrl_link_clk_disable(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + + clk_bulk_disable_unprepare(ctrl->num_link_clks, ctrl->link_clks); + + ctrl->link_clks_on = false; + + drm_dbg_dp(ctrl->drm_dev, "disabled link clocks\n"); + drm_dbg_dp(ctrl->drm_dev, "stream_clks:%s link_clks:%s core_clks:%s\n", + ctrl->stream_clks_on ? "on" : "off", + ctrl->link_clks_on ? "on" : "off", + ctrl->core_clks_on ? "on" : "off"); } static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl) { int ret = 0; - struct dp_io *dp_io = &ctrl->parser->io; - struct phy *phy = dp_io->phy; - struct phy_configure_opts_dp *opts_dp = &dp_io->phy_opts.dp; + struct phy *phy = ctrl->phy; const u8 *dpcd = ctrl->panel->dpcd; - opts_dp->lanes = ctrl->link->link_params.num_lanes; - opts_dp->link_rate = ctrl->link->link_params.rate / 100; - opts_dp->ssc = drm_dp_max_downspread(dpcd); + ctrl->phy_opts.dp.lanes = ctrl->link->link_params.num_lanes; + ctrl->phy_opts.dp.link_rate = ctrl->link->link_params.rate / 100; + ctrl->phy_opts.dp.ssc = drm_dp_max_downspread(dpcd); - phy_configure(phy, &dp_io->phy_opts); + phy_configure(phy, &ctrl->phy_opts); phy_power_on(phy); dev_pm_opp_set_rate(ctrl->dev, ctrl->link->link_params.rate * 1000); - ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, true); + ret = dp_ctrl_link_clk_enable(&ctrl->dp_ctrl); if (ret) DRM_ERROR("Unable to start link clocks. ret=%d\n", ret); @@ -1436,12 +1540,10 @@ void dp_ctrl_set_psr(struct dp_ctrl *dp_ctrl, bool enter) void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; - struct dp_io *dp_io; struct phy *phy; ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - dp_io = &ctrl->parser->io; - phy = dp_io->phy; + phy = ctrl->phy; dp_catalog_ctrl_phy_reset(ctrl->catalog); phy_init(phy); @@ -1453,12 +1555,10 @@ void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl) void dp_ctrl_phy_exit(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; - struct dp_io *dp_io; struct phy *phy; ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - dp_io = &ctrl->parser->io; - phy = dp_io->phy; + phy = ctrl->phy; dp_catalog_ctrl_phy_reset(ctrl->catalog); phy_exit(phy); @@ -1483,25 +1583,21 @@ static bool dp_ctrl_use_fixed_nvid(struct dp_ctrl_private *ctrl) static int dp_ctrl_reinitialize_mainlink(struct dp_ctrl_private *ctrl) { + struct phy *phy = ctrl->phy; int ret = 0; - struct dp_io *dp_io = &ctrl->parser->io; - struct phy *phy = dp_io->phy; - struct phy_configure_opts_dp *opts_dp = &dp_io->phy_opts.dp; dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false); - opts_dp->lanes = ctrl->link->link_params.num_lanes; - phy_configure(phy, &dp_io->phy_opts); + ctrl->phy_opts.dp.lanes = ctrl->link->link_params.num_lanes; + phy_configure(phy, &ctrl->phy_opts); /* * Disable and re-enable the mainlink clock since the * link clock might have been adjusted as part of the * link maintenance. */ dev_pm_opp_set_rate(ctrl->dev, 0); - ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false); - if (ret) { - DRM_ERROR("Failed to disable clocks. ret=%d\n", ret); - return ret; - } + + dp_ctrl_link_clk_disable(&ctrl->dp_ctrl); + phy_power_off(phy); /* hw recommended delay before re-enabling clocks */ msleep(20); @@ -1517,22 +1613,16 @@ static int dp_ctrl_reinitialize_mainlink(struct dp_ctrl_private *ctrl) static int dp_ctrl_deinitialize_mainlink(struct dp_ctrl_private *ctrl) { - struct dp_io *dp_io; struct phy *phy; - int ret; - dp_io = &ctrl->parser->io; - phy = dp_io->phy; + phy = ctrl->phy; dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false); dp_catalog_ctrl_reset(ctrl->catalog); dev_pm_opp_set_rate(ctrl->dev, 0); - ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false); - if (ret) { - DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret); - } + dp_ctrl_link_clk_disable(&ctrl->dp_ctrl); phy_power_off(phy); @@ -1576,7 +1666,7 @@ static bool dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl) drm_dbg_dp(ctrl->drm_dev, "request: 0x%x\n", pattern_requested); - if (dp_catalog_ctrl_update_vx_px(ctrl->catalog, + if (dp_ctrl_set_vx_px(ctrl, ctrl->link->phy_params.v_level, ctrl->link->phy_params.p_level)) { DRM_ERROR("Failed to set v/p levels\n"); @@ -1636,11 +1726,7 @@ static int dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl) * running. Add the global reset just before disabling the * link clocks and core clocks. */ - ret = dp_ctrl_off(&ctrl->dp_ctrl); - if (ret) { - DRM_ERROR("failed to disable DP controller\n"); - return ret; - } + dp_ctrl_off(&ctrl->dp_ctrl); ret = dp_ctrl_on_link(&ctrl->dp_ctrl); if (ret) { @@ -1649,14 +1735,23 @@ static int dp_ctrl_process_phy_test_request(struct dp_ctrl_private *ctrl) } pixel_rate = ctrl->panel->dp_mode.drm_mode.clock; - dp_ctrl_set_clock_rate(ctrl, DP_STREAM_PM, "stream_pixel", pixel_rate * 1000); - - ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, true); + ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000); if (ret) { - DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret); + DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret); return ret; } + if (ctrl->stream_clks_on) { + drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n"); + } else { + ret = clk_prepare_enable(ctrl->pixel_clk); + if (ret) { + DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret); + return ret; + } + ctrl->stream_clks_on = true; + } + dp_ctrl_send_phy_test_pattern(ctrl); return 0; @@ -1747,7 +1842,7 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl) rate = ctrl->panel->link_info.rate; pixel_rate = ctrl->panel->dp_mode.drm_mode.clock; - dp_power_clk_enable(ctrl->power, DP_CORE_PM, true); + dp_ctrl_core_clk_enable(&ctrl->dp_ctrl); if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) { drm_dbg_dp(ctrl->drm_dev, @@ -1758,6 +1853,8 @@ int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl) ctrl->link->link_params.rate = rate; ctrl->link->link_params.num_lanes = ctrl->panel->link_info.num_lanes; + if (ctrl->panel->dp_mode.out_fmt_is_yuv_420) + pixel_rate >>= 1; } drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n", @@ -1873,14 +1970,18 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train) pixel_rate = pixel_rate_orig = ctrl->panel->dp_mode.drm_mode.clock; - if (dp_ctrl->wide_bus_en) + if (dp_ctrl->wide_bus_en || ctrl->panel->dp_mode.out_fmt_is_yuv_420) pixel_rate >>= 1; drm_dbg_dp(ctrl->drm_dev, "rate=%d, num_lanes=%d, pixel_rate=%lu\n", ctrl->link->link_params.rate, ctrl->link->link_params.num_lanes, pixel_rate); - if (!dp_power_clk_status(ctrl->power, DP_CTRL_PM)) { /* link clk is off */ + drm_dbg_dp(ctrl->drm_dev, + "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n", + ctrl->core_clks_on, ctrl->link_clks_on, ctrl->stream_clks_on); + + if (!ctrl->link_clks_on) { /* link clk is off */ ret = dp_ctrl_enable_mainlink_clocks(ctrl); if (ret) { DRM_ERROR("Failed to start link clocks. ret=%d\n", ret); @@ -1888,14 +1989,23 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train) } } - dp_ctrl_set_clock_rate(ctrl, DP_STREAM_PM, "stream_pixel", pixel_rate * 1000); - - ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, true); + ret = clk_set_rate(ctrl->pixel_clk, pixel_rate * 1000); if (ret) { - DRM_ERROR("Unable to start pixel clocks. ret=%d\n", ret); + DRM_ERROR("Failed to set pixel clock rate. ret=%d\n", ret); goto end; } + if (ctrl->stream_clks_on) { + drm_dbg_dp(ctrl->drm_dev, "pixel clks already enabled\n"); + } else { + ret = clk_prepare_enable(ctrl->pixel_clk); + if (ret) { + DRM_ERROR("Failed to start pixel clocks. ret=%d\n", ret); + goto end; + } + ctrl->stream_clks_on = true; + } + if (force_link_train || !dp_ctrl_channel_eq_ok(ctrl)) dp_ctrl_link_retrain(ctrl); @@ -1912,7 +2022,8 @@ int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train) dp_catalog_ctrl_config_msa(ctrl->catalog, ctrl->link->link_params.rate, - pixel_rate_orig, dp_ctrl_use_fixed_nvid(ctrl)); + pixel_rate_orig, dp_ctrl_use_fixed_nvid(ctrl), + ctrl->panel->dp_mode.out_fmt_is_yuv_420); dp_ctrl_setup_tr_unit(ctrl); @@ -1930,36 +2041,28 @@ end: return ret; } -int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl) +void dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; - struct dp_io *dp_io; struct phy *phy; - int ret; ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - dp_io = &ctrl->parser->io; - phy = dp_io->phy; + 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); dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false); - if (dp_power_clk_status(ctrl->power, DP_STREAM_PM)) { - ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, false); - if (ret) { - DRM_ERROR("Failed to disable pclk. ret=%d\n", ret); - return ret; - } + if (ctrl->stream_clks_on) { + clk_disable_unprepare(ctrl->pixel_clk); + ctrl->stream_clks_on = false; } dev_pm_opp_set_rate(ctrl->dev, 0); - ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false); - if (ret) { - DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret); - return ret; - } + dp_ctrl_link_clk_disable(&ctrl->dp_ctrl); phy_power_off(phy); @@ -1969,26 +2072,19 @@ int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl) drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", phy, phy->init_count, phy->power_count); - return ret; } -int dp_ctrl_off_link(struct dp_ctrl *dp_ctrl) +void dp_ctrl_off_link(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; - struct dp_io *dp_io; struct phy *phy; - int ret; ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - dp_io = &ctrl->parser->io; - phy = dp_io->phy; + phy = ctrl->phy; dp_catalog_ctrl_mainlink_ctrl(ctrl->catalog, false); - ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false); - if (ret) { - DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret); - } + dp_ctrl_link_clk_disable(&ctrl->dp_ctrl); DRM_DEBUG_DP("Before, phy=%p init_count=%d power_on=%d\n", phy, phy->init_count, phy->power_count); @@ -1997,43 +2093,33 @@ int dp_ctrl_off_link(struct dp_ctrl *dp_ctrl) DRM_DEBUG_DP("After, phy=%p init_count=%d power_on=%d\n", phy, phy->init_count, phy->power_count); - - return ret; } -int dp_ctrl_off(struct dp_ctrl *dp_ctrl) +void dp_ctrl_off(struct dp_ctrl *dp_ctrl) { struct dp_ctrl_private *ctrl; - struct dp_io *dp_io; struct phy *phy; - int ret = 0; - - if (!dp_ctrl) - return -EINVAL; ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); - dp_io = &ctrl->parser->io; - phy = dp_io->phy; + 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); - ret = dp_power_clk_enable(ctrl->power, DP_STREAM_PM, false); - if (ret) - DRM_ERROR("Failed to disable pixel clocks. ret=%d\n", ret); + if (ctrl->stream_clks_on) { + clk_disable_unprepare(ctrl->pixel_clk); + ctrl->stream_clks_on = false; + } dev_pm_opp_set_rate(ctrl->dev, 0); - ret = dp_power_clk_enable(ctrl->power, DP_CTRL_PM, false); - if (ret) { - DRM_ERROR("Failed to disable link clocks. ret=%d\n", ret); - } + dp_ctrl_link_clk_disable(&ctrl->dp_ctrl); phy_power_off(phy); drm_dbg_dp(ctrl->drm_dev, "phy=%p init=%d power_on=%d\n", phy, phy->init_count, phy->power_count); - - return ret; } irqreturn_t dp_ctrl_isr(struct dp_ctrl *dp_ctrl) @@ -2081,10 +2167,60 @@ irqreturn_t dp_ctrl_isr(struct dp_ctrl *dp_ctrl) return ret; } +static const char *core_clks[] = { + "core_iface", + "core_aux", +}; + +static const char *ctrl_clks[] = { + "ctrl_link", + "ctrl_link_iface", +}; + +static int dp_ctrl_clk_init(struct dp_ctrl *dp_ctrl) +{ + struct dp_ctrl_private *ctrl; + struct device *dev; + int i, rc; + + ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl); + dev = ctrl->dev; + + ctrl->num_core_clks = ARRAY_SIZE(core_clks); + ctrl->core_clks = devm_kcalloc(dev, ctrl->num_core_clks, sizeof(*ctrl->core_clks), GFP_KERNEL); + if (!ctrl->core_clks) + return -ENOMEM; + + for (i = 0; i < ctrl->num_core_clks; i++) + ctrl->core_clks[i].id = core_clks[i]; + + rc = devm_clk_bulk_get(dev, ctrl->num_core_clks, ctrl->core_clks); + if (rc) + return rc; + + ctrl->num_link_clks = ARRAY_SIZE(ctrl_clks); + ctrl->link_clks = devm_kcalloc(dev, ctrl->num_link_clks, sizeof(*ctrl->link_clks), GFP_KERNEL); + if (!ctrl->link_clks) + return -ENOMEM; + + for (i = 0; i < ctrl->num_link_clks; i++) + ctrl->link_clks[i].id = ctrl_clks[i]; + + rc = devm_clk_bulk_get(dev, ctrl->num_link_clks, ctrl->link_clks); + if (rc) + return rc; + + ctrl->pixel_clk = devm_clk_get(dev, "stream_pixel"); + if (IS_ERR(ctrl->pixel_clk)) + return PTR_ERR(ctrl->pixel_clk); + + return 0; +} + struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link, struct dp_panel *panel, struct drm_dp_aux *aux, - struct dp_power *power, struct dp_catalog *catalog, - struct dp_parser *parser) + struct dp_catalog *catalog, + struct phy *phy) { struct dp_ctrl_private *ctrl; int ret; @@ -2118,13 +2254,18 @@ struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link, init_completion(&ctrl->video_comp); /* in parameters */ - ctrl->parser = parser; ctrl->panel = panel; - ctrl->power = power; ctrl->aux = aux; ctrl->link = link; ctrl->catalog = catalog; ctrl->dev = dev; + ctrl->phy = phy; + + ret = dp_ctrl_clk_init(&ctrl->dp_ctrl); + if (ret) { + dev_err(dev, "failed to init clocks\n"); + return ERR_PTR(ret); + } return &ctrl->dp_ctrl; } diff --git a/drivers/gpu/drm/msm/dp/dp_ctrl.h b/drivers/gpu/drm/msm/dp/dp_ctrl.h index b2c27d3532bf..fa014cee7e21 100644 --- a/drivers/gpu/drm/msm/dp/dp_ctrl.h +++ b/drivers/gpu/drm/msm/dp/dp_ctrl.h @@ -9,8 +9,6 @@ #include "dp_aux.h" #include "dp_panel.h" #include "dp_link.h" -#include "dp_parser.h" -#include "dp_power.h" #include "dp_catalog.h" struct dp_ctrl { @@ -18,18 +16,20 @@ struct dp_ctrl { bool wide_bus_en; }; +struct phy; + int dp_ctrl_on_link(struct dp_ctrl *dp_ctrl); int dp_ctrl_on_stream(struct dp_ctrl *dp_ctrl, bool force_link_train); -int dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl); -int dp_ctrl_off_link(struct dp_ctrl *dp_ctrl); -int dp_ctrl_off(struct dp_ctrl *dp_ctrl); +void dp_ctrl_off_link_stream(struct dp_ctrl *dp_ctrl); +void dp_ctrl_off_link(struct dp_ctrl *dp_ctrl); +void dp_ctrl_off(struct dp_ctrl *dp_ctrl); void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl); irqreturn_t dp_ctrl_isr(struct dp_ctrl *dp_ctrl); void dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl); struct dp_ctrl *dp_ctrl_get(struct device *dev, struct dp_link *link, struct dp_panel *panel, struct drm_dp_aux *aux, - struct dp_power *power, struct dp_catalog *catalog, - struct dp_parser *parser); + struct dp_catalog *catalog, + struct phy *phy); void dp_ctrl_reset_irq_ctrl(struct dp_ctrl *dp_ctrl, bool enable); void dp_ctrl_phy_init(struct dp_ctrl *dp_ctrl); @@ -39,4 +39,7 @@ void dp_ctrl_irq_phy_exit(struct dp_ctrl *dp_ctrl); void dp_ctrl_set_psr(struct dp_ctrl *dp_ctrl, bool enable); void dp_ctrl_config_psr(struct dp_ctrl *dp_ctrl); +int dp_ctrl_core_clk_enable(struct dp_ctrl *dp_ctrl); +void dp_ctrl_core_clk_disable(struct dp_ctrl *dp_ctrl); + #endif /* _DP_CTRL_H_ */ diff --git a/drivers/gpu/drm/msm/dp/dp_debug.c b/drivers/gpu/drm/msm/dp/dp_debug.c index 6c281dc095b9..eca5a02f9003 100644 --- a/drivers/gpu/drm/msm/dp/dp_debug.c +++ b/drivers/gpu/drm/msm/dp/dp_debug.c @@ -9,7 +9,6 @@ #include <drm/drm_connector.h> #include <drm/drm_file.h> -#include "dp_parser.h" #include "dp_catalog.h" #include "dp_aux.h" #include "dp_ctrl.h" @@ -101,7 +100,7 @@ static int dp_test_data_show(struct seq_file *m, void *data) seq_printf(m, "vdisplay: %d\n", debug->link->test_video.test_v_height); seq_printf(m, "bpc: %u\n", - dp_link_bit_depth_to_bpc(bpc)); + dp_link_bit_depth_to_bpp(bpc) / 3); } else { seq_puts(m, "0"); } diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 4c72124ffb5d..c4cb82af5c2f 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -9,19 +9,19 @@ #include <linux/debugfs.h> #include <linux/component.h> #include <linux/of_irq.h> +#include <linux/phy/phy.h> #include <linux/delay.h> #include <drm/display/drm_dp_aux_bus.h> +#include <drm/drm_edid.h> #include "msm_drv.h" #include "msm_kms.h" -#include "dp_parser.h" -#include "dp_power.h" +#include "dp_ctrl.h" #include "dp_catalog.h" #include "dp_aux.h" #include "dp_reg.h" #include "dp_link.h" #include "dp_panel.h" -#include "dp_ctrl.h" #include "dp_display.h" #include "dp_drm.h" #include "dp_audio.h" @@ -88,8 +88,6 @@ struct dp_display_private { struct drm_device *drm_dev; struct dentry *root; - struct dp_parser *parser; - struct dp_power *power; struct dp_catalog *catalog; struct drm_dp_aux *aux; struct dp_link *link; @@ -113,7 +111,7 @@ struct dp_display_private { struct dp_event event_list[DP_EVENT_Q_MAX]; spinlock_t event_lock; - bool wide_bus_en; + bool wide_bus_supported; struct dp_audio *audio; }; @@ -122,7 +120,7 @@ struct msm_dp_desc { phys_addr_t io_start; unsigned int id; unsigned int connector_type; - bool wide_bus_en; + bool wide_bus_supported; }; static const struct msm_dp_desc sc7180_dp_descs[] = { @@ -131,8 +129,8 @@ static const struct msm_dp_desc sc7180_dp_descs[] = { }; static const struct msm_dp_desc sc7280_dp_descs[] = { - { .io_start = 0x0ae90000, .id = MSM_DP_CONTROLLER_0, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x0aea0000, .id = MSM_DP_CONTROLLER_1, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_en = true }, + { .io_start = 0x0ae90000, .id = MSM_DP_CONTROLLER_0, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x0aea0000, .id = MSM_DP_CONTROLLER_1, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_supported = true }, {} }; @@ -144,22 +142,22 @@ static const struct msm_dp_desc sc8180x_dp_descs[] = { }; static const struct msm_dp_desc sc8280xp_dp_descs[] = { - { .io_start = 0x0ae90000, .id = MSM_DP_CONTROLLER_0, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x0ae98000, .id = MSM_DP_CONTROLLER_1, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x0ae9a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x0aea0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x22090000, .id = MSM_DP_CONTROLLER_0, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x22098000, .id = MSM_DP_CONTROLLER_1, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x2209a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, - { .io_start = 0x220a0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_en = true }, + { .io_start = 0x0ae90000, .id = MSM_DP_CONTROLLER_0, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x0ae98000, .id = MSM_DP_CONTROLLER_1, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x0ae9a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x0aea0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x22090000, .id = MSM_DP_CONTROLLER_0, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x22098000, .id = MSM_DP_CONTROLLER_1, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x2209a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, + { .io_start = 0x220a0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_DisplayPort, .wide_bus_supported = true }, {} }; static const struct msm_dp_desc sc8280xp_edp_descs[] = { - { .io_start = 0x0ae9a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_en = true }, - { .io_start = 0x0aea0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_en = true }, - { .io_start = 0x2209a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_en = true }, - { .io_start = 0x220a0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_en = true }, + { .io_start = 0x0ae9a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_supported = true }, + { .io_start = 0x0aea0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_supported = true }, + { .io_start = 0x2209a000, .id = MSM_DP_CONTROLLER_2, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_supported = true }, + { .io_start = 0x220a0000, .id = MSM_DP_CONTROLLER_3, .connector_type = DRM_MODE_CONNECTOR_eDP, .wide_bus_supported = true }, {} }; @@ -374,12 +372,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) int rc = 0; struct edid *edid; - dp->panel->max_dp_lanes = dp->parser->max_dp_lanes; - dp->panel->max_dp_link_rate = dp->parser->max_dp_link_rate; - - drm_dbg_dp(dp->drm_dev, "max_lanes=%d max_link_rate=%d\n", - dp->panel->max_dp_lanes, dp->panel->max_dp_link_rate); - rc = dp_panel_read_sink_caps(dp->panel, dp->dp_display.connector); if (rc) goto end; @@ -399,8 +391,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp) dp->audio_supported = drm_detect_monitor_audio(edid); dp_panel_handle_sink_request(dp->panel); - dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes; - /* * set sink to normal operation mode -- D0 * before dpcd read @@ -450,7 +440,7 @@ static void dp_display_host_init(struct dp_display_private *dp) dp->dp_display.connector_type, dp->core_initialized, dp->phy_initialized); - dp_power_init(dp->power); + dp_ctrl_core_clk_enable(dp->ctrl); dp_ctrl_reset_irq_ctrl(dp->ctrl, true); dp_aux_init(dp->aux); dp->core_initialized = true; @@ -464,7 +454,7 @@ static void dp_display_host_deinit(struct dp_display_private *dp) dp_ctrl_reset_irq_ctrl(dp->ctrl, false); dp_aux_deinit(dp->aux); - dp_power_deinit(dp->power); + dp_ctrl_core_clk_disable(dp->ctrl); dp->core_initialized = false; } @@ -730,16 +720,13 @@ static int dp_init_sub_modules(struct dp_display_private *dp) struct dp_panel_in panel_in = { .dev = dev, }; + struct phy *phy; - dp->parser = dp_parser_get(dp->dp_display.pdev); - if (IS_ERR(dp->parser)) { - rc = PTR_ERR(dp->parser); - DRM_ERROR("failed to initialize parser, rc = %d\n", rc); - dp->parser = NULL; - goto error; - } + phy = devm_phy_get(dev, "dp"); + if (IS_ERR(phy)) + return PTR_ERR(phy); - dp->catalog = dp_catalog_get(dev, &dp->parser->io); + dp->catalog = dp_catalog_get(dev); if (IS_ERR(dp->catalog)) { rc = PTR_ERR(dp->catalog); DRM_ERROR("failed to initialize catalog, rc = %d\n", rc); @@ -747,15 +734,9 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error; } - dp->power = dp_power_get(dev, dp->parser); - if (IS_ERR(dp->power)) { - rc = PTR_ERR(dp->power); - DRM_ERROR("failed to initialize power, rc = %d\n", rc); - dp->power = NULL; - goto error; - } - - dp->aux = dp_aux_get(dev, dp->catalog, dp->dp_display.is_edp); + dp->aux = dp_aux_get(dev, dp->catalog, + phy, + dp->dp_display.is_edp); if (IS_ERR(dp->aux)) { rc = PTR_ERR(dp->aux); DRM_ERROR("failed to initialize aux, rc = %d\n", rc); @@ -784,7 +765,8 @@ static int dp_init_sub_modules(struct dp_display_private *dp) } dp->ctrl = dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, - dp->power, dp->catalog, dp->parser); + dp->catalog, + phy); if (IS_ERR(dp->ctrl)) { rc = PTR_ERR(dp->ctrl); DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc); @@ -800,10 +782,6 @@ static int dp_init_sub_modules(struct dp_display_private *dp) goto error_ctrl; } - /* populate wide_bus_en to differernt layers */ - dp->ctrl->wide_bus_en = dp->wide_bus_en; - dp->catalog->wide_bus_en = dp->wide_bus_en; - return rc; error_ctrl: @@ -824,6 +802,7 @@ static int dp_display_set_mode(struct msm_dp *dp_display, drm_mode_copy(&dp->panel->dp_mode.drm_mode, &mode->drm_mode); dp->panel->dp_mode.bpp = mode->bpp; dp->panel->dp_mode.capabilities = mode->capabilities; + dp->panel->dp_mode.out_fmt_is_yuv_420 = mode->out_fmt_is_yuv_420; dp_panel_init_panel_info(dp->panel); return 0; } @@ -952,6 +931,10 @@ enum drm_mode_status dp_bridge_mode_valid(struct drm_bridge *bridge, dp_display = container_of(dp, struct dp_display_private, dp_display); link_info = &dp_display->panel->link_info; + if (drm_mode_is_420_only(&dp->connector->display_info, mode) && + dp_display->panel->vsc_sdp_supported) + mode_pclk_khz /= 2; + mode_bpp = dp->connector->display_info.bpc * num_components; if (!mode_bpp) mode_bpp = default_bpp; @@ -1226,16 +1209,25 @@ static const struct msm_dp_desc *dp_display_get_desc(struct platform_device *pde return NULL; } -static int dp_display_get_next_bridge(struct msm_dp *dp); - static int dp_display_probe_tail(struct device *dev) { struct msm_dp *dp = dev_get_drvdata(dev); int ret; - ret = dp_display_get_next_bridge(dp); - if (ret) - return ret; + /* + * External bridges are mandatory for eDP interfaces: one has to + * provide at least an eDP panel (which gets wrapped into panel-bridge). + * + * For DisplayPort interfaces external bridges are optional, so + * silently ignore an error if one is not present (-ENODEV). + */ + dp->next_bridge = devm_drm_of_get_bridge(&dp->pdev->dev, dp->pdev->dev.of_node, 1, 0); + if (IS_ERR(dp->next_bridge)) { + ret = PTR_ERR(dp->next_bridge); + dp->next_bridge = NULL; + if (dp->is_edp || ret != -ENODEV) + return ret; + } ret = component_add(dev, &dp_display_comp_ops); if (ret) @@ -1272,7 +1264,7 @@ static int dp_display_probe(struct platform_device *pdev) dp->name = "drm_dp"; dp->id = desc->id; dp->dp_display.connector_type = desc->connector_type; - dp->wide_bus_en = desc->wide_bus_en; + dp->wide_bus_supported = desc->wide_bus_supported; dp->dp_display.is_edp = (dp->dp_display.connector_type == DRM_MODE_CONNECTOR_eDP); @@ -1282,18 +1274,6 @@ static int dp_display_probe(struct platform_device *pdev) return -EPROBE_DEFER; } - rc = dp->parser->parse(dp->parser); - if (rc) { - DRM_ERROR("device tree parsing failed\n"); - goto err; - } - - rc = dp_power_client_init(dp->power); - if (rc) { - DRM_ERROR("Power client create failed\n"); - goto err; - } - /* setup event q */ mutex_init(&dp->event_mutex); init_waitqueue_head(&dp->event_q); @@ -1412,13 +1392,34 @@ void __exit msm_dp_unregister(void) platform_driver_unregister(&dp_display_driver); } +bool msm_dp_is_yuv_420_enabled(const struct msm_dp *dp_display, + const struct drm_display_mode *mode) +{ + struct dp_display_private *dp; + const struct drm_display_info *info; + + dp = container_of(dp_display, struct dp_display_private, dp_display); + info = &dp_display->connector->display_info; + + return dp->panel->vsc_sdp_supported && drm_mode_is_420_only(info, mode); +} + +bool msm_dp_needs_periph_flush(const struct msm_dp *dp_display, + const struct drm_display_mode *mode) +{ + return msm_dp_is_yuv_420_enabled(dp_display, mode); +} + bool msm_dp_wide_bus_available(const struct msm_dp *dp_display) { struct dp_display_private *dp; dp = container_of(dp_display, struct dp_display_private, dp_display); - return dp->wide_bus_en; + if (dp->dp_mode.out_fmt_is_yuv_420) + return false; + + return dp->wide_bus_supported; } void dp_display_debugfs_init(struct msm_dp *dp_display, struct dentry *root, bool is_edp) @@ -1440,32 +1441,8 @@ void dp_display_debugfs_init(struct msm_dp *dp_display, struct dentry *root, boo } } -static int dp_display_get_next_bridge(struct msm_dp *dp) -{ - int rc; - struct dp_display_private *dp_priv; - - dp_priv = container_of(dp, struct dp_display_private, dp_display); - - /* - * External bridges are mandatory for eDP interfaces: one has to - * provide at least an eDP panel (which gets wrapped into panel-bridge). - * - * For DisplayPort interfaces external bridges are optional, so - * silently ignore an error if one is not present (-ENODEV). - */ - rc = devm_dp_parser_find_next_bridge(&dp->pdev->dev, dp_priv->parser); - if (!dp->is_edp && rc == -ENODEV) - return 0; - - if (!rc) - dp->next_bridge = dp_priv->parser->next_bridge; - - return rc; -} - int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, - struct drm_encoder *encoder) + struct drm_encoder *encoder, bool yuv_supported) { struct dp_display_private *dp_priv; int ret; @@ -1481,7 +1458,7 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, return ret; } - dp_display->connector = dp_drm_connector_init(dp_display, encoder); + dp_display->connector = dp_drm_connector_init(dp_display, encoder, yuv_supported); if (IS_ERR(dp_display->connector)) { ret = PTR_ERR(dp_display->connector); DRM_DEV_ERROR(dev->dev, @@ -1611,8 +1588,10 @@ void dp_bridge_mode_set(struct drm_bridge *drm_bridge, struct msm_dp_bridge *dp_bridge = to_dp_bridge(drm_bridge); struct msm_dp *dp = dp_bridge->dp_display; struct dp_display_private *dp_display; + struct dp_panel *dp_panel; dp_display = container_of(dp, struct dp_display_private, dp_display); + dp_panel = dp_display->panel; memset(&dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode)); @@ -1631,6 +1610,16 @@ void dp_bridge_mode_set(struct drm_bridge *drm_bridge, dp_display->dp_mode.h_active_low = !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC); + + dp_display->dp_mode.out_fmt_is_yuv_420 = + drm_mode_is_420_only(&dp->connector->display_info, adjusted_mode) && + dp_panel->vsc_sdp_supported; + + /* populate wide_bus_support to different layers */ + dp_display->ctrl->wide_bus_en = + dp_display->dp_mode.out_fmt_is_yuv_420 ? false : dp_display->wide_bus_supported; + dp_display->catalog->wide_bus_en = + dp_display->dp_mode.out_fmt_is_yuv_420 ? false : dp_display->wide_bus_supported; } void dp_bridge_hpd_enable(struct drm_bridge *bridge) diff --git a/drivers/gpu/drm/msm/dp/dp_display.h b/drivers/gpu/drm/msm/dp/dp_display.h index 102f3507d824..234dada88687 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.h +++ b/drivers/gpu/drm/msm/dp/dp_display.h @@ -10,6 +10,8 @@ #include <sound/hdmi-codec.h> #include "disp/msm_disp_snapshot.h" +#define DP_MAX_PIXEL_CLK_KHZ 675000 + struct msm_dp { struct drm_device *drm_dev; struct platform_device *pdev; @@ -28,7 +30,6 @@ struct msm_dp { bool wide_bus_en; - u32 max_dp_lanes; struct dp_audio *dp_audio; bool psr_supported; }; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c index 46e6889037e8..a819a4ff76a9 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.c +++ b/drivers/gpu/drm/msm/dp/dp_drm.c @@ -353,7 +353,8 @@ int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, } /* connector initialization */ -struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder) +struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder, + bool yuv_supported) { struct drm_connector *connector = NULL; @@ -364,6 +365,9 @@ struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct dr if (!dp_display->is_edp) drm_connector_attach_dp_subconnector_property(connector); + if (yuv_supported) + connector->ycbcr_420_allowed = true; + drm_connector_attach_encoder(connector, encoder); return connector; diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h index b3d684db2383..45e57ac25a4d 100644 --- a/drivers/gpu/drm/msm/dp/dp_drm.h +++ b/drivers/gpu/drm/msm/dp/dp_drm.h @@ -19,7 +19,8 @@ struct msm_dp_bridge { #define to_dp_bridge(x) container_of((x), struct msm_dp_bridge, bridge) -struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder); +struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder, + bool yuv_supported); int dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev, struct drm_encoder *encoder); diff --git a/drivers/gpu/drm/msm/dp/dp_link.h b/drivers/gpu/drm/msm/dp/dp_link.h index 9dd4dd926530..83da170bc56b 100644 --- a/drivers/gpu/drm/msm/dp/dp_link.h +++ b/drivers/gpu/drm/msm/dp/dp_link.h @@ -112,29 +112,6 @@ static inline u32 dp_link_bit_depth_to_bpp(u32 tbd) } } -/** - * dp_test_bit_depth_to_bpc() - convert test bit depth to bpc - * @tbd: test bit depth - * - * Returns the bits per comp (bpc) to be used corresponding to the - * bit depth value. This function assumes that bit depth has - * already been validated. - */ -static inline u32 dp_link_bit_depth_to_bpc(u32 tbd) -{ - switch (tbd) { - case DP_TEST_BIT_DEPTH_6: - return 6; - case DP_TEST_BIT_DEPTH_8: - return 8; - case DP_TEST_BIT_DEPTH_10: - return 10; - case DP_TEST_BIT_DEPTH_UNKNOWN: - default: - return 0; - } -} - void dp_link_reset_phy_params_vx_px(struct dp_link *dp_link); u32 dp_link_get_test_bits_depth(struct dp_link *dp_link, u32 bpp); int dp_link_process_request(struct dp_link *dp_link); diff --git a/drivers/gpu/drm/msm/dp/dp_panel.c b/drivers/gpu/drm/msm/dp/dp_panel.c index 127f6af995cd..8e7069453952 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.c +++ b/drivers/gpu/drm/msm/dp/dp_panel.c @@ -4,11 +4,16 @@ */ #include "dp_panel.h" +#include "dp_utils.h" #include <drm/drm_connector.h> #include <drm/drm_edid.h> +#include <drm/drm_of.h> #include <drm/drm_print.h> +#define DP_MAX_NUM_DP_LANES 4 +#define DP_LINK_RATE_HBR2 540000 /* kbytes */ + struct dp_panel_private { struct device *dev; struct drm_device *drm_dev; @@ -53,6 +58,7 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel) if (rc) return rc; + dp_panel->vsc_sdp_supported = drm_dp_vsc_sdp_supported(panel->aux, dpcd); link_info = &dp_panel->link_info; link_info->revision = dpcd[DP_DPCD_REV]; major = (link_info->revision >> 4) & 0x0f; @@ -138,6 +144,9 @@ int dp_panel_read_sink_caps(struct dp_panel *dp_panel, panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + drm_dbg_dp(panel->drm_dev, "max_lanes=%d max_link_rate=%d\n", + dp_panel->max_dp_lanes, dp_panel->max_dp_link_rate); + rc = dp_panel_read_dpcd(dp_panel); if (rc) { DRM_ERROR("read dpcd failed %d\n", rc); @@ -280,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; @@ -343,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; @@ -386,10 +446,65 @@ int dp_panel_init_panel_info(struct dp_panel *dp_panel) return 0; } +static u32 dp_panel_link_frequencies(struct device_node *of_node) +{ + struct device_node *endpoint; + u64 frequency = 0; + int cnt; + + endpoint = of_graph_get_endpoint_by_regs(of_node, 1, 0); /* port@1 */ + if (!endpoint) + return 0; + + cnt = of_property_count_u64_elems(endpoint, "link-frequencies"); + + if (cnt > 0) + of_property_read_u64_index(endpoint, "link-frequencies", + cnt - 1, &frequency); + of_node_put(endpoint); + + do_div(frequency, + 10 * /* from symbol rate to link rate */ + 1000); /* kbytes */ + + return frequency; +} + +static int dp_panel_parse_dt(struct dp_panel *dp_panel) +{ + struct dp_panel_private *panel; + struct device_node *of_node; + int cnt; + + panel = container_of(dp_panel, struct dp_panel_private, dp_panel); + of_node = panel->dev->of_node; + + /* + * data-lanes is the property of dp_out endpoint + */ + cnt = drm_of_get_data_lanes_count_ep(of_node, 1, 0, 1, DP_MAX_NUM_DP_LANES); + if (cnt < 0) { + /* legacy code, data-lanes is the property of mdss_dp node */ + cnt = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES); + } + + if (cnt > 0) + dp_panel->max_dp_lanes = cnt; + else + dp_panel->max_dp_lanes = DP_MAX_NUM_DP_LANES; /* 4 lanes */ + + dp_panel->max_dp_link_rate = dp_panel_link_frequencies(of_node); + if (!dp_panel->max_dp_link_rate) + dp_panel->max_dp_link_rate = DP_LINK_RATE_HBR2; + + return 0; +} + struct dp_panel *dp_panel_get(struct dp_panel_in *in) { struct dp_panel_private *panel; struct dp_panel *dp_panel; + int ret; if (!in->dev || !in->catalog || !in->aux || !in->link) { DRM_ERROR("invalid input\n"); @@ -408,6 +523,10 @@ struct dp_panel *dp_panel_get(struct dp_panel_in *in) dp_panel = &panel->dp_panel; dp_panel->max_bw_code = DP_LINK_BW_8_1; + ret = dp_panel_parse_dt(dp_panel); + if (ret) + return ERR_PTR(ret); + return dp_panel; } diff --git a/drivers/gpu/drm/msm/dp/dp_panel.h b/drivers/gpu/drm/msm/dp/dp_panel.h index a0dfc579c5f9..e843f5062d1f 100644 --- a/drivers/gpu/drm/msm/dp/dp_panel.h +++ b/drivers/gpu/drm/msm/dp/dp_panel.h @@ -19,6 +19,7 @@ struct dp_display_mode { u32 bpp; u32 h_active_low; u32 v_active_low; + bool out_fmt_is_yuv_420; }; struct dp_panel_in { @@ -45,6 +46,7 @@ struct dp_panel { struct dp_display_mode dp_mode; struct dp_panel_psr psr_cap; bool video_test; + bool vsc_sdp_supported; u32 vic; u32 max_dp_lanes; diff --git a/drivers/gpu/drm/msm/dp/dp_parser.c b/drivers/gpu/drm/msm/dp/dp_parser.c deleted file mode 100644 index 7032dcc8842b..000000000000 --- a/drivers/gpu/drm/msm/dp/dp_parser.c +++ /dev/null @@ -1,327 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. - */ - -#include <linux/of_gpio.h> -#include <linux/phy/phy.h> - -#include <drm/drm_of.h> -#include <drm/drm_print.h> -#include <drm/drm_bridge.h> - -#include "dp_parser.h" -#include "dp_reg.h" - -#define DP_DEFAULT_AHB_OFFSET 0x0000 -#define DP_DEFAULT_AHB_SIZE 0x0200 -#define DP_DEFAULT_AUX_OFFSET 0x0200 -#define DP_DEFAULT_AUX_SIZE 0x0200 -#define DP_DEFAULT_LINK_OFFSET 0x0400 -#define DP_DEFAULT_LINK_SIZE 0x0C00 -#define DP_DEFAULT_P0_OFFSET 0x1000 -#define DP_DEFAULT_P0_SIZE 0x0400 - -static void __iomem *dp_ioremap(struct platform_device *pdev, int idx, size_t *len) -{ - struct resource *res; - void __iomem *base; - - base = devm_platform_get_and_ioremap_resource(pdev, idx, &res); - if (!IS_ERR(base)) - *len = resource_size(res); - - return base; -} - -static int dp_parser_ctrl_res(struct dp_parser *parser) -{ - struct platform_device *pdev = parser->pdev; - struct dp_io *io = &parser->io; - struct dss_io_data *dss = &io->dp_controller; - - dss->ahb.base = dp_ioremap(pdev, 0, &dss->ahb.len); - if (IS_ERR(dss->ahb.base)) - return PTR_ERR(dss->ahb.base); - - dss->aux.base = dp_ioremap(pdev, 1, &dss->aux.len); - if (IS_ERR(dss->aux.base)) { - /* - * The initial binding had a single reg, but in order to - * support variation in the sub-region sizes this was split. - * dp_ioremap() will fail with -EINVAL here if only a single - * reg is specified, so fill in the sub-region offsets and - * lengths based on this single region. - */ - if (PTR_ERR(dss->aux.base) == -EINVAL) { - if (dss->ahb.len < DP_DEFAULT_P0_OFFSET + DP_DEFAULT_P0_SIZE) { - DRM_ERROR("legacy memory region not large enough\n"); - return -EINVAL; - } - - dss->ahb.len = DP_DEFAULT_AHB_SIZE; - dss->aux.base = dss->ahb.base + DP_DEFAULT_AUX_OFFSET; - dss->aux.len = DP_DEFAULT_AUX_SIZE; - dss->link.base = dss->ahb.base + DP_DEFAULT_LINK_OFFSET; - dss->link.len = DP_DEFAULT_LINK_SIZE; - dss->p0.base = dss->ahb.base + DP_DEFAULT_P0_OFFSET; - dss->p0.len = DP_DEFAULT_P0_SIZE; - } else { - DRM_ERROR("unable to remap aux region: %pe\n", dss->aux.base); - return PTR_ERR(dss->aux.base); - } - } else { - dss->link.base = dp_ioremap(pdev, 2, &dss->link.len); - if (IS_ERR(dss->link.base)) { - DRM_ERROR("unable to remap link region: %pe\n", dss->link.base); - return PTR_ERR(dss->link.base); - } - - dss->p0.base = dp_ioremap(pdev, 3, &dss->p0.len); - if (IS_ERR(dss->p0.base)) { - DRM_ERROR("unable to remap p0 region: %pe\n", dss->p0.base); - return PTR_ERR(dss->p0.base); - } - } - - io->phy = devm_phy_get(&pdev->dev, "dp"); - if (IS_ERR(io->phy)) - return PTR_ERR(io->phy); - - return 0; -} - -static u32 dp_parser_link_frequencies(struct device_node *of_node) -{ - struct device_node *endpoint; - u64 frequency = 0; - int cnt; - - endpoint = of_graph_get_endpoint_by_regs(of_node, 1, 0); /* port@1 */ - if (!endpoint) - return 0; - - cnt = of_property_count_u64_elems(endpoint, "link-frequencies"); - - if (cnt > 0) - of_property_read_u64_index(endpoint, "link-frequencies", - cnt - 1, &frequency); - of_node_put(endpoint); - - do_div(frequency, - 10 * /* from symbol rate to link rate */ - 1000); /* kbytes */ - - return frequency; -} - -static int dp_parser_misc(struct dp_parser *parser) -{ - struct device_node *of_node = parser->pdev->dev.of_node; - int cnt; - - /* - * data-lanes is the property of dp_out endpoint - */ - cnt = drm_of_get_data_lanes_count_ep(of_node, 1, 0, 1, DP_MAX_NUM_DP_LANES); - if (cnt < 0) { - /* legacy code, data-lanes is the property of mdss_dp node */ - cnt = drm_of_get_data_lanes_count(of_node, 1, DP_MAX_NUM_DP_LANES); - } - - if (cnt > 0) - parser->max_dp_lanes = cnt; - else - parser->max_dp_lanes = DP_MAX_NUM_DP_LANES; /* 4 lanes */ - - parser->max_dp_link_rate = dp_parser_link_frequencies(of_node); - if (!parser->max_dp_link_rate) - parser->max_dp_link_rate = DP_LINK_RATE_HBR2; - - return 0; -} - -static inline bool dp_parser_check_prefix(const char *clk_prefix, - const char *clk_name) -{ - return !strncmp(clk_prefix, clk_name, strlen(clk_prefix)); -} - -static int dp_parser_init_clk_data(struct dp_parser *parser) -{ - int num_clk, i, rc; - int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0; - const char *clk_name; - struct device *dev = &parser->pdev->dev; - struct dss_module_power *core_power = &parser->mp[DP_CORE_PM]; - struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM]; - struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM]; - - num_clk = of_property_count_strings(dev->of_node, "clock-names"); - if (num_clk <= 0) { - DRM_ERROR("no clocks are defined\n"); - return -EINVAL; - } - - for (i = 0; i < num_clk; i++) { - rc = of_property_read_string_index(dev->of_node, - "clock-names", i, &clk_name); - if (rc < 0) - return rc; - - if (dp_parser_check_prefix("core", clk_name)) - core_clk_count++; - - if (dp_parser_check_prefix("ctrl", clk_name)) - ctrl_clk_count++; - - if (dp_parser_check_prefix("stream", clk_name)) - stream_clk_count++; - } - - /* Initialize the CORE power module */ - if (core_clk_count == 0) { - DRM_ERROR("no core clocks are defined\n"); - return -EINVAL; - } - - core_power->num_clk = core_clk_count; - core_power->clocks = devm_kcalloc(dev, - core_power->num_clk, sizeof(struct clk_bulk_data), - GFP_KERNEL); - if (!core_power->clocks) - return -ENOMEM; - - /* Initialize the CTRL power module */ - if (ctrl_clk_count == 0) { - DRM_ERROR("no ctrl clocks are defined\n"); - return -EINVAL; - } - - ctrl_power->num_clk = ctrl_clk_count; - ctrl_power->clocks = devm_kcalloc(dev, - ctrl_power->num_clk, sizeof(struct clk_bulk_data), - GFP_KERNEL); - if (!ctrl_power->clocks) { - ctrl_power->num_clk = 0; - return -ENOMEM; - } - - /* Initialize the STREAM power module */ - if (stream_clk_count == 0) { - DRM_ERROR("no stream (pixel) clocks are defined\n"); - return -EINVAL; - } - - stream_power->num_clk = stream_clk_count; - stream_power->clocks = devm_kcalloc(dev, - stream_power->num_clk, sizeof(struct clk_bulk_data), - GFP_KERNEL); - if (!stream_power->clocks) { - stream_power->num_clk = 0; - return -ENOMEM; - } - - return 0; -} - -static int dp_parser_clock(struct dp_parser *parser) -{ - int rc = 0, i = 0; - int num_clk = 0; - int core_clk_index = 0, ctrl_clk_index = 0, stream_clk_index = 0; - int core_clk_count = 0, ctrl_clk_count = 0, stream_clk_count = 0; - const char *clk_name; - struct device *dev = &parser->pdev->dev; - struct dss_module_power *core_power = &parser->mp[DP_CORE_PM]; - struct dss_module_power *ctrl_power = &parser->mp[DP_CTRL_PM]; - struct dss_module_power *stream_power = &parser->mp[DP_STREAM_PM]; - - rc = dp_parser_init_clk_data(parser); - if (rc) { - DRM_ERROR("failed to initialize power data %d\n", rc); - return -EINVAL; - } - - core_clk_count = core_power->num_clk; - ctrl_clk_count = ctrl_power->num_clk; - stream_clk_count = stream_power->num_clk; - - num_clk = core_clk_count + ctrl_clk_count + stream_clk_count; - - for (i = 0; i < num_clk; i++) { - rc = of_property_read_string_index(dev->of_node, "clock-names", - i, &clk_name); - if (rc) { - DRM_ERROR("error reading clock-names %d\n", rc); - return rc; - } - if (dp_parser_check_prefix("core", clk_name) && - core_clk_index < core_clk_count) { - core_power->clocks[core_clk_index].id = devm_kstrdup(dev, clk_name, GFP_KERNEL); - core_clk_index++; - } else if (dp_parser_check_prefix("stream", clk_name) && - stream_clk_index < stream_clk_count) { - stream_power->clocks[stream_clk_index].id = devm_kstrdup(dev, clk_name, GFP_KERNEL); - stream_clk_index++; - } else if (dp_parser_check_prefix("ctrl", clk_name) && - ctrl_clk_index < ctrl_clk_count) { - ctrl_power->clocks[ctrl_clk_index].id = devm_kstrdup(dev, clk_name, GFP_KERNEL); - ctrl_clk_index++; - } - } - - return 0; -} - -int devm_dp_parser_find_next_bridge(struct device *dev, struct dp_parser *parser) -{ - struct platform_device *pdev = parser->pdev; - struct drm_bridge *bridge; - - bridge = devm_drm_of_get_bridge(dev, pdev->dev.of_node, 1, 0); - if (IS_ERR(bridge)) - return PTR_ERR(bridge); - - parser->next_bridge = bridge; - - return 0; -} - -static int dp_parser_parse(struct dp_parser *parser) -{ - int rc = 0; - - if (!parser) { - DRM_ERROR("invalid input\n"); - return -EINVAL; - } - - rc = dp_parser_ctrl_res(parser); - if (rc) - return rc; - - rc = dp_parser_misc(parser); - if (rc) - return rc; - - rc = dp_parser_clock(parser); - if (rc) - return rc; - - return 0; -} - -struct dp_parser *dp_parser_get(struct platform_device *pdev) -{ - struct dp_parser *parser; - - parser = devm_kzalloc(&pdev->dev, sizeof(*parser), GFP_KERNEL); - if (!parser) - return ERR_PTR(-ENOMEM); - - parser->parse = dp_parser_parse; - parser->pdev = pdev; - - return parser; -} diff --git a/drivers/gpu/drm/msm/dp/dp_parser.h b/drivers/gpu/drm/msm/dp/dp_parser.h deleted file mode 100644 index 1f068626d445..000000000000 --- a/drivers/gpu/drm/msm/dp/dp_parser.h +++ /dev/null @@ -1,155 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. - */ - -#ifndef _DP_PARSER_H_ -#define _DP_PARSER_H_ - -#include <linux/platform_device.h> -#include <linux/phy/phy.h> -#include <linux/phy/phy-dp.h> - -#include "msm_drv.h" - -#define DP_LABEL "MDSS DP DISPLAY" -#define DP_MAX_PIXEL_CLK_KHZ 675000 -#define DP_MAX_NUM_DP_LANES 4 -#define DP_LINK_RATE_HBR2 540000 /* kbytes */ - -enum dp_pm_type { - DP_CORE_PM, - DP_CTRL_PM, - DP_STREAM_PM, - DP_PHY_PM, - DP_MAX_PM -}; - -struct dss_io_region { - size_t len; - void __iomem *base; -}; - -struct dss_io_data { - struct dss_io_region ahb; - struct dss_io_region aux; - struct dss_io_region link; - struct dss_io_region p0; -}; - -static inline const char *dp_parser_pm_name(enum dp_pm_type module) -{ - switch (module) { - case DP_CORE_PM: return "DP_CORE_PM"; - case DP_CTRL_PM: return "DP_CTRL_PM"; - case DP_STREAM_PM: return "DP_STREAM_PM"; - case DP_PHY_PM: return "DP_PHY_PM"; - default: return "???"; - } -} - -/** - * struct dp_display_data - display related device tree data. - * - * @ctrl_node: referece to controller device - * @phy_node: reference to phy device - * @is_active: is the controller currently active - * @name: name of the display - * @display_type: type of the display - */ -struct dp_display_data { - struct device_node *ctrl_node; - struct device_node *phy_node; - bool is_active; - const char *name; - const char *display_type; -}; - -/** - * struct dp_ctrl_resource - controller's IO related data - * - * @dp_controller: Display Port controller mapped memory address - * @phy_io: phy's mapped memory address - */ -struct dp_io { - struct dss_io_data dp_controller; - struct phy *phy; - union phy_configure_opts phy_opts; -}; - -/** - * struct dp_pinctrl - DP's pin control - * - * @pin: pin-controller's instance - * @state_active: active state pin control - * @state_hpd_active: hpd active state pin control - * @state_suspend: suspend state pin control - */ -struct dp_pinctrl { - struct pinctrl *pin; - struct pinctrl_state *state_active; - struct pinctrl_state *state_hpd_active; - struct pinctrl_state *state_suspend; -}; - -/* Regulators for DP devices */ -struct dp_reg_entry { - char name[32]; - int enable_load; - int disable_load; -}; - -struct dss_module_power { - unsigned int num_clk; - struct clk_bulk_data *clocks; -}; - -/** - * struct dp_parser - DP parser's data exposed to clients - * - * @pdev: platform data of the client - * @mp: gpio, regulator and clock related data - * @pinctrl: pin-control related data - * @disp_data: controller's display related data - * @parse: function to be called by client to parse device tree. - */ -struct dp_parser { - struct platform_device *pdev; - struct dss_module_power mp[DP_MAX_PM]; - struct dp_pinctrl pinctrl; - struct dp_io io; - struct dp_display_data disp_data; - u32 max_dp_lanes; - u32 max_dp_link_rate; - struct drm_bridge *next_bridge; - - int (*parse)(struct dp_parser *parser); -}; - -/** - * dp_parser_get() - get the DP's device tree parser module - * - * @pdev: platform data of the client - * return: pointer to dp_parser structure. - * - * This function provides client capability to parse the - * device tree and populate the data structures. The data - * related to clock, regulators, pin-control and other - * can be parsed using this module. - */ -struct dp_parser *dp_parser_get(struct platform_device *pdev); - -/** - * devm_dp_parser_find_next_bridge() - find an additional bridge to DP - * - * @dev: device to tie bridge lifetime to - * @parser: dp_parser data from client - * - * This function is used to find any additional bridge attached to - * the DP controller. The eDP interface requires a panel bridge. - * - * Return: 0 if able to get the bridge, otherwise negative errno for failure. - */ -int devm_dp_parser_find_next_bridge(struct device *dev, struct dp_parser *parser); - -#endif diff --git a/drivers/gpu/drm/msm/dp/dp_power.c b/drivers/gpu/drm/msm/dp/dp_power.c deleted file mode 100644 index c4843dd69f47..000000000000 --- a/drivers/gpu/drm/msm/dp/dp_power.c +++ /dev/null @@ -1,183 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. - */ - -#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__ - -#include <linux/clk.h> -#include <linux/clk-provider.h> -#include <linux/regulator/consumer.h> -#include <linux/pm_opp.h> -#include "dp_power.h" -#include "msm_drv.h" - -struct dp_power_private { - struct dp_parser *parser; - struct device *dev; - struct drm_device *drm_dev; - struct clk *link_clk_src; - struct clk *pixel_provider; - struct clk *link_provider; - - struct dp_power dp_power; -}; - -static int dp_power_clk_init(struct dp_power_private *power) -{ - int rc = 0; - struct dss_module_power *core, *ctrl, *stream; - struct device *dev = power->dev; - - core = &power->parser->mp[DP_CORE_PM]; - ctrl = &power->parser->mp[DP_CTRL_PM]; - stream = &power->parser->mp[DP_STREAM_PM]; - - rc = devm_clk_bulk_get(dev, core->num_clk, core->clocks); - if (rc) - return rc; - - rc = devm_clk_bulk_get(dev, ctrl->num_clk, ctrl->clocks); - if (rc) - return -ENODEV; - - rc = devm_clk_bulk_get(dev, stream->num_clk, stream->clocks); - if (rc) - return -ENODEV; - - return 0; -} - -int dp_power_clk_status(struct dp_power *dp_power, enum dp_pm_type pm_type) -{ - struct dp_power_private *power; - - power = container_of(dp_power, struct dp_power_private, dp_power); - - drm_dbg_dp(power->drm_dev, - "core_clk_on=%d link_clk_on=%d stream_clk_on=%d\n", - dp_power->core_clks_on, dp_power->link_clks_on, dp_power->stream_clks_on); - - if (pm_type == DP_CORE_PM) - return dp_power->core_clks_on; - - if (pm_type == DP_CTRL_PM) - return dp_power->link_clks_on; - - if (pm_type == DP_STREAM_PM) - return dp_power->stream_clks_on; - - return 0; -} - -int dp_power_clk_enable(struct dp_power *dp_power, - enum dp_pm_type pm_type, bool enable) -{ - int rc = 0; - struct dp_power_private *power; - struct dss_module_power *mp; - - power = container_of(dp_power, struct dp_power_private, dp_power); - - if (pm_type != DP_CORE_PM && pm_type != DP_CTRL_PM && - pm_type != DP_STREAM_PM) { - DRM_ERROR("unsupported power module: %s\n", - dp_parser_pm_name(pm_type)); - return -EINVAL; - } - - if (enable) { - if (pm_type == DP_CORE_PM && dp_power->core_clks_on) { - drm_dbg_dp(power->drm_dev, - "core clks already enabled\n"); - return 0; - } - - if (pm_type == DP_CTRL_PM && dp_power->link_clks_on) { - drm_dbg_dp(power->drm_dev, - "links clks already enabled\n"); - return 0; - } - - if (pm_type == DP_STREAM_PM && dp_power->stream_clks_on) { - drm_dbg_dp(power->drm_dev, - "pixel clks already enabled\n"); - return 0; - } - - if ((pm_type == DP_CTRL_PM) && (!dp_power->core_clks_on)) { - drm_dbg_dp(power->drm_dev, - "Enable core clks before link clks\n"); - mp = &power->parser->mp[DP_CORE_PM]; - - rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks); - if (rc) - return rc; - - dp_power->core_clks_on = true; - } - } - - mp = &power->parser->mp[pm_type]; - if (enable) { - rc = clk_bulk_prepare_enable(mp->num_clk, mp->clocks); - if (rc) - return rc; - } else { - clk_bulk_disable_unprepare(mp->num_clk, mp->clocks); - } - - if (pm_type == DP_CORE_PM) - dp_power->core_clks_on = enable; - else if (pm_type == DP_STREAM_PM) - dp_power->stream_clks_on = enable; - else - dp_power->link_clks_on = enable; - - drm_dbg_dp(power->drm_dev, "%s clocks for %s\n", - enable ? "enable" : "disable", - dp_parser_pm_name(pm_type)); - drm_dbg_dp(power->drm_dev, - "strem_clks:%s link_clks:%s core_clks:%s\n", - dp_power->stream_clks_on ? "on" : "off", - dp_power->link_clks_on ? "on" : "off", - dp_power->core_clks_on ? "on" : "off"); - - return 0; -} - -int dp_power_client_init(struct dp_power *dp_power) -{ - struct dp_power_private *power; - - power = container_of(dp_power, struct dp_power_private, dp_power); - - return dp_power_clk_init(power); -} - -int dp_power_init(struct dp_power *dp_power) -{ - return dp_power_clk_enable(dp_power, DP_CORE_PM, true); -} - -int dp_power_deinit(struct dp_power *dp_power) -{ - return dp_power_clk_enable(dp_power, DP_CORE_PM, false); -} - -struct dp_power *dp_power_get(struct device *dev, struct dp_parser *parser) -{ - struct dp_power_private *power; - struct dp_power *dp_power; - - power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); - if (!power) - return ERR_PTR(-ENOMEM); - - power->parser = parser; - power->dev = dev; - - dp_power = &power->dp_power; - - return dp_power; -} diff --git a/drivers/gpu/drm/msm/dp/dp_power.h b/drivers/gpu/drm/msm/dp/dp_power.h deleted file mode 100644 index 55ada51edb57..000000000000 --- a/drivers/gpu/drm/msm/dp/dp_power.h +++ /dev/null @@ -1,95 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2012-2020, The Linux Foundation. All rights reserved. - */ - -#ifndef _DP_POWER_H_ -#define _DP_POWER_H_ - -#include "dp_parser.h" - -/** - * sruct dp_power - DisplayPort's power related data - * - * @init: initializes the regulators/core clocks/GPIOs/pinctrl - * @deinit: turns off the regulators/core clocks/GPIOs/pinctrl - * @clk_enable: enable/disable the DP clocks - * @set_pixel_clk_parent: set the parent of DP pixel clock - */ -struct dp_power { - bool core_clks_on; - bool link_clks_on; - bool stream_clks_on; -}; - -/** - * dp_power_init() - enable power supplies for display controller - * - * @power: instance of power module - * return: 0 if success or error if failure. - * - * This API will turn on the regulators and configures gpio's - * aux/hpd. - */ -int dp_power_init(struct dp_power *power); - -/** - * dp_power_deinit() - turn off regulators and gpios. - * - * @power: instance of power module - * return: 0 for success - * - * This API turns off power and regulators. - */ -int dp_power_deinit(struct dp_power *power); - -/** - * dp_power_clk_status() - display controller clocks status - * - * @power: instance of power module - * @pm_type: type of pm, core/ctrl/phy - * return: status of power clocks - * - * This API return status of DP clocks - */ - -int dp_power_clk_status(struct dp_power *dp_power, enum dp_pm_type pm_type); - -/** - * dp_power_clk_enable() - enable display controller clocks - * - * @power: instance of power module - * @pm_type: type of pm, core/ctrl/phy - * @enable: enables or disables - * return: pointer to allocated power module data - * - * This API will call setrate and enable for DP clocks - */ - -int dp_power_clk_enable(struct dp_power *power, enum dp_pm_type pm_type, - bool enable); - -/** - * dp_power_client_init() - initialize clock and regulator modules - * - * @power: instance of power module - * return: 0 for success, error for failure. - * - * This API will configure the DisplayPort's clocks and regulator - * modules. - */ -int dp_power_client_init(struct dp_power *power); - -/** - * dp_power_get() - configure and get the DisplayPort power module data - * - * @parser: instance of parser module - * return: pointer to allocated power module data - * - * This API will configure the DisplayPort's power module and provides - * methods to be called by the client to configure the power related - * modules. - */ -struct dp_power *dp_power_get(struct device *dev, struct dp_parser *parser); - -#endif /* _DP_POWER_H_ */ diff --git a/drivers/gpu/drm/msm/dp/dp_reg.h b/drivers/gpu/drm/msm/dp/dp_reg.h index 78785ed4b40c..3835c7f5cb98 100644 --- a/drivers/gpu/drm/msm/dp/dp_reg.h +++ b/drivers/gpu/drm/msm/dp/dp_reg.h @@ -6,6 +6,9 @@ #ifndef _DP_REG_H_ #define _DP_REG_H_ +#include <linux/bitfield.h> +#include <linux/bits.h> + /* DP_TX Registers */ #define REG_DP_HW_VERSION (0x00000000) @@ -102,6 +105,9 @@ #define DP_MAINLINK_CTRL_ENABLE (0x00000001) #define DP_MAINLINK_CTRL_RESET (0x00000002) #define DP_MAINLINK_CTRL_SW_BYPASS_SCRAMBLER (0x00000010) +#define DP_MAINLINK_CTRL_FLUSH_MODE_MASK GENMASK(24, 23) +#define DP_MAINLINK_FLUSH_MODE_UPDATE_SDP FIELD_PREP(DP_MAINLINK_CTRL_FLUSH_MODE_MASK, 1) +#define DP_MAINLINK_FLUSH_MODE_SDE_PERIPH_UPDATE FIELD_PREP(DP_MAINLINK_CTRL_FLUSH_MODE_MASK, 3) #define DP_MAINLINK_FB_BOUNDARY_SEL (0x02000000) #define REG_DP_STATE_CTRL (0x00000004) @@ -142,6 +148,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 +211,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 new file mode 100644 index 000000000000..da9207caf72d --- /dev/null +++ b/drivers/gpu/drm/msm/dp/dp_utils.c @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024, The Linux Foundation. All rights reserved. + */ + +#include <linux/types.h> + +#include "dp_utils.h" + +#define DP_SDP_HEADER_SIZE 8 + +u8 dp_utils_get_g0_value(u8 data) +{ + u8 c[4]; + u8 g[4]; + u8 ret_data = 0; + u8 i; + + for (i = 0; i < 4; i++) + c[i] = (data >> i) & 0x01; + + g[0] = c[3]; + g[1] = c[0] ^ c[3]; + g[2] = c[1]; + g[3] = c[2]; + + for (i = 0; i < 4; i++) + ret_data = ((g[i] & 0x01) << i) | ret_data; + + return ret_data; +} + +u8 dp_utils_get_g1_value(u8 data) +{ + u8 c[4]; + u8 g[4]; + u8 ret_data = 0; + u8 i; + + for (i = 0; i < 4; i++) + c[i] = (data >> i) & 0x01; + + g[0] = c[0] ^ c[3]; + g[1] = c[0] ^ c[1] ^ c[3]; + g[2] = c[1] ^ c[2]; + g[3] = c[2] ^ c[3]; + + for (i = 0; i < 4; i++) + ret_data = ((g[i] & 0x01) << i) | ret_data; + + return ret_data; +} + +u8 dp_utils_calculate_parity(u32 data) +{ + u8 x0 = 0; + u8 x1 = 0; + u8 ci = 0; + u8 iData = 0; + u8 i = 0; + u8 parity_byte; + u8 num_byte = (data & 0xFF00) > 0 ? 8 : 2; + + for (i = 0; i < num_byte; i++) { + iData = (data >> i * 4) & 0xF; + + ci = iData ^ x1; + x1 = x0 ^ dp_utils_get_g1_value(ci); + x0 = dp_utils_get_g0_value(ci); + } + + parity_byte = x1 | (x0 << 4); + + 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 new file mode 100644 index 000000000000..7c056d9798dc --- /dev/null +++ b/drivers/gpu/drm/msm/dp/dp_utils.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2024, The Linux Foundation. All rights reserved. + */ + +#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 +#define PARITY_BYTE_1_BIT 24 +#define HEADER_BYTE_2_BIT 0 +#define PARITY_BYTE_2_BIT 8 +#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_ */ |