// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021, The Linux Foundation. All rights reserved. * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include "adreno.h" #include "adreno_gen8.h" #include "adreno_pm4types.h" #include "adreno_trace.h" #define PREEMPT_RECORD(_field) \ offsetof(struct gen8_cp_preemption_record, _field) #define PREEMPT_SMMU_RECORD(_field) \ offsetof(struct gen8_cp_smmu_info, _field) static void _update_wptr(struct adreno_device *adreno_dev, bool reset_timer, bool atomic) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct adreno_ringbuffer *rb = adreno_dev->cur_rb; unsigned long flags; int ret = 0; spin_lock_irqsave(&rb->preempt_lock, flags); if (!atomic) { /* * We might have skipped updating the wptr in case we are in * dispatcher context. Do it now. */ if (rb->skip_inline_wptr) { ret = gen8_fenced_write(adreno_dev, GEN8_CP_RB_WPTR_GC, rb->wptr, FENCE_STATUS_WRITEDROPPED0_MASK); reset_timer = true; rb->skip_inline_wptr = false; } } else { u32 wptr; kgsl_regread(device, GEN8_CP_RB_WPTR_GC, &wptr); if (wptr != rb->wptr) { kgsl_regwrite(device, GEN8_CP_RB_WPTR_GC, rb->wptr); reset_timer = true; } } if (reset_timer) rb->dispatch_q.expires = jiffies + msecs_to_jiffies(adreno_drawobj_timeout); spin_unlock_irqrestore(&rb->preempt_lock, flags); if (!atomic) { /* If WPTR update fails, set the fault and trigger recovery */ if (ret) { gmu_core_fault_snapshot(device); adreno_dispatcher_fault(adreno_dev, ADRENO_GMU_FAULT_SKIP_SNAPSHOT); } } } static void _power_collapse_set(struct adreno_device *adreno_dev, bool val) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); gmu_core_regwrite(device, GEN8_GMUCX_PWR_COL_PREEMPTION_KEEPALIVE, (val ? 1 : 0)); } static void _gen8_preemption_done(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); u32 status; /* * In the very unlikely case that the power is off, do nothing - the * state will be reset on power up and everybody will be happy */ if (!kgsl_state_is_awake(device)) return; kgsl_regread(device, GEN8_CP_CONTEXT_SWITCH_CNTL, &status); if (status & 0x1) { dev_err(device->dev, "Preemption not complete: status=%X cur=%d R/W=%X/%X next=%d R/W=%X/%X\n", status, adreno_dev->cur_rb->id, adreno_get_rptr(adreno_dev->cur_rb), adreno_dev->cur_rb->wptr, adreno_dev->next_rb->id, adreno_get_rptr(adreno_dev->next_rb), adreno_dev->next_rb->wptr); /* Set a fault and restart */ adreno_dispatcher_fault(adreno_dev, ADRENO_PREEMPT_FAULT); return; } adreno_dev->preempt.count++; del_timer_sync(&adreno_dev->preempt.timer); kgsl_regread(device, GEN8_CP_CONTEXT_SWITCH_LEVEL_STATUS, &status); trace_adreno_preempt_done(adreno_dev->cur_rb->id, adreno_dev->next_rb->id, status, 0); /* Clean up all the bits */ adreno_dev->prev_rb = adreno_dev->cur_rb; adreno_dev->cur_rb = adreno_dev->next_rb; adreno_dev->next_rb = NULL; /* Update the wptr for the new command queue */ _update_wptr(adreno_dev, true, false); /* Update the dispatcher timer for the new command queue */ mod_timer(&adreno_dev->dispatcher.timer, adreno_dev->cur_rb->dispatch_q.expires); /* Clear the preempt state */ adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); } static void _gen8_preemption_fault(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); u32 status; /* * If the power is on check the preemption status one more time - if it * was successful then just transition to the complete state */ if (kgsl_state_is_awake(device)) { kgsl_regread(device, GEN8_CP_CONTEXT_SWITCH_CNTL, &status); if (!(status & 0x1)) { adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE); adreno_dispatcher_schedule(device); return; } } dev_err(device->dev, "Preemption Fault: cur=%d R/W=0x%x/0x%x, next=%d R/W=0x%x/0x%x\n", adreno_dev->cur_rb->id, adreno_get_rptr(adreno_dev->cur_rb), adreno_dev->cur_rb->wptr, adreno_dev->next_rb->id, adreno_get_rptr(adreno_dev->next_rb), adreno_dev->next_rb->wptr); adreno_dispatcher_fault(adreno_dev, ADRENO_PREEMPT_FAULT); } static void _gen8_preemption_worker(struct work_struct *work) { struct adreno_preemption *preempt = container_of(work, struct adreno_preemption, work); struct adreno_device *adreno_dev = container_of(preempt, struct adreno_device, preempt); struct kgsl_device *device = KGSL_DEVICE(adreno_dev); /* Need to take the mutex to make sure that the power stays on */ mutex_lock(&device->mutex); if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_FAULTED)) _gen8_preemption_fault(adreno_dev); mutex_unlock(&device->mutex); } /* Find the highest priority active ringbuffer */ static struct adreno_ringbuffer *gen8_next_ringbuffer( struct adreno_device *adreno_dev) { struct adreno_ringbuffer *rb; unsigned long flags; u32 i; FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { bool empty; spin_lock_irqsave(&rb->preempt_lock, flags); empty = adreno_rb_empty(rb); spin_unlock_irqrestore(&rb->preempt_lock, flags); if (!empty) return rb; } return NULL; } void gen8_preemption_trigger(struct adreno_device *adreno_dev, bool atomic) { const struct adreno_gen8_core *gen8_core = to_gen8_core(adreno_dev); struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct kgsl_iommu *iommu = KGSL_IOMMU(device); struct adreno_ringbuffer *next; u64 ttbr0, gpuaddr; u32 contextidr, cntl; unsigned long flags; struct adreno_preemption *preempt = &adreno_dev->preempt; /* Put ourselves into a possible trigger state */ if (!adreno_move_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE, ADRENO_PREEMPT_START)) return; /* Get the next ringbuffer to preempt in */ next = gen8_next_ringbuffer(adreno_dev); /* * Nothing to do if every ringbuffer is empty or if the current * ringbuffer is the only active one */ if (next == NULL || next == adreno_dev->cur_rb) { /* * Update any critical things that might have been skipped while * we were looking for a new ringbuffer */ if (next != NULL) { _update_wptr(adreno_dev, false, atomic); mod_timer(&adreno_dev->dispatcher.timer, adreno_dev->cur_rb->dispatch_q.expires); } adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); return; } /* Turn off the dispatcher timer */ del_timer(&adreno_dev->dispatcher.timer); /* * This is the most critical section - we need to take care not to race * until we have programmed the CP for the switch */ spin_lock_irqsave(&next->preempt_lock, flags); /* Get the pagetable from the pagetable info. */ kgsl_sharedmem_readq(device->scratch, &ttbr0, SCRATCH_RB_OFFSET(next->id, ttbr0)); kgsl_sharedmem_readl(device->scratch, &contextidr, SCRATCH_RB_OFFSET(next->id, contextidr)); kgsl_sharedmem_writel(next->preemption_desc, PREEMPT_RECORD(wptr), next->wptr); spin_unlock_irqrestore(&next->preempt_lock, flags); /* And write it to the smmu info */ if (kgsl_mmu_is_perprocess(&device->mmu)) { kgsl_sharedmem_writeq(iommu->smmu_info, PREEMPT_SMMU_RECORD(ttbr0), ttbr0); kgsl_sharedmem_writel(iommu->smmu_info, PREEMPT_SMMU_RECORD(context_idr), contextidr); } kgsl_sharedmem_readq(preempt->scratch, &gpuaddr, next->id * sizeof(u64)); /* * Set a keepalive bit before the first preemption register write. * This is required since while each individual write to the context * switch registers will wake the GPU from collapse, it will not in * itself cause GPU activity. Thus, the GPU could technically be * re-collapsed between subsequent register writes leading to a * prolonged preemption sequence. The keepalive bit prevents any * further power collapse while it is set. * It is more efficient to use a keepalive+wake-on-fence approach here * rather than an OOB. Both keepalive and the fence are effectively * free when the GPU is already powered on, whereas an OOB requires an * unconditional handshake with the GMU. */ _power_collapse_set(adreno_dev, true); /* * Fenced writes on this path will make sure the GPU is woken up * in case it was power collapsed by the GMU. */ if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_PNSR_ADDR_LO, lower_32_bits(next->preemption_desc->gpuaddr), FENCE_STATUS_WRITEDROPPED1_MASK)) goto err; /* * Above fence writes will make sure GMU comes out of * IFPC state if its was in IFPC state but it doesn't * guarantee that GMU FW actually moved to ACTIVE state * i.e. wake-up from IFPC is complete. * Wait for GMU to move to ACTIVE state before triggering * preemption. This is require to make sure CP doesn't * interrupt GMU during wake-up from IFPC. */ if (!atomic && gmu_core_dev_wait_for_active_transition(device)) goto err; if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_PNSR_ADDR_HI, upper_32_bits(next->preemption_desc->gpuaddr), FENCE_STATUS_WRITEDROPPED1_MASK)) goto err; if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_PSR_ADDR_LO, lower_32_bits(next->secure_preemption_desc->gpuaddr), FENCE_STATUS_WRITEDROPPED1_MASK)) goto err; if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_PSR_ADDR_HI, upper_32_bits(next->secure_preemption_desc->gpuaddr), FENCE_STATUS_WRITEDROPPED1_MASK)) goto err; if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_NPR_ADDR_LO, lower_32_bits(gpuaddr), FENCE_STATUS_WRITEDROPPED1_MASK)) goto err; if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_NPR_ADDR_HI, upper_32_bits(gpuaddr), FENCE_STATUS_WRITEDROPPED1_MASK)) goto err; adreno_dev->next_rb = next; /* Start the timer to detect a stuck preemption */ mod_timer(&adreno_dev->preempt.timer, jiffies + msecs_to_jiffies(ADRENO_PREEMPT_TIMEOUT)); cntl = (preempt->preempt_level << 6) | 0x01; /* Skip save/restore during L1 preemption */ if (preempt->skipsaverestore) cntl |= (1 << 9); /* Enable GMEM save/restore across preemption */ if (preempt->usesgmem) cntl |= (1 << 8); trace_adreno_preempt_trigger(adreno_dev->cur_rb->id, adreno_dev->next_rb->id, cntl, 0); adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED); if (gen8_core->qos_value) kgsl_sharedmem_writel(preempt->scratch, PREEMPT_SCRATCH_OFFSET(QOS_VALUE_IDX), gen8_core->qos_value[next->id]); /* Trigger the preemption */ if (gen8_fenced_write(adreno_dev, GEN8_CP_CONTEXT_SWITCH_CNTL, cntl, FENCE_STATUS_WRITEDROPPED1_MASK)) { adreno_dev->next_rb = NULL; del_timer(&adreno_dev->preempt.timer); goto err; } return; err: /* If fenced write fails, take inline snapshot and trigger recovery */ if (!in_interrupt()) { gmu_core_fault_snapshot(device); adreno_dispatcher_fault(adreno_dev, ADRENO_GMU_FAULT_SKIP_SNAPSHOT); } else { adreno_dispatcher_fault(adreno_dev, ADRENO_GMU_FAULT); } adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); /* Clear the keep alive */ _power_collapse_set(adreno_dev, false); } void gen8_preemption_callback(struct adreno_device *adreno_dev, int bit) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); u32 status; if (!adreno_move_preempt_state(adreno_dev, ADRENO_PREEMPT_TRIGGERED, ADRENO_PREEMPT_PENDING)) return; kgsl_regread(device, GEN8_CP_CONTEXT_SWITCH_CNTL, &status); if (status & 0x1) { dev_err(KGSL_DEVICE(adreno_dev)->dev, "preempt interrupt with non-zero status: %X\n", status); /* * Under the assumption that this is a race between the * interrupt and the register, schedule the worker to clean up. * If the status still hasn't resolved itself by the time we get * there then we have to assume something bad happened */ adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE); adreno_dispatcher_schedule(KGSL_DEVICE(adreno_dev)); return; } adreno_dev->preempt.count++; /* * We can now safely clear the preemption keepalive bit, allowing * power collapse to resume its regular activity. */ _power_collapse_set(adreno_dev, false); del_timer(&adreno_dev->preempt.timer); kgsl_regread(device, GEN8_CP_CONTEXT_SWITCH_LEVEL_STATUS, &status); trace_adreno_preempt_done(adreno_dev->cur_rb->id, adreno_dev->next_rb->id, status, 0); adreno_dev->prev_rb = adreno_dev->cur_rb; adreno_dev->cur_rb = adreno_dev->next_rb; adreno_dev->next_rb = NULL; /* Update the wptr if it changed while preemption was ongoing */ _update_wptr(adreno_dev, true, true); /* Update the dispatcher timer for the new command queue */ mod_timer(&adreno_dev->dispatcher.timer, adreno_dev->cur_rb->dispatch_q.expires); adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); gen8_preemption_trigger(adreno_dev, true); } void gen8_preemption_prepare_postamble(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); const struct adreno_gen8_core *gen8_core = to_gen8_core(adreno_dev); struct adreno_preemption *preempt = &adreno_dev->preempt; u32 *postamble, count = 0; /* * First 28 dwords of the device scratch buffer are used to store shadow rb data. * Reserve 15 dwords in the device scratch buffer from SCRATCH_POSTAMBLE_OFFSET for * KMD postamble pm4 packets. This should be in *device->scratch* so that userspace * cannot access it. */ postamble = device->scratch->hostptr + SCRATCH_POSTAMBLE_OFFSET; /* * Reserve 4 dwords in the scratch buffer for dynamic QOS control feature. To ensure QOS * value is updated for first preemption, send it during bootup */ if (gen8_core->qos_value) { postamble[count++] = cp_type7_packet(CP_MEM_TO_REG, 3); postamble[count++] = GEN8_RBBM_GBIF_CLIENT_QOS_CNTL; postamble[count++] = lower_32_bits(PREEMPT_SCRATCH_ADDR(adreno_dev, QOS_VALUE_IDX)); postamble[count++] = upper_32_bits(PREEMPT_SCRATCH_ADDR(adreno_dev, QOS_VALUE_IDX)); } /* * Since postambles are not preserved across slumber, necessary packets * must be sent to GPU before first submission. * * If a packet needs to be sent before first submission, add it above this. */ preempt->postamble_bootup_len = count; /* Reserve 15 dwords in the device scratch buffer to clear perfcounters */ if (!adreno_dev->perfcounter) { postamble[count++] = cp_type7_packet(CP_REG_RMW, 3); postamble[count++] = GEN8_RBBM_PERFCTR_SRAM_INIT_CMD; postamble[count++] = 0x0; postamble[count++] = 0x1; postamble[count++] = cp_type7_packet(CP_REG_RMW, 3); postamble[count++] = GEN8_RBBM_SLICE_PERFCTR_SRAM_INIT_CMD; postamble[count++] = 0x0; postamble[count++] = 0x1; postamble[count++] = cp_type7_packet(CP_WAIT_REG_MEM, 6); postamble[count++] = 0x3; postamble[count++] = GEN8_RBBM_PERFCTR_SRAM_INIT_STATUS; postamble[count++] = 0x0; postamble[count++] = 0x1; postamble[count++] = 0x1; postamble[count++] = 0x0; } preempt->postamble_len = count; } void gen8_preemption_schedule(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); if (!adreno_is_preemption_enabled(adreno_dev)) return; mutex_lock(&device->mutex); if (adreno_in_preempt_state(adreno_dev, ADRENO_PREEMPT_COMPLETE)) _gen8_preemption_done(adreno_dev); gen8_preemption_trigger(adreno_dev, false); mutex_unlock(&device->mutex); } u32 gen8_preemption_pre_ibsubmit(struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb, struct adreno_context *drawctxt, u32 *cmds) { u32 *cmds_orig = cmds; if (!adreno_is_preemption_enabled(adreno_dev)) return 0; if (test_and_set_bit(ADRENO_RB_SET_PSEUDO_DONE, &rb->flags)) goto done; *cmds++ = cp_type7_packet(CP_THREAD_CONTROL, 1); *cmds++ = CP_SET_THREAD_BR; *cmds++ = cp_type7_packet(CP_SET_PSEUDO_REGISTER, 12); /* NULL SMMU_INFO buffer - we track in KMD */ *cmds++ = SET_PSEUDO_SMMU_INFO; cmds += cp_gpuaddr(adreno_dev, cmds, 0x0); *cmds++ = SET_PSEUDO_PRIV_NON_SECURE_SAVE_ADDR; cmds += cp_gpuaddr(adreno_dev, cmds, rb->preemption_desc->gpuaddr); *cmds++ = SET_PSEUDO_PRIV_SECURE_SAVE_ADDR; cmds += cp_gpuaddr(adreno_dev, cmds, rb->secure_preemption_desc->gpuaddr); /* * There is no need to specify this address when we are about to * trigger preemption. This is because CP internally stores this * address specified here in the CP_SET_PSEUDO_REGISTER payload to * the context record and thus knows from where to restore * the saved perfcounters for the new ringbuffer. */ *cmds++ = SET_PSEUDO_COUNTER; cmds += cp_gpuaddr(adreno_dev, cmds, rb->perfcounter_save_restore_desc->gpuaddr); done: if (drawctxt) { struct adreno_ringbuffer *rb = drawctxt->rb; u64 dest = PREEMPT_SCRATCH_ADDR(adreno_dev, rb->id); u64 gpuaddr = drawctxt->base.user_ctxt_record->memdesc.gpuaddr; *cmds++ = cp_mem_packet(adreno_dev, CP_MEM_WRITE, 2, 2); cmds += cp_gpuaddr(adreno_dev, cmds, dest); *cmds++ = lower_32_bits(gpuaddr); *cmds++ = upper_32_bits(gpuaddr); if (adreno_dev->preempt.postamble_len) { u64 kmd_postamble_addr = SCRATCH_POSTAMBLE_ADDR(KGSL_DEVICE(adreno_dev)); *cmds++ = cp_type7_packet(CP_SET_AMBLE, 3); *cmds++ = lower_32_bits(kmd_postamble_addr); *cmds++ = upper_32_bits(kmd_postamble_addr); *cmds++ = FIELD_PREP(GENMASK(22, 20), CP_KMD_AMBLE_TYPE) | (FIELD_PREP(GENMASK(19, 0), adreno_dev->preempt.postamble_len)); } } return (u32) (cmds - cmds_orig); } u32 gen8_preemption_post_ibsubmit(struct adreno_device *adreno_dev, u32 *cmds) { u32 index = 0; if (!adreno_is_preemption_enabled(adreno_dev)) return 0; if (adreno_dev->cur_rb) { u64 dest = PREEMPT_SCRATCH_ADDR(adreno_dev, adreno_dev->cur_rb->id); cmds[index++] = cp_type7_packet(CP_MEM_WRITE, 4); cmds[index++] = lower_32_bits(dest); cmds[index++] = upper_32_bits(dest); cmds[index++] = 0; cmds[index++] = 0; } cmds[index++] = cp_type7_packet(CP_THREAD_CONTROL, 1); cmds[index++] = CP_SET_THREAD_BOTH; cmds[index++] = cp_type7_packet(CP_CONTEXT_SWITCH_YIELD, 4); cmds[index++] = 0; cmds[index++] = 0; cmds[index++] = 1; cmds[index++] = 0; return index; } void gen8_preemption_start(struct adreno_device *adreno_dev) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct kgsl_iommu *iommu = KGSL_IOMMU(device); struct adreno_ringbuffer *rb; u32 i; if (!adreno_is_preemption_enabled(adreno_dev)) return; /* Force the state to be clear */ adreno_set_preempt_state(adreno_dev, ADRENO_PREEMPT_NONE); if (kgsl_mmu_is_perprocess(&device->mmu)) { /* smmu_info is allocated and mapped in gen8_preemption_iommu_init */ kgsl_sharedmem_writel(iommu->smmu_info, PREEMPT_SMMU_RECORD(magic), GEN8_CP_SMMU_INFO_MAGIC_REF); kgsl_sharedmem_writeq(iommu->smmu_info, PREEMPT_SMMU_RECORD(ttbr0), MMU_DEFAULT_TTBR0(device)); /* The CP doesn't use the asid record, so poison it */ kgsl_sharedmem_writel(iommu->smmu_info, PREEMPT_SMMU_RECORD(asid), 0xdecafbad); kgsl_sharedmem_writel(iommu->smmu_info, PREEMPT_SMMU_RECORD(context_idr), 0); kgsl_regwrite(device, GEN8_CP_CONTEXT_SWITCH_SMMU_INFO_LO, lower_32_bits(iommu->smmu_info->gpuaddr)); kgsl_regwrite(device, GEN8_CP_CONTEXT_SWITCH_SMMU_INFO_HI, upper_32_bits(iommu->smmu_info->gpuaddr)); } FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { kgsl_sharedmem_writel(rb->preemption_desc, PREEMPT_RECORD(rptr), 0); kgsl_sharedmem_writel(rb->preemption_desc, PREEMPT_RECORD(wptr), 0); adreno_ringbuffer_set_pagetable(device, rb, device->mmu.defaultpagetable); clear_bit(ADRENO_RB_SET_PSEUDO_DONE, &rb->flags); } } static void reset_rb_preempt_record(struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb) { memset(rb->preemption_desc->hostptr, 0x0, rb->preemption_desc->size); kgsl_sharedmem_writel(rb->preemption_desc, PREEMPT_RECORD(magic), GEN8_CP_CTXRECORD_MAGIC_REF); kgsl_sharedmem_writel(rb->preemption_desc, PREEMPT_RECORD(cntl), GEN8_CP_RB_CNTL_DEFAULT); kgsl_sharedmem_writeq(rb->preemption_desc, PREEMPT_RECORD(rptr_addr), SCRATCH_RB_GPU_ADDR( KGSL_DEVICE(adreno_dev), rb->id, rptr)); kgsl_sharedmem_writeq(rb->preemption_desc, PREEMPT_RECORD(rbase), rb->buffer_desc->gpuaddr); kgsl_sharedmem_writeq(rb->preemption_desc, PREEMPT_RECORD(bv_rptr_addr), SCRATCH_RB_GPU_ADDR( KGSL_DEVICE(adreno_dev), rb->id, bv_rptr)); } void gen8_reset_preempt_records(struct adreno_device *adreno_dev) { int i; struct adreno_ringbuffer *rb; if (!adreno_is_preemption_enabled(adreno_dev)) return; FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { reset_rb_preempt_record(adreno_dev, rb); } } static int gen8_preemption_ringbuffer_init(struct adreno_device *adreno_dev, struct adreno_ringbuffer *rb) { struct kgsl_device *device = KGSL_DEVICE(adreno_dev); const struct adreno_gen8_core *gen8_core = to_gen8_core(adreno_dev); u64 ctxt_record_size = GEN8_CP_CTXRECORD_SIZE_IN_BYTES; int ret; if (gen8_core->ctxt_record_size) ctxt_record_size = gen8_core->ctxt_record_size; ret = adreno_allocate_global(device, &rb->preemption_desc, ctxt_record_size, SZ_16K, 0, KGSL_MEMDESC_PRIVILEGED, "preemption_desc"); if (ret) return ret; ret = adreno_allocate_global(device, &rb->secure_preemption_desc, ctxt_record_size, 0, KGSL_MEMFLAGS_SECURE, KGSL_MEMDESC_PRIVILEGED, "secure_preemption_desc"); if (ret) return ret; ret = adreno_allocate_global(device, &rb->perfcounter_save_restore_desc, GEN8_CP_PERFCOUNTER_SAVE_RESTORE_SIZE, 0, 0, KGSL_MEMDESC_PRIVILEGED, "perfcounter_save_restore_desc"); if (ret) return ret; reset_rb_preempt_record(adreno_dev, rb); return 0; } int gen8_preemption_init(struct adreno_device *adreno_dev) { u32 flags = ADRENO_FEATURE(adreno_dev, ADRENO_APRIV) ? KGSL_MEMDESC_PRIVILEGED : 0; struct kgsl_device *device = KGSL_DEVICE(adreno_dev); struct kgsl_iommu *iommu = KGSL_IOMMU(device); struct adreno_preemption *preempt = &adreno_dev->preempt; struct adreno_ringbuffer *rb; int ret; u32 i; /* We are dependent on IOMMU to make preemption go on the CP side */ if (kgsl_mmu_get_mmutype(device) != KGSL_MMU_TYPE_IOMMU) { ret = -ENODEV; goto done; } INIT_WORK(&preempt->work, _gen8_preemption_worker); /* Allocate mem for storing preemption switch record */ FOR_EACH_RINGBUFFER(adreno_dev, rb, i) { ret = gen8_preemption_ringbuffer_init(adreno_dev, rb); if (ret) goto done; } ret = adreno_allocate_global(device, &preempt->scratch, PAGE_SIZE, 0, 0, flags, "preempt_scratch"); if (ret) goto done; /* Allocate mem for storing preemption smmu record */ if (kgsl_mmu_is_perprocess(&device->mmu)) { ret = adreno_allocate_global(device, &iommu->smmu_info, PAGE_SIZE, 0, KGSL_MEMFLAGS_GPUREADONLY, KGSL_MEMDESC_PRIVILEGED, "smmu_info"); if (ret) goto done; } return 0; done: clear_bit(ADRENO_DEVICE_PREEMPTION, &adreno_dev->priv); return ret; } int gen8_preemption_context_init(struct kgsl_context *context) { struct kgsl_device *device = context->device; struct adreno_device *adreno_dev = ADRENO_DEVICE(device); u64 flags = 0; if (!adreno_preemption_feature_set(adreno_dev)) return 0; if (context->flags & KGSL_CONTEXT_SECURE) flags |= KGSL_MEMFLAGS_SECURE; if (is_compat_task()) flags |= KGSL_MEMFLAGS_FORCE_32BIT; /* * gpumem_alloc_entry takes an extra refcount. Put it only when * destroying the context to keep the context record valid */ context->user_ctxt_record = gpumem_alloc_entry(context->dev_priv, GEN8_CP_CTXRECORD_USER_RESTORE_SIZE, flags); if (IS_ERR(context->user_ctxt_record)) { int ret = PTR_ERR(context->user_ctxt_record); context->user_ctxt_record = NULL; return ret; } return 0; }