drm/amd/display: Add debugfs interface for ODM combine info
authorAurabindo Pillai <aurabindo.pillai@amd.com>
Wed, 16 Aug 2023 20:03:20 +0000 (16:03 -0400)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 30 Aug 2023 19:37:59 +0000 (15:37 -0400)
[Why]
For use with IGT tests in userspace, the number of ODM segments in use
is required to be exposed to userspace to verify that ODM Combine is
working as expected when special timings are committed.

[How]
Add a connector specific debugfs entry that prints the number of ODM
segments in use.

Reviewed-by: Wenjing Liu <wenjing.liu@amd.com>
Acked-by: Hamza Mahfooz <hamza.mahfooz@amd.com>
Signed-off-by: Aurabindo Pillai <aurabindo.pillai@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.c
drivers/gpu/drm/amd/display/dc/dcn32/dcn32_optc.h
drivers/gpu/drm/amd/display/dc/inc/hw/timing_generator.h

index 0c4dd922cfaf62ae523957980906bc19b9271dd3..17d1990ea832d87a41575ee86574d913500b66c8 100644 (file)
@@ -1201,6 +1201,35 @@ static int internal_display_show(struct seq_file *m, void *data)
        return 0;
 }
 
+/*
+ * Returns the number of segments used if ODM Combine mode is enabled.
+ * Example usage: cat /sys/kernel/debug/dri/0/DP-1/odm_combine_segments
+ */
+static int odm_combine_segments_show(struct seq_file *m, void *unused)
+{
+       struct drm_connector *connector = m->private;
+       struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+       struct dc_link *link = aconnector->dc_link;
+       struct pipe_ctx *pipe_ctx = NULL;
+       int i, segments = 0;
+
+       for (i = 0; i < MAX_PIPES; i++) {
+               pipe_ctx = &link->dc->current_state->res_ctx.pipe_ctx[i];
+               if (pipe_ctx->stream &&
+                   pipe_ctx->stream->link == link)
+                       break;
+       }
+
+       if (connector->status != connector_status_connected)
+               return -ENODEV;
+
+       if (pipe_ctx != NULL && pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments)
+               pipe_ctx->stream_res.tg->funcs->get_odm_combine_segments(pipe_ctx->stream_res.tg, &segments);
+
+       seq_printf(m, "%d\n", segments);
+       return 0;
+}
+
 /* function description
  *
  * generic SDP message access for testing
@@ -2713,6 +2742,7 @@ DEFINE_SHOW_ATTRIBUTE(dmub_tracebuffer);
 DEFINE_SHOW_ATTRIBUTE(dp_lttpr_status);
 DEFINE_SHOW_ATTRIBUTE(hdcp_sink_capability);
 DEFINE_SHOW_ATTRIBUTE(internal_display);
+DEFINE_SHOW_ATTRIBUTE(odm_combine_segments);
 DEFINE_SHOW_ATTRIBUTE(psr_capability);
 DEFINE_SHOW_ATTRIBUTE(dp_is_mst_connector);
 DEFINE_SHOW_ATTRIBUTE(dp_mst_progress_status);
@@ -2991,7 +3021,8 @@ static const struct {
 } connector_debugfs_entries[] = {
                {"force_yuv420_output", &force_yuv420_output_fops},
                {"trigger_hotplug", &trigger_hotplug_debugfs_fops},
-               {"internal_display", &internal_display_fops}
+               {"internal_display", &internal_display_fops},
+               {"odm_combine_segments", &odm_combine_segments_fops}
 };
 
 /*
index 8abb94f60078fc637a2e80087d8ac39c9c6c954c..e7e25c58c69285e4745300bc6e433bfb17269d93 100644 (file)
@@ -98,6 +98,30 @@ static void optc32_set_odm_combine(struct timing_generator *optc, int *opp_id, i
        optc1->opp_count = opp_cnt;
 }
 
+void optc32_get_odm_combine_segments(struct timing_generator *tg, int *odm_combine_segments)
+{
+       struct optc *optc1 = DCN10TG_FROM_TG(tg);
+       int segments;
+
+       REG_GET(OPTC_DATA_SOURCE_SELECT, OPTC_NUM_OF_INPUT_SEGMENT, &segments);
+
+       switch (segments) {
+       case 0:
+               *odm_combine_segments = 1;
+               break;
+       case 1:
+               *odm_combine_segments = 2;
+               break;
+       case 3:
+               *odm_combine_segments = 4;
+               break;
+       /* 2 is reserved */
+       case 2:
+       default:
+               *odm_combine_segments = -1;
+       }
+}
+
 void optc32_set_h_timing_div_manual_mode(struct timing_generator *optc, bool manual_mode)
 {
        struct optc *optc1 = DCN10TG_FROM_TG(optc);
@@ -303,6 +327,7 @@ static struct timing_generator_funcs dcn32_tg_funcs = {
                .set_dwb_source = NULL,
                .set_odm_bypass = optc32_set_odm_bypass,
                .set_odm_combine = optc32_set_odm_combine,
+               .get_odm_combine_segments = optc32_get_odm_combine_segments,
                .set_h_timing_div_manual_mode = optc32_set_h_timing_div_manual_mode,
                .get_optc_source = optc2_get_optc_source,
                .set_out_mux = optc3_set_out_mux,
index abf0121a1006010e4ab7bc09f9c414201977d970..93cc7fc8472c54920167ba3148917e85ec82b345 100644 (file)
 
 void dcn32_timing_generator_init(struct optc *optc1);
 void optc32_set_h_timing_div_manual_mode(struct timing_generator *optc, bool manual_mode);
+void optc32_get_odm_combine_segments(struct timing_generator *tg, int *odm_combine_segments);
 
 #endif /* __DC_OPTC_DCN32_H__ */
index c21e7ffd5bd020c94b2dfd07cc9cbc7e793d553f..9a00a99317b298c0b7e148acebb032c518c6f310 100644 (file)
@@ -309,6 +309,7 @@ struct timing_generator_funcs {
         */
        void (*set_odm_combine)(struct timing_generator *optc, int *opp_id, int opp_cnt,
                        struct dc_crtc_timing *timing);
+       void (*get_odm_combine_segments)(struct timing_generator *tg, int *odm_segments);
        void (*set_h_timing_div_manual_mode)(struct timing_generator *optc, bool manual_mode);
        void (*set_gsl)(struct timing_generator *optc, const struct gsl_params *params);
        void (*set_gsl_source_select)(struct timing_generator *optc,