ad4eb1f407
To improve code readability address multiple warnings and errors. Fixed coding style issues such as indentation, spacing, semicolon, etc. Change-Id: Id0f87f055ae69b92902d181ed61e0cf2fd77e6e2 Signed-off-by: Sanjay Yadav <quic_sanjyada@quicinc.com>
428 lines
12 KiB
C
428 lines
12 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2002,2007-2021, The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/interconnect.h>
|
|
#include <linux/sched/clock.h>
|
|
#include <linux/slab.h>
|
|
#include <soc/qcom/dcvs.h>
|
|
|
|
#include "a3xx_reg.h"
|
|
#include "a5xx_reg.h"
|
|
#include "a6xx_reg.h"
|
|
#include "adreno.h"
|
|
#include "adreno_pm4types.h"
|
|
#include "adreno_ringbuffer.h"
|
|
#include "adreno_trace.h"
|
|
#include "kgsl_trace.h"
|
|
|
|
|
|
#define RB_HOSTPTR(_rb, _pos) \
|
|
((unsigned int *) ((_rb)->buffer_desc->hostptr + \
|
|
((_pos) * sizeof(unsigned int))))
|
|
|
|
#define RB_GPUADDR(_rb, _pos) \
|
|
((_rb)->buffer_desc->gpuaddr + ((_pos) * sizeof(unsigned int)))
|
|
|
|
void adreno_get_submit_time(struct adreno_device *adreno_dev,
|
|
struct adreno_ringbuffer *rb,
|
|
struct adreno_submit_time *time)
|
|
{
|
|
const struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
|
|
unsigned long flags;
|
|
struct adreno_context *drawctxt = rb->drawctxt_active;
|
|
struct kgsl_context *context = &drawctxt->base;
|
|
|
|
if (!time)
|
|
return;
|
|
|
|
/*
|
|
* Here we are attempting to create a mapping between the
|
|
* GPU time domain (alwayson counter) and the CPU time domain
|
|
* (local_clock) by sampling both values as close together as
|
|
* possible. This is useful for many types of debugging and
|
|
* profiling. In order to make this mapping as accurate as
|
|
* possible, we must turn off interrupts to avoid running
|
|
* interrupt handlers between the two samples.
|
|
*/
|
|
|
|
local_irq_save(flags);
|
|
|
|
time->ticks = gpudev->read_alwayson(adreno_dev);
|
|
|
|
/* Trace the GPU time to create a mapping to ftrace time */
|
|
trace_adreno_cmdbatch_sync(context->id, context->priority,
|
|
drawctxt->timestamp, time->ticks);
|
|
|
|
/* Get the kernel clock for time since boot */
|
|
time->ktime = local_clock();
|
|
|
|
/* Get the timeofday for the wall time (for the user) */
|
|
ktime_get_real_ts64(&time->utime);
|
|
|
|
local_irq_restore(flags);
|
|
}
|
|
|
|
unsigned int *adreno_ringbuffer_allocspace(struct adreno_ringbuffer *rb,
|
|
unsigned int dwords)
|
|
{
|
|
struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb);
|
|
unsigned int rptr = adreno_get_rptr(rb);
|
|
unsigned int ret;
|
|
|
|
if (rptr <= rb->_wptr) {
|
|
unsigned int *cmds;
|
|
|
|
if (rb->_wptr + dwords <= (KGSL_RB_DWORDS - 2)) {
|
|
ret = rb->_wptr;
|
|
rb->_wptr = (rb->_wptr + dwords) % KGSL_RB_DWORDS;
|
|
return RB_HOSTPTR(rb, ret);
|
|
}
|
|
|
|
/*
|
|
* There isn't enough space toward the end of ringbuffer. So
|
|
* look for space from the beginning of ringbuffer upto the
|
|
* read pointer.
|
|
*/
|
|
if (dwords < rptr) {
|
|
cmds = RB_HOSTPTR(rb, rb->_wptr);
|
|
*cmds = cp_packet(adreno_dev, CP_NOP,
|
|
KGSL_RB_DWORDS - rb->_wptr - 1);
|
|
rb->_wptr = dwords;
|
|
return RB_HOSTPTR(rb, 0);
|
|
}
|
|
}
|
|
|
|
if (rb->_wptr + dwords < rptr) {
|
|
ret = rb->_wptr;
|
|
rb->_wptr = (rb->_wptr + dwords) % KGSL_RB_DWORDS;
|
|
return RB_HOSTPTR(rb, ret);
|
|
}
|
|
|
|
return ERR_PTR(-ENOSPC);
|
|
}
|
|
|
|
void adreno_ringbuffer_stop(struct adreno_device *adreno_dev)
|
|
{
|
|
struct adreno_ringbuffer *rb;
|
|
int i;
|
|
|
|
FOR_EACH_RINGBUFFER(adreno_dev, rb, i)
|
|
kgsl_cancel_events(KGSL_DEVICE(adreno_dev), &(rb->events));
|
|
}
|
|
|
|
static int _rb_readtimestamp(struct kgsl_device *device,
|
|
void *priv, enum kgsl_timestamp_type type,
|
|
unsigned int *timestamp)
|
|
{
|
|
return adreno_rb_readtimestamp(ADRENO_DEVICE(device), priv, type,
|
|
timestamp);
|
|
}
|
|
|
|
int adreno_ringbuffer_setup(struct adreno_device *adreno_dev,
|
|
struct adreno_ringbuffer *rb, int id)
|
|
{
|
|
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
|
unsigned int priv = 0;
|
|
int ret;
|
|
|
|
/* allocate a chunk of memory to create user profiling IB1s */
|
|
adreno_allocate_global(device, &rb->profile_desc, PAGE_SIZE,
|
|
0, KGSL_MEMFLAGS_GPUREADONLY, 0, "profile_desc");
|
|
|
|
if (ADRENO_FEATURE(adreno_dev, ADRENO_APRIV))
|
|
priv |= KGSL_MEMDESC_PRIVILEGED;
|
|
|
|
ret = adreno_allocate_global(device, &rb->buffer_desc, KGSL_RB_SIZE,
|
|
SZ_4K, KGSL_MEMFLAGS_GPUREADONLY, priv, "ringbuffer");
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!list_empty(&rb->events.group))
|
|
return 0;
|
|
|
|
rb->id = id;
|
|
kgsl_add_event_group(device, &rb->events, NULL, _rb_readtimestamp, rb,
|
|
"rb_events-%d", id);
|
|
|
|
rb->timestamp = 0;
|
|
init_waitqueue_head(&rb->ts_expire_waitq);
|
|
|
|
spin_lock_init(&rb->preempt_lock);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void adreno_preemption_timer(struct timer_list *t)
|
|
{
|
|
struct adreno_preemption *preempt = from_timer(preempt, t, timer);
|
|
struct adreno_device *adreno_dev = container_of(preempt,
|
|
struct adreno_device, preempt);
|
|
|
|
/* We should only be here from a triggered state */
|
|
if (!adreno_move_preempt_state(adreno_dev,
|
|
ADRENO_PREEMPT_TRIGGERED, ADRENO_PREEMPT_FAULTED))
|
|
return;
|
|
|
|
/* Schedule the worker to take care of the details */
|
|
queue_work(system_unbound_wq, &adreno_dev->preempt.work);
|
|
}
|
|
|
|
void adreno_drawobj_set_constraint(struct kgsl_device *device,
|
|
struct kgsl_drawobj *drawobj)
|
|
{
|
|
struct kgsl_context *context = drawobj->context;
|
|
unsigned long flags = drawobj->flags;
|
|
|
|
/*
|
|
* Check if the context has a constraint and constraint flags are
|
|
* set.
|
|
*/
|
|
if (context->pwr_constraint.type &&
|
|
((context->flags & KGSL_CONTEXT_PWR_CONSTRAINT) ||
|
|
(drawobj->flags & KGSL_CONTEXT_PWR_CONSTRAINT)))
|
|
kgsl_pwrctrl_set_constraint(device, &context->pwr_constraint,
|
|
context->id, drawobj->timestamp);
|
|
|
|
if (context->l3_pwr_constraint.type &&
|
|
((context->flags & KGSL_CONTEXT_PWR_CONSTRAINT) ||
|
|
(flags & KGSL_CONTEXT_PWR_CONSTRAINT))) {
|
|
|
|
if (!device->num_l3_pwrlevels) {
|
|
dev_err_once(device->dev,
|
|
"l3 voting not available\n");
|
|
return;
|
|
}
|
|
|
|
switch (context->l3_pwr_constraint.type) {
|
|
case KGSL_CONSTRAINT_L3_PWRLEVEL: {
|
|
unsigned int sub_type;
|
|
unsigned int new_l3;
|
|
int ret = 0;
|
|
struct dcvs_freq freq = {0};
|
|
|
|
if (!device->l3_vote)
|
|
return;
|
|
|
|
sub_type = context->l3_pwr_constraint.sub_type;
|
|
|
|
/*
|
|
* If an L3 constraint is already set, set the new
|
|
* one only if it is higher.
|
|
*/
|
|
new_l3 = max_t(unsigned int, sub_type + 1,
|
|
device->cur_l3_pwrlevel);
|
|
new_l3 = min_t(unsigned int, new_l3,
|
|
device->num_l3_pwrlevels - 1);
|
|
|
|
if (device->cur_l3_pwrlevel == new_l3)
|
|
return;
|
|
|
|
freq.ib = device->l3_freq[new_l3];
|
|
freq.hw_type = DCVS_L3;
|
|
ret = qcom_dcvs_update_votes(KGSL_L3_DEVICE, &freq, 1,
|
|
DCVS_SLOW_PATH);
|
|
if (!ret) {
|
|
trace_kgsl_constraint(device,
|
|
KGSL_CONSTRAINT_L3_PWRLEVEL, new_l3, 1);
|
|
device->cur_l3_pwrlevel = new_l3;
|
|
} else {
|
|
dev_err_ratelimited(device->dev,
|
|
"Could not set l3_vote: %d\n",
|
|
ret);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
|
|
struct kgsl_drawobj_cmd *cmdobj,
|
|
struct adreno_submit_time *time)
|
|
{
|
|
struct adreno_submit_time local = { 0 };
|
|
struct kgsl_drawobj *drawobj = DRAWOBJ(cmdobj);
|
|
struct adreno_context *drawctxt = ADRENO_CONTEXT(drawobj->context);
|
|
struct adreno_ringbuffer *rb = drawctxt->rb;
|
|
const struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
|
|
u32 flags = 0;
|
|
int ret;
|
|
|
|
/*
|
|
* If SKIP CMD flag is set for current context
|
|
* a) set SKIPCMD as fault_recovery for current commandbatch
|
|
* b) store context's commandbatch fault_policy in current
|
|
* commandbatch fault_policy and clear context's commandbatch
|
|
* fault_policy
|
|
* c) force preamble for commandbatch
|
|
*/
|
|
if (test_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->base.priv) &&
|
|
(!test_bit(CMDOBJ_SKIP, &cmdobj->priv))) {
|
|
|
|
set_bit(KGSL_FT_SKIPCMD, &cmdobj->fault_recovery);
|
|
cmdobj->fault_policy = drawctxt->fault_policy;
|
|
set_bit(CMDOBJ_FORCE_PREAMBLE, &cmdobj->priv);
|
|
|
|
/* if context is detached print fault recovery */
|
|
adreno_fault_skipcmd_detached(adreno_dev, drawctxt, drawobj);
|
|
|
|
/* clear the drawctxt flags */
|
|
clear_bit(ADRENO_CONTEXT_SKIP_CMD, &drawctxt->base.priv);
|
|
drawctxt->fault_policy = 0;
|
|
}
|
|
|
|
/* Check if user profiling should be enabled */
|
|
|
|
if ((drawobj->flags & KGSL_DRAWOBJ_PROFILING) &&
|
|
cmdobj->profiling_buf_entry) {
|
|
flags |= F_USER_PROFILE;
|
|
|
|
/*
|
|
* we want to use an adreno_submit_time struct to get the
|
|
* precise moment when the command is submitted to the
|
|
* ringbuffer. If an upstream caller already passed down a
|
|
* pointer piggyback on that otherwise use a local struct
|
|
*/
|
|
if (!time)
|
|
time = &local;
|
|
|
|
time->drawobj = drawobj;
|
|
}
|
|
|
|
flags |= F_PREAMBLE;
|
|
|
|
/*
|
|
* When preamble is enabled, the preamble buffer with state restoration
|
|
* commands are stored in the first node of the IB chain.
|
|
* We can skip that if a context switch hasn't occurred.
|
|
*/
|
|
if ((drawctxt->base.flags & KGSL_CONTEXT_PREAMBLE) &&
|
|
!test_bit(CMDOBJ_FORCE_PREAMBLE, &cmdobj->priv) &&
|
|
(rb->drawctxt_active == drawctxt))
|
|
flags &= ~F_PREAMBLE;
|
|
|
|
/*
|
|
* In skip mode don't issue the draw IBs but keep all the other
|
|
* accoutrements of a submision (including the interrupt) to keep
|
|
* the accounting sane. Set start_index and numibs to 0 to just
|
|
* generate the start and end markers and skip everything else
|
|
*/
|
|
if (test_bit(CMDOBJ_SKIP, &cmdobj->priv)) {
|
|
flags &= ~F_PREAMBLE;
|
|
flags |= F_SKIP;
|
|
}
|
|
|
|
/* Enable kernel profiling */
|
|
if (test_bit(CMDOBJ_PROFILE, &cmdobj->priv))
|
|
flags |= F_KERNEL_PROFILE;
|
|
|
|
/* Add a WFI to the end of the submission */
|
|
if (test_bit(CMDOBJ_WFI, &cmdobj->priv))
|
|
flags |= F_WFI;
|
|
|
|
/*
|
|
* For some targets, we need to execute a dummy shader operation after a
|
|
* power collapse
|
|
*/
|
|
if (test_and_clear_bit(ADRENO_DEVICE_PWRON, &adreno_dev->priv) &&
|
|
test_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv))
|
|
flags |= F_PWRON_FIXUP;
|
|
|
|
/* Check to see the submission should be secure */
|
|
if (drawobj->context->flags & KGSL_CONTEXT_SECURE)
|
|
flags |= F_SECURE;
|
|
|
|
/* process any profiling results that are available into the log_buf */
|
|
adreno_profile_process_results(adreno_dev);
|
|
|
|
ret = gpudev->ringbuffer_submitcmd(adreno_dev, cmdobj,
|
|
flags, time);
|
|
|
|
if (!ret) {
|
|
set_bit(KGSL_CONTEXT_PRIV_SUBMITTED, &drawobj->context->priv);
|
|
cmdobj->global_ts = drawctxt->internal_timestamp;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* adreno_ringbuffer_wait_callback() - Callback function for event registered
|
|
* on a ringbuffer timestamp
|
|
* @device: Device for which the callback is valid
|
|
* @context: The context of the event
|
|
* @priv: The private parameter of the event
|
|
* @result: Result of the event trigger
|
|
*/
|
|
static void adreno_ringbuffer_wait_callback(struct kgsl_device *device,
|
|
struct kgsl_event_group *group,
|
|
void *priv, int result)
|
|
{
|
|
struct adreno_ringbuffer *rb = group->priv;
|
|
|
|
wake_up_all(&rb->ts_expire_waitq);
|
|
}
|
|
|
|
/* check if timestamp is greater than the current rb timestamp */
|
|
static inline int adreno_ringbuffer_check_timestamp(
|
|
struct adreno_ringbuffer *rb,
|
|
unsigned int timestamp, int type)
|
|
{
|
|
struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb);
|
|
unsigned int ts;
|
|
|
|
adreno_rb_readtimestamp(adreno_dev, rb, type, &ts);
|
|
return (timestamp_cmp(ts, timestamp) >= 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* adreno_ringbuffer_waittimestamp() - Wait for a RB timestamp
|
|
* @rb: The ringbuffer to wait on
|
|
* @timestamp: The timestamp to wait for
|
|
* @msecs: The wait timeout period
|
|
*/
|
|
int adreno_ringbuffer_waittimestamp(struct adreno_ringbuffer *rb,
|
|
unsigned int timestamp,
|
|
unsigned int msecs)
|
|
{
|
|
struct adreno_device *adreno_dev = ADRENO_RB_DEVICE(rb);
|
|
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
|
|
int ret;
|
|
unsigned long wait_time;
|
|
|
|
/* check immediately if timeout is 0 */
|
|
if (msecs == 0)
|
|
return adreno_ringbuffer_check_timestamp(rb,
|
|
timestamp, KGSL_TIMESTAMP_RETIRED) ? 0 : -EBUSY;
|
|
|
|
ret = kgsl_add_event(device, &rb->events, timestamp,
|
|
adreno_ringbuffer_wait_callback, NULL);
|
|
if (ret)
|
|
return ret;
|
|
|
|
mutex_unlock(&device->mutex);
|
|
|
|
wait_time = msecs_to_jiffies(msecs);
|
|
if (wait_event_timeout(rb->ts_expire_waitq,
|
|
!kgsl_event_pending(device, &rb->events, timestamp,
|
|
adreno_ringbuffer_wait_callback, NULL),
|
|
wait_time) == 0)
|
|
ret = -ETIMEDOUT;
|
|
|
|
mutex_lock(&device->mutex);
|
|
/*
|
|
* after wake up make sure that expected timestamp has retired
|
|
* because the wakeup could have happened due to a cancel event
|
|
*/
|
|
if (!ret && !adreno_ringbuffer_check_timestamp(rb,
|
|
timestamp, KGSL_TIMESTAMP_RETIRED)) {
|
|
ret = -EAGAIN;
|
|
}
|
|
|
|
return ret;
|
|
}
|