aboutsummaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/msm/a5xx_reg.h1
-rw-r--r--drivers/gpu/msm/adreno.c37
-rw-r--r--drivers/gpu/msm/adreno.h28
-rw-r--r--drivers/gpu/msm/adreno_a3xx.c5
-rw-r--r--drivers/gpu/msm/adreno_a4xx.c5
-rw-r--r--drivers/gpu/msm/adreno_a5xx.c13
-rw-r--r--drivers/gpu/msm/adreno_a5xx_preempt.c2
-rw-r--r--drivers/gpu/msm/adreno_a5xx_snapshot.c3
-rw-r--r--drivers/gpu/msm/adreno_debugfs.c2
-rw-r--r--drivers/gpu/msm/adreno_dispatch.c9
-rw-r--r--drivers/gpu/msm/kgsl.c53
-rw-r--r--drivers/gpu/msm/kgsl.h18
-rw-r--r--drivers/gpu/msm/kgsl_cffdump.c7
-rw-r--r--drivers/gpu/msm/kgsl_cmdbatch.c2
-rw-r--r--drivers/gpu/msm/kgsl_debugfs.c93
-rw-r--r--drivers/gpu/msm/kgsl_device.h8
-rw-r--r--drivers/gpu/msm/kgsl_events.c10
-rw-r--r--drivers/gpu/msm/kgsl_iommu.c16
-rw-r--r--drivers/gpu/msm/kgsl_pool.c158
-rw-r--r--drivers/gpu/msm/kgsl_pool.h4
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.c408
-rw-r--r--drivers/gpu/msm/kgsl_pwrctrl.h2
-rw-r--r--drivers/gpu/msm/kgsl_pwrscale.c14
-rw-r--r--drivers/gpu/msm/kgsl_pwrscale.h4
-rw-r--r--drivers/gpu/msm/kgsl_sharedmem.c92
-rw-r--r--drivers/gpu/msm/kgsl_snapshot.c6
26 files changed, 739 insertions, 261 deletions
diff --git a/drivers/gpu/msm/a5xx_reg.h b/drivers/gpu/msm/a5xx_reg.h
index e3974f0b..f25b0182 100644
--- a/drivers/gpu/msm/a5xx_reg.h
+++ b/drivers/gpu/msm/a5xx_reg.h
@@ -640,6 +640,7 @@
/* UCHE registers */
#define A5XX_UCHE_ADDR_MODE_CNTL 0xE80
+#define A5XX_UCHE_MODE_CNTL 0xE81
#define A5XX_UCHE_WRITE_THRU_BASE_LO 0xE87
#define A5XX_UCHE_WRITE_THRU_BASE_HI 0xE88
#define A5XX_UCHE_TRAP_BASE_LO 0xE89
diff --git a/drivers/gpu/msm/adreno.c b/drivers/gpu/msm/adreno.c
index d814f675..e20e9f8d 100644
--- a/drivers/gpu/msm/adreno.c
+++ b/drivers/gpu/msm/adreno.c
@@ -579,7 +579,6 @@ void adreno_cp_callback(struct adreno_device *adreno_dev, int bit)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
- kgsl_schedule_work(&device->event_work);
adreno_dispatcher_schedule(device);
}
@@ -589,11 +588,21 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device)
struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
struct adreno_irq *irq_params = gpudev->irq;
irqreturn_t ret = IRQ_NONE;
- unsigned int status = 0, tmp;
+ unsigned int status = 0, tmp, int_bit;
int i;
adreno_readreg(adreno_dev, ADRENO_REG_RBBM_INT_0_STATUS, &status);
+ /*
+ * Clear all the interrupt bits but ADRENO_INT_RBBM_AHB_ERROR. Because
+ * even if we clear it here, it will stay high until it is cleared
+ * in its respective handler. Otherwise, the interrupt handler will
+ * fire again.
+ */
+ int_bit = ADRENO_INT_BIT(adreno_dev, ADRENO_INT_RBBM_AHB_ERROR);
+ adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD,
+ status & ~int_bit);
+
/* Loop through all set interrupts and call respective handlers */
for (tmp = status; tmp != 0;) {
i = fls(tmp) - 1;
@@ -612,9 +621,14 @@ static irqreturn_t adreno_irq_handler(struct kgsl_device *device)
gpudev->irq_trace(adreno_dev, status);
- if (status)
+ /*
+ * Clear ADRENO_INT_RBBM_AHB_ERROR bit after this interrupt has been
+ * cleared in its respective handler
+ */
+ if (status & int_bit)
adreno_writereg(adreno_dev, ADRENO_REG_RBBM_INT_CLEAR_CMD,
- status);
+ int_bit);
+
return ret;
}
@@ -2054,7 +2068,7 @@ static int adreno_setproperty(struct kgsl_device_private *dev_priv,
KGSL_STATE_ACTIVE);
device->pwrctrl.ctrl_flags = KGSL_PWR_ON;
adreno_fault_detect_stop(adreno_dev);
- kgsl_pwrscale_disable(device);
+ kgsl_pwrscale_disable(device, true);
}
mutex_unlock(&device->mutex);
@@ -2778,6 +2792,18 @@ static void adreno_regulator_disable_poll(struct kgsl_device *device)
adreno_iommu_sync(device, false);
}
+static void adreno_gpu_model(struct kgsl_device *device, char *str,
+ size_t bufsz)
+{
+ struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
+
+ snprintf(str, bufsz, "Adreno%d%d%dv%d",
+ ADRENO_CHIPID_CORE(adreno_dev->chipid),
+ ADRENO_CHIPID_MAJOR(adreno_dev->chipid),
+ ADRENO_CHIPID_MINOR(adreno_dev->chipid),
+ ADRENO_CHIPID_PATCH(adreno_dev->chipid) + 1);
+}
+
static const struct kgsl_functable adreno_functable = {
/* Mandatory functions */
.regread = adreno_regread,
@@ -2814,6 +2840,7 @@ static const struct kgsl_functable adreno_functable = {
.regulator_disable = adreno_regulator_disable,
.pwrlevel_change_settings = adreno_pwrlevel_change_settings,
.regulator_disable_poll = adreno_regulator_disable_poll,
+ .gpu_model = adreno_gpu_model,
};
static struct platform_driver adreno_platform_driver = {
diff --git a/drivers/gpu/msm/adreno.h b/drivers/gpu/msm/adreno.h
index 40c1178c..0a1629a6 100644
--- a/drivers/gpu/msm/adreno.h
+++ b/drivers/gpu/msm/adreno.h
@@ -196,6 +196,10 @@ struct adreno_gpudev;
/* Time to allow preemption to complete (in ms) */
#define ADRENO_PREEMPT_TIMEOUT 10000
+#define ADRENO_INT_BIT(a, _bit) (((a)->gpucore->gpudev->int_bits) ? \
+ (adreno_get_int(a, _bit) < 0 ? 0 : \
+ BIT(adreno_get_int(a, _bit))) : 0)
+
/**
* enum adreno_preempt_states
* ADRENO_PREEMPT_NONE: No preemption is scheduled
@@ -572,6 +576,11 @@ enum adreno_regs {
ADRENO_REG_REGISTER_MAX,
};
+enum adreno_int_bits {
+ ADRENO_INT_RBBM_AHB_ERROR,
+ ADRENO_INT_BITS_MAX,
+};
+
/**
* adreno_reg_offsets: Holds array of register offsets
* @offsets: Offset array of size defined by enum adreno_regs
@@ -587,6 +596,7 @@ struct adreno_reg_offsets {
#define ADRENO_REG_UNUSED 0xFFFFFFFF
#define ADRENO_REG_SKIP 0xFFFFFFFE
#define ADRENO_REG_DEFINE(_offset, _reg) [_offset] = _reg
+#define ADRENO_INT_DEFINE(_offset, _val) ADRENO_REG_DEFINE(_offset, _val)
/*
* struct adreno_vbif_data - Describes vbif register value pair
@@ -724,6 +734,7 @@ struct adreno_gpudev {
* so define them in the structure and use them as variables.
*/
const struct adreno_reg_offsets *reg_offsets;
+ unsigned int *const int_bits;
const struct adreno_ft_perf_counters *ft_perf_counters;
unsigned int ft_perf_counters_count;
@@ -1088,6 +1099,23 @@ static inline unsigned int adreno_getreg(struct adreno_device *adreno_dev,
return gpudev->reg_offsets->offsets[offset_name];
}
+/*
+ * adreno_get_int() - Returns the offset value of an interrupt bit from
+ * the interrupt bit array in the gpudev node
+ * @adreno_dev: Pointer to the the adreno device
+ * @bit_name: The interrupt bit enum whose bit is returned
+ */
+static inline unsigned int adreno_get_int(struct adreno_device *adreno_dev,
+ enum adreno_int_bits bit_name)
+{
+ struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
+
+ if (bit_name >= ADRENO_INT_BITS_MAX)
+ return -ERANGE;
+
+ return gpudev->int_bits[bit_name];
+}
+
/**
* adreno_gpu_fault() - Return the current state of the GPU
* @adreno_dev: A pointer to the adreno_device to query
diff --git a/drivers/gpu/msm/adreno_a3xx.c b/drivers/gpu/msm/adreno_a3xx.c
index 157f57bf..6a03a14c 100644
--- a/drivers/gpu/msm/adreno_a3xx.c
+++ b/drivers/gpu/msm/adreno_a3xx.c
@@ -1465,6 +1465,10 @@ static struct adreno_coresight a3xx_coresight = {
.groups = a3xx_coresight_groups,
};
+static unsigned int a3xx_int_bits[ADRENO_INT_BITS_MAX] = {
+ ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A3XX_INT_RBBM_AHB_ERROR),
+};
+
/* Register offset defines for A3XX */
static unsigned int a3xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, A3XX_CP_ME_RAM_WADDR),
@@ -1893,6 +1897,7 @@ int a3xx_microcode_load(struct adreno_device *adreno_dev,
struct adreno_gpudev adreno_a3xx_gpudev = {
.reg_offsets = &a3xx_reg_offsets,
+ .int_bits = a3xx_int_bits,
.ft_perf_counters = a3xx_ft_perf_counters,
.ft_perf_counters_count = ARRAY_SIZE(a3xx_ft_perf_counters),
.perfcounters = &a3xx_perfcounters,
diff --git a/drivers/gpu/msm/adreno_a4xx.c b/drivers/gpu/msm/adreno_a4xx.c
index b15d23cf..7a8236fb 100644
--- a/drivers/gpu/msm/adreno_a4xx.c
+++ b/drivers/gpu/msm/adreno_a4xx.c
@@ -727,6 +727,10 @@ static void a4xx_err_callback(struct adreno_device *adreno_dev, int bit)
}
}
+static unsigned int a4xx_int_bits[ADRENO_INT_BITS_MAX] = {
+ ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A4XX_INT_RBBM_AHB_ERROR),
+};
+
/* Register offset defines for A4XX, in order of enum adreno_regs */
static unsigned int a4xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_CP_ME_RAM_WADDR, A4XX_CP_ME_RAM_WADDR),
@@ -1753,6 +1757,7 @@ static struct adreno_snapshot_data a4xx_snapshot_data = {
struct adreno_gpudev adreno_a4xx_gpudev = {
.reg_offsets = &a4xx_reg_offsets,
+ .int_bits = a4xx_int_bits,
.ft_perf_counters = a4xx_ft_perf_counters,
.ft_perf_counters_count = ARRAY_SIZE(a4xx_ft_perf_counters),
.perfcounters = &a4xx_perfcounters,
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 9cccc24b..01bbcecc 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -1942,6 +1942,11 @@ static void a5xx_start(struct adreno_device *adreno_dev)
*/
kgsl_regrmw(device, A5XX_RB_DBG_ECO_CNT, 0, (1 << 9));
}
+ /*
+ * Disable UCHE global filter as SP can invalidate/flush
+ * independently
+ */
+ kgsl_regwrite(device, A5XX_UCHE_MODE_CNTL, BIT(29));
/* Set the USE_RETENTION_FLOPS chicken bit */
kgsl_regwrite(device, A5XX_CP_CHICKEN_DBG, 0x02000000);
@@ -2944,6 +2949,10 @@ static struct adreno_ft_perf_counters a5xx_ft_perf_counters[] = {
{KGSL_PERFCOUNTER_GROUP_TSE, A5XX_TSE_INPUT_PRIM_NUM},
};
+static unsigned int a5xx_int_bits[ADRENO_INT_BITS_MAX] = {
+ ADRENO_INT_DEFINE(ADRENO_INT_RBBM_AHB_ERROR, A5XX_INT_RBBM_AHB_ERROR),
+};
+
/* Register offset defines for A5XX, in order of enum adreno_regs */
static unsigned int a5xx_register_offsets[ADRENO_REG_REGISTER_MAX] = {
ADRENO_REG_DEFINE(ADRENO_REG_CP_WFI_PEND_CTR, A5XX_CP_WFI_PEND_CTR),
@@ -3172,7 +3181,6 @@ static void a5xx_irq_storm_worker(struct work_struct *work)
mutex_unlock(&device->mutex);
/* Reschedule just to make sure everything retires */
- kgsl_schedule_work(&device->event_work);
adreno_dispatcher_schedule(device);
}
@@ -3223,8 +3231,6 @@ static void a5xx_cp_callback(struct adreno_device *adreno_dev, int bit)
}
a5xx_preemption_trigger(adreno_dev);
-
- kgsl_schedule_work(&device->event_work);
adreno_dispatcher_schedule(device);
}
@@ -3579,6 +3585,7 @@ static struct adreno_coresight a5xx_coresight = {
struct adreno_gpudev adreno_a5xx_gpudev = {
.reg_offsets = &a5xx_reg_offsets,
+ .int_bits = a5xx_int_bits,
.ft_perf_counters = a5xx_ft_perf_counters,
.ft_perf_counters_count = ARRAY_SIZE(a5xx_ft_perf_counters),
.coresight = &a5xx_coresight,
diff --git a/drivers/gpu/msm/adreno_a5xx_preempt.c b/drivers/gpu/msm/adreno_a5xx_preempt.c
index 27c3e85a..6266d6d0 100644
--- a/drivers/gpu/msm/adreno_a5xx_preempt.c
+++ b/drivers/gpu/msm/adreno_a5xx_preempt.c
@@ -311,6 +311,8 @@ void a5xx_preempt_callback(struct adreno_device *adreno_dev, int bit)
adreno_dev->cur_rb->dispatch_q.expires);
adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE);
+
+ a5xx_preemption_trigger(adreno_dev);
}
void a5xx_preemption_schedule(struct adreno_device *adreno_dev)
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index 04d82844..2f7b4171 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -131,6 +131,9 @@ static const struct adreno_debugbus_block a5xx_debugbus_blocks[] = {
/* Width of A5XX_CP_DRAW_STATE_ADDR is 8 bits */
#define A5XX_CP_DRAW_STATE_ADDR_WIDTH 8
+/* Width of A5XX_CP_DRAW_STATE_ADDR is 8 bits */
+#define A5XX_CP_DRAW_STATE_ADDR_WIDTH 8
+
/* a5xx_snapshot_cp_pm4() - Dump PM4 data in snapshot */
static size_t a5xx_snapshot_cp_pm4(struct kgsl_device *device, u8 *buf,
size_t remain, void *priv)
diff --git a/drivers/gpu/msm/adreno_debugfs.c b/drivers/gpu/msm/adreno_debugfs.c
index 9cbcd06d..680827e5 100644
--- a/drivers/gpu/msm/adreno_debugfs.c
+++ b/drivers/gpu/msm/adreno_debugfs.c
@@ -138,7 +138,7 @@ static void sync_event_print(struct seq_file *s,
break;
}
case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
- seq_printf(s, "sync: [%p] %s", sync_event->handle,
+ seq_printf(s, "sync: [%pK] %s", sync_event->handle,
(sync_event->handle && sync_event->handle->fence)
? sync_event->handle->fence->name : "NULL");
break;
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index bfc547fe..522c3274 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -1814,7 +1814,7 @@ static int dispatcher_do_fault(struct adreno_device *adreno_dev)
}
}
- if (!adreno_cmdqueue_is_empty(dispatch_q)) {
+ if (dispatch_q && !adreno_cmdqueue_is_empty(dispatch_q)) {
cmdbatch = dispatch_q->cmd_q[dispatch_q->head];
trace_adreno_cmdbatch_fault(cmdbatch, fault);
}
@@ -2123,19 +2123,18 @@ static void adreno_dispatcher_work(struct work_struct *work)
break;
}
+ kgsl_process_event_groups(device);
+
/*
* dispatcher_do_fault() returns 0 if no faults occurred. If that is the
* case, then clean up preemption and try to schedule more work
*/
if (dispatcher_do_fault(adreno_dev) == 0) {
+
/* Clean up after preemption */
if (gpudev->preemption_schedule)
gpudev->preemption_schedule(adreno_dev);
- /* Re-kick the event engine to catch stragglers */
- if (dispatcher->inflight == 0 && count != 0)
- kgsl_schedule_work(&device->event_work);
-
/* Run the scheduler for to dispatch new commands */
_adreno_dispatcher_issuecmds(adreno_dev);
}
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index f6f1e4a0..136eafe5 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -2434,6 +2434,8 @@ static int kgsl_setup_dma_buf(struct kgsl_device *device,
meta->dmabuf = dmabuf;
meta->attach = attach;
+ attach->priv = entry;
+
entry->priv_data = meta;
entry->memdesc.pagetable = pagetable;
entry->memdesc.size = 0;
@@ -2484,6 +2486,45 @@ out:
}
#endif
+#ifdef CONFIG_DMA_SHARED_BUFFER
+void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
+ int *egl_surface_count, int *egl_image_count)
+{
+ struct kgsl_dma_buf_meta *meta = entry->priv_data;
+ struct dma_buf *dmabuf = meta->dmabuf;
+ struct dma_buf_attachment *mem_entry_buf_attachment = meta->attach;
+ struct device *buf_attachment_dev = mem_entry_buf_attachment->dev;
+ struct dma_buf_attachment *attachment = NULL;
+
+ mutex_lock(&dmabuf->lock);
+ list_for_each_entry(attachment, &dmabuf->attachments, node) {
+ struct kgsl_mem_entry *scan_mem_entry = NULL;
+
+ if (attachment->dev != buf_attachment_dev)
+ continue;
+
+ scan_mem_entry = attachment->priv;
+ if (!scan_mem_entry)
+ continue;
+
+ switch (kgsl_memdesc_get_memtype(&scan_mem_entry->memdesc)) {
+ case KGSL_MEMTYPE_EGL_SURFACE:
+ (*egl_surface_count)++;
+ break;
+ case KGSL_MEMTYPE_EGL_IMAGE:
+ (*egl_image_count)++;
+ break;
+ }
+ }
+ mutex_unlock(&dmabuf->lock);
+}
+#else
+void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
+ int *egl_surface_count, int *egl_image_count)
+{
+}
+#endif
+
long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
unsigned int cmd, void *data)
{
@@ -3890,9 +3931,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
disable_irq(device->pwrctrl.interrupt_num);
KGSL_DRV_INFO(device,
- "dev_id %d regs phys 0x%08lx size 0x%08x virt %p\n",
- device->id, device->reg_phys, device->reg_len,
- device->reg_virt);
+ "dev_id %d regs phys 0x%08lx size 0x%08x\n",
+ device->id, device->reg_phys, device->reg_len);
rwlock_init(&device->context_lock);
@@ -3952,8 +3992,8 @@ int kgsl_device_platform_probe(struct kgsl_device *device)
PM_QOS_DEFAULT_VALUE);
}
-
- device->events_wq = create_singlethread_workqueue("kgsl-events");
+ device->events_wq = alloc_workqueue("kgsl-events",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
/* Initalize the snapshot engine */
kgsl_device_snapshot_init(device);
@@ -4096,7 +4136,8 @@ static int __init kgsl_core_init(void)
INIT_LIST_HEAD(&kgsl_driver.pagetable_list);
- kgsl_driver.workqueue = create_singlethread_workqueue("kgsl-workqueue");
+ kgsl_driver.workqueue = alloc_workqueue("kgsl-workqueue",
+ WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_SYSFS, 0);
kgsl_driver.mem_workqueue = alloc_workqueue("kgsl-mementry",
WQ_UNBOUND | WQ_MEM_RECLAIM, 0);
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index ee7149e1..367a5b79 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -379,6 +379,9 @@ long kgsl_ioctl_gpuobj_set_info(struct kgsl_device_private *dev_priv,
void kgsl_mem_entry_destroy(struct kref *kref);
+void kgsl_get_egl_counts(struct kgsl_mem_entry *entry,
+ int *egl_surface_count, int *egl_image_count);
+
struct kgsl_mem_entry * __must_check
kgsl_sharedmem_find(struct kgsl_process_private *private, uint64_t gpuaddr);
@@ -542,4 +545,19 @@ static inline void __user *to_user_ptr(uint64_t address)
return (void __user *)(uintptr_t)address;
}
+static inline void kgsl_gpu_sysfs_add_link(struct kobject *dst,
+ struct kobject *src, const char *src_name,
+ const char *dst_name)
+{
+ struct kernfs_node *old;
+
+ if (dst == NULL || src == NULL)
+ return;
+
+ old = sysfs_get_dirent(src->sd, src_name);
+ if (IS_ERR_OR_NULL(old))
+ return;
+
+ kernfs_create_link(dst->sd, dst_name, old);
+}
#endif /* __KGSL_H */
diff --git a/drivers/gpu/msm/kgsl_cffdump.c b/drivers/gpu/msm/kgsl_cffdump.c
index 2e90f78a..8e783f8c 100644
--- a/drivers/gpu/msm/kgsl_cffdump.c
+++ b/drivers/gpu/msm/kgsl_cffdump.c
@@ -513,10 +513,6 @@ EXPORT_SYMBOL(kgsl_cffdump_waitirq);
static int subbuf_start_handler(struct rchan_buf *buf,
void *subbuf, void *prev_subbuf, size_t prev_padding)
{
- pr_debug("kgsl: cffdump: subbuf_start_handler(subbuf=%p, prev_subbuf"
- "=%p, prev_padding=%08zx)\n", subbuf, prev_subbuf,
- prev_padding);
-
if (relay_buf_full(buf)) {
if (!suspended) {
suspended = 1;
@@ -573,9 +569,6 @@ static struct rchan *create_channel(unsigned subbuf_size, unsigned n_subbufs)
{
struct rchan *chan;
- pr_info("kgsl: cffdump: relay: create_channel: subbuf_size %u, "
- "n_subbufs %u, dir 0x%p\n", subbuf_size, n_subbufs, dir);
-
chan = relay_open("cpu", dir, subbuf_size,
n_subbufs, &relay_callbacks, NULL);
if (!chan) {
diff --git a/drivers/gpu/msm/kgsl_cmdbatch.c b/drivers/gpu/msm/kgsl_cmdbatch.c
index ceca8b1e..6272410c 100644
--- a/drivers/gpu/msm/kgsl_cmdbatch.c
+++ b/drivers/gpu/msm/kgsl_cmdbatch.c
@@ -80,7 +80,7 @@ void kgsl_dump_syncpoints(struct kgsl_device *device,
}
case KGSL_CMD_SYNCPOINT_TYPE_FENCE:
if (event->handle)
- dev_err(device->dev, " fence: [%p] %s\n",
+ dev_err(device->dev, " fence: [%pK] %s\n",
event->handle->fence,
event->handle->name);
else
diff --git a/drivers/gpu/msm/kgsl_debugfs.c b/drivers/gpu/msm/kgsl_debugfs.c
index 93ac790f..4fada8ed 100644
--- a/drivers/gpu/msm/kgsl_debugfs.c
+++ b/drivers/gpu/msm/kgsl_debugfs.c
@@ -125,13 +125,15 @@ static char get_cacheflag(const struct kgsl_memdesc *m)
}
-static int print_mem_entry(int id, void *ptr, void *data)
+static int print_mem_entry(void *data, void *ptr)
{
struct seq_file *s = data;
struct kgsl_mem_entry *entry = ptr;
char flags[9];
char usage[16];
struct kgsl_memdesc *m = &entry->memdesc;
+ unsigned int usermem_type = kgsl_memdesc_usermem_type(m);
+ int egl_surface_count = 0, egl_image_count = 0;
flags[0] = kgsl_memdesc_is_global(m) ? 'g' : '-';
flags[1] = '-';
@@ -145,12 +147,17 @@ static int print_mem_entry(int id, void *ptr, void *data)
kgsl_get_memory_usage(usage, sizeof(usage), m->flags);
- seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu",
+ if (usermem_type == KGSL_MEM_ENTRY_ION)
+ kgsl_get_egl_counts(entry, &egl_surface_count,
+ &egl_image_count);
+
+ seq_printf(s, "%pK %pK %16llu %5d %9s %10s %16s %5d %16llu %6d %6d",
(uint64_t *)(uintptr_t) m->gpuaddr,
(unsigned long *) m->useraddr,
m->size, entry->id, flags,
- memtype_str(kgsl_memdesc_usermem_type(m)),
- usage, (m->sgt ? m->sgt->nents : 0), m->mapsize);
+ memtype_str(usermem_type),
+ usage, (m->sgt ? m->sgt->nents : 0), m->mapsize,
+ egl_surface_count, egl_image_count);
if (entry->metadata[0] != 0)
seq_printf(s, " %s", entry->metadata);
@@ -160,25 +167,83 @@ static int print_mem_entry(int id, void *ptr, void *data)
return 0;
}
-static int process_mem_print(struct seq_file *s, void *unused)
+static struct kgsl_mem_entry *process_mem_seq_find(struct seq_file *s,
+ void *ptr, loff_t pos)
{
+ struct kgsl_mem_entry *entry = ptr;
struct kgsl_process_private *private = s->private;
+ int id = 0;
+ loff_t temp_pos = 1;
- seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s\n",
- "gpuaddr", "useraddr", "size", "id", "flags", "type",
- "usage", "sglen", "mapsize");
+ if (entry != SEQ_START_TOKEN)
+ id = entry->id + 1;
spin_lock(&private->mem_lock);
- idr_for_each(&private->mem_idr, print_mem_entry, s);
+ for (entry = idr_get_next(&private->mem_idr, &id); entry;
+ id++, entry = idr_get_next(&private->mem_idr, &id),
+ temp_pos++) {
+ if (temp_pos == pos && kgsl_mem_entry_get(entry)) {
+ spin_unlock(&private->mem_lock);
+ goto found;
+ }
+ }
spin_unlock(&private->mem_lock);
- return 0;
+ entry = NULL;
+found:
+ if (ptr != SEQ_START_TOKEN)
+ kgsl_mem_entry_put(ptr);
+
+ return entry;
}
+static void *process_mem_seq_start(struct seq_file *s, loff_t *pos)
+{
+ loff_t seq_file_offset = *pos;
+
+ if (seq_file_offset == 0)
+ return SEQ_START_TOKEN;
+ else
+ return process_mem_seq_find(s, SEQ_START_TOKEN,
+ seq_file_offset);
+}
+
+static void process_mem_seq_stop(struct seq_file *s, void *ptr)
+{
+ if (ptr && ptr != SEQ_START_TOKEN)
+ kgsl_mem_entry_put(ptr);
+}
+
+static void *process_mem_seq_next(struct seq_file *s, void *ptr,
+ loff_t *pos)
+{
+ ++*pos;
+ return process_mem_seq_find(s, ptr, 1);
+}
+
+static int process_mem_seq_show(struct seq_file *s, void *ptr)
+{
+ if (ptr == SEQ_START_TOKEN) {
+ seq_printf(s, "%16s %16s %16s %5s %9s %10s %16s %5s %16s %6s %6s\n",
+ "gpuaddr", "useraddr", "size", "id", "flags", "type",
+ "usage", "sglen", "mapsize", "eglsrf", "eglimg");
+ return 0;
+ } else
+ return print_mem_entry(s, ptr);
+}
+
+static const struct seq_operations process_mem_seq_fops = {
+ .start = process_mem_seq_start,
+ .stop = process_mem_seq_stop,
+ .next = process_mem_seq_next,
+ .show = process_mem_seq_show,
+};
+
static int process_mem_open(struct inode *inode, struct file *file)
{
int ret;
pid_t pid = (pid_t) (unsigned long) inode->i_private;
+ struct seq_file *s = NULL;
struct kgsl_process_private *private = NULL;
private = kgsl_process_private_find(pid);
@@ -186,9 +251,13 @@ static int process_mem_open(struct inode *inode, struct file *file)
if (!private)
return -ENODEV;
- ret = single_open(file, process_mem_print, private);
+ ret = seq_open(file, &process_mem_seq_fops);
if (ret)
kgsl_process_private_put(private);
+ else {
+ s = file->private_data;
+ s->private = private;
+ }
return ret;
}
@@ -201,7 +270,7 @@ static int process_mem_release(struct inode *inode, struct file *file)
if (private)
kgsl_process_private_put(private);
- return single_release(inode, file);
+ return seq_release(inode, file);
}
static const struct file_operations process_mem_fops = {
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index f864a947..6240015f 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -169,6 +169,8 @@ struct kgsl_functable {
void (*pwrlevel_change_settings)(struct kgsl_device *device,
unsigned int prelevel, unsigned int postlevel, bool post);
void (*regulator_disable_poll)(struct kgsl_device *device);
+ void (*gpu_model)(struct kgsl_device *device, char *str,
+ size_t bufsz);
};
struct kgsl_ioctl {
@@ -275,7 +277,6 @@ struct kgsl_device {
int mem_log;
int pwr_log;
struct kgsl_pwrscale pwrscale;
- struct work_struct event_work;
int reset_counter; /* Track how many GPU core resets have occured */
int cff_dump_enable;
@@ -285,6 +286,7 @@ struct kgsl_device {
/* Number of active contexts seen globally for this device */
int active_context_count;
+ struct kobject *gpu_sysfs_kobj;
};
#define KGSL_MMU_DEVICE(_mmu) \
@@ -295,8 +297,6 @@ struct kgsl_device {
.cmdbatch_gate = COMPLETION_INITIALIZER((_dev).cmdbatch_gate),\
.idle_check_ws = __WORK_INITIALIZER((_dev).idle_check_ws,\
kgsl_idle_check),\
- .event_work = __WORK_INITIALIZER((_dev).event_work,\
- kgsl_process_events),\
.context_idr = IDR_INIT((_dev).context_idr),\
.wait_queue = __WAIT_QUEUE_HEAD_INITIALIZER((_dev).wait_queue),\
.active_cnt_wq = __WAIT_QUEUE_HEAD_INITIALIZER((_dev).active_cnt_wq),\
@@ -617,7 +617,7 @@ void kgsl_process_event_group(struct kgsl_device *device,
struct kgsl_event_group *group);
void kgsl_flush_event_group(struct kgsl_device *device,
struct kgsl_event_group *group);
-void kgsl_process_events(struct work_struct *work);
+void kgsl_process_event_groups(struct kgsl_device *device);
void kgsl_context_destroy(struct kref *kref);
diff --git a/drivers/gpu/msm/kgsl_events.c b/drivers/gpu/msm/kgsl_events.c
index 6f70b9dd..6e8abf36 100644
--- a/drivers/gpu/msm/kgsl_events.c
+++ b/drivers/gpu/msm/kgsl_events.c
@@ -314,22 +314,16 @@ EXPORT_SYMBOL(kgsl_add_event);
static DEFINE_RWLOCK(group_lock);
static LIST_HEAD(group_list);
-/**
- * kgsl_process_events() - Work queue for processing new timestamp events
- * @work: Pointer to a work_struct
- */
-void kgsl_process_events(struct work_struct *work)
+void kgsl_process_event_groups(struct kgsl_device *device)
{
struct kgsl_event_group *group;
- struct kgsl_device *device = container_of(work, struct kgsl_device,
- event_work);
read_lock(&group_lock);
list_for_each_entry(group, &group_list, group)
_process_event_group(device, group, false);
read_unlock(&group_lock);
}
-EXPORT_SYMBOL(kgsl_process_events);
+EXPORT_SYMBOL(kgsl_process_event_groups);
/**
* kgsl_del_event_group() - Remove a GPU event group
diff --git a/drivers/gpu/msm/kgsl_iommu.c b/drivers/gpu/msm/kgsl_iommu.c
index b467ef81..fb25c2ae 100644
--- a/drivers/gpu/msm/kgsl_iommu.c
+++ b/drivers/gpu/msm/kgsl_iommu.c
@@ -323,8 +323,8 @@ static int _iommu_map_sync_pc(struct kgsl_pagetable *pt,
_unlock_if_secure_mmu(memdesc, pt->mmu);
if (ret) {
- KGSL_CORE_ERR("map err: %p, 0x%016llX, 0x%llx, 0x%x, %d\n",
- iommu_pt->domain, gpuaddr, size, flags, ret);
+ KGSL_CORE_ERR("map err: 0x%016llX, 0x%llx, 0x%x, %d\n",
+ gpuaddr, size, flags, ret);
return -ENODEV;
}
@@ -351,8 +351,8 @@ static int _iommu_unmap_sync_pc(struct kgsl_pagetable *pt,
_unlock_if_secure_mmu(memdesc, pt->mmu);
if (unmapped != size) {
- KGSL_CORE_ERR("unmap err: %p, 0x%016llx, 0x%llx, %zd\n",
- iommu_pt->domain, addr, size, unmapped);
+ KGSL_CORE_ERR("unmap err: 0x%016llx, 0x%llx, %zd\n",
+ addr, size, unmapped);
return -ENODEV;
}
@@ -421,8 +421,8 @@ static int _iommu_map_sg_offset_sync_pc(struct kgsl_pagetable *pt,
if (size != 0) {
/* Cleanup on error */
_iommu_unmap_sync_pc(pt, memdesc, addr, mapped);
- KGSL_CORE_ERR("map err: %p, 0x%016llX, %d, %x, %zd\n",
- iommu_pt->domain, addr, nents, flags, mapped);
+ KGSL_CORE_ERR("map err: 0x%016llX, %d, %x, %zd\n",
+ addr, nents, flags, mapped);
return -ENODEV;
}
@@ -451,8 +451,8 @@ static int _iommu_map_sg_sync_pc(struct kgsl_pagetable *pt,
_unlock_if_secure_mmu(memdesc, pt->mmu);
if (mapped == 0) {
- KGSL_CORE_ERR("map err: %p, 0x%016llX, %d, %x, %zd\n",
- iommu_pt->domain, addr, nents, flags, mapped);
+ KGSL_CORE_ERR("map err: 0x%016llX, %d, %x, %zd\n",
+ addr, nents, flags, mapped);
return -ENODEV;
}
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index f5402fdc..214bd50a 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -21,21 +21,18 @@
#include "kgsl_device.h"
#include "kgsl_pool.h"
-/**
- * struct kgsl_page_pool - Structure to hold information for the pool
- * @pool_order: Page order describing the size of the page
- * @page_count: Number of pages currently present in the pool
- * @reserved_pages: Number of pages reserved at init for the pool
- * @allocation_allowed: Tells if reserved pool gets exhausted, can we allocate
- * from system memory
- * @list_lock: Spinlock for page list in the pool
- * @page_list: List of pages held/reserved in this pool
+/*
+ * Maximum pool size in terms of pages
+ * = (Number of pools * Max size per pool)
*/
+#define KGSL_POOL_MAX_PAGES (2 * 4096)
+
+/* Set the max pool size to 8192 pages */
+static unsigned int kgsl_pool_max_pages = KGSL_POOL_MAX_PAGES;
+
struct kgsl_page_pool {
unsigned int pool_order;
int page_count;
- unsigned int reserved_pages;
- bool allocation_allowed;
spinlock_t list_lock;
struct list_head page_list;
};
@@ -43,34 +40,15 @@ struct kgsl_page_pool {
static struct kgsl_page_pool kgsl_pools[] = {
{
.pool_order = 0,
- .reserved_pages = 2048,
- .allocation_allowed = true,
.list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[0].list_lock),
.page_list = LIST_HEAD_INIT(kgsl_pools[0].page_list),
},
#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
{
- .pool_order = 1,
- .reserved_pages = 1024,
- .allocation_allowed = true,
+ .pool_order = 4,
.list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[1].list_lock),
.page_list = LIST_HEAD_INIT(kgsl_pools[1].page_list),
},
- {
- .pool_order = 4,
- .reserved_pages = 256,
- .allocation_allowed = false,
- .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[2].list_lock),
- .page_list = LIST_HEAD_INIT(kgsl_pools[2].page_list),
- },
- {
- .pool_order = 8,
- .reserved_pages = 32,
- .allocation_allowed = false,
- .list_lock = __SPIN_LOCK_UNLOCKED(kgsl_pools[3].list_lock),
- .page_list = LIST_HEAD_INIT(kgsl_pools[3].page_list),
- },
-
#endif
};
@@ -90,28 +68,10 @@ _kgsl_get_pool_from_order(unsigned int order)
return NULL;
}
-/* Map the page into kernel and zero it out */
-static void
-_kgsl_pool_zero_page(struct page *p, unsigned int pool_order)
-{
- int i;
-
- for (i = 0; i < (1 << pool_order); i++) {
- struct page *page = nth_page(p, i);
- void *addr = kmap_atomic(page);
-
- memset(addr, 0, PAGE_SIZE);
- dmac_flush_range(addr, addr + PAGE_SIZE);
- kunmap_atomic(addr);
- }
-}
-
/* Add a page to specified pool */
static void
_kgsl_pool_add_page(struct kgsl_page_pool *pool, struct page *p)
{
- _kgsl_pool_zero_page(p, pool->pool_order);
-
spin_lock(&pool->list_lock);
list_add_tail(&p->lru, &pool->page_list);
pool->page_count++;
@@ -196,28 +156,20 @@ _kgsl_pool_shrink(struct kgsl_page_pool *pool, int num_pages)
* (current_pool_size - target_pages) pages from pool
* starting from higher order pool.
*/
-static unsigned long
-kgsl_pool_reduce(unsigned int target_pages, bool exit)
+static int
+kgsl_pool_reduce(unsigned int target_pages)
{
int total_pages = 0;
int i;
int nr_removed;
struct kgsl_page_pool *pool;
- unsigned long pcount = 0;
+ unsigned int pcount = 0;
total_pages = kgsl_pool_size_total();
for (i = (KGSL_NUM_POOLS - 1); i >= 0; i--) {
pool = &kgsl_pools[i];
- /*
- * Only reduce the pool sizes for pools which are allowed to
- * allocate memory unless we are at close, in which case the
- * reserved memory for all pools needs to be freed
- */
- if (!pool->allocation_allowed && !exit)
- continue;
-
total_pages -= pcount;
nr_removed = total_pages - target_pages;
@@ -296,16 +248,6 @@ void kgsl_pool_free_pages(struct page **pages, unsigned int pcount)
kgsl_pool_free_page(p);
}
}
-static int kgsl_pool_idx_lookup(unsigned int order)
-{
- int i;
-
- for (i = 0; i < KGSL_NUM_POOLS; i++)
- if (order == kgsl_pools[i].pool_order)
- return i;
-
- return -ENOMEM;
-}
/**
* kgsl_pool_alloc_page() - Allocate a page of requested size
@@ -316,56 +258,37 @@ static int kgsl_pool_idx_lookup(unsigned int order)
*
* Return total page count on success and negative value on failure
*/
-int kgsl_pool_alloc_page(int *page_size, struct page **pages,
- unsigned int pages_len, unsigned int *align)
+int kgsl_pool_alloc_page(int page_size, struct page **pages,
+ unsigned int pages_len)
{
int j;
int pcount = 0;
struct kgsl_page_pool *pool;
struct page *page = NULL;
struct page *p = NULL;
- int order = get_order(*page_size);
- int pool_idx;
- if ((pages == NULL) || pages_len < (*page_size >> PAGE_SHIFT))
+ if ((pages == NULL) || pages_len < (page_size >> PAGE_SHIFT))
return -EINVAL;
- pool = _kgsl_get_pool_from_order(order);
+ pool = _kgsl_get_pool_from_order(get_order(page_size));
+
if (pool == NULL)
return -EINVAL;
- pool_idx = kgsl_pool_idx_lookup(order);
page = _kgsl_pool_get_page(pool);
/* Allocate a new page if not allocated from pool */
if (page == NULL) {
- gfp_t gfp_mask = kgsl_gfp_mask(order);
-
- /* Only allocate non-reserved memory for certain pools */
- if (!pool->allocation_allowed && pool_idx > 0) {
- *page_size = PAGE_SIZE <<
- kgsl_pools[pool_idx-1].pool_order;
- *align = ilog2(*page_size);
- return -EAGAIN;
- }
+ gfp_t gfp_mask = kgsl_gfp_mask(get_order(page_size));
- page = alloc_pages(gfp_mask, order);
-
- if (!page) {
- if (pool_idx > 0) {
- /* Retry with lower order pages */
- *page_size = PAGE_SIZE <<
- kgsl_pools[pool_idx-1].pool_order;
- *align = ilog2(*page_size);
- return -EAGAIN;
- } else
- return -ENOMEM;
- }
+ page = alloc_pages(gfp_mask,
+ get_order(page_size));
- _kgsl_pool_zero_page(page, order);
+ if (!page)
+ return -ENOMEM;
}
- for (j = 0; j < (*page_size >> PAGE_SHIFT); j++) {
+ for (j = 0; j < (page_size >> PAGE_SHIFT); j++) {
p = nth_page(page, j);
pages[pcount] = p;
pcount++;
@@ -384,34 +307,18 @@ void kgsl_pool_free_page(struct page *page)
page_order = compound_order(page);
- pool = _kgsl_get_pool_from_order(page_order);
- if (pool != NULL) {
- _kgsl_pool_add_page(pool, page);
- return;
+ if (kgsl_pool_size_total() < kgsl_pool_max_pages) {
+ pool = _kgsl_get_pool_from_order(page_order);
+ if (pool != NULL) {
+ _kgsl_pool_add_page(pool, page);
+ return;
+ }
}
/* Give back to system as not added to pool */
__free_pages(page, page_order);
}
-static void kgsl_pool_reserve_pages(void)
-{
- int i, j;
-
- for (i = 0; i < KGSL_NUM_POOLS; i++) {
- struct page *page;
-
- for (j = 0; j < kgsl_pools[i].reserved_pages; j++) {
- int order = kgsl_pools[i].pool_order;
- gfp_t gfp_mask = kgsl_gfp_mask(order);
-
- page = alloc_pages(gfp_mask, order);
- if (page != NULL)
- _kgsl_pool_add_page(&kgsl_pools[i], page);
- }
- }
-}
-
/* Functions for the shrinker */
static unsigned long
@@ -426,7 +333,7 @@ kgsl_pool_shrink_scan_objects(struct shrinker *shrinker,
int target_pages = (nr > total_pages) ? 0 : (total_pages - nr);
/* Reduce pool size to target_pages */
- return kgsl_pool_reduce(target_pages, false);
+ return kgsl_pool_reduce(target_pages);
}
static unsigned long
@@ -447,9 +354,6 @@ static struct shrinker kgsl_pool_shrinker = {
void kgsl_init_page_pools(void)
{
- /* Reserve the appropriate number of pages for each pool */
- kgsl_pool_reserve_pages();
-
/* Initialize shrinker */
register_shrinker(&kgsl_pool_shrinker);
}
@@ -457,7 +361,7 @@ void kgsl_init_page_pools(void)
void kgsl_exit_page_pools(void)
{
/* Release all pages in pools, if any.*/
- kgsl_pool_reduce(0, true);
+ kgsl_pool_reduce(0);
/* Unregister shrinker */
unregister_shrinker(&kgsl_pool_shrinker);
diff --git a/drivers/gpu/msm/kgsl_pool.h b/drivers/gpu/msm/kgsl_pool.h
index 198e4fde..34ddbefa 100644
--- a/drivers/gpu/msm/kgsl_pool.h
+++ b/drivers/gpu/msm/kgsl_pool.h
@@ -37,8 +37,8 @@ void kgsl_pool_free_sgt(struct sg_table *sgt);
void kgsl_pool_free_pages(struct page **pages, unsigned int page_count);
void kgsl_init_page_pools(void);
void kgsl_exit_page_pools(void);
-int kgsl_pool_alloc_page(int *page_size, struct page **pages,
- unsigned int pages_len, unsigned int *align);
+int kgsl_pool_alloc_page(int page_size, struct page **pages,
+ unsigned int pages_len);
void kgsl_pool_free_page(struct page *p);
#endif /* __KGSL_POOL_H */
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index 2eb213db..ccb37f99 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -21,11 +21,13 @@
#include <linux/delay.h>
#include <linux/msm_adreno_devfreq.h>
#include <linux/of_device.h>
+#include <linux/thermal.h>
#include "kgsl.h"
#include "kgsl_pwrscale.h"
#include "kgsl_device.h"
#include "kgsl_trace.h"
+#include "adreno.h"
#include <soc/qcom/devfreq_devbw.h>
#define KGSL_PWRFLAGS_POWER_ON 0
@@ -70,6 +72,7 @@ static const char * const clocks[] = {
static unsigned int ib_votes[KGSL_MAX_BUSLEVELS];
static int last_vote_buslevel;
static int max_vote_buslevel;
+static int limit_pwrlevel;
static void kgsl_pwrctrl_clk(struct kgsl_device *device, int state,
int requested_state);
@@ -574,26 +577,14 @@ static ssize_t kgsl_pwrctrl_max_pwrlevel_show(struct device *dev,
return snprintf(buf, PAGE_SIZE, "%u\n", pwr->max_pwrlevel);
}
-static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{ struct kgsl_device *device = kgsl_device_from_dev(dev);
- struct kgsl_pwrctrl *pwr;
- int ret;
- unsigned int level = 0;
-
- if (device == NULL)
- return 0;
-
- pwr = &device->pwrctrl;
-
- ret = kgsl_sysfs_store(buf, &level);
- if (ret)
- return ret;
+static void kgsl_pwrctrl_min_pwrlevel_set(struct kgsl_device *device,
+ int level)
+{
+ struct kgsl_pwrctrl *pwr = &device->pwrctrl;
mutex_lock(&device->mutex);
- if (level > pwr->num_pwrlevels - 2)
- level = pwr->num_pwrlevels - 2;
+ if (level > (pwr->num_pwrlevels - 2 - limit_pwrlevel))
+ level = pwr->num_pwrlevels - 2 - limit_pwrlevel;
/* You can't set a minimum power level lower than the maximum */
if (level < pwr->max_pwrlevel)
@@ -605,6 +596,24 @@ static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
kgsl_pwrctrl_pwrlevel_change(device, pwr->active_pwrlevel);
mutex_unlock(&device->mutex);
+}
+
+static ssize_t kgsl_pwrctrl_min_pwrlevel_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int ret;
+ unsigned int level = 0;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &level);
+ if (ret)
+ return ret;
+
+ kgsl_pwrctrl_min_pwrlevel_set(device, level);
return count;
}
@@ -648,24 +657,13 @@ static int _get_nearest_pwrlevel(struct kgsl_pwrctrl *pwr, unsigned int clock)
return -ERANGE;
}
-static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+static void kgsl_pwrctrl_max_clock_set(struct kgsl_device *device, int val)
{
- struct kgsl_device *device = kgsl_device_from_dev(dev);
struct kgsl_pwrctrl *pwr;
- unsigned int val = 0;
- int level, ret;
-
- if (device == NULL)
- return 0;
+ int level;
pwr = &device->pwrctrl;
- ret = kgsl_sysfs_store(buf, &val);
- if (ret)
- return ret;
-
mutex_lock(&device->mutex);
level = _get_nearest_pwrlevel(pwr, val);
/* If the requested power level is not supported by hw, try cycling */
@@ -699,21 +697,37 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
if (pwr->sysfs_pwr_limit)
kgsl_pwr_limits_set_freq(pwr->sysfs_pwr_limit,
pwr->pwrlevels[level].gpu_freq);
- return count;
+ return;
err:
mutex_unlock(&device->mutex);
- return count;
}
-static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+static ssize_t kgsl_pwrctrl_max_gpuclk_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
{
-
struct kgsl_device *device = kgsl_device_from_dev(dev);
+ unsigned int val = 0;
+ int ret;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &val);
+ if (ret)
+ return ret;
+
+ kgsl_pwrctrl_max_clock_set(device, val);
+
+ return count;
+}
+
+static unsigned int kgsl_pwrctrl_max_clock_get(struct kgsl_device *device)
+{
struct kgsl_pwrctrl *pwr;
unsigned int freq;
+
if (device == NULL)
return 0;
pwr = &device->pwrctrl;
@@ -727,7 +741,17 @@ static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
(TH_HZ - pwr->thermal_timeout) * (hfreq / TH_HZ);
}
- return snprintf(buf, PAGE_SIZE, "%d\n", freq);
+ return freq;
+}
+
+static ssize_t kgsl_pwrctrl_max_gpuclk_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ kgsl_pwrctrl_max_clock_get(device));
}
static ssize_t kgsl_pwrctrl_gpuclk_store(struct device *dev,
@@ -910,9 +934,14 @@ static ssize_t kgsl_pwrctrl_gpu_available_frequencies_show(
if (device == NULL)
return 0;
pwr = &device->pwrctrl;
- for (index = 0; index < pwr->num_pwrlevels - 1; index++)
- num_chars += snprintf(buf + num_chars, PAGE_SIZE, "%d ",
- pwr->pwrlevels[index].gpu_freq);
+ for (index = 0; index < pwr->num_pwrlevels - 1; index++) {
+ num_chars += scnprintf(buf + num_chars,
+ PAGE_SIZE - num_chars - 1,
+ "%d ", pwr->pwrlevels[index].gpu_freq);
+ /* One space for trailing null and another for the newline */
+ if (num_chars >= PAGE_SIZE - 2)
+ break;
+ }
buf[num_chars++] = '\n';
return num_chars;
}
@@ -1181,6 +1210,237 @@ static ssize_t kgsl_popp_show(struct device *dev,
test_bit(POPP_ON, &device->pwrscale.popp_state));
}
+static ssize_t kgsl_pwrctrl_pwrscale_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int ret;
+ unsigned int enable = 0;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &enable);
+ if (ret)
+ return ret;
+
+ mutex_lock(&device->mutex);
+
+ if (enable)
+ kgsl_pwrscale_enable(device);
+ else
+ kgsl_pwrscale_disable(device, false);
+
+ mutex_unlock(&device->mutex);
+
+ return count;
+}
+
+static ssize_t kgsl_pwrctrl_pwrscale_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrscale *psc;
+
+ if (device == NULL)
+ return 0;
+ psc = &device->pwrscale;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", psc->enabled);
+}
+
+static ssize_t kgsl_pwrctrl_gpu_model_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ char model_str[32] = {0};
+
+ if (device == NULL)
+ return 0;
+
+ device->ftbl->gpu_model(device, model_str, sizeof(model_str));
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", model_str);
+}
+
+static ssize_t kgsl_pwrctrl_gpu_busy_percentage_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int ret;
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_clk_stats *stats;
+ unsigned int busy_percent = 0;
+
+ if (device == NULL)
+ return 0;
+ stats = &device->pwrctrl.clk_stats;
+
+ if (stats->total_old != 0)
+ busy_percent = (stats->busy_old * 100) / stats->total_old;
+
+ ret = snprintf(buf, PAGE_SIZE, "%d %%\n", busy_percent);
+
+ /* Reset the stats if GPU is OFF */
+ if (!test_bit(KGSL_PWRFLAGS_AXI_ON, &device->pwrctrl.power_flags)) {
+ stats->busy_old = 0;
+ stats->total_old = 0;
+ }
+ return ret;
+}
+
+static ssize_t kgsl_pwrctrl_min_clock_mhz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+
+ if (device == NULL)
+ return 0;
+ pwr = &device->pwrctrl;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ pwr->pwrlevels[pwr->min_pwrlevel].gpu_freq / 1000000);
+}
+
+static ssize_t kgsl_pwrctrl_min_clock_mhz_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ int level, ret;
+ unsigned int freq;
+ struct kgsl_pwrctrl *pwr;
+
+ if (device == NULL)
+ return 0;
+
+ pwr = &device->pwrctrl;
+
+ ret = kgsl_sysfs_store(buf, &freq);
+ if (ret)
+ return ret;
+
+ freq *= 1000000;
+ level = _get_nearest_pwrlevel(pwr, freq);
+
+ if (level >= 0)
+ kgsl_pwrctrl_min_pwrlevel_set(device, level);
+
+ return count;
+}
+
+static ssize_t kgsl_pwrctrl_max_clock_mhz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ unsigned int freq;
+
+ if (device == NULL)
+ return 0;
+
+ freq = kgsl_pwrctrl_max_clock_get(device);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", freq / 1000000);
+}
+
+static ssize_t kgsl_pwrctrl_max_clock_mhz_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ unsigned int val = 0;
+ int ret;
+
+ if (device == NULL)
+ return 0;
+
+ ret = kgsl_sysfs_store(buf, &val);
+ if (ret)
+ return ret;
+
+ val *= 1000000;
+ kgsl_pwrctrl_max_clock_set(device, val);
+
+ return count;
+}
+
+static ssize_t kgsl_pwrctrl_clock_mhz_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+
+ if (device == NULL)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n",
+ kgsl_pwrctrl_active_freq(&device->pwrctrl) / 1000000);
+}
+
+static ssize_t kgsl_pwrctrl_freq_table_mhz_show(
+ struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+ int index, num_chars = 0;
+
+ if (device == NULL)
+ return 0;
+
+ pwr = &device->pwrctrl;
+ for (index = 0; index < pwr->num_pwrlevels - 1; index++) {
+ num_chars += scnprintf(buf + num_chars,
+ PAGE_SIZE - num_chars - 1,
+ "%d ", pwr->pwrlevels[index].gpu_freq / 1000000);
+ /* One space for trailing null and another for the newline */
+ if (num_chars >= PAGE_SIZE - 2)
+ break;
+ }
+
+ buf[num_chars++] = '\n';
+
+ return num_chars;
+}
+
+static ssize_t kgsl_pwrctrl_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct kgsl_device *device = kgsl_device_from_dev(dev);
+ struct kgsl_pwrctrl *pwr;
+ int ret, id = 0;
+ long temperature = 0;
+
+ if (device == NULL)
+ goto done;
+
+ pwr = &device->pwrctrl;
+
+ if (!pwr->tsens_name)
+ goto done;
+
+ id = sensor_get_id((char *)pwr->tsens_name);
+ if (id < 0)
+ goto done;
+
+ ret = sensor_get_temp(id, &temperature);
+ if (ret)
+ goto done;
+
+ return snprintf(buf, PAGE_SIZE, "%ld\n",
+ temperature);
+done:
+ return 0;
+}
+
static DEVICE_ATTR(gpuclk, 0644, kgsl_pwrctrl_gpuclk_show,
kgsl_pwrctrl_gpuclk_store);
static DEVICE_ATTR(max_gpuclk, 0644, kgsl_pwrctrl_max_gpuclk_show,
@@ -1234,6 +1494,20 @@ static DEVICE_ATTR(popp, 0644, kgsl_popp_show, kgsl_popp_store);
static DEVICE_ATTR(force_non_retention_on, 0644,
kgsl_pwrctrl_force_non_retention_on_show,
kgsl_pwrctrl_force_non_retention_on_store);
+static DEVICE_ATTR(pwrscale, 0644,
+ kgsl_pwrctrl_pwrscale_show,
+ kgsl_pwrctrl_pwrscale_store);
+static DEVICE_ATTR(gpu_model, 0444, kgsl_pwrctrl_gpu_model_show, NULL);
+static DEVICE_ATTR(gpu_busy_percentage, 0444,
+ kgsl_pwrctrl_gpu_busy_percentage_show, NULL);
+static DEVICE_ATTR(min_clock_mhz, 0644, kgsl_pwrctrl_min_clock_mhz_show,
+ kgsl_pwrctrl_min_clock_mhz_store);
+static DEVICE_ATTR(max_clock_mhz, 0644, kgsl_pwrctrl_max_clock_mhz_show,
+ kgsl_pwrctrl_max_clock_mhz_store);
+static DEVICE_ATTR(clock_mhz, 0444, kgsl_pwrctrl_clock_mhz_show, NULL);
+static DEVICE_ATTR(freq_table_mhz, 0444,
+ kgsl_pwrctrl_freq_table_mhz_show, NULL);
+static DEVICE_ATTR(temp, 0444, kgsl_pwrctrl_temp_show, NULL);
static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_gpuclk,
@@ -1256,12 +1530,51 @@ static const struct device_attribute *pwrctrl_attr_list[] = {
&dev_attr_bus_split,
&dev_attr_default_pwrlevel,
&dev_attr_popp,
+ &dev_attr_pwrscale,
+ &dev_attr_gpu_model,
+ &dev_attr_gpu_busy_percentage,
+ &dev_attr_min_clock_mhz,
+ &dev_attr_max_clock_mhz,
+ &dev_attr_clock_mhz,
+ &dev_attr_freq_table_mhz,
+ &dev_attr_temp,
NULL
};
+struct sysfs_link {
+ const char *src;
+ const char *dst;
+};
+
+static struct sysfs_link link_names[] = {
+ { "gpu_model", "gpu_model",},
+ { "gpu_busy_percentage", "gpu_busy",},
+ { "min_clock_mhz", "gpu_min_clock",},
+ { "max_clock_mhz", "gpu_max_clock",},
+ { "clock_mhz", "gpu_clock",},
+ { "freq_table_mhz", "gpu_freq_table",},
+ { "temp", "gpu_tmu",},
+};
+
int kgsl_pwrctrl_init_sysfs(struct kgsl_device *device)
{
- return kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list);
+ int i, ret;
+
+ ret = kgsl_create_device_sysfs_files(device->dev, pwrctrl_attr_list);
+ if (ret)
+ return ret;
+
+ device->gpu_sysfs_kobj = kobject_create_and_add("gpu", kernel_kobj);
+ if (IS_ERR_OR_NULL(device->gpu_sysfs_kobj))
+ return (device->gpu_sysfs_kobj == NULL) ?
+ -ENOMEM : PTR_ERR(device->gpu_sysfs_kobj);
+
+ for (i = 0; i < ARRAY_SIZE(link_names); i++)
+ kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+ &device->dev->kobj, link_names[i].src,
+ link_names[i].dst);
+
+ return 0;
}
void kgsl_pwrctrl_uninit_sysfs(struct kgsl_device *device)
@@ -1701,6 +2014,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
struct msm_bus_scale_pdata *bus_scale_table;
struct device_node *gpubw_dev_node = NULL;
struct platform_device *p2dev;
+ struct adreno_device *adreno_dev =
+ container_of(device, struct adreno_device, dev);
bus_scale_table = msm_bus_cl_get_pdata(device->pdev);
if (bus_scale_table == NULL)
@@ -1743,10 +2058,13 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
return -EINVAL;
}
+ if (adreno_is_a506(adreno_dev))
+ limit_pwrlevel = 1;
+
/* Initialize the user and thermal clock constraints */
pwr->max_pwrlevel = 0;
- pwr->min_pwrlevel = pwr->num_pwrlevels - 2;
+ pwr->min_pwrlevel = pwr->num_pwrlevels - 2 - limit_pwrlevel;
pwr->thermal_pwrlevel = 0;
pwr->wakeup_maxpwrlevel = 0;
@@ -1881,6 +2199,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
(unsigned long) device);
devfreq_vbif_register_callback(kgsl_get_bw);
+ /* temperature sensor name */
+ of_property_read_string(pdev->dev.of_node, "qcom,tsens-name",
+ &pwr->tsens_name);
+
return result;
}
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.h b/drivers/gpu/msm/kgsl_pwrctrl.h
index a99fd4d3..5f8bb88c 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.h
+++ b/drivers/gpu/msm/kgsl_pwrctrl.h
@@ -154,6 +154,7 @@ struct kgsl_regulator {
* @deep_nap_timer - Timer struct for entering deep nap
* @deep_nap_timeout - Timeout for entering deep nap
* @gx_retention - true if retention voltage is allowed
+ * @tsens_name - pointer to temperature sensor name of GPU temperature sensor
*/
struct kgsl_pwrctrl {
@@ -209,6 +210,7 @@ struct kgsl_pwrctrl {
bool gx_retention;
unsigned int gpu_bimc_int_clk_freq;
bool gpu_bimc_interface_enabled;
+ const char *tsens_name;
};
int kgsl_pwrctrl_init(struct kgsl_device *device);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.c b/drivers/gpu/msm/kgsl_pwrscale.c
index ad01d43a..1d0c72a6 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.c
+++ b/drivers/gpu/msm/kgsl_pwrscale.c
@@ -189,19 +189,21 @@ EXPORT_SYMBOL(kgsl_pwrscale_update);
/*
* kgsl_pwrscale_disable - temporarily disable the governor
* @device: The device
+ * @turbo: Indicates if pwrlevel should be forced to turbo
*
* Temporarily disable the governor, to prevent interference
* with profiling tools that expect a fixed clock frequency.
* This function must be called with the device mutex locked.
*/
-void kgsl_pwrscale_disable(struct kgsl_device *device)
+void kgsl_pwrscale_disable(struct kgsl_device *device, bool turbo)
{
BUG_ON(!mutex_is_locked(&device->mutex));
if (device->pwrscale.devfreqptr)
queue_work(device->pwrscale.devfreq_wq,
&device->pwrscale.devfreq_suspend_ws);
device->pwrscale.enabled = false;
- kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
+ if (turbo)
+ kgsl_pwrctrl_pwrlevel_change(device, KGSL_PWRLEVEL_TURBO);
}
EXPORT_SYMBOL(kgsl_pwrscale_disable);
@@ -874,6 +876,14 @@ int kgsl_pwrscale_init(struct device *dev, const char *governor)
pwrscale->history[i].type = i;
}
+ /* Add links to the devfreq sysfs nodes */
+ kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+ &pwrscale->devfreqptr->dev.kobj, "governor",
+ "gpu_governor");
+ kgsl_gpu_sysfs_add_link(device->gpu_sysfs_kobj,
+ &pwrscale->devfreqptr->dev.kobj,
+ "available_governors", "gpu_available_governor");
+
return 0;
}
EXPORT_SYMBOL(kgsl_pwrscale_init);
diff --git a/drivers/gpu/msm/kgsl_pwrscale.h b/drivers/gpu/msm/kgsl_pwrscale.h
index c8531786..0756a449 100644
--- a/drivers/gpu/msm/kgsl_pwrscale.h
+++ b/drivers/gpu/msm/kgsl_pwrscale.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2010-2016, 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
@@ -123,7 +123,7 @@ void kgsl_pwrscale_sleep(struct kgsl_device *device);
void kgsl_pwrscale_wake(struct kgsl_device *device);
void kgsl_pwrscale_enable(struct kgsl_device *device);
-void kgsl_pwrscale_disable(struct kgsl_device *device);
+void kgsl_pwrscale_disable(struct kgsl_device *device, bool turbo);
int kgsl_devfreq_target(struct device *dev, unsigned long *freq, u32 flags);
int kgsl_devfreq_get_dev_status(struct device *, struct devfreq_dev_status *);
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 73edc3f7..735b1886 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -574,12 +574,11 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
void *addr = (memdesc->hostptr) ?
memdesc->hostptr : (void *) memdesc->useraddr;
- /* Make sure that size is non-zero */
- if (!size)
+ if (size == 0 || size > UINT_MAX)
return -EINVAL;
- /* Make sure that the offset + size isn't bigger than we can handle */
- if ((offset + size) > ULONG_MAX)
+ /* Make sure that the offset + size does not overflow */
+ if ((offset + size < offset) || (offset + size < size))
return -ERANGE;
/* Make sure the offset + size do not overflow the address */
@@ -620,14 +619,8 @@ EXPORT_SYMBOL(kgsl_cache_range_op);
#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
static inline int get_page_size(size_t size, unsigned int align)
{
- if (align >= ilog2(SZ_1M) && size >= SZ_1M)
- return SZ_1M;
- else if (align >= ilog2(SZ_64K) && size >= SZ_64K)
- return SZ_64K;
- else if (align >= ilog2(SZ_8K) && size >= SZ_8K)
- return SZ_8K;
- else
- return PAGE_SIZE;
+ return (align >= ilog2(SZ_64K) && size >= SZ_64K)
+ ? SZ_64K : PAGE_SIZE;
}
#else
static inline int get_page_size(size_t size, unsigned int align)
@@ -636,6 +629,56 @@ static inline int get_page_size(size_t size, unsigned int align)
}
#endif
+static void kgsl_zero_pages(struct page **pages, unsigned int pcount)
+{
+ unsigned int j;
+ unsigned int step = ((VMALLOC_END - VMALLOC_START)/8) >> PAGE_SHIFT;
+ pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL);
+ void *ptr;
+
+ /*
+ * All memory that goes to the user has to be zeroed out before it gets
+ * exposed to userspace. This means that the memory has to be mapped in
+ * the kernel, zeroed (memset) and then unmapped. This also means that
+ * the dcache has to be flushed to ensure coherency between the kernel
+ * and user pages. We used to pass __GFP_ZERO to alloc_page which mapped
+ * zeroed and unmaped each individual page, and then we had to turn
+ * around and call flush_dcache_page() on that page to clear the caches.
+ * This was killing us for performance. Instead, we found it is much
+ * faster to allocate the pages without GFP_ZERO, map a chunk of the
+ * range ('step' pages), memset it, flush it and then unmap
+ * - this results in a factor of 4 improvement for speed for large
+ * buffers. There is a small decrease in speed for small buffers,
+ * but only on the order of a few microseconds at best. The 'step'
+ * size is based on a guess at the amount of free vmalloc space, but
+ * will scale down if there's not enough free space.
+ */
+ for (j = 0; j < pcount; j += step) {
+ step = min(step, pcount - j);
+
+ ptr = vmap(&pages[j], step, VM_IOREMAP, page_prot);
+
+ if (ptr != NULL) {
+ memset(ptr, 0, step * PAGE_SIZE);
+ dmac_flush_range(ptr, ptr + step * PAGE_SIZE);
+ vunmap(ptr);
+ } else {
+ int k;
+ /* Very, very, very slow path */
+
+ for (k = j; k < j + step; k++) {
+ ptr = kmap_atomic(pages[k]);
+ memset(ptr, 0, PAGE_SIZE);
+ dmac_flush_range(ptr, ptr + PAGE_SIZE);
+ kunmap_atomic(ptr);
+ }
+ /* scale down the step size to avoid this path */
+ if (step > 1)
+ step >>= 1;
+ }
+ }
+}
+
int
kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
struct kgsl_pagetable *pagetable,
@@ -660,10 +703,8 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
* larger however to accomodate hardware quirks
*/
- if (align < ilog2(page_size)) {
+ if (align < ilog2(page_size))
kgsl_memdesc_set_align(memdesc, ilog2(page_size));
- align = ilog2(page_size);
- }
/*
* There needs to be enough room in the page array to be able to
@@ -695,13 +736,18 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
while (len > 0) {
int page_count;
- page_count = kgsl_pool_alloc_page(&page_size,
+ /* don't waste space at the end of the allocation*/
+ if (len < page_size)
+ page_size = PAGE_SIZE;
+
+ page_count = kgsl_pool_alloc_page(page_size,
memdesc->pages + pcount,
- len_alloc - pcount,
- &align);
+ len_alloc - pcount);
if (page_count <= 0) {
- if (page_count == -EAGAIN)
+ if (page_size != PAGE_SIZE) {
+ page_size = PAGE_SIZE;
continue;
+ }
/*
* Update sglen and memdesc size,as requested allocation
@@ -723,9 +769,6 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
len -= page_size;
memdesc->size += page_size;
memdesc->page_count += page_count;
-
- /* Get the needed page size for the next iteration */
- page_size = get_page_size(len, align);
}
/* Call to the hypervisor to lock any secure buffer allocations */
@@ -783,6 +826,11 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
KGSL_STATS_ADD(memdesc->size, &kgsl_driver.stats.page_alloc,
&kgsl_driver.stats.page_alloc_max);
+ /*
+ * Zero out the pages.
+ */
+ kgsl_zero_pages(memdesc->pages, pcount);
+
done:
if (ret) {
if (memdesc->pages) {
diff --git a/drivers/gpu/msm/kgsl_snapshot.c b/drivers/gpu/msm/kgsl_snapshot.c
index 17106079..ea4dab4a 100644
--- a/drivers/gpu/msm/kgsl_snapshot.c
+++ b/drivers/gpu/msm/kgsl_snapshot.c
@@ -1060,6 +1060,9 @@ void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
size_t size = 0;
void *ptr;
+ if (IS_ERR_OR_NULL(device))
+ return;
+
kgsl_snapshot_process_ib_obj_list(snapshot);
list_for_each_entry(obj, &snapshot->obj_list, node) {
@@ -1074,9 +1077,6 @@ void kgsl_snapshot_save_frozen_objs(struct work_struct *work)
goto done;
snapshot->mempool = vmalloc(size);
- if (snapshot->mempool != NULL)
- KGSL_DRV_ERR(device, "snapshot: mempool address %p, size %zx\n",
- snapshot->mempool, size);
ptr = snapshot->mempool;
snapshot->mempool_size = 0;