diff options
| author | wzedlare <vedatak01@gmail.com> | 2017-06-18 16:38:26 +0000 |
|---|---|---|
| committer | wzedlare <vedatak01@gmail.com> | 2017-06-19 16:57:11 +0000 |
| commit | c7d4e3fd588e3ba3d3fa4d5cfa224aa54bc288bf (patch) | |
| tree | b8b64cb9deb6832c1e41f58f0f143514beafc709 /drivers/gpu | |
| parent | 28c99c87b881bb664c44bb26e80a681f87d54e60 (diff) | |
Change-Id: Ia4c94f09e29843b1af34d466243378a357e97b70
Diffstat (limited to 'drivers/gpu')
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; |
