msm: kgsl: Add a power constraints interface

Add an interface to hint the user specified power
constraint. User can specify a particular GPU power level
or a bandwidth vote. The power constraints are per context
or command batch. Power constraints are only for hinting
and should be used judiciously.

Change-Id: Ic10a5fff4abc88c51cd45a7d7ac58c0d4e5dcb25
Signed-off-by: Suman Tatiraju <sumant@codeaurora.org>
Signed-off-by: Lucille Sylvester <lsylvest@codeaurora.org>
This commit is contained in:
Suman Tatiraju 2013-11-18 18:31:19 -08:00 committed by Carter Cooper
parent dbfb277e44
commit eeb3ca12b7
11 changed files with 284 additions and 12 deletions

View file

@ -2226,12 +2226,65 @@ static int adreno_getproperty(struct kgsl_device *device,
return status;
}
static int adreno_setproperty(struct kgsl_device *device,
static int adreno_set_constraint(struct kgsl_device *device,
struct kgsl_context *context,
struct kgsl_device_constraint *constraint)
{
int status = 0;
switch (constraint->type) {
case KGSL_CONSTRAINT_PWRLEVEL: {
struct kgsl_device_constraint_pwrlevel pwr;
if (constraint->size != sizeof(pwr)) {
status = -EINVAL;
break;
}
if (copy_from_user(&pwr,
(void __user *)constraint->data,
sizeof(pwr))) {
status = -EFAULT;
break;
}
if (pwr.level >= KGSL_CONSTRAINT_PWR_MAXLEVELS) {
status = -EINVAL;
break;
}
context->pwr_constraint.type =
KGSL_CONSTRAINT_PWRLEVEL;
context->pwr_constraint.sub_type = pwr.level;
trace_kgsl_user_pwrlevel_constraint(device,
context->id,
context->pwr_constraint.type,
context->pwr_constraint.sub_type);
}
break;
case KGSL_CONSTRAINT_NONE:
if (context->pwr_constraint.type == KGSL_CONSTRAINT_PWRLEVEL)
trace_kgsl_user_pwrlevel_constraint(device,
context->id,
KGSL_CONSTRAINT_NONE,
context->pwr_constraint.sub_type);
context->pwr_constraint.type = KGSL_CONSTRAINT_NONE;
break;
default:
status = -EINVAL;
break;
}
return status;
}
static int adreno_setproperty(struct kgsl_device_private *dev_priv,
enum kgsl_property_type type,
void *value,
unsigned int sizebytes)
{
int status = -EINVAL;
struct kgsl_device *device = dev_priv->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
switch (type) {
@ -2260,6 +2313,28 @@ static int adreno_setproperty(struct kgsl_device *device,
status = 0;
}
break;
case KGSL_PROP_PWR_CONSTRAINT: {
struct kgsl_device_constraint constraint;
struct kgsl_context *context;
if (sizebytes != sizeof(constraint))
break;
if (copy_from_user(&constraint, value,
sizeof(constraint))) {
status = -EFAULT;
break;
}
context = kgsl_context_get_owner(dev_priv,
constraint.context_id);
if (context == NULL)
break;
status = adreno_set_constraint(device, context,
&constraint);
kgsl_context_put(context);
}
break;
default:
break;
}

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2002,2007-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -429,7 +429,8 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
KGSL_CONTEXT_PER_CONTEXT_TS |
KGSL_CONTEXT_USER_GENERATED_TS |
KGSL_CONTEXT_NO_FAULT_TOLERANCE |
KGSL_CONTEXT_TYPE_MASK);
KGSL_CONTEXT_TYPE_MASK |
KGSL_CONTEXT_PWR_CONSTRAINT);
/* Always enable per-context timestamps */
*flags |= KGSL_CONTEXT_PER_CONTEXT_TS;
@ -444,6 +445,9 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
if (*flags & KGSL_CONTEXT_USER_GENERATED_TS)
drawctxt->flags |= CTXT_FLAGS_USER_GENERATED_TS;
if (*flags & KGSL_CONTEXT_PWR_CONSTRAINT)
drawctxt->flags |= CTXT_FLAGS_PWR_CONSTRAINT;
mutex_init(&drawctxt->mutex);
init_waitqueue_head(&drawctxt->wq);
init_waitqueue_head(&drawctxt->waiting);

