summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/msm/dp
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2024-03-14 04:34:05 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2024-03-14 04:34:05 +0300
commit480e035fc4c714fb5536e64ab9db04fedc89e910 (patch)
tree01341ee43abe7ecb8efb4e7bbbb1c3b3b50f7ec8 /drivers/gpu/drm/msm/dp
parente5e038b7ae9da96b93974bf072ca1876899a01a3 (diff)
parent119b225f01e4d3ce974cd3b4d982c76a380c796d (diff)
downloadlinux-480e035fc4c714fb5536e64ab9db04fedc89e910.tar.xz
Merge tag 'drm-next-2024-03-13' of https://gitlab.freedesktop.org/drm/kernel
Pull drm updates from Dave Airlie: "Highlights are usual, more AMD IP blocks for future hw, i915/xe changes, Displayport tunnelling support for i915, msm YUV over DP changes, new tests for ttm, but its mostly a lot of stuff all over the place from lots of people. core: - EDID cleanups - scheduler error handling fixes - managed: add drmm_release_action() with tests - add ratelimited drm debug print - DPCD PSR early transport macro - DP tunneling and bandwidth allocation helpers - remove built-in edids - dp: Avoid AUX transfers on powered-down displays - dp: Add VSC SDP helpers cross drivers: - use new drm print helpers - switch to ->read_edid callback - gem: add stats for shared buffers plus updates to amdgpu, i915, xe syncobj: - fixes to waiting and sleeping ttm: - add tests - fix errno codes - simply busy-placement handling - fix page decryption media: - tc358743: fix v4l device registration video: - move all kernel parameters for video behind CONFIG_VIDEO sound: - remove <drm/drm_edid.h> include from header ci: - add tests for msm - fix apq8016 runner efifb: - use copy of global screen_info state vesafb: - use copy of global screen_info state simplefb: - fix logging bridge: - ite-6505: fix DP link-training bug - samsung-dsim: fix error checking in probe - samsung-dsim: add bsh-smm-s2/pro boards - tc358767: fix regmap usage - imx: add i.MX8MP HDMI PVI plus DT bindings - imx: add i.MX8MP HDMI TX plus DT bindings - sii902x: fix probing and unregistration - tc358767: limit pixel PLL input range - switch to new drm_bridge_read_edid() interface panel: - ltk050h3146w: error-handling fixes - panel-edp: support delay between power-on and enable; use put_sync in unprepare; support Mediatek MT8173 Chromebooks, BOE NV116WHM-N49 V8.0, BOE NV122WUM-N41, CSO MNC207QS1-1 plus DT bindings - panel-lvds: support EDT ETML0700Z9NDHA plus DT bindings - panel-novatek: FRIDA FRD400B25025-A-CTK plus DT bindings - add BOE TH101MB31IG002-28A plus DT bindings - add EDT ETML1010G3DRA plus DT bindings - add Novatek NT36672E LCD DSI plus DT bindings - nt36523: support 120Hz timings, fix includes - simple: fix display timings on RK32FN48H - visionox-vtdr6130: fix initialization - add Powkiddy RGB10MAX3 plus DT bindings - st7703: support panel rotation plus DT bindings - add Himax HX83112A plus DT bindings - ltk500hd1829: add support for ltk101b4029w and admatec 9904370 - simple: add BOE BP082WX1-100 8.2" panel plus DT bindungs panel-orientation-quirks: - GPD Win Mini amdgpu: - Validate DMABuf imports in compute VMs - Add RAS ACA framework - PSP 13 fixes - Misc code cleanups - Replay fixes - Atom interpretor PS, WS bounds checking - DML2 fixes - Audio fixes - DCN 3.5 Z state fixes - Remove deprecated ida_simple usage - UBSAN fixes - RAS fixes - Enable seq64 infrastructure - DC color block enablement - Documentation updates - DC documentation updates - DMCUB updates - ATHUB 4.1 support - LSDMA 7.0 support - JPEG DPG support - IH 7.0 support - HDP 7.0 support - VCN 5.0 support - SMU 13.0.6 updates - NBIO 7.11 updates - SDMA 6.1 updates - MMHUB 3.3 updates - DCN 3.5.1 support - NBIF 6.3.1 support - VPE 6.1.1 support amdkfd: - Validate DMABuf imports in compute VMs - SVM fixes - Trap handler updates and enhancements - Fix cache size reporting - Relocate the trap handler radeon: - Atom interpretor PS, WS bounds checking - Misc code cleanups xe: - new query for GuC submission version - Remove unused persistent exec_queues - Add vram frequency sysfs attributes - Add the flag XE_VM_BIND_FLAG_DUMPABLE - Drop pre-production workarounds - Drop kunit tests for unsupported platforms - Start pumbling SR-IOV support with memory based interrupts for VF - Allow to map BO in GGTT with PAT index corresponding to XE_CACHE_UC to work with memory based interrupts - Add GuC Doorbells Manager as prep work SR-IOV - Implement additional workarounds for xe2 and MTL - Program a few registers according to perfomance guide spec for Xe2 - Fix remaining 32b build issues and enable it back - Fix build with CONFIG_DEBUG_FS=n - Fix warnings from GuC ABI headers - Introduce Relay Communication for SR-IOV for VF <-> GuC <-> PF - Release mmap mappings on rpm suspend - Disable mid-thread preemption when not properly supported by hardware - Fix xe_exec by reserving extra fence slot for CPU bind - Fix xe_exec with full long running exec queue - Canonicalize addresses where needed for Xe2 and add to devcoredum - Toggle USM support for Xe2 - Only allow 1 ufence per exec / bind IOCTL - Add GuC firmware loading for Lunar Lake - Add XE_VMA_PTE_64K VMA flag i915: - Add more ADL-N PCI IDs - Enable fastboot also on older platforms - Early transport for panel replay and PSR - New ARL PCI IDs - DP TPS4 PHY test pattern support - Unify and improve VSC SDP for PSR and non-PSR cases - Refactor memory regions and improve debug logging - Rework global state serialization - Remove unused CDCLK divider fields - Unify HDCP connector logging format - Use display instead of graphics version in display code - Move VBT and opregion debugfs next to the implementation - Abstract opregion interface, use opaque type - MTL fixes - HPD handling fixes - Add GuC submission interface version query - Atomically invalidate userptr on mmu-notifier - Update handling of MMIO triggered reports - Don't make assumptions about intel_wakeref_t type - Extend driver code of Xe_LPG to Xe_LPG+ - Add flex arrays to struct i915_syncmap - Allow for very slow HuC loading - DP tunneling and bandwidth allocation support msm: - Correct bindings for MSM8976 and SM8650 platforms - Start migration of MDP5 platforms to DPU driver - X1E80100 MDSS support - DPU: - Improve DSC allocation, fixing several important corner cases - Add support for SDM630/SDM660 platforms - Simplify dpu_encoder_phys_ops - Apply fixes targeting DSC support with a single DSC encoder - Apply fixes for HCTL_EN timing configuration - X1E80100 support - Add support for YUV420 over DP - GPU: - fix sc7180 UBWC config - fix a7xx LLC config - new gpu support: a305B, a750, a702 - machine support: SM7150 (different power levels than other a618) - a7xx devcoredump support habanalabs: - configure IRQ affinity according to NUMA node - move HBM MMU page tables inside the HBM - improve device reset - check extended PCIe errors ivpu: - updates to firmware API - refactor BO allocation imx: - use devm_ functions during init hisilicon: - fix EDID includes mgag200: - improve ioremap usage - convert to struct drm_edid - Work around PCI write bursts nouveau: - disp: use kmemdup() - fix EDID includes - documentation fixes qaic: - fixes to BO handling - make use of DRM managed release - fix order of remove operations rockchip: - analogix_dp: get encoder port from DT - inno_hdmi: support HDMI for RK3128 - lvds: error-handling fixes ssd130x: - support SSD133x plus DT bindings tegra: - fix error handling tilcdc: - make use of DRM managed release v3d: - show memory stats in debugfs - Support display MMU page size vc4: - fix error handling in plane prepare_fb - fix framebuffer test in plane helpers virtio: - add venus capset defines vkms: - fix OOB access when programming the LUT - Kconfig improvements vmwgfx: - unmap surface before changing plane state - fix memory leak in error handling - documentation fixes - list command SVGA_3D_CMD_DEFINE_GB_SURFACE_V4 as invalid - fix null-pointer deref in execbuf - refactor display-mode probing - fix fencing for creating cursor MOBs - fix cursor-memory lifetime xlnx: - fix live video input for ZynqMP DPSUB lima: - fix memory leak loongson: - fail if no VRAM present meson: - switch to new drm_bridge_read_edid() interface renesas: - add RZ/G2L DU support plus DT bindings mxsfb: - Use managed mode config sun4i: - HDMI: updates to atomic mode setting mediatek: - Add display driver for MT8188 VDOSYS1 - DSI driver cleanups - Filter modes according to hardware capability - Fix a null pointer crash in mtk_drm_crtc_finish_page_flip etnaviv: - enhancements for NPU and MRT support" * tag 'drm-next-2024-03-13' of https://gitlab.freedesktop.org/drm/kernel: (1420 commits) drm/amd/display: Removed redundant @ symbol to fix kernel-doc warnings in -next repo drm/amd/pm: wait for completion of the EnableGfxImu message drm/amdgpu/soc21: add mode2 asic reset for SMU IP v14.0.1 drm/amdgpu: add smu 14.0.1 support drm/amdgpu: add VPE 6.1.1 discovery support drm/amdgpu/vpe: add VPE 6.1.1 support drm/amdgpu/vpe: don't emit cond exec command under collaborate mode drm/amdgpu/vpe: add collaborate mode support for VPE drm/amdgpu/vpe: add PRED_EXE and COLLAB_SYNC OPCODE drm/amdgpu/vpe: add multi instance VPE support drm/amdgpu/discovery: add nbif v6_3_1 ip block drm/amdgpu: Add nbif v6_3_1 ip block support drm/amdgpu: Add pcie v6_1_0 ip headers (v5) drm/amdgpu: Add nbif v6_3_1 ip headers (v5) arch/powerpc: Remove <linux/fb.h> from backlight code macintosh/via-pmu-backlight: Include <linux/backlight.h> fbdev/chipsfb: Include <linux/backlight.h> drm/etnaviv: Restore some id values drm/amdkfd: make kfd_class constant drm/amdgpu: add ring timeout information in devcoredump ...
Diffstat (limited to 'drivers/gpu/drm/msm/dp')
-rw-r--r--drivers/gpu/drm/msm/dp/dp_audio.c101
-rw-r--r--drivers/gpu/drm/msm/dp/dp_aux.c9
-rw-r--r--drivers/gpu/drm/msm/dp/dp_aux.h2
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.c271
-rw-r--r--drivers/gpu/drm/msm/dp/dp_catalog.h15
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.c375
-rw-r--r--drivers/gpu/drm/msm/dp/dp_ctrl.h17
-rw-r--r--drivers/gpu/drm/msm/dp/dp_debug.c3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.c185
-rw-r--r--drivers/gpu/drm/msm/dp/dp_display.h3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.c6
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.h3
-rw-r--r--drivers/gpu/drm/msm/dp/dp_link.h23
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.c119
-rw-r--r--drivers/gpu/drm/msm/dp/dp_panel.h2
-rw-r--r--drivers/gpu/drm/msm/dp/dp_parser.c327
-rw-r--r--drivers/gpu/drm/msm/dp/dp_parser.h155
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.c183
-rw-r--r--drivers/gpu/drm/msm/dp/dp_power.h95
-rw-r--r--drivers/gpu/drm/msm/dp/dp_reg.h9
-rw-r--r--drivers/gpu/drm/msm/dp/dp_utils.c96
-rw-r--r--drivers/gpu/drm/msm/dp/dp_utils.h36
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_ */