linux/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c
<<
>>
Prefs
   1/*
   2 * Copyright 2021 Advanced Micro Devices, Inc.
   3 *
   4 * Permission is hereby granted, free of charge, to any person obtaining a
   5 * copy of this software and associated documentation files (the "Software"),
   6 * to deal in the Software without restriction, including without limitation
   7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   8 * and/or sell copies of the Software, and to permit persons to whom the
   9 * Software is furnished to do so, subject to the following conditions:
  10 *
  11 * The above copyright notice and this permission notice shall be included in
  12 * all copies or substantial portions of the Software.
  13 *
  14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  17 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  18 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  19 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  20 * OTHER DEALINGS IN THE SOFTWARE.
  21 *
  22 * Authors: AMD
  23 *
  24 */
  25
  26#include "link_enc_cfg.h"
  27#include "resource.h"
  28#include "dc_link_dp.h"
  29
  30/* Check whether stream is supported by DIG link encoders. */
  31static bool is_dig_link_enc_stream(struct dc_stream_state *stream)
  32{
  33        bool is_dig_stream = false;
  34        struct link_encoder *link_enc = NULL;
  35        int i;
  36
  37        /* Loop over created link encoder objects. */
  38        for (i = 0; i < stream->ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
  39                link_enc = stream->ctx->dc->res_pool->link_encoders[i];
  40
  41                if (link_enc &&
  42                                ((uint32_t)stream->signal & link_enc->output_signals)) {
  43                        if (dc_is_dp_signal(stream->signal)) {
  44                                /* DIGs do not support DP2.0 streams with 128b/132b encoding. */
  45                                struct dc_link_settings link_settings = {0};
  46
  47                                decide_link_settings(stream, &link_settings);
  48                                if ((link_settings.link_rate >= LINK_RATE_LOW) &&
  49                                                link_settings.link_rate <= LINK_RATE_HIGH3) {
  50                                        is_dig_stream = true;
  51                                        break;
  52                                }
  53                        } else {
  54                                is_dig_stream = true;
  55                                break;
  56                        }
  57                }
  58        }
  59
  60        return is_dig_stream;
  61}
  62
  63/* Update DIG link encoder resource tracking variables in dc_state. */
  64static void update_link_enc_assignment(
  65                struct dc_state *state,
  66                struct dc_stream_state *stream,
  67                enum engine_id eng_id,
  68                bool add_enc)
  69{
  70        int eng_idx;
  71        int stream_idx;
  72        int i;
  73
  74        if (eng_id != ENGINE_ID_UNKNOWN) {
  75                eng_idx = eng_id - ENGINE_ID_DIGA;
  76                stream_idx = -1;
  77
  78                /* Index of stream in dc_state used to update correct entry in
  79                 * link_enc_assignments table.
  80                 */
  81                for (i = 0; i < state->stream_count; i++) {
  82                        if (stream == state->streams[i]) {
  83                                stream_idx = i;
  84                                break;
  85                        }
  86                }
  87
  88                /* Update link encoder assignments table, link encoder availability
  89                 * pool and link encoder assigned to stream in state.
  90                 * Add/remove encoder resource to/from stream.
  91                 */
  92                if (stream_idx != -1) {
  93                        if (add_enc) {
  94                                state->res_ctx.link_enc_assignments[stream_idx] = (struct link_enc_assignment){
  95                                        .valid = true,
  96                                        .ep_id = (struct display_endpoint_id) {
  97                                                .link_id = stream->link->link_id,
  98                                                .ep_type = stream->link->ep_type},
  99                                        .eng_id = eng_id};
 100                                state->res_ctx.link_enc_avail[eng_idx] = ENGINE_ID_UNKNOWN;
 101                                stream->link_enc = stream->ctx->dc->res_pool->link_encoders[eng_idx];
 102                        } else {
 103                                state->res_ctx.link_enc_assignments[stream_idx].valid = false;
 104                                state->res_ctx.link_enc_avail[eng_idx] = eng_id;
 105                                stream->link_enc = NULL;
 106                        }
 107                } else {
 108                        dm_output_to_console("%s: Stream not found in dc_state.\n", __func__);
 109                }
 110        }
 111}
 112
 113/* Return first available DIG link encoder. */
 114static enum engine_id find_first_avail_link_enc(
 115                const struct dc_context *ctx,
 116                const struct dc_state *state)
 117{
 118        enum engine_id eng_id = ENGINE_ID_UNKNOWN;
 119        int i;
 120
 121        for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) {
 122                eng_id = state->res_ctx.link_enc_avail[i];
 123                if (eng_id != ENGINE_ID_UNKNOWN)
 124                        break;
 125        }
 126
 127        return eng_id;
 128}
 129
 130/* Return stream using DIG link encoder resource. NULL if unused. */
 131static struct dc_stream_state *get_stream_using_link_enc(
 132                struct dc_state *state,
 133                enum engine_id eng_id)
 134{
 135        struct dc_stream_state *stream = NULL;
 136        int stream_idx = -1;
 137        int i;
 138
 139        for (i = 0; i < state->stream_count; i++) {
 140                struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i];
 141
 142                if (assignment.valid && (assignment.eng_id == eng_id)) {
 143                        stream_idx = i;
 144                        break;
 145                }
 146        }
 147
 148        if (stream_idx != -1)
 149                stream = state->streams[stream_idx];
 150        else
 151                dm_output_to_console("%s: No stream using DIG(%d).\n", __func__, eng_id);
 152
 153        return stream;
 154}
 155
 156void link_enc_cfg_init(
 157                struct dc *dc,
 158                struct dc_state *state)
 159{
 160        int i;
 161
 162        for (i = 0; i < dc->res_pool->res_cap->num_dig_link_enc; i++) {
 163                if (dc->res_pool->link_encoders[i])
 164                        state->res_ctx.link_enc_avail[i] = (enum engine_id) i;
 165                else
 166                        state->res_ctx.link_enc_avail[i] = ENGINE_ID_UNKNOWN;
 167        }
 168}
 169
 170void link_enc_cfg_link_encs_assign(
 171                struct dc *dc,
 172                struct dc_state *state,
 173                struct dc_stream_state *streams[],
 174                uint8_t stream_count)
 175{
 176        enum engine_id eng_id = ENGINE_ID_UNKNOWN;
 177        int i;
 178
 179        /* Release DIG link encoder resources before running assignment algorithm. */
 180        for (i = 0; i < stream_count; i++)
 181                dc->res_pool->funcs->link_enc_unassign(state, streams[i]);
 182
 183        /* (a) Assign DIG link encoders to physical (unmappable) endpoints first. */
 184        for (i = 0; i < stream_count; i++) {
 185                struct dc_stream_state *stream = streams[i];
 186
 187                /* Skip stream if not supported by DIG link encoder. */
 188                if (!is_dig_link_enc_stream(stream))
 189                        continue;
 190
 191                /* Physical endpoints have a fixed mapping to DIG link encoders. */
 192                if (!stream->link->is_dig_mapping_flexible) {
 193                        eng_id = stream->link->eng_id;
 194                        update_link_enc_assignment(state, stream, eng_id, true);
 195                }
 196        }
 197
 198        /* (b) Then assign encoders to mappable endpoints. */
 199        eng_id = ENGINE_ID_UNKNOWN;
 200
 201        for (i = 0; i < stream_count; i++) {
 202                struct dc_stream_state *stream = streams[i];
 203
 204                /* Skip stream if not supported by DIG link encoder. */
 205                if (!is_dig_link_enc_stream(stream))
 206                        continue;
 207
 208                /* Mappable endpoints have a flexible mapping to DIG link encoders. */
 209                if (stream->link->is_dig_mapping_flexible) {
 210                        eng_id = find_first_avail_link_enc(stream->ctx, state);
 211                        update_link_enc_assignment(state, stream, eng_id, true);
 212                }
 213        }
 214}
 215
 216void link_enc_cfg_link_enc_unassign(
 217                struct dc_state *state,
 218                struct dc_stream_state *stream)
 219{
 220        enum engine_id eng_id = ENGINE_ID_UNKNOWN;
 221
 222        /* Only DIG link encoders. */
 223        if (!is_dig_link_enc_stream(stream))
 224                return;
 225
 226        if (stream->link_enc)
 227                eng_id = stream->link_enc->preferred_engine;
 228
 229        update_link_enc_assignment(state, stream, eng_id, false);
 230}
 231
 232bool link_enc_cfg_is_transmitter_mappable(
 233                struct dc_state *state,
 234                struct link_encoder *link_enc)
 235{
 236        bool is_mappable = false;
 237        enum engine_id eng_id = link_enc->preferred_engine;
 238        struct dc_stream_state *stream = get_stream_using_link_enc(state, eng_id);
 239
 240        if (stream)
 241                is_mappable = stream->link->is_dig_mapping_flexible;
 242
 243        return is_mappable;
 244}
 245
 246struct dc_link *link_enc_cfg_get_link_using_link_enc(
 247                struct dc_state *state,
 248                enum engine_id eng_id)
 249{
 250        struct dc_link *link = NULL;
 251        int stream_idx = -1;
 252        int i;
 253
 254        for (i = 0; i < state->stream_count; i++) {
 255                struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i];
 256
 257                if (assignment.valid && (assignment.eng_id == eng_id)) {
 258                        stream_idx = i;
 259                        break;
 260                }
 261        }
 262
 263        if (stream_idx != -1)
 264                link = state->streams[stream_idx]->link;
 265        else
 266                dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id);
 267
 268        return link;
 269}
 270
 271struct link_encoder *link_enc_cfg_get_link_enc_used_by_link(
 272                struct dc_state *state,
 273                const struct dc_link *link)
 274{
 275        struct link_encoder *link_enc = NULL;
 276        struct display_endpoint_id ep_id;
 277        int stream_idx = -1;
 278        int i;
 279
 280        ep_id = (struct display_endpoint_id) {
 281                .link_id = link->link_id,
 282                .ep_type = link->ep_type};
 283
 284        for (i = 0; i < state->stream_count; i++) {
 285                struct link_enc_assignment assignment = state->res_ctx.link_enc_assignments[i];
 286
 287                if (assignment.valid &&
 288                                assignment.ep_id.link_id.id == ep_id.link_id.id &&
 289                                assignment.ep_id.link_id.enum_id == ep_id.link_id.enum_id &&
 290                                assignment.ep_id.link_id.type == ep_id.link_id.type &&
 291                                assignment.ep_id.ep_type == ep_id.ep_type) {
 292                        stream_idx = i;
 293                        break;
 294                }
 295        }
 296
 297        if (stream_idx != -1)
 298                link_enc = state->streams[stream_idx]->link_enc;
 299
 300        return link_enc;
 301}
 302
 303struct link_encoder *link_enc_cfg_get_next_avail_link_enc(
 304        const struct dc *dc,
 305        const struct dc_state *state)
 306{
 307        struct link_encoder *link_enc = NULL;
 308        enum engine_id eng_id;
 309
 310        eng_id = find_first_avail_link_enc(dc->ctx, state);
 311        if (eng_id != ENGINE_ID_UNKNOWN)
 312                link_enc = dc->res_pool->link_encoders[eng_id - ENGINE_ID_DIGA];
 313
 314        return link_enc;
 315}
 316