View file

@ -54,6 +54,8 @@
#define CTXT_FLAGS_NO_FAULT_TOLERANCE BIT(16)
/* Force the preamble for the next submission */
#define CTXT_FLAGS_FORCE_PREAMBLE BIT(17)
/* power constraints enabled */
#define CTXT_FLAGS_PWR_CONSTRAINT BIT(18)
/* Symbolic table for the adreno draw context type */
#define ADRENO_DRAWCTXT_TYPES \

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2002,2007-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -987,6 +987,67 @@ adreno_ringbuffer_issueibcmds(struct kgsl_device_private *dev_priv,
return ret;
}
unsigned int adreno_ringbuffer_get_constraint(struct kgsl_device *device,
struct kgsl_context *context)
{
unsigned int pwrlevel = device->pwrctrl.active_pwrlevel;
switch (context->pwr_constraint.type) {
case KGSL_CONSTRAINT_PWRLEVEL: {
switch (context->pwr_constraint.sub_type) {
case KGSL_CONSTRAINT_PWR_MAX:
pwrlevel = device->pwrctrl.max_pwrlevel;
break;
case KGSL_CONSTRAINT_PWR_MIN:
pwrlevel = device->pwrctrl.min_pwrlevel;
break;
default:
break;
}
}
break;
}
return pwrlevel;
}
void adreno_ringbuffer_set_constraint(struct kgsl_device *device,
struct kgsl_cmdbatch *cmdbatch)
{
unsigned int constraint;
struct kgsl_context *context = cmdbatch->context;
struct adreno_context *drawctxt;
drawctxt = ADRENO_CONTEXT(context);
/*
* Check if the context has a constraint and constraint flags are
* set.
*/
if (context->pwr_constraint.type &&
((drawctxt->flags & KGSL_CONTEXT_PWR_CONSTRAINT) ||
(cmdbatch->flags & KGSL_CONTEXT_PWR_CONSTRAINT))) {
constraint = adreno_ringbuffer_get_constraint(device, context);
/*
* If a constraint is already set, set a new
* constraint only if it is faster
*/
if ((device->pwrctrl.constraint.type ==
KGSL_CONSTRAINT_NONE) || (constraint <
device->pwrctrl.constraint.hint.pwrlevel.level)) {
kgsl_pwrctrl_pwrlevel_change(device, constraint);
device->pwrctrl.constraint.type =
context->pwr_constraint.type;
device->pwrctrl.constraint.hint.
pwrlevel.level = constraint;
}
}
}
/* adreno_rindbuffer_submitcmd - submit userspace IBs to the GPU */
int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
struct kgsl_cmdbatch *cmdbatch)
@ -1094,6 +1155,9 @@ int adreno_ringbuffer_submitcmd(struct adreno_device *adreno_dev,
test_bit(ADRENO_DEVICE_PWRON_FIXUP, &adreno_dev->priv))
flags |= KGSL_CMD_FLAGS_PWRON_FIXUP;
/* Set the constraints before adding to ringbuffer */
adreno_ringbuffer_set_constraint(device, cmdbatch);
ret = adreno_ringbuffer_addcmds(&adreno_dev->ringbuffer,
drawctxt,
flags,

View file

@ -21,6 +21,7 @@
#define TRACE_INCLUDE_FILE adreno_trace
#include <linux/tracepoint.h>
#include "kgsl_device.h"
TRACE_EVENT(adreno_cmdbatch_queued,
TP_PROTO(struct kgsl_cmdbatch *cmdbatch, unsigned int queued),
@ -264,6 +265,36 @@ TRACE_EVENT(adreno_gpu_fault,
__entry->ib2base, __entry->ib2size)
);
TRACE_EVENT(kgsl_user_pwrlevel_constraint,
TP_PROTO(struct kgsl_device *device, unsigned int id, unsigned int type,
unsigned int sub_type),
TP_ARGS(device, id, type, sub_type),
TP_STRUCT__entry(
__string(device_name, device->name)
__field(unsigned int, id)
__field(unsigned int, type)
__field(unsigned int, sub_type)
),
TP_fast_assign(
__assign_str(device_name, device->name);
__entry->id = id;
__entry->type = type;
__entry->sub_type = sub_type;
),
TP_printk(
"d_name=%s ctx=%u constraint_type=%s constraint_subtype=%s",
__get_str(device_name), __entry->id,
__print_symbolic(__entry->type, KGSL_CONSTRAINT_TYPES),
__print_symbolic(__entry->sub_type,
KGSL_CONSTRAINT_PWRLEVEL_SUBTYPES)
)
);
#endif /* _ADRENO_TRACE_H */
/* This part must be outside protection */

View file

@ -1397,8 +1397,8 @@ static long kgsl_ioctl_device_setproperty(struct kgsl_device_private *dev_priv,
if (dev_priv->device->ftbl->setproperty)
result = dev_priv->device->ftbl->setproperty(
dev_priv->device, param->type,
param->value, param->sizebytes);
dev_priv, param->type, param->value,
param->sizebytes);
return result;
}

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2002,2007-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2002,2007-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -127,8 +127,8 @@ struct kgsl_functable {
void (*drawctxt_destroy) (struct kgsl_context *context);
long (*ioctl) (struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data);
int (*setproperty) (struct kgsl_device *device,
enum kgsl_property_type type, void *value,
int (*setproperty) (struct kgsl_device_private *dev_priv,
enum kgsl_property_type type, void __user *value,
unsigned int sizebytes);
int (*postmortem_dump) (struct kgsl_device *device, int manual);
void (*drawctxt_sched)(struct kgsl_device *device,
@ -361,7 +361,7 @@ struct kgsl_process_private;
* is set.
* @fault_count: number of times gpu hanged in last _context_throttle_time ms
* @fault_time: time of the first gpu hang in last _context_throttle_time ms
>>>>>>> 28ee7f6... msm: kgsl: Enhance GFT to avoid hang->recover->hang cycle
* @pwr_constraint: power constraint from userspace for this context
*/
struct kgsl_context {
struct kref refcount;
@ -379,6 +379,7 @@ struct kgsl_context {
unsigned int pagefault_ts;
unsigned int fault_count;
unsigned long fault_time;
struct kgsl_pwr_constraint pwr_constraint;
};
struct kgsl_process_private {

View file

@ -1,4 +1,4 @@
/* Copyright (c) 2010-2013, The Linux Foundation. All rights reserved.
/* Copyright (c) 2010-2014, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@ -27,6 +27,18 @@
#define KGSL_MAX_CLKS 6
/* Only two supported levels, min & max */
#define KGSL_CONSTRAINT_PWR_MAXLEVELS 2
/* Symbolic table for the constraint type */
#define KGSL_CONSTRAINT_TYPES \
{ KGSL_CONSTRAINT_NONE, "None" }, \
{ KGSL_CONSTRAINT_PWRLEVEL, "Pwrlevel" }
/* Symbolic table for the constraint sub type */
#define KGSL_CONSTRAINT_PWRLEVEL_SUBTYPES \
{ KGSL_CONSTRAINT_PWR_MIN, "Min" }, \
{ KGSL_CONSTRAINT_PWR_MAX, "Max" }
struct platform_device;
struct kgsl_clk_stats {
@ -40,6 +52,16 @@ struct kgsl_clk_stats {
unsigned int elapsed_old;
};
struct kgsl_pwr_constraint {
unsigned int type;
unsigned int sub_type;
union {
struct {
unsigned int level;
} pwrlevel;
} hint;
};
/**
* struct kgsl_pwrctrl - Power control settings for a KGSL device
* @interrupt_num - The interrupt number for the device
@ -65,6 +87,7 @@ struct kgsl_clk_stats {
* @pm_qos_req_dma - the power management quality of service structure
* @pm_qos_latency - allowed CPU latency in microseconds
* @step_mul - multiplier for moving between power levels
* @constraint - currently active power constraint
*/
struct kgsl_pwrctrl {
@ -94,6 +117,7 @@ struct kgsl_pwrctrl {
unsigned int pm_qos_latency;
unsigned int step_mul;
unsigned int irq_last;
struct kgsl_pwr_constraint constraint;
};
void kgsl_pwrctrl_irq(struct kgsl_device *device, int state);

View file

@ -22,6 +22,7 @@
#include "kgsl.h"
#include "kgsl_pwrscale.h"
#include "kgsl_device.h"
#include "kgsl_trace.h"
#define TZ_GOVERNOR_PERFORMANCE 0
#define TZ_GOVERNOR_ONDEMAND 1
@ -187,9 +188,19 @@ static void tz_idle(struct kgsl_device *device, struct kgsl_pwrscale *pwrscale)
*/
if (val > 0)
val *= pwr->step_mul;
if (val)
if (val) {
kgsl_pwrctrl_pwrlevel_change(device,
pwr->active_pwrlevel + val);
if (pwr->constraint.type != KGSL_CONSTRAINT_NONE) {
/* Trace the constraint being un-set by the driver */
trace_kgsl_constraint(device,
pwr->constraint.type,
pwr->active_pwrlevel, 0);
/*Invalidate the constraint set */
pwr->constraint.type = KGSL_CONSTRAINT_NONE;
}
}
}
static void tz_busy(struct kgsl_device *device,

View file

@ -676,6 +676,36 @@ TRACE_EVENT(kgsl_context_destroy,
)
);
TRACE_EVENT(kgsl_constraint,
TP_PROTO(struct kgsl_device *device, unsigned int type,
unsigned int value, unsigned int on),
TP_ARGS(device, type, value, on),
TP_STRUCT__entry(
__string(device_name, device->name)
__field(unsigned int, type)
__field(unsigned int, value)
__field(unsigned int, on)
),
TP_fast_assign(
__assign_str(device_name, device->name);
__entry->type = type;
__entry->value = value;
__entry->on = on;
),
TP_printk(
"d_name=%s constraint_type=%s constraint_value=%u status=%s",
__get_str(device_name),
__print_symbolic(__entry->type, KGSL_CONSTRAINT_TYPES),
__entry->value,
__entry->on ? "ON" : "OFF"
)
);
TRACE_EVENT(kgsl_mmu_pagefault,
TP_PROTO(struct kgsl_device *device, unsigned int page,

View file

@ -24,6 +24,7 @@
#define KGSL_CONTEXT_NO_FAULT_TOLERANCE 0x00000200
#define KGSL_CONTEXT_SYNC 0x00000400
#define KGSL_CONTEXT_PWR_CONSTRAINT 0x00000800
/* bits [12:15] are reserved for future use */
#define KGSL_CONTEXT_TYPE_MASK 0x01F00000
#define KGSL_CONTEXT_TYPE_SHIFT 20
@ -196,6 +197,7 @@ enum kgsl_property_type {
KGSL_PROP_VERSION = 0x00000008,
KGSL_PROP_GPU_RESET_STAT = 0x00000009,
KGSL_PROP_PWRCTRL = 0x0000000E,
KGSL_PROP_PWR_CONSTRAINT = 0x00000012,
};
struct kgsl_shadowprop {
@ -883,6 +885,34 @@ struct kgsl_submit_commands {
#define IOCTL_KGSL_SUBMIT_COMMANDS \
_IOWR(KGSL_IOC_TYPE, 0x3D, struct kgsl_submit_commands)
/**
* struct kgsl_device_constraint - device constraint argument
* @context_id: KGSL context ID
* @type: type of constraint i.e pwrlevel/none
* @data: constraint data
* @size: size of the constraint data
*/
struct kgsl_device_constraint {
unsigned int type;
unsigned int context_id;
void __user *data;
size_t size;
};
/* Constraint Type*/
#define KGSL_CONSTRAINT_NONE 0
#define KGSL_CONSTRAINT_PWRLEVEL 1
/* PWRLEVEL constraint level*/
/* set to min frequency */
#define KGSL_CONSTRAINT_PWR_MIN 0
/* set to max frequency */
#define KGSL_CONSTRAINT_PWR_MAX 1
struct kgsl_device_constraint_pwrlevel {
unsigned int level;
};
#ifdef __KERNEL__
#ifdef CONFIG_MSM_KGSL_DRM
int kgsl_gem_obj_addr(int drm_fd, int handle, unsigned long *start,