summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2022-09-11 21:46:57 +1000
committerDave Airlie <airlied@redhat.com>2022-09-11 22:03:07 +1000
commitfb34d8a04e5876552cd0d4f9e14400ee13f116fb (patch)
tree4bdd1c43f67c6c013d2b08b11cecfd576fa1fa4b /drivers/gpu/drm
parent8284bae723f025cb6a8431566757a3854a3c53eb (diff)
parent5d832b6694e094b176627ed9918a1b21c56fb742 (diff)
Merge tag 'drm-misc-next-2022-09-09' of git://anongit.freedesktop.org/drm/drm-misc into drm-next
drm-misc-next for v6.1-rc1: [airlied - fix sun4i_tv build] UAPI Changes: - Hide unregistered connectors from GETCONNECTOR ioctl. - drm/virtio no longer advertises LINEAR modifier, as it doesn't work. - Cross-subsystem Changes: - Fix GPF in udmabuf failure path. Core Changes: - Rework TTM placement to use intersect/compatible functions. - Drop legacy DP-MST support. - More DP-MST related fixes, and move all state into atomic. - Make DRM_MIPI_DBI select DRM_KMS_HELPER. - Add audio_infoframe packing for DP. - Add logging when some atomic check functions fail. - Assorted documentation updates and fixes. Driver Changes: - Assorted cleanups and fixes in msm, lcdif, nouveau, virtio, panel/ilitek, bridge/icn6211, tve200, gma500, bridge/*, panfrost, via, bochs, qxl, sun4i. - Add add AUO B133UAN02.1, IVO M133NW4J-R3, Innolux N120ACA-EA1 eDP panels. - Improve DP-MST modeset state handling in amdgpu, nouveau, i915. - Drop DP-MST from radeon driver, it was broken and only user of legacy DP-MST. - Handle unplugging better in vc4. - Simplify drm cmdparser tests. - Add DP support to ti-sn65dsi86. - Add MT8195 DP support to mediatek. - Support RGB565, XRGB64, and ARGB64 formats in vkms. - Convert sun4i tv support to atomic. - Refactor vc4/vec TV Modesetting, and fix timings. - Use atomic helpers instead of simple display helpers in ssd130x. Maintainer changes: - Add Douglas Anderson as reviewer for panel-edp. Signed-off-by: Dave Airlie <airlied@redhat.com> From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/a489485b-3ebc-c734-0f80-aed963d89efe@linux.intel.com
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/Kconfig1
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c38
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c45
-rw-r--r--drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c68
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c68
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c108
-rw-r--r--drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c125
-rw-r--r--drivers/gpu/drm/amd/display/dc/core/dc_link.c10
-rw-r--r--drivers/gpu/drm/amd/display/dc/dm_helpers.h4
-rw-r--r--drivers/gpu/drm/amd/display/include/link_service_types.h14
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.c37
-rw-r--r--drivers/gpu/drm/bridge/analogix/anx7625.h6
-rw-r--r--drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c3
-rw-r--r--drivers/gpu/drm/bridge/chipone-icn6211.c44
-rw-r--r--drivers/gpu/drm/bridge/chrontel-ch7033.c15
-rw-r--r--drivers/gpu/drm/bridge/ite-it6505.c8
-rw-r--r--drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c4
-rw-r--r--drivers/gpu/drm/bridge/parade-ps8640.c5
-rw-r--r--drivers/gpu/drm/bridge/synopsys/dw-hdmi.c13
-rw-r--r--drivers/gpu/drm/bridge/tc358767.c15
-rw-r--r--drivers/gpu/drm/bridge/ti-sn65dsi86.c72
-rw-r--r--drivers/gpu/drm/display/drm_dp_helper.c32
-rw-r--r--drivers/gpu/drm/display/drm_dp_mst_topology.c1139
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c83
-rw-r--r--drivers/gpu/drm/drm_mode_config.c3
-rw-r--r--drivers/gpu/drm/gma500/cdv_intel_dp.c2
-rw-r--r--drivers/gpu/drm/gma500/oaktrail_crtc.c2
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_sdvo.c62
-rw-r--r--drivers/gpu/drm/i915/display/intel_display.c6
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c9
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c97
-rw-r--r--drivers/gpu/drm/i915/display/intel_hdcp.c24
-rw-r--r--drivers/gpu/drm/i915/gem/i915_gem_ttm.c41
-rw-r--r--drivers/gpu/drm/i915/i915_ttm_buddy_manager.c73
-rw-r--r--drivers/gpu/drm/mediatek/Kconfig9
-rw-r--r--drivers/gpu/drm/mediatek/Makefile2
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp.c2661
-rw-r--r--drivers/gpu/drm/mediatek/mtk_dp_reg.h356
-rw-r--r--drivers/gpu/drm/msm/msm_drv.c13
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.c21
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_drv.h1
-rw-r--r--drivers/gpu/drm/mxsfb/lcdif_kms.c12
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.c197
-rw-r--r--drivers/gpu/drm/nouveau/dispnv50/disp.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.c18
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_connector.h3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_hwmon.c85
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.c29
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_mem.h6
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_ttm.c24
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c2
-rw-r--r--drivers/gpu/drm/panel/Kconfig4
-rw-r--r--drivers/gpu/drm/panel/panel-edp.c5
-rw-r--r--drivers/gpu/drm/panfrost/panfrost_mmu.c44
-rw-r--r--drivers/gpu/drm/qxl/qxl_drv.c3
-rw-r--r--drivers/gpu/drm/radeon/Makefile2
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c11
-rw-r--r--drivers/gpu/drm/radeon/atombios_encoders.c59
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c61
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c1
-rw-r--r--drivers/gpu/drm/radeon/radeon_dp_mst.c778
-rw-r--r--drivers/gpu/drm/radeon/radeon_drv.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c14
-rw-r--r--drivers/gpu/drm/radeon/radeon_irq_kms.c10
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h40
-rw-r--r--drivers/gpu/drm/scheduler/sched_main.c3
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.c260
-rw-r--r--drivers/gpu/drm/solomon/ssd130x.h9
-rw-r--r--drivers/gpu/drm/sun4i/sun4i_tv.c64
-rw-r--r--drivers/gpu/drm/tests/drm_cmdline_parser_test.c373
-rw-r--r--drivers/gpu/drm/tiny/bochs.c2
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c9
-rw-r--r--drivers/gpu/drm/ttm/ttm_range_manager.c33
-rw-r--r--drivers/gpu/drm/ttm/ttm_resource.c64
-rw-r--r--drivers/gpu/drm/tve200/tve200_drv.c3
-rw-r--r--drivers/gpu/drm/vc4/vc4_crtc.c41
-rw-r--r--drivers/gpu/drm/vc4/vc4_hdmi.c7
-rw-r--r--drivers/gpu/drm/vc4/vc4_hvs.c4
-rw-r--r--drivers/gpu/drm/vc4/vc4_plane.c20
-rw-r--r--drivers/gpu/drm/vc4/vc4_vec.c127
-rw-r--r--drivers/gpu/drm/via/via_dri1.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_display.c2
-rw-r--r--drivers/gpu/drm/virtio/virtgpu_ioctl.c2
-rw-r--r--drivers/gpu/drm/vkms/Makefile1
-rw-r--r--drivers/gpu/drm/vkms/vkms_composer.c314
-rw-r--r--drivers/gpu/drm/vkms/vkms_drv.h33
-rw-r--r--drivers/gpu/drm/vkms/vkms_formats.c301
-rw-r--r--drivers/gpu/drm/vkms/vkms_formats.h12
-rw-r--r--drivers/gpu/drm/vkms/vkms_plane.c50
-rw-r--r--drivers/gpu/drm/vkms/vkms_writeback.c39
91 files changed, 5617 insertions, 2913 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 0b2ad7212ee6..2f52e8941074 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -31,6 +31,7 @@ menuconfig DRM
config DRM_MIPI_DBI
tristate
depends on DRM
+ select DRM_KMS_HELPER
config DRM_MIPI_DSI
bool
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
index 8c6b2284cf56..1f3302aebeff 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c
@@ -205,6 +205,42 @@ void amdgpu_gtt_mgr_recover(struct amdgpu_gtt_mgr *mgr)
}
/**
+ * amdgpu_gtt_mgr_intersects - test for intersection
+ *
+ * @man: Our manager object
+ * @res: The resource to test
+ * @place: The place for the new allocation
+ * @size: The size of the new allocation
+ *
+ * Simplified intersection test, only interesting if we need GART or not.
+ */
+static bool amdgpu_gtt_mgr_intersects(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res);
+}
+
+/**
+ * amdgpu_gtt_mgr_compatible - test for compatibility
+ *
+ * @man: Our manager object
+ * @res: The resource to test
+ * @place: The place for the new allocation
+ * @size: The size of the new allocation
+ *
+ * Simplified compatibility test.
+ */
+static bool amdgpu_gtt_mgr_compatible(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ return !place->lpfn || amdgpu_gtt_mgr_has_gart_addr(res);
+}
+
+/**
* amdgpu_gtt_mgr_debug - dump VRAM table
*
* @man: TTM memory type manager
@@ -225,6 +261,8 @@ static void amdgpu_gtt_mgr_debug(struct ttm_resource_manager *man,
static const struct ttm_resource_manager_func amdgpu_gtt_mgr_func = {
.alloc = amdgpu_gtt_mgr_new,
.free = amdgpu_gtt_mgr_del,
+ .intersects = amdgpu_gtt_mgr_intersects,
+ .compatible = amdgpu_gtt_mgr_compatible,
.debug = amdgpu_gtt_mgr_debug
};
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
index 2e97099808ca..b1c455329023 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c
@@ -1330,11 +1330,12 @@ uint64_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm,
static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
const struct ttm_place *place)
{
- unsigned long num_pages = bo->resource->num_pages;
struct dma_resv_iter resv_cursor;
- struct amdgpu_res_cursor cursor;
struct dma_fence *f;
+ if (!amdgpu_bo_is_amdgpu_bo(bo))
+ return ttm_bo_eviction_valuable(bo, place);
+
/* Swapout? */
if (bo->resource->mem_type == TTM_PL_SYSTEM)
return true;
@@ -1353,40 +1354,20 @@ static bool amdgpu_ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
return false;
}
- switch (bo->resource->mem_type) {
- case AMDGPU_PL_PREEMPT:
- /* Preemptible BOs don't own system resources managed by the
- * driver (pages, VRAM, GART space). They point to resources
- * owned by someone else (e.g. pageable memory in user mode
- * or a DMABuf). They are used in a preemptible context so we
- * can guarantee no deadlocks and good QoS in case of MMU
- * notifiers or DMABuf move notifiers from the resource owner.
- */
+ /* Preemptible BOs don't own system resources managed by the
+ * driver (pages, VRAM, GART space). They point to resources
+ * owned by someone else (e.g. pageable memory in user mode
+ * or a DMABuf). They are used in a preemptible context so we
+ * can guarantee no deadlocks and good QoS in case of MMU
+ * notifiers or DMABuf move notifiers from the resource owner.
+ */
+ if (bo->resource->mem_type == AMDGPU_PL_PREEMPT)
return false;
- case TTM_PL_TT:
- if (amdgpu_bo_is_amdgpu_bo(bo) &&
- amdgpu_bo_encrypted(ttm_to_amdgpu_bo(bo)))
- return false;
- return true;
- case TTM_PL_VRAM:
- /* Check each drm MM node individually */
- amdgpu_res_first(bo->resource, 0, (u64)num_pages << PAGE_SHIFT,
- &cursor);
- while (cursor.remaining) {
- if (place->fpfn < PFN_DOWN(cursor.start + cursor.size)
- && !(place->lpfn &&
- place->lpfn <= PFN_DOWN(cursor.start)))
- return true;
-
- amdgpu_res_next(&cursor, cursor.size);
- }
+ if (bo->resource->mem_type == TTM_PL_TT &&
+ amdgpu_bo_encrypted(ttm_to_amdgpu_bo(bo)))
return false;
- default:
- break;
- }
-
return ttm_bo_eviction_valuable(bo, place);
}
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
index 28ec5f8ac1c1..d1a2619fa89f 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c
@@ -721,6 +721,72 @@ uint64_t amdgpu_vram_mgr_vis_usage(struct amdgpu_vram_mgr *mgr)
}
/**
+ * amdgpu_vram_mgr_intersects - test each drm buddy block for intersection
+ *
+ * @man: TTM memory type manager
+ * @res: The resource to test
+ * @place: The place to test against
+ * @size: Size of the new allocation
+ *
+ * Test each drm buddy block for intersection for eviction decision.
+ */
+static bool amdgpu_vram_mgr_intersects(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct amdgpu_vram_mgr_resource *mgr = to_amdgpu_vram_mgr_resource(res);
+ struct drm_buddy_block *block;
+
+ /* Check each drm buddy block individually */
+ list_for_each_entry(block, &mgr->blocks, link) {
+ unsigned long fpfn =
+ amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT;
+ unsigned long lpfn = fpfn +
+ (amdgpu_vram_mgr_block_size(block) >> PAGE_SHIFT);
+
+ if (place->fpfn < lpfn &&
+ (place->lpfn && place->lpfn > fpfn))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * amdgpu_vram_mgr_compatible - test each drm buddy block for compatibility
+ *
+ * @man: TTM memory type manager
+ * @res: The resource to test
+ * @place: The place to test against
+ * @size: Size of the new allocation
+ *
+ * Test each drm buddy block for placement compatibility.
+ */
+static bool amdgpu_vram_mgr_compatible(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct amdgpu_vram_mgr_resource *mgr = to_amdgpu_vram_mgr_resource(res);
+ struct drm_buddy_block *block;
+
+ /* Check each drm buddy block individually */
+ list_for_each_entry(block, &mgr->blocks, link) {
+ unsigned long fpfn =
+ amdgpu_vram_mgr_block_start(block) >> PAGE_SHIFT;
+ unsigned long lpfn = fpfn +
+ (amdgpu_vram_mgr_block_size(block) >> PAGE_SHIFT);
+
+ if (fpfn < place->fpfn ||
+ (place->lpfn && lpfn > place->lpfn))
+ return false;
+ }
+
+ return true;
+}
+
+/**
* amdgpu_vram_mgr_debug - dump VRAM table
*
* @man: TTM memory type manager
@@ -753,6 +819,8 @@ static void amdgpu_vram_mgr_debug(struct ttm_resource_manager *man,
static const struct ttm_resource_manager_func amdgpu_vram_mgr_func = {
.alloc = amdgpu_vram_mgr_new,
.free = amdgpu_vram_mgr_del,
+ .intersects = amdgpu_vram_mgr_intersects,
+ .compatible = amdgpu_vram_mgr_compatible,
.debug = amdgpu_vram_mgr_debug
};
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 19d595321a60..e702f0d72d53 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -2808,7 +2808,8 @@ static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
};
static struct drm_mode_config_helper_funcs amdgpu_dm_mode_config_helperfuncs = {
- .atomic_commit_tail = amdgpu_dm_atomic_commit_tail
+ .atomic_commit_tail = amdgpu_dm_atomic_commit_tail,
+ .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
};
static void update_connector_ext_caps(struct amdgpu_dm_connector *aconnector)
@@ -6295,10 +6296,17 @@ amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
drm_atomic_get_old_connector_state(state, conn);
struct drm_crtc *crtc = new_con_state->crtc;
struct drm_crtc_state *new_crtc_state;
+ struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(conn);
int ret;
trace_amdgpu_dm_connector_atomic_check(new_con_state);
+ if (conn->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
+ ret = drm_dp_mst_root_conn_atomic_check(new_con_state, &aconn->mst_mgr);
+ if (ret < 0)
+ return ret;
+ }
+
if (!crtc)
return 0;
@@ -6382,6 +6390,7 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
const struct drm_display_mode *adjusted_mode = &crtc_state->adjusted_mode;
struct drm_dp_mst_topology_mgr *mst_mgr;
struct drm_dp_mst_port *mst_port;
+ struct drm_dp_mst_topology_state *mst_state;
enum dc_color_depth color_depth;
int clock, bpp = 0;
bool is_y420 = false;
@@ -6395,6 +6404,13 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
if (!crtc_state->connectors_changed && !crtc_state->mode_changed)
return 0;
+ mst_state = drm_atomic_get_mst_topology_state(state, mst_mgr);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+
+ if (!mst_state->pbn_div)
+ mst_state->pbn_div = dm_mst_get_pbn_divider(aconnector->mst_port->dc_link);
+
if (!state->duplicated) {
int max_bpc = conn_state->max_requested_bpc;
is_y420 = drm_mode_is_420_also(&connector->display_info, adjusted_mode) &&
@@ -6406,11 +6422,10 @@ static int dm_encoder_helper_atomic_check(struct drm_encoder *encoder,
clock = adjusted_mode->clock;
dm_new_connector_state->pbn = drm_dp_calc_pbn_mode(clock, bpp, false);
}
- dm_new_connector_state->vcpi_slots = drm_dp_atomic_find_vcpi_slots(state,
- mst_mgr,
- mst_port,
- dm_new_connector_state->pbn,
- dm_mst_get_pbn_divider(aconnector->dc_link));
+
+ dm_new_connector_state->vcpi_slots =
+ drm_dp_atomic_find_time_slots(state, mst_mgr, mst_port,
+ dm_new_connector_state->pbn);
if (dm_new_connector_state->vcpi_slots < 0) {
DRM_DEBUG_ATOMIC("failed finding vcpi slots: %d\n", (int)dm_new_connector_state->vcpi_slots);
return dm_new_connector_state->vcpi_slots;
@@ -6480,18 +6495,12 @@ static int dm_update_mst_vcpi_slots_for_dsc(struct drm_atomic_state *state,
dm_conn_state->pbn = pbn;
dm_conn_state->vcpi_slots = slot_num;
- drm_dp_mst_atomic_enable_dsc(state,
- aconnector->port,
- dm_conn_state->pbn,
- 0,
+ drm_dp_mst_atomic_enable_dsc(state, aconnector->port, dm_conn_state->pbn,
false);
continue;
}
- vcpi = drm_dp_mst_atomic_enable_dsc(state,
- aconnector->port,
- pbn, pbn_div,
- true);
+ vcpi = drm_dp_mst_atomic_enable_dsc(state, aconnector->port, pbn, true);
if (vcpi < 0)
return vcpi;
@@ -7966,6 +7975,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
DRM_ERROR("Waiting for fences timed out!");
drm_atomic_helper_update_legacy_modeset_state(dev, state);
+ drm_dp_mst_atomic_wait_for_dependencies(state);
dm_state = dm_atomic_get_new_state(state);
if (dm_state && dm_state->context) {
@@ -8364,7 +8374,6 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state)
dc_release_state(dc_state_temp);
}
-
static int dm_force_atomic_commit(struct drm_connector *connector)
{
int ret = 0;
@@ -9335,8 +9344,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state;
#if defined(CONFIG_DRM_AMD_DC_DCN)
struct dsc_mst_fairness_vars vars[MAX_PIPES];
- struct drm_dp_mst_topology_state *mst_state;
- struct drm_dp_mst_topology_mgr *mgr;
#endif
trace_amdgpu_dm_atomic_check_begin(state);
@@ -9575,33 +9582,6 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev,
lock_and_validation_needed = true;
}
-#if defined(CONFIG_DRM_AMD_DC_DCN)
- /* set the slot info for each mst_state based on the link encoding format */
- for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
- struct amdgpu_dm_connector *aconnector;
- struct drm_connector *connector;
- struct drm_connector_list_iter iter;
- u8 link_coding_cap;
-
- if (!mgr->mst_state )
- continue;
-
- drm_connector_list_iter_begin(dev, &iter);
- drm_for_each_connector_iter(connector, &iter) {
- int id = connector->index;
-
- if (id == mst_state->mgr->conn_base_id) {
- aconnector = to_amdgpu_dm_connector(connector);
- link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link);
- drm_dp_mst_update_slots(mst_state, link_coding_cap);
-
- break;
- }
- }
- drm_connector_list_iter_end(&iter);
-
- }
-#endif
/**
* Streams and planes are reset when there are changes that affect
* bandwidth. Anything that affects bandwidth needs to go through
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
index a0154a5f7183..7fa36ff42c0d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c
@@ -27,6 +27,7 @@
#include <linux/acpi.h>
#include <linux/i2c.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_probe_helper.h>
#include <drm/amdgpu_drm.h>
#include <drm/drm_edid.h>
@@ -153,41 +154,28 @@ enum dc_edid_status dm_helpers_parse_edid_caps(
return result;
}
-static void get_payload_table(
- struct amdgpu_dm_connector *aconnector,
- struct dp_mst_stream_allocation_table *proposed_table)
+static void
+fill_dc_mst_payload_table_from_drm(struct drm_dp_mst_topology_state *mst_state,
+ struct amdgpu_dm_connector *aconnector,
+ struct dc_dp_mst_stream_allocation_table *table)
{
- int i;
- struct drm_dp_mst_topology_mgr *mst_mgr =
- &aconnector->mst_port->mst_mgr;
-
- mutex_lock(&mst_mgr->payload_lock);
-
- proposed_table->stream_count = 0;
-
- /* number of active streams */
- for (i = 0; i < mst_mgr->max_payloads; i++) {
- if (mst_mgr->payloads[i].num_slots == 0)
- break; /* end of vcp_id table */
-
- ASSERT(mst_mgr->payloads[i].payload_state !=
- DP_PAYLOAD_DELETE_LOCAL);
-
- if (mst_mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL ||
- mst_mgr->payloads[i].payload_state ==
- DP_PAYLOAD_REMOTE) {
-
- struct dp_mst_stream_allocation *sa =
- &proposed_table->stream_allocations[
- proposed_table->stream_count];
-
- sa->slot_count = mst_mgr->payloads[i].num_slots;
- sa->vcp_id = mst_mgr->proposed_vcpis[i]->vcpi;
- proposed_table->stream_count++;
- }
+ struct dc_dp_mst_stream_allocation_table new_table = { 0 };
+ struct dc_dp_mst_stream_allocation *sa;
+ struct drm_dp_mst_atomic_payload *payload;
+
+ /* Fill payload info*/
+ list_for_each_entry(payload, &mst_state->payloads, next) {
+ if (payload->delete)
+ continue;
+
+ sa = &new_table.stream_allocations[new_table.stream_count];
+ sa->slot_count = payload->time_slots;
+ sa->vcp_id = payload->vcpi;
+ new_table.stream_count++;
}
- mutex_unlock(&mst_mgr->payload_lock);
+ /* Overwrite the old table */
+ *table = new_table;
}
void dm_helpers_dp_update_branch_info(
@@ -201,15 +189,13 @@ void dm_helpers_dp_update_branch_info(
bool dm_helpers_dp_mst_write_payload_allocation_table(
struct dc_context *ctx,
const struct dc_stream_state *stream,
- struct dp_mst_stream_allocation_table *proposed_table,
+ struct dc_dp_mst_stream_allocation_table *proposed_table,
bool enable)
{
struct amdgpu_dm_connector *aconnector;
- struct dm_connector_state *dm_conn_state;
+ struct drm_dp_mst_topology_state *mst_state;
+ struct drm_dp_mst_atomic_payload *payload;
struct drm_dp_mst_topology_mgr *mst_mgr;
- struct drm_dp_mst_port *mst_port;
- bool ret;
- u8 link_coding_cap = DP_8b_10b_ENCODING;
aconnector = (struct amdgpu_dm_connector *)stream->dm_stream_context;
/* Accessing the connector state is required for vcpi_slots allocation
@@ -220,40 +206,21 @@ bool dm_helpers_dp_mst_write_payload_allocation_table(
if (!aconnector || !aconnector->mst_port)
return false;
- dm_conn_state = to_dm_connector_state(aconnector->base.state);
-
mst_mgr = &aconnector->mst_port->mst_mgr;
-
- if (!mst_mgr->mst_state)
- return false;
-
- mst_port = aconnector->port;
-
-#if defined(CONFIG_DRM_AMD_DC_DCN)
- link_coding_cap = dc_link_dp_mst_decide_link_encoding_format(aconnector->dc_link);
-#endif
-
- if (enable) {
-
- ret = drm_dp_mst_allocate_vcpi(mst_mgr, mst_port,
- dm_conn_state->pbn,
- dm_conn_state->vcpi_slots);
- if (!ret)
- return false;
-
- } else {
- drm_dp_mst_reset_vcpi_slots(mst_mgr, mst_port);
- }
+ mst_state = to_drm_dp_mst_topology_state(mst_mgr->base.state);
/* It's OK for this to fail */
- drm_dp_update_payload_part1(mst_mgr, (link_coding_cap == DP_CAP_ANSI_128B132B) ? 0:1);
+ payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->port);
+ if (enable)
+ drm_dp_add_payload_part1(mst_mgr, mst_state, payload);
+ else
+ drm_dp_remove_payload(mst_mgr, mst_state, payload);
/* mst_mgr->->payloads are VC payload notify MST branch using DPCD or
* AUX message. The sequence is slot 1-63 allocated sequence for each
* stream. AMD ASIC stream slot allocation should follow the same
* sequence. copy DRM MST allocation to dc */
-
- get_payload_table(aconnector, proposed_table);
+ fill_dc_mst_payload_table_from_drm(mst_state, aconnector, proposed_table);
return true;
}
@@ -310,8 +277,9 @@ bool dm_helpers_dp_mst_send_payload_allocation(
bool enable)
{
struct amdgpu_dm_connector *aconnector;
+ struct drm_dp_mst_topology_state *mst_state;
struct drm_dp_mst_topology_mgr *mst_mgr;
- struct drm_dp_mst_port *mst_port;
+ struct drm_dp_mst_atomic_payload *payload;
enum mst_progress_status set_flag = MST_ALLOCATE_NEW_PAYLOAD;
enum mst_progress_status clr_flag = MST_CLEAR_ALLOCATED_PAYLOAD;
@@ -320,19 +288,16 @@ bool dm_helpers_dp_mst_send_payload_allocation(
if (!aconnector || !aconnector->mst_port)
return false;
- mst_port = aconnector->port;
-
mst_mgr = &aconnector->mst_port->mst_mgr;
+ mst_state = to_drm_dp_mst_topology_state(mst_mgr->base.state);
- if (!mst_mgr->mst_state)
- return false;
-
+ payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->port);
if (!enable) {
set_flag = MST_CLEAR_ALLOCATED_PAYLOAD;
clr_flag = MST_ALLOCATE_NEW_PAYLOAD;
}
- if (drm_dp_update_payload_part2(mst_mgr)) {
+ if (enable && drm_dp_add_payload_part2(mst_mgr, mst_state->base.state, payload)) {
amdgpu_dm_set_mst_status(&aconnector->mst_status,
set_flag, false);
} else {
@@ -342,9 +307,6 @@ bool dm_helpers_dp_mst_send_payload_allocation(
clr_flag, false);
}
- if (!enable)
- drm_dp_mst_deallocate_vcpi(mst_mgr, mst_port);
-
return true;
}
diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
index 2e74ccf7df5b..bd9606307dc7 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_mst_types.c
@@ -447,34 +447,13 @@ dm_dp_mst_detect(struct drm_connector *connector,
}
static int dm_dp_mst_atomic_check(struct drm_connector *connector,
- struct drm_atomic_state *state)
+ struct drm_atomic_state *state)
{
- struct drm_connector_state *new_conn_state =
- drm_atomic_get_new_connector_state(state, connector);
- struct drm_connector_state *old_conn_state =
- drm_atomic_get_old_connector_state(state, connector);
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
- struct drm_crtc_state *new_crtc_state;
- struct drm_dp_mst_topology_mgr *mst_mgr;
- struct drm_dp_mst_port *mst_port;
+ struct drm_dp_mst_topology_mgr *mst_mgr = &aconnector->mst_port->mst_mgr;
+ struct drm_dp_mst_port *mst_port = aconnector->port;
- mst_port = aconnector->port;
- mst_mgr = &aconnector->mst_port->mst_mgr;
-
- if (!old_conn_state->crtc)
- return 0;
-
- if (new_conn_state->crtc) {
- new_crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
- if (!new_crtc_state ||
- !drm_atomic_crtc_needs_modeset(new_crtc_state) ||
- new_crtc_state->enable)
- return 0;
- }
-
- return drm_dp_atomic_release_vcpi_slots(state,
- mst_mgr,
- mst_port);
+ return drm_dp_atomic_release_time_slots(state, mst_mgr, mst_port);
}
static const struct drm_connector_helper_funcs dm_dp_mst_connector_helper_funcs = {
@@ -618,15 +597,8 @@ void amdgpu_dm_initialize_dp_connector(struct amdgpu_display_manager *dm,
dc_link_dp_get_max_link_enc_cap(aconnector->dc_link, &max_link_enc_cap);
aconnector->mst_mgr.cbs = &dm_mst_cbs;
- drm_dp_mst_topology_mgr_init(
- &aconnector->mst_mgr,
- adev_to_drm(dm->adev),
- &aconnector->dm_dp_aux.aux,
- 16,
- 4,
- max_link_enc_cap.lane_count,
- drm_dp_bw_code_to_link_rate(max_link_enc_cap.link_rate),
- aconnector->connector_id);
+ drm_dp_mst_topology_mgr_init(&aconnector->mst_mgr, adev_to_drm(dm->adev),
+ &aconnector->dm_dp_aux.aux, 16, 4, aconnector->connector_id);
drm_connector_attach_dp_subconnector_property(&aconnector->base);
}
@@ -731,6 +703,7 @@ static int bpp_x16_from_pbn(struct dsc_mst_fairness_params param, int pbn)
}
static bool increase_dsc_bpp(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_state *mst_state,
struct dc_link *dc_link,
struct dsc_mst_fairness_params *params,
struct dsc_mst_fairness_vars *vars,
@@ -743,12 +716,9 @@ static bool increase_dsc_bpp(struct drm_atomic_state *state,
int min_initial_slack;
int next_index;
int remaining_to_increase = 0;
- int pbn_per_timeslot;
int link_timeslots_used;
int fair_pbn_alloc;
- pbn_per_timeslot = dm_mst_get_pbn_divider(dc_link);
-
for (i = 0; i < count; i++) {
if (vars[i + k].dsc_enabled) {
initial_slack[i] =
@@ -779,46 +749,43 @@ static bool increase_dsc_bpp(struct drm_atomic_state *state,
link_timeslots_used = 0;
for (i = 0; i < count; i++)
- link_timeslots_used += DIV_ROUND_UP(vars[i + k].pbn, pbn_per_timeslot);
+ link_timeslots_used += DIV_ROUND_UP(vars[i + k].pbn, mst_state->pbn_div);
- fair_pbn_alloc = (63 - link_timeslots_used) / remaining_to_increase * pbn_per_timeslot;
+ fair_pbn_alloc =
+ (63 - link_timeslots_used) / remaining_to_increase * mst_state->pbn_div;
if (initial_slack[next_index] > fair_pbn_alloc) {
vars[next_index].pbn += fair_pbn_alloc;
- if (drm_dp_atomic_find_vcpi_slots(state,
+ if (drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
- vars[next_index].pbn,
- pbn_per_timeslot) < 0)
+ vars[next_index].pbn) < 0)
return false;
if (!drm_dp_mst_atomic_check(state)) {
vars[next_index].bpp_x16 = bpp_x16_from_pbn(params[next_index], vars[next_index].pbn);
} else {
vars[next_index].pbn -= fair_pbn_alloc;
- if (drm_dp_atomic_find_vcpi_slots(state,
+ if (drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
- vars[next_index].pbn,
- pbn_per_timeslot) < 0)
+ vars[next_index].pbn) < 0)
return false;
}
} else {
vars[next_index].pbn += initial_slack[next_index];
- if (drm_dp_atomic_find_vcpi_slots(state,
+ if (drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
- vars[next_index].pbn,
- pbn_per_timeslot) < 0)
+ vars[next_index].pbn) < 0)
return false;
if (!drm_dp_mst_atomic_check(state)) {
vars[next_index].bpp_x16 = params[next_index].bw_range.max_target_bpp_x16;
} else {
vars[next_index].pbn -= initial_slack[next_index];
- if (drm_dp_atomic_find_vcpi_slots(state,
+ if (drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
- vars[next_index].pbn,
- pbn_per_timeslot) < 0)
+ vars[next_index].pbn) < 0)
return false;
}
}
@@ -872,11 +839,10 @@ static bool try_disable_dsc(struct drm_atomic_state *state,
break;
vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.stream_kbps);
- if (drm_dp_atomic_find_vcpi_slots(state,
+ if (drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
- vars[next_index].pbn,
- dm_mst_get_pbn_divider(dc_link)) < 0)
+ vars[next_index].pbn) < 0)
return false;
if (!drm_dp_mst_atomic_check(state)) {
@@ -884,11 +850,10 @@ static bool try_disable_dsc(struct drm_atomic_state *state,
vars[next_index].bpp_x16 = 0;
} else {
vars[next_index].pbn = kbps_to_peak_pbn(params[next_index].bw_range.max_kbps);
- if (drm_dp_atomic_find_vcpi_slots(state,
+ if (drm_dp_atomic_find_time_slots(state,
params[next_index].port->mgr,
params[next_index].port,
- vars[next_index].pbn,
- dm_mst_get_pbn_divider(dc_link)) < 0)
+ vars[next_index].pbn) < 0)
return false;
}
@@ -902,17 +867,27 @@ static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
struct dc_state *dc_state,
struct dc_link *dc_link,
struct dsc_mst_fairness_vars *vars,
+ struct drm_dp_mst_topology_mgr *mgr,
int *link_vars_start_index)
{
- int i, k;
struct dc_stream_state *stream;
struct dsc_mst_fairness_params params[MAX_PIPES];
struct amdgpu_dm_connector *aconnector;
+ struct drm_dp_mst_topology_state *mst_state = drm_atomic_get_mst_topology_state(state, mgr);
int count = 0;
+ int i, k;
bool debugfs_overwrite = false;
memset(params, 0, sizeof(params));
+ if (IS_ERR(mst_state))
+ return false;
+
+ mst_state->pbn_div = dm_mst_get_pbn_divider(dc_link);
+#if defined(CONFIG_DRM_AMD_DC_DCN)
+ drm_dp_mst_update_slots(mst_state, dc_link_dp_mst_decide_link_encoding_format(dc_link));
+#endif
+
/* Set up params */
for (i = 0; i < dc_state->stream_count; i++) {
struct dc_dsc_policy dsc_policy = {0};
@@ -971,11 +946,8 @@ static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps);
vars[i + k].dsc_enabled = false;
vars[i + k].bpp_x16 = 0;
- if (drm_dp_atomic_find_vcpi_slots(state,
- params[i].port->mgr,
- params[i].port,
- vars[i + k].pbn,
- dm_mst_get_pbn_divider(dc_link)) < 0)
+ if (drm_dp_atomic_find_time_slots(state, params[i].port->mgr, params[i].port,
+ vars[i + k].pbn) < 0)
return false;
}
if (!drm_dp_mst_atomic_check(state) && !debugfs_overwrite) {
@@ -989,21 +961,15 @@ static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.min_kbps);
vars[i + k].dsc_enabled = true;
vars[i + k].bpp_x16 = params[i].bw_range.min_target_bpp_x16;
- if (drm_dp_atomic_find_vcpi_slots(state,
- params[i].port->mgr,
- params[i].port,
- vars[i + k].pbn,
- dm_mst_get_pbn_divider(dc_link)) < 0)
+ if (drm_dp_atomic_find_time_slots(state, params[i].port->mgr,
+ params[i].port, vars[i + k].pbn) < 0)
return false;
} else {
vars[i + k].pbn = kbps_to_peak_pbn(params[i].bw_range.stream_kbps);
vars[i + k].dsc_enabled = false;
vars[i + k].bpp_x16 = 0;
- if (drm_dp_atomic_find_vcpi_slots(state,
- params[i].port->mgr,
- params[i].port,
- vars[i + k].pbn,
- dm_mst_get_pbn_divider(dc_link)) < 0)
+ if (drm_dp_atomic_find_time_slots(state, params[i].port->mgr,
+ params[i].port, vars[i + k].pbn) < 0)
return false;
}
}
@@ -1011,7 +977,7 @@ static bool compute_mst_dsc_configs_for_link(struct drm_atomic_state *state,
return false;
/* Optimize degree of compression */
- if (!increase_dsc_bpp(state, dc_link, params, vars, count, k))
+ if (!increase_dsc_bpp(state, mst_state, dc_link, params, vars, count, k))
return false;
if (!try_disable_dsc(state, dc_link, params, vars, count, k))
@@ -1157,8 +1123,9 @@ bool compute_mst_dsc_configs_for_state(struct drm_atomic_state *state,
continue;
mutex_lock(&aconnector->mst_mgr.lock);
- if (!compute_mst_dsc_configs_for_link(state, dc_state, stream->link,
- vars, &link_vars_start_index)) {
+ if (!compute_mst_dsc_configs_for_link(state, dc_state, stream->link, vars,
+ &aconnector->mst_mgr,
+ &link_vars_start_index)) {
mutex_unlock(&aconnector->mst_mgr.lock);
return false;
}
@@ -1216,10 +1183,8 @@ static bool
continue;
mutex_lock(&aconnector->mst_mgr.lock);
- if (!compute_mst_dsc_configs_for_link(state,
- dc_state,
- stream->link,
- vars,
+ if (!compute_mst_dsc_configs_for_link(state, dc_state, stream->link, vars,
+ &aconnector->mst_mgr,
&link_vars_start_index)) {
mutex_unlock(&aconnector->mst_mgr.lock);
return false;
diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link.c b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
index 66d2ae7aacf5..506fdbbc1b60 100644
--- a/drivers/gpu/drm/amd/display/dc/core/dc_link.c
+++ b/drivers/gpu/drm/amd/display/dc/core/dc_link.c
@@ -3516,7 +3516,7 @@ static void update_mst_stream_alloc_table(
struct dc_link *link,
struct stream_encoder *stream_enc,
struct hpo_dp_stream_encoder *hpo_dp_stream_enc, // TODO: Rename stream_enc to dio_stream_enc?
- const struct dp_mst_stream_allocation_table *proposed_table)
+ const struct dc_dp_mst_stream_allocation_table *proposed_table)
{
struct link_mst_stream_allocation work_table[MAX_CONTROLLER_NUM] = { 0 };
struct link_mst_stream_allocation *dc_alloc;
@@ -3679,7 +3679,7 @@ enum dc_status dc_link_allocate_mst_payload(struct pipe_ctx *pipe_ctx)
{
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
- struct dp_mst_stream_allocation_table proposed_table = {0};
+ struct dc_dp_mst_stream_allocation_table proposed_table = {0};
struct fixed31_32 avg_time_slots_per_mtp;
struct fixed31_32 pbn;
struct fixed31_32 pbn_per_slot;
@@ -3784,7 +3784,7 @@ enum dc_status dc_link_reduce_mst_payload(struct pipe_ctx *pipe_ctx, uint32_t bw
struct fixed31_32 avg_time_slots_per_mtp;
struct fixed31_32 pbn;
struct fixed31_32 pbn_per_slot;
- struct dp_mst_stream_allocation_table proposed_table = {0};
+ struct dc_dp_mst_stream_allocation_table proposed_table = {0};
uint8_t i;
const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
DC_LOGGER_INIT(link->ctx->logger);
@@ -3873,7 +3873,7 @@ enum dc_status dc_link_increase_mst_payload(struct pipe_ctx *pipe_ctx, uint32_t
struct fixed31_32 avg_time_slots_per_mtp;
struct fixed31_32 pbn;
struct fixed31_32 pbn_per_slot;
- struct dp_mst_stream_allocation_table proposed_table = {0};
+ struct dc_dp_mst_stream_allocation_table proposed_table = {0};
uint8_t i;
enum act_return_status ret;
const struct link_hwss *link_hwss = get_link_hwss(link, &pipe_ctx->link_res);
@@ -3957,7 +3957,7 @@ static enum dc_status deallocate_mst_payload(struct pipe_ctx *pipe_ctx)
{
struct dc_stream_state *stream = pipe_ctx->stream;
struct dc_link *link = stream->link;
- struct dp_mst_stream_allocation_table proposed_table = {0};
+ struct dc_dp_mst_stream_allocation_table proposed_table = {0};
struct fixed31_32 avg_time_slots_per_mtp = dc_fixpt_from_int(0);
int i;
bool mst_mode = (link->type == dc_connection_mst_branch);
diff --git a/drivers/gpu/drm/amd/display/dc/dm_helpers.h b/drivers/gpu/drm/amd/display/dc/dm_helpers.h
index fb6a2d7b6470..8173f4b80424 100644
--- a/drivers/gpu/drm/amd/display/dc/dm_helpers.h
+++ b/drivers/gpu/drm/amd/display/dc/dm_helpers.h
@@ -33,7 +33,7 @@
#include "dc_types.h"
#include "dc.h"
-struct dp_mst_stream_allocation_table;
+struct dc_dp_mst_stream_allocation_table;
struct aux_payload;
enum aux_return_code_type;
@@ -77,7 +77,7 @@ void dm_helpers_dp_update_branch_info(
bool dm_helpers_dp_mst_write_payload_allocation_table(
struct dc_context *ctx,
const struct dc_stream_state *stream,
- struct dp_mst_stream_allocation_table *proposed_table,
+ struct dc_dp_mst_stream_allocation_table *proposed_table,
bool enable);
/*
diff --git a/drivers/gpu/drm/amd/display/include/link_service_types.h b/drivers/gpu/drm/amd/display/include/link_service_types.h
index 79fabc51c991..d76ab72baf0c 100644
--- a/drivers/gpu/drm/amd/display/include/link_service_types.h
+++ b/drivers/gpu/drm/amd/display/include/link_service_types.h
@@ -246,8 +246,16 @@ union dpcd_training_lane_set {
};
+/* AMD's copy of various payload data for MST. We have two copies of the payload table (one in DRM,
+ * one in DC) since DRM's MST helpers can't be accessed here. This stream allocation table should
+ * _ONLY_ be filled out from DM and then passed to DC, do NOT use these for _any_ kind of atomic
+ * state calculations in DM, or you will break something.
+ */
+
+struct drm_dp_mst_port;
+
/* DP MST stream allocation (payload bandwidth number) */
-struct dp_mst_stream_allocation {
+struct dc_dp_mst_stream_allocation {
uint8_t vcp_id;
/* number of slots required for the DP stream in
* transport packet */
@@ -255,11 +263,11 @@ struct dp_mst_stream_allocation {
};
/* DP MST stream allocation table */
-struct dp_mst_stream_allocation_table {
+struct dc_dp_mst_stream_allocation_table {
/* number of DP video streams */
int stream_count;
/* array of stream allocations */
- struct dp_mst_stream_allocation stream_allocations[MAX_CONTROLLER_NUM];
+ struct dc_dp_mst_stream_allocation stream_allocations[MAX_CONTROLLER_NUM];
};
#endif /*__DAL_LINK_SERVICE_TYPES_H__*/
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c
index 79fc7a50b497..0c323b5a1c99 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.c
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.c
@@ -1440,6 +1440,20 @@ static void anx7625_start_dp_work(struct anx7625_data *ctx)
static int anx7625_read_hpd_status_p0(struct anx7625_data *ctx)
{
+ int ret;
+
+ /* Set irq detect window to 2ms */
+ ret = anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+ HPD_DET_TIMER_BIT0_7, HPD_TIME & 0xFF);
+ ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+ HPD_DET_TIMER_BIT8_15,
+ (HPD_TIME >> 8) & 0xFF);
+ ret |= anx7625_reg_write(ctx, ctx->i2c.tx_p2_client,
+ HPD_DET_TIMER_BIT16_23,
+ (HPD_TIME >> 16) & 0xFF);
+ if (ret < 0)
+ return ret;
+
return anx7625_reg_read(ctx, ctx->i2c.rx_p0_client, SYSTEM_STSTUS);
}
@@ -1797,8 +1811,13 @@ static int anx7625_audio_hw_params(struct device *dev, void *data,
int wl, ch, rate;
int ret = 0;
- if (fmt->fmt != HDMI_DSP_A) {
- DRM_DEV_ERROR(dev, "only supports DSP_A\n");
+ if (anx7625_sink_detect(ctx) == connector_status_disconnected) {
+ DRM_DEV_DEBUG_DRIVER(dev, "DP not connected\n");
+ return 0;
+ }
+
+ if (fmt->fmt != HDMI_DSP_A && fmt->fmt != HDMI_I2S) {
+ DRM_DEV_ERROR(dev, "only supports DSP_A & I2S\n");
return -EINVAL;
}
@@ -1806,10 +1825,16 @@ static int anx7625_audio_hw_params(struct device *dev, void *data,
params->sample_rate, params->sample_width,
params->cea.channels);
- ret |= anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client,
- AUDIO_CHANNEL_STATUS_6,
- ~I2S_SLAVE_MODE,
- TDM_SLAVE_MODE);
+ if (fmt->fmt == HDMI_DSP_A)
+ ret = anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client,
+ AUDIO_CHANNEL_STATUS_6,
+ ~I2S_SLAVE_MODE,
+ TDM_SLAVE_MODE);
+ else
+ ret = anx7625_write_and_or(ctx, ctx->i2c.tx_p2_client,
+ AUDIO_CHANNEL_STATUS_6,
+ ~TDM_SLAVE_MODE,
+ I2S_SLAVE_MODE);
/* Word length */
switch (params->sample_width) {
diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.h b/drivers/gpu/drm/bridge/analogix/anx7625.h
index e257a84db962..14f33d6be289 100644
--- a/drivers/gpu/drm/bridge/analogix/anx7625.h
+++ b/drivers/gpu/drm/bridge/analogix/anx7625.h
@@ -132,6 +132,12 @@
#define I2S_SLAVE_MODE 0x08
#define AUDIO_LAYOUT 0x01
+#define HPD_DET_TIMER_BIT0_7 0xea
+#define HPD_DET_TIMER_BIT8_15 0xeb
+#define HPD_DET_TIMER_BIT16_23 0xec
+/* HPD debounce time 2ms for 27M clock */
+#define HPD_TIME 54000
+
#define AUDIO_CONTROL_REGISTER 0xe6
#define TDM_TIMING_MODE 0x08
diff --git a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
index ab63e7b11944..31442a922502 100644
--- a/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
+++ b/drivers/gpu/drm/bridge/cadence/cdns-mhdp8546-core.c
@@ -2605,7 +2605,8 @@ static int cdns_mhdp_remove(struct platform_device *pdev)
pm_runtime_disable(&pdev->dev);
cancel_work_sync(&mhdp->modeset_retry_work);
- flush_scheduled_work();
+ flush_work(&mhdp->hpd_work);
+ /* Ignoring mhdp->hdcp.check_work and mhdp->hdcp.prop_work here. */
clk_disable_unprepare(mhdp->clk);
diff --git a/drivers/gpu/drm/bridge/chipone-icn6211.c b/drivers/gpu/drm/bridge/chipone-icn6211.c
index b07d2d16c3cf..bf920c3503aa 100644
--- a/drivers/gpu/drm/bridge/chipone-icn6211.c
+++ b/drivers/gpu/drm/bridge/chipone-icn6211.c
@@ -11,6 +11,7 @@
#include <linux/bitfield.h>
#include <linux/bits.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
@@ -151,6 +152,8 @@ struct chipone {
struct regulator *vdd1;
struct regulator *vdd2;
struct regulator *vdd3;
+ struct clk *refclk;
+ unsigned long refclk_rate;
bool interface_i2c;
};
@@ -259,7 +262,7 @@ static void chipone_configure_pll(struct chipone *icn,
/*
* DSI byte clock frequency (input into PLL) is calculated as:
- * DSI_CLK = mode clock * bpp / dsi_data_lanes / 8
+ * DSI_CLK = HS clock / 4
*
* DPI pixel clock frequency (output from PLL) is mode clock.
*
@@ -273,8 +276,10 @@ static void chipone_configure_pll(struct chipone *icn,
* It seems the PLL input clock after applying P pre-divider have
* to be lower than 20 MHz.
*/
- fin = mode_clock * mipi_dsi_pixel_format_to_bpp(icn->dsi->format) /
- icn->dsi->lanes / 8; /* in Hz */
+ if (icn->refclk)
+ fin = icn->refclk_rate;
+ else
+ fin = icn->dsi->hs_rate / 4; /* in Hz */
/* Minimum value of P predivider for PLL input in 5..20 MHz */
p_min = clamp(DIV_ROUND_UP(fin, 20000000), 1U, 31U);
@@ -319,16 +324,18 @@ static void chipone_configure_pll(struct chipone *icn,
best_p_pot = !(best_p & 1);
dev_dbg(icn->dev,
- "PLL: P[3:0]=%d P[4]=2*%d M=%d S[7:5]=2^%d delta=%d => DSI f_in=%d Hz ; DPI f_out=%d Hz\n",
+ "PLL: P[3:0]=%d P[4]=2*%d M=%d S[7:5]=2^%d delta=%d => DSI f_in(%s)=%d Hz ; DPI f_out=%d Hz\n",
best_p >> best_p_pot, best_p_pot, best_m, best_s + 1,
- min_delta, fin, (fin * best_m) / (best_p << (best_s + 1)));
+ min_delta, icn->refclk ? "EXT" : "DSI", fin,
+ (fin * best_m) / (best_p << (best_s + 1)));
ref_div = PLL_REF_DIV_P(best_p >> best_p_pot) | PLL_REF_DIV_S(best_s);
if (best_p_pot) /* Prefer /2 pre-divider */
ref_div |= PLL_REF_DIV_Pe;
- /* Clock source selection fixed to MIPI DSI clock lane */
- chipone_writeb(icn, PLL_CTRL(6), PLL_CTRL_6_MIPI_CLK);
+ /* Clock source selection either external clock or MIPI DSI clock lane */
+ chipone_writeb(icn, PLL_CTRL(6),
+ icn->refclk ? PLL_CTRL_6_EXTERNAL : PLL_CTRL_6_MIPI_CLK);
chipone_writeb(icn, PLL_REF_DIV, ref_div);
chipone_writeb(icn, PLL_INT(0), best_m);
}
@@ -464,6 +471,11 @@ static void chipone_atomic_pre_enable(struct drm_bridge *bridge,
"failed to enable VDD3 regulator: %d\n", ret);
}
+ ret = clk_prepare_enable(icn->refclk);
+ if (ret)
+ DRM_DEV_ERROR(icn->dev,
+ "failed to enable RECLK clock: %d\n", ret);
+
gpiod_set_value(icn->enable_gpio, 1);
usleep_range(10000, 11000);
@@ -474,6 +486,8 @@ static void chipone_atomic_post_disable(struct drm_bridge *bridge,
{
struct chipone *icn = bridge_to_chipone(bridge);
+ clk_disable_unprepare(icn->refclk);
+
if (icn->vdd1)
regulator_disable(icn->vdd1);
@@ -515,6 +529,8 @@ static int chipone_dsi_attach(struct chipone *icn)
dsi->format = MIPI_DSI_FMT_RGB888;
dsi->mode_flags = MIPI_DSI_MODE_VIDEO | MIPI_DSI_MODE_VIDEO_BURST |
MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_NO_EOT_PACKET;
+ dsi->hs_rate = 500000000;
+ dsi->lp_rate = 16000000;
ret = mipi_dsi_attach(dsi);
if (ret < 0)
@@ -617,6 +633,20 @@ static int chipone_parse_dt(struct chipone *icn)
struct device *dev = icn->dev;
int ret;
+ icn->refclk = devm_clk_get_optional(dev, "refclk");
+ if (IS_ERR(icn->refclk)) {
+ ret = PTR_ERR(icn->refclk);
+ DRM_DEV_ERROR(dev, "failed to get REFCLK clock: %d\n", ret);
+ return ret;
+ } else if (icn->refclk) {
+ icn->refclk_rate = clk_get_rate(icn->refclk);
+ if (icn->refclk_rate < 10000000 || icn->refclk_rate > 154000000) {
+ DRM_DEV_ERROR(dev, "REFCLK out of range: %ld Hz\n",
+ icn->refclk_rate);
+ return -EINVAL;
+ }
+ }
+
icn->vdd1 = devm_regulator_get_optional(dev, "vdd1");
if (IS_ERR(icn->vdd1)) {
ret = PTR_ERR(icn->vdd1);
diff --git a/drivers/gpu/drm/bridge/chrontel-ch7033.c b/drivers/gpu/drm/bridge/chrontel-ch7033.c
index ba060277c3fd..c5719908ce2d 100644
--- a/drivers/gpu/drm/bridge/chrontel-ch7033.c
+++ b/drivers/gpu/drm/bridge/chrontel-ch7033.c
@@ -68,6 +68,7 @@ enum {
BYTE_SWAP_GBR = 3,
BYTE_SWAP_BRG = 4,
BYTE_SWAP_BGR = 5,
+ BYTE_SWAP_MAX = 6,
};
/* Page 0, Register 0x19 */
@@ -355,6 +356,8 @@ static void ch7033_bridge_mode_set(struct drm_bridge *bridge,
int hsynclen = mode->hsync_end - mode->hsync_start;
int vbporch = mode->vsync_start - mode->vdisplay;
int vsynclen = mode->vsync_end - mode->vsync_start;
+ u8 byte_swap;
+ int ret;
/*
* Page 4
@@ -398,8 +401,16 @@ static void ch7033_bridge_mode_set(struct drm_bridge *bridge,
regmap_write(priv->regmap, 0x15, vbporch);
regmap_write(priv->regmap, 0x16, vsynclen);
- /* Input color swap. */
- regmap_update_bits(priv->regmap, 0x18, SWAP, BYTE_SWAP_BGR);
+ /* Input color swap. Byte order is optional and will default to
+ * BYTE_SWAP_BGR to preserve backwards compatibility with existing
+ * driver.
+ */
+ ret = of_property_read_u8(priv->bridge.of_node, "chrontel,byteswap",
+ &byte_swap);
+ if (!ret && byte_swap < BYTE_SWAP_MAX)
+ regmap_update_bits(priv->regmap, 0x18, SWAP, byte_swap);
+ else
+ regmap_update_bits(priv->regmap, 0x18, SWAP, BYTE_SWAP_BGR);
/* Input clock and sync polarity. */
regmap_update_bits(priv->regmap, 0x19, 0x1, mode->clock >> 16);
diff --git a/drivers/gpu/drm/bridge/ite-it6505.c b/drivers/gpu/drm/bridge/ite-it6505.c
index f9251ec49bf0..2bb957cffd94 100644
--- a/drivers/gpu/drm/bridge/ite-it6505.c
+++ b/drivers/gpu/drm/bridge/ite-it6505.c
@@ -2951,9 +2951,6 @@ static void it6505_bridge_atomic_enable(struct drm_bridge *bridge,
if (ret)
dev_err(dev, "Failed to setup AVI infoframe: %d", ret);
- it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link,
- DP_SET_POWER_D0);
-
it6505_update_video_parameter(it6505, mode);
ret = it6505_send_video_infoframe(it6505, &frame);
@@ -2963,6 +2960,9 @@ static void it6505_bridge_atomic_enable(struct drm_bridge *bridge,
it6505_int_mask_enable(it6505);
it6505_video_reset(it6505);
+
+ it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link,
+ DP_SET_POWER_D0);
}
static void it6505_bridge_atomic_disable(struct drm_bridge *bridge,
@@ -2974,9 +2974,9 @@ static void it6505_bridge_atomic_disable(struct drm_bridge *bridge,
DRM_DEV_DEBUG_DRIVER(dev, "start");
if (it6505->powered) {
- it6505_video_disable(it6505);
it6505_drm_dp_link_set_power(&it6505->aux, &it6505->link,
DP_SET_POWER_D3);
+ it6505_video_disable(it6505);
}
}
diff --git a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
index cce98bf2a4e7..72248a565579 100644
--- a/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
+++ b/drivers/gpu/drm/bridge/megachips-stdpxxxx-ge-b850v3-fw.c
@@ -296,7 +296,9 @@ static void ge_b850v3_lvds_remove(void)
* This check is to avoid both the drivers
* removing the bridge in their remove() function
*/
- if (!ge_b850v3_lvds_ptr)
+ if (!ge_b850v3_lvds_ptr ||
+ !ge_b850v3_lvds_ptr->stdp2690_i2c ||
+ !ge_b850v3_lvds_ptr->stdp4028_i2c)
goto out;
drm_bridge_remove(&ge_b850v3_lvds_ptr->bridge);
diff --git a/drivers/gpu/drm/bridge/parade-ps8640.c b/drivers/gpu/drm/bridge/parade-ps8640.c
index 49107a6cdac1..d7483c13c569 100644
--- a/drivers/gpu/drm/bridge/parade-ps8640.c
+++ b/drivers/gpu/drm/bridge/parade-ps8640.c
@@ -375,6 +375,11 @@ static int __maybe_unused ps8640_resume(struct device *dev)
gpiod_set_value(ps_bridge->gpio_reset, 1);
usleep_range(2000, 2500);
gpiod_set_value(ps_bridge->gpio_reset, 0);
+ /* Double reset for T4 and T5 */
+ msleep(50);
+ gpiod_set_value(ps_bridge->gpio_reset, 1);
+ msleep(50);
+ gpiod_set_value(ps_bridge->gpio_reset, 0);
/*
* Mystery 200 ms delay for the "MCU to be ready". It's unclear if
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index 25a60eb4d67c..40d8ca37f5bc 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -3096,6 +3096,7 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
{
struct dw_hdmi *hdmi = dev_id;
u8 intr_stat, phy_int_pol, phy_pol_mask, phy_stat;
+ enum drm_connector_status status = connector_status_unknown;
intr_stat = hdmi_readb(hdmi, HDMI_IH_PHY_STAT0);
phy_int_pol = hdmi_readb(hdmi, HDMI_PHY_POL0);
@@ -3134,13 +3135,15 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
cec_notifier_phys_addr_invalidate(hdmi->cec_notifier);
mutex_unlock(&hdmi->cec_notifier_mutex);
}
- }
- if (intr_stat & HDMI_IH_PHY_STAT0_HPD) {
- enum drm_connector_status status = phy_int_pol & HDMI_PHY_HPD
- ? connector_status_connected
- : connector_status_disconnected;
+ if (phy_stat & HDMI_PHY_HPD)
+ status = connector_status_connected;
+
+ if (!(phy_stat & (HDMI_PHY_HPD | HDMI_PHY_RX_SENSE)))
+ status = connector_status_disconnected;
+ }
+ if (status != connector_status_unknown) {
dev_dbg(hdmi->dev, "EVENT=%s\n",
status == connector_status_connected ?
"plugin" : "plugout");
diff --git a/drivers/gpu/drm/bridge/tc358767.c b/drivers/gpu/drm/bridge/tc358767.c
index baaed4d37911..89e060b273ef 100644
--- a/drivers/gpu/drm/bridge/tc358767.c
+++ b/drivers/gpu/drm/bridge/tc358767.c
@@ -1913,22 +1913,23 @@ static int tc_mipi_dsi_host_attach(struct tc_data *tc)
static int tc_probe_dpi_bridge_endpoint(struct tc_data *tc)
{
struct device *dev = tc->dev;
+ struct drm_bridge *bridge;
struct drm_panel *panel;
int ret;
/* port@1 is the DPI input/output port */
- ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, NULL);
+ ret = drm_of_find_panel_or_bridge(dev->of_node, 1, 0, &panel, &bridge);
if (ret && ret != -ENODEV)
return ret;
if (panel) {
- struct drm_bridge *panel_bridge;
-
- panel_bridge = devm_drm_panel_bridge_add(dev, panel);
- if (IS_ERR(panel_bridge))
- return PTR_ERR(panel_bridge);
+ bridge = devm_drm_panel_bridge_add(dev, panel);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+ }
- tc->panel_bridge = panel_bridge;
+ if (bridge) {
+ tc->panel_bridge = bridge;
tc->bridge.type = DRM_MODE_CONNECTOR_DPI;
tc->bridge.funcs = &tc_dpi_bridge_funcs;
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
index 90bbabde1595..3c3561942eb6 100644
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
@@ -29,6 +29,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_bridge_connector.h>
+#include <drm/drm_edid.h>
#include <drm/drm_mipi_dsi.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
@@ -68,6 +69,7 @@
#define BPP_18_RGB BIT(0)
#define SN_HPD_DISABLE_REG 0x5C
#define HPD_DISABLE BIT(0)
+#define HPD_DEBOUNCED_STATE BIT(4)
#define SN_GPIO_IO_REG 0x5E
#define SN_GPIO_INPUT_SHIFT 4
#define SN_GPIO_OUTPUT_SHIFT 0
@@ -92,6 +94,8 @@
#define SN_DATARATE_CONFIG_REG 0x94
#define DP_DATARATE_MASK GENMASK(7, 5)
#define DP_DATARATE(x) ((x) << 5)
+#define SN_TRAINING_SETTING_REG 0x95
+#define SCRAMBLE_DISABLE BIT(4)
#define SN_ML_TX_MODE_REG 0x96
#define ML_TX_MAIN_LINK_OFF 0
#define ML_TX_NORMAL_MODE BIT(0)
@@ -747,6 +751,29 @@ ti_sn_bridge_mode_valid(struct drm_bridge *bridge,
if (mode->clock > 594000)
return MODE_CLOCK_HIGH;
+ /*
+ * The front and back porch registers are 8 bits, and pulse width
+ * registers are 15 bits, so reject any modes with larger periods.
+ */
+
+ if ((mode->hsync_start - mode->hdisplay) > 0xff)
+ return MODE_HBLANK_WIDE;
+
+ if ((mode->vsync_start - mode->vdisplay) > 0xff)
+ return MODE_VBLANK_WIDE;
+
+ if ((mode->hsync_end - mode->hsync_start) > 0x7fff)
+ return MODE_HSYNC_WIDE;
+
+ if ((mode->vsync_end - mode->vsync_start) > 0x7fff)
+ return MODE_VSYNC_WIDE;
+
+ if ((mode->htotal - mode->hsync_end) > 0xff)
+ return MODE_HBLANK_WIDE;
+
+ if ((mode->vtotal - mode->vsync_end) > 0xff)
+ return MODE_VBLANK_WIDE;
+
return MODE_OK;
}
@@ -1047,12 +1074,23 @@ static void ti_sn_bridge_atomic_enable(struct drm_bridge *bridge,
/*
* The SN65DSI86 only supports ASSR Display Authentication method and
- * this method is enabled by default. An eDP panel must support this
+ * this method is enabled for eDP panels. An eDP panel must support this
* authentication method. We need to enable this method in the eDP panel
* at DisplayPort address 0x0010A prior to link training.
+ *
+ * As only ASSR is supported by SN65DSI86, for full DisplayPort displays
+ * we need to disable the scrambler.
*/
- drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET,
- DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
+ if (pdata->bridge.type == DRM_MODE_CONNECTOR_eDP) {
+ drm_dp_dpcd_writeb(&pdata->aux, DP_EDP_CONFIGURATION_SET,
+ DP_ALTERNATE_SCRAMBLER_RESET_ENABLE);
+
+ regmap_update_bits(pdata->regmap, SN_TRAINING_SETTING_REG,
+ SCRAMBLE_DISABLE, 0);
+ } else {
+ regmap_update_bits(pdata->regmap, SN_TRAINING_SETTING_REG,
+ SCRAMBLE_DISABLE, SCRAMBLE_DISABLE);
+ }
bpp = ti_sn_bridge_get_bpp(connector);
/* Set the DP output format (18 bpp or 24 bpp) */
@@ -1122,10 +1160,33 @@ static void ti_sn_bridge_atomic_post_disable(struct drm_bridge *bridge,
pm_runtime_put_sync(pdata->dev);
}
+static enum drm_connector_status ti_sn_bridge_detect(struct drm_bridge *bridge)
+{
+ struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+ int val = 0;
+
+ pm_runtime_get_sync(pdata->dev);
+ regmap_read(pdata->regmap, SN_HPD_DISABLE_REG, &val);
+ pm_runtime_put_autosuspend(pdata->dev);
+
+ return val & HPD_DEBOUNCED_STATE ? connector_status_connected
+ : connector_status_disconnected;
+}
+
+static struct edid *ti_sn_bridge_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct ti_sn65dsi86 *pdata = bridge_to_ti_sn65dsi86(bridge);
+
+ return drm_get_edid(connector, &pdata->aux.ddc);
+}
+
static const struct drm_bridge_funcs ti_sn_bridge_funcs = {
.attach = ti_sn_bridge_attach,
.detach = ti_sn_bridge_detach,
.mode_valid = ti_sn_bridge_mode_valid,
+ .get_edid = ti_sn_bridge_get_edid,
+ .detect = ti_sn_bridge_detect,
.atomic_pre_enable = ti_sn_bridge_atomic_pre_enable,
.atomic_enable = ti_sn_bridge_atomic_enable,
.atomic_disable = ti_sn_bridge_atomic_disable,
@@ -1218,6 +1279,11 @@ static int ti_sn_bridge_probe(struct auxiliary_device *adev,
pdata->bridge.funcs = &ti_sn_bridge_funcs;
pdata->bridge.of_node = np;
+ pdata->bridge.type = pdata->next_bridge->type == DRM_MODE_CONNECTOR_DisplayPort
+ ? DRM_MODE_CONNECTOR_DisplayPort : DRM_MODE_CONNECTOR_eDP;
+
+ if (pdata->bridge.type == DRM_MODE_CONNECTOR_DisplayPort)
+ pdata->bridge.ops = DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_DETECT;
drm_bridge_add(&pdata->bridge);
diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c
index 32b295003f49..92990a3d577a 100644
--- a/drivers/gpu/drm/display/drm_dp_helper.c
+++ b/drivers/gpu/drm/display/drm_dp_helper.c
@@ -390,6 +390,38 @@ void drm_dp_link_train_channel_eq_delay(const struct drm_dp_aux *aux,
}
EXPORT_SYMBOL(drm_dp_link_train_channel_eq_delay);
+/**
+ * drm_dp_phy_name() - Get the name of the given DP PHY
+ * @dp_phy: The DP PHY identifier
+ *
+ * Given the @dp_phy, get a user friendly name of the DP PHY, either "DPRX" or
+ * "LTTPR <N>", or "<INVALID DP PHY>" on errors. The returned string is always
+ * non-NULL and valid.
+ *
+ * Returns: Name of the DP PHY.
+ */
+const char *drm_dp_phy_name(enum drm_dp_phy dp_phy)
+{
+ static const char * const phy_names[] = {
+ [DP_PHY_DPRX] = "DPRX",
+ [DP_PHY_LTTPR1] = "LTTPR 1",
+ [DP_PHY_LTTPR2] = "LTTPR 2",
+ [DP_PHY_LTTPR3] = "LTTPR 3",
+ [DP_PHY_LTTPR4] = "LTTPR 4",
+ [DP_PHY_LTTPR5] = "LTTPR 5",
+ [DP_PHY_LTTPR6] = "LTTPR 6",
+ [DP_PHY_LTTPR7] = "LTTPR 7",
+ [DP_PHY_LTTPR8] = "LTTPR 8",
+ };
+
+ if (dp_phy < 0 || dp_phy >= ARRAY_SIZE(phy_names) ||
+ WARN_ON(!phy_names[dp_phy]))
+ return "<INVALID DP PHY>";
+
+ return phy_names[dp_phy];
+}
+EXPORT_SYMBOL(drm_dp_phy_name);
+
void drm_dp_lttpr_link_train_clock_recovery_delay(void)
{
usleep_range(100, 200);
diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c
index 7a94a5288e8d..4442cc5602d4 100644
--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
@@ -68,8 +68,7 @@ static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);
static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
- int id,
- struct drm_dp_payload *payload);
+ int id, u8 start_slot, u8 num_slots);
static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port,
@@ -1235,57 +1234,6 @@ build_query_stream_enc_status(struct drm_dp_sideband_msg_tx *msg, u8 stream_id,
return 0;
}
-static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_vcpi *vcpi)
-{
- int ret, vcpi_ret;
-
- mutex_lock(&mgr->payload_lock);
- ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
- if (ret > mgr->max_payloads) {
- ret = -EINVAL;
- drm_dbg_kms(mgr->dev, "out of payload ids %d\n", ret);
- goto out_unlock;
- }
-
- vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1);
- if (vcpi_ret > mgr->max_payloads) {
- ret = -EINVAL;
- drm_dbg_kms(mgr->dev, "out of vcpi ids %d\n", ret);
- goto out_unlock;
- }
-
- set_bit(ret, &mgr->payload_mask);
- set_bit(vcpi_ret, &mgr->vcpi_mask);
- vcpi->vcpi = vcpi_ret + 1;
- mgr->proposed_vcpis[ret - 1] = vcpi;
-out_unlock:
- mutex_unlock(&mgr->payload_lock);
- return ret;
-}
-
-static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
- int vcpi)
-{
- int i;
-
- if (vcpi == 0)
- return;
-
- mutex_lock(&mgr->payload_lock);
- drm_dbg_kms(mgr->dev, "putting payload %d\n", vcpi);
- clear_bit(vcpi - 1, &mgr->vcpi_mask);
-
- for (i = 0; i < mgr->max_payloads; i++) {
- if (mgr->proposed_vcpis[i] &&
- mgr->proposed_vcpis[i]->vcpi == vcpi) {
- mgr->proposed_vcpis[i] = NULL;
- clear_bit(i + 1, &mgr->payload_mask);
- }
- }
- mutex_unlock(&mgr->payload_lock);
-}
-
static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_sideband_msg_tx *txmsg)
{
@@ -1738,6 +1686,20 @@ drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {}
#define save_port_topology_ref(port, type)
#endif
+struct drm_dp_mst_atomic_payload *
+drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state,
+ struct drm_dp_mst_port *port)
+{
+ struct drm_dp_mst_atomic_payload *payload;
+
+ list_for_each_entry(payload, &state->payloads, next)
+ if (payload->port == port)
+ return payload;
+
+ return NULL;
+}
+EXPORT_SYMBOL(drm_atomic_get_mst_payload_state);
+
static void drm_dp_destroy_mst_branch_device(struct kref *kref)
{
struct drm_dp_mst_branch *mstb =
@@ -2496,7 +2458,7 @@ fail_put:
return ret;
}
-static void
+static int
drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
struct drm_dp_connection_status_notify *conn_stat)
{
@@ -2509,7 +2471,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
port = drm_dp_get_port(mstb, conn_stat->port_number);
if (!port)
- return;
+ return 0;
if (port->connector) {
if (!port->input && conn_stat->input_port) {
@@ -2562,8 +2524,7 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,
out:
drm_dp_mst_topology_put_port(port);
- if (dowork)
- queue_work(system_long_wq, &mstb->mgr->work);
+ return dowork;
}
static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
@@ -3240,6 +3201,8 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port,
struct drm_dp_query_stream_enc_status_ack_reply *status)
{
+ struct drm_dp_mst_topology_state *state;
+ struct drm_dp_mst_atomic_payload *payload;
struct drm_dp_sideband_msg_tx *txmsg;
u8 nonce[7];
int ret;
@@ -3256,6 +3219,10 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr,
get_random_bytes(nonce, sizeof(nonce));
+ drm_modeset_lock(&mgr->base.lock, NULL);
+ state = to_drm_dp_mst_topology_state(mgr->base.state);
+ payload = drm_atomic_get_mst_payload_state(state, port);
+
/*
* "Source device targets the QUERY_STREAM_ENCRYPTION_STATUS message
* transaction at the MST Branch device directly connected to the
@@ -3263,7 +3230,7 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr,
*/
txmsg->dst = mgr->mst_primary;
- build_query_stream_enc_status(txmsg, port->vcpi.vcpi, nonce);
+ build_query_stream_enc_status(txmsg, payload->vcpi, nonce);
drm_dp_queue_down_tx(mgr, txmsg);
@@ -3280,6 +3247,7 @@ int drm_dp_send_query_stream_enc_status(struct drm_dp_mst_topology_mgr *mgr,
memcpy(status, &txmsg->reply.u.enc_status, sizeof(*status));
out:
+ drm_modeset_unlock(&mgr->base.lock);
drm_dp_mst_topology_put_port(port);
out_get_port:
kfree(txmsg);
@@ -3288,238 +3256,162 @@ out_get_port:
EXPORT_SYMBOL(drm_dp_send_query_stream_enc_status);
static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
- int id,
- struct drm_dp_payload *payload)
+ struct drm_dp_mst_atomic_payload *payload)
{
- int ret;
-
- ret = drm_dp_dpcd_write_payload(mgr, id, payload);
- if (ret < 0) {
- payload->payload_state = 0;
- return ret;
- }
- payload->payload_state = DP_PAYLOAD_LOCAL;
- return 0;
+ return drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot,
+ payload->time_slots);
}
static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- int id,
- struct drm_dp_payload *payload)
+ struct drm_dp_mst_atomic_payload *payload)
{
int ret;
+ struct drm_dp_mst_port *port = drm_dp_mst_topology_get_port_validated(mgr, payload->port);
- ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
- if (ret < 0)
- return ret;
- payload->payload_state = DP_PAYLOAD_REMOTE;
+ if (!port)
+ return -EIO;
+
+ ret = drm_dp_payload_send_msg(mgr, port, payload->vcpi, payload->pbn);
+ drm_dp_mst_topology_put_port(port);
return ret;
}
static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- int id,
- struct drm_dp_payload *payload)
+ struct drm_dp_mst_topology_state *mst_state,
+ struct drm_dp_mst_atomic_payload *payload)
{
drm_dbg_kms(mgr->dev, "\n");
- /* it's okay for these to fail */
- if (port) {
- drm_dp_payload_send_msg(mgr, port, id, 0);
- }
- drm_dp_dpcd_write_payload(mgr, id, payload);
- payload->payload_state = DP_PAYLOAD_DELETE_LOCAL;
- return 0;
-}
+ /* it's okay for these to fail */
+ drm_dp_payload_send_msg(mgr, payload->port, payload->vcpi, 0);
+ drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot, 0);
-static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
- int id,
- struct drm_dp_payload *payload)
-{
- payload->payload_state = 0;
return 0;
}
/**
- * drm_dp_update_payload_part1() - Execute payload update part 1
- * @mgr: manager to use.
- * @start_slot: this is the cur slot
- *
- * NOTE: start_slot is a temporary workaround for non-atomic drivers,
- * this will be removed when non-atomic mst helpers are moved out of the helper
+ * drm_dp_add_payload_part1() - Execute payload update part 1
+ * @mgr: Manager to use.
+ * @mst_state: The MST atomic state
+ * @payload: The payload to write
*
- * This iterates over all proposed virtual channels, and tries to
- * allocate space in the link for them. For 0->slots transitions,
- * this step just writes the VCPI to the MST device. For slots->0
- * transitions, this writes the updated VCPIs and removes the
- * remote VC payloads.
+ * Determines the starting time slot for the given payload, and programs the VCPI for this payload
+ * into hardware. After calling this, the driver should generate ACT and payload packets.
*
- * after calling this the driver should generate ACT and payload
- * packets.
+ * Returns: 0 on success, error code on failure. In the event that this fails,
+ * @payload.vc_start_slot will also be set to -1.
*/
-int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr, int start_slot)
+int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_topology_state *mst_state,
+ struct drm_dp_mst_atomic_payload *payload)
{
- struct drm_dp_payload req_payload;
struct drm_dp_mst_port *port;
- int i, j;
- int cur_slots = start_slot;
- bool skip;
+ int ret;
- mutex_lock(&mgr->payload_lock);
- for (i = 0; i < mgr->max_payloads; i++) {
- struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i];
- struct drm_dp_payload *payload = &mgr->payloads[i];
- bool put_port = false;
+ port = drm_dp_mst_topology_get_port_validated(mgr, payload->port);
+ if (!port)
+ return 0;
- /* solve the current payloads - compare to the hw ones
- - update the hw view */
- req_payload.start_slot = cur_slots;
- if (vcpi) {
- port = container_of(vcpi, struct drm_dp_mst_port,
- vcpi);
+ if (mgr->payload_count == 0)
+ mgr->next_start_slot = mst_state->start_slot;
- mutex_lock(&mgr->lock);
- skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
- mutex_unlock(&mgr->lock);
+ payload->vc_start_slot = mgr->next_start_slot;
- if (skip) {
- drm_dbg_kms(mgr->dev,
- "Virtual channel %d is not in current topology\n",
- i);
- continue;
- }
- /* Validated ports don't matter if we're releasing
- * VCPI
- */
- if (vcpi->num_slots) {
- port = drm_dp_mst_topology_get_port_validated(
- mgr, port);
- if (!port) {
- if (vcpi->num_slots == payload->num_slots) {
- cur_slots += vcpi->num_slots;
- payload->start_slot = req_payload.start_slot;
- continue;
- } else {
- drm_dbg_kms(mgr->dev,
- "Fail:set payload to invalid sink");
- mutex_unlock(&mgr->payload_lock);
- return -EINVAL;
- }
- }
- put_port = true;
- }
+ ret = drm_dp_create_payload_step1(mgr, payload);
+ drm_dp_mst_topology_put_port(port);
+ if (ret < 0) {
+ drm_warn(mgr->dev, "Failed to create MST payload for port %p: %d\n",
+ payload->port, ret);
+ payload->vc_start_slot = -1;
+ return ret;
+ }
- req_payload.num_slots = vcpi->num_slots;
- req_payload.vcpi = vcpi->vcpi;
- } else {
- port = NULL;
- req_payload.num_slots = 0;
- }
+ mgr->payload_count++;
+ mgr->next_start_slot += payload->time_slots;
- payload->start_slot = req_payload.start_slot;
- /* work out what is required to happen with this payload */
- if (payload->num_slots != req_payload.num_slots) {
-
- /* need to push an update for this payload */
- if (req_payload.num_slots) {
- drm_dp_create_payload_step1(mgr, vcpi->vcpi,
- &req_payload);
- payload->num_slots = req_payload.num_slots;
- payload->vcpi = req_payload.vcpi;
-
- } else if (payload->num_slots) {
- payload->num_slots = 0;
- drm_dp_destroy_payload_step1(mgr, port,
- payload->vcpi,
- payload);
- req_payload.payload_state =
- payload->payload_state;
- payload->start_slot = 0;
- }
- payload->payload_state = req_payload.payload_state;
- }
- cur_slots += req_payload.num_slots;
+ return 0;
+}
+EXPORT_SYMBOL(drm_dp_add_payload_part1);
- if (put_port)
- drm_dp_mst_topology_put_port(port);
- }
+/**
+ * drm_dp_remove_payload() - Remove an MST payload
+ * @mgr: Manager to use.
+ * @mst_state: The MST atomic state
+ * @payload: The payload to write
+ *
+ * Removes a payload from an MST topology if it was successfully assigned a start slot. Also updates
+ * the starting time slots of all other payloads which would have been shifted towards the start of
+ * the VC table as a result. After calling this, the driver should generate ACT and payload packets.
+ */
+void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_topology_state *mst_state,
+ struct drm_dp_mst_atomic_payload *payload)
+{
+ struct drm_dp_mst_atomic_payload *pos;
+ bool send_remove = false;
- for (i = 0; i < mgr->max_payloads; /* do nothing */) {
- if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) {
- i++;
- continue;
- }
+ /* We failed to make the payload, so nothing to do */
+ if (payload->vc_start_slot == -1)
+ return;
- drm_dbg_kms(mgr->dev, "removing payload %d\n", i);
- for (j = i; j < mgr->max_payloads - 1; j++) {
- mgr->payloads[j] = mgr->payloads[j + 1];
- mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1];
+ mutex_lock(&mgr->lock);
+ send_remove = drm_dp_mst_port_downstream_of_branch(payload->port, mgr->mst_primary);
+ mutex_unlock(&mgr->lock);
- if (mgr->proposed_vcpis[j] &&
- mgr->proposed_vcpis[j]->num_slots) {
- set_bit(j + 1, &mgr->payload_mask);
- } else {
- clear_bit(j + 1, &mgr->payload_mask);
- }
- }
+ if (send_remove)
+ drm_dp_destroy_payload_step1(mgr, mst_state, payload);
+ else
+ drm_dbg_kms(mgr->dev, "Payload for VCPI %d not in topology, not sending remove\n",
+ payload->vcpi);
- memset(&mgr->payloads[mgr->max_payloads - 1], 0,
- sizeof(struct drm_dp_payload));
- mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL;
- clear_bit(mgr->max_payloads, &mgr->payload_mask);
+ list_for_each_entry(pos, &mst_state->payloads, next) {
+ if (pos != payload && pos->vc_start_slot > payload->vc_start_slot)
+ pos->vc_start_slot -= payload->time_slots;
}
- mutex_unlock(&mgr->payload_lock);
+ payload->vc_start_slot = -1;
- return 0;
+ mgr->payload_count--;
+ mgr->next_start_slot -= payload->time_slots;
}
-EXPORT_SYMBOL(drm_dp_update_payload_part1);
+EXPORT_SYMBOL(drm_dp_remove_payload);
/**
- * drm_dp_update_payload_part2() - Execute payload update part 2
- * @mgr: manager to use.
+ * drm_dp_add_payload_part2() - Execute payload update part 2
+ * @mgr: Manager to use.
+ * @state: The global atomic state
+ * @payload: The payload to update
+ *
+ * If @payload was successfully assigned a starting time slot by drm_dp_add_payload_part1(), this
+ * function will send the sideband messages to finish allocating this payload.
*
- * This iterates over all proposed virtual channels, and tries to
- * allocate space in the link for them. For 0->slots transitions,
- * this step writes the remote VC payload commands. For slots->0
- * this just resets some internal state.
+ * Returns: 0 on success, negative error code on failure.
*/
-int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
+int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_atomic_state *state,
+ struct drm_dp_mst_atomic_payload *payload)
{
- struct drm_dp_mst_port *port;
- int i;
int ret = 0;
- bool skip;
- mutex_lock(&mgr->payload_lock);
- for (i = 0; i < mgr->max_payloads; i++) {
-
- if (!mgr->proposed_vcpis[i])
- continue;
-
- port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
-
- mutex_lock(&mgr->lock);
- skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
- mutex_unlock(&mgr->lock);
-
- if (skip)
- continue;
+ /* Skip failed payloads */
+ if (payload->vc_start_slot == -1) {
+ drm_dbg_kms(state->dev, "Part 1 of payload creation for %s failed, skipping part 2\n",
+ payload->port->connector->name);
+ return -EIO;
+ }
- drm_dbg_kms(mgr->dev, "payload %d %d\n", i, mgr->payloads[i].payload_state);
- if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
- ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
- } else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
- ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
- }
- if (ret) {
- mutex_unlock(&mgr->payload_lock);
- return ret;
- }
+ ret = drm_dp_create_payload_step2(mgr, payload);
+ if (ret < 0) {
+ if (!payload->delete)
+ drm_err(mgr->dev, "Step 2 of creating MST payload for %p failed: %d\n",
+ payload->port, ret);
+ else
+ drm_dbg_kms(mgr->dev, "Step 2 of removing MST payload for %p failed: %d\n",
+ payload->port, ret);
}
- mutex_unlock(&mgr->payload_lock);
- return 0;
+
+ return ret;
}
-EXPORT_SYMBOL(drm_dp_update_payload_part2);
+EXPORT_SYMBOL(drm_dp_add_payload_part2);
static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port,
@@ -3699,7 +3591,6 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
int ret = 0;
struct drm_dp_mst_branch *mstb = NULL;
- mutex_lock(&mgr->payload_lock);
mutex_lock(&mgr->lock);
if (mst_state == mgr->mst_state)
goto out_unlock;
@@ -3707,10 +3598,6 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
mgr->mst_state = mst_state;
/* set the device into MST mode */
if (mst_state) {
- struct drm_dp_payload reset_pay;
- int lane_count;
- int link_rate;
-
WARN_ON(mgr->mst_primary);
/* get dpcd info */
@@ -3721,16 +3608,6 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
goto out_unlock;
}
- lane_count = min_t(int, mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, mgr->max_lane_count);
- link_rate = min_t(int, drm_dp_bw_code_to_link_rate(mgr->dpcd[1]), mgr->max_link_rate);
- mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr,
- link_rate,
- lane_count);
- if (mgr->pbn_div == 0) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
/* add initial branch device at LCT 1 */
mstb = drm_dp_add_mst_branch_device(1, NULL);
if (mstb == NULL) {
@@ -3750,9 +3627,8 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
if (ret < 0)
goto out_unlock;
- reset_pay.start_slot = 0;
- reset_pay.num_slots = 0x3f;
- drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
+ /* Write reset payload */
+ drm_dp_dpcd_write_payload(mgr, 0, 0, 0x3f);
queue_work(system_long_wq, &mgr->work);
@@ -3764,19 +3640,11 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
/* this can fail if the device is gone */
drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
ret = 0;
- memset(mgr->payloads, 0,
- mgr->max_payloads * sizeof(mgr->payloads[0]));
- memset(mgr->proposed_vcpis, 0,
- mgr->max_payloads * sizeof(mgr->proposed_vcpis[0]));
- mgr->payload_mask = 0;
- set_bit(0, &mgr->payload_mask);
- mgr->vcpi_mask = 0;
mgr->payload_id_table_cleared = false;
}
out_unlock:
mutex_unlock(&mgr->lock);
- mutex_unlock(&mgr->payload_lock);
if (mstb)
drm_dp_mst_topology_put_mstb(mstb);
return ret;
@@ -4047,7 +3915,7 @@ drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_branch *mstb = NULL;
struct drm_dp_sideband_msg_req_body *msg = &up_req->msg;
struct drm_dp_sideband_msg_hdr *hdr = &up_req->hdr;
- bool hotplug = false;
+ bool hotplug = false, dowork = false;
if (hdr->broadcast) {
const u8 *guid = NULL;
@@ -4070,11 +3938,14 @@ drm_dp_mst_process_up_req(struct drm_dp_mst_topology_mgr *mgr,
/* TODO: Add missing handler for DP_RESOURCE_STATUS_NOTIFY events */
if (msg->req_type == DP_CONNECTION_STATUS_NOTIFY) {
- drm_dp_mst_handle_conn_stat(mstb, &msg->u.conn_stat);
+ dowork = drm_dp_mst_handle_conn_stat(mstb, &msg->u.conn_stat);
hotplug = true;
}
drm_dp_mst_topology_put_mstb(mstb);
+
+ if (dowork)
+ queue_work(system_long_wq, &mgr->work);
return hotplug;
}
@@ -4293,341 +4164,352 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
EXPORT_SYMBOL(drm_dp_mst_get_edid);
/**
- * drm_dp_find_vcpi_slots() - Find VCPI slots for this PBN value
- * @mgr: manager to use
- * @pbn: payload bandwidth to convert into slots.
- *
- * Calculate the number of VCPI slots that will be required for the given PBN
- * value. This function is deprecated, and should not be used in atomic
- * drivers.
- *
- * RETURNS:
- * The total slots required for this port, or error.
- */
-int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
- int pbn)
-{
- int num_slots;
-
- num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
-
- /* max. time slots - one slot for MTP header */
- if (num_slots > 63)
- return -ENOSPC;
- return num_slots;
-}
-EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
-
-static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_vcpi *vcpi, int pbn, int slots)
-{
- int ret;
-
- vcpi->pbn = pbn;
- vcpi->aligned_pbn = slots * mgr->pbn_div;
- vcpi->num_slots = slots;
-
- ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
- if (ret < 0)
- return ret;
- return 0;
-}
-
-/**
- * drm_dp_atomic_find_vcpi_slots() - Find and add VCPI slots to the state
+ * drm_dp_atomic_find_time_slots() - Find and add time slots to the state
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @port: port to find vcpi slots for
+ * @port: port to find time slots for
* @pbn: bandwidth required for the mode in PBN
- * @pbn_div: divider for DSC mode that takes FEC into account
*
- * Allocates VCPI slots to @port, replacing any previous VCPI allocations it
- * may have had. Any atomic drivers which support MST must call this function
- * in their &drm_encoder_helper_funcs.atomic_check() callback to change the
- * current VCPI allocation for the new state, but only when
- * &drm_crtc_state.mode_changed or &drm_crtc_state.connectors_changed is set
- * to ensure compatibility with userspace applications that still use the
- * legacy modesetting UAPI.
+ * Allocates time slots to @port, replacing any previous time slot allocations it may
+ * have had. Any atomic drivers which support MST must call this function in
+ * their &drm_encoder_helper_funcs.atomic_check() callback unconditionally to
+ * change the current time slot allocation for the new state, and ensure the MST
+ * atomic state is added whenever the state of payloads in the topology changes.
*
* Allocations set by this function are not checked against the bandwidth
* restraints of @mgr until the driver calls drm_dp_mst_atomic_check().
*
* Additionally, it is OK to call this function multiple times on the same
* @port as needed. It is not OK however, to call this function and
- * drm_dp_atomic_release_vcpi_slots() in the same atomic check phase.
+ * drm_dp_atomic_release_time_slots() in the same atomic check phase.
*
* See also:
- * drm_dp_atomic_release_vcpi_slots()
+ * drm_dp_atomic_release_time_slots()
* drm_dp_mst_atomic_check()
*
* Returns:
* Total slots in the atomic state assigned for this port, or a negative error
* code if the port no longer exists
*/
-int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
+int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state,
struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port, int pbn,
- int pbn_div)
+ struct drm_dp_mst_port *port, int pbn)
{
struct drm_dp_mst_topology_state *topology_state;
- struct drm_dp_vcpi_allocation *pos, *vcpi = NULL;
- int prev_slots, prev_bw, req_slots;
+ struct drm_dp_mst_atomic_payload *payload = NULL;
+ struct drm_connector_state *conn_state;
+ int prev_slots = 0, prev_bw = 0, req_slots;
topology_state = drm_atomic_get_mst_topology_state(state, mgr);
if (IS_ERR(topology_state))
return PTR_ERR(topology_state);
- /* Find the current allocation for this port, if any */
- list_for_each_entry(pos, &topology_state->vcpis, next) {
- if (pos->port == port) {
- vcpi = pos;
- prev_slots = vcpi->vcpi;
- prev_bw = vcpi->pbn;
+ conn_state = drm_atomic_get_new_connector_state(state, port->connector);
+ topology_state->pending_crtc_mask |= drm_crtc_mask(conn_state->crtc);
- /*
- * This should never happen, unless the driver tries
- * releasing and allocating the same VCPI allocation,
- * which is an error
- */
- if (WARN_ON(!prev_slots)) {
- drm_err(mgr->dev,
- "cannot allocate and release VCPI on [MST PORT:%p] in the same state\n",
- port);
- return -EINVAL;
- }
+ /* Find the current allocation for this port, if any */
+ payload = drm_atomic_get_mst_payload_state(topology_state, port);
+ if (payload) {
+ prev_slots = payload->time_slots;
+ prev_bw = payload->pbn;
- break;
+ /*
+ * This should never happen, unless the driver tries
+ * releasing and allocating the same timeslot allocation,
+ * which is an error
+ */
+ if (drm_WARN_ON(mgr->dev, payload->delete)) {
+ drm_err(mgr->dev,
+ "cannot allocate and release time slots on [MST PORT:%p] in the same state\n",
+ port);
+ return -EINVAL;
}
}
- if (!vcpi) {
- prev_slots = 0;
- prev_bw = 0;
- }
-
- if (pbn_div <= 0)
- pbn_div = mgr->pbn_div;
- req_slots = DIV_ROUND_UP(pbn, pbn_div);
+ req_slots = DIV_ROUND_UP(pbn, topology_state->pbn_div);
- drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] VCPI %d -> %d\n",
+ drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] TU %d -> %d\n",
port->connector->base.id, port->connector->name,
port, prev_slots, req_slots);
drm_dbg_atomic(mgr->dev, "[CONNECTOR:%d:%s] [MST PORT:%p] PBN %d -> %d\n",
port->connector->base.id, port->connector->name,
port, prev_bw, pbn);
- /* Add the new allocation to the state */
- if (!vcpi) {
- vcpi = kzalloc(sizeof(*vcpi), GFP_KERNEL);
- if (!vcpi)
+ /* Add the new allocation to the state, note the VCPI isn't assigned until the end */
+ if (!payload) {
+ payload = kzalloc(sizeof(*payload), GFP_KERNEL);
+ if (!payload)
return -ENOMEM;
drm_dp_mst_get_port_malloc(port);
- vcpi->port = port;
- list_add(&vcpi->next, &topology_state->vcpis);
+ payload->port = port;
+ payload->vc_start_slot = -1;
+ list_add(&payload->next, &topology_state->payloads);
}
- vcpi->vcpi = req_slots;
- vcpi->pbn = pbn;
+ payload->time_slots = req_slots;
+ payload->pbn = pbn;
return req_slots;
}
-EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
+EXPORT_SYMBOL(drm_dp_atomic_find_time_slots);
/**
- * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
+ * drm_dp_atomic_release_time_slots() - Release allocated time slots
* @state: global atomic state
* @mgr: MST topology manager for the port
- * @port: The port to release the VCPI slots from
+ * @port: The port to release the time slots from
*
- * Releases any VCPI slots that have been allocated to a port in the atomic
- * state. Any atomic drivers which support MST must call this function in
- * their &drm_connector_helper_funcs.atomic_check() callback when the
- * connector will no longer have VCPI allocated (e.g. because its CRTC was
- * removed) when it had VCPI allocated in the previous atomic state.
+ * Releases any time slots that have been allocated to a port in the atomic
+ * state. Any atomic drivers which support MST must call this function
+ * unconditionally in their &drm_connector_helper_funcs.atomic_check() callback.
+ * This helper will check whether time slots would be released by the new state and
+ * respond accordingly, along with ensuring the MST state is always added to the
+ * atomic state whenever a new state would modify the state of payloads on the
+ * topology.
*
* It is OK to call this even if @port has been removed from the system.
* Additionally, it is OK to call this function multiple times on the same
* @port as needed. It is not OK however, to call this function and
- * drm_dp_atomic_find_vcpi_slots() on the same @port in a single atomic check
+ * drm_dp_atomic_find_time_slots() on the same @port in a single atomic check
* phase.
*
* See also:
- * drm_dp_atomic_find_vcpi_slots()
+ * drm_dp_atomic_find_time_slots()
* drm_dp_mst_atomic_check()
*
* Returns:
- * 0 if all slots for this port were added back to
- * &drm_dp_mst_topology_state.avail_slots or negative error code
+ * 0 on success, negative error code otherwise
*/
-int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
+int drm_dp_atomic_release_time_slots(struct drm_atomic_state *state,
struct drm_dp_mst_topology_mgr *mgr,
struct drm_dp_mst_port *port)
{
struct drm_dp_mst_topology_state *topology_state;
- struct drm_dp_vcpi_allocation *pos;
- bool found = false;
+ struct drm_dp_mst_atomic_payload *payload;
+ struct drm_connector_state *old_conn_state, *new_conn_state;
+ bool update_payload = true;
+
+ old_conn_state = drm_atomic_get_old_connector_state(state, port->connector);
+ if (!old_conn_state->crtc)
+ return 0;
+
+ /* If the CRTC isn't disabled by this state, don't release it's payload */
+ new_conn_state = drm_atomic_get_new_connector_state(state, port->connector);
+ if (new_conn_state->crtc) {
+ struct drm_crtc_state *crtc_state =
+ drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+
+ /* No modeset means no payload changes, so it's safe to not pull in the MST state */
+ if (!crtc_state || !drm_atomic_crtc_needs_modeset(crtc_state))
+ return 0;
+
+ if (!crtc_state->mode_changed && !crtc_state->connectors_changed)
+ update_payload = false;
+ }
topology_state = drm_atomic_get_mst_topology_state(state, mgr);
if (IS_ERR(topology_state))
return PTR_ERR(topology_state);
- list_for_each_entry(pos, &topology_state->vcpis, next) {
- if (pos->port == port) {
- found = true;
- break;
- }
- }
- if (WARN_ON(!found)) {
- drm_err(mgr->dev, "no VCPI for [MST PORT:%p] found in mst state %p\n",
+ topology_state->pending_crtc_mask |= drm_crtc_mask(old_conn_state->crtc);
+ if (!update_payload)
+ return 0;
+
+ payload = drm_atomic_get_mst_payload_state(topology_state, port);
+ if (WARN_ON(!payload)) {
+ drm_err(mgr->dev, "No payload for [MST PORT:%p] found in mst state %p\n",
port, &topology_state->base);
return -EINVAL;
}
- drm_dbg_atomic(mgr->dev, "[MST PORT:%p] VCPI %d -> 0\n", port, pos->vcpi);
- if (pos->vcpi) {
+ if (new_conn_state->crtc)
+ return 0;
+
+ drm_dbg_atomic(mgr->dev, "[MST PORT:%p] TU %d -> 0\n", port, payload->time_slots);
+ if (!payload->delete) {
drm_dp_mst_put_port_malloc(port);
- pos->vcpi = 0;
- pos->pbn = 0;
+ payload->pbn = 0;
+ payload->delete = true;
+ topology_state->payload_mask &= ~BIT(payload->vcpi - 1);
}
return 0;
}
-EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
+EXPORT_SYMBOL(drm_dp_atomic_release_time_slots);
/**
- * drm_dp_mst_update_slots() - updates the slot info depending on the DP ecoding format
- * @mst_state: mst_state to update
- * @link_encoding_cap: the ecoding format on the link
- */
-void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap)
-{
- if (link_encoding_cap == DP_CAP_ANSI_128B132B) {
- mst_state->total_avail_slots = 64;
- mst_state->start_slot = 0;
- } else {
- mst_state->total_avail_slots = 63;
- mst_state->start_slot = 1;
- }
-
- DRM_DEBUG_KMS("%s encoding format on mst_state 0x%p\n",
- (link_encoding_cap == DP_CAP_ANSI_128B132B) ? "128b/132b":"8b/10b",
- mst_state);
-}
-EXPORT_SYMBOL(drm_dp_mst_update_slots);
-
-/**
- * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
- * @mgr: manager for this port
- * @port: port to allocate a virtual channel for.
- * @pbn: payload bandwidth number to request
- * @slots: returned number of slots for this PBN.
+ * drm_dp_mst_atomic_setup_commit() - setup_commit hook for MST helpers
+ * @state: global atomic state
+ *
+ * This function saves all of the &drm_crtc_commit structs in an atomic state that touch any CRTCs
+ * currently assigned to an MST topology. Drivers must call this hook from their
+ * &drm_mode_config_helper_funcs.atomic_commit_setup hook.
+ *
+ * Returns:
+ * 0 if all CRTC commits were retrieved successfully, negative error code otherwise
*/
-bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port, int pbn, int slots)
+int drm_dp_mst_atomic_setup_commit(struct drm_atomic_state *state)
{
- int ret;
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_dp_mst_topology_state *mst_state;
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ int i, j, commit_idx, num_commit_deps;
- if (slots < 0)
- return false;
+ for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+ if (!mst_state->pending_crtc_mask)
+ continue;
- port = drm_dp_mst_topology_get_port_validated(mgr, port);
- if (!port)
- return false;
+ num_commit_deps = hweight32(mst_state->pending_crtc_mask);
+ mst_state->commit_deps = kmalloc_array(num_commit_deps,
+ sizeof(*mst_state->commit_deps), GFP_KERNEL);
+ if (!mst_state->commit_deps)
+ return -ENOMEM;
+ mst_state->num_commit_deps = num_commit_deps;
- if (port->vcpi.vcpi > 0) {
- drm_dbg_kms(mgr->dev,
- "payload: vcpi %d already allocated for pbn %d - requested pbn %d\n",
- port->vcpi.vcpi, port->vcpi.pbn, pbn);
- if (pbn == port->vcpi.pbn) {
- drm_dp_mst_topology_put_port(port);
- return true;
+ commit_idx = 0;
+ for_each_new_crtc_in_state(state, crtc, crtc_state, j) {
+ if (mst_state->pending_crtc_mask & drm_crtc_mask(crtc)) {
+ mst_state->commit_deps[commit_idx++] =
+ drm_crtc_commit_get(crtc_state->commit);
+ }
}
}
- ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn, slots);
- if (ret) {
- drm_dbg_kms(mgr->dev, "failed to init vcpi slots=%d ret=%d\n",
- DIV_ROUND_UP(pbn, mgr->pbn_div), ret);
- drm_dp_mst_topology_put_port(port);
- goto out;
- }
- drm_dbg_kms(mgr->dev, "initing vcpi for pbn=%d slots=%d\n", pbn, port->vcpi.num_slots);
-
- /* Keep port allocated until its payload has been removed */
- drm_dp_mst_get_port_malloc(port);
- drm_dp_mst_topology_put_port(port);
- return true;
-out:
- return false;
+ return 0;
}
-EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
+EXPORT_SYMBOL(drm_dp_mst_atomic_setup_commit);
-int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+/**
+ * drm_dp_mst_atomic_wait_for_dependencies() - Wait for all pending commits on MST topologies,
+ * prepare new MST state for commit
+ * @state: global atomic state
+ *
+ * Goes through any MST topologies in this atomic state, and waits for any pending commits which
+ * touched CRTCs that were/are on an MST topology to be programmed to hardware and flipped to before
+ * returning. This is to prevent multiple non-blocking commits affecting an MST topology from racing
+ * with eachother by forcing them to be executed sequentially in situations where the only resources
+ * the modeset objects in these commits share are an MST topology.
+ *
+ * This function also prepares the new MST state for commit by performing some state preparation
+ * which can't be done until this point, such as reading back the final VC start slots (which are
+ * determined at commit-time) from the previous state.
+ *
+ * All MST drivers must call this function after calling drm_atomic_helper_wait_for_dependencies(),
+ * or whatever their equivalent of that is.
+ */
+void drm_dp_mst_atomic_wait_for_dependencies(struct drm_atomic_state *state)
{
- int slots = 0;
+ struct drm_dp_mst_topology_state *old_mst_state, *new_mst_state;
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_dp_mst_atomic_payload *old_payload, *new_payload;
+ int i, j, ret;
+
+ for_each_oldnew_mst_mgr_in_state(state, mgr, old_mst_state, new_mst_state, i) {
+ for (j = 0; j < old_mst_state->num_commit_deps; j++) {
+ ret = drm_crtc_commit_wait(old_mst_state->commit_deps[j]);
+ if (ret < 0)
+ drm_err(state->dev, "Failed to wait for %s: %d\n",
+ old_mst_state->commit_deps[j]->crtc->name, ret);
+ }
- port = drm_dp_mst_topology_get_port_validated(mgr, port);
- if (!port)
- return slots;
+ /* Now that previous state is committed, it's safe to copy over the start slot
+ * assignments
+ */
+ list_for_each_entry(old_payload, &old_mst_state->payloads, next) {
+ if (old_payload->delete)
+ continue;
- slots = port->vcpi.num_slots;
- drm_dp_mst_topology_put_port(port);
- return slots;
+ new_payload = drm_atomic_get_mst_payload_state(new_mst_state,
+ old_payload->port);
+ new_payload->vc_start_slot = old_payload->vc_start_slot;
+ }
+ }
}
-EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots);
+EXPORT_SYMBOL(drm_dp_mst_atomic_wait_for_dependencies);
/**
- * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
- * @mgr: manager for this port
- * @port: unverified pointer to a port.
+ * drm_dp_mst_root_conn_atomic_check() - Serialize CRTC commits on MST-capable connectors operating
+ * in SST mode
+ * @new_conn_state: The new connector state of the &drm_connector
+ * @mgr: The MST topology manager for the &drm_connector
+ *
+ * Since MST uses fake &drm_encoder structs, the generic atomic modesetting code isn't able to
+ * serialize non-blocking commits happening on the real DP connector of an MST topology switching
+ * into/away from MST mode - as the CRTC on the real DP connector and the CRTCs on the connector's
+ * MST topology will never share the same &drm_encoder.
*
- * This just resets the number of slots for the ports VCPI for later programming.
+ * This function takes care of this serialization issue, by checking a root MST connector's atomic
+ * state to determine if it is about to have a modeset - and then pulling in the MST topology state
+ * if so, along with adding any relevant CRTCs to &drm_dp_mst_topology_state.pending_crtc_mask.
+ *
+ * Drivers implementing MST must call this function from the
+ * &drm_connector_helper_funcs.atomic_check hook of any physical DP &drm_connector capable of
+ * driving MST sinks.
+ *
+ * Returns:
+ * 0 on success, negative error code otherwise
*/
-void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
+int drm_dp_mst_root_conn_atomic_check(struct drm_connector_state *new_conn_state,
+ struct drm_dp_mst_topology_mgr *mgr)
{
- /*
- * A port with VCPI will remain allocated until its VCPI is
- * released, no verified ref needed
- */
+ struct drm_atomic_state *state = new_conn_state->state;
+ struct drm_connector_state *old_conn_state =
+ drm_atomic_get_old_connector_state(state, new_conn_state->connector);
+ struct drm_crtc_state *crtc_state;
+ struct drm_dp_mst_topology_state *mst_state = NULL;
+
+ if (new_conn_state->crtc) {
+ crtc_state = drm_atomic_get_new_crtc_state(state, new_conn_state->crtc);
+ if (crtc_state && drm_atomic_crtc_needs_modeset(crtc_state)) {
+ mst_state = drm_atomic_get_mst_topology_state(state, mgr);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+
+ mst_state->pending_crtc_mask |= drm_crtc_mask(new_conn_state->crtc);
+ }
+ }
- port->vcpi.num_slots = 0;
+ if (old_conn_state->crtc) {
+ crtc_state = drm_atomic_get_new_crtc_state(state, old_conn_state->crtc);
+ if (crtc_state && drm_atomic_crtc_needs_modeset(crtc_state)) {
+ if (!mst_state) {
+ mst_state = drm_atomic_get_mst_topology_state(state, mgr);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+ }
+
+ mst_state->pending_crtc_mask |= drm_crtc_mask(old_conn_state->crtc);
+ }
+ }
+
+ return 0;
}
-EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
+EXPORT_SYMBOL(drm_dp_mst_root_conn_atomic_check);
/**
- * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
- * @mgr: manager for this port
- * @port: port to deallocate vcpi for
- *
- * This can be called unconditionally, regardless of whether
- * drm_dp_mst_allocate_vcpi() succeeded or not.
+ * drm_dp_mst_update_slots() - updates the slot info depending on the DP ecoding format
+ * @mst_state: mst_state to update
+ * @link_encoding_cap: the ecoding format on the link
*/
-void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port)
+void drm_dp_mst_update_slots(struct drm_dp_mst_topology_state *mst_state, uint8_t link_encoding_cap)
{
- bool skip;
-
- if (!port->vcpi.vcpi)
- return;
-
- mutex_lock(&mgr->lock);
- skip = !drm_dp_mst_port_downstream_of_branch(port, mgr->mst_primary);
- mutex_unlock(&mgr->lock);
-
- if (skip)
- return;
+ if (link_encoding_cap == DP_CAP_ANSI_128B132B) {
+ mst_state->total_avail_slots = 64;
+ mst_state->start_slot = 0;
+ } else {
+ mst_state->total_avail_slots = 63;
+ mst_state->start_slot = 1;
+ }
- drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
- port->vcpi.num_slots = 0;
- port->vcpi.pbn = 0;
- port->vcpi.aligned_pbn = 0;
- port->vcpi.vcpi = 0;
- drm_dp_mst_put_port_malloc(port);
+ DRM_DEBUG_KMS("%s encoding format on mst_state 0x%p\n",
+ (link_encoding_cap == DP_CAP_ANSI_128B132B) ? "128b/132b":"8b/10b",
+ mst_state);
}
-EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
+EXPORT_SYMBOL(drm_dp_mst_update_slots);
static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
- int id, struct drm_dp_payload *payload)
+ int id, u8 start_slot, u8 num_slots)
{
u8 payload_alloc[3], status;
int ret;
@@ -4637,8 +4519,8 @@ static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
DP_PAYLOAD_TABLE_UPDATED);
payload_alloc[0] = id;
- payload_alloc[1] = payload->start_slot;
- payload_alloc[2] = payload->num_slots;
+ payload_alloc[1] = start_slot;
+ payload_alloc[2] = num_slots;
ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
if (ret != 3) {
@@ -4853,8 +4735,9 @@ static void fetch_monitor_name(struct drm_dp_mst_topology_mgr *mgr,
void drm_dp_mst_dump_topology(struct seq_file *m,
struct drm_dp_mst_topology_mgr *mgr)
{
- int i;
- struct drm_dp_mst_port *port;
+ struct drm_dp_mst_topology_state *state;
+ struct drm_dp_mst_atomic_payload *payload;
+ int i, ret;
mutex_lock(&mgr->lock);
if (mgr->mst_primary)
@@ -4863,36 +4746,35 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
/* dump VCPIs */
mutex_unlock(&mgr->lock);
- mutex_lock(&mgr->payload_lock);
- seq_printf(m, "\n*** VCPI Info ***\n");
- seq_printf(m, "payload_mask: %lx, vcpi_mask: %lx, max_payloads: %d\n", mgr->payload_mask, mgr->vcpi_mask, mgr->max_payloads);
+ ret = drm_modeset_lock_single_interruptible(&mgr->base.lock);
+ if (ret < 0)
+ return;
+
+ state = to_drm_dp_mst_topology_state(mgr->base.state);
+ seq_printf(m, "\n*** Atomic state info ***\n");
+ seq_printf(m, "payload_mask: %x, max_payloads: %d, start_slot: %u, pbn_div: %d\n",
+ state->payload_mask, mgr->max_payloads, state->start_slot, state->pbn_div);
- seq_printf(m, "\n| idx | port # | vcp_id | # slots | sink name |\n");
+ seq_printf(m, "\n| idx | port | vcpi | slots | pbn | dsc | sink name |\n");
for (i = 0; i < mgr->max_payloads; i++) {
- if (mgr->proposed_vcpis[i]) {
+ list_for_each_entry(payload, &state->payloads, next) {
char name[14];
- port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
- fetch_monitor_name(mgr, port, name, sizeof(name));
- seq_printf(m, "%10d%10d%10d%10d%20s\n",
+ if (payload->vcpi != i || payload->delete)
+ continue;
+
+ fetch_monitor_name(mgr, payload->port, name, sizeof(name));
+ seq_printf(m, " %5d %6d %6d %02d - %02d %5d %5s %19s\n",
i,
- port->port_num,
- port->vcpi.vcpi,
- port->vcpi.num_slots,
+ payload->port->port_num,
+ payload->vcpi,
+ payload->vc_start_slot,
+ payload->vc_start_slot + payload->time_slots - 1,
+ payload->pbn,
+ payload->dsc_enabled ? "Y" : "N",
(*name != 0) ? name : "Unknown");
- } else
- seq_printf(m, "%6d - Unused\n", i);
- }
- seq_printf(m, "\n*** Payload Info ***\n");
- seq_printf(m, "| idx | state | start slot | # slots |\n");
- for (i = 0; i < mgr->max_payloads; i++) {
- seq_printf(m, "%10d%10d%15d%10d\n",
- i,
- mgr->payloads[i].payload_state,
- mgr->payloads[i].start_slot,
- mgr->payloads[i].num_slots);
+ }
}
- mutex_unlock(&mgr->payload_lock);
seq_printf(m, "\n*** DPCD Info ***\n");
mutex_lock(&mgr->lock);
@@ -4938,7 +4820,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
out:
mutex_unlock(&mgr->lock);
-
+ drm_modeset_unlock(&mgr->base.lock);
}
EXPORT_SYMBOL(drm_dp_mst_dump_topology);
@@ -5060,7 +4942,7 @@ drm_dp_mst_duplicate_state(struct drm_private_obj *obj)
{
struct drm_dp_mst_topology_state *state, *old_state =
to_dp_mst_topology_state(obj->state);
- struct drm_dp_vcpi_allocation *pos, *vcpi;
+ struct drm_dp_mst_atomic_payload *pos, *payload;
state = kmemdup(old_state, sizeof(*state), GFP_KERNEL);
if (!state)
@@ -5068,25 +4950,28 @@ drm_dp_mst_duplicate_state(struct drm_private_obj *obj)
__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
- INIT_LIST_HEAD(&state->vcpis);
+ INIT_LIST_HEAD(&state->payloads);
+ state->commit_deps = NULL;
+ state->num_commit_deps = 0;
+ state->pending_crtc_mask = 0;
- list_for_each_entry(pos, &old_state->vcpis, next) {
- /* Prune leftover freed VCPI allocations */
- if (!pos->vcpi)
+ list_for_each_entry(pos, &old_state->payloads, next) {
+ /* Prune leftover freed timeslot allocations */
+ if (pos->delete)
continue;
- vcpi = kmemdup(pos, sizeof(*vcpi), GFP_KERNEL);
- if (!vcpi)
+ payload = kmemdup(pos, sizeof(*payload), GFP_KERNEL);
+ if (!payload)
goto fail;
- drm_dp_mst_get_port_malloc(vcpi->port);
- list_add(&vcpi->next, &state->vcpis);
+ drm_dp_mst_get_port_malloc(payload->port);
+ list_add(&payload->next, &state->payloads);
}
return &state->base;
fail:
- list_for_each_entry_safe(pos, vcpi, &state->vcpis, next) {
+ list_for_each_entry_safe(pos, payload, &state->payloads, next) {
drm_dp_mst_put_port_malloc(pos->port);
kfree(pos);
}
@@ -5100,15 +4985,20 @@ static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
{
struct drm_dp_mst_topology_state *mst_state =
to_dp_mst_topology_state(state);
- struct drm_dp_vcpi_allocation *pos, *tmp;
+ struct drm_dp_mst_atomic_payload *pos, *tmp;
+ int i;
- list_for_each_entry_safe(pos, tmp, &mst_state->vcpis, next) {
- /* We only keep references to ports with non-zero VCPIs */
- if (pos->vcpi)
+ list_for_each_entry_safe(pos, tmp, &mst_state->payloads, next) {
+ /* We only keep references to ports with active payloads */
+ if (!pos->delete)
drm_dp_mst_put_port_malloc(pos->port);
kfree(pos);
}
+ for (i = 0; i < mst_state->num_commit_deps; i++)
+ drm_crtc_commit_put(mst_state->commit_deps[i]);
+
+ kfree(mst_state->commit_deps);
kfree(mst_state);
}
@@ -5135,7 +5025,7 @@ static int
drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb,
struct drm_dp_mst_topology_state *state)
{
- struct drm_dp_vcpi_allocation *vcpi;
+ struct drm_dp_mst_atomic_payload *payload;
struct drm_dp_mst_port *port;
int pbn_used = 0, ret;
bool found = false;
@@ -5143,9 +5033,9 @@ drm_dp_mst_atomic_check_mstb_bw_limit(struct drm_dp_mst_branch *mstb,
/* Check that we have at least one port in our state that's downstream
* of this branch, otherwise we can skip this branch
*/
- list_for_each_entry(vcpi, &state->vcpis, next) {
- if (!vcpi->pbn ||
- !drm_dp_mst_port_downstream_of_branch(vcpi->port, mstb))
+ list_for_each_entry(payload, &state->payloads, next) {
+ if (!payload->pbn ||
+ !drm_dp_mst_port_downstream_of_branch(payload->port, mstb))
continue;
found = true;
@@ -5176,25 +5066,15 @@ static int
drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
struct drm_dp_mst_topology_state *state)
{
- struct drm_dp_vcpi_allocation *vcpi;
+ struct drm_dp_mst_atomic_payload *payload;
int pbn_used = 0;
if (port->pdt == DP_PEER_DEVICE_NONE)
return 0;
if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
- bool found = false;
-
- list_for_each_entry(vcpi, &state->vcpis, next) {
- if (vcpi->port != port)
- continue;
- if (!vcpi->pbn)
- return 0;
-
- found = true;
- break;
- }
- if (!found)
+ payload = drm_atomic_get_mst_payload_state(state, port);
+ if (!payload)
return 0;
/*
@@ -5208,7 +5088,7 @@ drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
return -EINVAL;
}
- pbn_used = vcpi->pbn;
+ pbn_used = payload->pbn;
} else {
pbn_used = drm_dp_mst_atomic_check_mstb_bw_limit(port->mstb,
state);
@@ -5230,28 +5110,28 @@ drm_dp_mst_atomic_check_port_bw_limit(struct drm_dp_mst_port *port,
}
static inline int
-drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_topology_state *mst_state)
+drm_dp_mst_atomic_check_payload_alloc_limits(struct drm_dp_mst_topology_mgr *mgr,
+ struct drm_dp_mst_topology_state *mst_state)
{
- struct drm_dp_vcpi_allocation *vcpi;
+ struct drm_dp_mst_atomic_payload *payload;
int avail_slots = mst_state->total_avail_slots, payload_count = 0;
- list_for_each_entry(vcpi, &mst_state->vcpis, next) {
- /* Releasing VCPI is always OK-even if the port is gone */
- if (!vcpi->vcpi) {
- drm_dbg_atomic(mgr->dev, "[MST PORT:%p] releases all VCPI slots\n",
- vcpi->port);
+ list_for_each_entry(payload, &mst_state->payloads, next) {
+ /* Releasing payloads is always OK-even if the port is gone */
+ if (payload->delete) {
+ drm_dbg_atomic(mgr->dev, "[MST PORT:%p] releases all time slots\n",
+ payload->port);
continue;
}
- drm_dbg_atomic(mgr->dev, "[MST PORT:%p] requires %d vcpi slots\n",
- vcpi->port, vcpi->vcpi);
+ drm_dbg_atomic(mgr->dev, "[MST PORT:%p] requires %d time slots\n",
+ payload->port, payload->time_slots);
- avail_slots -= vcpi->vcpi;
+ avail_slots -= payload->time_slots;
if (avail_slots < 0) {
drm_dbg_atomic(mgr->dev,
- "[MST PORT:%p] not enough VCPI slots in mst state %p (avail=%d)\n",
- vcpi->port, mst_state, avail_slots + vcpi->vcpi);
+ "[MST PORT:%p] not enough time slots in mst state %p (avail=%d)\n",
+ payload->port, mst_state, avail_slots + payload->time_slots);
return -ENOSPC;
}
@@ -5261,9 +5141,22 @@ drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr,
mgr, mst_state, mgr->max_payloads);
return -EINVAL;
}
+
+ /* Assign a VCPI */
+ if (!payload->vcpi) {
+ payload->vcpi = ffz(mst_state->payload_mask) + 1;
+ drm_dbg_atomic(mgr->dev, "[MST PORT:%p] assigned VCPI #%d\n",
+ payload->port, payload->vcpi);
+ mst_state->payload_mask |= BIT(payload->vcpi - 1);
+ }
}
- drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p VCPI avail=%d used=%d\n",
- mgr, mst_state, avail_slots, mst_state->total_avail_slots - avail_slots);
+
+ if (!payload_count)
+ mst_state->pbn_div = 0;
+
+ drm_dbg_atomic(mgr->dev, "[MST MGR:%p] mst state %p TU pbn_div=%d avail=%d used=%d\n",
+ mgr, mst_state, mst_state->pbn_div, avail_slots,
+ mst_state->total_avail_slots - avail_slots);
return 0;
}
@@ -5284,7 +5177,7 @@ drm_dp_mst_atomic_check_vcpi_alloc_limit(struct drm_dp_mst_topology_mgr *mgr,
int drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, struct drm_dp_mst_topology_mgr *mgr)
{
struct drm_dp_mst_topology_state *mst_state;
- struct drm_dp_vcpi_allocation *pos;
+ struct drm_dp_mst_atomic_payload *pos;
struct drm_connector *connector;
struct drm_connector_state *conn_state;
struct drm_crtc *crtc;
@@ -5295,7 +5188,7 @@ int drm_dp_mst_add_affected_dsc_crtcs(struct drm_atomic_state *state, struct drm
if (IS_ERR(mst_state))
return -EINVAL;
- list_for_each_entry(pos, &mst_state->vcpis, next) {
+ list_for_each_entry(pos, &mst_state->payloads, next) {
connector = pos->port->connector;
@@ -5334,7 +5227,6 @@ EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs);
* @state: Pointer to the new drm_atomic_state
* @port: Pointer to the affected MST Port
* @pbn: Newly recalculated bw required for link with DSC enabled
- * @pbn_div: Divider to calculate correct number of pbn per slot
* @enable: Boolean flag to enable or disable DSC on the port
*
* This function enables DSC on the given Port
@@ -5345,54 +5237,46 @@ EXPORT_SYMBOL(drm_dp_mst_add_affected_dsc_crtcs);
*/
int drm_dp_mst_atomic_enable_dsc(struct drm_atomic_state *state,
struct drm_dp_mst_port *port,
- int pbn, int pbn_div,
- bool enable)
+ int pbn, bool enable)
{
struct drm_dp_mst_topology_state *mst_state;
- struct drm_dp_vcpi_allocation *pos;
- bool found = false;
- int vcpi = 0;
+ struct drm_dp_mst_atomic_payload *payload;
+ int time_slots = 0;
mst_state = drm_atomic_get_mst_topology_state(state, port->mgr);
-
if (IS_ERR(mst_state))
return PTR_ERR(mst_state);
- list_for_each_entry(pos, &mst_state->vcpis, next) {
- if (pos->port == port) {
- found = true;
- break;
- }
- }
-
- if (!found) {
+ payload = drm_atomic_get_mst_payload_state(mst_state, port);
+ if (!payload) {
drm_dbg_atomic(state->dev,
- "[MST PORT:%p] Couldn't find VCPI allocation in mst state %p\n",
+ "[MST PORT:%p] Couldn't find payload in mst state %p\n",
port, mst_state);
return -EINVAL;
}
- if (pos->dsc_enabled == enable) {
+ if (payload->dsc_enabled == enable) {
drm_dbg_atomic(state->dev,
- "[MST PORT:%p] DSC flag is already set to %d, returning %d VCPI slots\n",
- port, enable, pos->vcpi);
- vcpi = pos->vcpi;
+ "[MST PORT:%p] DSC flag is already set to %d, returning %d time slots\n",
+ port, enable, payload->time_slots);
+ time_slots = payload->time_slots;
}
if (enable) {
- vcpi = drm_dp_atomic_find_vcpi_slots(state, port->mgr, port, pbn, pbn_div);
+ time_slots = drm_dp_atomic_find_time_slots(state, port->mgr, port, pbn);
drm_dbg_atomic(state->dev,
- "[MST PORT:%p] Enabling DSC flag, reallocating %d VCPI slots on the port\n",
- port, vcpi);
- if (vcpi < 0)
+ "[MST PORT:%p] Enabling DSC flag, reallocating %d time slots on the port\n",
+ port, time_slots);
+ if (time_slots < 0)
return -EINVAL;
}
- pos->dsc_enabled = enable;
+ payload->dsc_enabled = enable;
- return vcpi;
+ return time_slots;
}
EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc);
+
/**
* drm_dp_mst_atomic_check - Check that the new state of an MST topology in an
* atomic update is valid
@@ -5400,15 +5284,15 @@ EXPORT_SYMBOL(drm_dp_mst_atomic_enable_dsc);
*
* Checks the given topology state for an atomic update to ensure that it's
* valid. This includes checking whether there's enough bandwidth to support
- * the new VCPI allocations in the atomic update.
+ * the new timeslot allocations in the atomic update.
*
* Any atomic drivers supporting DP MST must make sure to call this after
* checking the rest of their state in their
* &drm_mode_config_funcs.atomic_check() callback.
*
* See also:
- * drm_dp_atomic_find_vcpi_slots()
- * drm_dp_atomic_release_vcpi_slots()
+ * drm_dp_atomic_find_time_slots()
+ * drm_dp_atomic_release_time_slots()
*
* Returns:
*
@@ -5424,7 +5308,7 @@ int drm_dp_mst_atomic_check(struct drm_atomic_state *state)
if (!mgr->mst_state)
continue;
- ret = drm_dp_mst_atomic_check_vcpi_alloc_limit(mgr, mst_state);
+ ret = drm_dp_mst_atomic_check_payload_alloc_limits(mgr, mst_state);
if (ret)
break;
@@ -5450,7 +5334,6 @@ EXPORT_SYMBOL(drm_dp_mst_topology_state_funcs);
/**
* drm_atomic_get_mst_topology_state: get MST topology state
- *
* @state: global atomic state
* @mgr: MST topology manager, also the private object in this case
*
@@ -5470,14 +5353,37 @@ struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_a
EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
/**
+ * drm_atomic_get_new_mst_topology_state: get new MST topology state in atomic state, if any
+ * @state: global atomic state
+ * @mgr: MST topology manager, also the private object in this case
+ *
+ * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
+ * state vtable so that the private object state returned is that of a MST
+ * topology object.
+ *
+ * Returns:
+ *
+ * The MST topology state, or NULL if there's no topology state for this MST mgr
+ * in the global atomic state
+ */
+struct drm_dp_mst_topology_state *
+drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_mgr *mgr)
+{
+ struct drm_private_state *priv_state =
+ drm_atomic_get_new_private_obj_state(state, &mgr->base);
+
+ return priv_state ? to_dp_mst_topology_state(priv_state) : NULL;
+}
+EXPORT_SYMBOL(drm_atomic_get_new_mst_topology_state);
+
+/**
* drm_dp_mst_topology_mgr_init - initialise a topology manager
* @mgr: manager struct to initialise
* @dev: device providing this structure - for i2c addition.
* @aux: DP helper aux channel to talk to this device
* @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
* @max_payloads: maximum number of payloads this GPU can source
- * @max_lane_count: maximum number of lanes this GPU supports
- * @max_link_rate: maximum link rate per lane this GPU supports in kHz
* @conn_base_id: the connector object ID the MST device is connected to.
*
* Return 0 for success, or negative error code on failure
@@ -5485,14 +5391,12 @@ EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
struct drm_device *dev, struct drm_dp_aux *aux,
int max_dpcd_transaction_bytes, int max_payloads,
- int max_lane_count, int max_link_rate,
int conn_base_id)
{
struct drm_dp_mst_topology_state *mst_state;
mutex_init(&mgr->lock);
mutex_init(&mgr->qlock);
- mutex_init(&mgr->payload_lock);
mutex_init(&mgr->delayed_destroy_lock);
mutex_init(&mgr->up_req_lock);
mutex_init(&mgr->probe_lock);
@@ -5522,19 +5426,7 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
mgr->aux = aux;
mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
mgr->max_payloads = max_payloads;
- mgr->max_lane_count = max_lane_count;
- mgr->max_link_rate = max_link_rate;
mgr->conn_base_id = conn_base_id;
- if (max_payloads + 1 > sizeof(mgr->payload_mask) * 8 ||
- max_payloads + 1 > sizeof(mgr->vcpi_mask) * 8)
- return -EINVAL;
- mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
- if (!mgr->payloads)
- return -ENOMEM;
- mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
- if (!mgr->proposed_vcpis)
- return -ENOMEM;
- set_bit(0, &mgr->payload_mask);
mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
if (mst_state == NULL)
@@ -5544,7 +5436,7 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
mst_state->start_slot = 1;
mst_state->mgr = mgr;
- INIT_LIST_HEAD(&mst_state->vcpis);
+ INIT_LIST_HEAD(&mst_state->payloads);
drm_atomic_private_obj_init(dev, &mgr->base,
&mst_state->base,
@@ -5567,19 +5459,12 @@ void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
destroy_workqueue(mgr->delayed_destroy_wq);
mgr->delayed_destroy_wq = NULL;
}
- mutex_lock(&mgr->payload_lock);
- kfree(mgr->payloads);
- mgr->payloads = NULL;
- kfree(mgr->proposed_vcpis);
- mgr->proposed_vcpis = NULL;
- mutex_unlock(&mgr->payload_lock);
mgr->dev = NULL;
mgr->aux = NULL;
drm_atomic_private_obj_fini(&mgr->base);
mgr->funcs = NULL;
mutex_destroy(&mgr->delayed_destroy_lock);
- mutex_destroy(&mgr->payload_lock);
mutex_destroy(&mgr->qlock);
mutex_destroy(&mgr->lock);
mutex_destroy(&mgr->up_req_lock);
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index c6abfd3d4b62..ee5fea48b5cb 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -702,8 +702,12 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
if (funcs->atomic_check)
ret = funcs->atomic_check(connector, state);
- if (ret)
+ if (ret) {
+ drm_dbg_atomic(dev,
+ "[CONNECTOR:%d:%s] driver check failed\n",
+ connector->base.id, connector->name);
return ret;
+ }
connectors_mask |= BIT(i);
}
@@ -745,8 +749,12 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
if (funcs->atomic_check)
ret = funcs->atomic_check(connector, state);
- if (ret)
+ if (ret) {
+ drm_dbg_atomic(dev,
+ "[CONNECTOR:%d:%s] driver check failed\n",
+ connector->base.id, connector->name);
return ret;
+ }
}
/*
@@ -778,6 +786,45 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
EXPORT_SYMBOL(drm_atomic_helper_check_modeset);
/**
+ * drm_atomic_helper_check_wb_connector_state() - Check writeback encoder state
+ * @encoder: encoder state to check
+ * @conn_state: connector state to check
+ *
+ * Checks if the writeback connector state is valid, and returns an error if it
+ * isn't.
+ *
+ * RETURNS:
+ * Zero for success or -errno
+ */
+int
+drm_atomic_helper_check_wb_encoder_state(struct drm_encoder *encoder,
+ struct drm_connector_state *conn_state)
+{
+ struct drm_writeback_job *wb_job = conn_state->writeback_job;
+ struct drm_property_blob *pixel_format_blob;
+ struct drm_framebuffer *fb;
+ size_t i, nformats;
+ u32 *formats;
+
+ if (!wb_job || !wb_job->fb)
+ return 0;
+
+ pixel_format_blob = wb_job->connector->pixel_formats_blob_ptr;
+ nformats = pixel_format_blob->length / sizeof(u32);
+ formats = pixel_format_blob->data;
+ fb = wb_job->fb;
+
+ for (i = 0; i < nformats; i++)
+ if (fb->format->format == formats[i])
+ return 0;
+
+ drm_dbg_kms(encoder->dev, "Invalid pixel format %p4cc\n", &fb->format->format);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(drm_atomic_helper_check_wb_encoder_state);
+
+/**
* drm_atomic_helper_check_plane_state() - Check plane state for validity
* @plane_state: plane state to check
* @crtc_state: CRTC state to check
@@ -1788,7 +1835,7 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
struct drm_plane_state *old_plane_state = NULL;
struct drm_plane_state *new_plane_state = NULL;
const struct drm_plane_helper_funcs *funcs;
- int i, n_planes = 0;
+ int i, ret, n_planes = 0;
for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
if (drm_atomic_crtc_needs_modeset(crtc_state))
@@ -1799,19 +1846,34 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
n_planes++;
/* FIXME: we support only single plane updates for now */
- if (n_planes != 1)
+ if (n_planes != 1) {
+ drm_dbg_atomic(dev,
+ "only single plane async updates are supported\n");
return -EINVAL;
+ }
if (!new_plane_state->crtc ||
- old_plane_state->crtc != new_plane_state->crtc)
+ old_plane_state->crtc != new_plane_state->crtc) {
+ drm_dbg_atomic(dev,
+ "[PLANE:%d:%s] async update cannot change CRTC\n",
+ plane->base.id, plane->name);
return -EINVAL;
+ }
funcs = plane->helper_private;
- if (!funcs->atomic_async_update)
+ if (!funcs->atomic_async_update) {
+ drm_dbg_atomic(dev,
+ "[PLANE:%d:%s] driver does not support async updates\n",
+ plane->base.id, plane->name);
return -EINVAL;
+ }
- if (new_plane_state->fence)
+ if (new_plane_state->fence) {
+ drm_dbg_atomic(dev,
+ "[PLANE:%d:%s] missing fence for async update\n",
+ plane->base.id, plane->name);
return -EINVAL;
+ }
/*
* Don't do an async update if there is an outstanding commit modifying
@@ -1826,7 +1888,12 @@ int drm_atomic_helper_async_check(struct drm_device *dev,
return -EBUSY;
}
- return funcs->atomic_async_check(plane, state);
+ ret = funcs->atomic_async_check(plane, state);
+ if (ret != 0)
+ drm_dbg_atomic(dev,
+ "[PLANE:%d:%s] driver async check failed\n",
+ plane->base.id, plane->name);
+ return ret;
}
EXPORT_SYMBOL(drm_atomic_helper_async_check);
diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c
index 688c8afe0bf1..939d621c9ad4 100644
--- a/drivers/gpu/drm/drm_mode_config.c
+++ b/drivers/gpu/drm/drm_mode_config.c
@@ -151,6 +151,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
count = 0;
connector_id = u64_to_user_ptr(card_res->connector_id_ptr);
drm_for_each_connector_iter(connector, &conn_iter) {
+ if (connector->registration_state != DRM_CONNECTOR_REGISTERED)
+ continue;
+
/* only expose writeback connectors if userspace understands them */
if (!file_priv->writeback_connectors &&
(connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK))
diff --git a/drivers/gpu/drm/gma500/cdv_intel_dp.c b/drivers/gpu/drm/gma500/cdv_intel_dp.c
index bb2e9d64018a..53b967282d6a 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_dp.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_dp.c
@@ -115,7 +115,7 @@ i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
/*
* Write a single byte to the current I2C address, the
- * the I2C link must be running or this returns -EIO
+ * I2C link must be running or this returns -EIO
*/
static int
i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
diff --git a/drivers/gpu/drm/gma500/oaktrail_crtc.c b/drivers/gpu/drm/gma500/oaktrail_crtc.c
index 6004390d647a..64761f46b434 100644
--- a/drivers/gpu/drm/gma500/oaktrail_crtc.c
+++ b/drivers/gpu/drm/gma500/oaktrail_crtc.c
@@ -310,7 +310,7 @@ static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
temp & ~PIPEACONF_ENABLE, i);
REG_READ_WITH_AUX(map->conf, i);
}
- /* Wait for for the pipe disable to take effect. */
+ /* Wait for the pipe disable to take effect. */
gma_wait_for_vblank(dev);
temp = REG_READ_WITH_AUX(map->dpll, i);
diff --git a/drivers/gpu/drm/gma500/psb_intel_sdvo.c b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
index a85aace25548..bdced46dd333 100644
--- a/drivers/gpu/drm/gma500/psb_intel_sdvo.c
+++ b/drivers/gpu/drm/gma500/psb_intel_sdvo.c
@@ -400,26 +400,38 @@ static const struct _sdvo_cmd_name {
#define IS_SDVOB(reg) (reg == SDVOB)
#define SDVO_NAME(svdo) (IS_SDVOB((svdo)->sdvo_reg) ? "SDVOB" : "SDVOC")
-static void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo, u8 cmd,
- const void *args, int args_len)
+static void psb_intel_sdvo_debug_write(struct psb_intel_sdvo *psb_intel_sdvo,
+ u8 cmd, const void *args, int args_len)
{
- int i;
+ struct drm_device *dev = psb_intel_sdvo->base.base.dev;
+ int i, pos = 0;
+ char buffer[73];
+
+#define BUF_PRINT(args...) \
+ pos += snprintf(buffer + pos, max_t(int, sizeof(buffer) - pos, 0), args)
+
+ for (i = 0; i < args_len; i++) {
+ BUF_PRINT("%02X ", ((u8 *)args)[i]);
+ }
+
+ for (; i < 8; i++) {
+ BUF_PRINT(" ");
+ }
- DRM_DEBUG_KMS("%s: W: %02X ",
- SDVO_NAME(psb_intel_sdvo), cmd);
- for (i = 0; i < args_len; i++)
- DRM_DEBUG_KMS("%02X ", ((u8 *)args)[i]);
- for (; i < 8; i++)
- DRM_DEBUG_KMS(" ");
for (i = 0; i < ARRAY_SIZE(sdvo_cmd_names); i++) {
if (cmd == sdvo_cmd_names[i].cmd) {
- DRM_DEBUG_KMS("(%s)", sdvo_cmd_names[i].name);
+ BUF_PRINT("(%s)", sdvo_cmd_names[i].name);
break;
}
}
+
if (i == ARRAY_SIZE(sdvo_cmd_names))
- DRM_DEBUG_KMS("(%02X)", cmd);
- DRM_DEBUG_KMS("\n");
+ BUF_PRINT("(%02X)", cmd);
+
+ drm_WARN_ON(dev, pos >= sizeof(buffer) - 1);
+#undef BUF_PRINT
+
+ DRM_DEBUG_KMS("%s: W: %02X %s\n", SDVO_NAME(psb_intel_sdvo), cmd, buffer);
}
static const char *cmd_status_names[] = {
@@ -490,13 +502,13 @@ static bool psb_intel_sdvo_write_cmd(struct psb_intel_sdvo *psb_intel_sdvo, u8 c
}
static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
- void *response, int response_len)
+ void *response, int response_len)
{
+ struct drm_device *dev = psb_intel_sdvo->base.base.dev;
+ char buffer[73];
+ int i, pos = 0;
u8 retry = 5;
u8 status;
- int i;
-
- DRM_DEBUG_KMS("%s: R: ", SDVO_NAME(psb_intel_sdvo));
/*
* The documentation states that all commands will be
@@ -520,10 +532,13 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
goto log_fail;
}
+#define BUF_PRINT(args...) \
+ pos += snprintf(buffer + pos, max_t(int, sizeof(buffer) - pos, 0), args)
+
if (status <= SDVO_CMD_STATUS_SCALING_NOT_SUPP)
- DRM_DEBUG_KMS("(%s)", cmd_status_names[status]);
+ BUF_PRINT("(%s)", cmd_status_names[status]);
else
- DRM_DEBUG_KMS("(??? %d)", status);
+ BUF_PRINT("(??? %d)", status);
if (status != SDVO_CMD_STATUS_SUCCESS)
goto log_fail;
@@ -534,13 +549,18 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
SDVO_I2C_RETURN_0 + i,
&((u8 *)response)[i]))
goto log_fail;
- DRM_DEBUG_KMS(" %02X", ((u8 *)response)[i]);
+ BUF_PRINT(" %02X", ((u8 *)response)[i]);
}
- DRM_DEBUG_KMS("\n");
+
+ drm_WARN_ON(dev, pos >= sizeof(buffer) - 1);
+#undef BUF_PRINT
+
+ DRM_DEBUG_KMS("%s: R: %s\n", SDVO_NAME(psb_intel_sdvo), buffer);
return true;
log_fail:
- DRM_DEBUG_KMS("... failed\n");
+ DRM_DEBUG_KMS("%s: R: ... failed %s\n",
+ SDVO_NAME(psb_intel_sdvo), buffer);
return false;
}
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index bf80b57ca63a..b8dc62203cd1 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -7531,6 +7531,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state)
intel_atomic_commit_fence_wait(state);
drm_atomic_helper_wait_for_dependencies(&state->base);
+ drm_dp_mst_atomic_wait_for_dependencies(&state->base);
if (state->modeset)
wakeref = intel_display_power_get(dev_priv, POWER_DOMAIN_MODESET);
@@ -8599,6 +8600,10 @@ out:
return ret;
}
+static const struct drm_mode_config_helper_funcs intel_mode_config_funcs = {
+ .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
+};
+
static void intel_mode_config_init(struct drm_i915_private *i915)
{
struct drm_mode_config *mode_config = &i915->drm.mode_config;
@@ -8613,6 +8618,7 @@ static void intel_mode_config_init(struct drm_i915_private *i915)
mode_config->prefer_shadow = 1;
mode_config->funcs = &intel_mode_funcs;
+ mode_config->helper_private = &intel_mode_config_funcs;
mode_config->async_page_flip = HAS_ASYNC_FLIPS(i915);
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 32292c0be2bd..a4e113253df3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -4992,12 +4992,21 @@ static int intel_dp_connector_atomic_check(struct drm_connector *conn,
{
struct drm_i915_private *dev_priv = to_i915(conn->dev);
struct intel_atomic_state *state = to_intel_atomic_state(_state);
+ struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(_state, conn);
+ struct intel_connector *intel_conn = to_intel_connector(conn);
+ struct intel_dp *intel_dp = enc_to_intel_dp(intel_conn->encoder);
int ret;
ret = intel_digital_connector_atomic_check(conn, &state->base);
if (ret)
return ret;
+ if (intel_dp_mst_source_support(intel_dp)) {
+ ret = drm_dp_mst_root_conn_atomic_check(conn_state, &intel_dp->mst_mgr);
+ if (ret)
+ return ret;
+ }
+
/*
* We don't enable port sync on BDW due to missing w/as and
* due to not having adjusted the modeset sequence appropriately.
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index 14d2a64193b2..7713c19042f3 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -52,6 +52,7 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
struct drm_atomic_state *state = crtc_state->uapi.state;
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
struct intel_dp *intel_dp = &intel_mst->primary->dp;
+ struct drm_dp_mst_topology_state *mst_state;
struct intel_connector *connector =
to_intel_connector(conn_state->connector);
struct drm_i915_private *i915 = to_i915(connector->base.dev);
@@ -60,22 +61,28 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
bool constant_n = drm_dp_has_quirk(&intel_dp->desc, DP_DPCD_QUIRK_CONSTANT_N);
int bpp, slots = -EINVAL;
+ mst_state = drm_atomic_get_mst_topology_state(state, &intel_dp->mst_mgr);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+
crtc_state->lane_count = limits->max_lane_count;
crtc_state->port_clock = limits->max_rate;
+ // TODO: Handle pbn_div changes by adding a new MST helper
+ if (!mst_state->pbn_div) {
+ mst_state->pbn_div = drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr,
+ limits->max_rate,
+ limits->max_lane_count);
+ }
+
for (bpp = limits->max_bpp; bpp >= limits->min_bpp; bpp -= 2 * 3) {
crtc_state->pipe_bpp = bpp;
crtc_state->pbn = drm_dp_calc_pbn_mode(adjusted_mode->crtc_clock,
crtc_state->pipe_bpp,
false);
-
- slots = drm_dp_atomic_find_vcpi_slots(state, &intel_dp->mst_mgr,
- connector->port,
- crtc_state->pbn,
- drm_dp_get_vc_payload_bw(&intel_dp->mst_mgr,
- crtc_state->port_clock,
- crtc_state->lane_count));
+ slots = drm_dp_atomic_find_time_slots(state, &intel_dp->mst_mgr,
+ connector->port, crtc_state->pbn);
if (slots == -EDEADLK)
return slots;
if (slots >= 0)
@@ -308,14 +315,8 @@ intel_dp_mst_atomic_check(struct drm_connector *connector,
struct drm_atomic_state *_state)
{
struct intel_atomic_state *state = to_intel_atomic_state(_state);
- struct drm_connector_state *new_conn_state =
- drm_atomic_get_new_connector_state(&state->base, connector);
- struct drm_connector_state *old_conn_state =
- drm_atomic_get_old_connector_state(&state->base, connector);
struct intel_connector *intel_connector =
to_intel_connector(connector);
- struct drm_crtc *new_crtc = new_conn_state->crtc;
- struct drm_dp_mst_topology_mgr *mgr;
int ret;
ret = intel_digital_connector_atomic_check(connector, &state->base);
@@ -326,28 +327,9 @@ intel_dp_mst_atomic_check(struct drm_connector *connector,
if (ret)
return ret;
- if (!old_conn_state->crtc)
- return 0;
-
- /* We only want to free VCPI if this state disables the CRTC on this
- * connector
- */
- if (new_crtc) {
- struct intel_crtc *crtc = to_intel_crtc(new_crtc);
- struct intel_crtc_state *crtc_state =
- intel_atomic_get_new_crtc_state(state, crtc);
-
- if (!crtc_state ||
- !drm_atomic_crtc_needs_modeset(&crtc_state->uapi) ||
- crtc_state->uapi.enable)
- return 0;
- }
-
- mgr = &enc_to_mst(to_intel_encoder(old_conn_state->best_encoder))->primary->dp.mst_mgr;
- ret = drm_dp_atomic_release_vcpi_slots(&state->base, mgr,
- intel_connector->port);
-
- return ret;
+ return drm_dp_atomic_release_time_slots(&state->base,
+ &intel_connector->mst_port->mst_mgr,
+ intel_connector->port);
}
static void clear_act_sent(struct intel_encoder *encoder,
@@ -383,21 +365,17 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state,
struct intel_dp *intel_dp = &dig_port->dp;
struct intel_connector *connector =
to_intel_connector(old_conn_state->connector);
+ struct drm_dp_mst_topology_state *mst_state =
+ drm_atomic_get_mst_topology_state(&state->base, &intel_dp->mst_mgr);
struct drm_i915_private *i915 = to_i915(connector->base.dev);
- int start_slot = intel_dp_is_uhbr(old_crtc_state) ? 0 : 1;
- int ret;
drm_dbg_kms(&i915->drm, "active links %d\n",
intel_dp->active_mst_links);
intel_hdcp_disable(intel_mst->connector);
- drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, connector->port);
-
- ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr, start_slot);
- if (ret) {
- drm_dbg_kms(&i915->drm, "failed to update payload %d\n", ret);
- }
+ drm_dp_remove_payload(&intel_dp->mst_mgr, mst_state,
+ drm_atomic_get_mst_payload_state(mst_state, connector->port));
intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state);
}
@@ -425,8 +403,6 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
intel_disable_transcoder(old_crtc_state);
- drm_dp_update_payload_part2(&intel_dp->mst_mgr);
-
clear_act_sent(encoder, old_crtc_state);
intel_de_rmw(dev_priv, TRANS_DDI_FUNC_CTL(old_crtc_state->cpu_transcoder),
@@ -434,8 +410,6 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
wait_for_act_sent(encoder, old_crtc_state);
- drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, connector->port);
-
intel_ddi_disable_transcoder_func(old_crtc_state);
if (DISPLAY_VER(dev_priv) >= 9)
@@ -502,7 +476,8 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_connector *connector =
to_intel_connector(conn_state->connector);
- int start_slot = intel_dp_is_uhbr(pipe_config) ? 0 : 1;
+ struct drm_dp_mst_topology_state *mst_state =
+ drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
int ret;
bool first_mst_stream;
@@ -528,16 +503,13 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
dig_port->base.pre_enable(state, &dig_port->base,
pipe_config, NULL);
- ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
- connector->port,
- pipe_config->pbn,
- pipe_config->dp_m_n.tu);
- if (!ret)
- drm_err(&dev_priv->drm, "failed to allocate vcpi\n");
-
intel_dp->active_mst_links++;
- ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr, start_slot);
+ ret = drm_dp_add_payload_part1(&intel_dp->mst_mgr, mst_state,
+ drm_atomic_get_mst_payload_state(mst_state, connector->port));
+ if (ret < 0)
+ drm_err(&dev_priv->drm, "Failed to create MST payload for %s: %d\n",
+ connector->base.name, ret);
/*
* Before Gen 12 this is not done as part of
@@ -560,7 +532,10 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
struct intel_digital_port *dig_port = intel_mst->primary;
struct intel_dp *intel_dp = &dig_port->dp;
+ struct intel_connector *connector = to_intel_connector(conn_state->connector);
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
+ struct drm_dp_mst_topology_state *mst_state =
+ drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
enum transcoder trans = pipe_config->cpu_transcoder;
drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder);
@@ -588,7 +563,8 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
wait_for_act_sent(encoder, pipe_config);
- drm_dp_update_payload_part2(&intel_dp->mst_mgr);
+ drm_dp_add_payload_part2(&intel_dp->mst_mgr, &state->base,
+ drm_atomic_get_mst_payload_state(mst_state, connector->port));
if (DISPLAY_VER(dev_priv) >= 12 && pipe_config->fec_enable)
intel_de_rmw(dev_priv, CHICKEN_TRANS(trans), 0,
@@ -972,8 +948,6 @@ intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id)
struct intel_dp *intel_dp = &dig_port->dp;
enum port port = dig_port->base.port;
int ret;
- int max_source_rate =
- intel_dp->source_rates[intel_dp->num_source_rates - 1];
if (!HAS_DP_MST(i915) || intel_dp_is_edp(intel_dp))
return 0;
@@ -989,10 +963,7 @@ intel_dp_mst_encoder_init(struct intel_digital_port *dig_port, int conn_base_id)
/* create encoders */
intel_dp_create_fake_mst_encoders(dig_port);
ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, &i915->drm,
- &intel_dp->aux, 16, 3,
- dig_port->max_lanes,
- max_source_rate,
- conn_base_id);
+ &intel_dp->aux, 16, 3, conn_base_id);
if (ret) {
intel_dp->mst_mgr.cbs = NULL;
return ret;
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c
index 8ea66a2e1b09..7dbc9f0bb24f 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.c
@@ -30,8 +30,30 @@
static int intel_conn_to_vcpi(struct intel_connector *connector)
{
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_dp_mst_atomic_payload *payload;
+ struct drm_dp_mst_topology_state *mst_state;
+ int vcpi = 0;
+
/* For HDMI this is forced to be 0x0. For DP SST also this is 0x0. */
- return connector->port ? connector->port->vcpi.vcpi : 0;
+ if (!connector->port)
+ return 0;
+ mgr = connector->port->mgr;
+
+ drm_modeset_lock(&mgr->base.lock, NULL);
+ mst_state = to_drm_dp_mst_topology_state(mgr->base.state);
+ payload = drm_atomic_get_mst_payload_state(mst_state, connector->port);
+ if (drm_WARN_ON(mgr->dev, !payload))
+ goto out;
+
+ vcpi = payload->vcpi;
+ if (drm_WARN_ON(mgr->dev, vcpi < 0)) {
+ vcpi = 0;
+ goto out;
+ }
+out:
+ drm_modeset_unlock(&mgr->base.lock);
+ return vcpi;
}
/*
diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
index 5a5cf332d8a5..bc9c432edffe 100644
--- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
+++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c
@@ -361,7 +361,6 @@ static bool i915_ttm_eviction_valuable(struct ttm_buffer_object *bo,
const struct ttm_place *place)
{
struct drm_i915_gem_object *obj = i915_ttm_to_gem(bo);
- struct ttm_resource *res = bo->resource;
if (!obj)
return false;
@@ -378,45 +377,7 @@ static bool i915_ttm_eviction_valuable(struct ttm_buffer_object *bo,
if (!i915_gem_object_evictable(obj))
return false;
- switch (res->mem_type) {
- case I915_PL_LMEM0: {
- struct ttm_resource_manager *man =
- ttm_manager_type(bo->bdev, res->mem_type);
- struct i915_ttm_buddy_resource *bman_res =
- to_ttm_buddy_resource(res);
- struct drm_buddy *mm = bman_res->mm;
- struct drm_buddy_block *block;
-
- if (!place->fpfn && !place->lpfn)
- return true;
-
- GEM_BUG_ON(!place->lpfn);
-
- /*
- * If we just want something mappable then we can quickly check
- * if the current victim resource is using any of the CPU
- * visible portion.
- */
- if (!place->fpfn &&
- place->lpfn == i915_ttm_buddy_man_visible_size(man))
- return bman_res->used_visible_size > 0;
-
- /* Real range allocation */
- list_for_each_entry(block, &bman_res->blocks, link) {
- unsigned long fpfn =
- drm_buddy_block_offset(block) >> PAGE_SHIFT;
- unsigned long lpfn = fpfn +
- (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
-
- if (place->fpfn < lpfn && place->lpfn > fpfn)
- return true;
- }
- return false;
- } default:
- break;
- }
-
- return true;
+ return ttm_bo_eviction_valuable(bo, place);
}
static void i915_ttm_evict_flags(struct ttm_buffer_object *bo,
diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
index 427de1aaab36..e19452f0e100 100644
--- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
+++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c
@@ -173,6 +173,77 @@ static void i915_ttm_buddy_man_free(struct ttm_resource_manager *man,
kfree(bman_res);
}
+static bool i915_ttm_buddy_man_intersects(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
+ struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
+ struct drm_buddy *mm = &bman->mm;
+ struct drm_buddy_block *block;
+
+ if (!place->fpfn && !place->lpfn)
+ return true;
+
+ GEM_BUG_ON(!place->lpfn);
+
+ /*
+ * If we just want something mappable then we can quickly check
+ * if the current victim resource is using any of the CPU
+ * visible portion.
+ */
+ if (!place->fpfn &&
+ place->lpfn == i915_ttm_buddy_man_visible_size(man))
+ return bman_res->used_visible_size > 0;
+
+ /* Check each drm buddy block individually */
+ list_for_each_entry(block, &bman_res->blocks, link) {
+ unsigned long fpfn =
+ drm_buddy_block_offset(block) >> PAGE_SHIFT;
+ unsigned long lpfn = fpfn +
+ (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
+
+ if (place->fpfn < lpfn && place->lpfn > fpfn)
+ return true;
+ }
+
+ return false;
+}
+
+static bool i915_ttm_buddy_man_compatible(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct i915_ttm_buddy_resource *bman_res = to_ttm_buddy_resource(res);
+ struct i915_ttm_buddy_manager *bman = to_buddy_manager(man);
+ struct drm_buddy *mm = &bman->mm;
+ struct drm_buddy_block *block;
+
+ if (!place->fpfn && !place->lpfn)
+ return true;
+
+ GEM_BUG_ON(!place->lpfn);
+
+ if (!place->fpfn &&
+ place->lpfn == i915_ttm_buddy_man_visible_size(man))
+ return bman_res->used_visible_size == res->num_pages;
+
+ /* Check each drm buddy block individually */
+ list_for_each_entry(block, &bman_res->blocks, link) {
+ unsigned long fpfn =
+ drm_buddy_block_offset(block) >> PAGE_SHIFT;
+ unsigned long lpfn = fpfn +
+ (drm_buddy_block_size(mm, block) >> PAGE_SHIFT);
+
+ if (fpfn < place->fpfn || lpfn > place->lpfn)
+ return false;
+ }
+
+ return true;
+}
+
static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man,
struct drm_printer *printer)
{
@@ -200,6 +271,8 @@ static void i915_ttm_buddy_man_debug(struct ttm_resource_manager *man,
static const struct ttm_resource_manager_func i915_ttm_buddy_manager_func = {
.alloc = i915_ttm_buddy_man_alloc,
.free = i915_ttm_buddy_man_free,
+ .intersects = i915_ttm_buddy_man_intersects,
+ .compatible = i915_ttm_buddy_man_compatible,
.debug = i915_ttm_buddy_man_debug,
};
diff --git a/drivers/gpu/drm/mediatek/Kconfig b/drivers/gpu/drm/mediatek/Kconfig
index 6d7d0e207082..369e495d0c3e 100644
--- a/drivers/gpu/drm/mediatek/Kconfig
+++ b/drivers/gpu/drm/mediatek/Kconfig
@@ -21,6 +21,15 @@ config DRM_MEDIATEK
This driver provides kernel mode setting and
buffer management to userspace.
+config DRM_MEDIATEK_DP
+ tristate "DRM DPTX Support for MediaTek SoCs"
+ depends on DRM_MEDIATEK
+ select PHY_MTK_DP
+ select DRM_DISPLAY_HELPER
+ select DRM_DISPLAY_DP_HELPER
+ help
+ DRM/KMS Display Port driver for MediaTek SoCs.
+
config DRM_MEDIATEK_HDMI
tristate "DRM HDMI Support for Mediatek SoCs"
depends on DRM_MEDIATEK
diff --git a/drivers/gpu/drm/mediatek/Makefile b/drivers/gpu/drm/mediatek/Makefile
index 6e604a933ed0..3517d1c65cd7 100644
--- a/drivers/gpu/drm/mediatek/Makefile
+++ b/drivers/gpu/drm/mediatek/Makefile
@@ -23,3 +23,5 @@ mediatek-drm-hdmi-objs := mtk_cec.o \
mtk_hdmi_ddc.o
obj-$(CONFIG_DRM_MEDIATEK_HDMI) += mediatek-drm-hdmi.o
+
+obj-$(CONFIG_DRM_MEDIATEK_DP) += mtk_dp.o
diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c
new file mode 100644
index 000000000000..dfa942ca62da
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp.c
@@ -0,0 +1,2661 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+
+#include <drm/display/drm_dp.h>
+#include <drm/display/drm_dp_helper.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_edid.h>
+#include <drm/drm_of.h>
+#include <drm/drm_panel.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <linux/arm-smccc.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/media-bus-format.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soc/mediatek/mtk_sip_svc.h>
+#include <sound/hdmi-codec.h>
+#include <video/videomode.h>
+
+#include "mtk_dp_reg.h"
+
+#define MTK_DP_SIP_CONTROL_AARCH32 MTK_SIP_SMC_CMD(0x523)
+#define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5))
+#define MTK_DP_SIP_ATF_VIDEO_UNMUTE BIT(5)
+
+#define MTK_DP_THREAD_CABLE_STATE_CHG BIT(0)
+#define MTK_DP_THREAD_HPD_EVENT BIT(1)
+
+#define MTK_DP_4P1T 4
+#define MTK_DP_HDE 2
+#define MTK_DP_PIX_PER_ADDR 2
+#define MTK_DP_AUX_WAIT_REPLY_COUNT 20
+#define MTK_DP_TBC_BUF_READ_START_ADDR 0x8
+#define MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY 5
+#define MTK_DP_TRAIN_DOWNSCALE_RETRY 10
+#define MTK_DP_VERSION 0x11
+#define MTK_DP_SDP_AUI 0x4
+
+enum {
+ MTK_DP_CAL_GLB_BIAS_TRIM = 0,
+ MTK_DP_CAL_CLKTX_IMPSE,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2,
+ MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2,
+ MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3,
+ MTK_DP_CAL_MAX,
+};
+
+struct mtk_dp_train_info {
+ bool sink_ssc;
+ bool cable_plugged_in;
+ /* link_rate is in multiple of 0.27Gbps */
+ int link_rate;
+ int lane_count;
+ unsigned int channel_eq_pattern;
+};
+
+struct mtk_dp_audio_cfg {
+ bool detect_monitor;
+ int sad_count;
+ int sample_rate;
+ int word_length_bits;
+ int channels;
+};
+
+struct mtk_dp_info {
+ enum dp_pixelformat format;
+ struct videomode vm;
+ struct mtk_dp_audio_cfg audio_cur_cfg;
+};
+
+struct mtk_dp_efuse_fmt {
+ unsigned short idx;
+ unsigned short shift;
+ unsigned short mask;
+ unsigned short min_val;
+ unsigned short max_val;
+ unsigned short default_val;
+};
+
+struct mtk_dp {
+ bool enabled;
+ bool need_debounce;
+ u8 max_lanes;
+ u8 max_linkrate;
+ u8 rx_cap[DP_RECEIVER_CAP_SIZE];
+ u32 cal_data[MTK_DP_CAL_MAX];
+ u32 irq_thread_handle;
+ /* irq_thread_lock is used to protect irq_thread_handle */
+ spinlock_t irq_thread_lock;
+
+ struct device *dev;
+ struct drm_bridge bridge;
+ struct drm_bridge *next_bridge;
+ struct drm_connector *conn;
+ struct drm_device *drm_dev;
+ struct drm_dp_aux aux;
+
+ const struct mtk_dp_data *data;
+ struct mtk_dp_info info;
+ struct mtk_dp_train_info train_info;
+
+ struct platform_device *phy_dev;
+ struct phy *phy;
+ struct regmap *regs;
+ struct timer_list debounce_timer;
+
+ /* For audio */
+ bool audio_enable;
+ hdmi_codec_plugged_cb plugged_cb;
+ struct platform_device *audio_pdev;
+
+ struct device *codec_dev;
+ /* protect the plugged_cb as it's used in both bridge ops and audio */
+ struct mutex update_plugged_status_lock;
+};
+
+struct mtk_dp_data {
+ int bridge_type;
+ unsigned int smc_cmd;
+ const struct mtk_dp_efuse_fmt *efuse_fmt;
+ bool audio_supported;
+};
+
+static const struct mtk_dp_efuse_fmt mt8195_edp_efuse_fmt[MTK_DP_CAL_MAX] = {
+ [MTK_DP_CAL_GLB_BIAS_TRIM] = {
+ .idx = 3,
+ .shift = 27,
+ .mask = 0x1f,
+ .min_val = 1,
+ .max_val = 0x1e,
+ .default_val = 0xf,
+ },
+ [MTK_DP_CAL_CLKTX_IMPSE] = {
+ .idx = 0,
+ .shift = 9,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+ .idx = 2,
+ .shift = 28,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+ .idx = 2,
+ .shift = 20,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+ .idx = 2,
+ .shift = 12,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+ .idx = 2,
+ .shift = 4,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+ .idx = 2,
+ .shift = 24,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+ .idx = 2,
+ .shift = 16,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+ .idx = 2,
+ .shift = 8,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+ .idx = 2,
+ .shift = 0,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+};
+
+static const struct mtk_dp_efuse_fmt mt8195_dp_efuse_fmt[MTK_DP_CAL_MAX] = {
+ [MTK_DP_CAL_GLB_BIAS_TRIM] = {
+ .idx = 0,
+ .shift = 27,
+ .mask = 0x1f,
+ .min_val = 1,
+ .max_val = 0x1e,
+ .default_val = 0xf,
+ },
+ [MTK_DP_CAL_CLKTX_IMPSE] = {
+ .idx = 0,
+ .shift = 13,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] = {
+ .idx = 1,
+ .shift = 28,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] = {
+ .idx = 1,
+ .shift = 20,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] = {
+ .idx = 1,
+ .shift = 12,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] = {
+ .idx = 1,
+ .shift = 4,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] = {
+ .idx = 1,
+ .shift = 24,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] = {
+ .idx = 1,
+ .shift = 16,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] = {
+ .idx = 1,
+ .shift = 8,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+ [MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] = {
+ .idx = 1,
+ .shift = 0,
+ .mask = 0xf,
+ .min_val = 1,
+ .max_val = 0xe,
+ .default_val = 0x8,
+ },
+};
+
+static struct regmap_config mtk_dp_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = SEC_OFFSET + 0x90,
+ .name = "mtk-dp-registers",
+};
+
+static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b)
+{
+ return container_of(b, struct mtk_dp, bridge);
+}
+
+static u32 mtk_dp_read(struct mtk_dp *mtk_dp, u32 offset)
+{
+ u32 read_val;
+ int ret;
+
+ ret = regmap_read(mtk_dp->regs, offset, &read_val);
+ if (ret) {
+ dev_err(mtk_dp->dev, "Failed to read register 0x%x: %d\n",
+ offset, ret);
+ return 0;
+ }
+
+ return read_val;
+}
+
+static int mtk_dp_write(struct mtk_dp *mtk_dp, u32 offset, u32 val)
+{
+ int ret = regmap_write(mtk_dp->regs, offset, val);
+
+ if (ret)
+ dev_err(mtk_dp->dev,
+ "Failed to write register 0x%x with value 0x%x\n",
+ offset, val);
+ return ret;
+}
+
+static int mtk_dp_update_bits(struct mtk_dp *mtk_dp, u32 offset,
+ u32 val, u32 mask)
+{
+ int ret = regmap_update_bits(mtk_dp->regs, offset, mask, val);
+
+ if (ret)
+ dev_err(mtk_dp->dev,
+ "Failed to update register 0x%x with value 0x%x, mask 0x%x\n",
+ offset, val, mask);
+ return ret;
+}
+
+static void mtk_dp_bulk_16bit_write(struct mtk_dp *mtk_dp, u32 offset, u8 *buf,
+ size_t length)
+{
+ int i;
+
+ /* 2 bytes per register */
+ for (i = 0; i < length; i += 2) {
+ u32 val = buf[i] | (i + 1 < length ? buf[i + 1] << 8 : 0);
+
+ if (mtk_dp_write(mtk_dp, offset + i * 2, val))
+ return;
+ }
+}
+
+static void mtk_dp_msa_bypass_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+ u32 mask = HTOTAL_SEL_DP_ENC0_P0 | VTOTAL_SEL_DP_ENC0_P0 |
+ HSTART_SEL_DP_ENC0_P0 | VSTART_SEL_DP_ENC0_P0 |
+ HWIDTH_SEL_DP_ENC0_P0 | VHEIGHT_SEL_DP_ENC0_P0 |
+ HSP_SEL_DP_ENC0_P0 | HSW_SEL_DP_ENC0_P0 |
+ VSP_SEL_DP_ENC0_P0 | VSW_SEL_DP_ENC0_P0;
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030, enable ? 0 : mask, mask);
+}
+
+static void mtk_dp_set_msa(struct mtk_dp *mtk_dp)
+{
+ struct drm_display_mode mode;
+ struct videomode *vm = &mtk_dp->info.vm;
+
+ drm_display_mode_from_videomode(vm, &mode);
+
+ /* horizontal */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3010,
+ mode.htotal, HTOTAL_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3018,
+ vm->hsync_len + vm->hback_porch,
+ HSTART_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+ vm->hsync_len, HSW_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3028,
+ 0, HSP_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3020,
+ vm->hactive, HWIDTH_SW_DP_ENC0_P0_MASK);
+
+ /* vertical */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3014,
+ mode.vtotal, VTOTAL_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_301C,
+ vm->vsync_len + vm->vback_porch,
+ VSTART_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+ vm->vsync_len, VSW_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_302C,
+ 0, VSP_SW_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3024,
+ vm->vactive, VHEIGHT_SW_DP_ENC0_P0_MASK);
+
+ /* horizontal */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3064,
+ vm->hactive, HDE_NUM_LAST_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3154,
+ mode.htotal, PGEN_HTOTAL_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3158,
+ vm->hfront_porch,
+ PGEN_HSYNC_RISING_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_315C,
+ vm->hsync_len,
+ PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3160,
+ vm->hback_porch + vm->hsync_len,
+ PGEN_HFDE_START_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3164,
+ vm->hactive,
+ PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+
+ /* vertical */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3168,
+ mode.vtotal,
+ PGEN_VTOTAL_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_316C,
+ vm->vfront_porch,
+ PGEN_VSYNC_RISING_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3170,
+ vm->vsync_len,
+ PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3174,
+ vm->vback_porch + vm->vsync_len,
+ PGEN_VFDE_START_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3178,
+ vm->vactive,
+ PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK);
+}
+
+static int mtk_dp_set_color_format(struct mtk_dp *mtk_dp,
+ enum dp_pixelformat color_format)
+{
+ u32 val;
+
+ /* update MISC0 */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+ color_format << DP_TEST_COLOR_FORMAT_SHIFT,
+ DP_TEST_COLOR_FORMAT_MASK);
+
+ switch (color_format) {
+ case DP_PIXELFORMAT_YUV422:
+ val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422;
+ break;
+ case DP_PIXELFORMAT_RGB:
+ val = PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB;
+ break;
+ default:
+ drm_warn(mtk_dp->drm_dev, "Unsupported color format: %d\n",
+ color_format);
+ return -EINVAL;
+ }
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+ val, PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK);
+ return 0;
+}
+
+static void mtk_dp_set_color_depth(struct mtk_dp *mtk_dp)
+{
+ /* Only support 8 bits currently */
+ /* Update MISC0 */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3034,
+ DP_MSA_MISC_8_BPC, DP_TEST_BIT_DEPTH_MASK);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+ VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT,
+ VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_config_mn_mode(struct mtk_dp *mtk_dp)
+{
+ /* 0: hw mode, 1: sw mode */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+ 0, VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_set_sram_read_start(struct mtk_dp *mtk_dp, u32 val)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+ val, SRAM_START_READ_THRD_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_setup_encoder(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_303C,
+ VIDEO_MN_GEN_EN_DP_ENC0_P0,
+ VIDEO_MN_GEN_EN_DP_ENC0_P0);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+ SDP_DOWN_CNT_DP_ENC0_P0_VAL,
+ SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+ SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL,
+ SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3300,
+ VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL << 8,
+ VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364,
+ FIFO_READ_START_POINT_DP_ENC1_P0_VAL << 12,
+ FIFO_READ_START_POINT_DP_ENC1_P0_MASK);
+ mtk_dp_write(mtk_dp, MTK_DP_ENC1_P0_3368, DP_ENC1_P0_3368_VAL);
+}
+
+static void mtk_dp_pg_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3038,
+ enable ? VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK : 0,
+ VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31B0,
+ PGEN_PATTERN_SEL_VAL << 4, PGEN_PATTERN_SEL_MASK);
+}
+
+static void mtk_dp_audio_setup_channels(struct mtk_dp *mtk_dp,
+ struct mtk_dp_audio_cfg *cfg)
+{
+ u32 channel_enable_bits;
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3324,
+ AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX,
+ AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK);
+
+ /* audio channel count change reset */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+ DP_ENC_DUMMY_RW_1, DP_ENC_DUMMY_RW_1);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3304,
+ AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+ AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+ AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK,
+ AU_PRTY_REGEN_DP_ENC1_P0_MASK |
+ AU_CH_STS_REGEN_DP_ENC1_P0_MASK |
+ AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK);
+
+ switch (cfg->channels) {
+ case 2:
+ channel_enable_bits = AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+ AUDIO_2CH_EN_DP_ENC0_P0_MASK;
+ break;
+ case 8:
+ default:
+ channel_enable_bits = AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+ AUDIO_8CH_EN_DP_ENC0_P0_MASK;
+ break;
+ }
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+ channel_enable_bits | AU_EN_DP_ENC0_P0,
+ AUDIO_2CH_SEL_DP_ENC0_P0_MASK |
+ AUDIO_2CH_EN_DP_ENC0_P0_MASK |
+ AUDIO_8CH_SEL_DP_ENC0_P0_MASK |
+ AUDIO_8CH_EN_DP_ENC0_P0_MASK |
+ AU_EN_DP_ENC0_P0);
+
+ /* audio channel count change reset */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4, 0, DP_ENC_DUMMY_RW_1);
+
+ /* enable audio reset */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_33F4,
+ DP_ENC_DUMMY_RW_1_AUDIO_RST_EN,
+ DP_ENC_DUMMY_RW_1_AUDIO_RST_EN);
+}
+
+static void mtk_dp_audio_channel_status_set(struct mtk_dp *mtk_dp,
+ struct mtk_dp_audio_cfg *cfg)
+{
+ struct snd_aes_iec958 iec = { 0 };
+
+ switch (cfg->sample_rate) {
+ case 32000:
+ iec.status[3] = IEC958_AES3_CON_FS_32000;
+ break;
+ case 44100:
+ iec.status[3] = IEC958_AES3_CON_FS_44100;
+ break;
+ case 48000:
+ iec.status[3] = IEC958_AES3_CON_FS_48000;
+ break;
+ case 88200:
+ iec.status[3] = IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ iec.status[3] = IEC958_AES3_CON_FS_96000;
+ break;
+ case 192000:
+ iec.status[3] = IEC958_AES3_CON_FS_192000;
+ break;
+ default:
+ iec.status[3] = IEC958_AES3_CON_FS_NOTID;
+ break;
+ }
+
+ switch (cfg->word_length_bits) {
+ case 16:
+ iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16;
+ break;
+ case 20:
+ iec.status[4] = IEC958_AES4_CON_WORDLEN_20_16 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ case 24:
+ iec.status[4] = IEC958_AES4_CON_WORDLEN_24_20 |
+ IEC958_AES4_CON_MAX_WORDLEN_24;
+ break;
+ default:
+ iec.status[4] = IEC958_AES4_CON_WORDLEN_NOTID;
+ }
+
+ /* IEC 60958 consumer channel status bits */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_308C,
+ 0, CH_STATUS_0_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3090,
+ iec.status[3] << 8, CH_STATUS_1_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3094,
+ iec.status[4], CH_STATUS_2_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_sdp_asp_set_channels(struct mtk_dp *mtk_dp,
+ int channels)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_312C,
+ (min(8, channels) - 1) << 8,
+ ASP_HB2_DP_ENC0_P0_MASK | ASP_HB3_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_audio_set_divider(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30BC,
+ AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2,
+ AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_trigger_aui(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+ MTK_DP_SDP_AUI, SDP_PACKET_TYPE_DP_ENC1_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3280,
+ SDP_PACKET_W_DP_ENC1_P0, SDP_PACKET_W_DP_ENC1_P0);
+}
+
+static void mtk_dp_sdp_set_data(struct mtk_dp *mtk_dp, u8 *data_bytes)
+{
+ mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_ENC1_P0_3200,
+ data_bytes, 0x10);
+}
+
+static void mtk_dp_sdp_set_header_aui(struct mtk_dp *mtk_dp,
+ struct dp_sdp_header *header)
+{
+ u32 db_addr = MTK_DP_ENC0_P0_30D8 + (MTK_DP_SDP_AUI - 1) * 8;
+
+ mtk_dp_bulk_16bit_write(mtk_dp, db_addr, (u8 *)header, 4);
+}
+
+static void mtk_dp_disable_sdp_aui(struct mtk_dp *mtk_dp)
+{
+ /* Disable periodic send */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc, 0,
+ 0xff << ((MTK_DP_ENC0_P0_30A8 & 3) * 8));
+}
+
+static void mtk_dp_setup_sdp_aui(struct mtk_dp *mtk_dp,
+ struct dp_sdp *sdp)
+{
+ u32 shift;
+
+ mtk_dp_sdp_set_data(mtk_dp, sdp->db);
+ mtk_dp_sdp_set_header_aui(mtk_dp, &sdp->sdp_header);
+ mtk_dp_disable_sdp_aui(mtk_dp);
+
+ shift = (MTK_DP_ENC0_P0_30A8 & 3) * 8;
+
+ mtk_dp_sdp_trigger_aui(mtk_dp);
+ /* Enable periodic sending */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A8 & 0xfffc,
+ 0x05 << shift, 0xff << shift);
+}
+
+static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, DP_AUX_P0_3640_VAL);
+}
+
+static void mtk_dp_aux_set_cmd(struct mtk_dp *mtk_dp, u8 cmd, u32 addr)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3644,
+ cmd, MCU_REQUEST_COMMAND_AUX_TX_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3648,
+ addr, MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_364C,
+ addr >> 16, MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_clear_fifo(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+ MCU_ACK_TRAN_COMPLETE_AUX_TX_P0,
+ MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 |
+ PHY_FIFO_RST_AUX_TX_P0_MASK |
+ MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+}
+
+static void mtk_dp_aux_request_ready(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3630,
+ AUX_TX_REQUEST_READY_AUX_TX_P0,
+ AUX_TX_REQUEST_READY_AUX_TX_P0);
+}
+
+static void mtk_dp_aux_fill_write_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+ size_t length)
+{
+ mtk_dp_bulk_16bit_write(mtk_dp, MTK_DP_AUX_P0_3708, buf, length);
+}
+
+static void mtk_dp_aux_read_rx_fifo(struct mtk_dp *mtk_dp, u8 *buf,
+ size_t length, int read_delay)
+{
+ int read_pos;
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+ 0, AUX_RD_MODE_AUX_TX_P0_MASK);
+
+ for (read_pos = 0; read_pos < length; read_pos++) {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3620,
+ AUX_RX_FIFO_READ_PULSE_TX_P0,
+ AUX_RX_FIFO_READ_PULSE_TX_P0);
+
+ /* Hardware needs time to update the data */
+ usleep_range(read_delay, read_delay * 2);
+ buf[read_pos] = (u8)(mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3620) &
+ AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK);
+ }
+}
+
+static void mtk_dp_aux_set_length(struct mtk_dp *mtk_dp, size_t length)
+{
+ if (length > 0) {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3650,
+ (length - 1) << 12,
+ MCU_REQ_DATA_NUM_AUX_TX_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+ 0,
+ AUX_NO_LENGTH_AUX_TX_P0 |
+ AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+ AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+ } else {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+ AUX_NO_LENGTH_AUX_TX_P0,
+ AUX_NO_LENGTH_AUX_TX_P0 |
+ AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+ AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+ }
+}
+
+static int mtk_dp_aux_wait_for_completion(struct mtk_dp *mtk_dp, bool is_read)
+{
+ int wait_reply = MTK_DP_AUX_WAIT_REPLY_COUNT;
+
+ while (--wait_reply) {
+ u32 aux_irq_status;
+
+ if (is_read) {
+ u32 fifo_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3618);
+
+ if (fifo_status &
+ (AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK |
+ AUX_RX_FIFO_FULL_AUX_TX_P0_MASK)) {
+ return 0;
+ }
+ }
+
+ aux_irq_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3640);
+ if (aux_irq_status & AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+ return 0;
+
+ if (aux_irq_status & AUX_400US_TIMEOUT_IRQ_AUX_TX_P0)
+ return -ETIMEDOUT;
+
+ /* Give the hardware a chance to reach completion before retrying */
+ usleep_range(100, 500);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static int mtk_dp_aux_do_transfer(struct mtk_dp *mtk_dp, bool is_read, u8 cmd,
+ u32 addr, u8 *buf, size_t length)
+{
+ int ret;
+ u32 reply_cmd;
+
+ if (is_read && (length > DP_AUX_MAX_PAYLOAD_BYTES ||
+ (cmd == DP_AUX_NATIVE_READ && !length)))
+ return -EINVAL;
+
+ if (!is_read)
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+ AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0,
+ AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0);
+
+ /* We need to clear fifo and irq before sending commands to the sink device. */
+ mtk_dp_aux_clear_fifo(mtk_dp);
+ mtk_dp_aux_irq_clear(mtk_dp);
+
+ mtk_dp_aux_set_cmd(mtk_dp, cmd, addr);
+ mtk_dp_aux_set_length(mtk_dp, length);
+
+ if (!is_read) {
+ if (length)
+ mtk_dp_aux_fill_write_fifo(mtk_dp, buf, length);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3704,
+ AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK,
+ AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK);
+ }
+
+ mtk_dp_aux_request_ready(mtk_dp);
+
+ /* Wait for feedback from sink device. */
+ ret = mtk_dp_aux_wait_for_completion(mtk_dp, is_read);
+
+ reply_cmd = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3624) &
+ AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK;
+
+ if (ret || reply_cmd) {
+ u32 phy_status = mtk_dp_read(mtk_dp, MTK_DP_AUX_P0_3628) &
+ AUX_RX_PHY_STATE_AUX_TX_P0_MASK;
+ if (phy_status != AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE) {
+ drm_err(mtk_dp->drm_dev,
+ "AUX Rx Aux hang, need SW reset\n");
+ return -EIO;
+ }
+
+ return -ETIMEDOUT;
+ }
+
+ if (!length) {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_362C,
+ 0,
+ AUX_NO_LENGTH_AUX_TX_P0 |
+ AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK |
+ AUX_RESERVED_RW_0_AUX_TX_P0_MASK);
+ } else if (is_read) {
+ int read_delay;
+
+ if (cmd == (DP_AUX_I2C_READ | DP_AUX_I2C_MOT) ||
+ cmd == DP_AUX_I2C_READ)
+ read_delay = 500;
+ else
+ read_delay = 100;
+
+ mtk_dp_aux_read_rx_fifo(mtk_dp, buf, length, read_delay);
+ }
+
+ return 0;
+}
+
+static void mtk_dp_set_swing_pre_emphasis(struct mtk_dp *mtk_dp, int lane_num,
+ int swing_val, int preemphasis)
+{
+ u32 lane_shift = lane_num * DP_TX1_VOLT_SWING_SHIFT;
+
+ dev_dbg(mtk_dp->dev,
+ "link training: swing_val = 0x%x, pre-emphasis = 0x%x\n",
+ swing_val, preemphasis);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+ swing_val << (DP_TX0_VOLT_SWING_SHIFT + lane_shift),
+ DP_TX0_VOLT_SWING_MASK << lane_shift);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+ preemphasis << (DP_TX0_PRE_EMPH_SHIFT + lane_shift),
+ DP_TX0_PRE_EMPH_MASK << lane_shift);
+}
+
+static void mtk_dp_reset_swing_pre_emphasis(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_SWING_EMP,
+ 0,
+ DP_TX0_VOLT_SWING_MASK |
+ DP_TX1_VOLT_SWING_MASK |
+ DP_TX2_VOLT_SWING_MASK |
+ DP_TX3_VOLT_SWING_MASK |
+ DP_TX0_PRE_EMPH_MASK |
+ DP_TX1_PRE_EMPH_MASK |
+ DP_TX2_PRE_EMPH_MASK |
+ DP_TX3_PRE_EMPH_MASK);
+}
+
+static u32 mtk_dp_swirq_get_clear(struct mtk_dp *mtk_dp)
+{
+ u32 irq_status = mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_35D0) &
+ SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK;
+
+ if (irq_status) {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+ irq_status, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35C8,
+ 0, SW_IRQ_CLR_DP_TRANS_P0_MASK);
+ }
+
+ return irq_status;
+}
+
+static u32 mtk_dp_hwirq_get_clear(struct mtk_dp *mtk_dp)
+{
+ u32 irq_status = (mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3418) &
+ IRQ_STATUS_DP_TRANS_P0_MASK) >> 12;
+
+ if (irq_status) {
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+ irq_status, IRQ_CLR_DP_TRANS_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+ 0, IRQ_CLR_DP_TRANS_P0_MASK);
+ }
+
+ return irq_status;
+}
+
+static void mtk_dp_hwirq_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3418,
+ enable ? 0 :
+ IRQ_MASK_DP_TRANS_P0_DISC_IRQ |
+ IRQ_MASK_DP_TRANS_P0_CONN_IRQ |
+ IRQ_MASK_DP_TRANS_P0_INT_IRQ,
+ IRQ_MASK_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_settings(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_342C,
+ XTAL_FREQ_DP_TRANS_P0_DEFAULT,
+ XTAL_FREQ_DP_TRANS_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3540,
+ FEC_CLOCK_EN_MODE_DP_TRANS_P0,
+ FEC_CLOCK_EN_MODE_DP_TRANS_P0);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_31EC,
+ AUDIO_CH_SRC_SEL_DP_ENC0_P0,
+ AUDIO_CH_SRC_SEL_DP_ENC0_P0);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+ 0, SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_IRQ_MASK,
+ IRQ_MASK_AUX_TOP_IRQ, IRQ_MASK_AUX_TOP_IRQ);
+}
+
+static void mtk_dp_initialize_hpd_detect_settings(struct mtk_dp *mtk_dp)
+{
+ u32 val;
+ /* Debounce threshold */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+ 8, HPD_DEB_THD_DP_TRANS_P0_MASK);
+
+ val = (HPD_INT_THD_DP_TRANS_P0_LOWER_500US |
+ HPD_INT_THD_DP_TRANS_P0_UPPER_1100US) << 4;
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+ val, HPD_INT_THD_DP_TRANS_P0_MASK);
+
+ /*
+ * Connect threshold 1.5ms + 5 x 0.1ms = 2ms
+ * Disconnect threshold 1.5ms + 5 x 0.1ms = 2ms
+ */
+ val = (5 << 8) | (5 << 12);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3410,
+ val,
+ HPD_DISC_THD_DP_TRANS_P0_MASK |
+ HPD_CONN_THD_DP_TRANS_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3430,
+ HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT,
+ HPD_INT_THD_ECO_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_initialize_aux_settings(struct mtk_dp *mtk_dp)
+{
+ /* modify timeout threshold = 0x1595 */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_360C,
+ AUX_TIMEOUT_THR_AUX_TX_P0_VAL,
+ AUX_TIMEOUT_THR_AUX_TX_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3658,
+ 0, AUX_TX_OV_EN_AUX_TX_P0_MASK);
+ /* 25 for 26M */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3634,
+ AUX_TX_OVER_SAMPLE_RATE_FOR_26M << 8,
+ AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK);
+ /* 13 for 26M */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3614,
+ AUX_RX_UI_CNT_THR_AUX_FOR_26M,
+ AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_37C8,
+ MTK_ATOP_EN_AUX_TX_P0,
+ MTK_ATOP_EN_AUX_TX_P0);
+}
+
+static void mtk_dp_initialize_digital_settings(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_304C,
+ 0, VBID_VIDEO_MUTE_DP_ENC0_P0_MASK);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3368,
+ BS2BS_MODE_DP_ENC1_P0_VAL << 12,
+ BS2BS_MODE_DP_ENC1_P0_MASK);
+
+ /* dp tx encoder reset all sw */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+ DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0,
+ DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+
+ /* Wait for sw reset to complete */
+ usleep_range(1000, 5000);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3004,
+ 0, DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0);
+}
+
+static void mtk_dp_digital_sw_reset(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+ DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0,
+ DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+
+ /* Wait for sw reset to complete */
+ usleep_range(1000, 5000);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_340C,
+ 0, DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0);
+}
+
+static void mtk_dp_set_lanes(struct mtk_dp *mtk_dp, int lanes)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_35F0,
+ lanes == 0 ? 0 : DP_TRANS_DUMMY_RW_0,
+ DP_TRANS_DUMMY_RW_0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+ lanes, LANE_NUM_DP_ENC0_P0_MASK);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_34A4,
+ lanes << 2, LANE_NUM_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_get_calibration_data(struct mtk_dp *mtk_dp)
+{
+ const struct mtk_dp_efuse_fmt *fmt;
+ struct device *dev = mtk_dp->dev;
+ struct nvmem_cell *cell;
+ u32 *cal_data = mtk_dp->cal_data;
+ u32 *buf;
+ int i;
+ size_t len;
+
+ cell = nvmem_cell_get(dev, "dp_calibration_data");
+ if (IS_ERR(cell)) {
+ dev_warn(dev, "Failed to get nvmem cell dp_calibration_data\n");
+ goto use_default_val;
+ }
+
+ buf = (u32 *)nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf) || ((len / sizeof(u32)) != 4)) {
+ dev_warn(dev, "Failed to read nvmem_cell_read\n");
+
+ if (!IS_ERR(buf))
+ kfree(buf);
+
+ goto use_default_val;
+ }
+
+ for (i = 0; i < MTK_DP_CAL_MAX; i++) {
+ fmt = &mtk_dp->data->efuse_fmt[i];
+ cal_data[i] = (buf[fmt->idx] >> fmt->shift) & fmt->mask;
+
+ if (cal_data[i] < fmt->min_val || cal_data[i] > fmt->max_val) {
+ dev_warn(mtk_dp->dev, "Invalid efuse data, idx = %d\n", i);
+ kfree(buf);
+ goto use_default_val;
+ }
+ }
+ kfree(buf);
+
+ return;
+
+use_default_val:
+ dev_warn(mtk_dp->dev, "Use default calibration data\n");
+ for (i = 0; i < MTK_DP_CAL_MAX; i++)
+ cal_data[i] = mtk_dp->data->efuse_fmt[i].default_val;
+}
+
+static void mtk_dp_set_calibration_data(struct mtk_dp *mtk_dp)
+{
+ u32 *cal_data = mtk_dp->cal_data;
+
+ mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_DPAUX_TX,
+ cal_data[MTK_DP_CAL_CLKTX_IMPSE] << 20,
+ RG_CKM_PT0_CKTX_IMPSEL);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_GLB_BIAS_GEN_00,
+ cal_data[MTK_DP_CAL_GLB_BIAS_TRIM] << 16,
+ RG_XTP_GLB_BIAS_INTR_CTRL);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_0] << 12,
+ RG_XTP_LN0_TX_IMPSEL_PMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_0,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_0] << 16,
+ RG_XTP_LN0_TX_IMPSEL_NMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_1] << 12,
+ RG_XTP_LN1_TX_IMPSEL_PMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_1,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_1] << 16,
+ RG_XTP_LN1_TX_IMPSEL_NMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_2] << 12,
+ RG_XTP_LN2_TX_IMPSEL_PMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_2,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_2] << 16,
+ RG_XTP_LN2_TX_IMPSEL_NMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_PMOS_3] << 12,
+ RG_XTP_LN3_TX_IMPSEL_PMOS);
+ mtk_dp_update_bits(mtk_dp, DP_PHY_LANE_TX_3,
+ cal_data[MTK_DP_CAL_LN_TX_IMPSEL_NMOS_3] << 16,
+ RG_XTP_LN3_TX_IMPSEL_NMOS);
+}
+
+static int mtk_dp_phy_configure(struct mtk_dp *mtk_dp,
+ u32 link_rate, int lane_count)
+{
+ int ret;
+ union phy_configure_opts phy_opts = {
+ .dp = {
+ .link_rate = drm_dp_bw_code_to_link_rate(link_rate) / 100,
+ .set_rate = 1,
+ .lanes = lane_count,
+ .set_lanes = 1,
+ .ssc = mtk_dp->train_info.sink_ssc,
+ }
+ };
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, DP_PWR_STATE_BANDGAP,
+ DP_PWR_STATE_MASK);
+
+ ret = phy_configure(mtk_dp->phy, &phy_opts);
+ if (ret)
+ return ret;
+
+ mtk_dp_set_calibration_data(mtk_dp);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL_LANE, DP_PWR_STATE_MASK);
+
+ return 0;
+}
+
+static void mtk_dp_set_idle_pattern(struct mtk_dp *mtk_dp, bool enable)
+{
+ u32 val = POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK |
+ POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK |
+ POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK |
+ POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK;
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3580,
+ enable ? val : 0, val);
+}
+
+static void mtk_dp_train_set_pattern(struct mtk_dp *mtk_dp, int pattern)
+{
+ /* TPS1 */
+ if (pattern == 1)
+ mtk_dp_set_idle_pattern(mtk_dp, false);
+
+ mtk_dp_update_bits(mtk_dp,
+ MTK_DP_TRANS_P0_3400,
+ pattern ? BIT(pattern - 1) << 12 : 0,
+ PATTERN1_EN_DP_TRANS_P0_MASK |
+ PATTERN2_EN_DP_TRANS_P0_MASK |
+ PATTERN3_EN_DP_TRANS_P0_MASK |
+ PATTERN4_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_set_enhanced_frame_mode(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+ ENHANCED_FRAME_EN_DP_ENC0_P0,
+ ENHANCED_FRAME_EN_DP_ENC0_P0);
+}
+
+static void mtk_dp_training_set_scramble(struct mtk_dp *mtk_dp, bool enable)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TRANS_P0_3404,
+ enable ? DP_SCR_EN_DP_TRANS_P0_MASK : 0,
+ DP_SCR_EN_DP_TRANS_P0_MASK);
+}
+
+static void mtk_dp_video_mute(struct mtk_dp *mtk_dp, bool enable)
+{
+ struct arm_smccc_res res;
+ u32 val = VIDEO_MUTE_SEL_DP_ENC0_P0 |
+ (enable ? VIDEO_MUTE_SW_DP_ENC0_P0 : 0);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3000,
+ val,
+ VIDEO_MUTE_SEL_DP_ENC0_P0 |
+ VIDEO_MUTE_SW_DP_ENC0_P0);
+
+ arm_smccc_smc(MTK_DP_SIP_CONTROL_AARCH32,
+ mtk_dp->data->smc_cmd, enable,
+ 0, 0, 0, 0, 0, &res);
+
+ dev_dbg(mtk_dp->dev, "smc cmd: 0x%x, p1: 0x%x, ret: 0x%lx-0x%lx\n",
+ mtk_dp->data->smc_cmd, enable, res.a0, res.a1);
+}
+
+static void mtk_dp_audio_mute(struct mtk_dp *mtk_dp, bool mute)
+{
+ u32 val[3];
+
+ if (mute) {
+ val[0] = VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+ VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0;
+ val[1] = 0;
+ val[2] = 0;
+ } else {
+ val[0] = 0;
+ val[1] = AU_EN_DP_ENC0_P0;
+ /* Send one every two frames */
+ val[2] = 0x0F;
+ }
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3030,
+ val[0],
+ VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 |
+ VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3088,
+ val[1], AU_EN_DP_ENC0_P0);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_30A4,
+ val[2], AU_TS_CFG_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_power_enable(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+ 0, SW_RST_B_PHYD);
+
+ /* Wait for power enable */
+ usleep_range(10, 200);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_RESET_AND_PROBE,
+ SW_RST_B_PHYD, SW_RST_B_PHYD);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL, DP_PWR_STATE_MASK);
+ mtk_dp_write(mtk_dp, MTK_DP_1040,
+ RG_DPAUX_RX_VALID_DEGLITCH_EN | RG_XTP_GLB_CKDET_EN |
+ RG_DPAUX_RX_EN);
+ mtk_dp_update_bits(mtk_dp, MTK_DP_0034, 0, DA_CKM_CKTX0_EN_FORCE_EN);
+}
+
+static void mtk_dp_power_disable(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_write(mtk_dp, MTK_DP_TOP_PWR_STATE, 0);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_0034,
+ DA_CKM_CKTX0_EN_FORCE_EN, DA_CKM_CKTX0_EN_FORCE_EN);
+
+ /* Disable RX */
+ mtk_dp_write(mtk_dp, MTK_DP_1040, 0);
+ mtk_dp_write(mtk_dp, MTK_DP_TOP_MEM_PD,
+ 0x550 | FUSE_SEL | MEM_ISO_EN);
+}
+
+static void mtk_dp_initialize_priv_data(struct mtk_dp *mtk_dp)
+{
+ mtk_dp->train_info.link_rate = DP_LINK_BW_5_4;
+ mtk_dp->train_info.lane_count = mtk_dp->max_lanes;
+ mtk_dp->train_info.cable_plugged_in = false;
+
+ mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+ memset(&mtk_dp->info.vm, 0, sizeof(struct videomode));
+ mtk_dp->audio_enable = false;
+}
+
+static void mtk_dp_sdp_set_down_cnt_init(struct mtk_dp *mtk_dp,
+ u32 sram_read_start)
+{
+ u32 sdp_down_cnt_init = 0;
+ struct drm_display_mode mode;
+ struct videomode *vm = &mtk_dp->info.vm;
+
+ drm_display_mode_from_videomode(vm, &mode);
+
+ if (mode.clock > 0)
+ sdp_down_cnt_init = sram_read_start *
+ mtk_dp->train_info.link_rate * 2700 * 8 /
+ (mode.clock * 4);
+
+ switch (mtk_dp->train_info.lane_count) {
+ case 1:
+ sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x1A);
+ break;
+ case 2:
+ /* case for LowResolution && High Audio Sample Rate */
+ sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 0x10);
+ sdp_down_cnt_init += mode.vtotal <= 525 ? 4 : 0;
+ break;
+ case 4:
+ default:
+ sdp_down_cnt_init = max_t(u32, sdp_down_cnt_init, 6);
+ break;
+ }
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC0_P0_3040,
+ sdp_down_cnt_init,
+ SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK);
+}
+
+static void mtk_dp_sdp_set_down_cnt_init_in_hblank(struct mtk_dp *mtk_dp)
+{
+ int pix_clk_mhz;
+ u32 dc_offset;
+ u32 spd_down_cnt_init = 0;
+ struct drm_display_mode mode;
+ struct videomode *vm = &mtk_dp->info.vm;
+
+ drm_display_mode_from_videomode(vm, &mode);
+
+ pix_clk_mhz = mtk_dp->info.format == DP_PIXELFORMAT_YUV420 ?
+ mode.clock / 2000 : mode.clock / 1000;
+
+ switch (mtk_dp->train_info.lane_count) {
+ case 1:
+ spd_down_cnt_init = 0x20;
+ break;
+ case 2:
+ dc_offset = (mode.vtotal <= 525) ? 0x14 : 0x00;
+ spd_down_cnt_init = 0x18 + dc_offset;
+ break;
+ case 4:
+ default:
+ dc_offset = (mode.vtotal <= 525) ? 0x08 : 0x00;
+ if (pix_clk_mhz > mtk_dp->train_info.link_rate * 27)
+ spd_down_cnt_init = 0x8;
+ else
+ spd_down_cnt_init = 0x10 + dc_offset;
+ break;
+ }
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_ENC1_P0_3364, spd_down_cnt_init,
+ SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK);
+}
+
+static void mtk_dp_setup_tu(struct mtk_dp *mtk_dp)
+{
+ u32 sram_read_start = min_t(u32, MTK_DP_TBC_BUF_READ_START_ADDR,
+ mtk_dp->info.vm.hactive /
+ mtk_dp->train_info.lane_count /
+ MTK_DP_4P1T / MTK_DP_HDE /
+ MTK_DP_PIX_PER_ADDR);
+ mtk_dp_set_sram_read_start(mtk_dp, sram_read_start);
+ mtk_dp_setup_encoder(mtk_dp);
+ mtk_dp_sdp_set_down_cnt_init_in_hblank(mtk_dp);
+ mtk_dp_sdp_set_down_cnt_init(mtk_dp, sram_read_start);
+}
+
+static void mtk_dp_set_tx_out(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_setup_tu(mtk_dp);
+}
+
+static void mtk_dp_train_update_swing_pre(struct mtk_dp *mtk_dp, int lanes,
+ u8 dpcd_adjust_req[2])
+{
+ int lane;
+
+ for (lane = 0; lane < lanes; ++lane) {
+ u8 val;
+ u8 swing;
+ u8 preemphasis;
+ int index = lane / 2;
+ int shift = lane % 2 ? DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT : 0;
+
+ swing = (dpcd_adjust_req[index] >> shift) &
+ DP_ADJUST_VOLTAGE_SWING_LANE0_MASK;
+ preemphasis = ((dpcd_adjust_req[index] >> shift) &
+ DP_ADJUST_PRE_EMPHASIS_LANE0_MASK) >>
+ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT;
+ val = swing << DP_TRAIN_VOLTAGE_SWING_SHIFT |
+ preemphasis << DP_TRAIN_PRE_EMPHASIS_SHIFT;
+
+ if (swing == DP_TRAIN_VOLTAGE_SWING_LEVEL_3)
+ val |= DP_TRAIN_MAX_SWING_REACHED;
+ if (preemphasis == 3)
+ val |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
+
+ mtk_dp_set_swing_pre_emphasis(mtk_dp, lane, swing, preemphasis);
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_LANE0_SET + lane,
+ val);
+ }
+}
+
+static void mtk_dp_pattern(struct mtk_dp *mtk_dp, bool is_tps1)
+{
+ int pattern;
+ unsigned int aux_offset;
+
+ if (is_tps1) {
+ pattern = 1;
+ aux_offset = DP_LINK_SCRAMBLING_DISABLE | DP_TRAINING_PATTERN_1;
+ } else {
+ aux_offset = mtk_dp->train_info.channel_eq_pattern;
+
+ switch (mtk_dp->train_info.channel_eq_pattern) {
+ case DP_TRAINING_PATTERN_4:
+ pattern = 4;
+ break;
+ case DP_TRAINING_PATTERN_3:
+ pattern = 3;
+ aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+ break;
+ case DP_TRAINING_PATTERN_2:
+ default:
+ pattern = 2;
+ aux_offset |= DP_LINK_SCRAMBLING_DISABLE;
+ break;
+ }
+ }
+
+ mtk_dp_train_set_pattern(mtk_dp, pattern);
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET, aux_offset);
+}
+
+static int mtk_dp_train_setting(struct mtk_dp *mtk_dp, u8 target_link_rate,
+ u8 target_lane_count)
+{
+ int ret;
+
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LINK_BW_SET, target_link_rate);
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_LANE_COUNT_SET,
+ target_lane_count | DP_LANE_COUNT_ENHANCED_FRAME_EN);
+
+ if (mtk_dp->train_info.sink_ssc)
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_DOWNSPREAD_CTRL,
+ DP_SPREAD_AMP_0_5);
+
+ mtk_dp_set_lanes(mtk_dp, target_lane_count / 2);
+ ret = mtk_dp_phy_configure(mtk_dp, target_link_rate, target_lane_count);
+ if (ret)
+ return ret;
+
+ dev_dbg(mtk_dp->dev,
+ "Link train target_link_rate = 0x%x, target_lane_count = 0x%x\n",
+ target_link_rate, target_lane_count);
+
+ return 0;
+}
+
+static int mtk_dp_train_cr(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+ u8 lane_adjust[2] = {};
+ u8 link_status[DP_LINK_STATUS_SIZE] = {};
+ u8 prev_lane_adjust = 0xff;
+ int train_retries = 0;
+ int voltage_retries = 0;
+
+ mtk_dp_pattern(mtk_dp, true);
+
+ /* In DP spec 1.4, the retry count of CR is defined as 10. */
+ do {
+ train_retries++;
+ if (!mtk_dp->train_info.cable_plugged_in) {
+ mtk_dp_train_set_pattern(mtk_dp, 0);
+ return -ENODEV;
+ }
+
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+ lane_adjust, sizeof(lane_adjust));
+ mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+ lane_adjust);
+
+ drm_dp_link_train_clock_recovery_delay(&mtk_dp->aux,
+ mtk_dp->rx_cap);
+
+ /* check link status from sink device */
+ drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+ if (drm_dp_clock_recovery_ok(link_status,
+ target_lane_count)) {
+ dev_dbg(mtk_dp->dev, "Link train CR pass\n");
+ return 0;
+ }
+
+ /*
+ * In DP spec 1.4, if current voltage level is the same
+ * with previous voltage level, we need to retry 5 times.
+ */
+ if (prev_lane_adjust == link_status[4]) {
+ voltage_retries++;
+ /*
+ * Condition of CR fail:
+ * 1. Failed to pass CR using the same voltage
+ * level over five times.
+ * 2. Failed to pass CR when the current voltage
+ * level is the same with previous voltage
+ * level and reach max voltage level (3).
+ */
+ if (voltage_retries > MTK_DP_TRAIN_VOLTAGE_LEVEL_RETRY ||
+ (prev_lane_adjust & DP_ADJUST_VOLTAGE_SWING_LANE0_MASK) == 3) {
+ dev_dbg(mtk_dp->dev, "Link train CR fail\n");
+ break;
+ }
+ } else {
+ /*
+ * If the voltage level is changed, we need to
+ * re-calculate this retry count.
+ */
+ voltage_retries = 0;
+ }
+ prev_lane_adjust = link_status[4];
+ } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+ /* Failed to train CR, and disable pattern. */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+ mtk_dp_train_set_pattern(mtk_dp, 0);
+
+ return -ETIMEDOUT;
+}
+
+static int mtk_dp_train_eq(struct mtk_dp *mtk_dp, u8 target_lane_count)
+{
+ u8 lane_adjust[2] = {};
+ u8 link_status[DP_LINK_STATUS_SIZE] = {};
+ int train_retries = 0;
+
+ mtk_dp_pattern(mtk_dp, false);
+
+ do {
+ train_retries++;
+ if (!mtk_dp->train_info.cable_plugged_in) {
+ mtk_dp_train_set_pattern(mtk_dp, 0);
+ return -ENODEV;
+ }
+
+ drm_dp_dpcd_read(&mtk_dp->aux, DP_ADJUST_REQUEST_LANE0_1,
+ lane_adjust, sizeof(lane_adjust));
+ mtk_dp_train_update_swing_pre(mtk_dp, target_lane_count,
+ lane_adjust);
+
+ drm_dp_link_train_channel_eq_delay(&mtk_dp->aux,
+ mtk_dp->rx_cap);
+
+ /* check link status from sink device */
+ drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status);
+ if (drm_dp_channel_eq_ok(link_status, target_lane_count)) {
+ dev_dbg(mtk_dp->dev, "Link train EQ pass\n");
+
+ /* Training done, and disable pattern. */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+ mtk_dp_train_set_pattern(mtk_dp, 0);
+ return 0;
+ }
+ dev_dbg(mtk_dp->dev, "Link train EQ fail\n");
+ } while (train_retries < MTK_DP_TRAIN_DOWNSCALE_RETRY);
+
+ /* Failed to train EQ, and disable pattern. */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_TRAINING_PATTERN_SET,
+ DP_TRAINING_PATTERN_DISABLE);
+ mtk_dp_train_set_pattern(mtk_dp, 0);
+
+ return -ETIMEDOUT;
+}
+
+static int mtk_dp_parse_capabilities(struct mtk_dp *mtk_dp)
+{
+ u8 val;
+ ssize_t ret;
+
+ drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap);
+
+ if (drm_dp_tps4_supported(mtk_dp->rx_cap))
+ mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_4;
+ else if (drm_dp_tps3_supported(mtk_dp->rx_cap))
+ mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_3;
+ else
+ mtk_dp->train_info.channel_eq_pattern = DP_TRAINING_PATTERN_2;
+
+ mtk_dp->train_info.sink_ssc = drm_dp_max_downspread(mtk_dp->rx_cap);
+
+ ret = drm_dp_dpcd_readb(&mtk_dp->aux, DP_MSTM_CAP, &val);
+ if (ret < 1) {
+ drm_err(mtk_dp->drm_dev, "Read mstm cap failed\n");
+ return ret == 0 ? -EIO : ret;
+ }
+
+ if (val & DP_MST_CAP) {
+ /* Clear DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0 */
+ ret = drm_dp_dpcd_readb(&mtk_dp->aux,
+ DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+ &val);
+ if (ret < 1) {
+ drm_err(mtk_dp->drm_dev, "Read irq vector failed\n");
+ return ret == 0 ? -EIO : ret;
+ }
+
+ if (val)
+ drm_dp_dpcd_writeb(&mtk_dp->aux,
+ DP_DEVICE_SERVICE_IRQ_VECTOR_ESI0,
+ val);
+ }
+
+ return 0;
+}
+
+static bool mtk_dp_edid_parse_audio_capabilities(struct mtk_dp *mtk_dp,
+ struct mtk_dp_audio_cfg *cfg)
+{
+ if (!mtk_dp->data->audio_supported)
+ return false;
+
+ if (mtk_dp->info.audio_cur_cfg.sad_count <= 0) {
+ drm_info(mtk_dp->drm_dev, "The SADs is NULL\n");
+ return false;
+ }
+
+ return true;
+}
+
+static void mtk_dp_train_change_mode(struct mtk_dp *mtk_dp)
+{
+ phy_reset(mtk_dp->phy);
+ mtk_dp_reset_swing_pre_emphasis(mtk_dp);
+}
+
+static int mtk_dp_training(struct mtk_dp *mtk_dp)
+{
+ int ret;
+ u8 lane_count, link_rate, train_limit, max_link_rate;
+
+ link_rate = min_t(u8, mtk_dp->max_linkrate,
+ mtk_dp->rx_cap[DP_MAX_LINK_RATE]);
+ max_link_rate = link_rate;
+ lane_count = min_t(u8, mtk_dp->max_lanes,
+ drm_dp_max_lane_count(mtk_dp->rx_cap));
+
+ /*
+ * TPS are generated by the hardware pattern generator. From the
+ * hardware setting we need to disable this scramble setting before
+ * use the TPS pattern generator.
+ */
+ mtk_dp_training_set_scramble(mtk_dp, false);
+
+ for (train_limit = 6; train_limit > 0; train_limit--) {
+ mtk_dp_train_change_mode(mtk_dp);
+
+ ret = mtk_dp_train_setting(mtk_dp, link_rate, lane_count);
+ if (ret)
+ return ret;
+
+ ret = mtk_dp_train_cr(mtk_dp, lane_count);
+ if (ret == -ENODEV) {
+ return ret;
+ } else if (ret) {
+ /* reduce link rate */
+ switch (link_rate) {
+ case DP_LINK_BW_1_62:
+ lane_count = lane_count / 2;
+ link_rate = max_link_rate;
+ if (lane_count == 0)
+ return -EIO;
+ break;
+ case DP_LINK_BW_2_7:
+ link_rate = DP_LINK_BW_1_62;
+ break;
+ case DP_LINK_BW_5_4:
+ link_rate = DP_LINK_BW_2_7;
+ break;
+ case DP_LINK_BW_8_1:
+ link_rate = DP_LINK_BW_5_4;
+ break;
+ default:
+ return -EINVAL;
+ };
+ continue;
+ }
+
+ ret = mtk_dp_train_eq(mtk_dp, lane_count);
+ if (ret == -ENODEV) {
+ return ret;
+ } else if (ret) {
+ /* reduce lane count */
+ if (lane_count == 0)
+ return -EIO;
+ lane_count /= 2;
+ continue;
+ }
+
+ /* if we can run to this, training is done. */
+ break;
+ }
+
+ if (train_limit == 0)
+ return -ETIMEDOUT;
+
+ mtk_dp->train_info.link_rate = link_rate;
+ mtk_dp->train_info.lane_count = lane_count;
+
+ /*
+ * After training done, we need to output normal stream instead of TPS,
+ * so we need to enable scramble.
+ */
+ mtk_dp_training_set_scramble(mtk_dp, true);
+ mtk_dp_set_enhanced_frame_mode(mtk_dp);
+
+ return 0;
+}
+
+static void mtk_dp_video_enable(struct mtk_dp *mtk_dp, bool enable)
+{
+ /* the mute sequence is different between enable and disable */
+ if (enable) {
+ mtk_dp_msa_bypass_enable(mtk_dp, false);
+ mtk_dp_pg_enable(mtk_dp, false);
+ mtk_dp_set_tx_out(mtk_dp);
+ mtk_dp_video_mute(mtk_dp, false);
+ } else {
+ mtk_dp_video_mute(mtk_dp, true);
+ mtk_dp_pg_enable(mtk_dp, true);
+ mtk_dp_msa_bypass_enable(mtk_dp, true);
+ }
+}
+
+static void mtk_dp_audio_sdp_setup(struct mtk_dp *mtk_dp,
+ struct mtk_dp_audio_cfg *cfg)
+{
+ struct dp_sdp sdp;
+ struct hdmi_audio_infoframe frame;
+
+ hdmi_audio_infoframe_init(&frame);
+ frame.coding_type = HDMI_AUDIO_CODING_TYPE_PCM;
+ frame.channels = cfg->channels;
+ frame.sample_frequency = cfg->sample_rate;
+
+ switch (cfg->word_length_bits) {
+ case 16:
+ frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_16;
+ break;
+ case 20:
+ frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_20;
+ break;
+ case 24:
+ default:
+ frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_24;
+ break;
+ }
+
+ hdmi_audio_infoframe_pack_for_dp(&frame, &sdp, MTK_DP_VERSION);
+
+ mtk_dp_audio_sdp_asp_set_channels(mtk_dp, cfg->channels);
+ mtk_dp_setup_sdp_aui(mtk_dp, &sdp);
+}
+
+static void mtk_dp_audio_setup(struct mtk_dp *mtk_dp,
+ struct mtk_dp_audio_cfg *cfg)
+{
+ mtk_dp_audio_sdp_setup(mtk_dp, cfg);
+ mtk_dp_audio_channel_status_set(mtk_dp, cfg);
+
+ mtk_dp_audio_setup_channels(mtk_dp, cfg);
+ mtk_dp_audio_set_divider(mtk_dp);
+}
+
+static int mtk_dp_video_config(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_config_mn_mode(mtk_dp);
+ mtk_dp_set_msa(mtk_dp);
+ mtk_dp_set_color_depth(mtk_dp);
+ return mtk_dp_set_color_format(mtk_dp, mtk_dp->info.format);
+}
+
+static void mtk_dp_init_port(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_set_idle_pattern(mtk_dp, true);
+ mtk_dp_initialize_priv_data(mtk_dp);
+
+ mtk_dp_initialize_settings(mtk_dp);
+ mtk_dp_initialize_aux_settings(mtk_dp);
+ mtk_dp_initialize_digital_settings(mtk_dp);
+
+ mtk_dp_update_bits(mtk_dp, MTK_DP_AUX_P0_3690,
+ RX_REPLY_COMPLETE_MODE_AUX_TX_P0,
+ RX_REPLY_COMPLETE_MODE_AUX_TX_P0);
+ mtk_dp_initialize_hpd_detect_settings(mtk_dp);
+
+ mtk_dp_digital_sw_reset(mtk_dp);
+}
+
+static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev)
+{
+ struct mtk_dp *mtk_dp = dev;
+ unsigned long flags;
+ u32 status;
+
+ if (mtk_dp->need_debounce && mtk_dp->train_info.cable_plugged_in)
+ msleep(100);
+
+ spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+ status = mtk_dp->irq_thread_handle;
+ mtk_dp->irq_thread_handle = 0;
+ spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+ if (status & MTK_DP_THREAD_CABLE_STATE_CHG) {
+ drm_helper_hpd_irq_event(mtk_dp->bridge.dev);
+
+ if (!mtk_dp->train_info.cable_plugged_in) {
+ mtk_dp_disable_sdp_aui(mtk_dp);
+ memset(&mtk_dp->info.audio_cur_cfg, 0,
+ sizeof(mtk_dp->info.audio_cur_cfg));
+
+ mtk_dp->need_debounce = false;
+ mod_timer(&mtk_dp->debounce_timer,
+ jiffies + msecs_to_jiffies(100) - 1);
+ }
+ }
+
+ if (status & MTK_DP_THREAD_HPD_EVENT)
+ dev_dbg(mtk_dp->dev, "Receive IRQ from sink devices\n");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev)
+{
+ struct mtk_dp *mtk_dp = dev;
+ bool cable_sta_chg = false;
+ unsigned long flags;
+ u32 irq_status = mtk_dp_swirq_get_clear(mtk_dp) |
+ mtk_dp_hwirq_get_clear(mtk_dp);
+
+ if (!irq_status)
+ return IRQ_HANDLED;
+
+ spin_lock_irqsave(&mtk_dp->irq_thread_lock, flags);
+
+ if (irq_status & MTK_DP_HPD_INTERRUPT)
+ mtk_dp->irq_thread_handle |= MTK_DP_THREAD_HPD_EVENT;
+
+ /* Cable state is changed. */
+ if (irq_status != MTK_DP_HPD_INTERRUPT) {
+ mtk_dp->irq_thread_handle |= MTK_DP_THREAD_CABLE_STATE_CHG;
+ cable_sta_chg = true;
+ }
+
+ spin_unlock_irqrestore(&mtk_dp->irq_thread_lock, flags);
+
+ if (cable_sta_chg) {
+ if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) &
+ HPD_DB_DP_TRANS_P0_MASK))
+ mtk_dp->train_info.cable_plugged_in = true;
+ else
+ mtk_dp->train_info.cable_plugged_in = false;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int mtk_dp_dt_parse(struct mtk_dp *mtk_dp,
+ struct platform_device *pdev)
+{
+ struct device_node *endpoint;
+ struct device *dev = &pdev->dev;
+ int ret;
+ void __iomem *base;
+ u32 linkrate;
+ int len;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ mtk_dp->regs = devm_regmap_init_mmio(dev, base, &mtk_dp_regmap_config);
+ if (IS_ERR(mtk_dp->regs))
+ return PTR_ERR(mtk_dp->regs);
+
+ endpoint = of_graph_get_endpoint_by_regs(pdev->dev.of_node, 1, -1);
+ len = of_property_count_elems_of_size(endpoint,
+ "data-lanes", sizeof(u32));
+ if (len < 0 || len > 4 || len == 3) {
+ dev_err(dev, "invalid data lane size: %d\n", len);
+ return -EINVAL;
+ }
+
+ mtk_dp->max_lanes = len;
+
+ ret = device_property_read_u32(dev, "max-linkrate-mhz", &linkrate);
+ if (ret) {
+ dev_err(dev, "failed to read max linkrate: %d\n", ret);
+ return ret;
+ }
+
+ mtk_dp->max_linkrate = drm_dp_link_rate_to_bw_code(linkrate * 100);
+
+ return 0;
+}
+
+static void mtk_dp_update_plugged_status(struct mtk_dp *mtk_dp)
+{
+ mutex_lock(&mtk_dp->update_plugged_status_lock);
+ if (mtk_dp->plugged_cb && mtk_dp->codec_dev)
+ mtk_dp->plugged_cb(mtk_dp->codec_dev,
+ mtk_dp->enabled &
+ mtk_dp->info.audio_cur_cfg.detect_monitor);
+ mutex_unlock(&mtk_dp->update_plugged_status_lock);
+}
+
+static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge *bridge)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ enum drm_connector_status ret = connector_status_disconnected;
+ bool enabled = mtk_dp->enabled;
+ u8 sink_count = 0;
+
+ if (mtk_dp->train_info.cable_plugged_in) {
+ if (!enabled) {
+ /* power on aux */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL_LANE,
+ DP_PWR_STATE_MASK);
+
+ /* power on panel */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+ usleep_range(2000, 5000);
+ }
+ /*
+ * Some dongles still source HPD when they do not connect to any
+ * sink device. To avoid this, we need to read the sink count
+ * to make sure we do connect to sink devices. After this detect
+ * function, we just need to check the HPD connection to check
+ * whether we connect to a sink device.
+ */
+ drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, &sink_count);
+ if (DP_GET_SINK_COUNT(sink_count))
+ ret = connector_status_connected;
+
+ if (!enabled) {
+ /* power off panel */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+ usleep_range(2000, 3000);
+
+ /* power off aux */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL,
+ DP_PWR_STATE_MASK);
+ }
+ }
+ return ret;
+}
+
+static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge,
+ struct drm_connector *connector)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ bool enabled = mtk_dp->enabled;
+ struct edid *new_edid = NULL;
+ struct mtk_dp_audio_cfg *audio_caps = &mtk_dp->info.audio_cur_cfg;
+ struct cea_sad *sads;
+
+ if (!enabled) {
+ drm_bridge_chain_pre_enable(bridge);
+
+ /* power on aux */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL_LANE,
+ DP_PWR_STATE_MASK);
+
+ /* power on panel */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+ usleep_range(2000, 5000);
+ }
+
+ new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc);
+
+ /*
+ * Parse capability here to let atomic_get_input_bus_fmts and
+ * mode_valid use the capability to calculate sink bitrates.
+ */
+ if (mtk_dp_parse_capabilities(mtk_dp)) {
+ drm_err(mtk_dp->drm_dev, "Can't parse capabilities\n");
+ new_edid = NULL;
+ }
+
+ if (new_edid) {
+ audio_caps->sad_count = drm_edid_to_sad(new_edid, &sads);
+ audio_caps->detect_monitor = drm_detect_monitor_audio(new_edid);
+ }
+
+ if (!enabled) {
+ /* power off panel */
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+ usleep_range(2000, 3000);
+
+ /* power off aux */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL,
+ DP_PWR_STATE_MASK);
+
+ drm_bridge_chain_post_disable(bridge);
+ }
+
+ return new_edid;
+}
+
+static ssize_t mtk_dp_aux_transfer(struct drm_dp_aux *mtk_aux,
+ struct drm_dp_aux_msg *msg)
+{
+ struct mtk_dp *mtk_dp;
+ bool is_read;
+ u8 request;
+ size_t accessed_bytes = 0;
+ int ret;
+
+ mtk_dp = container_of(mtk_aux, struct mtk_dp, aux);
+
+ if (!mtk_dp->train_info.cable_plugged_in) {
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ switch (msg->request) {
+ case DP_AUX_I2C_MOT:
+ case DP_AUX_I2C_WRITE:
+ case DP_AUX_NATIVE_WRITE:
+ case DP_AUX_I2C_WRITE_STATUS_UPDATE:
+ case DP_AUX_I2C_WRITE_STATUS_UPDATE | DP_AUX_I2C_MOT:
+ request = msg->request & ~DP_AUX_I2C_WRITE_STATUS_UPDATE;
+ is_read = false;
+ break;
+ case DP_AUX_I2C_READ:
+ case DP_AUX_NATIVE_READ:
+ case DP_AUX_I2C_READ | DP_AUX_I2C_MOT:
+ request = msg->request;
+ is_read = true;
+ break;
+ default:
+ drm_err(mtk_aux->drm_dev, "invalid aux cmd = %d\n",
+ msg->request);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ do {
+ size_t to_access = min_t(size_t, DP_AUX_MAX_PAYLOAD_BYTES,
+ msg->size - accessed_bytes);
+
+ ret = mtk_dp_aux_do_transfer(mtk_dp, is_read, request,
+ msg->address + accessed_bytes,
+ msg->buffer + accessed_bytes,
+ to_access);
+
+ if (ret) {
+ drm_info(mtk_dp->drm_dev,
+ "Failed to do AUX transfer: %d\n", ret);
+ goto err;
+ }
+ accessed_bytes += to_access;
+ } while (accessed_bytes < msg->size);
+
+ msg->reply = DP_AUX_NATIVE_REPLY_ACK | DP_AUX_I2C_REPLY_ACK;
+ return msg->size;
+err:
+ msg->reply = DP_AUX_NATIVE_REPLY_NACK | DP_AUX_I2C_REPLY_NACK;
+ return ret;
+}
+
+static int mtk_dp_poweron(struct mtk_dp *mtk_dp)
+{
+ int ret;
+
+ ret = phy_init(mtk_dp->phy);
+ if (ret) {
+ dev_err(mtk_dp->dev, "Failed to initialize phy: %d\n", ret);
+ return ret;
+ }
+
+ mtk_dp_init_port(mtk_dp);
+ mtk_dp_power_enable(mtk_dp);
+
+ return 0;
+}
+
+static void mtk_dp_poweroff(struct mtk_dp *mtk_dp)
+{
+ mtk_dp_power_disable(mtk_dp);
+ phy_exit(mtk_dp->phy);
+}
+
+static int mtk_dp_bridge_attach(struct drm_bridge *bridge,
+ enum drm_bridge_attach_flags flags)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ int ret;
+
+ if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) {
+ dev_err(mtk_dp->dev, "Driver does not provide a connector!");
+ return -EINVAL;
+ }
+
+ mtk_dp->aux.drm_dev = bridge->dev;
+ ret = drm_dp_aux_register(&mtk_dp->aux);
+ if (ret) {
+ dev_err(mtk_dp->dev,
+ "failed to register DP AUX channel: %d\n", ret);
+ return ret;
+ }
+
+ ret = mtk_dp_poweron(mtk_dp);
+ if (ret)
+ goto err_aux_register;
+
+ if (mtk_dp->next_bridge) {
+ ret = drm_bridge_attach(bridge->encoder, mtk_dp->next_bridge,
+ &mtk_dp->bridge, flags);
+ if (ret) {
+ drm_warn(mtk_dp->drm_dev,
+ "Failed to attach external bridge: %d\n", ret);
+ goto err_bridge_attach;
+ }
+ }
+
+ mtk_dp->drm_dev = bridge->dev;
+
+ mtk_dp_hwirq_enable(mtk_dp, true);
+
+ return 0;
+
+err_bridge_attach:
+ mtk_dp_poweroff(mtk_dp);
+err_aux_register:
+ drm_dp_aux_unregister(&mtk_dp->aux);
+ return ret;
+}
+
+static void mtk_dp_bridge_detach(struct drm_bridge *bridge)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+ mtk_dp_hwirq_enable(mtk_dp, false);
+ mtk_dp->drm_dev = NULL;
+ mtk_dp_poweroff(mtk_dp);
+ drm_dp_aux_unregister(&mtk_dp->aux);
+}
+
+static void mtk_dp_bridge_atomic_enable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ int ret;
+
+ mtk_dp->conn = drm_atomic_get_new_connector_for_encoder(old_state->base.state,
+ bridge->encoder);
+ if (!mtk_dp->conn) {
+ drm_err(mtk_dp->drm_dev,
+ "Can't enable bridge as connector is missing\n");
+ return;
+ }
+
+ /* power on aux */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL_LANE,
+ DP_PWR_STATE_MASK);
+
+ if (mtk_dp->train_info.cable_plugged_in) {
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D0);
+ usleep_range(2000, 5000);
+ }
+
+ /* Training */
+ ret = mtk_dp_training(mtk_dp);
+ if (ret) {
+ drm_err(mtk_dp->drm_dev, "Training failed, %d\n", ret);
+ goto power_off_aux;
+ }
+
+ ret = mtk_dp_video_config(mtk_dp);
+ if (ret)
+ goto power_off_aux;
+
+ mtk_dp_video_enable(mtk_dp, true);
+
+ mtk_dp->audio_enable =
+ mtk_dp_edid_parse_audio_capabilities(mtk_dp,
+ &mtk_dp->info.audio_cur_cfg);
+ if (mtk_dp->audio_enable) {
+ mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+ mtk_dp_audio_mute(mtk_dp, false);
+ } else {
+ memset(&mtk_dp->info.audio_cur_cfg, 0,
+ sizeof(mtk_dp->info.audio_cur_cfg));
+ }
+
+ mtk_dp->enabled = true;
+ mtk_dp_update_plugged_status(mtk_dp);
+
+ return;
+power_off_aux:
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL,
+ DP_PWR_STATE_MASK);
+}
+
+static void mtk_dp_bridge_atomic_disable(struct drm_bridge *bridge,
+ struct drm_bridge_state *old_state)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+
+ mtk_dp->enabled = false;
+ mtk_dp_update_plugged_status(mtk_dp);
+ mtk_dp_video_enable(mtk_dp, false);
+ mtk_dp_audio_mute(mtk_dp, true);
+
+ if (mtk_dp->train_info.cable_plugged_in) {
+ drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, DP_SET_POWER_D3);
+ usleep_range(2000, 3000);
+ }
+
+ /* power off aux */
+ mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE,
+ DP_PWR_STATE_BANDGAP_TPLL,
+ DP_PWR_STATE_MASK);
+
+ /* Ensure the sink is muted */
+ msleep(20);
+}
+
+static enum drm_mode_status
+mtk_dp_bridge_mode_valid(struct drm_bridge *bridge,
+ const struct drm_display_info *info,
+ const struct drm_display_mode *mode)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ u32 bpp = info->color_formats & DRM_COLOR_FORMAT_YCBCR422 ? 16 : 24;
+ u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+ drm_dp_max_lane_count(mtk_dp->rx_cap),
+ drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+ mtk_dp->max_lanes);
+
+ if (rate < mode->clock * bpp / 8)
+ return MODE_CLOCK_HIGH;
+
+ return MODE_OK;
+}
+
+static u32 *mtk_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ unsigned int *num_output_fmts)
+{
+ u32 *output_fmts;
+
+ *num_output_fmts = 0;
+ output_fmts = kmalloc(sizeof(*output_fmts), GFP_KERNEL);
+ if (!output_fmts)
+ return NULL;
+ *num_output_fmts = 1;
+ output_fmts[0] = MEDIA_BUS_FMT_FIXED;
+ return output_fmts;
+}
+
+static const u32 mt8195_input_fmts[] = {
+ MEDIA_BUS_FMT_RGB888_1X24,
+ MEDIA_BUS_FMT_YUV8_1X24,
+ MEDIA_BUS_FMT_YUYV8_1X16,
+};
+
+static u32 *mtk_dp_bridge_atomic_get_input_bus_fmts(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state,
+ u32 output_fmt,
+ unsigned int *num_input_fmts)
+{
+ u32 *input_fmts;
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ struct drm_display_mode *mode = &crtc_state->adjusted_mode;
+ struct drm_display_info *display_info =
+ &conn_state->connector->display_info;
+ u32 rate = min_t(u32, drm_dp_max_link_rate(mtk_dp->rx_cap) *
+ drm_dp_max_lane_count(mtk_dp->rx_cap),
+ drm_dp_bw_code_to_link_rate(mtk_dp->max_linkrate) *
+ mtk_dp->max_lanes);
+
+ *num_input_fmts = 0;
+
+ /*
+ * If the linkrate is smaller than datarate of RGB888, larger than
+ * datarate of YUV422 and sink device supports YUV422, we output YUV422
+ * format. Use this condition, we can support more resolution.
+ */
+ if ((rate < (mode->clock * 24 / 8)) &&
+ (rate > (mode->clock * 16 / 8)) &&
+ (display_info->color_formats & DRM_COLOR_FORMAT_YCBCR422)) {
+ input_fmts = kcalloc(1, sizeof(*input_fmts), GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+ *num_input_fmts = 1;
+ input_fmts[0] = MEDIA_BUS_FMT_YUYV8_1X16;
+ } else {
+ input_fmts = kcalloc(ARRAY_SIZE(mt8195_input_fmts),
+ sizeof(*input_fmts),
+ GFP_KERNEL);
+ if (!input_fmts)
+ return NULL;
+
+ *num_input_fmts = ARRAY_SIZE(mt8195_input_fmts);
+ memcpy(input_fmts, mt8195_input_fmts, sizeof(mt8195_input_fmts));
+ }
+
+ return input_fmts;
+}
+
+static int mtk_dp_bridge_atomic_check(struct drm_bridge *bridge,
+ struct drm_bridge_state *bridge_state,
+ struct drm_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge);
+ struct drm_crtc *crtc = conn_state->crtc;
+ unsigned int input_bus_format;
+
+ input_bus_format = bridge_state->input_bus_cfg.format;
+
+ dev_dbg(mtk_dp->dev, "input format 0x%04x, output format 0x%04x\n",
+ bridge_state->input_bus_cfg.format,
+ bridge_state->output_bus_cfg.format);
+
+ if (input_bus_format == MEDIA_BUS_FMT_YUYV8_1X16)
+ mtk_dp->info.format = DP_PIXELFORMAT_YUV422;
+ else
+ mtk_dp->info.format = DP_PIXELFORMAT_RGB;
+
+ if (!crtc) {
+ drm_err(mtk_dp->drm_dev,
+ "Can't enable bridge as connector state doesn't have a crtc\n");
+ return -EINVAL;
+ }
+
+ drm_display_mode_to_videomode(&crtc_state->adjusted_mode, &mtk_dp->info.vm);
+
+ return 0;
+}
+
+static const struct drm_bridge_funcs mtk_dp_bridge_funcs = {
+ .atomic_check = mtk_dp_bridge_atomic_check,
+ .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
+ .atomic_get_output_bus_fmts = mtk_dp_bridge_atomic_get_output_bus_fmts,
+ .atomic_get_input_bus_fmts = mtk_dp_bridge_atomic_get_input_bus_fmts,
+ .atomic_reset = drm_atomic_helper_bridge_reset,
+ .attach = mtk_dp_bridge_attach,
+ .detach = mtk_dp_bridge_detach,
+ .atomic_enable = mtk_dp_bridge_atomic_enable,
+ .atomic_disable = mtk_dp_bridge_atomic_disable,
+ .mode_valid = mtk_dp_bridge_mode_valid,
+ .get_edid = mtk_dp_get_edid,
+ .detect = mtk_dp_bdg_detect,
+};
+
+static void mtk_dp_debounce_timer(struct timer_list *t)
+{
+ struct mtk_dp *mtk_dp = from_timer(mtk_dp, t, debounce_timer);
+
+ mtk_dp->need_debounce = true;
+}
+
+/*
+ * HDMI audio codec callbacks
+ */
+static int mtk_dp_audio_hw_params(struct device *dev, void *data,
+ struct hdmi_codec_daifmt *daifmt,
+ struct hdmi_codec_params *params)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+ if (!mtk_dp->enabled) {
+ dev_err(mtk_dp->dev, "%s, DP is not ready!\n", __func__);
+ return -ENODEV;
+ }
+
+ mtk_dp->info.audio_cur_cfg.channels = params->cea.channels;
+ mtk_dp->info.audio_cur_cfg.sample_rate = params->sample_rate;
+
+ mtk_dp_audio_setup(mtk_dp, &mtk_dp->info.audio_cur_cfg);
+
+ return 0;
+}
+
+static int mtk_dp_audio_startup(struct device *dev, void *data)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+ mtk_dp_audio_mute(mtk_dp, false);
+
+ return 0;
+}
+
+static void mtk_dp_audio_shutdown(struct device *dev, void *data)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+ mtk_dp_audio_mute(mtk_dp, true);
+}
+
+static int mtk_dp_audio_get_eld(struct device *dev, void *data, uint8_t *buf,
+ size_t len)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+ if (mtk_dp->enabled)
+ memcpy(buf, mtk_dp->conn->eld, len);
+ else
+ memset(buf, 0, len);
+
+ return 0;
+}
+
+static int mtk_dp_audio_hook_plugged_cb(struct device *dev, void *data,
+ hdmi_codec_plugged_cb fn,
+ struct device *codec_dev)
+{
+ struct mtk_dp *mtk_dp = data;
+
+ mutex_lock(&mtk_dp->update_plugged_status_lock);
+ mtk_dp->plugged_cb = fn;
+ mtk_dp->codec_dev = codec_dev;
+ mutex_unlock(&mtk_dp->update_plugged_status_lock);
+
+ mtk_dp_update_plugged_status(mtk_dp);
+
+ return 0;
+}
+
+static const struct hdmi_codec_ops mtk_dp_audio_codec_ops = {
+ .hw_params = mtk_dp_audio_hw_params,
+ .audio_startup = mtk_dp_audio_startup,
+ .audio_shutdown = mtk_dp_audio_shutdown,
+ .get_eld = mtk_dp_audio_get_eld,
+ .hook_plugged_cb = mtk_dp_audio_hook_plugged_cb,
+ .no_capture_mute = 1,
+};
+
+static int mtk_dp_register_audio_driver(struct device *dev)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+ struct hdmi_codec_pdata codec_data = {
+ .ops = &mtk_dp_audio_codec_ops,
+ .max_i2s_channels = 8,
+ .i2s = 1,
+ .data = mtk_dp,
+ };
+
+ mtk_dp->audio_pdev = platform_device_register_data(dev,
+ HDMI_CODEC_DRV_NAME,
+ PLATFORM_DEVID_AUTO,
+ &codec_data,
+ sizeof(codec_data));
+ return PTR_ERR_OR_ZERO(mtk_dp->audio_pdev);
+}
+
+static int mtk_dp_probe(struct platform_device *pdev)
+{
+ struct mtk_dp *mtk_dp;
+ struct device *dev = &pdev->dev;
+ int ret, irq_num;
+
+ mtk_dp = devm_kzalloc(dev, sizeof(*mtk_dp), GFP_KERNEL);
+ if (!mtk_dp)
+ return -ENOMEM;
+
+ mtk_dp->dev = dev;
+ mtk_dp->data = (struct mtk_dp_data *)of_device_get_match_data(dev);
+
+ irq_num = platform_get_irq(pdev, 0);
+ if (irq_num < 0)
+ return dev_err_probe(dev, irq_num,
+ "failed to request dp irq resource\n");
+
+ mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, 1, 0);
+ if (IS_ERR(mtk_dp->next_bridge) &&
+ PTR_ERR(mtk_dp->next_bridge) == -ENODEV)
+ mtk_dp->next_bridge = NULL;
+ else if (IS_ERR(mtk_dp->next_bridge))
+ return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge),
+ "Failed to get bridge\n");
+
+ ret = mtk_dp_dt_parse(mtk_dp, pdev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to parse dt\n");
+
+ drm_dp_aux_init(&mtk_dp->aux);
+ mtk_dp->aux.name = "aux_mtk_dp";
+ mtk_dp->aux.transfer = mtk_dp_aux_transfer;
+
+ spin_lock_init(&mtk_dp->irq_thread_lock);
+
+ ret = devm_request_threaded_irq(dev, irq_num, mtk_dp_hpd_event,
+ mtk_dp_hpd_event_thread,
+ IRQ_TYPE_LEVEL_HIGH, dev_name(dev),
+ mtk_dp);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to request mediatek dptx irq\n");
+
+ mutex_init(&mtk_dp->update_plugged_status_lock);
+
+ platform_set_drvdata(pdev, mtk_dp);
+
+ if (mtk_dp->data->audio_supported) {
+ ret = mtk_dp_register_audio_driver(dev);
+ if (ret) {
+ dev_err(dev, "Failed to register audio driver: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ mtk_dp->phy_dev = platform_device_register_data(dev, "mediatek-dp-phy",
+ PLATFORM_DEVID_AUTO,
+ &mtk_dp->regs,
+ sizeof(struct regmap *));
+ if (IS_ERR(mtk_dp->phy_dev))
+ return dev_err_probe(dev, PTR_ERR(mtk_dp->phy_dev),
+ "Failed to create device mediatek-dp-phy\n");
+
+ mtk_dp_get_calibration_data(mtk_dp);
+
+ mtk_dp->phy = devm_phy_get(&mtk_dp->phy_dev->dev, "dp");
+
+ if (IS_ERR(mtk_dp->phy)) {
+ platform_device_unregister(mtk_dp->phy_dev);
+ return dev_err_probe(dev, PTR_ERR(mtk_dp->phy),
+ "Failed to get phy\n");
+ }
+
+ mtk_dp->bridge.funcs = &mtk_dp_bridge_funcs;
+ mtk_dp->bridge.of_node = dev->of_node;
+
+ mtk_dp->bridge.ops =
+ DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | DRM_BRIDGE_OP_HPD;
+ mtk_dp->bridge.type = mtk_dp->data->bridge_type;
+
+ drm_bridge_add(&mtk_dp->bridge);
+
+ mtk_dp->need_debounce = true;
+ timer_setup(&mtk_dp->debounce_timer, mtk_dp_debounce_timer, 0);
+
+ pm_runtime_enable(dev);
+ pm_runtime_get_sync(dev);
+
+ return 0;
+}
+
+static int mtk_dp_remove(struct platform_device *pdev)
+{
+ struct mtk_dp *mtk_dp = platform_get_drvdata(pdev);
+
+ pm_runtime_put(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ del_timer_sync(&mtk_dp->debounce_timer);
+ drm_bridge_remove(&mtk_dp->bridge);
+ platform_device_unregister(mtk_dp->phy_dev);
+ if (mtk_dp->audio_pdev)
+ platform_device_unregister(mtk_dp->audio_pdev);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mtk_dp_suspend(struct device *dev)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+ mtk_dp_power_disable(mtk_dp);
+ mtk_dp_hwirq_enable(mtk_dp, false);
+ pm_runtime_put_sync(dev);
+
+ return 0;
+}
+
+static int mtk_dp_resume(struct device *dev)
+{
+ struct mtk_dp *mtk_dp = dev_get_drvdata(dev);
+
+ pm_runtime_get_sync(dev);
+ mtk_dp_init_port(mtk_dp);
+ mtk_dp_hwirq_enable(mtk_dp, true);
+ mtk_dp_power_enable(mtk_dp);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, mtk_dp_suspend, mtk_dp_resume);
+
+static const struct mtk_dp_data mt8195_edp_data = {
+ .bridge_type = DRM_MODE_CONNECTOR_eDP,
+ .smc_cmd = MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE,
+ .efuse_fmt = mt8195_edp_efuse_fmt,
+ .audio_supported = false,
+};
+
+static const struct mtk_dp_data mt8195_dp_data = {
+ .bridge_type = DRM_MODE_CONNECTOR_DisplayPort,
+ .smc_cmd = MTK_DP_SIP_ATF_VIDEO_UNMUTE,
+ .efuse_fmt = mt8195_dp_efuse_fmt,
+ .audio_supported = true,
+};
+
+static const struct of_device_id mtk_dp_of_match[] = {
+ {
+ .compatible = "mediatek,mt8195-edp-tx",
+ .data = &mt8195_edp_data,
+ },
+ {
+ .compatible = "mediatek,mt8195-dp-tx",
+ .data = &mt8195_dp_data,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_dp_of_match);
+
+struct platform_driver mtk_dp_driver = {
+ .probe = mtk_dp_probe,
+ .remove = mtk_dp_remove,
+ .driver = {
+ .name = "mediatek-drm-dp",
+ .of_match_table = mtk_dp_of_match,
+ .pm = &mtk_dp_pm_ops,
+ },
+};
+
+module_platform_driver(mtk_dp_driver);
+
+MODULE_AUTHOR("Jitao Shi <jitao.shi@mediatek.com>");
+MODULE_AUTHOR("Markus Schneider-Pargmann <msp@baylibre.com>");
+MODULE_AUTHOR("Bo-Chen Chen <rex-bc.chen@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek DisplayPort Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
new file mode 100644
index 000000000000..096ad6572a5e
--- /dev/null
+++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h
@@ -0,0 +1,356 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2019-2022 MediaTek Inc.
+ * Copyright (c) 2022 BayLibre
+ */
+#ifndef _MTK_DP_REG_H_
+#define _MTK_DP_REG_H_
+
+#define SEC_OFFSET 0x4000
+
+#define MTK_DP_HPD_DISCONNECT BIT(1)
+#define MTK_DP_HPD_CONNECT BIT(2)
+#define MTK_DP_HPD_INTERRUPT BIT(3)
+
+/* offset: 0x0 */
+#define DP_PHY_GLB_BIAS_GEN_00 0x0
+#define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(20, 16)
+#define DP_PHY_GLB_DPAUX_TX 0x8
+#define RG_CKM_PT0_CKTX_IMPSEL GENMASK(23, 20)
+#define MTK_DP_0034 0x34
+#define DA_XTP_GLB_CKDET_EN_FORCE_VAL BIT(15)
+#define DA_XTP_GLB_CKDET_EN_FORCE_EN BIT(14)
+#define DA_CKM_INTCKTX_EN_FORCE_VAL BIT(13)
+#define DA_CKM_INTCKTX_EN_FORCE_EN BIT(12)
+#define DA_CKM_CKTX0_EN_FORCE_VAL BIT(11)
+#define DA_CKM_CKTX0_EN_FORCE_EN BIT(10)
+#define DA_CKM_XTAL_CK_FORCE_VAL BIT(9)
+#define DA_CKM_XTAL_CK_FORCE_EN BIT(8)
+#define DA_CKM_BIAS_LPF_EN_FORCE_VAL BIT(7)
+#define DA_CKM_BIAS_LPF_EN_FORCE_EN BIT(6)
+#define DA_CKM_BIAS_EN_FORCE_VAL BIT(5)
+#define DA_CKM_BIAS_EN_FORCE_EN BIT(4)
+#define DA_XTP_GLB_AVD10_ON_FORCE_VAL BIT(3)
+#define DA_XTP_GLB_AVD10_ON_FORCE BIT(2)
+#define DA_XTP_GLB_LDO_EN_FORCE_VAL BIT(1)
+#define DA_XTP_GLB_LDO_EN_FORCE_EN BIT(0)
+#define DP_PHY_LANE_TX_0 0x104
+#define RG_XTP_LN0_TX_IMPSEL_PMOS GENMASK(15, 12)
+#define RG_XTP_LN0_TX_IMPSEL_NMOS GENMASK(19, 16)
+#define DP_PHY_LANE_TX_1 0x204
+#define RG_XTP_LN1_TX_IMPSEL_PMOS GENMASK(15, 12)
+#define RG_XTP_LN1_TX_IMPSEL_NMOS GENMASK(19, 16)
+#define DP_PHY_LANE_TX_2 0x304
+#define RG_XTP_LN2_TX_IMPSEL_PMOS GENMASK(15, 12)
+#define RG_XTP_LN2_TX_IMPSEL_NMOS GENMASK(19, 16)
+#define DP_PHY_LANE_TX_3 0x404
+#define RG_XTP_LN3_TX_IMPSEL_PMOS GENMASK(15, 12)
+#define RG_XTP_LN3_TX_IMPSEL_NMOS GENMASK(19, 16)
+#define MTK_DP_1040 0x1040
+#define RG_DPAUX_RX_VALID_DEGLITCH_EN BIT(2)
+#define RG_XTP_GLB_CKDET_EN BIT(1)
+#define RG_DPAUX_RX_EN BIT(0)
+
+/* offset: TOP_OFFSET (0x2000) */
+#define MTK_DP_TOP_PWR_STATE 0x2000
+#define DP_PWR_STATE_MASK GENMASK(1, 0)
+#define DP_PWR_STATE_BANDGAP BIT(0)
+#define DP_PWR_STATE_BANDGAP_TPLL BIT(1)
+#define DP_PWR_STATE_BANDGAP_TPLL_LANE GENMASK(1, 0)
+#define MTK_DP_TOP_SWING_EMP 0x2004
+#define DP_TX0_VOLT_SWING_MASK GENMASK(1, 0)
+#define DP_TX0_VOLT_SWING_SHIFT 0
+#define DP_TX0_PRE_EMPH_MASK GENMASK(3, 2)
+#define DP_TX0_PRE_EMPH_SHIFT 2
+#define DP_TX1_VOLT_SWING_MASK GENMASK(9, 8)
+#define DP_TX1_VOLT_SWING_SHIFT 8
+#define DP_TX1_PRE_EMPH_MASK GENMASK(11, 10)
+#define DP_TX2_VOLT_SWING_MASK GENMASK(17, 16)
+#define DP_TX2_PRE_EMPH_MASK GENMASK(19, 18)
+#define DP_TX3_VOLT_SWING_MASK GENMASK(25, 24)
+#define DP_TX3_PRE_EMPH_MASK GENMASK(27, 26)
+#define MTK_DP_TOP_RESET_AND_PROBE 0x2020
+#define SW_RST_B_PHYD BIT(4)
+#define MTK_DP_TOP_IRQ_MASK 0x202c
+#define IRQ_MASK_AUX_TOP_IRQ BIT(2)
+#define MTK_DP_TOP_MEM_PD 0x2038
+#define MEM_ISO_EN BIT(0)
+#define FUSE_SEL BIT(2)
+
+/* offset: ENC0_OFFSET (0x3000) */
+#define MTK_DP_ENC0_P0_3000 0x3000
+#define LANE_NUM_DP_ENC0_P0_MASK GENMASK(1, 0)
+#define VIDEO_MUTE_SW_DP_ENC0_P0 BIT(2)
+#define VIDEO_MUTE_SEL_DP_ENC0_P0 BIT(3)
+#define ENHANCED_FRAME_EN_DP_ENC0_P0 BIT(4)
+#define MTK_DP_ENC0_P0_3004 0x3004
+#define VIDEO_M_CODE_SEL_DP_ENC0_P0_MASK BIT(8)
+#define DP_TX_ENCODER_4P_RESET_SW_DP_ENC0_P0 BIT(9)
+#define MTK_DP_ENC0_P0_3010 0x3010
+#define HTOTAL_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3014 0x3014
+#define VTOTAL_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3018 0x3018
+#define HSTART_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_301C 0x301c
+#define VSTART_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3020 0x3020
+#define HWIDTH_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3024 0x3024
+#define VHEIGHT_SW_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3028 0x3028
+#define HSW_SW_DP_ENC0_P0_MASK GENMASK(14, 0)
+#define HSP_SW_DP_ENC0_P0_MASK BIT(15)
+#define MTK_DP_ENC0_P0_302C 0x302c
+#define VSW_SW_DP_ENC0_P0_MASK GENMASK(14, 0)
+#define VSP_SW_DP_ENC0_P0_MASK BIT(15)
+#define MTK_DP_ENC0_P0_3030 0x3030
+#define HTOTAL_SEL_DP_ENC0_P0 BIT(0)
+#define VTOTAL_SEL_DP_ENC0_P0 BIT(1)
+#define HSTART_SEL_DP_ENC0_P0 BIT(2)
+#define VSTART_SEL_DP_ENC0_P0 BIT(3)
+#define HWIDTH_SEL_DP_ENC0_P0 BIT(4)
+#define VHEIGHT_SEL_DP_ENC0_P0 BIT(5)
+#define HSP_SEL_DP_ENC0_P0 BIT(6)
+#define HSW_SEL_DP_ENC0_P0 BIT(7)
+#define VSP_SEL_DP_ENC0_P0 BIT(8)
+#define VSW_SEL_DP_ENC0_P0 BIT(9)
+#define VBID_AUDIO_MUTE_FLAG_SW_DP_ENC0_P0 BIT(11)
+#define VBID_AUDIO_MUTE_FLAG_SEL_DP_ENC0_P0 BIT(12)
+#define MTK_DP_ENC0_P0_3034 0x3034
+#define MTK_DP_ENC0_P0_3038 0x3038
+#define VIDEO_SOURCE_SEL_DP_ENC0_P0_MASK BIT(11)
+#define MTK_DP_ENC0_P0_303C 0x303c
+#define SRAM_START_READ_THRD_DP_ENC0_P0_MASK GENMASK(5, 0)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_MASK GENMASK(10, 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_16BIT (0 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_12BIT (1 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_10BIT (2 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_8BIT (3 << 8)
+#define VIDEO_COLOR_DEPTH_DP_ENC0_P0_6BIT (4 << 8)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_MASK GENMASK(14, 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_RGB (0 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR422 (1 << 12)
+#define PIXEL_ENCODE_FORMAT_DP_ENC0_P0_YCBCR420 (2 << 12)
+#define VIDEO_MN_GEN_EN_DP_ENC0_P0 BIT(15)
+#define MTK_DP_ENC0_P0_3040 0x3040
+#define SDP_DOWN_CNT_DP_ENC0_P0_VAL 0x20
+#define SDP_DOWN_CNT_INIT_DP_ENC0_P0_MASK GENMASK(11, 0)
+#define MTK_DP_ENC0_P0_304C 0x304c
+#define VBID_VIDEO_MUTE_DP_ENC0_P0_MASK BIT(2)
+#define SDP_VSYNC_RISING_MASK_DP_ENC0_P0_MASK BIT(8)
+#define MTK_DP_ENC0_P0_3064 0x3064
+#define HDE_NUM_LAST_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3088 0x3088
+#define AU_EN_DP_ENC0_P0 BIT(6)
+#define AUDIO_8CH_EN_DP_ENC0_P0_MASK BIT(7)
+#define AUDIO_8CH_SEL_DP_ENC0_P0_MASK BIT(8)
+#define AUDIO_2CH_EN_DP_ENC0_P0_MASK BIT(14)
+#define AUDIO_2CH_SEL_DP_ENC0_P0_MASK BIT(15)
+#define MTK_DP_ENC0_P0_308C 0x308c
+#define CH_STATUS_0_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3090 0x3090
+#define CH_STATUS_1_DP_ENC0_P0_MASK GENMASK(15, 0)
+#define MTK_DP_ENC0_P0_3094 0x3094
+#define CH_STATUS_2_DP_ENC0_P0_MASK GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A0 0x30a0
+#define DP_ENC0_30A0_MASK (BIT(7) | BIT(8) | BIT(12))
+#define MTK_DP_ENC0_P0_30A4 0x30a4
+#define AU_TS_CFG_DP_ENC0_P0_MASK GENMASK(7, 0)
+#define MTK_DP_ENC0_P0_30A8 0x30a8
+#define MTK_DP_ENC0_P0_30BC 0x30bc
+#define ISRC_CONT_DP_ENC0_P0 BIT(0)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MASK GENMASK(10, 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_2 (1 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_4 (2 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_MUL_8 (3 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_2 (5 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_4 (6 << 8)
+#define AUDIO_M_CODE_MULT_DIV_SEL_DP_ENC0_P0_DIV_8 (7 << 8)
+#define MTK_DP_ENC0_P0_30D8 0x30d8
+#define MTK_DP_ENC0_P0_312C 0x312c
+#define ASP_HB2_DP_ENC0_P0_MASK GENMASK(7, 0)
+#define ASP_HB3_DP_ENC0_P0_MASK GENMASK(15, 8)
+#define MTK_DP_ENC0_P0_3130 0x3130
+#define MTK_DP_ENC0_P0_3138 0x3138
+#define MTK_DP_ENC0_P0_3154 0x3154
+#define PGEN_HTOTAL_DP_ENC0_P0_MASK GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3158 0x3158
+#define PGEN_HSYNC_RISING_DP_ENC0_P0_MASK GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_315C 0x315c
+#define PGEN_HSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3160 0x3160
+#define PGEN_HFDE_START_DP_ENC0_P0_MASK GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3164 0x3164
+#define PGEN_HFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK GENMASK(13, 0)
+#define MTK_DP_ENC0_P0_3168 0x3168
+#define PGEN_VTOTAL_DP_ENC0_P0_MASK GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_316C 0x316c
+#define PGEN_VSYNC_RISING_DP_ENC0_P0_MASK GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3170 0x3170
+#define PGEN_VSYNC_PULSE_WIDTH_DP_ENC0_P0_MASK GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3174 0x3174
+#define PGEN_VFDE_START_DP_ENC0_P0_MASK GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_3178 0x3178
+#define PGEN_VFDE_ACTIVE_WIDTH_DP_ENC0_P0_MASK GENMASK(12, 0)
+#define MTK_DP_ENC0_P0_31B0 0x31b0
+#define PGEN_PATTERN_SEL_VAL 4
+#define PGEN_PATTERN_SEL_MASK GENMASK(6, 4)
+#define MTK_DP_ENC0_P0_31EC 0x31ec
+#define AUDIO_CH_SRC_SEL_DP_ENC0_P0 BIT(4)
+#define ISRC1_HB3_DP_ENC0_P0_MASK GENMASK(15, 8)
+
+/* offset: ENC1_OFFSET (0x3200) */
+#define MTK_DP_ENC1_P0_3200 0x3200
+#define MTK_DP_ENC1_P0_3280 0x3280
+#define SDP_PACKET_TYPE_DP_ENC1_P0_MASK GENMASK(4, 0)
+#define SDP_PACKET_W_DP_ENC1_P0 BIT(5)
+#define SDP_PACKET_W_DP_ENC1_P0_MASK BIT(5)
+#define MTK_DP_ENC1_P0_328C 0x328c
+#define VSC_DATA_RDY_VESA_DP_ENC1_P0_MASK BIT(7)
+#define MTK_DP_ENC1_P0_3300 0x3300
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_VAL 2
+#define VIDEO_AFIFO_RDY_SEL_DP_ENC1_P0_MASK GENMASK(9, 8)
+#define MTK_DP_ENC1_P0_3304 0x3304
+#define AU_PRTY_REGEN_DP_ENC1_P0_MASK BIT(8)
+#define AU_CH_STS_REGEN_DP_ENC1_P0_MASK BIT(9)
+#define AUDIO_SAMPLE_PRSENT_REGEN_DP_ENC1_P0_MASK BIT(12)
+#define MTK_DP_ENC1_P0_3324 0x3324
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_MASK GENMASK(9, 8)
+#define AUDIO_SOURCE_MUX_DP_ENC1_P0_DPRX 0
+#define MTK_DP_ENC1_P0_3364 0x3364
+#define SDP_DOWN_CNT_IN_HBLANK_DP_ENC1_P0_VAL 0x20
+#define SDP_DOWN_CNT_INIT_IN_HBLANK_DP_ENC1_P0_MASK GENMASK(11, 0)
+#define FIFO_READ_START_POINT_DP_ENC1_P0_VAL 4
+#define FIFO_READ_START_POINT_DP_ENC1_P0_MASK GENMASK(15, 12)
+#define MTK_DP_ENC1_P0_3368 0x3368
+#define VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 BIT(0)
+#define VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 BIT(4)
+#define SDP_DP13_EN_DP_ENC1_P0 BIT(8)
+#define BS2BS_MODE_DP_ENC1_P0 BIT(12)
+#define BS2BS_MODE_DP_ENC1_P0_MASK GENMASK(13, 12)
+#define BS2BS_MODE_DP_ENC1_P0_VAL 1
+#define DP_ENC1_P0_3368_VAL (VIDEO_SRAM_FIFO_CNT_RESET_SEL_DP_ENC1_P0 | \
+ VIDEO_STABLE_CNT_THRD_DP_ENC1_P0 | \
+ SDP_DP13_EN_DP_ENC1_P0 | \
+ BS2BS_MODE_DP_ENC1_P0)
+#define MTK_DP_ENC1_P0_33F4 0x33f4
+#define DP_ENC_DUMMY_RW_1_AUDIO_RST_EN BIT(0)
+#define DP_ENC_DUMMY_RW_1 BIT(9)
+
+/* offset: TRANS_OFFSET (0x3400) */
+#define MTK_DP_TRANS_P0_3400 0x3400
+#define PATTERN1_EN_DP_TRANS_P0_MASK BIT(12)
+#define PATTERN2_EN_DP_TRANS_P0_MASK BIT(13)
+#define PATTERN3_EN_DP_TRANS_P0_MASK BIT(14)
+#define PATTERN4_EN_DP_TRANS_P0_MASK BIT(15)
+#define MTK_DP_TRANS_P0_3404 0x3404
+#define DP_SCR_EN_DP_TRANS_P0_MASK BIT(0)
+#define MTK_DP_TRANS_P0_340C 0x340c
+#define DP_TX_TRANSMITTER_4P_RESET_SW_DP_TRANS_P0 BIT(13)
+#define MTK_DP_TRANS_P0_3410 0x3410
+#define HPD_DEB_THD_DP_TRANS_P0_MASK GENMASK(3, 0)
+#define HPD_INT_THD_DP_TRANS_P0_MASK GENMASK(7, 4)
+#define HPD_INT_THD_DP_TRANS_P0_LOWER_500US (2 << 4)
+#define HPD_INT_THD_DP_TRANS_P0_UPPER_1100US (2 << 6)
+#define HPD_DISC_THD_DP_TRANS_P0_MASK GENMASK(11, 8)
+#define HPD_CONN_THD_DP_TRANS_P0_MASK GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_3414 0x3414
+#define HPD_DB_DP_TRANS_P0_MASK BIT(2)
+#define MTK_DP_TRANS_P0_3418 0x3418
+#define IRQ_CLR_DP_TRANS_P0_MASK GENMASK(3, 0)
+#define IRQ_MASK_DP_TRANS_P0_MASK GENMASK(7, 4)
+#define IRQ_MASK_DP_TRANS_P0_DISC_IRQ (BIT(1) << 4)
+#define IRQ_MASK_DP_TRANS_P0_CONN_IRQ (BIT(2) << 4)
+#define IRQ_MASK_DP_TRANS_P0_INT_IRQ (BIT(3) << 4)
+#define IRQ_STATUS_DP_TRANS_P0_MASK GENMASK(15, 12)
+#define MTK_DP_TRANS_P0_342C 0x342c
+#define XTAL_FREQ_DP_TRANS_P0_DEFAULT (BIT(0) | BIT(3) | BIT(5) | BIT(6))
+#define XTAL_FREQ_DP_TRANS_P0_MASK GENMASK(7, 0)
+#define MTK_DP_TRANS_P0_3430 0x3430
+#define HPD_INT_THD_ECO_DP_TRANS_P0_MASK GENMASK(1, 0)
+#define HPD_INT_THD_ECO_DP_TRANS_P0_HIGH_BOUND_EXT BIT(1)
+#define MTK_DP_TRANS_P0_34A4 0x34a4
+#define LANE_NUM_DP_TRANS_P0_MASK GENMASK(3, 2)
+#define MTK_DP_TRANS_P0_3540 0x3540
+#define FEC_EN_DP_TRANS_P0_MASK BIT(0)
+#define FEC_CLOCK_EN_MODE_DP_TRANS_P0 BIT(3)
+#define MTK_DP_TRANS_P0_3580 0x3580
+#define POST_MISC_DATA_LANE0_OV_DP_TRANS_P0_MASK BIT(8)
+#define POST_MISC_DATA_LANE1_OV_DP_TRANS_P0_MASK BIT(9)
+#define POST_MISC_DATA_LANE2_OV_DP_TRANS_P0_MASK BIT(10)
+#define POST_MISC_DATA_LANE3_OV_DP_TRANS_P0_MASK BIT(11)
+#define MTK_DP_TRANS_P0_35C8 0x35c8
+#define SW_IRQ_CLR_DP_TRANS_P0_MASK GENMASK(15, 0)
+#define SW_IRQ_STATUS_DP_TRANS_P0_MASK GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35D0 0x35d0
+#define SW_IRQ_FINAL_STATUS_DP_TRANS_P0_MASK GENMASK(15, 0)
+#define MTK_DP_TRANS_P0_35F0 0x35f0
+#define DP_TRANS_DUMMY_RW_0 BIT(3)
+#define DP_TRANS_DUMMY_RW_0_MASK GENMASK(3, 2)
+
+/* offset: AUX_OFFSET (0x3600) */
+#define MTK_DP_AUX_P0_360C 0x360c
+#define AUX_TIMEOUT_THR_AUX_TX_P0_MASK GENMASK(12, 0)
+#define AUX_TIMEOUT_THR_AUX_TX_P0_VAL 0x1595
+#define MTK_DP_AUX_P0_3614 0x3614
+#define AUX_RX_UI_CNT_THR_AUX_TX_P0_MASK GENMASK(6, 0)
+#define AUX_RX_UI_CNT_THR_AUX_FOR_26M 13
+#define MTK_DP_AUX_P0_3618 0x3618
+#define AUX_RX_FIFO_FULL_AUX_TX_P0_MASK BIT(9)
+#define AUX_RX_FIFO_WRITE_POINTER_AUX_TX_P0_MASK GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3620 0x3620
+#define AUX_RD_MODE_AUX_TX_P0_MASK BIT(9)
+#define AUX_RX_FIFO_READ_PULSE_TX_P0 BIT(8)
+#define AUX_RX_FIFO_READ_DATA_AUX_TX_P0_MASK GENMASK(7, 0)
+#define MTK_DP_AUX_P0_3624 0x3624
+#define AUX_RX_REPLY_COMMAND_AUX_TX_P0_MASK GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3628 0x3628
+#define AUX_RX_PHY_STATE_AUX_TX_P0_MASK GENMASK(9, 0)
+#define AUX_RX_PHY_STATE_AUX_TX_P0_RX_IDLE BIT(0)
+#define MTK_DP_AUX_P0_362C 0x362c
+#define AUX_NO_LENGTH_AUX_TX_P0 BIT(0)
+#define AUX_TX_AUXTX_OV_EN_AUX_TX_P0_MASK BIT(1)
+#define AUX_RESERVED_RW_0_AUX_TX_P0_MASK GENMASK(15, 2)
+#define MTK_DP_AUX_P0_3630 0x3630
+#define AUX_TX_REQUEST_READY_AUX_TX_P0 BIT(3)
+#define MTK_DP_AUX_P0_3634 0x3634
+#define AUX_TX_OVER_SAMPLE_RATE_AUX_TX_P0_MASK GENMASK(15, 8)
+#define AUX_TX_OVER_SAMPLE_RATE_FOR_26M 25
+#define MTK_DP_AUX_P0_3640 0x3640
+#define AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0 BIT(6)
+#define AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 BIT(5)
+#define AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 BIT(4)
+#define AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 BIT(3)
+#define AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 BIT(2)
+#define AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 BIT(1)
+#define AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 BIT(0)
+#define DP_AUX_P0_3640_VAL (AUX_400US_TIMEOUT_IRQ_AUX_TX_P0 | \
+ AUX_RX_DATA_RECV_IRQ_AUX_TX_P0 | \
+ AUX_RX_ADDR_RECV_IRQ_AUX_TX_P0 | \
+ AUX_RX_CMD_RECV_IRQ_AUX_TX_P0 | \
+ AUX_RX_MCCS_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+ AUX_RX_EDID_RECV_COMPLETE_IRQ_AUX_TX_P0 | \
+ AUX_RX_AUX_RECV_COMPLETE_IRQ_AUX_TX_P0)
+#define MTK_DP_AUX_P0_3644 0x3644
+#define MCU_REQUEST_COMMAND_AUX_TX_P0_MASK GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3648 0x3648
+#define MCU_REQUEST_ADDRESS_LSB_AUX_TX_P0_MASK GENMASK(15, 0)
+#define MTK_DP_AUX_P0_364C 0x364c
+#define MCU_REQUEST_ADDRESS_MSB_AUX_TX_P0_MASK GENMASK(3, 0)
+#define MTK_DP_AUX_P0_3650 0x3650
+#define MCU_REQ_DATA_NUM_AUX_TX_P0_MASK GENMASK(15, 12)
+#define PHY_FIFO_RST_AUX_TX_P0_MASK BIT(9)
+#define MCU_ACK_TRAN_COMPLETE_AUX_TX_P0 BIT(8)
+#define MTK_DP_AUX_P0_3658 0x3658
+#define AUX_TX_OV_EN_AUX_TX_P0_MASK BIT(0)
+#define MTK_DP_AUX_P0_3690 0x3690
+#define RX_REPLY_COMPLETE_MODE_AUX_TX_P0 BIT(8)
+#define MTK_DP_AUX_P0_3704 0x3704
+#define AUX_TX_FIFO_WDATA_NEW_MODE_T_AUX_TX_P0_MASK BIT(1)
+#define AUX_TX_FIFO_NEW_MODE_EN_AUX_TX_P0 BIT(2)
+#define MTK_DP_AUX_P0_3708 0x3708
+#define MTK_DP_AUX_P0_37C8 0x37c8
+#define MTK_ATOP_EN_AUX_TX_P0 BIT(0)
+
+#endif /*_MTK_DP_REG_H_*/
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 1d0bafedd585..226d8d4629d2 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -1242,10 +1242,15 @@ void msm_drv_shutdown(struct platform_device *pdev)
struct msm_drm_private *priv = platform_get_drvdata(pdev);
struct drm_device *drm = priv ? priv->dev : NULL;
- if (!priv || !priv->kms)
- return;
-
- drm_atomic_helper_shutdown(drm);
+ /*
+ * Shutdown the hw if we're far enough along where things might be on.
+ * If we run this too early, we'll end up panicking in any variety of
+ * places. Since we don't register the drm device until late in
+ * msm_drm_init, drm_dev->registered is used as an indicator that the
+ * shutdown will be successful.
+ */
+ if (drm && drm->registered)
+ drm_atomic_helper_shutdown(drm);
}
static struct platform_driver msm_platform_driver = {
diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.c b/drivers/gpu/drm/mxsfb/lcdif_drv.c
index 05db135800db..075002ed6fb0 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_drv.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_drv.c
@@ -8,7 +8,6 @@
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/io.h>
-#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
@@ -16,10 +15,8 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
-#include <drm/drm_connector.h>
#include <drm/drm_drv.h>
#include <drm/drm_fb_helper.h>
-#include <drm/drm_fourcc.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_mode_config.h>
@@ -45,23 +42,11 @@ static int lcdif_attach_bridge(struct lcdif_drm_private *lcdif)
{
struct drm_device *drm = lcdif->drm;
struct drm_bridge *bridge;
- struct drm_panel *panel;
int ret;
- ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
- &bridge);
- if (ret)
- return ret;
-
- if (panel) {
- bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
- DRM_MODE_CONNECTOR_DPI);
- if (IS_ERR(bridge))
- return PTR_ERR(bridge);
- }
-
- if (!bridge)
- return -ENODEV;
+ bridge = devm_drm_of_get_bridge(drm->dev, drm->dev->of_node, 0, 0);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
ret = drm_bridge_attach(&lcdif->encoder, bridge, NULL, 0);
if (ret)
diff --git a/drivers/gpu/drm/mxsfb/lcdif_drv.h b/drivers/gpu/drm/mxsfb/lcdif_drv.h
index cb916341e845..6cdba6e20c02 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_drv.h
+++ b/drivers/gpu/drm/mxsfb/lcdif_drv.h
@@ -8,6 +8,7 @@
#ifndef __LCDIF_DRV_H__
#define __LCDIF_DRV_H__
+#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_encoder.h>
diff --git a/drivers/gpu/drm/mxsfb/lcdif_kms.c b/drivers/gpu/drm/mxsfb/lcdif_kms.c
index c7efc0d27f0e..b1092aab1423 100644
--- a/drivers/gpu/drm/mxsfb/lcdif_kms.c
+++ b/drivers/gpu/drm/mxsfb/lcdif_kms.c
@@ -17,9 +17,9 @@
#include <drm/drm_bridge.h>
#include <drm/drm_crtc.h>
#include <drm/drm_encoder.h>
-#include <drm/drm_framebuffer.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
+#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_atomic_helper.h>
#include <drm/drm_gem_dma_helper.h>
#include <drm/drm_plane.h>
@@ -122,8 +122,8 @@ static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags)
writel(ctrl, lcdif->base + LCDC_V8_CTRL);
- writel(DISP_SIZE_DELTA_Y(m->crtc_vdisplay) |
- DISP_SIZE_DELTA_X(m->crtc_hdisplay),
+ writel(DISP_SIZE_DELTA_Y(m->vdisplay) |
+ DISP_SIZE_DELTA_X(m->hdisplay),
lcdif->base + LCDC_V8_DISP_SIZE);
writel(HSYN_PARA_BP_H(m->htotal - m->hsync_end) |
@@ -138,8 +138,8 @@ static void lcdif_set_mode(struct lcdif_drm_private *lcdif, u32 bus_flags)
VSYN_HSYN_WIDTH_PW_H(m->hsync_end - m->hsync_start),
lcdif->base + LCDC_V8_VSYN_HSYN_WIDTH);
- writel(CTRLDESCL0_1_HEIGHT(m->crtc_vdisplay) |
- CTRLDESCL0_1_WIDTH(m->crtc_hdisplay),
+ writel(CTRLDESCL0_1_HEIGHT(m->vdisplay) |
+ CTRLDESCL0_1_WIDTH(m->hdisplay),
lcdif->base + LCDC_V8_CTRLDESCL0_1);
writel(CTRLDESCL0_3_PITCH(lcdif->crtc.primary->state->fb->pitches[0]),
@@ -203,7 +203,7 @@ static void lcdif_crtc_mode_set_nofb(struct lcdif_drm_private *lcdif,
DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
m->crtc_clock,
(int)(clk_get_rate(lcdif->clk) / 1000));
- DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
+ DRM_DEV_DEBUG_DRIVER(drm->dev, "Bridge bus_flags: 0x%08X\n",
bus_flags);
DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c
index c8fedd7d227f..33c97d510999 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.c
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c
@@ -932,6 +932,7 @@ struct nv50_msto {
struct nv50_head *head;
struct nv50_mstc *mstc;
bool disabled;
+ bool enabled;
};
struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder)
@@ -947,57 +948,37 @@ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder)
return msto->mstc->mstm->outp;
}
-static struct drm_dp_payload *
-nv50_msto_payload(struct nv50_msto *msto)
-{
- struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev);
- struct nv50_mstc *mstc = msto->mstc;
- struct nv50_mstm *mstm = mstc->mstm;
- int vcpi = mstc->port->vcpi.vcpi, i;
-
- WARN_ON(!mutex_is_locked(&mstm->mgr.payload_lock));
-
- NV_ATOMIC(drm, "%s: vcpi %d\n", msto->encoder.name, vcpi);
- for (i = 0; i < mstm->mgr.max_payloads; i++) {
- struct drm_dp_payload *payload = &mstm->mgr.payloads[i];
- NV_ATOMIC(drm, "%s: %d: vcpi %d start 0x%02x slots 0x%02x\n",
- mstm->outp->base.base.name, i, payload->vcpi,
- payload->start_slot, payload->num_slots);
- }
-
- for (i = 0; i < mstm->mgr.max_payloads; i++) {
- struct drm_dp_payload *payload = &mstm->mgr.payloads[i];
- if (payload->vcpi == vcpi)
- return payload;
- }
-
- return NULL;
-}
-
static void
-nv50_msto_cleanup(struct nv50_msto *msto)
+nv50_msto_cleanup(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_state *mst_state,
+ struct drm_dp_mst_topology_mgr *mgr,
+ struct nv50_msto *msto)
{
struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev);
- struct nv50_mstc *mstc = msto->mstc;
- struct nv50_mstm *mstm = mstc->mstm;
-
- if (!msto->disabled)
- return;
+ struct drm_dp_mst_atomic_payload *payload =
+ drm_atomic_get_mst_payload_state(mst_state, msto->mstc->port);
NV_ATOMIC(drm, "%s: msto cleanup\n", msto->encoder.name);
- drm_dp_mst_deallocate_vcpi(&mstm->mgr, mstc->port);
-
- msto->mstc = NULL;
- msto->disabled = false;
+ if (msto->disabled) {
+ msto->mstc = NULL;
+ msto->disabled = false;
+ } else if (msto->enabled) {
+ drm_dp_add_payload_part2(mgr, state, payload);
+ msto->enabled = false;
+ }
}
static void
-nv50_msto_prepare(struct nv50_msto *msto)
+nv50_msto_prepare(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_state *mst_state,
+ struct drm_dp_mst_topology_mgr *mgr,
+ struct nv50_msto *msto)
{
struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev);
struct nv50_mstc *mstc = msto->mstc;
struct nv50_mstm *mstm = mstc->mstm;
+ struct drm_dp_mst_atomic_payload *payload;
struct {
struct nv50_disp_mthd_v1 base;
struct nv50_disp_sor_dp_mst_vcpi_v0 vcpi;
@@ -1009,17 +990,21 @@ nv50_msto_prepare(struct nv50_msto *msto)
(0x0100 << msto->head->base.index),
};
- mutex_lock(&mstm->mgr.payload_lock);
-
NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name);
- if (mstc->port->vcpi.vcpi > 0) {
- struct drm_dp_payload *payload = nv50_msto_payload(msto);
- if (payload) {
- args.vcpi.start_slot = payload->start_slot;
- args.vcpi.num_slots = payload->num_slots;
- args.vcpi.pbn = mstc->port->vcpi.pbn;
- args.vcpi.aligned_pbn = mstc->port->vcpi.aligned_pbn;
- }
+
+ payload = drm_atomic_get_mst_payload_state(mst_state, mstc->port);
+
+ // TODO: Figure out if we want to do a better job of handling VCPI allocation failures here?
+ if (msto->disabled) {
+ drm_dp_remove_payload(mgr, mst_state, payload);
+ } else {
+ if (msto->enabled)
+ drm_dp_add_payload_part1(mgr, mst_state, payload);
+
+ args.vcpi.start_slot = payload->vc_start_slot;
+ args.vcpi.num_slots = payload->time_slots;
+ args.vcpi.pbn = payload->pbn;
+ args.vcpi.aligned_pbn = payload->time_slots * mst_state->pbn_div;
}
NV_ATOMIC(drm, "%s: %s: %02x %02x %04x %04x\n",
@@ -1028,7 +1013,6 @@ nv50_msto_prepare(struct nv50_msto *msto)
args.vcpi.pbn, args.vcpi.aligned_pbn);
nvif_mthd(&drm->display->disp.object, 0, &args, sizeof(args));
- mutex_unlock(&mstm->mgr.payload_lock);
}
static int
@@ -1038,6 +1022,7 @@ nv50_msto_atomic_check(struct drm_encoder *encoder,
{
struct drm_atomic_state *state = crtc_state->state;
struct drm_connector *connector = conn_state->connector;
+ struct drm_dp_mst_topology_state *mst_state;
struct nv50_mstc *mstc = nv50_mstc(connector);
struct nv50_mstm *mstm = mstc->mstm;
struct nv50_head_atom *asyh = nv50_head_atom(crtc_state);
@@ -1049,7 +1034,7 @@ nv50_msto_atomic_check(struct drm_encoder *encoder,
if (ret)
return ret;
- if (!crtc_state->mode_changed && !crtc_state->connectors_changed)
+ if (!drm_atomic_crtc_needs_modeset(crtc_state))
return 0;
/*
@@ -1065,8 +1050,18 @@ nv50_msto_atomic_check(struct drm_encoder *encoder,
false);
}
- slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port,
- asyh->dp.pbn, 0);
+ mst_state = drm_atomic_get_mst_topology_state(state, &mstm->mgr);
+ if (IS_ERR(mst_state))
+ return PTR_ERR(mst_state);
+
+ if (!mst_state->pbn_div) {
+ struct nouveau_encoder *outp = mstc->mstm->outp;
+
+ mst_state->pbn_div = drm_dp_get_vc_payload_bw(&mstm->mgr,
+ outp->dp.link_bw, outp->dp.link_nr);
+ }
+
+ slots = drm_dp_atomic_find_time_slots(state, &mstm->mgr, mstc->port, asyh->dp.pbn);
if (slots < 0)
return slots;
@@ -1098,7 +1093,6 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st
struct drm_connector *connector;
struct drm_connector_list_iter conn_iter;
u8 proto;
- bool r;
drm_connector_list_iter_begin(encoder->dev, &conn_iter);
drm_for_each_connector_iter(connector, &conn_iter) {
@@ -1113,10 +1107,6 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st
if (WARN_ON(!mstc))
return;
- r = drm_dp_mst_allocate_vcpi(&mstm->mgr, mstc->port, asyh->dp.pbn, asyh->dp.tu);
- if (!r)
- DRM_DEBUG_KMS("Failed to allocate VCPI\n");
-
if (!mstm->links++)
nv50_outp_acquire(mstm->outp, false /*XXX: MST audio.*/);
@@ -1129,6 +1119,7 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st
nv50_dp_bpc_to_depth(asyh->or.bpc));
msto->mstc = mstc;
+ msto->enabled = true;
mstm->modified = true;
}
@@ -1139,8 +1130,6 @@ nv50_msto_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *s
struct nv50_mstc *mstc = msto->mstc;
struct nv50_mstm *mstm = mstc->mstm;
- drm_dp_mst_reset_vcpi_slots(&mstm->mgr, mstc->port);
-
mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0);
mstm->modified = true;
if (!--mstm->links)
@@ -1255,29 +1244,8 @@ nv50_mstc_atomic_check(struct drm_connector *connector,
{
struct nv50_mstc *mstc = nv50_mstc(connector);
struct drm_dp_mst_topology_mgr *mgr = &mstc->mstm->mgr;
- struct drm_connector_state *new_conn_state =
- drm_atomic_get_new_connector_state(state, connector);
- struct drm_connector_state *old_conn_state =
- drm_atomic_get_old_connector_state(state, connector);
- struct drm_crtc_state *crtc_state;
- struct drm_crtc *new_crtc = new_conn_state->crtc;
-
- if (!old_conn_state->crtc)
- return 0;
-
- /* We only want to free VCPI if this state disables the CRTC on this
- * connector
- */
- if (new_crtc) {
- crtc_state = drm_atomic_get_new_crtc_state(state, new_crtc);
-
- if (!crtc_state ||
- !drm_atomic_crtc_needs_modeset(crtc_state) ||
- crtc_state->enable)
- return 0;
- }
- return drm_dp_atomic_release_vcpi_slots(state, mgr, mstc->port);
+ return drm_dp_atomic_release_time_slots(state, mgr, mstc->port);
}
static int
@@ -1381,7 +1349,9 @@ nv50_mstc_new(struct nv50_mstm *mstm, struct drm_dp_mst_port *port,
}
static void
-nv50_mstm_cleanup(struct nv50_mstm *mstm)
+nv50_mstm_cleanup(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_state *mst_state,
+ struct nv50_mstm *mstm)
{
struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev);
struct drm_encoder *encoder;
@@ -1389,14 +1359,12 @@ nv50_mstm_cleanup(struct nv50_mstm *mstm)
NV_ATOMIC(drm, "%s: mstm cleanup\n", mstm->outp->base.base.name);
drm_dp_check_act_status(&mstm->mgr);
- drm_dp_update_payload_part2(&mstm->mgr);
-
drm_for_each_encoder(encoder, mstm->outp->base.base.dev) {
if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) {
struct nv50_msto *msto = nv50_msto(encoder);
struct nv50_mstc *mstc = msto->mstc;
if (mstc && mstc->mstm == mstm)
- nv50_msto_cleanup(msto);
+ nv50_msto_cleanup(state, mst_state, &mstm->mgr, msto);
}
}
@@ -1404,20 +1372,34 @@ nv50_mstm_cleanup(struct nv50_mstm *mstm)
}
static void
-nv50_mstm_prepare(struct nv50_mstm *mstm)
+nv50_mstm_prepare(struct drm_atomic_state *state,
+ struct drm_dp_mst_topology_state *mst_state,
+ struct nv50_mstm *mstm)
{
struct nouveau_drm *drm = nouveau_drm(mstm->outp->base.base.dev);
struct drm_encoder *encoder;
NV_ATOMIC(drm, "%s: mstm prepare\n", mstm->outp->base.base.name);
- drm_dp_update_payload_part1(&mstm->mgr, 1);
+ /* Disable payloads first */
drm_for_each_encoder(encoder, mstm->outp->base.base.dev) {
if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) {
struct nv50_msto *msto = nv50_msto(encoder);
struct nv50_mstc *mstc = msto->mstc;
- if (mstc && mstc->mstm == mstm)
- nv50_msto_prepare(msto);
+ if (mstc && mstc->mstm == mstm && msto->disabled)
+ nv50_msto_prepare(state, mst_state, &mstm->mgr, msto);
+ }
+ }
+
+ /* Add payloads for new heads, while also updating the start slots of any unmodified (but
+ * active) heads that may have had their VC slots shifted left after the previous step
+ */
+ drm_for_each_encoder(encoder, mstm->outp->base.base.dev) {
+ if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) {
+ struct nv50_msto *msto = nv50_msto(encoder);
+ struct nv50_mstc *mstc = msto->mstc;
+ if (mstc && mstc->mstm == mstm && !msto->disabled)
+ nv50_msto_prepare(state, mst_state, &mstm->mgr, msto);
}
}
@@ -1614,9 +1596,7 @@ nv50_mstm_new(struct nouveau_encoder *outp, struct drm_dp_aux *aux, int aux_max,
mstm->mgr.cbs = &nv50_mstm;
ret = drm_dp_mst_topology_mgr_init(&mstm->mgr, dev, aux, aux_max,
- max_payloads, outp->dcb->dpconf.link_nr,
- drm_dp_bw_code_to_link_rate(outp->dcb->dpconf.link_bw),
- conn_base_id);
+ max_payloads, conn_base_id);
if (ret)
return ret;
@@ -1834,7 +1814,7 @@ nv50_sor_func = {
.destroy = nv50_sor_destroy,
};
-static bool nv50_has_mst(struct nouveau_drm *drm)
+bool nv50_has_mst(struct nouveau_drm *drm)
{
struct nvkm_bios *bios = nvxx_bios(&drm->client.device);
u32 data;
@@ -2068,20 +2048,20 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe)
static void
nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock)
{
+ struct drm_dp_mst_topology_mgr *mgr;
+ struct drm_dp_mst_topology_state *mst_state;
struct nouveau_drm *drm = nouveau_drm(state->dev);
struct nv50_disp *disp = nv50_disp(drm->dev);
struct nv50_core *core = disp->core;
struct nv50_mstm *mstm;
- struct drm_encoder *encoder;
+ int i;
NV_ATOMIC(drm, "commit core %08x\n", interlock[NV50_DISP_INTERLOCK_BASE]);
- drm_for_each_encoder(encoder, drm->dev) {
- if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
- mstm = nouveau_encoder(encoder)->dp.mstm;
- if (mstm && mstm->modified)
- nv50_mstm_prepare(mstm);
- }
+ for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+ mstm = nv50_mstm(mgr);
+ if (mstm->modified)
+ nv50_mstm_prepare(state, mst_state, mstm);
}
core->func->ntfy_init(disp->sync, NV50_DISP_CORE_NTFY);
@@ -2090,12 +2070,10 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock)
disp->core->chan.base.device))
NV_ERROR(drm, "core notifier timeout\n");
- drm_for_each_encoder(encoder, drm->dev) {
- if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
- mstm = nouveau_encoder(encoder)->dp.mstm;
- if (mstm && mstm->modified)
- nv50_mstm_cleanup(mstm);
- }
+ for_each_new_mst_mgr_in_state(state, mgr, mst_state, i) {
+ mstm = nv50_mstm(mgr);
+ if (mstm->modified)
+ nv50_mstm_cleanup(state, mst_state, mstm);
}
}
@@ -2136,6 +2114,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state)
nv50_crc_atomic_stop_reporting(state);
drm_atomic_helper_wait_for_fences(dev, state, false);
drm_atomic_helper_wait_for_dependencies(state);
+ drm_dp_mst_atomic_wait_for_dependencies(state);
drm_atomic_helper_update_legacy_modeset_state(dev, state);
drm_atomic_helper_calc_timestamping_constants(state);
@@ -2616,6 +2595,11 @@ nv50_disp_func = {
.atomic_state_free = nv50_disp_atomic_state_free,
};
+static const struct drm_mode_config_helper_funcs
+nv50_disp_helper_func = {
+ .atomic_commit_setup = drm_dp_mst_atomic_setup_commit,
+};
+
/******************************************************************************
* Init
*****************************************************************************/
@@ -2699,6 +2683,7 @@ nv50_display_create(struct drm_device *dev)
nouveau_display(dev)->fini = nv50_display_fini;
disp->disp = &nouveau_display(dev)->disp;
dev->mode_config.funcs = &nv50_disp_func;
+ dev->mode_config.helper_private = &nv50_disp_helper_func;
dev->mode_config.quirk_addfb_prefer_xbgr_30bpp = true;
dev->mode_config.normalize_zpos = true;
diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h
index 38dec11e7dda..9d66c9c726c3 100644
--- a/drivers/gpu/drm/nouveau/dispnv50/disp.h
+++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h
@@ -106,6 +106,8 @@ void nv50_dmac_destroy(struct nv50_dmac *);
*/
struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder);
+bool nv50_has_mst(struct nouveau_drm *drm);
+
u32 *evo_wait(struct nv50_dmac *, int nr);
void evo_kick(u32 *, struct nv50_dmac *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 8100c75ee731..1991bbb1d05c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -1106,11 +1106,25 @@ nouveau_connector_best_encoder(struct drm_connector *connector)
return NULL;
}
+static int
+nouveau_connector_atomic_check(struct drm_connector *connector, struct drm_atomic_state *state)
+{
+ struct nouveau_connector *nv_conn = nouveau_connector(connector);
+ struct drm_connector_state *conn_state =
+ drm_atomic_get_new_connector_state(state, connector);
+
+ if (!nv_conn->dp_encoder || !nv50_has_mst(nouveau_drm(connector->dev)))
+ return 0;
+
+ return drm_dp_mst_root_conn_atomic_check(conn_state, &nv_conn->dp_encoder->dp.mstm->mgr);
+}
+
static const struct drm_connector_helper_funcs
nouveau_connector_helper_funcs = {
.get_modes = nouveau_connector_get_modes,
.mode_valid = nouveau_connector_mode_valid,
.best_encoder = nouveau_connector_best_encoder,
+ .atomic_check = nouveau_connector_atomic_check,
};
static const struct drm_connector_funcs
@@ -1368,7 +1382,7 @@ nouveau_connector_create(struct drm_device *dev,
return ERR_PTR(-ENOMEM);
}
drm_dp_aux_init(&nv_connector->aux);
- fallthrough;
+ break;
default:
funcs = &nouveau_connector_funcs;
break;
@@ -1431,6 +1445,8 @@ nouveau_connector_create(struct drm_device *dev,
switch (type) {
case DRM_MODE_CONNECTOR_DisplayPort:
+ nv_connector->dp_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP);
+ fallthrough;
case DRM_MODE_CONNECTOR_eDP:
drm_dp_cec_register_connector(&nv_connector->aux, connector);
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 4bf0c703eee7..f4e17ff68bf9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -128,6 +128,9 @@ struct nouveau_connector {
struct drm_dp_aux aux;
+ /* The fixed DP encoder for this connector, if there is one */
+ struct nouveau_encoder *dp_encoder;
+
int dithering_mode;
int scaling_mode;
diff --git a/drivers/gpu/drm/nouveau/nouveau_hwmon.c b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
index 1c3104d20571..a7db7c31064b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hwmon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hwmon.c
@@ -211,75 +211,24 @@ static const struct attribute_group temp1_auto_point_sensor_group = {
#define N_ATTR_GROUPS 3
-static const u32 nouveau_config_chip[] = {
- HWMON_C_UPDATE_INTERVAL,
- 0
-};
-
-static const u32 nouveau_config_in[] = {
- HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | HWMON_I_LABEL,
- 0
-};
-
-static const u32 nouveau_config_temp[] = {
- HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_HYST |
- HWMON_T_CRIT | HWMON_T_CRIT_HYST | HWMON_T_EMERGENCY |
- HWMON_T_EMERGENCY_HYST,
- 0
-};
-
-static const u32 nouveau_config_fan[] = {
- HWMON_F_INPUT,
- 0
-};
-
-static const u32 nouveau_config_pwm[] = {
- HWMON_PWM_INPUT | HWMON_PWM_ENABLE,
- 0
-};
-
-static const u32 nouveau_config_power[] = {
- HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT,
- 0
-};
-
-static const struct hwmon_channel_info nouveau_chip = {
- .type = hwmon_chip,
- .config = nouveau_config_chip,
-};
-
-static const struct hwmon_channel_info nouveau_temp = {
- .type = hwmon_temp,
- .config = nouveau_config_temp,
-};
-
-static const struct hwmon_channel_info nouveau_fan = {
- .type = hwmon_fan,
- .config = nouveau_config_fan,
-};
-
-static const struct hwmon_channel_info nouveau_in = {
- .type = hwmon_in,
- .config = nouveau_config_in,
-};
-
-static const struct hwmon_channel_info nouveau_pwm = {
- .type = hwmon_pwm,
- .config = nouveau_config_pwm,
-};
-
-static const struct hwmon_channel_info nouveau_power = {
- .type = hwmon_power,
- .config = nouveau_config_power,
-};
-
static const struct hwmon_channel_info *nouveau_info[] = {
- &nouveau_chip,
- &nouveau_temp,
- &nouveau_fan,
- &nouveau_in,
- &nouveau_pwm,
- &nouveau_power,
+ HWMON_CHANNEL_INFO(chip,
+ HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT |
+ HWMON_T_MAX | HWMON_T_MAX_HYST |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST |
+ HWMON_T_EMERGENCY | HWMON_T_EMERGENCY_HYST),
+ HWMON_CHANNEL_INFO(fan,
+ HWMON_F_INPUT),
+ HWMON_CHANNEL_INFO(in,
+ HWMON_I_INPUT |
+ HWMON_I_MIN | HWMON_I_MAX |
+ HWMON_I_LABEL),
+ HWMON_CHANNEL_INFO(pwm,
+ HWMON_PWM_INPUT | HWMON_PWM_ENABLE),
+ HWMON_CHANNEL_INFO(power,
+ HWMON_P_INPUT | HWMON_P_CAP_MAX | HWMON_P_CRIT),
NULL
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 2e517cdc24c9..76f8edefa637 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -187,3 +187,32 @@ nouveau_mem_new(struct nouveau_cli *cli, u8 kind, u8 comp,
*res = &mem->base;
return 0;
}
+
+bool
+nouveau_mem_intersects(struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ u32 num_pages = PFN_UP(size);
+
+ /* Don't evict BOs outside of the requested placement range */
+ if (place->fpfn >= (res->start + num_pages) ||
+ (place->lpfn && place->lpfn <= res->start))
+ return false;
+
+ return true;
+}
+
+bool
+nouveau_mem_compatible(struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ u32 num_pages = PFN_UP(size);
+
+ if (res->start < place->fpfn ||
+ (place->lpfn && (res->start + num_pages) > place->lpfn))
+ return false;
+
+ return true;
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.h b/drivers/gpu/drm/nouveau/nouveau_mem.h
index 325551eba5cd..1ee6cdb9ad9b 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.h
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.h
@@ -25,6 +25,12 @@ int nouveau_mem_new(struct nouveau_cli *, u8 kind, u8 comp,
struct ttm_resource **);
void nouveau_mem_del(struct ttm_resource_manager *man,
struct ttm_resource *);
+bool nouveau_mem_intersects(struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size);
+bool nouveau_mem_compatible(struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size);
int nouveau_mem_vram(struct ttm_resource *, bool contig, u8 page);
int nouveau_mem_host(struct ttm_resource *, struct ttm_tt *);
void nouveau_mem_fini(struct nouveau_mem *);
diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
index 85f1f5a0fe5d..9602c30928f2 100644
--- a/drivers/gpu/drm/nouveau/nouveau_ttm.c
+++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
@@ -42,6 +42,24 @@ nouveau_manager_del(struct ttm_resource_manager *man,
nouveau_mem_del(man, reg);
}
+static bool
+nouveau_manager_intersects(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ return nouveau_mem_intersects(res, place, size);
+}
+
+static bool
+nouveau_manager_compatible(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ return nouveau_mem_compatible(res, place, size);
+}
+
static int
nouveau_vram_manager_new(struct ttm_resource_manager *man,
struct ttm_buffer_object *bo,
@@ -73,6 +91,8 @@ nouveau_vram_manager_new(struct ttm_resource_manager *man,
const struct ttm_resource_manager_func nouveau_vram_manager = {
.alloc = nouveau_vram_manager_new,
.free = nouveau_manager_del,
+ .intersects = nouveau_manager_intersects,
+ .compatible = nouveau_manager_compatible,
};
static int
@@ -97,6 +117,8 @@ nouveau_gart_manager_new(struct ttm_resource_manager *man,
const struct ttm_resource_manager_func nouveau_gart_manager = {
.alloc = nouveau_gart_manager_new,
.free = nouveau_manager_del,
+ .intersects = nouveau_manager_intersects,
+ .compatible = nouveau_manager_compatible,
};
static int
@@ -130,6 +152,8 @@ nv04_gart_manager_new(struct ttm_resource_manager *man,
const struct ttm_resource_manager_func nv04_gart_manager = {
.alloc = nv04_gart_manager_new,
.free = nouveau_manager_del,
+ .intersects = nouveau_manager_intersects,
+ .compatible = nouveau_manager_compatible,
};
static int
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c
index a139dafffe06..7c33542f651b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/clk/gm20b.c
@@ -581,7 +581,7 @@ gm20b_clk_prog(struct nvkm_clk *base)
/*
* Interim step for changing DVFS detection settings: low enough
- * frequency to be safe at at DVFS coeff = 0.
+ * frequency to be safe at DVFS coeff = 0.
*
* 1. If voltage is increasing:
* - safe frequency target matches the lowest - old - frequency
diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig
index 584a69f99af6..a582ddd583c2 100644
--- a/drivers/gpu/drm/panel/Kconfig
+++ b/drivers/gpu/drm/panel/Kconfig
@@ -165,8 +165,8 @@ config DRM_PANEL_ILITEK_IL9322
config DRM_PANEL_ILITEK_ILI9341
tristate "Ilitek ILI9341 240x320 QVGA panels"
depends on OF && SPI
- depends on DRM_KMS_HELPER
- depends on DRM_GEM_DMA_HELPER
+ select DRM_KMS_HELPER
+ select DRM_GEM_DMA_HELPER
depends on BACKLIGHT_CLASS_DEVICE
select DRM_MIPI_DBI
help
diff --git a/drivers/gpu/drm/panel/panel-edp.c b/drivers/gpu/drm/panel/panel-edp.c
index 335153bb76e2..060f4f98bc04 100644
--- a/drivers/gpu/drm/panel/panel-edp.c
+++ b/drivers/gpu/drm/panel/panel-edp.c
@@ -53,7 +53,7 @@ struct panel_delay {
* before the HPD signal is reliable. Ideally this is 0 but some panels,
* board designs, or bad pulldown configs can cause a glitch here.
*
- * NOTE: on some old panel data this number appers to be much too big.
+ * NOTE: on some old panel data this number appears to be much too big.
* Presumably some old panels simply didn't have HPD hooked up and put
* the hpd_absent here because this field predates the
* hpd_absent. While that works, it's non-ideal.
@@ -1877,6 +1877,7 @@ static const struct panel_delay delay_200_500_e200 = {
*/
static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('A', 'U', 'O', 0x1062, &delay_200_500_e50, "B120XAN01.0"),
+ EDP_PANEL_ENTRY('A', 'U', 'O', 0x1e9b, &delay_200_500_e50, "B133UAN02.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x405c, &auo_b116xak01.delay, "B116XAK01"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x615c, &delay_200_500_e50, "B116XAN06.1"),
EDP_PANEL_ENTRY('A', 'U', 'O', 0x8594, &delay_200_500_e50, "B133UAN01.0"),
@@ -1888,8 +1889,10 @@ static const struct edp_panel_entry edp_panels[] = {
EDP_PANEL_ENTRY('B', 'O', 'E', 0x0a5d, &delay_200_500_e50, "NV116WHM-N45"),
EDP_PANEL_ENTRY('C', 'M', 'N', 0x114c, &innolux_n116bca_ea1.delay, "N116BCA-EA1"),
+ EDP_PANEL_ENTRY('C', 'M', 'N', 0x1247, &delay_200_500_e80_d50, "N120ACA-EA1"),
EDP_PANEL_ENTRY('I', 'V', 'O', 0x057d, &delay_200_500_e200, "R140NWF5 RH"),
+ EDP_PANEL_ENTRY('I', 'V', 'O', 0x854b, &delay_200_500_p2e100, "M133NW4J-R3"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x0624, &kingdisplay_kd116n21_30nv_a010.delay, "116N21-30NV-A010"),
EDP_PANEL_ENTRY('K', 'D', 'B', 0x1120, &delay_200_500_e80_d50, "116N29-30NK-C007"),
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index b285a8001b1d..e246d914e7f6 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -248,11 +248,15 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
mmu_write(pfdev, MMU_INT_MASK, ~0);
}
-static size_t get_pgsize(u64 addr, size_t size)
+static size_t get_pgsize(u64 addr, size_t size, size_t *count)
{
- if (addr & (SZ_2M - 1) || size < SZ_2M)
- return SZ_4K;
+ size_t blk_offset = -addr % SZ_2M;
+ if (blk_offset || size < SZ_2M) {
+ *count = min_not_zero(blk_offset, size) / SZ_4K;
+ return SZ_4K;
+ }
+ *count = size / SZ_2M;
return SZ_2M;
}
@@ -287,12 +291,16 @@ static int mmu_map_sg(struct panfrost_device *pfdev, struct panfrost_mmu *mmu,
dev_dbg(pfdev->dev, "map: as=%d, iova=%llx, paddr=%lx, len=%zx", mmu->as, iova, paddr, len);
while (len) {
- size_t pgsize = get_pgsize(iova | paddr, len);
-
- ops->map(ops, iova, paddr, pgsize, prot, GFP_KERNEL);
- iova += pgsize;
- paddr += pgsize;
- len -= pgsize;
+ size_t pgcount, mapped = 0;
+ size_t pgsize = get_pgsize(iova | paddr, len, &pgcount);
+
+ ops->map_pages(ops, iova, paddr, pgsize, pgcount, prot,
+ GFP_KERNEL, &mapped);
+ /* Don't get stuck if things have gone wrong */
+ mapped = max(mapped, pgsize);
+ iova += mapped;
+ paddr += mapped;
+ len -= mapped;
}
}
@@ -344,15 +352,17 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping)
mapping->mmu->as, iova, len);
while (unmapped_len < len) {
- size_t unmapped_page;
- size_t pgsize = get_pgsize(iova, len - unmapped_len);
-
- if (ops->iova_to_phys(ops, iova)) {
- unmapped_page = ops->unmap(ops, iova, pgsize, NULL);
- WARN_ON(unmapped_page != pgsize);
+ size_t unmapped_page, pgcount;
+ size_t pgsize = get_pgsize(iova, len - unmapped_len, &pgcount);
+
+ if (bo->is_heap)
+ pgcount = 1;
+ if (!bo->is_heap || ops->iova_to_phys(ops, iova)) {
+ unmapped_page = ops->unmap_pages(ops, iova, pgsize, pgcount, NULL);
+ WARN_ON(unmapped_page != pgsize * pgcount);
}
- iova += pgsize;
- unmapped_len += pgsize;
+ iova += pgsize * pgcount;
+ unmapped_len += pgsize * pgcount;
}
panfrost_mmu_flush_range(pfdev, mapping->mmu,
diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c
index 1cb6f0c224bb..3044ca948ce2 100644
--- a/drivers/gpu/drm/qxl/qxl_drv.c
+++ b/drivers/gpu/drm/qxl/qxl_drv.c
@@ -194,7 +194,6 @@ static int qxl_drm_resume(struct drm_device *dev, bool thaw)
qdev->ram_header->int_mask = QXL_INTERRUPT_MASK;
if (!thaw) {
qxl_reinit_memslots(qdev);
- qxl_ring_init_hdr(qdev->release_ring);
}
qxl_create_monitors_object(qdev);
@@ -220,6 +219,7 @@ static int qxl_pm_resume(struct device *dev)
{
struct pci_dev *pdev = to_pci_dev(dev);
struct drm_device *drm_dev = pci_get_drvdata(pdev);
+ struct qxl_device *qdev = to_qxl(drm_dev);
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
@@ -227,6 +227,7 @@ static int qxl_pm_resume(struct device *dev)
return -EIO;
}
+ qxl_io_reset(qdev);
return qxl_drm_resume(drm_dev, false);
}
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
index e3ab3aca1396..bb4e56f2f170 100644
--- a/drivers/gpu/drm/radeon/Makefile
+++ b/drivers/gpu/drm/radeon/Makefile
@@ -49,7 +49,7 @@ radeon-y += radeon_device.o radeon_asic.o radeon_kms.o \
rv770_smc.o cypress_dpm.o btc_dpm.o sumo_dpm.o sumo_smc.o trinity_dpm.o \
trinity_smc.o ni_dpm.o si_smc.o si_dpm.o kv_smc.o kv_dpm.o ci_smc.o \
ci_dpm.o dce6_afmt.o radeon_vm.o radeon_ucode.o radeon_ib.o \
- radeon_sync.o radeon_audio.o radeon_dp_auxch.o radeon_dp_mst.o
+ radeon_sync.o radeon_audio.o radeon_dp_auxch.o
radeon-$(CONFIG_MMU_NOTIFIER) += radeon_mn.o
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 69f1bc073902..d28d3acb3ba1 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -617,13 +617,6 @@ static u32 atombios_adjust_pll(struct drm_crtc *crtc,
}
}
- if (radeon_encoder->is_mst_encoder) {
- struct radeon_encoder_mst *mst_enc = radeon_encoder->enc_priv;
- struct radeon_connector_atom_dig *dig_connector = mst_enc->connector->con_priv;
-
- dp_clock = dig_connector->dp_clock;
- }
-
/* use recommended ref_div for ss */
if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
if (radeon_crtc->ss_enabled) {
@@ -972,9 +965,7 @@ static bool atombios_crtc_prepare_pll(struct drm_crtc *crtc, struct drm_display_
radeon_crtc->bpc = 8;
radeon_crtc->ss_enabled = false;
- if (radeon_encoder->is_mst_encoder) {
- radeon_dp_mst_prepare_pll(crtc, mode);
- } else if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
+ if ((radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT | ATOM_DEVICE_DFP_SUPPORT)) ||
(radeon_encoder_get_dp_bridge_encoder_id(radeon_crtc->encoder) != ENCODER_OBJECT_ID_NONE)) {
struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
struct drm_connector *connector =
diff --git a/drivers/gpu/drm/radeon/atombios_encoders.c b/drivers/gpu/drm/radeon/atombios_encoders.c
index c93040e60d04..0eae05dfb385 100644
--- a/drivers/gpu/drm/radeon/atombios_encoders.c
+++ b/drivers/gpu/drm/radeon/atombios_encoders.c
@@ -667,15 +667,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *dig_connector;
- struct radeon_encoder_atom_dig *dig_enc;
- if (radeon_encoder_is_digital(encoder)) {
- dig_enc = radeon_encoder->enc_priv;
- if (dig_enc->active_mst_links)
- return ATOM_ENCODER_MODE_DP_MST;
- }
- if (radeon_encoder->is_mst_encoder || radeon_encoder->offset)
- return ATOM_ENCODER_MODE_DP_MST;
/* dp bridges are always DP */
if (radeon_encoder_get_dp_bridge_encoder_id(encoder) != ENCODER_OBJECT_ID_NONE)
return ATOM_ENCODER_MODE_DP;
@@ -1723,10 +1715,6 @@ radeon_atom_encoder_dpms_dig(struct drm_encoder *encoder, int mode)
case DRM_MODE_DPMS_SUSPEND:
case DRM_MODE_DPMS_OFF:
- /* don't power off encoders with active MST links */
- if (dig->active_mst_links)
- return;
-
if (ASIC_IS_DCE4(rdev)) {
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(encoder)) && connector)
atombios_dig_encoder_setup(encoder, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0);
@@ -1992,53 +1980,6 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
radeon_atombios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
}
-void
-atombios_set_mst_encoder_crtc_source(struct drm_encoder *encoder, int fe)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
- int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
- uint8_t frev, crev;
- union crtc_source_param args;
-
- memset(&args, 0, sizeof(args));
-
- if (!atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev))
- return;
-
- if (frev != 1 && crev != 2)
- DRM_ERROR("Unknown table for MST %d, %d\n", frev, crev);
-
- args.v2.ucCRTC = radeon_crtc->crtc_id;
- args.v2.ucEncodeMode = ATOM_ENCODER_MODE_DP_MST;
-
- switch (fe) {
- case 0:
- args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
- break;
- case 1:
- args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
- break;
- case 2:
- args.v2.ucEncoderID = ASIC_INT_DIG3_ENCODER_ID;
- break;
- case 3:
- args.v2.ucEncoderID = ASIC_INT_DIG4_ENCODER_ID;
- break;
- case 4:
- args.v2.ucEncoderID = ASIC_INT_DIG5_ENCODER_ID;
- break;
- case 5:
- args.v2.ucEncoderID = ASIC_INT_DIG6_ENCODER_ID;
- break;
- case 6:
- args.v2.ucEncoderID = ASIC_INT_DIG7_ENCODER_ID;
- break;
- }
- atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
-}
-
static void
atombios_apply_encoder_quirks(struct drm_encoder *encoder,
struct drm_display_mode *mode)
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 28c4413f4dc8..204127bad89c 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -826,8 +826,6 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
}
radeon_link_encoder_connector(dev);
-
- radeon_setup_mst_connector(dev);
return true;
}
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index 58db79921cd3..f7431d224604 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -37,33 +37,12 @@
#include <linux/pm_runtime.h>
#include <linux/vga_switcheroo.h>
-static int radeon_dp_handle_hpd(struct drm_connector *connector)
-{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- int ret;
-
- ret = radeon_dp_mst_check_status(radeon_connector);
- if (ret == -EINVAL)
- return 1;
- return 0;
-}
void radeon_connector_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct radeon_device *rdev = dev->dev_private;
struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {
- struct radeon_connector_atom_dig *dig_connector =
- radeon_connector->con_priv;
-
- if (radeon_connector->is_mst_connector)
- return;
- if (dig_connector->is_mst) {
- radeon_dp_handle_hpd(connector);
- return;
- }
- }
/* bail if the connector does not have hpd pin, e.g.,
* VGA, TV, etc.
*/
@@ -1664,9 +1643,6 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
int r;
- if (radeon_dig_connector->is_mst)
- return connector_status_disconnected;
-
if (!drm_kms_helper_is_poll_worker()) {
r = pm_runtime_get_sync(connector->dev->dev);
if (r < 0) {
@@ -1729,21 +1705,12 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector);
if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
ret = connector_status_connected;
- if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
+ if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT)
radeon_dp_getdpcd(radeon_connector);
- r = radeon_dp_mst_probe(radeon_connector);
- if (r == 1)
- ret = connector_status_disconnected;
- }
} else {
if (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) {
- if (radeon_dp_getdpcd(radeon_connector)) {
- r = radeon_dp_mst_probe(radeon_connector);
- if (r == 1)
- ret = connector_status_disconnected;
- else
- ret = connector_status_connected;
- }
+ if (radeon_dp_getdpcd(radeon_connector))
+ ret = connector_status_connected;
} else {
/* try non-aux ddc (DP to DVI/HDMI/etc. adapter) */
if (radeon_ddc_probe(radeon_connector, false))
@@ -2561,25 +2528,3 @@ radeon_add_legacy_connector(struct drm_device *dev,
connector->display_info.subpixel_order = subpixel_order;
drm_connector_register(connector);
}
-
-void radeon_setup_mst_connector(struct drm_device *dev)
-{
- struct radeon_device *rdev = dev->dev_private;
- struct drm_connector *connector;
- struct radeon_connector *radeon_connector;
-
- if (!ASIC_IS_DCE5(rdev))
- return;
-
- if (radeon_mst == 0)
- return;
-
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- radeon_connector = to_radeon_connector(connector);
-
- if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
- continue;
-
- radeon_dp_mst_init(radeon_connector);
- }
-}
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 2b12389f841a..a215ff1363cd 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1438,7 +1438,6 @@ int radeon_device_init(struct radeon_device *rdev,
goto failed;
radeon_gem_debugfs_init(rdev);
- radeon_mst_debugfs_init(rdev);
if (rdev->flags & RADEON_IS_AGP && !rdev->accel_working) {
/* Acceleration not working on AGP card try again
diff --git a/drivers/gpu/drm/radeon/radeon_dp_mst.c b/drivers/gpu/drm/radeon/radeon_dp_mst.c
deleted file mode 100644
index 54ced1f4ff67..000000000000
--- a/drivers/gpu/drm/radeon/radeon_dp_mst.c
+++ /dev/null
@@ -1,778 +0,0 @@
-// SPDX-License-Identifier: MIT
-
-#include <drm/display/drm_dp_mst_helper.h>
-#include <drm/drm_fb_helper.h>
-#include <drm/drm_file.h>
-#include <drm/drm_probe_helper.h>
-
-#include "atom.h"
-#include "ni_reg.h"
-#include "radeon.h"
-
-static struct radeon_encoder *radeon_dp_create_fake_mst_encoder(struct radeon_connector *connector);
-
-static int radeon_atom_set_enc_offset(int id)
-{
- static const int offsets[] = { EVERGREEN_CRTC0_REGISTER_OFFSET,
- EVERGREEN_CRTC1_REGISTER_OFFSET,
- EVERGREEN_CRTC2_REGISTER_OFFSET,
- EVERGREEN_CRTC3_REGISTER_OFFSET,
- EVERGREEN_CRTC4_REGISTER_OFFSET,
- EVERGREEN_CRTC5_REGISTER_OFFSET,
- 0x13830 - 0x7030 };
-
- return offsets[id];
-}
-
-static int radeon_dp_mst_set_be_cntl(struct radeon_encoder *primary,
- struct radeon_encoder_mst *mst_enc,
- enum radeon_hpd_id hpd, bool enable)
-{
- struct drm_device *dev = primary->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- uint32_t reg;
- int retries = 0;
- uint32_t temp;
-
- reg = RREG32(NI_DIG_BE_CNTL + primary->offset);
-
- /* set MST mode */
- reg &= ~NI_DIG_FE_DIG_MODE(7);
- reg |= NI_DIG_FE_DIG_MODE(NI_DIG_MODE_DP_MST);
-
- if (enable)
- reg |= NI_DIG_FE_SOURCE_SELECT(1 << mst_enc->fe);
- else
- reg &= ~NI_DIG_FE_SOURCE_SELECT(1 << mst_enc->fe);
-
- reg |= NI_DIG_HPD_SELECT(hpd);
- DRM_DEBUG_KMS("writing 0x%08x 0x%08x\n", NI_DIG_BE_CNTL + primary->offset, reg);
- WREG32(NI_DIG_BE_CNTL + primary->offset, reg);
-
- if (enable) {
- uint32_t offset = radeon_atom_set_enc_offset(mst_enc->fe);
-
- do {
- temp = RREG32(NI_DIG_FE_CNTL + offset);
- } while ((temp & NI_DIG_SYMCLK_FE_ON) && retries++ < 10000);
- if (retries == 10000)
- DRM_ERROR("timed out waiting for FE %d %d\n", primary->offset, mst_enc->fe);
- }
- return 0;
-}
-
-static int radeon_dp_mst_set_stream_attrib(struct radeon_encoder *primary,
- int stream_number,
- int fe,
- int slots)
-{
- struct drm_device *dev = primary->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- u32 temp, val;
- int retries = 0;
- int satreg, satidx;
-
- satreg = stream_number >> 1;
- satidx = stream_number & 1;
-
- temp = RREG32(NI_DP_MSE_SAT0 + satreg + primary->offset);
-
- val = NI_DP_MSE_SAT_SLOT_COUNT0(slots) | NI_DP_MSE_SAT_SRC0(fe);
-
- val <<= (16 * satidx);
-
- temp &= ~(0xffff << (16 * satidx));
-
- temp |= val;
-
- DRM_DEBUG_KMS("writing 0x%08x 0x%08x\n", NI_DP_MSE_SAT0 + satreg + primary->offset, temp);
- WREG32(NI_DP_MSE_SAT0 + satreg + primary->offset, temp);
-
- WREG32(NI_DP_MSE_SAT_UPDATE + primary->offset, 1);
-
- do {
- unsigned value1, value2;
- udelay(10);
- temp = RREG32(NI_DP_MSE_SAT_UPDATE + primary->offset);
-
- value1 = temp & NI_DP_MSE_SAT_UPDATE_MASK;
- value2 = temp & NI_DP_MSE_16_MTP_KEEPOUT;
-
- if (!value1 && !value2)
- break;
- } while (retries++ < 50);
-
- if (retries == 10000)
- DRM_ERROR("timed out waitin for SAT update %d\n", primary->offset);
-
- /* MTP 16 ? */
- return 0;
-}
-
-static int radeon_dp_mst_update_stream_attribs(struct radeon_connector *mst_conn,
- struct radeon_encoder *primary)
-{
- struct drm_device *dev = mst_conn->base.dev;
- struct stream_attribs new_attribs[6];
- int i;
- int idx = 0;
- struct radeon_connector *radeon_connector;
- struct drm_connector *connector;
-
- memset(new_attribs, 0, sizeof(new_attribs));
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- struct radeon_encoder *subenc;
- struct radeon_encoder_mst *mst_enc;
-
- radeon_connector = to_radeon_connector(connector);
- if (!radeon_connector->is_mst_connector)
- continue;
-
- if (radeon_connector->mst_port != mst_conn)
- continue;
-
- subenc = radeon_connector->mst_encoder;
- mst_enc = subenc->enc_priv;
-
- if (!mst_enc->enc_active)
- continue;
-
- new_attribs[idx].fe = mst_enc->fe;
- new_attribs[idx].slots = drm_dp_mst_get_vcpi_slots(&mst_conn->mst_mgr, mst_enc->port);
- idx++;
- }
-
- for (i = 0; i < idx; i++) {
- if (new_attribs[i].fe != mst_conn->cur_stream_attribs[i].fe ||
- new_attribs[i].slots != mst_conn->cur_stream_attribs[i].slots) {
- radeon_dp_mst_set_stream_attrib(primary, i, new_attribs[i].fe, new_attribs[i].slots);
- mst_conn->cur_stream_attribs[i].fe = new_attribs[i].fe;
- mst_conn->cur_stream_attribs[i].slots = new_attribs[i].slots;
- }
- }
-
- for (i = idx; i < mst_conn->enabled_attribs; i++) {
- radeon_dp_mst_set_stream_attrib(primary, i, 0, 0);
- mst_conn->cur_stream_attribs[i].fe = 0;
- mst_conn->cur_stream_attribs[i].slots = 0;
- }
- mst_conn->enabled_attribs = idx;
- return 0;
-}
-
-static int radeon_dp_mst_set_vcp_size(struct radeon_encoder *mst, s64 avg_time_slots_per_mtp)
-{
- struct drm_device *dev = mst->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder_mst *mst_enc = mst->enc_priv;
- uint32_t val, temp;
- uint32_t offset = radeon_atom_set_enc_offset(mst_enc->fe);
- int retries = 0;
- uint32_t x = drm_fixp2int(avg_time_slots_per_mtp);
- uint32_t y = drm_fixp2int_ceil((avg_time_slots_per_mtp - x) << 26);
-
- val = NI_DP_MSE_RATE_X(x) | NI_DP_MSE_RATE_Y(y);
-
- WREG32(NI_DP_MSE_RATE_CNTL + offset, val);
-
- do {
- temp = RREG32(NI_DP_MSE_RATE_UPDATE + offset);
- udelay(10);
- } while ((temp & 0x1) && (retries++ < 10000));
-
- if (retries >= 10000)
- DRM_ERROR("timed out wait for rate cntl %d\n", mst_enc->fe);
- return 0;
-}
-
-static int radeon_dp_mst_get_ddc_modes(struct drm_connector *connector)
-{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_connector *master = radeon_connector->mst_port;
- struct edid *edid;
- int ret = 0;
-
- edid = drm_dp_mst_get_edid(connector, &master->mst_mgr, radeon_connector->port);
- radeon_connector->edid = edid;
- DRM_DEBUG_KMS("edid retrieved %p\n", edid);
- if (radeon_connector->edid) {
- drm_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
- ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
- return ret;
- }
- drm_connector_update_edid_property(&radeon_connector->base, NULL);
-
- return ret;
-}
-
-static int radeon_dp_mst_get_modes(struct drm_connector *connector)
-{
- return radeon_dp_mst_get_ddc_modes(connector);
-}
-
-static enum drm_mode_status
-radeon_dp_mst_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- /* TODO - validate mode against available PBN for link */
- if (mode->clock < 10000)
- return MODE_CLOCK_LOW;
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK)
- return MODE_H_ILLEGAL;
-
- return MODE_OK;
-}
-
-static struct
-drm_encoder *radeon_mst_best_encoder(struct drm_connector *connector)
-{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
-
- return &radeon_connector->mst_encoder->base;
-}
-
-static int
-radeon_dp_mst_detect(struct drm_connector *connector,
- struct drm_modeset_acquire_ctx *ctx,
- bool force)
-{
- struct radeon_connector *radeon_connector =
- to_radeon_connector(connector);
- struct radeon_connector *master = radeon_connector->mst_port;
-
- if (drm_connector_is_unregistered(connector))
- return connector_status_disconnected;
-
- return drm_dp_mst_detect_port(connector, ctx, &master->mst_mgr,
- radeon_connector->port);
-}
-
-static const struct drm_connector_helper_funcs radeon_dp_mst_connector_helper_funcs = {
- .get_modes = radeon_dp_mst_get_modes,
- .mode_valid = radeon_dp_mst_mode_valid,
- .best_encoder = radeon_mst_best_encoder,
- .detect_ctx = radeon_dp_mst_detect,
-};
-
-static void
-radeon_dp_mst_connector_destroy(struct drm_connector *connector)
-{
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- struct radeon_encoder *radeon_encoder = radeon_connector->mst_encoder;
-
- drm_encoder_cleanup(&radeon_encoder->base);
- kfree(radeon_encoder);
- drm_connector_cleanup(connector);
- kfree(radeon_connector);
-}
-
-static const struct drm_connector_funcs radeon_dp_mst_connector_funcs = {
- .dpms = drm_helper_connector_dpms,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = radeon_dp_mst_connector_destroy,
-};
-
-static struct drm_connector *radeon_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
- struct drm_dp_mst_port *port,
- const char *pathprop)
-{
- struct radeon_connector *master = container_of(mgr, struct radeon_connector, mst_mgr);
- struct drm_device *dev = master->base.dev;
- struct radeon_connector *radeon_connector;
- struct drm_connector *connector;
-
- radeon_connector = kzalloc(sizeof(*radeon_connector), GFP_KERNEL);
- if (!radeon_connector)
- return NULL;
-
- radeon_connector->is_mst_connector = true;
- connector = &radeon_connector->base;
- radeon_connector->port = port;
- radeon_connector->mst_port = master;
- DRM_DEBUG_KMS("\n");
-
- drm_connector_init(dev, connector, &radeon_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
- drm_connector_helper_add(connector, &radeon_dp_mst_connector_helper_funcs);
- radeon_connector->mst_encoder = radeon_dp_create_fake_mst_encoder(master);
-
- drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
- drm_object_attach_property(&connector->base, dev->mode_config.tile_property, 0);
- drm_connector_set_path_property(connector, pathprop);
-
- return connector;
-}
-
-static const struct drm_dp_mst_topology_cbs mst_cbs = {
- .add_connector = radeon_dp_add_mst_connector,
-};
-
-static struct
-radeon_connector *radeon_mst_find_connector(struct drm_encoder *encoder)
-{
- struct drm_device *dev = encoder->dev;
- struct drm_connector *connector;
-
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- struct radeon_connector *radeon_connector = to_radeon_connector(connector);
- if (!connector->encoder)
- continue;
- if (!radeon_connector->is_mst_connector)
- continue;
-
- DRM_DEBUG_KMS("checking %p vs %p\n", connector->encoder, encoder);
- if (connector->encoder == encoder)
- return radeon_connector;
- }
- return NULL;
-}
-
-void radeon_dp_mst_prepare_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
-{
- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
- struct drm_device *dev = crtc->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(radeon_crtc->encoder);
- struct radeon_encoder_mst *mst_enc = radeon_encoder->enc_priv;
- struct radeon_connector *radeon_connector = radeon_mst_find_connector(&radeon_encoder->base);
- int dp_clock;
- struct radeon_connector_atom_dig *dig_connector = mst_enc->connector->con_priv;
-
- if (radeon_connector) {
- radeon_connector->pixelclock_for_modeset = mode->clock;
- if (radeon_connector->base.display_info.bpc)
- radeon_crtc->bpc = radeon_connector->base.display_info.bpc;
- else
- radeon_crtc->bpc = 8;
- }
-
- DRM_DEBUG_KMS("dp_clock %p %d\n", dig_connector, dig_connector->dp_clock);
- dp_clock = dig_connector->dp_clock;
- radeon_crtc->ss_enabled =
- radeon_atombios_get_asic_ss_info(rdev, &radeon_crtc->ss,
- ASIC_INTERNAL_SS_ON_DP,
- dp_clock);
-}
-
-static void
-radeon_mst_encoder_dpms(struct drm_encoder *encoder, int mode)
-{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder, *primary;
- struct radeon_encoder_mst *mst_enc;
- struct radeon_encoder_atom_dig *dig_enc;
- struct radeon_connector *radeon_connector;
- struct drm_crtc *crtc;
- struct radeon_crtc *radeon_crtc;
- int slots;
- s64 fixed_pbn, fixed_pbn_per_slot, avg_time_slots_per_mtp;
- if (!ASIC_IS_DCE5(rdev)) {
- DRM_ERROR("got mst dpms on non-DCE5\n");
- return;
- }
-
- radeon_connector = radeon_mst_find_connector(encoder);
- if (!radeon_connector)
- return;
-
- radeon_encoder = to_radeon_encoder(encoder);
-
- mst_enc = radeon_encoder->enc_priv;
-
- primary = mst_enc->primary;
-
- dig_enc = primary->enc_priv;
-
- crtc = encoder->crtc;
- DRM_DEBUG_KMS("got connector %d\n", dig_enc->active_mst_links);
-
- switch (mode) {
- case DRM_MODE_DPMS_ON:
- dig_enc->active_mst_links++;
-
- radeon_crtc = to_radeon_crtc(crtc);
-
- if (dig_enc->active_mst_links == 1) {
- mst_enc->fe = dig_enc->dig_encoder;
- mst_enc->fe_from_be = true;
- atombios_set_mst_encoder_crtc_source(encoder, mst_enc->fe);
-
- atombios_dig_encoder_setup(&primary->base, ATOM_ENCODER_CMD_SETUP, 0);
- atombios_dig_transmitter_setup2(&primary->base, ATOM_TRANSMITTER_ACTION_ENABLE,
- 0, 0, dig_enc->dig_encoder);
-
- if (radeon_dp_needs_link_train(mst_enc->connector) ||
- dig_enc->active_mst_links == 1) {
- radeon_dp_link_train(&primary->base, &mst_enc->connector->base);
- }
-
- } else {
- mst_enc->fe = radeon_atom_pick_dig_encoder(encoder, radeon_crtc->crtc_id);
- if (mst_enc->fe == -1)
- DRM_ERROR("failed to get frontend for dig encoder\n");
- mst_enc->fe_from_be = false;
- atombios_set_mst_encoder_crtc_source(encoder, mst_enc->fe);
- }
-
- DRM_DEBUG_KMS("dig encoder is %d %d %d\n", dig_enc->dig_encoder,
- dig_enc->linkb, radeon_crtc->crtc_id);
-
- slots = drm_dp_find_vcpi_slots(&radeon_connector->mst_port->mst_mgr,
- mst_enc->pbn);
- drm_dp_mst_allocate_vcpi(&radeon_connector->mst_port->mst_mgr,
- radeon_connector->port,
- mst_enc->pbn, slots);
- drm_dp_update_payload_part1(&radeon_connector->mst_port->mst_mgr, 1);
-
- radeon_dp_mst_set_be_cntl(primary, mst_enc,
- radeon_connector->mst_port->hpd.hpd, true);
-
- mst_enc->enc_active = true;
- radeon_dp_mst_update_stream_attribs(radeon_connector->mst_port, primary);
-
- fixed_pbn = drm_int2fixp(mst_enc->pbn);
- fixed_pbn_per_slot = drm_int2fixp(radeon_connector->mst_port->mst_mgr.pbn_div);
- avg_time_slots_per_mtp = drm_fixp_div(fixed_pbn, fixed_pbn_per_slot);
- radeon_dp_mst_set_vcp_size(radeon_encoder, avg_time_slots_per_mtp);
-
- atombios_dig_encoder_setup2(&primary->base, ATOM_ENCODER_CMD_DP_VIDEO_ON, 0,
- mst_enc->fe);
- drm_dp_check_act_status(&radeon_connector->mst_port->mst_mgr);
-
- drm_dp_update_payload_part2(&radeon_connector->mst_port->mst_mgr);
-
- break;
- case DRM_MODE_DPMS_STANDBY:
- case DRM_MODE_DPMS_SUSPEND:
- case DRM_MODE_DPMS_OFF:
- DRM_ERROR("DPMS OFF %d\n", dig_enc->active_mst_links);
-
- if (!mst_enc->enc_active)
- return;
-
- drm_dp_mst_reset_vcpi_slots(&radeon_connector->mst_port->mst_mgr, mst_enc->port);
- drm_dp_update_payload_part1(&radeon_connector->mst_port->mst_mgr, 1);
-
- drm_dp_check_act_status(&radeon_connector->mst_port->mst_mgr);
- /* and this can also fail */
- drm_dp_update_payload_part2(&radeon_connector->mst_port->mst_mgr);
-
- drm_dp_mst_deallocate_vcpi(&radeon_connector->mst_port->mst_mgr, mst_enc->port);
-
- mst_enc->enc_active = false;
- radeon_dp_mst_update_stream_attribs(radeon_connector->mst_port, primary);
-
- radeon_dp_mst_set_be_cntl(primary, mst_enc,
- radeon_connector->mst_port->hpd.hpd, false);
- atombios_dig_encoder_setup2(&primary->base, ATOM_ENCODER_CMD_DP_VIDEO_OFF, 0,
- mst_enc->fe);
-
- if (!mst_enc->fe_from_be)
- radeon_atom_release_dig_encoder(rdev, mst_enc->fe);
-
- mst_enc->fe_from_be = false;
- dig_enc->active_mst_links--;
- if (dig_enc->active_mst_links == 0) {
- /* drop link */
- }
-
- break;
- }
-
-}
-
-static bool radeon_mst_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct radeon_encoder_mst *mst_enc;
- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
- struct radeon_connector_atom_dig *dig_connector;
- int bpp = 24;
-
- mst_enc = radeon_encoder->enc_priv;
-
- mst_enc->pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp, false);
-
- mst_enc->primary->active_device = mst_enc->primary->devices & mst_enc->connector->devices;
- DRM_DEBUG_KMS("setting active device to %08x from %08x %08x for encoder %d\n",
- mst_enc->primary->active_device, mst_enc->primary->devices,
- mst_enc->connector->devices, mst_enc->primary->base.encoder_type);
-
-
- drm_mode_set_crtcinfo(adjusted_mode, 0);
- dig_connector = mst_enc->connector->con_priv;
- dig_connector->dp_lane_count = drm_dp_max_lane_count(dig_connector->dpcd);
- dig_connector->dp_clock = drm_dp_max_link_rate(dig_connector->dpcd);
- DRM_DEBUG_KMS("dig clock %p %d %d\n", dig_connector,
- dig_connector->dp_lane_count, dig_connector->dp_clock);
- return true;
-}
-
-static void radeon_mst_encoder_prepare(struct drm_encoder *encoder)
-{
- struct radeon_connector *radeon_connector;
- struct radeon_encoder *radeon_encoder, *primary;
- struct radeon_encoder_mst *mst_enc;
- struct radeon_encoder_atom_dig *dig_enc;
-
- radeon_connector = radeon_mst_find_connector(encoder);
- if (!radeon_connector) {
- DRM_DEBUG_KMS("failed to find connector %p\n", encoder);
- return;
- }
- radeon_encoder = to_radeon_encoder(encoder);
-
- radeon_mst_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
-
- mst_enc = radeon_encoder->enc_priv;
-
- primary = mst_enc->primary;
-
- dig_enc = primary->enc_priv;
-
- mst_enc->port = radeon_connector->port;
-
- if (dig_enc->dig_encoder == -1) {
- dig_enc->dig_encoder = radeon_atom_pick_dig_encoder(&primary->base, -1);
- primary->offset = radeon_atom_set_enc_offset(dig_enc->dig_encoder);
- atombios_set_mst_encoder_crtc_source(encoder, dig_enc->dig_encoder);
-
-
- }
- DRM_DEBUG_KMS("%d %d\n", dig_enc->dig_encoder, primary->offset);
-}
-
-static void
-radeon_mst_encoder_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- DRM_DEBUG_KMS("\n");
-}
-
-static void radeon_mst_encoder_commit(struct drm_encoder *encoder)
-{
- radeon_mst_encoder_dpms(encoder, DRM_MODE_DPMS_ON);
- DRM_DEBUG_KMS("\n");
-}
-
-static const struct drm_encoder_helper_funcs radeon_mst_helper_funcs = {
- .dpms = radeon_mst_encoder_dpms,
- .mode_fixup = radeon_mst_mode_fixup,
- .prepare = radeon_mst_encoder_prepare,
- .mode_set = radeon_mst_encoder_mode_set,
- .commit = radeon_mst_encoder_commit,
-};
-
-static void radeon_dp_mst_encoder_destroy(struct drm_encoder *encoder)
-{
- drm_encoder_cleanup(encoder);
- kfree(encoder);
-}
-
-static const struct drm_encoder_funcs radeon_dp_mst_enc_funcs = {
- .destroy = radeon_dp_mst_encoder_destroy,
-};
-
-static struct radeon_encoder *
-radeon_dp_create_fake_mst_encoder(struct radeon_connector *connector)
-{
- struct drm_device *dev = connector->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- struct radeon_encoder *radeon_encoder;
- struct radeon_encoder_mst *mst_enc;
- struct drm_encoder *encoder;
- const struct drm_connector_helper_funcs *connector_funcs = connector->base.helper_private;
- struct drm_encoder *enc_master = connector_funcs->best_encoder(&connector->base);
-
- DRM_DEBUG_KMS("enc master is %p\n", enc_master);
- radeon_encoder = kzalloc(sizeof(*radeon_encoder), GFP_KERNEL);
- if (!radeon_encoder)
- return NULL;
-
- radeon_encoder->enc_priv = kzalloc(sizeof(*mst_enc), GFP_KERNEL);
- if (!radeon_encoder->enc_priv) {
- kfree(radeon_encoder);
- return NULL;
- }
- encoder = &radeon_encoder->base;
- switch (rdev->num_crtc) {
- case 1:
- encoder->possible_crtcs = 0x1;
- break;
- case 2:
- default:
- encoder->possible_crtcs = 0x3;
- break;
- case 4:
- encoder->possible_crtcs = 0xf;
- break;
- case 6:
- encoder->possible_crtcs = 0x3f;
- break;
- }
-
- drm_encoder_init(dev, &radeon_encoder->base, &radeon_dp_mst_enc_funcs,
- DRM_MODE_ENCODER_DPMST, NULL);
- drm_encoder_helper_add(encoder, &radeon_mst_helper_funcs);
-
- mst_enc = radeon_encoder->enc_priv;
- mst_enc->connector = connector;
- mst_enc->primary = to_radeon_encoder(enc_master);
- radeon_encoder->is_mst_encoder = true;
- return radeon_encoder;
-}
-
-int
-radeon_dp_mst_init(struct radeon_connector *radeon_connector)
-{
- struct drm_device *dev = radeon_connector->base.dev;
- int max_link_rate;
-
- if (!radeon_connector->ddc_bus->has_aux)
- return 0;
-
- if (radeon_connector_is_dp12_capable(&radeon_connector->base))
- max_link_rate = 0x14;
- else
- max_link_rate = 0x0a;
-
- radeon_connector->mst_mgr.cbs = &mst_cbs;
- return drm_dp_mst_topology_mgr_init(&radeon_connector->mst_mgr, dev,
- &radeon_connector->ddc_bus->aux, 16, 6,
- 4, drm_dp_bw_code_to_link_rate(max_link_rate),
- radeon_connector->base.base.id);
-}
-
-int
-radeon_dp_mst_probe(struct radeon_connector *radeon_connector)
-{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- struct drm_device *dev = radeon_connector->base.dev;
- struct radeon_device *rdev = dev->dev_private;
- int ret;
- u8 msg[1];
-
- if (!radeon_mst)
- return 0;
-
- if (!ASIC_IS_DCE5(rdev))
- return 0;
-
- if (dig_connector->dpcd[DP_DPCD_REV] < 0x12)
- return 0;
-
- ret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux, DP_MSTM_CAP, msg,
- 1);
- if (ret) {
- if (msg[0] & DP_MST_CAP) {
- DRM_DEBUG_KMS("Sink is MST capable\n");
- dig_connector->is_mst = true;
- } else {
- DRM_DEBUG_KMS("Sink is not MST capable\n");
- dig_connector->is_mst = false;
- }
-
- }
- drm_dp_mst_topology_mgr_set_mst(&radeon_connector->mst_mgr,
- dig_connector->is_mst);
- return dig_connector->is_mst;
-}
-
-int
-radeon_dp_mst_check_status(struct radeon_connector *radeon_connector)
-{
- struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
- int retry;
-
- if (dig_connector->is_mst) {
- u8 esi[16] = { 0 };
- int dret;
- int ret = 0;
- bool handled;
-
- dret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux,
- DP_SINK_COUNT_ESI, esi, 8);
-go_again:
- if (dret == 8) {
- DRM_DEBUG_KMS("got esi %3ph\n", esi);
- ret = drm_dp_mst_hpd_irq(&radeon_connector->mst_mgr, esi, &handled);
-
- if (handled) {
- for (retry = 0; retry < 3; retry++) {
- int wret;
- wret = drm_dp_dpcd_write(&radeon_connector->ddc_bus->aux,
- DP_SINK_COUNT_ESI + 1, &esi[1], 3);
- if (wret == 3)
- break;
- }
-
- dret = drm_dp_dpcd_read(&radeon_connector->ddc_bus->aux,
- DP_SINK_COUNT_ESI, esi, 8);
- if (dret == 8) {
- DRM_DEBUG_KMS("got esi2 %3ph\n", esi);
- goto go_again;
- }
- } else
- ret = 0;
-
- return ret;
- } else {
- DRM_DEBUG_KMS("failed to get ESI - device may have failed %d\n", ret);
- dig_connector->is_mst = false;
- drm_dp_mst_topology_mgr_set_mst(&radeon_connector->mst_mgr,
- dig_connector->is_mst);
- /* send a hotplug event */
- }
- }
- return -EINVAL;
-}
-
-#if defined(CONFIG_DEBUG_FS)
-
-static int radeon_debugfs_mst_info_show(struct seq_file *m, void *unused)
-{
- struct radeon_device *rdev = (struct radeon_device *)m->private;
- struct drm_device *dev = rdev->ddev;
- struct drm_connector *connector;
- struct radeon_connector *radeon_connector;
- struct radeon_connector_atom_dig *dig_connector;
- int i;
-
- drm_modeset_lock_all(dev);
- list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
- if (connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
- continue;
-
- radeon_connector = to_radeon_connector(connector);
- dig_connector = radeon_connector->con_priv;
- if (radeon_connector->is_mst_connector)
- continue;
- if (!dig_connector->is_mst)
- continue;
- drm_dp_mst_dump_topology(m, &radeon_connector->mst_mgr);
-
- for (i = 0; i < radeon_connector->enabled_attribs; i++)
- seq_printf(m, "attrib %d: %d %d\n", i,
- radeon_connector->cur_stream_attribs[i].fe,
- radeon_connector->cur_stream_attribs[i].slots);
- }
- drm_modeset_unlock_all(dev);
- return 0;
-}
-
-DEFINE_SHOW_ATTRIBUTE(radeon_debugfs_mst_info);
-#endif
-
-void radeon_mst_debugfs_init(struct radeon_device *rdev)
-{
-#if defined(CONFIG_DEBUG_FS)
- struct dentry *root = rdev->ddev->primary->debugfs_root;
-
- debugfs_create_file("radeon_mst_info", 0444, root, rdev,
- &radeon_debugfs_mst_info_fops);
-
-#endif
-}
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index 956c72b5aa33..a28d5ceab628 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -172,7 +172,6 @@ int radeon_use_pflipirq = 2;
int radeon_bapm = -1;
int radeon_backlight = -1;
int radeon_auxch = -1;
-int radeon_mst = 0;
int radeon_uvd = 1;
int radeon_vce = 1;
@@ -263,9 +262,6 @@ module_param_named(backlight, radeon_backlight, int, 0444);
MODULE_PARM_DESC(auxch, "Use native auxch experimental support (1 = enable, 0 = disable, -1 = auto)");
module_param_named(auxch, radeon_auxch, int, 0444);
-MODULE_PARM_DESC(mst, "DisplayPort MST experimental support (1 = enable, 0 = disable)");
-module_param_named(mst, radeon_mst, int, 0444);
-
MODULE_PARM_DESC(uvd, "uvd enable/disable uvd support (1 = enable, 0 = disable)");
module_param_named(uvd, radeon_uvd, int, 0444);
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index 46549d5179ee..35c535e48b8d 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -244,16 +244,7 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
radeon_connector = to_radeon_connector(connector);
- if (radeon_encoder->is_mst_encoder) {
- struct radeon_encoder_mst *mst_enc;
-
- if (!radeon_connector->is_mst_connector)
- continue;
-
- mst_enc = radeon_encoder->enc_priv;
- if (mst_enc->connector == radeon_connector->mst_port)
- return connector;
- } else if (radeon_encoder->active_device & radeon_connector->devices)
+ if (radeon_encoder->active_device & radeon_connector->devices)
return connector;
}
return NULL;
@@ -399,9 +390,6 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
case DRM_MODE_CONNECTOR_DVID:
case DRM_MODE_CONNECTOR_HDMIA:
case DRM_MODE_CONNECTOR_DisplayPort:
- if (radeon_connector->is_mst_connector)
- return false;
-
dig_connector = radeon_connector->con_priv;
if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
(dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 3907785d0798..da2173435edd 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -100,16 +100,8 @@ static void radeon_hotplug_work_func(struct work_struct *work)
static void radeon_dp_work_func(struct work_struct *work)
{
- struct radeon_device *rdev = container_of(work, struct radeon_device,
- dp_work);
- struct drm_device *dev = rdev->ddev;
- struct drm_mode_config *mode_config = &dev->mode_config;
- struct drm_connector *connector;
-
- /* this should take a mutex */
- list_for_each_entry(connector, &mode_config->connector_list, head)
- radeon_connector_hotplug(connector);
}
+
/**
* radeon_driver_irq_preinstall_kms - drm irq preinstall callback
*
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index b34cffc162e2..6a6a73204226 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -31,7 +31,6 @@
#define RADEON_MODE_H
#include <drm/display/drm_dp_helper.h>
-#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
@@ -436,24 +435,12 @@ struct radeon_encoder_atom_dig {
int panel_mode;
struct radeon_afmt *afmt;
struct r600_audio_pin *pin;
- int active_mst_links;
};
struct radeon_encoder_atom_dac {
enum radeon_tv_std tv_std;
};
-struct radeon_encoder_mst {
- int crtc;
- struct radeon_encoder *primary;
- struct radeon_connector *connector;
- struct drm_dp_mst_port *port;
- int pbn;
- int fe;
- bool fe_from_be;
- bool enc_active;
-};
-
struct radeon_encoder {
struct drm_encoder base;
uint32_t encoder_enum;
@@ -475,8 +462,6 @@ struct radeon_encoder {
enum radeon_output_csc output_csc;
bool can_mst;
uint32_t offset;
- bool is_mst_encoder;
- /* front end for this mst encoder */
};
struct radeon_connector_atom_dig {
@@ -487,7 +472,6 @@ struct radeon_connector_atom_dig {
int dp_clock;
int dp_lane_count;
bool edp_on;
- bool is_mst;
};
struct radeon_gpio_rec {
@@ -531,11 +515,6 @@ enum radeon_connector_dither {
RADEON_FMT_DITHER_ENABLE = 1,
};
-struct stream_attribs {
- uint16_t fe;
- uint16_t slots;
-};
-
struct radeon_connector {
struct drm_connector base;
uint32_t connector_id;
@@ -558,14 +537,6 @@ struct radeon_connector {
enum radeon_connector_audio audio;
enum radeon_connector_dither dither;
int pixelclock_for_modeset;
- bool is_mst_connector;
- struct radeon_connector *mst_port;
- struct drm_dp_mst_port *port;
- struct drm_dp_mst_topology_mgr mst_mgr;
-
- struct radeon_encoder *mst_encoder;
- struct stream_attribs cur_stream_attribs[6];
- int enabled_attribs;
};
#define ENCODER_MODE_IS_DP(em) (((em) == ATOM_ENCODER_MODE_DP) || \
@@ -767,8 +738,6 @@ extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
extern void atombios_dig_transmitter_setup2(struct drm_encoder *encoder,
int action, uint8_t lane_num,
uint8_t lane_set, int fe);
-extern void atombios_set_mst_encoder_crtc_source(struct drm_encoder *encoder,
- int fe);
extern void radeon_atom_ext_encoder_setup_ddc(struct drm_encoder *encoder);
extern struct drm_encoder *radeon_get_external_encoder(struct drm_encoder *encoder);
void radeon_atom_copy_swap(u8 *dst, u8 *src, u8 num_bytes, bool to_le);
@@ -986,15 +955,6 @@ void radeon_crtc_handle_flip(struct radeon_device *rdev, int crtc_id);
int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled);
-/* mst */
-int radeon_dp_mst_init(struct radeon_connector *radeon_connector);
-int radeon_dp_mst_probe(struct radeon_connector *radeon_connector);
-int radeon_dp_mst_check_status(struct radeon_connector *radeon_connector);
-void radeon_mst_debugfs_init(struct radeon_device *rdev);
-void radeon_dp_mst_prepare_pll(struct drm_crtc *crtc, struct drm_display_mode *mode);
-
-void radeon_setup_mst_connector(struct drm_device *dev);
-
int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder, int fe_idx);
void radeon_atom_release_dig_encoder(struct radeon_device *rdev, int enc_idx);
#endif
diff --git a/drivers/gpu/drm/scheduler/sched_main.c b/drivers/gpu/drm/scheduler/sched_main.c
index e0ab14e0fb6b..e5a4ecde0063 100644
--- a/drivers/gpu/drm/scheduler/sched_main.c
+++ b/drivers/gpu/drm/scheduler/sched_main.c
@@ -198,7 +198,7 @@ static void drm_sched_job_done_cb(struct dma_fence *f, struct dma_fence_cb *cb)
}
/**
- * drm_sched_dependency_optimized
+ * drm_sched_dependency_optimized - test if the dependency can be optimized
*
* @fence: the dependency fence
* @entity: the entity which depends on the above fence
@@ -993,6 +993,7 @@ static int drm_sched_main(void *param)
* used
* @score: optional score atomic shared with other schedulers
* @name: name used for debugging
+ * @dev: target &struct device
*
* Return 0 on success, otherwise error code.
*/
diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c
index 94d92b726c34..c95221fff474 100644
--- a/drivers/gpu/drm/solomon/ssd130x.c
+++ b/drivers/gpu/drm/solomon/ssd130x.c
@@ -18,6 +18,7 @@
#include <linux/pwm.h>
#include <linux/regulator/consumer.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_damage_helper.h>
#include <drm/drm_edid.h>
@@ -564,61 +565,52 @@ static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_m
return ret;
}
-static int ssd130x_display_pipe_mode_valid(struct drm_simple_display_pipe *pipe,
- const struct drm_display_mode *mode)
+static int ssd130x_primary_plane_helper_atomic_check(struct drm_plane *plane,
+ struct drm_atomic_state *new_state)
{
- struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
+ struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(new_state, plane);
+ struct drm_crtc *new_crtc = new_plane_state->crtc;
+ struct drm_crtc_state *new_crtc_state = NULL;
- if (mode->hdisplay != ssd130x->mode.hdisplay &&
- mode->vdisplay != ssd130x->mode.vdisplay)
- return MODE_ONE_SIZE;
-
- if (mode->hdisplay != ssd130x->mode.hdisplay)
- return MODE_ONE_WIDTH;
-
- if (mode->vdisplay != ssd130x->mode.vdisplay)
- return MODE_ONE_HEIGHT;
+ if (new_crtc)
+ new_crtc_state = drm_atomic_get_new_crtc_state(new_state, new_crtc);
- return MODE_OK;
+ return drm_atomic_helper_check_plane_state(new_plane_state, new_crtc_state,
+ DRM_PLANE_NO_SCALING,
+ DRM_PLANE_NO_SCALING,
+ false, false);
}
-static void ssd130x_display_pipe_enable(struct drm_simple_display_pipe *pipe,
- struct drm_crtc_state *crtc_state,
- struct drm_plane_state *plane_state)
+static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
{
- struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
+ struct drm_plane_state *plane_state = plane->state;
+ struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(old_state, plane);
struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
- struct drm_device *drm = &ssd130x->drm;
- int idx, ret;
+ struct drm_device *drm = plane->dev;
+ struct drm_rect src_clip, dst_clip;
+ int idx;
- ret = ssd130x_power_on(ssd130x);
- if (ret)
+ if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip))
return;
- ret = ssd130x_init(ssd130x);
- if (ret)
- goto out_power_off;
+ dst_clip = plane_state->dst;
+ if (!drm_rect_intersect(&dst_clip, &src_clip))
+ return;
if (!drm_dev_enter(drm, &idx))
- goto out_power_off;
-
- ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &plane_state->dst);
-
- ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON);
+ return;
- backlight_enable(ssd130x->bl_dev);
+ ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip);
drm_dev_exit(idx);
-
- return;
-out_power_off:
- ssd130x_power_off(ssd130x);
}
-static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe)
+static void ssd130x_primary_plane_helper_atomic_disable(struct drm_plane *plane,
+ struct drm_atomic_state *old_state)
{
- struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
- struct drm_device *drm = &ssd130x->drm;
+ struct drm_device *drm = plane->dev;
+ struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
int idx;
if (!drm_dev_enter(drm, &idx))
@@ -626,56 +618,120 @@ static void ssd130x_display_pipe_disable(struct drm_simple_display_pipe *pipe)
ssd130x_clear_screen(ssd130x);
- backlight_disable(ssd130x->bl_dev);
+ drm_dev_exit(idx);
+}
- ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF);
+static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = {
+ DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
+ .atomic_check = ssd130x_primary_plane_helper_atomic_check,
+ .atomic_update = ssd130x_primary_plane_helper_atomic_update,
+ .atomic_disable = ssd130x_primary_plane_helper_atomic_disable,
+};
- ssd130x_power_off(ssd130x);
+static const struct drm_plane_funcs ssd130x_primary_plane_funcs = {
+ .update_plane = drm_atomic_helper_update_plane,
+ .disable_plane = drm_atomic_helper_disable_plane,
+ .destroy = drm_plane_cleanup,
+ DRM_GEM_SHADOW_PLANE_FUNCS,
+};
- drm_dev_exit(idx);
+static enum drm_mode_status ssd130x_crtc_helper_mode_valid(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ struct ssd130x_device *ssd130x = drm_to_ssd130x(crtc->dev);
+
+ if (mode->hdisplay != ssd130x->mode.hdisplay &&
+ mode->vdisplay != ssd130x->mode.vdisplay)
+ return MODE_ONE_SIZE;
+ else if (mode->hdisplay != ssd130x->mode.hdisplay)
+ return MODE_ONE_WIDTH;
+ else if (mode->vdisplay != ssd130x->mode.vdisplay)
+ return MODE_ONE_HEIGHT;
+
+ return MODE_OK;
}
-static void ssd130x_display_pipe_update(struct drm_simple_display_pipe *pipe,
- struct drm_plane_state *old_plane_state)
+static int ssd130x_crtc_helper_atomic_check(struct drm_crtc *crtc,
+ struct drm_atomic_state *new_state)
{
- struct ssd130x_device *ssd130x = drm_to_ssd130x(pipe->crtc.dev);
- struct drm_plane_state *plane_state = pipe->plane.state;
- struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
- struct drm_framebuffer *fb = plane_state->fb;
- struct drm_device *drm = &ssd130x->drm;
- struct drm_rect src_clip, dst_clip;
- int idx;
+ struct drm_crtc_state *new_crtc_state = drm_atomic_get_new_crtc_state(new_state, crtc);
+ int ret;
- if (!fb)
- return;
+ ret = drm_atomic_helper_check_crtc_state(new_crtc_state, false);
+ if (ret)
+ return ret;
- if (!pipe->crtc.state->active)
- return;
+ return drm_atomic_add_affected_planes(new_state, crtc);
+}
- if (!drm_atomic_helper_damage_merged(old_plane_state, plane_state, &src_clip))
- return;
+/*
+ * The CRTC is always enabled. Screen updates are performed by
+ * the primary plane's atomic_update function. Disabling clears
+ * the screen in the primary plane's atomic_disable function.
+ */
+static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs = {
+ .mode_valid = ssd130x_crtc_helper_mode_valid,
+ .atomic_check = ssd130x_crtc_helper_atomic_check,
+};
- dst_clip = plane_state->dst;
- if (!drm_rect_intersect(&dst_clip, &src_clip))
- return;
+static void ssd130x_crtc_reset(struct drm_crtc *crtc)
+{
+ struct drm_device *drm = crtc->dev;
+ struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
- if (!drm_dev_enter(drm, &idx))
+ ssd130x_init(ssd130x);
+
+ drm_atomic_helper_crtc_reset(crtc);
+}
+
+static const struct drm_crtc_funcs ssd130x_crtc_funcs = {
+ .reset = ssd130x_crtc_reset,
+ .destroy = drm_crtc_cleanup,
+ .set_config = drm_atomic_helper_set_config,
+ .page_flip = drm_atomic_helper_page_flip,
+ .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
+ .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
+};
+
+static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *drm = encoder->dev;
+ struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
+ int ret;
+
+ ret = ssd130x_power_on(ssd130x);
+ if (ret)
return;
- ssd130x_fb_blit_rect(plane_state->fb, &shadow_plane_state->data[0], &dst_clip);
+ ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_ON);
- drm_dev_exit(idx);
+ backlight_enable(ssd130x->bl_dev);
}
-static const struct drm_simple_display_pipe_funcs ssd130x_pipe_funcs = {
- .mode_valid = ssd130x_display_pipe_mode_valid,
- .enable = ssd130x_display_pipe_enable,
- .disable = ssd130x_display_pipe_disable,
- .update = ssd130x_display_pipe_update,
- DRM_GEM_SIMPLE_DISPLAY_PIPE_SHADOW_PLANE_FUNCS,
+static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
+{
+ struct drm_device *drm = encoder->dev;
+ struct ssd130x_device *ssd130x = drm_to_ssd130x(drm);
+
+ backlight_disable(ssd130x->bl_dev);
+
+ ssd130x_write_cmd(ssd130x, 1, SSD130X_DISPLAY_OFF);
+
+ ssd130x_power_off(ssd130x);
+}
+
+static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs = {
+ .atomic_enable = ssd130x_encoder_helper_atomic_enable,
+ .atomic_disable = ssd130x_encoder_helper_atomic_disable,
+};
+
+static const struct drm_encoder_funcs ssd130x_encoder_funcs = {
+ .destroy = drm_encoder_cleanup,
};
-static int ssd130x_connector_get_modes(struct drm_connector *connector)
+static int ssd130x_connector_helper_get_modes(struct drm_connector *connector)
{
struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev);
struct drm_display_mode *mode;
@@ -695,7 +751,7 @@ static int ssd130x_connector_get_modes(struct drm_connector *connector)
}
static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = {
- .get_modes = ssd130x_connector_get_modes,
+ .get_modes = ssd130x_connector_helper_get_modes,
};
static const struct drm_connector_funcs ssd130x_connector_funcs = {
@@ -806,8 +862,16 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
struct device *dev = ssd130x->dev;
struct drm_device *drm = &ssd130x->drm;
unsigned long max_width, max_height;
+ struct drm_plane *primary_plane;
+ struct drm_crtc *crtc;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
int ret;
+ /*
+ * Modesetting
+ */
+
ret = drmm_mode_config_init(drm);
if (ret) {
dev_err(dev, "DRM mode config init failed: %d\n", ret);
@@ -833,25 +897,65 @@ static int ssd130x_init_modeset(struct ssd130x_device *ssd130x)
drm->mode_config.preferred_depth = 32;
drm->mode_config.funcs = &ssd130x_mode_config_funcs;
- ret = drm_connector_init(drm, &ssd130x->connector, &ssd130x_connector_funcs,
+ /* Primary plane */
+
+ primary_plane = &ssd130x->primary_plane;
+ ret = drm_universal_plane_init(drm, primary_plane, 0, &ssd130x_primary_plane_funcs,
+ ssd130x_formats, ARRAY_SIZE(ssd130x_formats),
+ NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
+ if (ret) {
+ dev_err(dev, "DRM primary plane init failed: %d\n", ret);
+ return ret;
+ }
+
+ drm_plane_helper_add(primary_plane, &ssd130x_primary_plane_helper_funcs);
+
+ drm_plane_enable_fb_damage_clips(primary_plane);
+
+ /* CRTC */
+
+ crtc = &ssd130x->crtc;
+ ret = drm_crtc_init_with_planes(drm, crtc, primary_plane, NULL,
+ &ssd130x_crtc_funcs, NULL);
+ if (ret) {
+ dev_err(dev, "DRM crtc init failed: %d\n", ret);
+ return ret;
+ }
+
+ drm_crtc_helper_add(crtc, &ssd130x_crtc_helper_funcs);
+
+ /* Encoder */
+
+ encoder = &ssd130x->encoder;
+ ret = drm_encoder_init(drm, encoder, &ssd130x_encoder_funcs,
+ DRM_MODE_ENCODER_NONE, NULL);
+ if (ret) {
+ dev_err(dev, "DRM encoder init failed: %d\n", ret);
+ return ret;
+ }
+
+ drm_encoder_helper_add(encoder, &ssd130x_encoder_helper_funcs);
+
+ encoder->possible_crtcs = drm_crtc_mask(crtc);
+
+ /* Connector */
+
+ connector = &ssd130x->connector;
+ ret = drm_connector_init(drm, connector, &ssd130x_connector_funcs,
DRM_MODE_CONNECTOR_Unknown);
if (ret) {
dev_err(dev, "DRM connector init failed: %d\n", ret);
return ret;
}
- drm_connector_helper_add(&ssd130x->connector, &ssd130x_connector_helper_funcs);
+ drm_connector_helper_add(connector, &ssd130x_connector_helper_funcs);
- ret = drm_simple_display_pipe_init(drm, &ssd130x->pipe, &ssd130x_pipe_funcs,
- ssd130x_formats, ARRAY_SIZE(ssd130x_formats),
- NULL, &ssd130x->connector);
+ ret = drm_connector_attach_encoder(connector, encoder);
if (ret) {
- dev_err(dev, "DRM simple display pipeline init failed: %d\n", ret);
+ dev_err(dev, "DRM attach connector to encoder failed: %d\n", ret);
return ret;
}
- drm_plane_enable_fb_damage_clips(&ssd130x->pipe.plane);
-
drm_mode_config_reset(drm);
return 0;
diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h
index 4c4a84e962e7..03038c1b6476 100644
--- a/drivers/gpu/drm/solomon/ssd130x.h
+++ b/drivers/gpu/drm/solomon/ssd130x.h
@@ -13,8 +13,11 @@
#ifndef __SSD1307X_H__
#define __SSD1307X_H__
+#include <drm/drm_connector.h>
+#include <drm/drm_crtc.h>
#include <drm/drm_drv.h>
-#include <drm/drm_simple_kms_helper.h>
+#include <drm/drm_encoder.h>
+#include <drm/drm_plane_helper.h>
#include <linux/regmap.h>
@@ -42,8 +45,10 @@ struct ssd130x_deviceinfo {
struct ssd130x_device {
struct drm_device drm;
struct device *dev;
- struct drm_simple_display_pipe pipe;
struct drm_display_mode mode;
+ struct drm_plane primary_plane;
+ struct drm_crtc crtc;
+ struct drm_encoder encoder;
struct drm_connector connector;
struct i2c_client *client;
diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index 94883abe0dfd..c65f0a89b6b0 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -14,6 +14,7 @@
#include <linux/regmap.h>
#include <linux/reset.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_of.h>
#include <drm/drm_panel.h>
@@ -275,13 +276,6 @@ drm_encoder_to_sun4i_tv(struct drm_encoder *encoder)
encoder);
}
-static inline struct sun4i_tv *
-drm_connector_to_sun4i_tv(struct drm_connector *connector)
-{
- return container_of(connector, struct sun4i_tv,
- connector);
-}
-
/*
* FIXME: If only the drm_display_mode private field was usable, this
* could go away...
@@ -339,7 +333,8 @@ static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
mode->vtotal = mode->vsync_end + tv_mode->vback_porch;
}
-static void sun4i_tv_disable(struct drm_encoder *encoder)
+static void sun4i_tv_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
@@ -353,27 +348,18 @@ static void sun4i_tv_disable(struct drm_encoder *encoder)
sunxi_engine_disable_color_correction(crtc->engine);
}
-static void sun4i_tv_enable(struct drm_encoder *encoder)
+static void sun4i_tv_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
struct sun4i_crtc *crtc = drm_crtc_to_sun4i_crtc(encoder->crtc);
+ struct drm_crtc_state *crtc_state =
+ drm_atomic_get_new_crtc_state(state, encoder->crtc);
+ struct drm_display_mode *mode = &crtc_state->mode;
+ const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
DRM_DEBUG_DRIVER("Enabling the TV Output\n");
- sunxi_engine_apply_color_correction(crtc->engine);
-
- regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
- SUN4I_TVE_EN_ENABLE,
- SUN4I_TVE_EN_ENABLE);
-}
-
-static void sun4i_tv_mode_set(struct drm_encoder *encoder,
- struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- struct sun4i_tv *tv = drm_encoder_to_sun4i_tv(encoder);
- const struct tv_mode *tv_mode = sun4i_tv_find_tv_by_mode(mode);
-
/* Enable and map the DAC to the output */
regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
SUN4I_TVE_EN_DAC_MAP_MASK,
@@ -466,12 +452,17 @@ static void sun4i_tv_mode_set(struct drm_encoder *encoder,
SUN4I_TVE_RESYNC_FIELD : 0));
regmap_write(tv->regs, SUN4I_TVE_SLAVE_REG, 0);
+
+ sunxi_engine_apply_color_correction(crtc->engine);
+
+ regmap_update_bits(tv->regs, SUN4I_TVE_EN_REG,
+ SUN4I_TVE_EN_ENABLE,
+ SUN4I_TVE_EN_ENABLE);
}
static const struct drm_encoder_helper_funcs sun4i_tv_helper_funcs = {
- .disable = sun4i_tv_disable,
- .enable = sun4i_tv_enable,
- .mode_set = sun4i_tv_mode_set,
+ .atomic_disable = sun4i_tv_disable,
+ .atomic_enable = sun4i_tv_enable,
};
static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
@@ -497,27 +488,13 @@ static int sun4i_tv_comp_get_modes(struct drm_connector *connector)
return i;
}
-static int sun4i_tv_comp_mode_valid(struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- /* TODO */
- return MODE_OK;
-}
-
static const struct drm_connector_helper_funcs sun4i_tv_comp_connector_helper_funcs = {
.get_modes = sun4i_tv_comp_get_modes,
- .mode_valid = sun4i_tv_comp_mode_valid,
};
-static void
-sun4i_tv_comp_connector_destroy(struct drm_connector *connector)
-{
- drm_connector_cleanup(connector);
-}
-
static const struct drm_connector_funcs sun4i_tv_comp_connector_funcs = {
.fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = sun4i_tv_comp_connector_destroy,
+ .destroy = drm_connector_cleanup,
.reset = drm_atomic_helper_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
@@ -604,7 +581,7 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
if (ret) {
dev_err(dev,
"Couldn't initialise the Composite connector\n");
- goto err_cleanup_connector;
+ goto err_cleanup_encoder;
}
tv->connector.interlace_allowed = true;
@@ -612,7 +589,7 @@ static int sun4i_tv_bind(struct device *dev, struct device *master,
return 0;
-err_cleanup_connector:
+err_cleanup_encoder:
drm_encoder_cleanup(&tv->encoder);
err_disable_clk:
clk_disable_unprepare(tv->clk);
@@ -629,6 +606,7 @@ static void sun4i_tv_unbind(struct device *dev, struct device *master,
drm_connector_cleanup(&tv->connector);
drm_encoder_cleanup(&tv->encoder);
clk_disable_unprepare(tv->clk);
+ reset_control_assert(tv->reset);
}
static const struct component_ops sun4i_tv_ops = {
diff --git a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
index 59b29cdfdd35..09b806e27506 100644
--- a/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
+++ b/drivers/gpu/drm/tests/drm_cmdline_parser_test.c
@@ -16,7 +16,7 @@ static void drm_cmdline_test_force_e_only(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "e";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -34,7 +34,7 @@ static void drm_cmdline_test_force_D_only_not_digital(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "D";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -56,7 +56,7 @@ static void drm_cmdline_test_force_D_only_hdmi(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "D";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&connector_hdmi, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -78,7 +78,7 @@ static void drm_cmdline_test_force_D_only_dvi(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "D";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&connector_dvi, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -96,7 +96,7 @@ static void drm_cmdline_test_force_d_only(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "d";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -109,30 +109,12 @@ static void drm_cmdline_test_force_d_only(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_OFF);
}
-static void drm_cmdline_test_margin_only(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "m";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_interlace_only(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "i";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_res(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -149,48 +131,12 @@ static void drm_cmdline_test_res(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
-static void drm_cmdline_test_res_missing_x(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "x480";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_res_missing_y(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "1024x";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_res_bad_y(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "1024xtest";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_res_missing_y_bpp(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "1024x-24";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_res_vesa(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480M";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -212,7 +158,7 @@ static void drm_cmdline_test_res_vesa_rblank(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480MR";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -234,7 +180,7 @@ static void drm_cmdline_test_res_rblank(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480R";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -256,7 +202,7 @@ static void drm_cmdline_test_res_bpp(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480-24";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -274,21 +220,12 @@ static void drm_cmdline_test_res_bpp(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
-static void drm_cmdline_test_res_bad_bpp(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480-test";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_res_refresh(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480@60";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -306,21 +243,12 @@ static void drm_cmdline_test_res_refresh(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
-static void drm_cmdline_test_res_bad_refresh(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480@refresh";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_res_bpp_refresh(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480-24@60";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -344,7 +272,7 @@ static void drm_cmdline_test_res_bpp_refresh_interlaced(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480-24@60i";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -366,9 +294,9 @@ static void drm_cmdline_test_res_bpp_refresh_interlaced(struct kunit *test)
static void drm_cmdline_test_res_bpp_refresh_margins(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480-24@60m";
+ const char *cmdline = "720x480-24@60m";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -390,9 +318,9 @@ static void drm_cmdline_test_res_bpp_refresh_margins(struct kunit *test)
static void drm_cmdline_test_res_bpp_refresh_force_off(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480-24@60d";
+ const char *cmdline = "720x480-24@60d";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -411,21 +339,12 @@ static void drm_cmdline_test_res_bpp_refresh_force_off(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_OFF);
}
-static void drm_cmdline_test_res_bpp_refresh_force_on_off(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480-24@60de";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_res_bpp_refresh_force_on(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480-24@60e";
+ const char *cmdline = "720x480-24@60e";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -449,7 +368,7 @@ static void drm_cmdline_test_res_bpp_refresh_force_on_analog(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480-24@60D";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -476,7 +395,7 @@ static void drm_cmdline_test_res_bpp_refresh_force_on_digital(struct kunit *test
};
const char *cmdline = "720x480-24@60D";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -524,7 +443,7 @@ static void drm_cmdline_test_res_margins_force_on(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480me";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -546,7 +465,7 @@ static void drm_cmdline_test_res_vesa_margins(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480Mm";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -563,30 +482,12 @@ static void drm_cmdline_test_res_vesa_margins(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
-static void drm_cmdline_test_res_invalid_mode(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480f";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_res_bpp_wrong_place_mode(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480e-24";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_name(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "NTSC";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_STREQ(test, mode.name, "NTSC");
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -598,7 +499,7 @@ static void drm_cmdline_test_name_bpp(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "NTSC-24";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_STREQ(test, mode.name, "NTSC");
@@ -608,48 +509,12 @@ static void drm_cmdline_test_name_bpp(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.bpp, 24);
}
-static void drm_cmdline_test_name_bpp_refresh(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "NTSC-24@60";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_name_refresh(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "NTSC@60";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_name_refresh_wrong_mode(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "NTSC@60m";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_name_refresh_invalid_mode(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "NTSC@60f";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_name_option(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "NTSC,rotate=180";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_STREQ(test, mode.name, "NTSC");
@@ -661,7 +526,7 @@ static void drm_cmdline_test_name_bpp_option(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "NTSC-24,rotate=180";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_STREQ(test, mode.name, "NTSC");
@@ -675,7 +540,7 @@ static void drm_cmdline_test_rotate_0(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,rotate=0";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -698,7 +563,7 @@ static void drm_cmdline_test_rotate_90(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,rotate=90";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -721,7 +586,7 @@ static void drm_cmdline_test_rotate_180(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,rotate=180";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -744,7 +609,7 @@ static void drm_cmdline_test_rotate_270(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,rotate=270";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -762,39 +627,12 @@ static void drm_cmdline_test_rotate_270(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
-static void drm_cmdline_test_rotate_multiple(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480,rotate=0,rotate=90";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_rotate_invalid_val(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480,rotate=42";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
-static void drm_cmdline_test_rotate_truncated(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480,rotate=";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_hmirror(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,reflect_x";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -817,7 +655,7 @@ static void drm_cmdline_test_vmirror(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,reflect_y";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -841,7 +679,7 @@ static void drm_cmdline_test_margin_options(struct kunit *test)
const char *cmdline =
"720x480,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -867,7 +705,7 @@ static void drm_cmdline_test_multiple_options(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480,rotate=270,reflect_x";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -885,21 +723,12 @@ static void drm_cmdline_test_multiple_options(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
-static void drm_cmdline_test_invalid_option(struct kunit *test)
-{
- struct drm_cmdline_mode mode = { };
- const char *cmdline = "720x480,test=42";
-
- KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(cmdline,
- &no_connector, &mode));
-}
-
static void drm_cmdline_test_bpp_extra_and_option(struct kunit *test)
{
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480-24e,rotate=180";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -923,7 +752,7 @@ static void drm_cmdline_test_extra_and_option(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "720x480e,rotate=180";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_TRUE(test, mode.specified);
KUNIT_EXPECT_EQ(test, mode.xres, 720);
@@ -945,7 +774,7 @@ static void drm_cmdline_test_freestanding_options(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "margin_right=14,margin_left=24,margin_bottom=36,margin_top=42";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -968,7 +797,7 @@ static void drm_cmdline_test_freestanding_force_e_and_options(struct kunit *test
struct drm_cmdline_mode mode = { };
const char *cmdline = "e,margin_right=14,margin_left=24,margin_bottom=36,margin_top=42";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -991,7 +820,7 @@ static void drm_cmdline_test_panel_orientation(struct kunit *test)
struct drm_cmdline_mode mode = { };
const char *cmdline = "panel_orientation=upside_down";
- KUNIT_EXPECT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
+ KUNIT_ASSERT_TRUE(test, drm_mode_parse_command_line_for_connector(cmdline,
&no_connector, &mode));
KUNIT_EXPECT_FALSE(test, mode.specified);
KUNIT_EXPECT_FALSE(test, mode.refresh_specified);
@@ -1006,64 +835,148 @@ static void drm_cmdline_test_panel_orientation(struct kunit *test)
KUNIT_EXPECT_EQ(test, mode.force, DRM_FORCE_UNSPECIFIED);
}
+struct drm_cmdline_invalid_test {
+ const char *name;
+ const char *cmdline;
+};
+
+static void drm_cmdline_test_invalid(struct kunit *test)
+{
+ const struct drm_cmdline_invalid_test *params = test->param_value;
+ struct drm_cmdline_mode mode = { };
+
+ KUNIT_EXPECT_FALSE(test, drm_mode_parse_command_line_for_connector(params->cmdline,
+ &no_connector,
+ &mode));
+}
+
+static const struct drm_cmdline_invalid_test drm_cmdline_invalid_tests[] = {
+ {
+ .name = "margin_only",
+ .cmdline = "m",
+ },
+ {
+ .name = "interlace_only",
+ .cmdline = "i",
+ },
+ {
+ .name = "res_missing_x",
+ .cmdline = "x480",
+ },
+ {
+ .name = "res_missing_y",
+ .cmdline = "1024x",
+ },
+ {
+ .name = "res_bad_y",
+ .cmdline = "1024xtest",
+ },
+ {
+ .name = "res_missing_y_bpp",
+ .cmdline = "1024x-24",
+ },
+ {
+ .name = "res_bad_bpp",
+ .cmdline = "720x480-test",
+ },
+ {
+ .name = "res_bad_refresh",
+ .cmdline = "720x480@refresh",
+ },
+ {
+ .name = "res_bpp_refresh_force_on_off",
+ .cmdline = "720x480-24@60de",
+ },
+ {
+ .name = "res_invalid_mode",
+ .cmdline = "720x480f",
+ },
+ {
+ .name = "res_bpp_wrong_place_mode",
+ .cmdline = "720x480e-24",
+ },
+ {
+ .name = "name_bpp_refresh",
+ .cmdline = "NTSC-24@60",
+ },
+ {
+ .name = "name_refresh",
+ .cmdline = "NTSC@60",
+ },
+ {
+ .name = "name_refresh_wrong_mode",
+ .cmdline = "NTSC@60m",
+ },
+ {
+ .name = "name_refresh_invalid_mode",
+ .cmdline = "NTSC@60f",
+ },
+ {
+ .name = "rotate_multiple",
+ .cmdline = "720x480,rotate=0,rotate=90",
+ },
+ {
+ .name = "rotate_invalid_val",
+ .cmdline = "720x480,rotate=42",
+ },
+ {
+ .name = "rotate_truncated",
+ .cmdline = "720x480,rotate=",
+ },
+ {
+ .name = "invalid_option",
+ .cmdline = "720x480,test=42",
+ },
+};
+
+static void drm_cmdline_invalid_desc(const struct drm_cmdline_invalid_test *t,
+ char *desc)
+{
+ sprintf(desc, "%s", t->name);
+}
+
+KUNIT_ARRAY_PARAM(drm_cmdline_invalid, drm_cmdline_invalid_tests, drm_cmdline_invalid_desc);
+
static struct kunit_case drm_cmdline_parser_tests[] = {
KUNIT_CASE(drm_cmdline_test_force_d_only),
KUNIT_CASE(drm_cmdline_test_force_D_only_dvi),
KUNIT_CASE(drm_cmdline_test_force_D_only_hdmi),
KUNIT_CASE(drm_cmdline_test_force_D_only_not_digital),
KUNIT_CASE(drm_cmdline_test_force_e_only),
- KUNIT_CASE(drm_cmdline_test_margin_only),
- KUNIT_CASE(drm_cmdline_test_interlace_only),
KUNIT_CASE(drm_cmdline_test_res),
- KUNIT_CASE(drm_cmdline_test_res_missing_x),
- KUNIT_CASE(drm_cmdline_test_res_missing_y),
- KUNIT_CASE(drm_cmdline_test_res_bad_y),
- KUNIT_CASE(drm_cmdline_test_res_missing_y_bpp),
KUNIT_CASE(drm_cmdline_test_res_vesa),
KUNIT_CASE(drm_cmdline_test_res_vesa_rblank),
KUNIT_CASE(drm_cmdline_test_res_rblank),
KUNIT_CASE(drm_cmdline_test_res_bpp),
- KUNIT_CASE(drm_cmdline_test_res_bad_bpp),
KUNIT_CASE(drm_cmdline_test_res_refresh),
- KUNIT_CASE(drm_cmdline_test_res_bad_refresh),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_interlaced),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_margins),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_off),
- KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on_off),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on_analog),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_force_on_digital),
KUNIT_CASE(drm_cmdline_test_res_bpp_refresh_interlaced_margins_force_on),
KUNIT_CASE(drm_cmdline_test_res_margins_force_on),
KUNIT_CASE(drm_cmdline_test_res_vesa_margins),
- KUNIT_CASE(drm_cmdline_test_res_invalid_mode),
- KUNIT_CASE(drm_cmdline_test_res_bpp_wrong_place_mode),
KUNIT_CASE(drm_cmdline_test_name),
KUNIT_CASE(drm_cmdline_test_name_bpp),
- KUNIT_CASE(drm_cmdline_test_name_refresh),
- KUNIT_CASE(drm_cmdline_test_name_bpp_refresh),
- KUNIT_CASE(drm_cmdline_test_name_refresh_wrong_mode),
- KUNIT_CASE(drm_cmdline_test_name_refresh_invalid_mode),
KUNIT_CASE(drm_cmdline_test_name_option),
KUNIT_CASE(drm_cmdline_test_name_bpp_option),
KUNIT_CASE(drm_cmdline_test_rotate_0),
KUNIT_CASE(drm_cmdline_test_rotate_90),
KUNIT_CASE(drm_cmdline_test_rotate_180),
KUNIT_CASE(drm_cmdline_test_rotate_270),
- KUNIT_CASE(drm_cmdline_test_rotate_multiple),
- KUNIT_CASE(drm_cmdline_test_rotate_invalid_val),
- KUNIT_CASE(drm_cmdline_test_rotate_truncated),
KUNIT_CASE(drm_cmdline_test_hmirror),
KUNIT_CASE(drm_cmdline_test_vmirror),
KUNIT_CASE(drm_cmdline_test_margin_options),
KUNIT_CASE(drm_cmdline_test_multiple_options),
- KUNIT_CASE(drm_cmdline_test_invalid_option),
KUNIT_CASE(drm_cmdline_test_bpp_extra_and_option),
KUNIT_CASE(drm_cmdline_test_extra_and_option),
KUNIT_CASE(drm_cmdline_test_freestanding_options),
KUNIT_CASE(drm_cmdline_test_freestanding_force_e_and_options),
KUNIT_CASE(drm_cmdline_test_panel_orientation),
+ KUNIT_CASE_PARAM(drm_cmdline_test_invalid, drm_cmdline_invalid_gen_params),
{}
};
diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c
index 08de13774862..a51262289aef 100644
--- a/drivers/gpu/drm/tiny/bochs.c
+++ b/drivers/gpu/drm/tiny/bochs.c
@@ -309,6 +309,8 @@ static void bochs_hw_fini(struct drm_device *dev)
static void bochs_hw_blank(struct bochs_device *bochs, bool blank)
{
DRM_DEBUG_DRIVER("hw_blank %d\n", blank);
+ /* enable color bit (so VGA_IS1_RC access works) */
+ bochs_vga_writeb(bochs, VGA_MIS_W, VGA_MIS_COLOR);
/* discard ar_flip_flop */
(void)bochs_vga_readb(bochs, VGA_IS1_RC);
/* blank or unblank; we need only update index and set 0x20 */
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 590110fdf59c..7c8e8be774f1 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -518,6 +518,9 @@ out:
bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
const struct ttm_place *place)
{
+ struct ttm_resource *res = bo->resource;
+ struct ttm_device *bdev = bo->bdev;
+
dma_resv_assert_held(bo->base.resv);
if (bo->resource->mem_type == TTM_PL_SYSTEM)
return true;
@@ -525,11 +528,7 @@ bool ttm_bo_eviction_valuable(struct ttm_buffer_object *bo,
/* Don't evict this BO if it's outside of the
* requested placement range
*/
- if (place->fpfn >= (bo->resource->start + bo->resource->num_pages) ||
- (place->lpfn && place->lpfn <= bo->resource->start))
- return false;
-
- return true;
+ return ttm_resource_intersects(bdev, res, place, bo->base.size);
}
EXPORT_SYMBOL(ttm_bo_eviction_valuable);
diff --git a/drivers/gpu/drm/ttm/ttm_range_manager.c b/drivers/gpu/drm/ttm/ttm_range_manager.c
index d91666721dc6..4cfef2b3514d 100644
--- a/drivers/gpu/drm/ttm/ttm_range_manager.c
+++ b/drivers/gpu/drm/ttm/ttm_range_manager.c
@@ -113,6 +113,37 @@ static void ttm_range_man_free(struct ttm_resource_manager *man,
kfree(node);
}
+static bool ttm_range_man_intersects(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct drm_mm_node *node = &to_ttm_range_mgr_node(res)->mm_nodes[0];
+ u32 num_pages = PFN_UP(size);
+
+ /* Don't evict BOs outside of the requested placement range */
+ if (place->fpfn >= (node->start + num_pages) ||
+ (place->lpfn && place->lpfn <= node->start))
+ return false;
+
+ return true;
+}
+
+static bool ttm_range_man_compatible(struct ttm_resource_manager *man,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct drm_mm_node *node = &to_ttm_range_mgr_node(res)->mm_nodes[0];
+ u32 num_pages = PFN_UP(size);
+
+ if (node->start < place->fpfn ||
+ (place->lpfn && (node->start + num_pages) > place->lpfn))
+ return false;
+
+ return true;
+}
+
static void ttm_range_man_debug(struct ttm_resource_manager *man,
struct drm_printer *printer)
{
@@ -126,6 +157,8 @@ static void ttm_range_man_debug(struct ttm_resource_manager *man,
static const struct ttm_resource_manager_func ttm_range_manager_func = {
.alloc = ttm_range_man_alloc,
.free = ttm_range_man_free,
+ .intersects = ttm_range_man_intersects,
+ .compatible = ttm_range_man_compatible,
.debug = ttm_range_man_debug
};
diff --git a/drivers/gpu/drm/ttm/ttm_resource.c b/drivers/gpu/drm/ttm/ttm_resource.c
index 20f9adcc3235..a729c32a1e48 100644
--- a/drivers/gpu/drm/ttm/ttm_resource.c
+++ b/drivers/gpu/drm/ttm/ttm_resource.c
@@ -253,10 +253,71 @@ void ttm_resource_free(struct ttm_buffer_object *bo, struct ttm_resource **res)
}
EXPORT_SYMBOL(ttm_resource_free);
+/**
+ * ttm_resource_intersects - test for intersection
+ *
+ * @bdev: TTM device structure
+ * @res: The resource to test
+ * @place: The placement to test
+ * @size: How many bytes the new allocation needs.
+ *
+ * Test if @res intersects with @place and @size. Used for testing if evictions
+ * are valueable or not.
+ *
+ * Returns true if the res placement intersects with @place and @size.
+ */
+bool ttm_resource_intersects(struct ttm_device *bdev,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct ttm_resource_manager *man;
+
+ if (!res)
+ return false;
+
+ man = ttm_manager_type(bdev, res->mem_type);
+ if (!place || !man->func->intersects)
+ return true;
+
+ return man->func->intersects(man, res, place, size);
+}
+
+/**
+ * ttm_resource_compatible - test for compatibility
+ *
+ * @bdev: TTM device structure
+ * @res: The resource to test
+ * @place: The placement to test
+ * @size: How many bytes the new allocation needs.
+ *
+ * Test if @res compatible with @place and @size.
+ *
+ * Returns true if the res placement compatible with @place and @size.
+ */
+bool ttm_resource_compatible(struct ttm_device *bdev,
+ struct ttm_resource *res,
+ const struct ttm_place *place,
+ size_t size)
+{
+ struct ttm_resource_manager *man;
+
+ if (!res || !place)
+ return false;
+
+ man = ttm_manager_type(bdev, res->mem_type);
+ if (!man->func->compatible)
+ return true;
+
+ return man->func->compatible(man, res, place, size);
+}
+
static bool ttm_resource_places_compat(struct ttm_resource *res,
const struct ttm_place *places,
unsigned num_placement)
{
+ struct ttm_buffer_object *bo = res->bo;
+ struct ttm_device *bdev = bo->bdev;
unsigned i;
if (res->placement & TTM_PL_FLAG_TEMPORARY)
@@ -265,8 +326,7 @@ static bool ttm_resource_places_compat(struct ttm_resource *res,
for (i = 0; i < num_placement; i++) {
const struct ttm_place *heap = &places[i];
- if (res->start < heap->fpfn || (heap->lpfn &&
- (res->start + res->num_pages) > heap->lpfn))
+ if (!ttm_resource_compatible(bdev, res, heap, bo->base.size))
continue;
if ((res->mem_type == heap->mem_type) &&
diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c
index 79d790ae1670..04db72e3fa9c 100644
--- a/drivers/gpu/drm/tve200/tve200_drv.c
+++ b/drivers/gpu/drm/tve200/tve200_drv.c
@@ -64,7 +64,7 @@ static int tve200_modeset_init(struct drm_device *dev)
struct tve200_drm_dev_private *priv = dev->dev_private;
struct drm_panel *panel;
struct drm_bridge *bridge;
- int ret = 0;
+ int ret;
drm_mode_config_init(dev);
mode_config = &dev->mode_config;
@@ -92,6 +92,7 @@ static int tve200_modeset_init(struct drm_device *dev)
* method to get the connector out of the bridge.
*/
dev_err(dev->dev, "the bridge is not a panel\n");
+ ret = -EINVAL;
goto out_bridge;
}
diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c
index 2def6e2ad6f0..0108613e79d5 100644
--- a/drivers/gpu/drm/vc4/vc4_crtc.c
+++ b/drivers/gpu/drm/vc4/vc4_crtc.c
@@ -39,6 +39,7 @@
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_drv.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_vblank.h>
@@ -295,10 +296,17 @@ struct drm_encoder *vc4_get_crtc_encoder(struct drm_crtc *crtc,
static void vc4_crtc_pixelvalve_reset(struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ int idx;
+
+ if (!drm_dev_enter(dev, &idx))
+ return;
/* The PV needs to be disabled before it can be flushed */
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) & ~PV_CONTROL_EN);
CRTC_WRITE(PV_CONTROL, CRTC_READ(PV_CONTROL) | PV_CONTROL_FIFO_CLR);
+
+ drm_dev_exit(idx);
}
static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encoder,
@@ -321,6 +329,10 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
u32 format = is_dsi1 ? PV_CONTROL_FORMAT_DSIV_24 : PV_CONTROL_FORMAT_24;
u8 ppc = pv_data->pixels_per_clock;
bool debug_dump_regs = false;
+ int idx;
+
+ if (!drm_dev_enter(dev, &idx))
+ return;
if (debug_dump_regs) {
struct drm_printer p = drm_info_printer(&vc4_crtc->pdev->dev);
@@ -410,6 +422,8 @@ static void vc4_crtc_config_pv(struct drm_crtc *crtc, struct drm_encoder *encode
drm_crtc_index(crtc));
drm_print_regset32(&p, &vc4_crtc->regset);
}
+
+ drm_dev_exit(idx);
}
static void require_hvs_enabled(struct drm_device *dev)
@@ -430,7 +444,10 @@ static int vc4_crtc_disable(struct drm_crtc *crtc,
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct vc4_dev *vc4 = to_vc4_dev(dev);
- int ret;
+ int idx, ret;
+
+ if (!drm_dev_enter(dev, &idx))
+ return -ENODEV;
CRTC_WRITE(PV_V_CONTROL,
CRTC_READ(PV_V_CONTROL) & ~PV_VCONTROL_VIDEN);
@@ -464,6 +481,8 @@ static int vc4_crtc_disable(struct drm_crtc *crtc,
if (vc4_encoder && vc4_encoder->post_crtc_powerdown)
vc4_encoder->post_crtc_powerdown(encoder, state);
+ drm_dev_exit(idx);
+
return 0;
}
@@ -588,10 +607,14 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
struct drm_encoder *encoder = vc4_get_crtc_encoder(crtc, new_state);
struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder);
+ int idx;
drm_dbg(dev, "Enabling CRTC %s (%u) connected to Encoder %s (%u)",
crtc->name, crtc->base.id, encoder->name, encoder->base.id);
+ if (!drm_dev_enter(dev, &idx))
+ return;
+
require_hvs_enabled(dev);
/* Enable vblank irq handling before crtc is started otherwise
@@ -619,6 +642,8 @@ static void vc4_crtc_atomic_enable(struct drm_crtc *crtc,
if (vc4_encoder->post_crtc_enable)
vc4_encoder->post_crtc_enable(encoder, state);
+
+ drm_dev_exit(idx);
}
static enum drm_mode_status vc4_crtc_mode_valid(struct drm_crtc *crtc,
@@ -711,17 +736,31 @@ static int vc4_crtc_atomic_check(struct drm_crtc *crtc,
static int vc4_enable_vblank(struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ int idx;
+
+ if (!drm_dev_enter(dev, &idx))
+ return -ENODEV;
CRTC_WRITE(PV_INTEN, PV_INT_VFP_START);
+ drm_dev_exit(idx);
+
return 0;
}
static void vc4_disable_vblank(struct drm_crtc *crtc)
{
struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ int idx;
+
+ if (!drm_dev_enter(dev, &idx))
+ return;
CRTC_WRITE(PV_INTEN, 0);
+
+ drm_dev_exit(idx);
}
static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc)
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index fda450185c36..25299fbc083b 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1425,7 +1425,7 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
mutex_lock(&vc4_hdmi->mutex);
if (!drm_dev_enter(drm, &idx))
- return;
+ goto out;
if (vc4_hdmi->variant->csc_setup)
vc4_hdmi->variant->csc_setup(vc4_hdmi, conn_state, mode);
@@ -1436,6 +1436,7 @@ static void vc4_hdmi_encoder_pre_crtc_enable(struct drm_encoder *encoder,
drm_dev_exit(idx);
+out:
mutex_unlock(&vc4_hdmi->mutex);
}
@@ -1455,7 +1456,7 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
mutex_lock(&vc4_hdmi->mutex);
if (!drm_dev_enter(drm, &idx))
- return;
+ goto out;
spin_lock_irqsave(&vc4_hdmi->hw_lock, flags);
@@ -1516,6 +1517,8 @@ static void vc4_hdmi_encoder_post_crtc_enable(struct drm_encoder *encoder,
vc4_hdmi_enable_scrambling(encoder);
drm_dev_exit(idx);
+
+out:
mutex_unlock(&vc4_hdmi->mutex);
}
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 9e823e0de197..4ac9f5a2d5f9 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -71,11 +71,11 @@ void vc4_hvs_dump_state(struct vc4_hvs *hvs)
struct drm_printer p = drm_info_printer(&hvs->pdev->dev);
int idx, i;
- drm_print_regset32(&p, &hvs->regset);
-
if (!drm_dev_enter(drm, &idx))
return;
+ drm_print_regset32(&p, &hvs->regset);
+
DRM_INFO("HVS ctx:\n");
for (i = 0; i < 64; i += 4) {
DRM_INFO("0x%08x (%s): 0x%08x 0x%08x 0x%08x 0x%08x\n",
diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c
index eff9c63adfa7..8b92a45a3c89 100644
--- a/drivers/gpu/drm/vc4/vc4_plane.c
+++ b/drivers/gpu/drm/vc4/vc4_plane.c
@@ -19,6 +19,7 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic_uapi.h>
#include <drm/drm_blend.h>
+#include <drm/drm_drv.h>
#include <drm/drm_fb_dma_helper.h>
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
@@ -1219,6 +1220,10 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
{
struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
int i;
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ goto out;
vc4_state->hw_dlist = dlist;
@@ -1226,6 +1231,9 @@ u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
for (i = 0; i < vc4_state->dlist_count; i++)
writel(vc4_state->dlist[i], &dlist[i]);
+ drm_dev_exit(idx);
+
+out:
return vc4_state->dlist_count;
}
@@ -1245,6 +1253,10 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
uint32_t addr;
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ return;
/* We're skipping the address adjustment for negative origin,
* because this is only called on the primary plane.
@@ -1263,6 +1275,8 @@ void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
* also use our updated address.
*/
vc4_state->dlist[vc4_state->ptr0_offset] = addr;
+
+ drm_dev_exit(idx);
}
static void vc4_plane_atomic_async_update(struct drm_plane *plane,
@@ -1271,6 +1285,10 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane,
struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
plane);
struct vc4_plane_state *vc4_state, *new_vc4_state;
+ int idx;
+
+ if (!drm_dev_enter(plane->dev, &idx))
+ return;
swap(plane->state->fb, new_plane_state->fb);
plane->state->crtc_x = new_plane_state->crtc_x;
@@ -1333,6 +1351,8 @@ static void vc4_plane_atomic_async_update(struct drm_plane *plane,
&vc4_state->hw_dlist[vc4_state->pos2_offset]);
writel(vc4_state->dlist[vc4_state->ptr0_offset],
&vc4_state->hw_dlist[vc4_state->ptr0_offset]);
+
+ drm_dev_exit(idx);
}
static int vc4_plane_atomic_async_check(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 4a788c1c9058..0b3333865702 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -171,8 +171,6 @@ struct vc4_vec {
struct clk *clock;
- const struct vc4_vec_tv_mode *tv_mode;
-
struct debugfs_regset32 regset;
};
@@ -194,7 +192,9 @@ enum vc4_vec_tv_mode_id {
struct vc4_vec_tv_mode {
const struct drm_display_mode *mode;
- void (*mode_set)(struct vc4_vec *vec);
+ u32 config0;
+ u32 config1;
+ u32 custom_freq;
};
static const struct debugfs_reg32 vec_regs[] = {
@@ -224,95 +224,41 @@ static const struct debugfs_reg32 vec_regs[] = {
VC4_REG32(VEC_DAC_MISC),
};
-static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec)
-{
- struct drm_device *drm = vec->connector.dev;
- int idx;
-
- if (!drm_dev_enter(drm, &idx))
- return;
-
- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN);
- VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
-
- drm_dev_exit(idx);
-}
-
-static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec)
-{
- struct drm_device *drm = vec->connector.dev;
- int idx;
-
- if (!drm_dev_enter(drm, &idx))
- return;
-
- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD);
- VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
-
- drm_dev_exit(idx);
-}
-
static const struct drm_display_mode ntsc_mode = {
DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
- 480, 480 + 3, 480 + 3 + 3, 480 + 3 + 3 + 16, 0,
+ 480, 480 + 7, 480 + 7 + 6, 525, 0,
DRM_MODE_FLAG_INTERLACE)
};
-static void vc4_vec_pal_mode_set(struct vc4_vec *vec)
-{
- struct drm_device *drm = vec->connector.dev;
- int idx;
-
- if (!drm_dev_enter(drm, &idx))
- return;
-
- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
- VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS);
-
- drm_dev_exit(idx);
-}
-
-static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec)
-{
- struct drm_device *drm = vec->connector.dev;
- int idx;
-
- if (!drm_dev_enter(drm, &idx))
- return;
-
- VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD);
- VEC_WRITE(VEC_CONFIG1,
- VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ);
- VEC_WRITE(VEC_FREQ3_2, 0x223b);
- VEC_WRITE(VEC_FREQ1_0, 0x61d1);
-
- drm_dev_exit(idx);
-}
-
static const struct drm_display_mode pal_mode = {
DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
- 576, 576 + 2, 576 + 2 + 3, 576 + 2 + 3 + 20, 0,
+ 576, 576 + 4, 576 + 4 + 6, 625, 0,
DRM_MODE_FLAG_INTERLACE)
};
static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
[VC4_VEC_TV_MODE_NTSC] = {
.mode = &ntsc_mode,
- .mode_set = vc4_vec_ntsc_mode_set,
+ .config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_NTSC_J] = {
.mode = &ntsc_mode,
- .mode_set = vc4_vec_ntsc_j_mode_set,
+ .config0 = VEC_CONFIG0_NTSC_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_PAL] = {
.mode = &pal_mode,
- .mode_set = vc4_vec_pal_mode_set,
+ .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
[VC4_VEC_TV_MODE_PAL_M] = {
.mode = &pal_mode,
- .mode_set = vc4_vec_pal_m_mode_set,
+ .config0 = VEC_CONFIG0_PAL_BDGHI_STD,
+ .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
+ .custom_freq = 0x223b61d1,
},
};
@@ -368,14 +314,14 @@ static int vc4_vec_connector_init(struct drm_device *dev, struct vc4_vec *vec)
drm_object_attach_property(&connector->base,
dev->mode_config.tv_mode_property,
VC4_VEC_TV_MODE_NTSC);
- vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC];
drm_connector_attach_encoder(connector, &vec->encoder.base);
return 0;
}
-static void vc4_vec_encoder_disable(struct drm_encoder *encoder)
+static void vc4_vec_encoder_disable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct drm_device *drm = encoder->dev;
struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
@@ -406,10 +352,16 @@ err_dev_exit:
drm_dev_exit(idx);
}
-static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
+static void vc4_vec_encoder_enable(struct drm_encoder *encoder,
+ struct drm_atomic_state *state)
{
struct drm_device *drm = encoder->dev;
struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
+ struct drm_connector *connector = &vec->connector;
+ struct drm_connector_state *conn_state =
+ drm_atomic_get_new_connector_state(state, connector);
+ const struct vc4_vec_tv_mode *tv_mode =
+ &vc4_vec_tv_modes[conn_state->tv.mode];
int idx, ret;
if (!drm_dev_enter(drm, &idx))
@@ -468,7 +420,15 @@ static void vc4_vec_encoder_enable(struct drm_encoder *encoder)
/* Mask all interrupts. */
VEC_WRITE(VEC_MASK0, 0);
- vec->tv_mode->mode_set(vec);
+ VEC_WRITE(VEC_CONFIG0, tv_mode->config0);
+ VEC_WRITE(VEC_CONFIG1, tv_mode->config1);
+
+ if (tv_mode->custom_freq) {
+ VEC_WRITE(VEC_FREQ3_2,
+ (tv_mode->custom_freq >> 16) & 0xffff);
+ VEC_WRITE(VEC_FREQ1_0,
+ tv_mode->custom_freq & 0xffff);
+ }
VEC_WRITE(VEC_DAC_MISC,
VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N);
@@ -483,23 +443,6 @@ err_dev_exit:
drm_dev_exit(idx);
}
-
-static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
-{
- return true;
-}
-
-static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder,
- struct drm_crtc_state *crtc_state,
- struct drm_connector_state *conn_state)
-{
- struct vc4_vec *vec = encoder_to_vc4_vec(encoder);
-
- vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode];
-}
-
static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
@@ -516,11 +459,9 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder,
}
static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = {
- .disable = vc4_vec_encoder_disable,
- .enable = vc4_vec_encoder_enable,
- .mode_fixup = vc4_vec_encoder_mode_fixup,
.atomic_check = vc4_vec_encoder_atomic_check,
- .atomic_mode_set = vc4_vec_encoder_atomic_mode_set,
+ .atomic_disable = vc4_vec_encoder_disable,
+ .atomic_enable = vc4_vec_encoder_enable,
};
static int vc4_vec_late_register(struct drm_encoder *encoder)
diff --git a/drivers/gpu/drm/via/via_dri1.c b/drivers/gpu/drm/via/via_dri1.c
index f659c0c0465c..217d1e84b0ea 100644
--- a/drivers/gpu/drm/via/via_dri1.c
+++ b/drivers/gpu/drm/via/via_dri1.c
@@ -2961,7 +2961,7 @@ int via_dma_cleanup(struct drm_device *dev)
drm_via_private_t *dev_priv =
(drm_via_private_t *) dev->dev_private;
- if (dev_priv->ring.virtual_start) {
+ if (dev_priv->ring.virtual_start && dev_priv->mmio) {
via_cmdbuf_reset(dev_priv);
drm_legacy_ioremapfree(&dev_priv->ring.map, dev);
diff --git a/drivers/gpu/drm/virtio/virtgpu_display.c b/drivers/gpu/drm/virtio/virtgpu_display.c
index 5c7f198c0712..9ea7611a9e0f 100644
--- a/drivers/gpu/drm/virtio/virtgpu_display.c
+++ b/drivers/gpu/drm/virtio/virtgpu_display.c
@@ -349,6 +349,8 @@ int virtio_gpu_modeset_init(struct virtio_gpu_device *vgdev)
vgdev->ddev->mode_config.max_width = XRES_MAX;
vgdev->ddev->mode_config.max_height = YRES_MAX;
+ vgdev->ddev->mode_config.fb_modifiers_not_supported = true;
+
for (i = 0 ; i < vgdev->num_scanouts; ++i)
vgdev_output_init(vgdev, i);
diff --git a/drivers/gpu/drm/virtio/virtgpu_ioctl.c b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
index 9b2702116f93..3b1701607aae 100644
--- a/drivers/gpu/drm/virtio/virtgpu_ioctl.c
+++ b/drivers/gpu/drm/virtio/virtgpu_ioctl.c
@@ -168,7 +168,7 @@ static int virtio_gpu_execbuffer_ioctl(struct drm_device *dev, void *data,
* array contains any fence from a foreign context.
*/
ret = 0;
- if (!dma_fence_match_context(in_fence, vgdev->fence_drv.context))
+ if (!dma_fence_match_context(in_fence, fence_ctx + ring_idx))
ret = dma_fence_wait(in_fence, true);
dma_fence_put(in_fence);
diff --git a/drivers/gpu/drm/vkms/Makefile b/drivers/gpu/drm/vkms/Makefile
index 72f779cbfedd..1b28a6a32948 100644
--- a/drivers/gpu/drm/vkms/Makefile
+++ b/drivers/gpu/drm/vkms/Makefile
@@ -3,6 +3,7 @@ vkms-y := \
vkms_drv.o \
vkms_plane.o \
vkms_output.o \
+ vkms_formats.o \
vkms_crtc.o \
vkms_composer.o \
vkms_writeback.o
diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c
index 775b97766e08..8e53fa80742b 100644
--- a/drivers/gpu/drm/vkms/vkms_composer.c
+++ b/drivers/gpu/drm/vkms/vkms_composer.c
@@ -7,203 +7,185 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_gem_framebuffer_helper.h>
#include <drm/drm_vblank.h>
+#include <linux/minmax.h>
#include "vkms_drv.h"
-static u32 get_pixel_from_buffer(int x, int y, const u8 *buffer,
- const struct vkms_composer *composer)
+static u16 pre_mul_blend_channel(u16 src, u16 dst, u16 alpha)
{
- u32 pixel;
- int src_offset = composer->offset + (y * composer->pitch)
- + (x * composer->cpp);
+ u32 new_color;
- pixel = *(u32 *)&buffer[src_offset];
+ new_color = (src * 0xffff + dst * (0xffff - alpha));
- return pixel;
+ return DIV_ROUND_CLOSEST(new_color, 0xffff);
}
/**
- * compute_crc - Compute CRC value on output frame
+ * pre_mul_alpha_blend - alpha blending equation
+ * @src_frame_info: source framebuffer's metadata
+ * @stage_buffer: The line with the pixels from src_plane
+ * @output_buffer: A line buffer that receives all the blends output
*
- * @vaddr: address to final framebuffer
- * @composer: framebuffer's metadata
+ * Using the information from the `frame_info`, this blends only the
+ * necessary pixels from the `stage_buffer` to the `output_buffer`
+ * using premultiplied blend formula.
*
- * returns CRC value computed using crc32 on the visible portion of
- * the final framebuffer at vaddr_out
+ * The current DRM assumption is that pixel color values have been already
+ * pre-multiplied with the alpha channel values. See more
+ * drm_plane_create_blend_mode_property(). Also, this formula assumes a
+ * completely opaque background.
*/
-static uint32_t compute_crc(const u8 *vaddr,
- const struct vkms_composer *composer)
+static void pre_mul_alpha_blend(struct vkms_frame_info *frame_info,
+ struct line_buffer *stage_buffer,
+ struct line_buffer *output_buffer)
{
- int x, y;
- u32 crc = 0, pixel = 0;
- int x_src = composer->src.x1 >> 16;
- int y_src = composer->src.y1 >> 16;
- int h_src = drm_rect_height(&composer->src) >> 16;
- int w_src = drm_rect_width(&composer->src) >> 16;
-
- for (y = y_src; y < y_src + h_src; ++y) {
- for (x = x_src; x < x_src + w_src; ++x) {
- pixel = get_pixel_from_buffer(x, y, vaddr, composer);
- crc = crc32_le(crc, (void *)&pixel, sizeof(u32));
- }
+ int x_dst = frame_info->dst.x1;
+ struct pixel_argb_u16 *out = output_buffer->pixels + x_dst;
+ struct pixel_argb_u16 *in = stage_buffer->pixels;
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ stage_buffer->n_pixels);
+
+ for (int x = 0; x < x_limit; x++) {
+ out[x].a = (u16)0xffff;
+ out[x].r = pre_mul_blend_channel(in[x].r, out[x].r, in[x].a);
+ out[x].g = pre_mul_blend_channel(in[x].g, out[x].g, in[x].a);
+ out[x].b = pre_mul_blend_channel(in[x].b, out[x].b, in[x].a);
}
-
- return crc;
}
-static u8 blend_channel(u8 src, u8 dst, u8 alpha)
+static bool check_y_limit(struct vkms_frame_info *frame_info, int y)
{
- u32 pre_blend;
- u8 new_color;
-
- pre_blend = (src * 255 + dst * (255 - alpha));
-
- /* Faster div by 255 */
- new_color = ((pre_blend + ((pre_blend + 257) >> 8)) >> 8);
+ if (y >= frame_info->dst.y1 && y < frame_info->dst.y2)
+ return true;
- return new_color;
+ return false;
}
-/**
- * alpha_blend - alpha blending equation
- * @argb_src: src pixel on premultiplied alpha mode
- * @argb_dst: dst pixel completely opaque
- *
- * blend pixels using premultiplied blend formula. The current DRM assumption
- * is that pixel color values have been already pre-multiplied with the alpha
- * channel values. See more drm_plane_create_blend_mode_property(). Also, this
- * formula assumes a completely opaque background.
- */
-static void alpha_blend(const u8 *argb_src, u8 *argb_dst)
+static void fill_background(const struct pixel_argb_u16 *background_color,
+ struct line_buffer *output_buffer)
{
- u8 alpha;
-
- alpha = argb_src[3];
- argb_dst[0] = blend_channel(argb_src[0], argb_dst[0], alpha);
- argb_dst[1] = blend_channel(argb_src[1], argb_dst[1], alpha);
- argb_dst[2] = blend_channel(argb_src[2], argb_dst[2], alpha);
+ for (size_t i = 0; i < output_buffer->n_pixels; i++)
+ output_buffer->pixels[i] = *background_color;
}
/**
- * x_blend - blending equation that ignores the pixel alpha
+ * @wb_frame_info: The writeback frame buffer metadata
+ * @crtc_state: The crtc state
+ * @crc32: The crc output of the final frame
+ * @output_buffer: A buffer of a row that will receive the result of the blend(s)
+ * @stage_buffer: The line with the pixels from plane being blend to the output
*
- * overwrites RGB color value from src pixel to dst pixel.
+ * This function blends the pixels (Using the `pre_mul_alpha_blend`)
+ * from all planes, calculates the crc32 of the output from the former step,
+ * and, if necessary, convert and store the output to the writeback buffer.
*/
-static void x_blend(const u8 *xrgb_src, u8 *xrgb_dst)
+static void blend(struct vkms_writeback_job *wb,
+ struct vkms_crtc_state *crtc_state,
+ u32 *crc32, struct line_buffer *stage_buffer,
+ struct line_buffer *output_buffer, size_t row_size)
{
- memcpy(xrgb_dst, xrgb_src, sizeof(u8) * 3);
-}
+ struct vkms_plane_state **plane = crtc_state->active_planes;
+ u32 n_active_planes = crtc_state->num_active_planes;
-/**
- * blend - blend value at vaddr_src with value at vaddr_dst
- * @vaddr_dst: destination address
- * @vaddr_src: source address
- * @dst_composer: destination framebuffer's metadata
- * @src_composer: source framebuffer's metadata
- * @pixel_blend: blending equation based on plane format
- *
- * Blend the vaddr_src value with the vaddr_dst value using a pixel blend
- * equation according to the supported plane formats DRM_FORMAT_(A/XRGB8888)
- * and clearing alpha channel to an completely opaque background. This function
- * uses buffer's metadata to locate the new composite values at vaddr_dst.
- *
- * TODO: completely clear the primary plane (a = 0xff) before starting to blend
- * pixel color values
- */
-static void blend(void *vaddr_dst, void *vaddr_src,
- struct vkms_composer *dst_composer,
- struct vkms_composer *src_composer,
- void (*pixel_blend)(const u8 *, u8 *))
-{
- int i, j, j_dst, i_dst;
- int offset_src, offset_dst;
- u8 *pixel_dst, *pixel_src;
-
- int x_src = src_composer->src.x1 >> 16;
- int y_src = src_composer->src.y1 >> 16;
-
- int x_dst = src_composer->dst.x1;
- int y_dst = src_composer->dst.y1;
- int h_dst = drm_rect_height(&src_composer->dst);
- int w_dst = drm_rect_width(&src_composer->dst);
-
- int y_limit = y_src + h_dst;
- int x_limit = x_src + w_dst;
-
- for (i = y_src, i_dst = y_dst; i < y_limit; ++i) {
- for (j = x_src, j_dst = x_dst; j < x_limit; ++j) {
- offset_dst = dst_composer->offset
- + (i_dst * dst_composer->pitch)
- + (j_dst++ * dst_composer->cpp);
- offset_src = src_composer->offset
- + (i * src_composer->pitch)
- + (j * src_composer->cpp);
-
- pixel_src = (u8 *)(vaddr_src + offset_src);
- pixel_dst = (u8 *)(vaddr_dst + offset_dst);
- pixel_blend(pixel_src, pixel_dst);
- /* clearing alpha channel (0xff)*/
- pixel_dst[3] = 0xff;
+ const struct pixel_argb_u16 background_color = { .a = 0xffff };
+
+ size_t crtc_y_limit = crtc_state->base.crtc->mode.vdisplay;
+
+ for (size_t y = 0; y < crtc_y_limit; y++) {
+ fill_background(&background_color, output_buffer);
+
+ /* The active planes are composed associatively in z-order. */
+ for (size_t i = 0; i < n_active_planes; i++) {
+ if (!check_y_limit(plane[i]->frame_info, y))
+ continue;
+
+ plane[i]->plane_read(stage_buffer, plane[i]->frame_info, y);
+ pre_mul_alpha_blend(plane[i]->frame_info, stage_buffer,
+ output_buffer);
}
- i_dst++;
+
+ *crc32 = crc32_le(*crc32, (void *)output_buffer->pixels, row_size);
+
+ if (wb)
+ wb->wb_write(&wb->wb_frame_info, output_buffer, y);
}
}
-static void compose_plane(struct vkms_composer *primary_composer,
- struct vkms_composer *plane_composer,
- void *vaddr_out)
+static int check_format_funcs(struct vkms_crtc_state *crtc_state,
+ struct vkms_writeback_job *active_wb)
{
- struct drm_framebuffer *fb = &plane_composer->fb;
- void *vaddr;
- void (*pixel_blend)(const u8 *p_src, u8 *p_dst);
+ struct vkms_plane_state **planes = crtc_state->active_planes;
+ u32 n_active_planes = crtc_state->num_active_planes;
- if (WARN_ON(iosys_map_is_null(&plane_composer->map[0])))
- return;
+ for (size_t i = 0; i < n_active_planes; i++)
+ if (!planes[i]->plane_read)
+ return -1;
- vaddr = plane_composer->map[0].vaddr;
+ if (active_wb && !active_wb->wb_write)
+ return -1;
- if (fb->format->format == DRM_FORMAT_ARGB8888)
- pixel_blend = &alpha_blend;
- else
- pixel_blend = &x_blend;
+ return 0;
+}
+
+static int check_iosys_map(struct vkms_crtc_state *crtc_state)
+{
+ struct vkms_plane_state **plane_state = crtc_state->active_planes;
+ u32 n_active_planes = crtc_state->num_active_planes;
+
+ for (size_t i = 0; i < n_active_planes; i++)
+ if (iosys_map_is_null(&plane_state[i]->frame_info->map[0]))
+ return -1;
- blend(vaddr_out, vaddr, primary_composer, plane_composer, pixel_blend);
+ return 0;
}
-static int compose_active_planes(void **vaddr_out,
- struct vkms_composer *primary_composer,
- struct vkms_crtc_state *crtc_state)
+static int compose_active_planes(struct vkms_writeback_job *active_wb,
+ struct vkms_crtc_state *crtc_state,
+ u32 *crc32)
{
- struct drm_framebuffer *fb = &primary_composer->fb;
- struct drm_gem_object *gem_obj = drm_gem_fb_get_obj(fb, 0);
- const void *vaddr;
- int i;
-
- if (!*vaddr_out) {
- *vaddr_out = kvzalloc(gem_obj->size, GFP_KERNEL);
- if (!*vaddr_out) {
- DRM_ERROR("Cannot allocate memory for output frame.");
- return -ENOMEM;
- }
- }
+ size_t line_width, pixel_size = sizeof(struct pixel_argb_u16);
+ struct line_buffer output_buffer, stage_buffer;
+ int ret = 0;
+
+ /*
+ * This check exists so we can call `crc32_le` for the entire line
+ * instead doing it for each channel of each pixel in case
+ * `struct `pixel_argb_u16` had any gap added by the compiler
+ * between the struct fields.
+ */
+ static_assert(sizeof(struct pixel_argb_u16) == 8);
- if (WARN_ON(iosys_map_is_null(&primary_composer->map[0])))
+ if (WARN_ON(check_iosys_map(crtc_state)))
return -EINVAL;
- vaddr = primary_composer->map[0].vaddr;
+ if (WARN_ON(check_format_funcs(crtc_state, active_wb)))
+ return -EINVAL;
- memcpy(*vaddr_out, vaddr, gem_obj->size);
+ line_width = crtc_state->base.crtc->mode.hdisplay;
+ stage_buffer.n_pixels = line_width;
+ output_buffer.n_pixels = line_width;
- /* If there are other planes besides primary, we consider the active
- * planes should be in z-order and compose them associatively:
- * ((primary <- overlay) <- cursor)
- */
- for (i = 1; i < crtc_state->num_active_planes; i++)
- compose_plane(primary_composer,
- crtc_state->active_planes[i]->composer,
- *vaddr_out);
+ stage_buffer.pixels = kvmalloc(line_width * pixel_size, GFP_KERNEL);
+ if (!stage_buffer.pixels) {
+ DRM_ERROR("Cannot allocate memory for the output line buffer");
+ return -ENOMEM;
+ }
- return 0;
+ output_buffer.pixels = kvmalloc(line_width * pixel_size, GFP_KERNEL);
+ if (!output_buffer.pixels) {
+ DRM_ERROR("Cannot allocate memory for intermediate line buffer");
+ ret = -ENOMEM;
+ goto free_stage_buffer;
+ }
+
+ blend(active_wb, crtc_state, crc32, &stage_buffer,
+ &output_buffer, line_width * pixel_size);
+
+ kvfree(output_buffer.pixels);
+free_stage_buffer:
+ kvfree(stage_buffer.pixels);
+
+ return ret;
}
/**
@@ -221,13 +203,11 @@ void vkms_composer_worker(struct work_struct *work)
struct vkms_crtc_state,
composer_work);
struct drm_crtc *crtc = crtc_state->base.crtc;
+ struct vkms_writeback_job *active_wb = crtc_state->active_writeback;
struct vkms_output *out = drm_crtc_to_vkms_output(crtc);
- struct vkms_composer *primary_composer = NULL;
- struct vkms_plane_state *act_plane = NULL;
bool crc_pending, wb_pending;
- void *vaddr_out = NULL;
- u32 crc32 = 0;
u64 frame_start, frame_end;
+ u32 crc32 = 0;
int ret;
spin_lock_irq(&out->composer_lock);
@@ -247,35 +227,19 @@ void vkms_composer_worker(struct work_struct *work)
if (!crc_pending)
return;
- if (crtc_state->num_active_planes >= 1) {
- act_plane = crtc_state->active_planes[0];
- if (act_plane->base.base.plane->type == DRM_PLANE_TYPE_PRIMARY)
- primary_composer = act_plane->composer;
- }
-
- if (!primary_composer)
- return;
-
if (wb_pending)
- vaddr_out = crtc_state->active_writeback->data[0].vaddr;
+ ret = compose_active_planes(active_wb, crtc_state, &crc32);
+ else
+ ret = compose_active_planes(NULL, crtc_state, &crc32);
- ret = compose_active_planes(&vaddr_out, primary_composer,
- crtc_state);
- if (ret) {
- if (ret == -EINVAL && !wb_pending)
- kvfree(vaddr_out);
+ if (ret)
return;
- }
-
- crc32 = compute_crc(vaddr_out, primary_composer);
if (wb_pending) {
drm_writeback_signal_completion(&out->wb_connector, 0);
spin_lock_irq(&out->composer_lock);
crtc_state->wb_pending = false;
spin_unlock_irq(&out->composer_lock);
- } else {
- kvfree(vaddr_out);
}
/*
diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h
index 1d60654b553b..0a67b8073f7e 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.h
+++ b/drivers/gpu/drm/vkms/vkms_drv.h
@@ -23,28 +23,41 @@
#define NUM_OVERLAY_PLANES 8
-struct vkms_writeback_job {
- struct iosys_map map[DRM_FORMAT_MAX_PLANES];
- struct iosys_map data[DRM_FORMAT_MAX_PLANES];
-};
-
-struct vkms_composer {
- struct drm_framebuffer fb;
+struct vkms_frame_info {
+ struct drm_framebuffer *fb;
struct drm_rect src, dst;
- struct iosys_map map[4];
+ struct iosys_map map[DRM_FORMAT_MAX_PLANES];
unsigned int offset;
unsigned int pitch;
unsigned int cpp;
};
+struct pixel_argb_u16 {
+ u16 a, r, g, b;
+};
+
+struct line_buffer {
+ size_t n_pixels;
+ struct pixel_argb_u16 *pixels;
+};
+
+struct vkms_writeback_job {
+ struct iosys_map data[DRM_FORMAT_MAX_PLANES];
+ struct vkms_frame_info wb_frame_info;
+ void (*wb_write)(struct vkms_frame_info *frame_info,
+ const struct line_buffer *buffer, int y);
+};
+
/**
* vkms_plane_state - Driver specific plane state
* @base: base plane state
- * @composer: data required for composing computation
+ * @frame_info: data required for composing computation
*/
struct vkms_plane_state {
struct drm_shadow_plane_state base;
- struct vkms_composer *composer;
+ struct vkms_frame_info *frame_info;
+ void (*plane_read)(struct line_buffer *buffer,
+ const struct vkms_frame_info *frame_info, int y);
};
struct vkms_plane {
diff --git a/drivers/gpu/drm/vkms/vkms_formats.c b/drivers/gpu/drm/vkms/vkms_formats.c
new file mode 100644
index 000000000000..300abb4d1dfe
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_formats.c
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <drm/drm_rect.h>
+#include <linux/minmax.h>
+
+#include "vkms_formats.h"
+
+/* The following macros help doing fixed point arithmetic. */
+/*
+ * With Fixed-Point scale 15 we have 17 and 15 bits of integer and fractional
+ * parts respectively.
+ * | 0000 0000 0000 0000 0.000 0000 0000 0000 |
+ * 31 0
+ */
+#define SHIFT 15
+
+#define INT_TO_FIXED(a) ((a) << SHIFT)
+#define FIXED_MUL(a, b) ((s32)(((s64)(a) * (b)) >> SHIFT))
+#define FIXED_DIV(a, b) ((s32)(((s64)(a) << SHIFT) / (b)))
+/* This macro converts a fixed point number to int, and round half up it */
+#define FIXED_TO_INT_ROUND(a) (((a) + (1 << (SHIFT - 1))) >> SHIFT)
+#define INT_TO_FIXED_DIV(a, b) (FIXED_DIV(INT_TO_FIXED(a), INT_TO_FIXED(b)))
+#define INT_TO_FIXED_DIV(a, b) (FIXED_DIV(INT_TO_FIXED(a), INT_TO_FIXED(b)))
+
+static size_t pixel_offset(const struct vkms_frame_info *frame_info, int x, int y)
+{
+ return frame_info->offset + (y * frame_info->pitch)
+ + (x * frame_info->cpp);
+}
+
+/*
+ * packed_pixels_addr - Get the pointer to pixel of a given pair of coordinates
+ *
+ * @frame_info: Buffer metadata
+ * @x: The x(width) coordinate of the 2D buffer
+ * @y: The y(Heigth) coordinate of the 2D buffer
+ *
+ * Takes the information stored in the frame_info, a pair of coordinates, and
+ * returns the address of the first color channel.
+ * This function assumes the channels are packed together, i.e. a color channel
+ * comes immediately after another in the memory. And therefore, this function
+ * doesn't work for YUV with chroma subsampling (e.g. YUV420 and NV21).
+ */
+static void *packed_pixels_addr(const struct vkms_frame_info *frame_info,
+ int x, int y)
+{
+ size_t offset = pixel_offset(frame_info, x, y);
+
+ return (u8 *)frame_info->map[0].vaddr + offset;
+}
+
+static void *get_packed_src_addr(const struct vkms_frame_info *frame_info, int y)
+{
+ int x_src = frame_info->src.x1 >> 16;
+ int y_src = y - frame_info->dst.y1 + (frame_info->src.y1 >> 16);
+
+ return packed_pixels_addr(frame_info, x_src, y_src);
+}
+
+static void ARGB8888_to_argb_u16(struct line_buffer *stage_buffer,
+ const struct vkms_frame_info *frame_info, int y)
+{
+ struct pixel_argb_u16 *out_pixels = stage_buffer->pixels;
+ u8 *src_pixels = get_packed_src_addr(frame_info, y);
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ stage_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, src_pixels += 4) {
+ /*
+ * The 257 is the "conversion ratio". This number is obtained by the
+ * (2^16 - 1) / (2^8 - 1) division. Which, in this case, tries to get
+ * the best color value in a pixel format with more possibilities.
+ * A similar idea applies to others RGB color conversions.
+ */
+ out_pixels[x].a = (u16)src_pixels[3] * 257;
+ out_pixels[x].r = (u16)src_pixels[2] * 257;
+ out_pixels[x].g = (u16)src_pixels[1] * 257;
+ out_pixels[x].b = (u16)src_pixels[0] * 257;
+ }
+}
+
+static void XRGB8888_to_argb_u16(struct line_buffer *stage_buffer,
+ const struct vkms_frame_info *frame_info, int y)
+{
+ struct pixel_argb_u16 *out_pixels = stage_buffer->pixels;
+ u8 *src_pixels = get_packed_src_addr(frame_info, y);
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ stage_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, src_pixels += 4) {
+ out_pixels[x].a = (u16)0xffff;
+ out_pixels[x].r = (u16)src_pixels[2] * 257;
+ out_pixels[x].g = (u16)src_pixels[1] * 257;
+ out_pixels[x].b = (u16)src_pixels[0] * 257;
+ }
+}
+
+static void ARGB16161616_to_argb_u16(struct line_buffer *stage_buffer,
+ const struct vkms_frame_info *frame_info,
+ int y)
+{
+ struct pixel_argb_u16 *out_pixels = stage_buffer->pixels;
+ u16 *src_pixels = get_packed_src_addr(frame_info, y);
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ stage_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, src_pixels += 4) {
+ out_pixels[x].a = le16_to_cpu(src_pixels[3]);
+ out_pixels[x].r = le16_to_cpu(src_pixels[2]);
+ out_pixels[x].g = le16_to_cpu(src_pixels[1]);
+ out_pixels[x].b = le16_to_cpu(src_pixels[0]);
+ }
+}
+
+static void XRGB16161616_to_argb_u16(struct line_buffer *stage_buffer,
+ const struct vkms_frame_info *frame_info,
+ int y)
+{
+ struct pixel_argb_u16 *out_pixels = stage_buffer->pixels;
+ u16 *src_pixels = get_packed_src_addr(frame_info, y);
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ stage_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, src_pixels += 4) {
+ out_pixels[x].a = (u16)0xffff;
+ out_pixels[x].r = le16_to_cpu(src_pixels[2]);
+ out_pixels[x].g = le16_to_cpu(src_pixels[1]);
+ out_pixels[x].b = le16_to_cpu(src_pixels[0]);
+ }
+}
+
+static void RGB565_to_argb_u16(struct line_buffer *stage_buffer,
+ const struct vkms_frame_info *frame_info, int y)
+{
+ struct pixel_argb_u16 *out_pixels = stage_buffer->pixels;
+ u16 *src_pixels = get_packed_src_addr(frame_info, y);
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ stage_buffer->n_pixels);
+
+ s32 fp_rb_ratio = INT_TO_FIXED_DIV(65535, 31);
+ s32 fp_g_ratio = INT_TO_FIXED_DIV(65535, 63);
+
+ for (size_t x = 0; x < x_limit; x++, src_pixels++) {
+ u16 rgb_565 = le16_to_cpu(*src_pixels);
+ s32 fp_r = INT_TO_FIXED((rgb_565 >> 11) & 0x1f);
+ s32 fp_g = INT_TO_FIXED((rgb_565 >> 5) & 0x3f);
+ s32 fp_b = INT_TO_FIXED(rgb_565 & 0x1f);
+
+ out_pixels[x].a = (u16)0xffff;
+ out_pixels[x].r = FIXED_TO_INT_ROUND(FIXED_MUL(fp_r, fp_rb_ratio));
+ out_pixels[x].g = FIXED_TO_INT_ROUND(FIXED_MUL(fp_g, fp_g_ratio));
+ out_pixels[x].b = FIXED_TO_INT_ROUND(FIXED_MUL(fp_b, fp_rb_ratio));
+ }
+}
+
+/*
+ * The following functions take an line of argb_u16 pixels from the
+ * src_buffer, convert them to a specific format, and store them in the
+ * destination.
+ *
+ * They are used in the `compose_active_planes` to convert and store a line
+ * from the src_buffer to the writeback buffer.
+ */
+static void argb_u16_to_ARGB8888(struct vkms_frame_info *frame_info,
+ const struct line_buffer *src_buffer, int y)
+{
+ int x_dst = frame_info->dst.x1;
+ u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y);
+ struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ src_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) {
+ /*
+ * This sequence below is important because the format's byte order is
+ * in little-endian. In the case of the ARGB8888 the memory is
+ * organized this way:
+ *
+ * | Addr | = blue channel
+ * | Addr + 1 | = green channel
+ * | Addr + 2 | = Red channel
+ * | Addr + 3 | = Alpha channel
+ */
+ dst_pixels[3] = DIV_ROUND_CLOSEST(in_pixels[x].a, 257);
+ dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixels[x].r, 257);
+ dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixels[x].g, 257);
+ dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixels[x].b, 257);
+ }
+}
+
+static void argb_u16_to_XRGB8888(struct vkms_frame_info *frame_info,
+ const struct line_buffer *src_buffer, int y)
+{
+ int x_dst = frame_info->dst.x1;
+ u8 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y);
+ struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ src_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) {
+ dst_pixels[3] = 0xff;
+ dst_pixels[2] = DIV_ROUND_CLOSEST(in_pixels[x].r, 257);
+ dst_pixels[1] = DIV_ROUND_CLOSEST(in_pixels[x].g, 257);
+ dst_pixels[0] = DIV_ROUND_CLOSEST(in_pixels[x].b, 257);
+ }
+}
+
+static void argb_u16_to_ARGB16161616(struct vkms_frame_info *frame_info,
+ const struct line_buffer *src_buffer, int y)
+{
+ int x_dst = frame_info->dst.x1;
+ u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y);
+ struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ src_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) {
+ dst_pixels[3] = cpu_to_le16(in_pixels[x].a);
+ dst_pixels[2] = cpu_to_le16(in_pixels[x].r);
+ dst_pixels[1] = cpu_to_le16(in_pixels[x].g);
+ dst_pixels[0] = cpu_to_le16(in_pixels[x].b);
+ }
+}
+
+static void argb_u16_to_XRGB16161616(struct vkms_frame_info *frame_info,
+ const struct line_buffer *src_buffer, int y)
+{
+ int x_dst = frame_info->dst.x1;
+ u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y);
+ struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ src_buffer->n_pixels);
+
+ for (size_t x = 0; x < x_limit; x++, dst_pixels += 4) {
+ dst_pixels[3] = 0xffff;
+ dst_pixels[2] = cpu_to_le16(in_pixels[x].r);
+ dst_pixels[1] = cpu_to_le16(in_pixels[x].g);
+ dst_pixels[0] = cpu_to_le16(in_pixels[x].b);
+ }
+}
+
+static void argb_u16_to_RGB565(struct vkms_frame_info *frame_info,
+ const struct line_buffer *src_buffer, int y)
+{
+ int x_dst = frame_info->dst.x1;
+ u16 *dst_pixels = packed_pixels_addr(frame_info, x_dst, y);
+ struct pixel_argb_u16 *in_pixels = src_buffer->pixels;
+ int x_limit = min_t(size_t, drm_rect_width(&frame_info->dst),
+ src_buffer->n_pixels);
+
+ s32 fp_rb_ratio = INT_TO_FIXED_DIV(65535, 31);
+ s32 fp_g_ratio = INT_TO_FIXED_DIV(65535, 63);
+
+ for (size_t x = 0; x < x_limit; x++, dst_pixels++) {
+ s32 fp_r = INT_TO_FIXED(in_pixels[x].r);
+ s32 fp_g = INT_TO_FIXED(in_pixels[x].g);
+ s32 fp_b = INT_TO_FIXED(in_pixels[x].b);
+
+ u16 r = FIXED_TO_INT_ROUND(FIXED_DIV(fp_r, fp_rb_ratio));
+ u16 g = FIXED_TO_INT_ROUND(FIXED_DIV(fp_g, fp_g_ratio));
+ u16 b = FIXED_TO_INT_ROUND(FIXED_DIV(fp_b, fp_rb_ratio));
+
+ *dst_pixels = cpu_to_le16(r << 11 | g << 5 | b);
+ }
+}
+
+void *get_frame_to_line_function(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ return &ARGB8888_to_argb_u16;
+ case DRM_FORMAT_XRGB8888:
+ return &XRGB8888_to_argb_u16;
+ case DRM_FORMAT_ARGB16161616:
+ return &ARGB16161616_to_argb_u16;
+ case DRM_FORMAT_XRGB16161616:
+ return &XRGB16161616_to_argb_u16;
+ case DRM_FORMAT_RGB565:
+ return &RGB565_to_argb_u16;
+ default:
+ return NULL;
+ }
+}
+
+void *get_line_to_frame_function(u32 format)
+{
+ switch (format) {
+ case DRM_FORMAT_ARGB8888:
+ return &argb_u16_to_ARGB8888;
+ case DRM_FORMAT_XRGB8888:
+ return &argb_u16_to_XRGB8888;
+ case DRM_FORMAT_ARGB16161616:
+ return &argb_u16_to_ARGB16161616;
+ case DRM_FORMAT_XRGB16161616:
+ return &argb_u16_to_XRGB16161616;
+ case DRM_FORMAT_RGB565:
+ return &argb_u16_to_RGB565;
+ default:
+ return NULL;
+ }
+}
diff --git a/drivers/gpu/drm/vkms/vkms_formats.h b/drivers/gpu/drm/vkms/vkms_formats.h
new file mode 100644
index 000000000000..43b7c1979018
--- /dev/null
+++ b/drivers/gpu/drm/vkms/vkms_formats.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+
+#ifndef _VKMS_FORMATS_H_
+#define _VKMS_FORMATS_H_
+
+#include "vkms_drv.h"
+
+void *get_frame_to_line_function(u32 format);
+
+void *get_line_to_frame_function(u32 format);
+
+#endif /* _VKMS_FORMATS_H_ */
diff --git a/drivers/gpu/drm/vkms/vkms_plane.c b/drivers/gpu/drm/vkms/vkms_plane.c
index 8a810bfa1264..f4319066adcc 100644
--- a/drivers/gpu/drm/vkms/vkms_plane.c
+++ b/drivers/gpu/drm/vkms/vkms_plane.c
@@ -9,34 +9,40 @@
#include <drm/drm_gem_framebuffer_helper.h>
#include "vkms_drv.h"
+#include "vkms_formats.h"
static const u32 vkms_formats[] = {
DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XRGB16161616,
+ DRM_FORMAT_RGB565
};
static const u32 vkms_plane_formats[] = {
DRM_FORMAT_ARGB8888,
- DRM_FORMAT_XRGB8888
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XRGB16161616,
+ DRM_FORMAT_ARGB16161616,
+ DRM_FORMAT_RGB565
};
static struct drm_plane_state *
vkms_plane_duplicate_state(struct drm_plane *plane)
{
struct vkms_plane_state *vkms_state;
- struct vkms_composer *composer;
+ struct vkms_frame_info *frame_info;
vkms_state = kzalloc(sizeof(*vkms_state), GFP_KERNEL);
if (!vkms_state)
return NULL;
- composer = kzalloc(sizeof(*composer), GFP_KERNEL);
- if (!composer) {
- DRM_DEBUG_KMS("Couldn't allocate composer\n");
+ frame_info = kzalloc(sizeof(*frame_info), GFP_KERNEL);
+ if (!frame_info) {
+ DRM_DEBUG_KMS("Couldn't allocate frame_info\n");
kfree(vkms_state);
return NULL;
}
- vkms_state->composer = composer;
+ vkms_state->frame_info = frame_info;
__drm_gem_duplicate_shadow_plane_state(plane, &vkms_state->base);
@@ -49,16 +55,16 @@ static void vkms_plane_destroy_state(struct drm_plane *plane,
struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state);
struct drm_crtc *crtc = vkms_state->base.base.crtc;
- if (crtc) {
+ if (crtc && vkms_state->frame_info->fb) {
/* dropping the reference we acquired in
* vkms_primary_plane_update()
*/
- if (drm_framebuffer_read_refcount(&vkms_state->composer->fb))
- drm_framebuffer_put(&vkms_state->composer->fb);
+ if (drm_framebuffer_read_refcount(vkms_state->frame_info->fb))
+ drm_framebuffer_put(vkms_state->frame_info->fb);
}
- kfree(vkms_state->composer);
- vkms_state->composer = NULL;
+ kfree(vkms_state->frame_info);
+ vkms_state->frame_info = NULL;
__drm_gem_destroy_shadow_plane_state(&vkms_state->base);
kfree(vkms_state);
@@ -98,7 +104,8 @@ static void vkms_plane_atomic_update(struct drm_plane *plane,
struct vkms_plane_state *vkms_plane_state;
struct drm_shadow_plane_state *shadow_plane_state;
struct drm_framebuffer *fb = new_state->fb;
- struct vkms_composer *composer;
+ struct vkms_frame_info *frame_info;
+ u32 fmt = fb->format->format;
if (!new_state->crtc || !fb)
return;
@@ -106,15 +113,16 @@ static void vkms_plane_atomic_update(struct drm_plane *plane,
vkms_plane_state = to_vkms_plane_state(new_state);
shadow_plane_state = &vkms_plane_state->base;
- composer = vkms_plane_state->composer;
- memcpy(&composer->src, &new_state->src, sizeof(struct drm_rect));
- memcpy(&composer->dst, &new_state->dst, sizeof(struct drm_rect));
- memcpy(&composer->fb, fb, sizeof(struct drm_framebuffer));
- memcpy(&composer->map, &shadow_plane_state->data, sizeof(composer->map));
- drm_framebuffer_get(&composer->fb);
- composer->offset = fb->offsets[0];
- composer->pitch = fb->pitches[0];
- composer->cpp = fb->format->cpp[0];
+ frame_info = vkms_plane_state->frame_info;
+ memcpy(&frame_info->src, &new_state->src, sizeof(struct drm_rect));
+ memcpy(&frame_info->dst, &new_state->dst, sizeof(struct drm_rect));
+ frame_info->fb = fb;
+ memcpy(&frame_info->map, &shadow_plane_state->data, sizeof(frame_info->map));
+ drm_framebuffer_get(frame_info->fb);
+ frame_info->offset = fb->offsets[0];
+ frame_info->pitch = fb->pitches[0];
+ frame_info->cpp = fb->format->cpp[0];
+ vkms_plane_state->plane_read = get_frame_to_line_function(fmt);
}
static int vkms_plane_atomic_check(struct drm_plane *plane,
diff --git a/drivers/gpu/drm/vkms/vkms_writeback.c b/drivers/gpu/drm/vkms/vkms_writeback.c
index 3b3c1e757ab4..84a51cd281b9 100644
--- a/drivers/gpu/drm/vkms/vkms_writeback.c
+++ b/drivers/gpu/drm/vkms/vkms_writeback.c
@@ -12,9 +12,13 @@
#include <drm/drm_gem_shmem_helper.h>
#include "vkms_drv.h"
+#include "vkms_formats.h"
static const u32 vkms_wb_formats[] = {
DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_XRGB16161616,
+ DRM_FORMAT_ARGB16161616,
+ DRM_FORMAT_RGB565
};
static const struct drm_connector_funcs vkms_wb_connector_funcs = {
@@ -31,6 +35,7 @@ static int vkms_wb_encoder_atomic_check(struct drm_encoder *encoder,
{
struct drm_framebuffer *fb;
const struct drm_display_mode *mode = &crtc_state->mode;
+ int ret;
if (!conn_state->writeback_job || !conn_state->writeback_job->fb)
return 0;
@@ -42,11 +47,9 @@ static int vkms_wb_encoder_atomic_check(struct drm_encoder *encoder,
return -EINVAL;
}
- if (fb->format->format != vkms_wb_formats[0]) {
- DRM_DEBUG_KMS("Invalid pixel format %p4cc\n",
- &fb->format->format);
- return -EINVAL;
- }
+ ret = drm_atomic_helper_check_wb_encoder_state(encoder, conn_state);
+ if (ret < 0)
+ return ret;
return 0;
}
@@ -76,12 +79,15 @@ static int vkms_wb_prepare_job(struct drm_writeback_connector *wb_connector,
if (!vkmsjob)
return -ENOMEM;
- ret = drm_gem_fb_vmap(job->fb, vkmsjob->map, vkmsjob->data);
+ ret = drm_gem_fb_vmap(job->fb, vkmsjob->wb_frame_info.map, vkmsjob->data);
if (ret) {
DRM_ERROR("vmap failed: %d\n", ret);
goto err_kfree;
}
+ vkmsjob->wb_frame_info.fb = job->fb;
+ drm_framebuffer_get(vkmsjob->wb_frame_info.fb);
+
job->priv = vkmsjob;
return 0;
@@ -100,7 +106,9 @@ static void vkms_wb_cleanup_job(struct drm_writeback_connector *connector,
if (!job->fb)
return;
- drm_gem_fb_vunmap(job->fb, vkmsjob->map);
+ drm_gem_fb_vunmap(job->fb, vkmsjob->wb_frame_info.map);
+
+ drm_framebuffer_put(vkmsjob->wb_frame_info.fb);
vkmsdev = drm_device_to_vkms_device(job->fb->dev);
vkms_set_composer(&vkmsdev->output, false);
@@ -117,17 +125,32 @@ static void vkms_wb_atomic_commit(struct drm_connector *conn,
struct drm_writeback_connector *wb_conn = &output->wb_connector;
struct drm_connector_state *conn_state = wb_conn->base.state;
struct vkms_crtc_state *crtc_state = output->composer_state;
+ struct drm_framebuffer *fb = connector_state->writeback_job->fb;
+ u16 crtc_height = crtc_state->base.crtc->mode.vdisplay;
+ u16 crtc_width = crtc_state->base.crtc->mode.hdisplay;
+ struct vkms_writeback_job *active_wb;
+ struct vkms_frame_info *wb_frame_info;
+ u32 wb_format = fb->format->format;
if (!conn_state)
return;
vkms_set_composer(&vkmsdev->output, true);
+ active_wb = conn_state->writeback_job->priv;
+ wb_frame_info = &active_wb->wb_frame_info;
+
spin_lock_irq(&output->composer_lock);
- crtc_state->active_writeback = conn_state->writeback_job->priv;
+ crtc_state->active_writeback = active_wb;
+ wb_frame_info->offset = fb->offsets[0];
+ wb_frame_info->pitch = fb->pitches[0];
+ wb_frame_info->cpp = fb->format->cpp[0];
crtc_state->wb_pending = true;
spin_unlock_irq(&output->composer_lock);
drm_writeback_queue_job(wb_conn, connector_state);
+ active_wb->wb_write = get_line_to_frame_function(wb_format);
+ drm_rect_init(&wb_frame_info->src, 0, 0, crtc_width, crtc_height);
+ drm_rect_init(&wb_frame_info->dst, 0, 0, crtc_width, crtc_height);
}
static const struct drm_connector_helper_funcs vkms_wb_conn_helper_funcs = {