linux/drivers/gpu/drm/amd/display/dc/dc_helper.c
<<
>>
Prefs
   1/*
   2 * Copyright 2017 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 */
  23/*
  24 * dc_helper.c
  25 *
  26 *  Created on: Aug 30, 2016
  27 *      Author: agrodzov
  28 */
  29
  30#include <linux/delay.h>
  31#include <linux/stdarg.h>
  32
  33#include "dm_services.h"
  34
  35#include "dc.h"
  36#include "dc_dmub_srv.h"
  37#include "reg_helper.h"
  38
  39static inline void submit_dmub_read_modify_write(
  40        struct dc_reg_helper_state *offload,
  41        const struct dc_context *ctx)
  42{
  43        struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write;
  44        bool gather = false;
  45
  46        offload->should_burst_write =
  47                        (offload->same_addr_count == (DMUB_READ_MODIFY_WRITE_SEQ__MAX - 1));
  48        cmd_buf->header.payload_bytes =
  49                        sizeof(struct dmub_cmd_read_modify_write_sequence) * offload->reg_seq_count;
  50
  51        gather = ctx->dmub_srv->reg_helper_offload.gather_in_progress;
  52        ctx->dmub_srv->reg_helper_offload.gather_in_progress = false;
  53
  54        dc_dmub_srv_cmd_queue(ctx->dmub_srv, &offload->cmd_data);
  55
  56        ctx->dmub_srv->reg_helper_offload.gather_in_progress = gather;
  57
  58        memset(cmd_buf, 0, sizeof(*cmd_buf));
  59
  60        offload->reg_seq_count = 0;
  61        offload->same_addr_count = 0;
  62}
  63
  64static inline void submit_dmub_burst_write(
  65        struct dc_reg_helper_state *offload,
  66        const struct dc_context *ctx)
  67{
  68        struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write;
  69        bool gather = false;
  70
  71        cmd_buf->header.payload_bytes =
  72                        sizeof(uint32_t) * offload->reg_seq_count;
  73
  74        gather = ctx->dmub_srv->reg_helper_offload.gather_in_progress;
  75        ctx->dmub_srv->reg_helper_offload.gather_in_progress = false;
  76
  77        dc_dmub_srv_cmd_queue(ctx->dmub_srv, &offload->cmd_data);
  78
  79        ctx->dmub_srv->reg_helper_offload.gather_in_progress = gather;
  80
  81        memset(cmd_buf, 0, sizeof(*cmd_buf));
  82
  83        offload->reg_seq_count = 0;
  84}
  85
  86static inline void submit_dmub_reg_wait(
  87                struct dc_reg_helper_state *offload,
  88                const struct dc_context *ctx)
  89{
  90        struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait;
  91        bool gather = false;
  92
  93        gather = ctx->dmub_srv->reg_helper_offload.gather_in_progress;
  94        ctx->dmub_srv->reg_helper_offload.gather_in_progress = false;
  95
  96        dc_dmub_srv_cmd_queue(ctx->dmub_srv, &offload->cmd_data);
  97
  98        memset(cmd_buf, 0, sizeof(*cmd_buf));
  99        offload->reg_seq_count = 0;
 100
 101        ctx->dmub_srv->reg_helper_offload.gather_in_progress = gather;
 102}
 103
 104struct dc_reg_value_masks {
 105        uint32_t value;
 106        uint32_t mask;
 107};
 108
 109struct dc_reg_sequence {
 110        uint32_t addr;
 111        struct dc_reg_value_masks value_masks;
 112};
 113
 114static inline void set_reg_field_value_masks(
 115        struct dc_reg_value_masks *field_value_mask,
 116        uint32_t value,
 117        uint32_t mask,
 118        uint8_t shift)
 119{
 120        ASSERT(mask != 0);
 121
 122        field_value_mask->value = (field_value_mask->value & ~mask) | (mask & (value << shift));
 123        field_value_mask->mask = field_value_mask->mask | mask;
 124}
 125
 126static void set_reg_field_values(struct dc_reg_value_masks *field_value_mask,
 127                uint32_t addr, int n,
 128                uint8_t shift1, uint32_t mask1, uint32_t field_value1,
 129                va_list ap)
 130{
 131        uint32_t shift, mask, field_value;
 132        int i = 1;
 133
 134        /* gather all bits value/mask getting updated in this register */
 135        set_reg_field_value_masks(field_value_mask,
 136                        field_value1, mask1, shift1);
 137
 138        while (i < n) {
 139                shift = va_arg(ap, uint32_t);
 140                mask = va_arg(ap, uint32_t);
 141                field_value = va_arg(ap, uint32_t);
 142
 143                set_reg_field_value_masks(field_value_mask,
 144                                field_value, mask, shift);
 145                i++;
 146        }
 147}
 148
 149static void dmub_flush_buffer_execute(
 150                struct dc_reg_helper_state *offload,
 151                const struct dc_context *ctx)
 152{
 153        submit_dmub_read_modify_write(offload, ctx);
 154        dc_dmub_srv_cmd_execute(ctx->dmub_srv);
 155}
 156
 157static void dmub_flush_burst_write_buffer_execute(
 158                struct dc_reg_helper_state *offload,
 159                const struct dc_context *ctx)
 160{
 161        submit_dmub_burst_write(offload, ctx);
 162        dc_dmub_srv_cmd_execute(ctx->dmub_srv);
 163}
 164
 165static bool dmub_reg_value_burst_set_pack(const struct dc_context *ctx, uint32_t addr,
 166                uint32_t reg_val)
 167{
 168        struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
 169        struct dmub_rb_cmd_burst_write *cmd_buf = &offload->cmd_data.burst_write;
 170
 171        /* flush command if buffer is full */
 172        if (offload->reg_seq_count == DMUB_BURST_WRITE_VALUES__MAX)
 173                dmub_flush_burst_write_buffer_execute(offload, ctx);
 174
 175        if (offload->cmd_data.cmd_common.header.type == DMUB_CMD__REG_SEQ_BURST_WRITE &&
 176                        addr != cmd_buf->addr) {
 177                dmub_flush_burst_write_buffer_execute(offload, ctx);
 178                return false;
 179        }
 180
 181        cmd_buf->header.type = DMUB_CMD__REG_SEQ_BURST_WRITE;
 182        cmd_buf->header.sub_type = 0;
 183        cmd_buf->addr = addr;
 184        cmd_buf->write_values[offload->reg_seq_count] = reg_val;
 185        offload->reg_seq_count++;
 186
 187        return true;
 188}
 189
 190static uint32_t dmub_reg_value_pack(const struct dc_context *ctx, uint32_t addr,
 191                struct dc_reg_value_masks *field_value_mask)
 192{
 193        struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
 194        struct dmub_rb_cmd_read_modify_write *cmd_buf = &offload->cmd_data.read_modify_write;
 195        struct dmub_cmd_read_modify_write_sequence *seq;
 196
 197        /* flush command if buffer is full */
 198        if (offload->cmd_data.cmd_common.header.type != DMUB_CMD__REG_SEQ_BURST_WRITE &&
 199                        offload->reg_seq_count == DMUB_READ_MODIFY_WRITE_SEQ__MAX)
 200                dmub_flush_buffer_execute(offload, ctx);
 201
 202        if (offload->should_burst_write) {
 203                if (dmub_reg_value_burst_set_pack(ctx, addr, field_value_mask->value))
 204                        return field_value_mask->value;
 205                else
 206                        offload->should_burst_write = false;
 207        }
 208
 209        /* pack commands */
 210        cmd_buf->header.type = DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE;
 211        cmd_buf->header.sub_type = 0;
 212        seq = &cmd_buf->seq[offload->reg_seq_count];
 213
 214        if (offload->reg_seq_count) {
 215                if (cmd_buf->seq[offload->reg_seq_count - 1].addr == addr)
 216                        offload->same_addr_count++;
 217                else
 218                        offload->same_addr_count = 0;
 219        }
 220
 221        seq->addr = addr;
 222        seq->modify_mask = field_value_mask->mask;
 223        seq->modify_value = field_value_mask->value;
 224        offload->reg_seq_count++;
 225
 226        return field_value_mask->value;
 227}
 228
 229static void dmub_reg_wait_done_pack(const struct dc_context *ctx, uint32_t addr,
 230                uint32_t mask, uint32_t shift, uint32_t condition_value, uint32_t time_out_us)
 231{
 232        struct dc_reg_helper_state *offload = &ctx->dmub_srv->reg_helper_offload;
 233        struct dmub_rb_cmd_reg_wait *cmd_buf = &offload->cmd_data.reg_wait;
 234
 235        cmd_buf->header.type = DMUB_CMD__REG_REG_WAIT;
 236        cmd_buf->header.sub_type = 0;
 237        cmd_buf->reg_wait.addr = addr;
 238        cmd_buf->reg_wait.condition_field_value = mask & (condition_value << shift);
 239        cmd_buf->reg_wait.mask = mask;
 240        cmd_buf->reg_wait.time_out_us = time_out_us;
 241}
 242
 243uint32_t generic_reg_update_ex(const struct dc_context *ctx,
 244                uint32_t addr, int n,
 245                uint8_t shift1, uint32_t mask1, uint32_t field_value1,
 246                ...)
 247{
 248        struct dc_reg_value_masks field_value_mask = {0};
 249        uint32_t reg_val;
 250        va_list ap;
 251
 252        va_start(ap, field_value1);
 253
 254        set_reg_field_values(&field_value_mask, addr, n, shift1, mask1,
 255                        field_value1, ap);
 256
 257        va_end(ap);
 258
 259        if (ctx->dmub_srv &&
 260            ctx->dmub_srv->reg_helper_offload.gather_in_progress)
 261                return dmub_reg_value_pack(ctx, addr, &field_value_mask);
 262                /* todo: return void so we can decouple code running in driver from register states */
 263
 264        /* mmio write directly */
 265        reg_val = dm_read_reg(ctx, addr);
 266        reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value;
 267        dm_write_reg(ctx, addr, reg_val);
 268        return reg_val;
 269}
 270
 271uint32_t generic_reg_set_ex(const struct dc_context *ctx,
 272                uint32_t addr, uint32_t reg_val, int n,
 273                uint8_t shift1, uint32_t mask1, uint32_t field_value1,
 274                ...)
 275{
 276        struct dc_reg_value_masks field_value_mask = {0};
 277        va_list ap;
 278
 279        va_start(ap, field_value1);
 280
 281        set_reg_field_values(&field_value_mask, addr, n, shift1, mask1,
 282                        field_value1, ap);
 283
 284        va_end(ap);
 285
 286
 287        /* mmio write directly */
 288        reg_val = (reg_val & ~field_value_mask.mask) | field_value_mask.value;
 289
 290        if (ctx->dmub_srv &&
 291            ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
 292                return dmub_reg_value_burst_set_pack(ctx, addr, reg_val);
 293                /* todo: return void so we can decouple code running in driver from register states */
 294        }
 295
 296        dm_write_reg(ctx, addr, reg_val);
 297        return reg_val;
 298}
 299
 300uint32_t generic_reg_get(const struct dc_context *ctx, uint32_t addr,
 301                uint8_t shift, uint32_t mask, uint32_t *field_value)
 302{
 303        uint32_t reg_val = dm_read_reg(ctx, addr);
 304        *field_value = get_reg_field_value_ex(reg_val, mask, shift);
 305        return reg_val;
 306}
 307
 308uint32_t generic_reg_get2(const struct dc_context *ctx, uint32_t addr,
 309                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 310                uint8_t shift2, uint32_t mask2, uint32_t *field_value2)
 311{
 312        uint32_t reg_val = dm_read_reg(ctx, addr);
 313        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 314        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 315        return reg_val;
 316}
 317
 318uint32_t generic_reg_get3(const struct dc_context *ctx, uint32_t addr,
 319                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 320                uint8_t shift2, uint32_t mask2, uint32_t *field_value2,
 321                uint8_t shift3, uint32_t mask3, uint32_t *field_value3)
 322{
 323        uint32_t reg_val = dm_read_reg(ctx, addr);
 324        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 325        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 326        *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3);
 327        return reg_val;
 328}
 329
 330uint32_t generic_reg_get4(const struct dc_context *ctx, uint32_t addr,
 331                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 332                uint8_t shift2, uint32_t mask2, uint32_t *field_value2,
 333                uint8_t shift3, uint32_t mask3, uint32_t *field_value3,
 334                uint8_t shift4, uint32_t mask4, uint32_t *field_value4)
 335{
 336        uint32_t reg_val = dm_read_reg(ctx, addr);
 337        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 338        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 339        *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3);
 340        *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4);
 341        return reg_val;
 342}
 343
 344uint32_t generic_reg_get5(const struct dc_context *ctx, uint32_t addr,
 345                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 346                uint8_t shift2, uint32_t mask2, uint32_t *field_value2,
 347                uint8_t shift3, uint32_t mask3, uint32_t *field_value3,
 348                uint8_t shift4, uint32_t mask4, uint32_t *field_value4,
 349                uint8_t shift5, uint32_t mask5, uint32_t *field_value5)
 350{
 351        uint32_t reg_val = dm_read_reg(ctx, addr);
 352        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 353        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 354        *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3);
 355        *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4);
 356        *field_value5 = get_reg_field_value_ex(reg_val, mask5, shift5);
 357        return reg_val;
 358}
 359
 360uint32_t generic_reg_get6(const struct dc_context *ctx, uint32_t addr,
 361                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 362                uint8_t shift2, uint32_t mask2, uint32_t *field_value2,
 363                uint8_t shift3, uint32_t mask3, uint32_t *field_value3,
 364                uint8_t shift4, uint32_t mask4, uint32_t *field_value4,
 365                uint8_t shift5, uint32_t mask5, uint32_t *field_value5,
 366                uint8_t shift6, uint32_t mask6, uint32_t *field_value6)
 367{
 368        uint32_t reg_val = dm_read_reg(ctx, addr);
 369        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 370        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 371        *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3);
 372        *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4);
 373        *field_value5 = get_reg_field_value_ex(reg_val, mask5, shift5);
 374        *field_value6 = get_reg_field_value_ex(reg_val, mask6, shift6);
 375        return reg_val;
 376}
 377
 378uint32_t generic_reg_get7(const struct dc_context *ctx, uint32_t addr,
 379                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 380                uint8_t shift2, uint32_t mask2, uint32_t *field_value2,
 381                uint8_t shift3, uint32_t mask3, uint32_t *field_value3,
 382                uint8_t shift4, uint32_t mask4, uint32_t *field_value4,
 383                uint8_t shift5, uint32_t mask5, uint32_t *field_value5,
 384                uint8_t shift6, uint32_t mask6, uint32_t *field_value6,
 385                uint8_t shift7, uint32_t mask7, uint32_t *field_value7)
 386{
 387        uint32_t reg_val = dm_read_reg(ctx, addr);
 388        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 389        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 390        *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3);
 391        *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4);
 392        *field_value5 = get_reg_field_value_ex(reg_val, mask5, shift5);
 393        *field_value6 = get_reg_field_value_ex(reg_val, mask6, shift6);
 394        *field_value7 = get_reg_field_value_ex(reg_val, mask7, shift7);
 395        return reg_val;
 396}
 397
 398uint32_t generic_reg_get8(const struct dc_context *ctx, uint32_t addr,
 399                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 400                uint8_t shift2, uint32_t mask2, uint32_t *field_value2,
 401                uint8_t shift3, uint32_t mask3, uint32_t *field_value3,
 402                uint8_t shift4, uint32_t mask4, uint32_t *field_value4,
 403                uint8_t shift5, uint32_t mask5, uint32_t *field_value5,
 404                uint8_t shift6, uint32_t mask6, uint32_t *field_value6,
 405                uint8_t shift7, uint32_t mask7, uint32_t *field_value7,
 406                uint8_t shift8, uint32_t mask8, uint32_t *field_value8)
 407{
 408        uint32_t reg_val = dm_read_reg(ctx, addr);
 409        *field_value1 = get_reg_field_value_ex(reg_val, mask1, shift1);
 410        *field_value2 = get_reg_field_value_ex(reg_val, mask2, shift2);
 411        *field_value3 = get_reg_field_value_ex(reg_val, mask3, shift3);
 412        *field_value4 = get_reg_field_value_ex(reg_val, mask4, shift4);
 413        *field_value5 = get_reg_field_value_ex(reg_val, mask5, shift5);
 414        *field_value6 = get_reg_field_value_ex(reg_val, mask6, shift6);
 415        *field_value7 = get_reg_field_value_ex(reg_val, mask7, shift7);
 416        *field_value8 = get_reg_field_value_ex(reg_val, mask8, shift8);
 417        return reg_val;
 418}
 419/* note:  va version of this is pretty bad idea, since there is a output parameter pass by pointer
 420 * compiler won't be able to check for size match and is prone to stack corruption type of bugs
 421
 422uint32_t generic_reg_get(const struct dc_context *ctx,
 423                uint32_t addr, int n, ...)
 424{
 425        uint32_t shift, mask;
 426        uint32_t *field_value;
 427        uint32_t reg_val;
 428        int i = 0;
 429
 430        reg_val = dm_read_reg(ctx, addr);
 431
 432        va_list ap;
 433        va_start(ap, n);
 434
 435        while (i < n) {
 436                shift = va_arg(ap, uint32_t);
 437                mask = va_arg(ap, uint32_t);
 438                field_value = va_arg(ap, uint32_t *);
 439
 440                *field_value = get_reg_field_value_ex(reg_val, mask, shift);
 441                i++;
 442        }
 443
 444        va_end(ap);
 445
 446        return reg_val;
 447}
 448*/
 449
 450void generic_reg_wait(const struct dc_context *ctx,
 451        uint32_t addr, uint32_t shift, uint32_t mask, uint32_t condition_value,
 452        unsigned int delay_between_poll_us, unsigned int time_out_num_tries,
 453        const char *func_name, int line)
 454{
 455        uint32_t field_value;
 456        uint32_t reg_val;
 457        int i;
 458
 459        if (ctx->dmub_srv &&
 460            ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
 461                dmub_reg_wait_done_pack(ctx, addr, mask, shift, condition_value,
 462                                delay_between_poll_us * time_out_num_tries);
 463                return;
 464        }
 465
 466        /*
 467         * Something is terribly wrong if time out is > 3000ms.
 468         * 3000ms is the maximum time needed for SMU to pass values back.
 469         * This value comes from experiments.
 470         *
 471         */
 472        ASSERT(delay_between_poll_us * time_out_num_tries <= 3000000);
 473
 474        for (i = 0; i <= time_out_num_tries; i++) {
 475                if (i) {
 476                        if (delay_between_poll_us >= 1000)
 477                                msleep(delay_between_poll_us/1000);
 478                        else if (delay_between_poll_us > 0)
 479                                udelay(delay_between_poll_us);
 480                }
 481
 482                reg_val = dm_read_reg(ctx, addr);
 483
 484                field_value = get_reg_field_value_ex(reg_val, mask, shift);
 485
 486                if (field_value == condition_value) {
 487                        if (i * delay_between_poll_us > 1000 &&
 488                                        !IS_FPGA_MAXIMUS_DC(ctx->dce_environment))
 489                                DC_LOG_DC("REG_WAIT taking a while: %dms in %s line:%d\n",
 490                                                delay_between_poll_us * i / 1000,
 491                                                func_name, line);
 492                        return;
 493                }
 494        }
 495
 496        DC_LOG_WARNING("REG_WAIT timeout %dus * %d tries - %s line:%d\n",
 497                        delay_between_poll_us, time_out_num_tries,
 498                        func_name, line);
 499
 500        if (!IS_FPGA_MAXIMUS_DC(ctx->dce_environment))
 501                BREAK_TO_DEBUGGER();
 502}
 503
 504void generic_write_indirect_reg(const struct dc_context *ctx,
 505                uint32_t addr_index, uint32_t addr_data,
 506                uint32_t index, uint32_t data)
 507{
 508        dm_write_reg(ctx, addr_index, index);
 509        dm_write_reg(ctx, addr_data, data);
 510}
 511
 512uint32_t generic_read_indirect_reg(const struct dc_context *ctx,
 513                uint32_t addr_index, uint32_t addr_data,
 514                uint32_t index)
 515{
 516        uint32_t value = 0;
 517
 518        // when reg read, there should not be any offload.
 519        if (ctx->dmub_srv &&
 520            ctx->dmub_srv->reg_helper_offload.gather_in_progress) {
 521                ASSERT(false);
 522        }
 523
 524        dm_write_reg(ctx, addr_index, index);
 525        value = dm_read_reg(ctx, addr_data);
 526
 527        return value;
 528}
 529
 530uint32_t generic_indirect_reg_get(const struct dc_context *ctx,
 531                uint32_t addr_index, uint32_t addr_data,
 532                uint32_t index, int n,
 533                uint8_t shift1, uint32_t mask1, uint32_t *field_value1,
 534                ...)
 535{
 536        uint32_t shift, mask, *field_value;
 537        uint32_t value = 0;
 538        int i = 1;
 539
 540        va_list ap;
 541
 542        va_start(ap, field_value1);
 543
 544        value = generic_read_indirect_reg(ctx, addr_index, addr_data, index);
 545        *field_value1 = get_reg_field_value_ex(value, mask1, shift1);
 546
 547        while (i < n) {
 548                shift = va_arg(ap, uint32_t);
 549                mask = va_arg(ap, uint32_t);
 550                field_value = va_arg(ap, uint32_t *);
 551
 552                *field_value = get_reg_field_value_ex(value, mask, shift);
 553                i++;
 554        }
 555
 556        va_end(ap);
 557
 558        return value;
 559}
 560
 561uint32_t generic_indirect_reg_update_ex(const struct dc_context *ctx,
 562                uint32_t addr_index, uint32_t addr_data,
 563                uint32_t index, uint32_t reg_val, int n,
 564                uint8_t shift1, uint32_t mask1, uint32_t field_value1,
 565                ...)
 566{
 567        uint32_t shift, mask, field_value;
 568        int i = 1;
 569
 570        va_list ap;
 571
 572        va_start(ap, field_value1);
 573
 574        reg_val = set_reg_field_value_ex(reg_val, field_value1, mask1, shift1);
 575
 576        while (i < n) {
 577                shift = va_arg(ap, uint32_t);
 578                mask = va_arg(ap, uint32_t);
 579                field_value = va_arg(ap, uint32_t);
 580
 581                reg_val = set_reg_field_value_ex(reg_val, field_value, mask, shift);
 582                i++;
 583        }
 584
 585        generic_write_indirect_reg(ctx, addr_index, addr_data, index, reg_val);
 586        va_end(ap);
 587
 588        return reg_val;
 589}
 590
 591void reg_sequence_start_gather(const struct dc_context *ctx)
 592{
 593        /* if reg sequence is supported and enabled, set flag to
 594         * indicate we want to have REG_SET, REG_UPDATE macro build
 595         * reg sequence command buffer rather than MMIO directly.
 596         */
 597
 598        if (ctx->dmub_srv && ctx->dc->debug.dmub_offload_enabled) {
 599                struct dc_reg_helper_state *offload =
 600                        &ctx->dmub_srv->reg_helper_offload;
 601
 602                /* caller sequence mismatch.  need to debug caller.  offload will not work!!! */
 603                ASSERT(!offload->gather_in_progress);
 604
 605                offload->gather_in_progress = true;
 606        }
 607}
 608
 609void reg_sequence_start_execute(const struct dc_context *ctx)
 610{
 611        struct dc_reg_helper_state *offload;
 612
 613        if (!ctx->dmub_srv)
 614                return;
 615
 616        offload = &ctx->dmub_srv->reg_helper_offload;
 617
 618        if (offload && offload->gather_in_progress) {
 619                offload->gather_in_progress = false;
 620                offload->should_burst_write = false;
 621                switch (offload->cmd_data.cmd_common.header.type) {
 622                case DMUB_CMD__REG_SEQ_READ_MODIFY_WRITE:
 623                        submit_dmub_read_modify_write(offload, ctx);
 624                        break;
 625                case DMUB_CMD__REG_REG_WAIT:
 626                        submit_dmub_reg_wait(offload, ctx);
 627                        break;
 628                case DMUB_CMD__REG_SEQ_BURST_WRITE:
 629                        submit_dmub_burst_write(offload, ctx);
 630                        break;
 631                default:
 632                        return;
 633                }
 634
 635                dc_dmub_srv_cmd_execute(ctx->dmub_srv);
 636        }
 637}
 638
 639void reg_sequence_wait_done(const struct dc_context *ctx)
 640{
 641        /* callback to DM to poll for last submission done*/
 642        struct dc_reg_helper_state *offload;
 643
 644        if (!ctx->dmub_srv)
 645                return;
 646
 647        offload = &ctx->dmub_srv->reg_helper_offload;
 648
 649        if (offload &&
 650            ctx->dc->debug.dmub_offload_enabled &&
 651            !ctx->dc->debug.dmcub_emulation) {
 652                dc_dmub_srv_wait_idle(ctx->dmub_srv);
 653        }
 654}
 655