root/drivers/gpu/drm/arm/display/komeda/komeda_event.c
// SPDX-License-Identifier: GPL-2.0
/*
 * (C) COPYRIGHT 2019 ARM Limited. All rights reserved.
 * Author: James.Qian.Wang <james.qian.wang@arm.com>
 *
 */
#include <drm/drm_atomic.h>
#include <drm/drm_print.h>

#include "komeda_dev.h"

struct komeda_str {
        char *str;
        u32 sz;
        u32 len;
};

/* return 0 on success,  < 0 on no space.
 */
__printf(2, 3)
static int komeda_sprintf(struct komeda_str *str, const char *fmt, ...)
{
        va_list args;
        int num, free_sz;
        int err;

        free_sz = str->sz - str->len - 1;
        if (free_sz <= 0)
                return -ENOSPC;

        va_start(args, fmt);

        num = vsnprintf(str->str + str->len, free_sz, fmt, args);

        va_end(args);

        if (num < free_sz) {
                str->len += num;
                err = 0;
        } else {
                str->len = str->sz - 1;
                err = -ENOSPC;
        }

        return err;
}

static void evt_sprintf(struct komeda_str *str, u64 evt, const char *msg)
{
        if (evt)
                komeda_sprintf(str, msg);
}

static void evt_str(struct komeda_str *str, u64 events)
{
        if (events == 0ULL) {
                komeda_sprintf(str, "None");
                return;
        }

        evt_sprintf(str, events & KOMEDA_EVENT_VSYNC, "VSYNC|");
        evt_sprintf(str, events & KOMEDA_EVENT_FLIP, "FLIP|");
        evt_sprintf(str, events & KOMEDA_EVENT_EOW, "EOW|");
        evt_sprintf(str, events & KOMEDA_EVENT_MODE, "OP-MODE|");

        evt_sprintf(str, events & KOMEDA_EVENT_URUN, "UNDERRUN|");
        evt_sprintf(str, events & KOMEDA_EVENT_OVR, "OVERRUN|");

        /* GLB error */
        evt_sprintf(str, events & KOMEDA_ERR_MERR, "MERR|");
        evt_sprintf(str, events & KOMEDA_ERR_FRAMETO, "FRAMETO|");

        /* DOU error */
        evt_sprintf(str, events & KOMEDA_ERR_DRIFTTO, "DRIFTTO|");
        evt_sprintf(str, events & KOMEDA_ERR_FRAMETO, "FRAMETO|");
        evt_sprintf(str, events & KOMEDA_ERR_TETO, "TETO|");
        evt_sprintf(str, events & KOMEDA_ERR_CSCE, "CSCE|");

        /* LPU errors or events */
        evt_sprintf(str, events & KOMEDA_EVENT_IBSY, "IBSY|");
        evt_sprintf(str, events & KOMEDA_EVENT_EMPTY, "EMPTY|");
        evt_sprintf(str, events & KOMEDA_EVENT_FULL, "FULL|");
        evt_sprintf(str, events & KOMEDA_ERR_AXIE, "AXIE|");
        evt_sprintf(str, events & KOMEDA_ERR_ACE0, "ACE0|");
        evt_sprintf(str, events & KOMEDA_ERR_ACE1, "ACE1|");
        evt_sprintf(str, events & KOMEDA_ERR_ACE2, "ACE2|");
        evt_sprintf(str, events & KOMEDA_ERR_ACE3, "ACE3|");

        /* LPU TBU errors*/
        evt_sprintf(str, events & KOMEDA_ERR_TCF, "TCF|");
        evt_sprintf(str, events & KOMEDA_ERR_TTNG, "TTNG|");
        evt_sprintf(str, events & KOMEDA_ERR_TITR, "TITR|");
        evt_sprintf(str, events & KOMEDA_ERR_TEMR, "TEMR|");
        evt_sprintf(str, events & KOMEDA_ERR_TTF, "TTF|");

        /* CU errors*/
        evt_sprintf(str, events & KOMEDA_ERR_CPE, "COPROC|");
        evt_sprintf(str, events & KOMEDA_ERR_ZME, "ZME|");
        evt_sprintf(str, events & KOMEDA_ERR_CFGE, "CFGE|");
        evt_sprintf(str, events & KOMEDA_ERR_TEMR, "TEMR|");

        if (str->len > 0 && (str->str[str->len - 1] == '|')) {
                str->str[str->len - 1] = 0;
                str->len--;
        }
}

static bool is_new_frame(struct komeda_events *a)
{
        return (a->pipes[0] | a->pipes[1]) &
               (KOMEDA_EVENT_FLIP | KOMEDA_EVENT_EOW);
}

void komeda_print_events(struct komeda_events *evts, struct drm_device *dev)
{
        u64 print_evts = 0;
        static bool en_print = true;
        struct komeda_dev *mdev = dev->dev_private;
        u16 const err_verbosity = mdev->err_verbosity;
        u64 evts_mask = evts->global | evts->pipes[0] | evts->pipes[1];

        /* reduce the same msg print, only print the first evt for one frame */
        if (evts->global || is_new_frame(evts))
                en_print = true;
        if (!(err_verbosity & KOMEDA_DEV_PRINT_DISABLE_RATELIMIT) && !en_print)
                return;

        if (err_verbosity & KOMEDA_DEV_PRINT_ERR_EVENTS)
                print_evts |= KOMEDA_ERR_EVENTS;
        if (err_verbosity & KOMEDA_DEV_PRINT_WARN_EVENTS)
                print_evts |= KOMEDA_WARN_EVENTS;
        if (err_verbosity & KOMEDA_DEV_PRINT_INFO_EVENTS)
                print_evts |= KOMEDA_INFO_EVENTS;

        if (evts_mask & print_evts) {
                char msg[256];
                struct komeda_str str;
                struct drm_printer p = drm_info_printer(dev->dev);

                str.str = msg;
                str.sz  = sizeof(msg);
                str.len = 0;

                komeda_sprintf(&str, "gcu: ");
                evt_str(&str, evts->global);
                komeda_sprintf(&str, ", pipes[0]: ");
                evt_str(&str, evts->pipes[0]);
                komeda_sprintf(&str, ", pipes[1]: ");
                evt_str(&str, evts->pipes[1]);

                DRM_ERROR("err detect: %s\n", msg);
                if ((err_verbosity & KOMEDA_DEV_PRINT_DUMP_STATE_ON_EVENT) &&
                    (evts_mask & (KOMEDA_ERR_EVENTS | KOMEDA_WARN_EVENTS)))
                        drm_state_dump(dev, &p);

                en_print = false;
        }
}