Loading drivers/gpu/msm/adreno_a6xx_hfi.h +1 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ #define HFI_VALUE_LOG_EVENT_OFF 113 #define HFI_VALUE_DCVS_OBJ 114 #define HFI_VALUE_LM_CS0 115 #define HFI_VALUE_PREEMPT_COUNT 120 #define HFI_VALUE_GLOBAL_TOKEN 0xFFFFFFFF Loading drivers/gpu/msm/adreno_a6xx_hwsched.c +10 −5 Original line number Diff line number Diff line Loading @@ -987,18 +987,23 @@ static int a6xx_hwsched_bind(struct device *dev, struct device *master, void *data) { struct kgsl_device *device = dev_get_drvdata(master); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int ret; ret = a6xx_gmu_probe(device, to_platform_device(dev)); if (ret) goto error; ret = a6xx_hwsched_hfi_probe(ADRENO_DEVICE(device)); ret = a6xx_hwsched_hfi_probe(adreno_dev); if (ret) goto error; if (!ret) { set_bit(GMU_DISPATCH, &device->gmu_core.flags); if (ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION)) set_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); return 0; } error: a6xx_gmu_remove(device); Loading drivers/gpu/msm/adreno_a6xx_hwsched_hfi.c +66 −2 Original line number Diff line number Diff line Loading @@ -296,7 +296,7 @@ static irqreturn_t a6xx_hwsched_hfi_handler(int irq, void *data) } /* Ignore OOB bits */ status &= GENMASK(31, 31 - (oob_max - 1)); status &= GENMASK(31 - (oob_max - 1), 0); if (status & ~hfi->irq_mask) dev_err_ratelimited(&gmu->pdev->dev, Loading Loading @@ -545,6 +545,9 @@ static struct mem_alloc_entry *get_mem_alloc_entry( if (!(desc->flags & MEMFLAG_GFX_WRITEABLE)) flags |= KGSL_MEMFLAGS_GPUREADONLY; if (desc->flags & MEMFLAG_GFX_SECURE) flags |= KGSL_MEMFLAGS_SECURE; entry->gpu_md = kgsl_allocate_global(device, desc->size, flags, priv, memkind_string); if (IS_ERR(entry->gpu_md)) { Loading Loading @@ -730,6 +733,26 @@ static void enable_async_hfi(struct adreno_device *adreno_dev) (u32)~hfi->irq_mask); } static int enable_preemption(struct adreno_device *adreno_dev) { u32 data; if (!adreno_is_preemption_enabled(adreno_dev)) return 0; /* * Bits [0:1] contains the preemption level * Bit 2 is to enable/disable gmem save/restore * Bit 3 is to enable/disable skipsaverestore */ data = FIELD_PREP(GENMASK(1, 0), adreno_dev->preempt.preempt_level) | FIELD_PREP(BIT(2), adreno_dev->preempt.usesgmem) | FIELD_PREP(BIT(3), adreno_dev->preempt.skipsaverestore); return a6xx_hfi_send_feature_ctrl(adreno_dev, HFI_FEATURE_PREEMPTION, 1, data); } int a6xx_hwsched_hfi_start(struct adreno_device *adreno_dev) { struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); Loading Loading @@ -766,7 +789,11 @@ int a6xx_hwsched_hfi_start(struct adreno_device *adreno_dev) ret = a6xx_hfi_send_feature_ctrl(adreno_dev, HFI_FEATURE_KPROF, 1, 0); if (ret) return ret; goto err; ret = enable_preemption(adreno_dev); if (ret) goto err; ret = a6xx_hfi_send_core_fw_start(adreno_dev); if (ret) Loading Loading @@ -1326,3 +1353,40 @@ void a6xx_hwsched_context_detach(struct adreno_context *drawctxt) mutex_unlock(&device->mutex); } int a6xx_hwsched_preempt_count_get(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct hfi_get_value_cmd cmd; struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); struct a6xx_hwsched_hfi *hfi = to_a6xx_hwsched_hfi(adreno_dev); u32 seqnum = atomic_inc_return(&gmu->hfi.seqnum); struct pending_cmd pending_ack; int rc; if (device->state != KGSL_STATE_ACTIVE) return 0; CMD_MSG_HDR(cmd, H2F_MSG_GET_VALUE); cmd.hdr = MSG_HDR_SET_SEQNUM(cmd.hdr, seqnum); cmd.type = HFI_VALUE_PREEMPT_COUNT; cmd.subtype = 0; add_waiter(hfi, cmd.hdr, &pending_ack); rc = a6xx_hfi_cmdq_write(adreno_dev, (u32 *)&cmd); if (rc) goto done; rc = wait_ack_completion(adreno_dev, &pending_ack); if (rc) goto done; rc = check_ack_failure(adreno_dev, &pending_ack); done: del_waiter(hfi, &pending_ack); return rc ? rc : pending_ack.results[2]; } drivers/gpu/msm/adreno_a6xx_hwsched_hfi.h +14 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,9 @@ enum mem_kind { /* Host initializes the buffer */ #define MEMFLAG_HOST_INIT BIT(9) /* Gfx buffer needs to be secure */ #define MEMFLAG_GFX_SECURE BIT(12) struct mem_alloc_entry { struct hfi_mem_alloc_desc desc; struct kgsl_memdesc *gpu_md; Loading Loading @@ -214,4 +217,15 @@ void a6xx_hwsched_context_detach(struct adreno_context *drawctxt); /* Helper function to get to a6xx hwsched hfi device from adreno device */ struct a6xx_hwsched_hfi *to_a6xx_hwsched_hfi(struct adreno_device *adreno_dev); /** * a6xx_hwsched_preempt_count_get - Get preemption count from GMU * @adreno_dev: Pointer to adreno device * * This function sends a GET_VALUE HFI packet to get the number of * preemptions completed since last SLUMBER exit. * * Return: Preemption count on success or negative error on failure */ int a6xx_hwsched_preempt_count_get(struct adreno_device *adreno_dev); #endif drivers/gpu/msm/adreno_dispatch.c +128 −2 Original line number Diff line number Diff line Loading @@ -4,8 +4,10 @@ */ #include <linux/slab.h> #include <linux/sysfs.h> #include <soc/qcom/msm_performance.h> #include "adreno.h" #include "adreno_sysfs.h" #include "adreno_trace.h" #include "kgsl_gmu_core.h" #include "kgsl_timeline.h" Loading Loading @@ -858,8 +860,7 @@ static void dispatcher_handle_jobs_list(struct adreno_device *adreno_dev, llist_for_each_entry_safe(job, next, list, node) { int ret; if (adreno_gpu_stopped(adreno_dev) || adreno_drawctxt_bad(job->drawctxt)) { if (adreno_drawctxt_bad(job->drawctxt)) { kgsl_context_put(&job->drawctxt->base); kmem_cache_free(jobs_cache, job); continue; Loading @@ -879,6 +880,16 @@ static void dispatcher_handle_jobs_list(struct adreno_device *adreno_dev, continue; } /* * If gpu is in fault or dispatcher is halted, add back the jobs * so that they are processed after recovery or when dispatcher * is resumed. */ if (adreno_gpu_stopped(adreno_dev)) { llist_add(&job->node, &dispatcher->jobs[id]); continue; } ret = dispatcher_context_sendcmds(adreno_dev, job->drawctxt); /* Loading Loading @@ -2649,6 +2660,116 @@ void adreno_dispatcher_stop_fault_timer(struct kgsl_device *device) del_timer_sync(&dispatcher->fault_timer); } static int _skipsaverestore_store(struct adreno_device *adreno_dev, bool val) { adreno_dev->preempt.skipsaverestore = val ? true : false; return 0; } static bool _skipsaverestore_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.skipsaverestore; } static int _usesgmem_store(struct adreno_device *adreno_dev, bool val) { adreno_dev->preempt.usesgmem = val ? true : false; return 0; } static bool _usesgmem_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.usesgmem; } static int _preempt_level_store(struct adreno_device *adreno_dev, unsigned int val) { adreno_dev->preempt.preempt_level = min_t(unsigned int, val, 2); return 0; } static unsigned int _preempt_level_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.preempt_level; } static void change_preemption(struct adreno_device *adreno_dev, void *priv) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct kgsl_context *context; struct adreno_context *drawctxt; struct adreno_ringbuffer *rb; int id, i, ret; /* Make sure all ringbuffers are finished */ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { ret = adreno_ringbuffer_waittimestamp(rb, rb->timestamp, 2 * 1000); if (ret) { dev_err(device->dev, "Cannot disable preemption because couldn't idle ringbuffer[%d] ret: %d\n", rb->id, ret); return; } } change_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); adreno_dev->cur_rb = &adreno_dev->ringbuffers[0]; adreno_dev->next_rb = NULL; adreno_dev->prev_rb = NULL; /* Update the ringbuffer for each draw context */ write_lock(&device->context_lock); idr_for_each_entry(&device->context_idr, context, id) { drawctxt = ADRENO_CONTEXT(context); drawctxt->rb = adreno_ctx_get_rb(adreno_dev, drawctxt); /* * Make sure context destroy checks against the correct * ringbuffer's timestamp. */ adreno_rb_readtimestamp(adreno_dev, drawctxt->rb, KGSL_TIMESTAMP_RETIRED, &drawctxt->internal_timestamp); } write_unlock(&device->context_lock); } static int _preemption_store(struct adreno_device *adreno_dev, bool val) { if (!(ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION)) || (test_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv) == val)) return 0; return adreno_power_cycle(adreno_dev, change_preemption, NULL); } static bool _preemption_show(struct adreno_device *adreno_dev) { return adreno_is_preemption_enabled(adreno_dev); } static unsigned int _preempt_count_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.count; } static ADRENO_SYSFS_BOOL(preemption); static ADRENO_SYSFS_U32(preempt_level); static ADRENO_SYSFS_BOOL(usesgmem); static ADRENO_SYSFS_BOOL(skipsaverestore); static ADRENO_SYSFS_RO_U32(preempt_count); static const struct attribute *_preempt_attr_list[] = { &adreno_attr_preemption.attr.attr, &adreno_attr_preempt_level.attr.attr, &adreno_attr_usesgmem.attr.attr, &adreno_attr_skipsaverestore.attr.attr, &adreno_attr_preempt_count.attr.attr, NULL, }; /** * adreno_dispatcher_close() - close the dispatcher * @adreno_dev: pointer to the adreno device structure Loading @@ -2660,6 +2781,7 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev) struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; int i; struct adreno_ringbuffer *rb; struct kgsl_device *device = KGSL_DEVICE(adreno_dev); mutex_lock(&dispatcher->mutex); del_timer_sync(&dispatcher->timer); Loading @@ -2681,6 +2803,8 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev) kobject_put(&dispatcher->kobj); kmem_cache_destroy(jobs_cache); sysfs_remove_files(&device->dev->kobj, _preempt_attr_list); } struct dispatcher_attribute { Loading Loading @@ -2830,6 +2954,8 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) if (ret) return ret; sysfs_create_files(&device->dev->kobj, _preempt_attr_list); mutex_init(&dispatcher->mutex); timer_setup(&dispatcher->timer, adreno_dispatcher_timer, 0); Loading Loading
drivers/gpu/msm/adreno_a6xx_hfi.h +1 −0 Original line number Diff line number Diff line Loading @@ -78,6 +78,7 @@ #define HFI_VALUE_LOG_EVENT_OFF 113 #define HFI_VALUE_DCVS_OBJ 114 #define HFI_VALUE_LM_CS0 115 #define HFI_VALUE_PREEMPT_COUNT 120 #define HFI_VALUE_GLOBAL_TOKEN 0xFFFFFFFF Loading
drivers/gpu/msm/adreno_a6xx_hwsched.c +10 −5 Original line number Diff line number Diff line Loading @@ -987,18 +987,23 @@ static int a6xx_hwsched_bind(struct device *dev, struct device *master, void *data) { struct kgsl_device *device = dev_get_drvdata(master); struct adreno_device *adreno_dev = ADRENO_DEVICE(device); int ret; ret = a6xx_gmu_probe(device, to_platform_device(dev)); if (ret) goto error; ret = a6xx_hwsched_hfi_probe(ADRENO_DEVICE(device)); ret = a6xx_hwsched_hfi_probe(adreno_dev); if (ret) goto error; if (!ret) { set_bit(GMU_DISPATCH, &device->gmu_core.flags); if (ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION)) set_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); return 0; } error: a6xx_gmu_remove(device); Loading
drivers/gpu/msm/adreno_a6xx_hwsched_hfi.c +66 −2 Original line number Diff line number Diff line Loading @@ -296,7 +296,7 @@ static irqreturn_t a6xx_hwsched_hfi_handler(int irq, void *data) } /* Ignore OOB bits */ status &= GENMASK(31, 31 - (oob_max - 1)); status &= GENMASK(31 - (oob_max - 1), 0); if (status & ~hfi->irq_mask) dev_err_ratelimited(&gmu->pdev->dev, Loading Loading @@ -545,6 +545,9 @@ static struct mem_alloc_entry *get_mem_alloc_entry( if (!(desc->flags & MEMFLAG_GFX_WRITEABLE)) flags |= KGSL_MEMFLAGS_GPUREADONLY; if (desc->flags & MEMFLAG_GFX_SECURE) flags |= KGSL_MEMFLAGS_SECURE; entry->gpu_md = kgsl_allocate_global(device, desc->size, flags, priv, memkind_string); if (IS_ERR(entry->gpu_md)) { Loading Loading @@ -730,6 +733,26 @@ static void enable_async_hfi(struct adreno_device *adreno_dev) (u32)~hfi->irq_mask); } static int enable_preemption(struct adreno_device *adreno_dev) { u32 data; if (!adreno_is_preemption_enabled(adreno_dev)) return 0; /* * Bits [0:1] contains the preemption level * Bit 2 is to enable/disable gmem save/restore * Bit 3 is to enable/disable skipsaverestore */ data = FIELD_PREP(GENMASK(1, 0), adreno_dev->preempt.preempt_level) | FIELD_PREP(BIT(2), adreno_dev->preempt.usesgmem) | FIELD_PREP(BIT(3), adreno_dev->preempt.skipsaverestore); return a6xx_hfi_send_feature_ctrl(adreno_dev, HFI_FEATURE_PREEMPTION, 1, data); } int a6xx_hwsched_hfi_start(struct adreno_device *adreno_dev) { struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); Loading Loading @@ -766,7 +789,11 @@ int a6xx_hwsched_hfi_start(struct adreno_device *adreno_dev) ret = a6xx_hfi_send_feature_ctrl(adreno_dev, HFI_FEATURE_KPROF, 1, 0); if (ret) return ret; goto err; ret = enable_preemption(adreno_dev); if (ret) goto err; ret = a6xx_hfi_send_core_fw_start(adreno_dev); if (ret) Loading Loading @@ -1326,3 +1353,40 @@ void a6xx_hwsched_context_detach(struct adreno_context *drawctxt) mutex_unlock(&device->mutex); } int a6xx_hwsched_preempt_count_get(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct hfi_get_value_cmd cmd; struct a6xx_gmu_device *gmu = to_a6xx_gmu(adreno_dev); struct a6xx_hwsched_hfi *hfi = to_a6xx_hwsched_hfi(adreno_dev); u32 seqnum = atomic_inc_return(&gmu->hfi.seqnum); struct pending_cmd pending_ack; int rc; if (device->state != KGSL_STATE_ACTIVE) return 0; CMD_MSG_HDR(cmd, H2F_MSG_GET_VALUE); cmd.hdr = MSG_HDR_SET_SEQNUM(cmd.hdr, seqnum); cmd.type = HFI_VALUE_PREEMPT_COUNT; cmd.subtype = 0; add_waiter(hfi, cmd.hdr, &pending_ack); rc = a6xx_hfi_cmdq_write(adreno_dev, (u32 *)&cmd); if (rc) goto done; rc = wait_ack_completion(adreno_dev, &pending_ack); if (rc) goto done; rc = check_ack_failure(adreno_dev, &pending_ack); done: del_waiter(hfi, &pending_ack); return rc ? rc : pending_ack.results[2]; }
drivers/gpu/msm/adreno_a6xx_hwsched_hfi.h +14 −0 Original line number Diff line number Diff line Loading @@ -104,6 +104,9 @@ enum mem_kind { /* Host initializes the buffer */ #define MEMFLAG_HOST_INIT BIT(9) /* Gfx buffer needs to be secure */ #define MEMFLAG_GFX_SECURE BIT(12) struct mem_alloc_entry { struct hfi_mem_alloc_desc desc; struct kgsl_memdesc *gpu_md; Loading Loading @@ -214,4 +217,15 @@ void a6xx_hwsched_context_detach(struct adreno_context *drawctxt); /* Helper function to get to a6xx hwsched hfi device from adreno device */ struct a6xx_hwsched_hfi *to_a6xx_hwsched_hfi(struct adreno_device *adreno_dev); /** * a6xx_hwsched_preempt_count_get - Get preemption count from GMU * @adreno_dev: Pointer to adreno device * * This function sends a GET_VALUE HFI packet to get the number of * preemptions completed since last SLUMBER exit. * * Return: Preemption count on success or negative error on failure */ int a6xx_hwsched_preempt_count_get(struct adreno_device *adreno_dev); #endif
drivers/gpu/msm/adreno_dispatch.c +128 −2 Original line number Diff line number Diff line Loading @@ -4,8 +4,10 @@ */ #include <linux/slab.h> #include <linux/sysfs.h> #include <soc/qcom/msm_performance.h> #include "adreno.h" #include "adreno_sysfs.h" #include "adreno_trace.h" #include "kgsl_gmu_core.h" #include "kgsl_timeline.h" Loading Loading @@ -858,8 +860,7 @@ static void dispatcher_handle_jobs_list(struct adreno_device *adreno_dev, llist_for_each_entry_safe(job, next, list, node) { int ret; if (adreno_gpu_stopped(adreno_dev) || adreno_drawctxt_bad(job->drawctxt)) { if (adreno_drawctxt_bad(job->drawctxt)) { kgsl_context_put(&job->drawctxt->base); kmem_cache_free(jobs_cache, job); continue; Loading @@ -879,6 +880,16 @@ static void dispatcher_handle_jobs_list(struct adreno_device *adreno_dev, continue; } /* * If gpu is in fault or dispatcher is halted, add back the jobs * so that they are processed after recovery or when dispatcher * is resumed. */ if (adreno_gpu_stopped(adreno_dev)) { llist_add(&job->node, &dispatcher->jobs[id]); continue; } ret = dispatcher_context_sendcmds(adreno_dev, job->drawctxt); /* Loading Loading @@ -2649,6 +2660,116 @@ void adreno_dispatcher_stop_fault_timer(struct kgsl_device *device) del_timer_sync(&dispatcher->fault_timer); } static int _skipsaverestore_store(struct adreno_device *adreno_dev, bool val) { adreno_dev->preempt.skipsaverestore = val ? true : false; return 0; } static bool _skipsaverestore_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.skipsaverestore; } static int _usesgmem_store(struct adreno_device *adreno_dev, bool val) { adreno_dev->preempt.usesgmem = val ? true : false; return 0; } static bool _usesgmem_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.usesgmem; } static int _preempt_level_store(struct adreno_device *adreno_dev, unsigned int val) { adreno_dev->preempt.preempt_level = min_t(unsigned int, val, 2); return 0; } static unsigned int _preempt_level_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.preempt_level; } static void change_preemption(struct adreno_device *adreno_dev, void *priv) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct kgsl_context *context; struct adreno_context *drawctxt; struct adreno_ringbuffer *rb; int id, i, ret; /* Make sure all ringbuffers are finished */ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { ret = adreno_ringbuffer_waittimestamp(rb, rb->timestamp, 2 * 1000); if (ret) { dev_err(device->dev, "Cannot disable preemption because couldn't idle ringbuffer[%d] ret: %d\n", rb->id, ret); return; } } change_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); adreno_dev->cur_rb = &adreno_dev->ringbuffers[0]; adreno_dev->next_rb = NULL; adreno_dev->prev_rb = NULL; /* Update the ringbuffer for each draw context */ write_lock(&device->context_lock); idr_for_each_entry(&device->context_idr, context, id) { drawctxt = ADRENO_CONTEXT(context); drawctxt->rb = adreno_ctx_get_rb(adreno_dev, drawctxt); /* * Make sure context destroy checks against the correct * ringbuffer's timestamp. */ adreno_rb_readtimestamp(adreno_dev, drawctxt->rb, KGSL_TIMESTAMP_RETIRED, &drawctxt->internal_timestamp); } write_unlock(&device->context_lock); } static int _preemption_store(struct adreno_device *adreno_dev, bool val) { if (!(ADRENO_FEATURE(adreno_dev, ADRENO_PREEMPTION)) || (test_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv) == val)) return 0; return adreno_power_cycle(adreno_dev, change_preemption, NULL); } static bool _preemption_show(struct adreno_device *adreno_dev) { return adreno_is_preemption_enabled(adreno_dev); } static unsigned int _preempt_count_show(struct adreno_device *adreno_dev) { return adreno_dev->preempt.count; } static ADRENO_SYSFS_BOOL(preemption); static ADRENO_SYSFS_U32(preempt_level); static ADRENO_SYSFS_BOOL(usesgmem); static ADRENO_SYSFS_BOOL(skipsaverestore); static ADRENO_SYSFS_RO_U32(preempt_count); static const struct attribute *_preempt_attr_list[] = { &adreno_attr_preemption.attr.attr, &adreno_attr_preempt_level.attr.attr, &adreno_attr_usesgmem.attr.attr, &adreno_attr_skipsaverestore.attr.attr, &adreno_attr_preempt_count.attr.attr, NULL, }; /** * adreno_dispatcher_close() - close the dispatcher * @adreno_dev: pointer to the adreno device structure Loading @@ -2660,6 +2781,7 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev) struct adreno_dispatcher *dispatcher = &adreno_dev->dispatcher; int i; struct adreno_ringbuffer *rb; struct kgsl_device *device = KGSL_DEVICE(adreno_dev); mutex_lock(&dispatcher->mutex); del_timer_sync(&dispatcher->timer); Loading @@ -2681,6 +2803,8 @@ void adreno_dispatcher_close(struct adreno_device *adreno_dev) kobject_put(&dispatcher->kobj); kmem_cache_destroy(jobs_cache); sysfs_remove_files(&device->dev->kobj, _preempt_attr_list); } struct dispatcher_attribute { Loading Loading @@ -2830,6 +2954,8 @@ int adreno_dispatcher_init(struct adreno_device *adreno_dev) if (ret) return ret; sysfs_create_files(&device->dev->kobj, _preempt_attr_list); mutex_init(&dispatcher->mutex); timer_setup(&dispatcher->timer, adreno_dispatcher_timer, 0); Loading