diff --git a/Documentation/ABI/testing/sysfs-kernel-warn_count b/Documentation/ABI/testing/sysfs-kernel-warn_count index 08f083d2fd51..90a029813717 100644 --- a/Documentation/ABI/testing/sysfs-kernel-warn_count +++ b/Documentation/ABI/testing/sysfs-kernel-warn_count @@ -1,4 +1,4 @@ -What: /sys/kernel/oops_count +What: /sys/kernel/warn_count Date: November 2022 KernelVersion: 6.2.0 Contact: Linux Kernel Hardening List diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml index 5b28d2d51327..7e56337d8edc 100644 --- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml +++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml @@ -38,6 +38,7 @@ properties: - fsl,imx8mq-sai - fsl,imx8qm-sai - fsl,imx8ulp-sai + - fsl,imx93-sai - fsl,vf610-sai reg: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h index f50e3ba4d7a5..0040deaf8a83 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "amdgpu_sync.h" @@ -65,6 +66,7 @@ struct kgd_mem { struct mutex lock; struct amdgpu_bo *bo; struct dma_buf *dmabuf; + struct hmm_range *range; struct list_head attachments; /* protected by amdkfd_process_info.lock */ struct ttm_validate_buffer validate_list; @@ -75,7 +77,7 @@ struct kgd_mem { uint32_t alloc_flags; - atomic_t invalid; + uint32_t invalid; struct amdkfd_process_info *process_info; struct amdgpu_sync sync; @@ -131,7 +133,8 @@ struct amdkfd_process_info { struct amdgpu_amdkfd_fence *eviction_fence; /* MMU-notifier related fields */ - atomic_t evicted_bos; + struct mutex notifier_lock; + uint32_t evicted_bos; struct delayed_work restore_userptr_work; struct pid *pid; bool block_mmu_notifications; @@ -180,7 +183,8 @@ int kfd_debugfs_kfd_mem_limits(struct seq_file *m, void *data); bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm); struct amdgpu_amdkfd_fence *to_amdgpu_amdkfd_fence(struct dma_fence *f); int amdgpu_amdkfd_remove_fence_on_pt_pd_bos(struct amdgpu_bo *bo); -int amdgpu_amdkfd_evict_userptr(struct kgd_mem *mem, struct mm_struct *mm); +int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni, + unsigned long cur_seq, struct kgd_mem *mem); #else static inline bool amdkfd_fence_check_mm(struct dma_fence *f, struct mm_struct *mm) @@ -201,7 +205,8 @@ int amdgpu_amdkfd_remove_fence_on_pt_pd_bos(struct amdgpu_bo *bo) } static inline -int amdgpu_amdkfd_evict_userptr(struct kgd_mem *mem, struct mm_struct *mm) +int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni, + unsigned long cur_seq, struct kgd_mem *mem) { return 0; } @@ -265,8 +270,10 @@ int amdgpu_amdkfd_get_pcie_bandwidth_mbytes(struct amdgpu_device *adev, bool is_ (&((struct amdgpu_fpriv *) \ ((struct drm_file *)(drm_priv))->driver_priv)->vm) +int amdgpu_amdkfd_gpuvm_set_vm_pasid(struct amdgpu_device *adev, + struct file *filp, u32 pasid); int amdgpu_amdkfd_gpuvm_acquire_process_vm(struct amdgpu_device *adev, - struct file *filp, u32 pasid, + struct file *filp, void **process_info, struct dma_fence **ef); void amdgpu_amdkfd_gpuvm_release_process_vm(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c index 8782916e64a0..b15091d8310d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gpuvm.c @@ -964,7 +964,9 @@ static int init_user_pages(struct kgd_mem *mem, uint64_t user_addr, * later stage when it is scheduled by another ioctl called by * CRIU master process for the target pid for restore. */ - atomic_inc(&mem->invalid); + mutex_lock(&process_info->notifier_lock); + mem->invalid++; + mutex_unlock(&process_info->notifier_lock); mutex_unlock(&process_info->lock); return 0; } @@ -1301,6 +1303,7 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info, return -ENOMEM; mutex_init(&info->lock); + mutex_init(&info->notifier_lock); INIT_LIST_HEAD(&info->vm_list_head); INIT_LIST_HEAD(&info->kfd_bo_list); INIT_LIST_HEAD(&info->userptr_valid_list); @@ -1317,7 +1320,6 @@ static int init_kfd_vm(struct amdgpu_vm *vm, void **process_info, } info->pid = get_task_pid(current->group_leader, PIDTYPE_PID); - atomic_set(&info->evicted_bos, 0); INIT_DELAYED_WORK(&info->restore_userptr_work, amdgpu_amdkfd_restore_userptr_worker); @@ -1372,6 +1374,7 @@ reserve_pd_fail: put_pid(info->pid); create_evict_fence_fail: mutex_destroy(&info->lock); + mutex_destroy(&info->notifier_lock); kfree(info); } return ret; @@ -1426,8 +1429,36 @@ static void amdgpu_amdkfd_gpuvm_unpin_bo(struct amdgpu_bo *bo) amdgpu_bo_unreserve(bo); } +int amdgpu_amdkfd_gpuvm_set_vm_pasid(struct amdgpu_device *adev, + struct file *filp, u32 pasid) + +{ + struct amdgpu_fpriv *drv_priv; + struct amdgpu_vm *avm; + int ret; + + ret = amdgpu_file_to_fpriv(filp, &drv_priv); + if (ret) + return ret; + avm = &drv_priv->vm; + + /* Free the original amdgpu allocated pasid, + * will be replaced with kfd allocated pasid. + */ + if (avm->pasid) { + amdgpu_pasid_free(avm->pasid); + amdgpu_vm_set_pasid(adev, avm, 0); + } + + ret = amdgpu_vm_set_pasid(adev, avm, pasid); + if (ret) + return ret; + + return 0; +} + int amdgpu_amdkfd_gpuvm_acquire_process_vm(struct amdgpu_device *adev, - struct file *filp, u32 pasid, + struct file *filp, void **process_info, struct dma_fence **ef) { @@ -1444,22 +1475,11 @@ int amdgpu_amdkfd_gpuvm_acquire_process_vm(struct amdgpu_device *adev, if (avm->process_info) return -EINVAL; - /* Free the original amdgpu allocated pasid, - * will be replaced with kfd allocated pasid. - */ - if (avm->pasid) { - amdgpu_pasid_free(avm->pasid); - amdgpu_vm_set_pasid(adev, avm, 0); - } - /* Convert VM into a compute VM */ ret = amdgpu_vm_make_compute(adev, avm); if (ret) return ret; - ret = amdgpu_vm_set_pasid(adev, avm, pasid); - if (ret) - return ret; /* Initialize KFD part of the VM and process info */ ret = init_kfd_vm(avm, process_info, ef); if (ret) @@ -1496,6 +1516,7 @@ void amdgpu_amdkfd_gpuvm_destroy_cb(struct amdgpu_device *adev, cancel_delayed_work_sync(&process_info->restore_userptr_work); put_pid(process_info->pid); mutex_destroy(&process_info->lock); + mutex_destroy(&process_info->notifier_lock); kfree(process_info); } } @@ -1548,7 +1569,9 @@ int amdgpu_amdkfd_criu_resume(void *p) mutex_lock(&pinfo->lock); pr_debug("scheduling work\n"); - atomic_inc(&pinfo->evicted_bos); + mutex_lock(&pinfo->notifier_lock); + pinfo->evicted_bos++; + mutex_unlock(&pinfo->notifier_lock); if (!READ_ONCE(pinfo->block_mmu_notifications)) { ret = -EINVAL; goto out_unlock; @@ -1773,8 +1796,13 @@ int amdgpu_amdkfd_gpuvm_free_memory_of_gpu( list_del(&bo_list_entry->head); mutex_unlock(&process_info->lock); - /* No more MMU notifiers */ - amdgpu_hmm_unregister(mem->bo); + /* Cleanup user pages and MMU notifiers */ + if (amdgpu_ttm_tt_get_usermm(mem->bo->tbo.ttm)) { + amdgpu_hmm_unregister(mem->bo); + mutex_lock(&process_info->notifier_lock); + amdgpu_ttm_tt_discard_user_pages(mem->bo->tbo.ttm, mem->range); + mutex_unlock(&process_info->notifier_lock); + } ret = reserve_bo_and_cond_vms(mem, NULL, BO_VM_ALL, &ctx); if (unlikely(ret)) @@ -1864,6 +1892,16 @@ int amdgpu_amdkfd_gpuvm_map_memory_to_gpu( */ mutex_lock(&mem->process_info->lock); + /* Lock notifier lock. If we find an invalid userptr BO, we can be + * sure that the MMU notifier is no longer running + * concurrently and the queues are actually stopped + */ + if (amdgpu_ttm_tt_get_usermm(bo->tbo.ttm)) { + mutex_lock(&mem->process_info->notifier_lock); + is_invalid_userptr = !!mem->invalid; + mutex_unlock(&mem->process_info->notifier_lock); + } + mutex_lock(&mem->lock); domain = mem->domain; @@ -2241,34 +2279,38 @@ int amdgpu_amdkfd_gpuvm_import_dmabuf(struct amdgpu_device *adev, * * Runs in MMU notifier, may be in RECLAIM_FS context. This means it * cannot do any memory allocations, and cannot take any locks that - * are held elsewhere while allocating memory. Therefore this is as - * simple as possible, using atomic counters. + * are held elsewhere while allocating memory. * * It doesn't do anything to the BO itself. The real work happens in * restore, where we get updated page addresses. This function only * ensures that GPU access to the BO is stopped. */ -int amdgpu_amdkfd_evict_userptr(struct kgd_mem *mem, - struct mm_struct *mm) +int amdgpu_amdkfd_evict_userptr(struct mmu_interval_notifier *mni, + unsigned long cur_seq, struct kgd_mem *mem) { struct amdkfd_process_info *process_info = mem->process_info; - int evicted_bos; int r = 0; - /* Do not process MMU notifications until stage-4 IOCTL is received */ + /* Do not process MMU notifications during CRIU restore until + * KFD_CRIU_OP_RESUME IOCTL is received + */ if (READ_ONCE(process_info->block_mmu_notifications)) return 0; - atomic_inc(&mem->invalid); - evicted_bos = atomic_inc_return(&process_info->evicted_bos); - if (evicted_bos == 1) { + mutex_lock(&process_info->notifier_lock); + mmu_interval_set_seq(mni, cur_seq); + + mem->invalid++; + if (++process_info->evicted_bos == 1) { /* First eviction, stop the queues */ - r = kgd2kfd_quiesce_mm(mm, KFD_QUEUE_EVICTION_TRIGGER_USERPTR); + r = kgd2kfd_quiesce_mm(mni->mm, + KFD_QUEUE_EVICTION_TRIGGER_USERPTR); if (r) pr_err("Failed to quiesce KFD\n"); schedule_delayed_work(&process_info->restore_userptr_work, msecs_to_jiffies(AMDGPU_USERPTR_RESTORE_DELAY_MS)); } + mutex_unlock(&process_info->notifier_lock); return r; } @@ -2285,54 +2327,58 @@ static int update_invalid_user_pages(struct amdkfd_process_info *process_info, struct kgd_mem *mem, *tmp_mem; struct amdgpu_bo *bo; struct ttm_operation_ctx ctx = { false, false }; - int invalid, ret; + uint32_t invalid; + int ret = 0; - /* Move all invalidated BOs to the userptr_inval_list and - * release their user pages by migration to the CPU domain - */ + mutex_lock(&process_info->notifier_lock); + + /* Move all invalidated BOs to the userptr_inval_list */ list_for_each_entry_safe(mem, tmp_mem, &process_info->userptr_valid_list, - validate_list.head) { - if (!atomic_read(&mem->invalid)) - continue; /* BO is still valid */ - - bo = mem->bo; - - if (amdgpu_bo_reserve(bo, true)) - return -EAGAIN; - amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU); - ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); - amdgpu_bo_unreserve(bo); - if (ret) { - pr_err("%s: Failed to invalidate userptr BO\n", - __func__); - return -EAGAIN; - } - - list_move_tail(&mem->validate_list.head, - &process_info->userptr_inval_list); - } - - if (list_empty(&process_info->userptr_inval_list)) - return 0; /* All evicted userptr BOs were freed */ + validate_list.head) + if (mem->invalid) + list_move_tail(&mem->validate_list.head, + &process_info->userptr_inval_list); /* Go through userptr_inval_list and update any invalid user_pages */ list_for_each_entry(mem, &process_info->userptr_inval_list, validate_list.head) { - struct hmm_range *range; - - invalid = atomic_read(&mem->invalid); + invalid = mem->invalid; if (!invalid) /* BO hasn't been invalidated since the last - * revalidation attempt. Keep its BO list. + * revalidation attempt. Keep its page list. */ continue; bo = mem->bo; + amdgpu_ttm_tt_discard_user_pages(bo->tbo.ttm, mem->range); + mem->range = NULL; + + /* BO reservations and getting user pages (hmm_range_fault) + * must happen outside the notifier lock + */ + mutex_unlock(&process_info->notifier_lock); + + /* Move the BO to system (CPU) domain if necessary to unmap + * and free the SG table + */ + if (bo->tbo.resource->mem_type != TTM_PL_SYSTEM) { + if (amdgpu_bo_reserve(bo, true)) + return -EAGAIN; + amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU); + ret = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); + amdgpu_bo_unreserve(bo); + if (ret) { + pr_err("%s: Failed to invalidate userptr BO\n", + __func__); + return -EAGAIN; + } + } + /* Get updated user pages */ ret = amdgpu_ttm_tt_get_user_pages(bo, bo->tbo.ttm->pages, - &range); + &mem->range); if (ret) { pr_debug("Failed %d to get user pages\n", ret); @@ -2345,30 +2391,32 @@ static int update_invalid_user_pages(struct amdkfd_process_info *process_info, */ if (ret != -EFAULT) return ret; - } else { - /* - * FIXME: Cannot ignore the return code, must hold - * notifier_lock - */ - amdgpu_ttm_tt_get_user_pages_done(bo->tbo.ttm, range); + ret = 0; } + mutex_lock(&process_info->notifier_lock); + /* Mark the BO as valid unless it was invalidated * again concurrently. */ - if (atomic_cmpxchg(&mem->invalid, invalid, 0) != invalid) - return -EAGAIN; + if (mem->invalid != invalid) { + ret = -EAGAIN; + goto unlock_out; + } + mem->invalid = 0; } - return 0; +unlock_out: + mutex_unlock(&process_info->notifier_lock); + + return ret; } /* Validate invalid userptr BOs * - * Validates BOs on the userptr_inval_list, and moves them back to the - * userptr_valid_list. Also updates GPUVM page tables with new page - * addresses and waits for the page table updates to complete. + * Validates BOs on the userptr_inval_list. Also updates GPUVM page tables + * with new page addresses and waits for the page table updates to complete. */ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) { @@ -2439,9 +2487,6 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) } } - list_move_tail(&mem->validate_list.head, - &process_info->userptr_valid_list); - /* Update mapping. If the BO was not validated * (because we couldn't get user pages), this will * clear the page table entries, which will result in @@ -2457,7 +2502,9 @@ static int validate_invalid_user_pages(struct amdkfd_process_info *process_info) if (ret) { pr_err("%s: update PTE failed\n", __func__); /* make sure this gets validated again */ - atomic_inc(&mem->invalid); + mutex_lock(&process_info->notifier_lock); + mem->invalid++; + mutex_unlock(&process_info->notifier_lock); goto unreserve_out; } } @@ -2477,6 +2524,36 @@ out_no_mem: return ret; } +/* Confirm that all user pages are valid while holding the notifier lock + * + * Moves valid BOs from the userptr_inval_list back to userptr_val_list. + */ +static int confirm_valid_user_pages_locked(struct amdkfd_process_info *process_info) +{ + struct kgd_mem *mem, *tmp_mem; + int ret = 0; + + list_for_each_entry_safe(mem, tmp_mem, + &process_info->userptr_inval_list, + validate_list.head) { + bool valid = amdgpu_ttm_tt_get_user_pages_done( + mem->bo->tbo.ttm, mem->range); + + mem->range = NULL; + if (!valid) { + WARN(!mem->invalid, "Invalid BO not marked invalid"); + ret = -EAGAIN; + continue; + } + WARN(mem->invalid, "Valid BO is marked invalid"); + + list_move_tail(&mem->validate_list.head, + &process_info->userptr_valid_list); + } + + return ret; +} + /* Worker callback to restore evicted userptr BOs * * Tries to update and validate all userptr BOs. If successful and no @@ -2491,9 +2568,11 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work) restore_userptr_work); struct task_struct *usertask; struct mm_struct *mm; - int evicted_bos; + uint32_t evicted_bos; - evicted_bos = atomic_read(&process_info->evicted_bos); + mutex_lock(&process_info->notifier_lock); + evicted_bos = process_info->evicted_bos; + mutex_unlock(&process_info->notifier_lock); if (!evicted_bos) return; @@ -2516,9 +2595,6 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work) * and we can just restart the queues. */ if (!list_empty(&process_info->userptr_inval_list)) { - if (atomic_read(&process_info->evicted_bos) != evicted_bos) - goto unlock_out; /* Concurrent eviction, try again */ - if (validate_invalid_user_pages(process_info)) goto unlock_out; } @@ -2527,10 +2603,17 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work) * be a first eviction that calls quiesce_mm. The eviction * reference counting inside KFD will handle this case. */ - if (atomic_cmpxchg(&process_info->evicted_bos, evicted_bos, 0) != - evicted_bos) - goto unlock_out; - evicted_bos = 0; + mutex_lock(&process_info->notifier_lock); + if (process_info->evicted_bos != evicted_bos) + goto unlock_notifier_out; + + if (confirm_valid_user_pages_locked(process_info)) { + WARN(1, "User pages unexpectedly invalid"); + goto unlock_notifier_out; + } + + process_info->evicted_bos = evicted_bos = 0; + if (kgd2kfd_resume_mm(mm)) { pr_err("%s: Failed to resume KFD\n", __func__); /* No recovery from this failure. Probably the CP is @@ -2538,6 +2621,8 @@ static void amdgpu_amdkfd_restore_userptr_worker(struct work_struct *work) */ } +unlock_notifier_out: + mutex_unlock(&process_info->notifier_lock); unlock_out: mutex_unlock(&process_info->lock); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index cfa411c12072..afe6af9c0138 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -3016,14 +3016,15 @@ static int amdgpu_device_ip_suspend_phase2(struct amdgpu_device *adev) continue; } - /* skip suspend of gfx and psp for S0ix + /* skip suspend of gfx/mes and psp for S0ix * gfx is in gfxoff state, so on resume it will exit gfxoff just * like at runtime. PSP is also part of the always on hardware * so no need to suspend it. */ if (adev->in_s0ix && (adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_PSP || - adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX)) + adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_GFX || + adev->ip_blocks[i].version->type == AMD_IP_BLOCK_TYPE_MES)) continue; /* XXX handle errors */ @@ -4112,6 +4113,11 @@ int amdgpu_device_suspend(struct drm_device *dev, bool fbcon) adev->in_suspend = true; + /* Evict the majority of BOs before grabbing the full access */ + r = amdgpu_device_evict_resources(adev); + if (r) + return r; + if (amdgpu_sriov_vf(adev)) { amdgpu_virt_fini_data_exchange(adev); r = amdgpu_virt_request_full_gpu(adev, false); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 7383272c6a3a..b4f2d61ea0d5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -2039,6 +2039,15 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, "See modparam exp_hw_support\n"); return -ENODEV; } + /* differentiate between P10 and P11 asics with the same DID */ + if (pdev->device == 0x67FF && + (pdev->revision == 0xE3 || + pdev->revision == 0xE7 || + pdev->revision == 0xF3 || + pdev->revision == 0xF7)) { + flags &= ~AMD_ASIC_MASK; + flags |= CHIP_POLARIS10; + } /* Due to hardware bugs, S/G Display on raven requires a 1:1 IOMMU mapping, * however, SME requires an indirect IOMMU mapping because the encryption @@ -2108,12 +2117,12 @@ static int amdgpu_pci_probe(struct pci_dev *pdev, pci_set_drvdata(pdev, ddev); - ret = amdgpu_driver_load_kms(adev, ent->driver_data); + ret = amdgpu_driver_load_kms(adev, flags); if (ret) goto err_pci; retry_init: - ret = drm_dev_register(ddev, ent->driver_data); + ret = drm_dev_register(ddev, flags); if (ret == -EAGAIN && ++retry <= 3) { DRM_INFO("retry init %d\n", retry); /* Don't request EX mode too frequently which is attacking */ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c index 2c38ac7bc643..4620c4712ce3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fru_eeprom.c @@ -64,7 +64,8 @@ static bool is_fru_eeprom_supported(struct amdgpu_device *adev, u32 *fru_addr) sizeof(atom_ctx->vbios_version)) || strnstr(atom_ctx->vbios_version, "D163", sizeof(atom_ctx->vbios_version))) { - *fru_addr = FRU_EEPROM_MADDR_6; + if (fru_addr) + *fru_addr = FRU_EEPROM_MADDR_6; return true; } else { return false; @@ -83,7 +84,8 @@ static bool is_fru_eeprom_supported(struct amdgpu_device *adev, u32 *fru_addr) sizeof(atom_ctx->vbios_version))) { return false; } else { - *fru_addr = FRU_EEPROM_MADDR_6; + if (fru_addr) + *fru_addr = FRU_EEPROM_MADDR_6; return true; } } else { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index bee38c326537..bb7350ea1d75 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -113,7 +113,7 @@ int amdgpu_gem_object_create(struct amdgpu_device *adev, unsigned long size, bp.resv = resv; bp.preferred_domain = initial_domain; bp.flags = flags; - bp.domain = initial_domain | AMDGPU_GEM_DOMAIN_CPU; + bp.domain = initial_domain; bp.bo_ptr_size = sizeof(struct amdgpu_bo); r = amdgpu_bo_create_user(adev, &bp, &ubo); @@ -332,10 +332,20 @@ int amdgpu_gem_create_ioctl(struct drm_device *dev, void *data, } initial_domain = (u32)(0xffffffff & args->in.domains); +retry: r = amdgpu_gem_object_create(adev, size, args->in.alignment, - initial_domain, flags, ttm_bo_type_device, - resv, &gobj); + initial_domain, + flags, ttm_bo_type_device, resv, &gobj); if (r && r != -ERESTARTSYS) { + if (flags & AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED) { + flags &= ~AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED; + goto retry; + } + + if (initial_domain == AMDGPU_GEM_DOMAIN_VRAM) { + initial_domain |= AMDGPU_GEM_DOMAIN_GTT; + goto retry; + } DRM_DEBUG("Failed to allocate GEM object (%llu, %d, %llu, %d)\n", size, initial_domain, args->in.alignment, r); } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c index 65715cb395d8..2dadcfe43d03 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.c @@ -105,17 +105,11 @@ static bool amdgpu_hmm_invalidate_hsa(struct mmu_interval_notifier *mni, unsigned long cur_seq) { struct amdgpu_bo *bo = container_of(mni, struct amdgpu_bo, notifier); - struct amdgpu_device *adev = amdgpu_ttm_adev(bo->tbo.bdev); if (!mmu_notifier_range_blockable(range)) return false; - mutex_lock(&adev->notifier_lock); - - mmu_interval_set_seq(mni, cur_seq); - - amdgpu_amdkfd_evict_userptr(bo->kfd_bo, bo->notifier.mm); - mutex_unlock(&adev->notifier_lock); + amdgpu_amdkfd_evict_userptr(mni, cur_seq, bo->kfd_bo); return true; } @@ -244,9 +238,9 @@ out_free_range: return r; } -int amdgpu_hmm_range_get_pages_done(struct hmm_range *hmm_range) +bool amdgpu_hmm_range_get_pages_done(struct hmm_range *hmm_range) { - int r; + bool r; r = mmu_interval_read_retry(hmm_range->notifier, hmm_range->notifier_seq); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h index 13ed94d3b01b..e2edcd010ccc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_hmm.h @@ -29,12 +29,13 @@ #include #include #include +#include int amdgpu_hmm_range_get_pages(struct mmu_interval_notifier *notifier, uint64_t start, uint64_t npages, bool readonly, void *owner, struct page **pages, struct hmm_range **phmm_range); -int amdgpu_hmm_range_get_pages_done(struct hmm_range *hmm_range); +bool amdgpu_hmm_range_get_pages_done(struct hmm_range *hmm_range); #if defined(CONFIG_HMM_MIRROR) int amdgpu_hmm_register(struct amdgpu_bo *bo, unsigned long addr); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c index 2a9a2593dc18..fcb711a11a5b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.c @@ -165,6 +165,26 @@ bool amdgpu_vmid_had_gpu_reset(struct amdgpu_device *adev, atomic_read(&adev->gpu_reset_counter); } +/* Check if we need to switch to another set of resources */ +static bool amdgpu_vmid_gds_switch_needed(struct amdgpu_vmid *id, + struct amdgpu_job *job) +{ + return id->gds_base != job->gds_base || + id->gds_size != job->gds_size || + id->gws_base != job->gws_base || + id->gws_size != job->gws_size || + id->oa_base != job->oa_base || + id->oa_size != job->oa_size; +} + +/* Check if the id is compatible with the job */ +static bool amdgpu_vmid_compatible(struct amdgpu_vmid *id, + struct amdgpu_job *job) +{ + return id->pd_gpu_addr == job->vm_pd_addr && + !amdgpu_vmid_gds_switch_needed(id, job); +} + /** * amdgpu_vmid_grab_idle - grab idle VMID * @@ -258,14 +278,15 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm, { struct amdgpu_device *adev = ring->adev; unsigned vmhub = ring->funcs->vmhub; + struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; uint64_t fence_context = adev->fence_context + ring->idx; bool needs_flush = vm->use_cpu_for_update; uint64_t updates = amdgpu_vm_tlb_seq(vm); int r; - *id = vm->reserved_vmid[vmhub]; + *id = id_mgr->reserved; if ((*id)->owner != vm->immediate.fence_context || - (*id)->pd_gpu_addr != job->vm_pd_addr || + !amdgpu_vmid_compatible(*id, job) || (*id)->flushed_updates < updates || !(*id)->last_flush || ((*id)->last_flush->context != fence_context && @@ -294,8 +315,8 @@ static int amdgpu_vmid_grab_reserved(struct amdgpu_vm *vm, if (r) return r; - (*id)->flushed_updates = updates; job->vm_needs_flush = needs_flush; + job->spm_update_needed = true; return 0; } @@ -333,7 +354,7 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm, if ((*id)->owner != vm->immediate.fence_context) continue; - if ((*id)->pd_gpu_addr != job->vm_pd_addr) + if (!amdgpu_vmid_compatible(*id, job)) continue; if (!(*id)->last_flush || @@ -355,7 +376,6 @@ static int amdgpu_vmid_grab_used(struct amdgpu_vm *vm, if (r) return r; - (*id)->flushed_updates = updates; job->vm_needs_flush |= needs_flush; return 0; } @@ -408,22 +428,30 @@ int amdgpu_vmid_grab(struct amdgpu_vm *vm, struct amdgpu_ring *ring, if (r) goto error; - id->flushed_updates = amdgpu_vm_tlb_seq(vm); job->vm_needs_flush = true; } list_move_tail(&id->list, &id_mgr->ids_lru); } - id->pd_gpu_addr = job->vm_pd_addr; - id->owner = vm->immediate.fence_context; - + job->gds_switch_needed = amdgpu_vmid_gds_switch_needed(id, job); if (job->vm_needs_flush) { + id->flushed_updates = amdgpu_vm_tlb_seq(vm); dma_fence_put(id->last_flush); id->last_flush = NULL; } job->vmid = id - id_mgr->ids; job->pasid = vm->pasid; + + id->gds_base = job->gds_base; + id->gds_size = job->gds_size; + id->gws_base = job->gws_base; + id->gws_size = job->gws_size; + id->oa_base = job->oa_base; + id->oa_size = job->oa_size; + id->pd_gpu_addr = job->vm_pd_addr; + id->owner = vm->immediate.fence_context; + trace_amdgpu_vm_grab_id(vm, ring, job); error: @@ -435,31 +463,27 @@ int amdgpu_vmid_alloc_reserved(struct amdgpu_device *adev, struct amdgpu_vm *vm, unsigned vmhub) { - struct amdgpu_vmid_mgr *id_mgr; - struct amdgpu_vmid *idle; - int r = 0; + struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; - id_mgr = &adev->vm_manager.id_mgr[vmhub]; mutex_lock(&id_mgr->lock); if (vm->reserved_vmid[vmhub]) goto unlock; - if (atomic_inc_return(&id_mgr->reserved_vmid_num) > - AMDGPU_VM_MAX_RESERVED_VMID) { - DRM_ERROR("Over limitation of reserved vmid\n"); - atomic_dec(&id_mgr->reserved_vmid_num); - r = -EINVAL; - goto unlock; - } - /* Select the first entry VMID */ - idle = list_first_entry(&id_mgr->ids_lru, struct amdgpu_vmid, list); - list_del_init(&idle->list); - vm->reserved_vmid[vmhub] = idle; - mutex_unlock(&id_mgr->lock); - return 0; + ++id_mgr->reserved_use_count; + if (!id_mgr->reserved) { + struct amdgpu_vmid *id; + + id = list_first_entry(&id_mgr->ids_lru, struct amdgpu_vmid, + list); + /* Remove from normal round robin handling */ + list_del_init(&id->list); + id_mgr->reserved = id; + } + vm->reserved_vmid[vmhub] = true; + unlock: mutex_unlock(&id_mgr->lock); - return r; + return 0; } void amdgpu_vmid_free_reserved(struct amdgpu_device *adev, @@ -469,12 +493,12 @@ void amdgpu_vmid_free_reserved(struct amdgpu_device *adev, struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; mutex_lock(&id_mgr->lock); - if (vm->reserved_vmid[vmhub]) { - list_add(&vm->reserved_vmid[vmhub]->list, - &id_mgr->ids_lru); - vm->reserved_vmid[vmhub] = NULL; - atomic_dec(&id_mgr->reserved_vmid_num); + if (vm->reserved_vmid[vmhub] && + !--id_mgr->reserved_use_count) { + /* give the reserved ID back to normal round robin */ + list_add(&id_mgr->reserved->list, &id_mgr->ids_lru); } + vm->reserved_vmid[vmhub] = false; mutex_unlock(&id_mgr->lock); } @@ -541,7 +565,7 @@ void amdgpu_vmid_mgr_init(struct amdgpu_device *adev) mutex_init(&id_mgr->lock); INIT_LIST_HEAD(&id_mgr->ids_lru); - atomic_set(&id_mgr->reserved_vmid_num, 0); + id_mgr->reserved_use_count = 0; /* manage only VMIDs not used by KFD */ id_mgr->num_ids = adev->vm_manager.first_kfd_vmid; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h index 57efe61dceed..d1cc09b45da4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ids.h @@ -67,7 +67,8 @@ struct amdgpu_vmid_mgr { unsigned num_ids; struct list_head ids_lru; struct amdgpu_vmid ids[AMDGPU_NUM_VMID]; - atomic_t reserved_vmid_num; + struct amdgpu_vmid *reserved; + unsigned int reserved_use_count; }; int amdgpu_pasid_alloc(unsigned int bits); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h index a372802ea4e0..52f2e313ea17 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_job.h @@ -53,6 +53,8 @@ struct amdgpu_job { uint32_t preamble_status; uint32_t preemption_status; bool vm_needs_flush; + bool gds_switch_needed; + bool spm_update_needed; uint64_t vm_pd_addr; unsigned vmid; unsigned pasid; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c index 919bbea2e3ac..4e684c2afc70 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.c @@ -346,17 +346,16 @@ int amdgpu_bo_create_kernel(struct amdgpu_device *adev, * @adev: amdgpu device object * @offset: offset of the BO * @size: size of the BO - * @domain: where to place it * @bo_ptr: used to initialize BOs in structures * @cpu_addr: optional CPU address mapping * - * Creates a kernel BO at a specific offset in the address space of the domain. + * Creates a kernel BO at a specific offset in VRAM. * * Returns: * 0 on success, negative error code otherwise. */ int amdgpu_bo_create_kernel_at(struct amdgpu_device *adev, - uint64_t offset, uint64_t size, uint32_t domain, + uint64_t offset, uint64_t size, struct amdgpu_bo **bo_ptr, void **cpu_addr) { struct ttm_operation_ctx ctx = { false, false }; @@ -366,8 +365,9 @@ int amdgpu_bo_create_kernel_at(struct amdgpu_device *adev, offset &= PAGE_MASK; size = ALIGN(size, PAGE_SIZE); - r = amdgpu_bo_create_reserved(adev, size, PAGE_SIZE, domain, bo_ptr, - NULL, cpu_addr); + r = amdgpu_bo_create_reserved(adev, size, PAGE_SIZE, + AMDGPU_GEM_DOMAIN_VRAM, bo_ptr, NULL, + cpu_addr); if (r) return r; @@ -422,6 +422,8 @@ void amdgpu_bo_free_kernel(struct amdgpu_bo **bo, u64 *gpu_addr, if (*bo == NULL) return; + WARN_ON(amdgpu_ttm_adev((*bo)->tbo.bdev)->in_suspend); + if (likely(amdgpu_bo_reserve(*bo, true) == 0)) { if (cpu_addr) amdgpu_bo_kunmap(*bo); @@ -446,27 +448,24 @@ static bool amdgpu_bo_validate_size(struct amdgpu_device *adev, /* * If GTT is part of requested domains the check must succeed to - * allow fall back to GTT + * allow fall back to GTT. */ if (domain & AMDGPU_GEM_DOMAIN_GTT) { man = ttm_manager_type(&adev->mman.bdev, TTM_PL_TT); - if (size < man->size) + if (man && size < man->size) return true; - else - goto fail; - } - - if (domain & AMDGPU_GEM_DOMAIN_VRAM) { + else if (!man) + WARN_ON_ONCE("GTT domain requested but GTT mem manager uninitialized"); + goto fail; + } else if (domain & AMDGPU_GEM_DOMAIN_VRAM) { man = ttm_manager_type(&adev->mman.bdev, TTM_PL_VRAM); - if (size < man->size) + if (man && size < man->size) return true; - else - goto fail; + goto fail; } - /* TODO add more domains checks, such as AMDGPU_GEM_DOMAIN_CPU */ return true; @@ -581,7 +580,11 @@ int amdgpu_bo_create(struct amdgpu_device *adev, bo->flags |= AMDGPU_GEM_CREATE_VRAM_WIPE_ON_RELEASE; bo->tbo.bdev = &adev->mman.bdev; - amdgpu_bo_placement_from_domain(bo, bp->domain); + if (bp->domain & (AMDGPU_GEM_DOMAIN_GWS | AMDGPU_GEM_DOMAIN_OA | + AMDGPU_GEM_DOMAIN_GDS)) + amdgpu_bo_placement_from_domain(bo, AMDGPU_GEM_DOMAIN_CPU); + else + amdgpu_bo_placement_from_domain(bo, bp->domain); if (bp->type == ttm_bo_type_kernel) bo->tbo.priority = 1; @@ -1506,7 +1509,8 @@ u64 amdgpu_bo_gpu_offset_no_check(struct amdgpu_bo *bo) uint32_t amdgpu_bo_get_preferred_domain(struct amdgpu_device *adev, uint32_t domain) { - if (domain == (AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT)) { + if ((domain == (AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT)) && + ((adev->asic_type == CHIP_CARRIZO) || (adev->asic_type == CHIP_STONEY))) { domain = AMDGPU_GEM_DOMAIN_VRAM; if (adev->gmc.real_vram_size <= AMDGPU_SG_THRESHOLD) domain = AMDGPU_GEM_DOMAIN_GTT; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h index 147b79c10cbb..93207badf83f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_object.h @@ -284,7 +284,7 @@ int amdgpu_bo_create_kernel(struct amdgpu_device *adev, u32 domain, struct amdgpu_bo **bo_ptr, u64 *gpu_addr, void **cpu_addr); int amdgpu_bo_create_kernel_at(struct amdgpu_device *adev, - uint64_t offset, uint64_t size, uint32_t domain, + uint64_t offset, uint64_t size, struct amdgpu_bo **bo_ptr, void **cpu_addr); int amdgpu_bo_create_user(struct amdgpu_device *adev, struct amdgpu_bo_param *bp, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index b4236572eae1..55e0284b2bdd 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -695,8 +695,19 @@ out_unlock: return r; } +/* amdgpu_ttm_tt_discard_user_pages - Discard range and pfn array allocations + */ +void amdgpu_ttm_tt_discard_user_pages(struct ttm_tt *ttm, + struct hmm_range *range) +{ + struct amdgpu_ttm_tt *gtt = (void *)ttm; + + if (gtt && gtt->userptr && range) + amdgpu_hmm_range_get_pages_done(range); +} + /* - * amdgpu_ttm_tt_userptr_range_done - stop HMM track the CPU page table change + * amdgpu_ttm_tt_get_user_pages_done - stop HMM track the CPU page table change * Check if the pages backing this ttm range have been invalidated * * Returns: true if pages are still valid @@ -714,10 +725,6 @@ bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm, WARN_ONCE(!range->hmm_pfns, "No user pages to check\n"); - /* - * FIXME: Must always hold notifier_lock for this, and must - * not ignore the return code. - */ return !amdgpu_hmm_range_get_pages_done(range); } #endif @@ -1569,7 +1576,6 @@ static int amdgpu_ttm_fw_reserve_vram_init(struct amdgpu_device *adev) return amdgpu_bo_create_kernel_at(adev, adev->mman.fw_vram_usage_start_offset, adev->mman.fw_vram_usage_size, - AMDGPU_GEM_DOMAIN_VRAM, &adev->mman.fw_vram_usage_reserved_bo, &adev->mman.fw_vram_usage_va); } @@ -1595,7 +1601,6 @@ static int amdgpu_ttm_drv_reserve_vram_init(struct amdgpu_device *adev) return amdgpu_bo_create_kernel_at(adev, adev->mman.drv_vram_usage_start_offset, adev->mman.drv_vram_usage_size, - AMDGPU_GEM_DOMAIN_VRAM, &adev->mman.drv_vram_usage_reserved_bo, &adev->mman.drv_vram_usage_va); } @@ -1676,7 +1681,6 @@ static int amdgpu_ttm_reserve_tmr(struct amdgpu_device *adev) ret = amdgpu_bo_create_kernel_at(adev, ctx->c2p_train_data_offset, ctx->train_data_size, - AMDGPU_GEM_DOMAIN_VRAM, &ctx->c2p_bo, NULL); if (ret) { @@ -1690,7 +1694,6 @@ static int amdgpu_ttm_reserve_tmr(struct amdgpu_device *adev) ret = amdgpu_bo_create_kernel_at(adev, adev->gmc.real_vram_size - adev->mman.discovery_tmr_size, adev->mman.discovery_tmr_size, - AMDGPU_GEM_DOMAIN_VRAM, &adev->mman.discovery_memory, NULL); if (ret) { @@ -1791,21 +1794,18 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) * avoid display artifacts while transitioning between pre-OS * and driver. */ r = amdgpu_bo_create_kernel_at(adev, 0, adev->mman.stolen_vga_size, - AMDGPU_GEM_DOMAIN_VRAM, &adev->mman.stolen_vga_memory, NULL); if (r) return r; r = amdgpu_bo_create_kernel_at(adev, adev->mman.stolen_vga_size, adev->mman.stolen_extended_size, - AMDGPU_GEM_DOMAIN_VRAM, &adev->mman.stolen_extended_memory, NULL); if (r) return r; r = amdgpu_bo_create_kernel_at(adev, adev->mman.stolen_reserved_offset, adev->mman.stolen_reserved_size, - AMDGPU_GEM_DOMAIN_VRAM, &adev->mman.stolen_reserved_memory, NULL); if (r) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h index b4d8ba2789f3..e2cd5894afc9 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.h @@ -159,6 +159,8 @@ uint64_t amdgpu_ttm_domain_start(struct amdgpu_device *adev, uint32_t type); #if IS_ENABLED(CONFIG_DRM_AMDGPU_USERPTR) int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo, struct page **pages, struct hmm_range **range); +void amdgpu_ttm_tt_discard_user_pages(struct ttm_tt *ttm, + struct hmm_range *range); bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm, struct hmm_range *range); #else @@ -168,6 +170,10 @@ static inline int amdgpu_ttm_tt_get_user_pages(struct amdgpu_bo *bo, { return -EPERM; } +static inline void amdgpu_ttm_tt_discard_user_pages(struct ttm_tt *ttm, + struct hmm_range *range) +{ +} static inline bool amdgpu_ttm_tt_get_user_pages_done(struct ttm_tt *ttm, struct hmm_range *range) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c index 15544f262ec1..2994b9db196f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -395,7 +395,6 @@ static void amdgpu_virt_ras_reserve_bps(struct amdgpu_device *adev) */ if (amdgpu_bo_create_kernel_at(adev, bp << AMDGPU_GPU_PAGE_SHIFT, AMDGPU_GPU_PAGE_SIZE, - AMDGPU_GEM_DOMAIN_VRAM, &bo, NULL)) DRM_DEBUG("RAS WARN: reserve vram for retired page %llx fail\n", bp); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index c05cff979004..dc379dc22c77 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -484,25 +484,20 @@ bool amdgpu_vm_need_pipeline_sync(struct amdgpu_ring *ring, struct amdgpu_device *adev = ring->adev; unsigned vmhub = ring->funcs->vmhub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; - struct amdgpu_vmid *id; - bool gds_switch_needed; - bool vm_flush_needed = job->vm_needs_flush || ring->has_compute_vm_bug; if (job->vmid == 0) return false; - id = &id_mgr->ids[job->vmid]; - gds_switch_needed = ring->funcs->emit_gds_switch && ( - id->gds_base != job->gds_base || - id->gds_size != job->gds_size || - id->gws_base != job->gws_base || - id->gws_size != job->gws_size || - id->oa_base != job->oa_base || - id->oa_size != job->oa_size); - if (amdgpu_vmid_had_gpu_reset(adev, id)) + if (job->vm_needs_flush || ring->has_compute_vm_bug) return true; - return vm_flush_needed || gds_switch_needed; + if (ring->funcs->emit_gds_switch && job->gds_switch_needed) + return true; + + if (amdgpu_vmid_had_gpu_reset(adev, &id_mgr->ids[job->vmid])) + return true; + + return false; } /** @@ -524,27 +519,20 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, unsigned vmhub = ring->funcs->vmhub; struct amdgpu_vmid_mgr *id_mgr = &adev->vm_manager.id_mgr[vmhub]; struct amdgpu_vmid *id = &id_mgr->ids[job->vmid]; - bool gds_switch_needed = ring->funcs->emit_gds_switch && ( - id->gds_base != job->gds_base || - id->gds_size != job->gds_size || - id->gws_base != job->gws_base || - id->gws_size != job->gws_size || - id->oa_base != job->oa_base || - id->oa_size != job->oa_size); + bool spm_update_needed = job->spm_update_needed; + bool gds_switch_needed = ring->funcs->emit_gds_switch && + job->gds_switch_needed; bool vm_flush_needed = job->vm_needs_flush; struct dma_fence *fence = NULL; bool pasid_mapping_needed = false; unsigned patch_offset = 0; - bool update_spm_vmid_needed = (job->vm && (job->vm->reserved_vmid[vmhub] != NULL)); int r; - if (update_spm_vmid_needed && adev->gfx.rlc.funcs->update_spm_vmid) - adev->gfx.rlc.funcs->update_spm_vmid(adev, job->vmid); - if (amdgpu_vmid_had_gpu_reset(adev, id)) { gds_switch_needed = true; vm_flush_needed = true; pasid_mapping_needed = true; + spm_update_needed = true; } mutex_lock(&id_mgr->lock); @@ -577,6 +565,17 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, if (pasid_mapping_needed) amdgpu_gmc_emit_pasid_mapping(ring, job->vmid, job->pasid); + if (spm_update_needed && adev->gfx.rlc.funcs->update_spm_vmid) + adev->gfx.rlc.funcs->update_spm_vmid(adev, job->vmid); + + if (!ring->is_mes_queue && ring->funcs->emit_gds_switch && + gds_switch_needed) { + amdgpu_ring_emit_gds_switch(ring, job->vmid, job->gds_base, + job->gds_size, job->gws_base, + job->gws_size, job->oa_base, + job->oa_size); + } + if (vm_flush_needed || pasid_mapping_needed) { r = amdgpu_fence_emit(ring, &fence, NULL, 0); if (r) @@ -601,20 +600,6 @@ int amdgpu_vm_flush(struct amdgpu_ring *ring, struct amdgpu_job *job, } dma_fence_put(fence); - if (!ring->is_mes_queue && ring->funcs->emit_gds_switch && - gds_switch_needed) { - id->gds_base = job->gds_base; - id->gds_size = job->gds_size; - id->gws_base = job->gws_base; - id->gws_size = job->gws_size; - id->oa_base = job->oa_base; - id->oa_size = job->oa_size; - amdgpu_ring_emit_gds_switch(ring, job->vmid, job->gds_base, - job->gds_size, job->gws_base, - job->gws_size, job->oa_base, - job->oa_size); - } - if (ring->funcs->patch_cond_exec) amdgpu_ring_patch_cond_exec(ring, patch_offset); @@ -2383,7 +2368,6 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) union drm_amdgpu_vm *args = data; struct amdgpu_device *adev = drm_to_adev(dev); struct amdgpu_fpriv *fpriv = filp->driver_priv; - long timeout = msecs_to_jiffies(2000); int r; switch (args->in.op) { @@ -2395,21 +2379,6 @@ int amdgpu_vm_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) return r; break; case AMDGPU_VM_OP_UNRESERVE_VMID: - if (amdgpu_sriov_runtime(adev)) - timeout = 8 * timeout; - - /* Wait vm idle to make sure the vmid set in SPM_VMID is - * not referenced anymore. - */ - r = amdgpu_bo_reserve(fpriv->vm.root.bo, true); - if (r) - return r; - - r = amdgpu_vm_wait_idle(&fpriv->vm, timeout); - if (r < 0) - return r; - - amdgpu_bo_unreserve(fpriv->vm.root.bo); amdgpu_vmid_free_reserved(adev, &fpriv->vm, AMDGPU_GFXHUB_0); break; default: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h index 6546e786bf00..094bb4807303 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.h @@ -119,9 +119,6 @@ struct amdgpu_bo_vm; /* Reserve 2MB at top/bottom of address space for kernel use */ #define AMDGPU_VA_RESERVED_SIZE (2ULL << 20) -/* max vmids dedicated for process */ -#define AMDGPU_VM_MAX_RESERVED_VMID 1 - /* See vm_update_mode */ #define AMDGPU_VM_USE_CPU_FOR_GFX (1 << 0) #define AMDGPU_VM_USE_CPU_FOR_COMPUTE (1 << 1) @@ -298,8 +295,7 @@ struct amdgpu_vm { struct dma_fence *last_unlocked; unsigned int pasid; - /* dedicated to vm */ - struct amdgpu_vmid *reserved_vmid[AMDGPU_MAX_VMHUBS]; + bool reserved_vmid[AMDGPU_MAX_VMHUBS]; /* Flag to indicate if VM tables are updated by CPU or GPU (SDMA) */ bool use_cpu_for_update; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c index 59cf64216fbb..535cd6569bcc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm_sdma.c @@ -238,8 +238,10 @@ static int amdgpu_vm_sdma_update(struct amdgpu_vm_update_params *p, /* Wait for PD/PT moves to be completed */ dma_resv_iter_begin(&cursor, bo->tbo.base.resv, DMA_RESV_USAGE_KERNEL); dma_resv_for_each_fence_unlocked(&cursor, fence) { + dma_fence_get(fence); r = drm_sched_job_add_dependency(&p->job->base, fence); if (r) { + dma_fence_put(fence); dma_resv_iter_end(&cursor); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c index 50386eb2eec8..08d6cf79fb15 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v9_0.c @@ -1185,6 +1185,8 @@ static void gmc_v9_0_get_vm_pte(struct amdgpu_device *adev, struct amdgpu_bo_va_mapping *mapping, uint64_t *flags) { + struct amdgpu_bo *bo = mapping->bo_va->base.bo; + *flags &= ~AMDGPU_PTE_EXECUTABLE; *flags |= mapping->flags & AMDGPU_PTE_EXECUTABLE; @@ -1196,7 +1198,7 @@ static void gmc_v9_0_get_vm_pte(struct amdgpu_device *adev, *flags &= ~AMDGPU_PTE_VALID; } - if (mapping->bo_va->base.bo) + if (bo && bo->tbo.resource) gmc_v9_0_get_coherence_flags(adev, mapping->bo_va->base.bo, mapping, flags); } diff --git a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c index 5459366f49ff..970b066b37bb 100644 --- a/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/mes_v11_0.c @@ -1342,7 +1342,8 @@ static int mes_v11_0_late_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; - if (!amdgpu_in_reset(adev) && + /* it's only intended for use in mes_self_test case, not for s0ix and reset */ + if (!amdgpu_in_reset(adev) && !adev->in_s0ix && (adev->ip_versions[GC_HWIP][0] != IP_VERSION(11, 0, 3))) amdgpu_mes_self_test(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 7d5fdf450d0c..5562670b7b52 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -666,6 +666,7 @@ static int soc21_common_early_init(void *handle) AMD_CG_SUPPORT_VCN_MGCG | AMD_CG_SUPPORT_JPEG_MGCG; adev->pg_flags = AMD_PG_SUPPORT_VCN | + AMD_PG_SUPPORT_VCN_DPG | AMD_PG_SUPPORT_GFX_PG | AMD_PG_SUPPORT_JPEG; adev->external_rev_id = adev->rev_id + 0x1; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index a26257171ab7..51b1683ac5c1 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -689,13 +689,13 @@ void kfd_process_destroy_wq(void) } static void kfd_process_free_gpuvm(struct kgd_mem *mem, - struct kfd_process_device *pdd, void *kptr) + struct kfd_process_device *pdd, void **kptr) { struct kfd_dev *dev = pdd->dev; - if (kptr) { + if (kptr && *kptr) { amdgpu_amdkfd_gpuvm_unmap_gtt_bo_from_kernel(mem); - kptr = NULL; + *kptr = NULL; } amdgpu_amdkfd_gpuvm_unmap_memory_from_gpu(dev->adev, mem, pdd->drm_priv); @@ -795,7 +795,7 @@ static void kfd_process_device_destroy_ib_mem(struct kfd_process_device *pdd) if (!qpd->ib_kaddr || !qpd->ib_base) return; - kfd_process_free_gpuvm(qpd->ib_mem, pdd, qpd->ib_kaddr); + kfd_process_free_gpuvm(qpd->ib_mem, pdd, &qpd->ib_kaddr); } struct kfd_process *kfd_create_process(struct file *filep) @@ -1277,7 +1277,7 @@ static void kfd_process_device_destroy_cwsr_dgpu(struct kfd_process_device *pdd) if (!dev->cwsr_enabled || !qpd->cwsr_kaddr || !qpd->cwsr_base) return; - kfd_process_free_gpuvm(qpd->cwsr_mem, pdd, qpd->cwsr_kaddr); + kfd_process_free_gpuvm(qpd->cwsr_mem, pdd, &qpd->cwsr_kaddr); } void kfd_process_set_trap_handler(struct qcm_process_device *qpd, @@ -1576,9 +1576,9 @@ int kfd_process_device_init_vm(struct kfd_process_device *pdd, p = pdd->process; dev = pdd->dev; - ret = amdgpu_amdkfd_gpuvm_acquire_process_vm( - dev->adev, drm_file, p->pasid, - &p->kgd_process_info, &p->ef); + ret = amdgpu_amdkfd_gpuvm_acquire_process_vm(dev->adev, drm_file, + &p->kgd_process_info, + &p->ef); if (ret) { pr_err("Failed to create process VM object\n"); return ret; @@ -1593,13 +1593,19 @@ int kfd_process_device_init_vm(struct kfd_process_device *pdd, if (ret) goto err_init_cwsr; + ret = amdgpu_amdkfd_gpuvm_set_vm_pasid(dev->adev, drm_file, p->pasid); + if (ret) + goto err_set_pasid; + pdd->drm_file = drm_file; return 0; +err_set_pasid: + kfd_process_device_destroy_cwsr_dgpu(pdd); err_init_cwsr: + kfd_process_device_destroy_ib_mem(pdd); err_reserve_ib_mem: - kfd_process_device_free_bos(pdd); pdd->drm_priv = NULL; return ret; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 77277d90b6e2..50c783e19f5a 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1503,6 +1503,7 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) case IP_VERSION(3, 0, 1): case IP_VERSION(3, 1, 2): case IP_VERSION(3, 1, 3): + case IP_VERSION(3, 1, 4): case IP_VERSION(3, 1, 5): case IP_VERSION(3, 1, 6): init_data.flags.gpu_vm_support = true; diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubbub.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubbub.c index 0f746bb4e500..d51f1ce02874 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hubbub.c @@ -55,7 +55,7 @@ void hubbub1_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A); s = &wm->sets[1]; s->wm_set = 1; @@ -65,7 +65,7 @@ void hubbub1_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B); s = &wm->sets[2]; s->wm_set = 2; @@ -75,7 +75,7 @@ void hubbub1_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C); s = &wm->sets[3]; s->wm_set = 3; @@ -85,7 +85,7 @@ void hubbub1_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D); } void hubbub1_allow_self_refresh_control(struct hubbub *hubbub, bool allow) diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index c8ec11839b4d..fe2023f18b7d 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -159,7 +159,7 @@ static void dcn10_log_hubbub_state(struct dc *dc, DTN_INFO_MICRO_SEC(s->pte_meta_urgent); DTN_INFO_MICRO_SEC(s->sr_enter); DTN_INFO_MICRO_SEC(s->sr_exit); - DTN_INFO_MICRO_SEC(s->dram_clk_chanage); + DTN_INFO_MICRO_SEC(s->dram_clk_change); DTN_INFO("\n"); } diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c index e8b6065fffad..a0f8e31d2adc 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer_debug.c @@ -83,7 +83,7 @@ static unsigned int dcn10_get_hubbub_state(struct dc *dc, char *pBuf, unsigned i memset(&wm, 0, sizeof(struct dcn_hubbub_wm)); dc->res_pool->hubbub->funcs->wm_read_state(dc->res_pool->hubbub, &wm); - chars_printed = snprintf_count(pBuf, remaining_buffer, "wm_set_index,data_urgent,pte_meta_urgent,sr_enter,sr_exit,dram_clk_chanage\n"); + chars_printed = snprintf_count(pBuf, remaining_buffer, "wm_set_index,data_urgent,pte_meta_urgent,sr_enter,sr_exit,dram_clk_change\n"); remaining_buffer -= chars_printed; pBuf += chars_printed; @@ -98,7 +98,7 @@ static unsigned int dcn10_get_hubbub_state(struct dc *dc, char *pBuf, unsigned i (s->pte_meta_urgent * frac) / ref_clk_mhz / frac, (s->pte_meta_urgent * frac) / ref_clk_mhz % frac, (s->sr_enter * frac) / ref_clk_mhz / frac, (s->sr_enter * frac) / ref_clk_mhz % frac, (s->sr_exit * frac) / ref_clk_mhz / frac, (s->sr_exit * frac) / ref_clk_mhz % frac, - (s->dram_clk_chanage * frac) / ref_clk_mhz / frac, (s->dram_clk_chanage * frac) / ref_clk_mhz % frac); + (s->dram_clk_change * frac) / ref_clk_mhz / frac, (s->dram_clk_change * frac) / ref_clk_mhz % frac); remaining_buffer -= chars_printed; pBuf += chars_printed; } diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubbub.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubbub.c index aacb1fb5c73e..24bd93219936 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hubbub.c @@ -500,7 +500,7 @@ void hubbub2_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_A); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A); s = &wm->sets[1]; s->wm_set = 1; @@ -511,7 +511,7 @@ void hubbub2_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_B); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B); s = &wm->sets[2]; s->wm_set = 2; @@ -522,7 +522,7 @@ void hubbub2_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_C); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C); s = &wm->sets[3]; s->wm_set = 3; @@ -533,7 +533,7 @@ void hubbub2_wm_read_state(struct hubbub *hubbub, s->sr_enter = REG_READ(DCHUBBUB_ARB_ALLOW_SR_ENTER_WATERMARK_D); s->sr_exit = REG_READ(DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D); } - s->dram_clk_chanage = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D); + s->dram_clk_change = REG_READ(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D); } void hubbub2_get_dchub_ref_freq(struct hubbub *hubbub, diff --git a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubbub.c b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubbub.c index c5e200d09038..aeb0e0d9b70a 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/dcn21/dcn21_hubbub.c @@ -635,7 +635,7 @@ void hubbub21_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, &s->sr_exit); REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, - DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, &s->dram_clk_chanage); + DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_A, &s->dram_clk_change); s = &wm->sets[1]; s->wm_set = 1; @@ -649,7 +649,7 @@ void hubbub21_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, &s->sr_exit); REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, - DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, &s->dram_clk_chanage); + DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_B, &s->dram_clk_change); s = &wm->sets[2]; s->wm_set = 2; @@ -663,7 +663,7 @@ void hubbub21_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C, &s->sr_exit); REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, - DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, &s->dram_clk_chanage); + DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_C, &s->dram_clk_change); s = &wm->sets[3]; s->wm_set = 3; @@ -677,7 +677,7 @@ void hubbub21_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D, &s->sr_exit); REG_GET(DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, - DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, &s->dram_clk_chanage); + DCHUBBUB_ARB_ALLOW_DRAM_CLK_CHANGE_WATERMARK_D, &s->dram_clk_change); } static void hubbub21_apply_DEDCN21_147_wa(struct hubbub *hubbub) diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubbub.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubbub.c index 5947c2cb0f30..9501403a48a9 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubbub.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubbub.c @@ -865,7 +865,7 @@ static void hubbub32_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_A, &s->sr_exit); REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, - DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, &s->dram_clk_chanage); + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_A, &s->dram_clk_change); REG_GET(DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_A, DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_A, &s->usr_retrain); @@ -885,7 +885,7 @@ static void hubbub32_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_B, &s->sr_exit); REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, - DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, &s->dram_clk_chanage); + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_B, &s->dram_clk_change); REG_GET(DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_B, DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_B, &s->usr_retrain); @@ -905,7 +905,7 @@ static void hubbub32_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_C, &s->sr_exit); REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_C, - DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_C, &s->dram_clk_chanage); + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_C, &s->dram_clk_change); REG_GET(DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_C, DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_C, &s->usr_retrain); @@ -925,7 +925,7 @@ static void hubbub32_wm_read_state(struct hubbub *hubbub, DCHUBBUB_ARB_ALLOW_SR_EXIT_WATERMARK_D, &s->sr_exit); REG_GET(DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_D, - DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_D, &s->dram_clk_chanage); + DCHUBBUB_ARB_UCLK_PSTATE_CHANGE_WATERMARK_D, &s->dram_clk_change); REG_GET(DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_D, DCHUBBUB_ARB_USR_RETRAINING_WATERMARK_D, &s->usr_retrain); diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h index f2e1fcb668fb..5b0265c0df61 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/dchubbub.h @@ -46,7 +46,7 @@ struct dcn_hubbub_wm_set { uint32_t pte_meta_urgent; uint32_t sr_enter; uint32_t sr_exit; - uint32_t dram_clk_chanage; + uint32_t dram_clk_change; uint32_t usr_retrain; uint32_t fclk_pstate_change; }; diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h index b76f0f7e4299..d6b964cf73bd 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/pmfw_if/smu13_driver_if_v13_0_0.h @@ -522,9 +522,9 @@ typedef enum { TEMP_HOTSPOT_M, TEMP_MEM, TEMP_VR_GFX, + TEMP_VR_SOC, TEMP_VR_MEM0, TEMP_VR_MEM1, - TEMP_VR_SOC, TEMP_VR_U, TEMP_LIQUID0, TEMP_LIQUID1, diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h index a4e3425b1027..4180c71d930f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_types.h @@ -241,7 +241,8 @@ __SMU_DUMMY_MAP(GetGfxOffEntryCount), \ __SMU_DUMMY_MAP(LogGfxOffResidency), \ __SMU_DUMMY_MAP(SetNumBadMemoryPagesRetired), \ - __SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), + __SMU_DUMMY_MAP(SetBadMemoryPagesRetiredFlagsPerChannel), \ + __SMU_DUMMY_MAP(AllowGpo), #undef __SMU_DUMMY_MAP #define __SMU_DUMMY_MAP(type) SMU_MSG_##type diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h index 865d6358918d..e8c6febb8b64 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/smu_v13_0.h @@ -28,6 +28,7 @@ #define SMU13_DRIVER_IF_VERSION_INV 0xFFFFFFFF #define SMU13_DRIVER_IF_VERSION_YELLOW_CARP 0x04 #define SMU13_DRIVER_IF_VERSION_ALDE 0x08 +#define SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_0 0x34 #define SMU13_DRIVER_IF_VERSION_SMU_V13_0_4 0x07 #define SMU13_DRIVER_IF_VERSION_SMU_V13_0_5 0x04 #define SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_10 0x32 @@ -272,6 +273,9 @@ int smu_v13_0_init_pptable_microcode(struct smu_context *smu); int smu_v13_0_run_btc(struct smu_context *smu); +int smu_v13_0_gpo_control(struct smu_context *smu, + bool enablement); + int smu_v13_0_deep_sleep_control(struct smu_context *smu, bool enablement); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index f5e90e0a99df..e54b760b875b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -290,6 +290,8 @@ int smu_v13_0_check_fw_version(struct smu_context *smu) smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_ALDE; break; case IP_VERSION(13, 0, 0): + smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_0; + break; case IP_VERSION(13, 0, 10): smu->smc_driver_if_version = SMU13_DRIVER_IF_VERSION_SMU_V13_0_0_10; break; @@ -2180,6 +2182,21 @@ int smu_v13_0_run_btc(struct smu_context *smu) return res; } +int smu_v13_0_gpo_control(struct smu_context *smu, + bool enablement) +{ + int res; + + res = smu_cmn_send_smc_msg_with_param(smu, + SMU_MSG_AllowGpo, + enablement ? 1 : 0, + NULL); + if (res) + dev_err(smu->adev->dev, "SetGpoAllow %d failed!\n", enablement); + + return res; +} + int smu_v13_0_deep_sleep_control(struct smu_context *smu, bool enablement) { diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index 87d7c66e49ef..9643b21c636a 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -144,6 +144,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_0_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(SetNumBadMemoryPagesRetired, PPSMC_MSG_SetNumBadMemoryPagesRetired, 0), MSG_MAP(SetBadMemoryPagesRetiredFlagsPerChannel, PPSMC_MSG_SetBadMemoryPagesRetiredFlagsPerChannel, 0), + MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0), }; static struct cmn2asic_mapping smu_v13_0_0_clk_map[SMU_CLK_COUNT] = { @@ -210,6 +211,8 @@ static struct cmn2asic_mapping smu_v13_0_0_feature_mask_map[SMU_FEATURE_COUNT] = FEA_MAP(MEM_TEMP_READ), FEA_MAP(ATHUB_MMHUB_PG), FEA_MAP(SOC_PCC), + [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT}, + [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT}, }; static struct cmn2asic_mapping smu_v13_0_0_table_map[SMU_TABLE_COUNT] = { @@ -540,6 +543,23 @@ static int smu_v13_0_0_set_default_dpm_table(struct smu_context *smu) dpm_table); if (ret) return ret; + + /* + * Update the reported maximum shader clock to the value + * which can be guarded to be achieved on all cards. This + * is aligned with Window setting. And considering that value + * might be not the peak frequency the card can achieve, it + * is normal some real-time clock frequency can overtake this + * labelled maximum clock frequency(for example in pp_dpm_sclk + * sysfs output). + */ + if (skutable->DriverReportedClocks.GameClockAc && + (dpm_table->dpm_levels[dpm_table->count - 1].value > + skutable->DriverReportedClocks.GameClockAc)) { + dpm_table->dpm_levels[dpm_table->count - 1].value = + skutable->DriverReportedClocks.GameClockAc; + dpm_table->max = skutable->DriverReportedClocks.GameClockAc; + } } else { dpm_table->count = 1; dpm_table->dpm_levels[0].value = smu->smu_table.boot_values.gfxclk / 100; @@ -802,6 +822,57 @@ static int smu_v13_0_0_get_smu_metrics_data(struct smu_context *smu, return ret; } +static int smu_v13_0_0_get_dpm_ultimate_freq(struct smu_context *smu, + enum smu_clk_type clk_type, + uint32_t *min, + uint32_t *max) +{ + struct smu_13_0_dpm_context *dpm_context = + smu->smu_dpm.dpm_context; + struct smu_13_0_dpm_table *dpm_table; + + switch (clk_type) { + case SMU_MCLK: + case SMU_UCLK: + /* uclk dpm table */ + dpm_table = &dpm_context->dpm_tables.uclk_table; + break; + case SMU_GFXCLK: + case SMU_SCLK: + /* gfxclk dpm table */ + dpm_table = &dpm_context->dpm_tables.gfx_table; + break; + case SMU_SOCCLK: + /* socclk dpm table */ + dpm_table = &dpm_context->dpm_tables.soc_table; + break; + case SMU_FCLK: + /* fclk dpm table */ + dpm_table = &dpm_context->dpm_tables.fclk_table; + break; + case SMU_VCLK: + case SMU_VCLK1: + /* vclk dpm table */ + dpm_table = &dpm_context->dpm_tables.vclk_table; + break; + case SMU_DCLK: + case SMU_DCLK1: + /* dclk dpm table */ + dpm_table = &dpm_context->dpm_tables.dclk_table; + break; + default: + dev_err(smu->adev->dev, "Unsupported clock type!\n"); + return -EINVAL; + } + + if (min) + *min = dpm_table->min; + if (max) + *max = dpm_table->max; + + return 0; +} + static int smu_v13_0_0_read_sensor(struct smu_context *smu, enum amd_pp_sensors sensor, void *data, @@ -1304,9 +1375,17 @@ static int smu_v13_0_0_populate_umd_state_clk(struct smu_context *smu) &dpm_context->dpm_tables.fclk_table; struct smu_umd_pstate_table *pstate_table = &smu->pstate_table; + struct smu_table_context *table_context = &smu->smu_table; + PPTable_t *pptable = table_context->driver_pptable; + DriverReportedClocks_t driver_clocks = + pptable->SkuTable.DriverReportedClocks; pstate_table->gfxclk_pstate.min = gfx_table->min; - pstate_table->gfxclk_pstate.peak = gfx_table->max; + if (driver_clocks.GameClockAc && + (driver_clocks.GameClockAc < gfx_table->max)) + pstate_table->gfxclk_pstate.peak = driver_clocks.GameClockAc; + else + pstate_table->gfxclk_pstate.peak = gfx_table->max; pstate_table->uclk_pstate.min = mem_table->min; pstate_table->uclk_pstate.peak = mem_table->max; @@ -1323,12 +1402,12 @@ static int smu_v13_0_0_populate_umd_state_clk(struct smu_context *smu) pstate_table->fclk_pstate.min = fclk_table->min; pstate_table->fclk_pstate.peak = fclk_table->max; - /* - * For now, just use the mininum clock frequency. - * TODO: update them when the real pstate settings available - */ - pstate_table->gfxclk_pstate.standard = gfx_table->min; - pstate_table->uclk_pstate.standard = mem_table->min; + if (driver_clocks.BaseClockAc && + driver_clocks.BaseClockAc < gfx_table->max) + pstate_table->gfxclk_pstate.standard = driver_clocks.BaseClockAc; + else + pstate_table->gfxclk_pstate.standard = gfx_table->max; + pstate_table->uclk_pstate.standard = mem_table->max; pstate_table->socclk_pstate.standard = soc_table->min; pstate_table->vclk_pstate.standard = vclk_table->min; pstate_table->dclk_pstate.standard = dclk_table->min; @@ -1362,12 +1441,23 @@ out: static int smu_v13_0_0_get_fan_speed_pwm(struct smu_context *smu, uint32_t *speed) { + int ret; + if (!speed) return -EINVAL; - return smu_v13_0_0_get_smu_metrics_data(smu, - METRICS_CURR_FANPWM, - speed); + ret = smu_v13_0_0_get_smu_metrics_data(smu, + METRICS_CURR_FANPWM, + speed); + if (ret) { + dev_err(smu->adev->dev, "Failed to get fan speed(PWM)!"); + return ret; + } + + /* Convert the PMFW output which is in percent to pwm(255) based */ + *speed = MIN(*speed * 255 / 100, 255); + + return 0; } static int smu_v13_0_0_get_fan_speed_rpm(struct smu_context *smu, @@ -1899,7 +1989,7 @@ static const struct pptable_funcs smu_v13_0_0_ppt_funcs = { .get_enabled_mask = smu_cmn_get_enabled_mask, .dpm_set_vcn_enable = smu_v13_0_set_vcn_enable, .dpm_set_jpeg_enable = smu_v13_0_set_jpeg_enable, - .get_dpm_ultimate_freq = smu_v13_0_get_dpm_ultimate_freq, + .get_dpm_ultimate_freq = smu_v13_0_0_get_dpm_ultimate_freq, .get_vbios_bootup_values = smu_v13_0_get_vbios_bootup_values, .read_sensor = smu_v13_0_0_read_sensor, .feature_is_enabled = smu_cmn_feature_is_enabled, @@ -1947,6 +2037,7 @@ static const struct pptable_funcs smu_v13_0_0_ppt_funcs = { .set_df_cstate = smu_v13_0_0_set_df_cstate, .send_hbm_bad_pages_num = smu_v13_0_0_smu_send_bad_mem_page_num, .send_hbm_bad_channel_flag = smu_v13_0_0_send_bad_mem_channel_flag, + .gpo_control = smu_v13_0_gpo_control, }; void smu_v13_0_0_set_ppt_funcs(struct smu_context *smu) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c index c3c9ef523e59..5c6c6ad011ca 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_7_ppt.c @@ -123,6 +123,7 @@ static struct cmn2asic_msg_mapping smu_v13_0_7_message_map[SMU_MSG_MAX_COUNT] = MSG_MAP(SetMGpuFanBoostLimitRpm, PPSMC_MSG_SetMGpuFanBoostLimitRpm, 0), MSG_MAP(DFCstateControl, PPSMC_MSG_SetExternalClientDfCstateAllow, 0), MSG_MAP(ArmD3, PPSMC_MSG_ArmD3, 0), + MSG_MAP(AllowGpo, PPSMC_MSG_SetGpoAllow, 0), }; static struct cmn2asic_mapping smu_v13_0_7_clk_map[SMU_CLK_COUNT] = { @@ -189,6 +190,8 @@ static struct cmn2asic_mapping smu_v13_0_7_feature_mask_map[SMU_FEATURE_COUNT] = FEA_MAP(MEM_TEMP_READ), FEA_MAP(ATHUB_MMHUB_PG), FEA_MAP(SOC_PCC), + [SMU_FEATURE_DPM_VCLK_BIT] = {1, FEATURE_MM_DPM_BIT}, + [SMU_FEATURE_DPM_DCLK_BIT] = {1, FEATURE_MM_DPM_BIT}, }; static struct cmn2asic_mapping smu_v13_0_7_table_map[SMU_TABLE_COUNT] = { @@ -1360,12 +1363,23 @@ static int smu_v13_0_7_populate_umd_state_clk(struct smu_context *smu) static int smu_v13_0_7_get_fan_speed_pwm(struct smu_context *smu, uint32_t *speed) { + int ret; + if (!speed) return -EINVAL; - return smu_v13_0_7_get_smu_metrics_data(smu, - METRICS_CURR_FANPWM, - speed); + ret = smu_v13_0_7_get_smu_metrics_data(smu, + METRICS_CURR_FANPWM, + speed); + if (ret) { + dev_err(smu->adev->dev, "Failed to get fan speed(PWM)!"); + return ret; + } + + /* Convert the PMFW output which is in percent to pwm(255) based */ + *speed = MIN(*speed * 255 / 100, 255); + + return 0; } static int smu_v13_0_7_get_fan_speed_rpm(struct smu_context *smu, @@ -1437,7 +1451,7 @@ static int smu_v13_0_7_get_power_limit(struct smu_context *smu, static int smu_v13_0_7_get_power_profile_mode(struct smu_context *smu, char *buf) { - DpmActivityMonitorCoeffIntExternal_t activity_monitor_external[PP_SMC_POWER_PROFILE_COUNT]; + DpmActivityMonitorCoeffIntExternal_t *activity_monitor_external; uint32_t i, j, size = 0; int16_t workload_type = 0; int result = 0; @@ -1445,6 +1459,12 @@ static int smu_v13_0_7_get_power_profile_mode(struct smu_context *smu, char *buf if (!buf) return -EINVAL; + activity_monitor_external = kcalloc(PP_SMC_POWER_PROFILE_COUNT, + sizeof(*activity_monitor_external), + GFP_KERNEL); + if (!activity_monitor_external) + return -ENOMEM; + size += sysfs_emit_at(buf, size, " "); for (i = 0; i <= PP_SMC_POWER_PROFILE_WINDOW3D; i++) size += sysfs_emit_at(buf, size, "%-14s%s", amdgpu_pp_profile_name[i], @@ -1457,15 +1477,17 @@ static int smu_v13_0_7_get_power_profile_mode(struct smu_context *smu, char *buf workload_type = smu_cmn_to_asic_specific_index(smu, CMN2ASIC_MAPPING_WORKLOAD, i); - if (workload_type < 0) - return -EINVAL; + if (workload_type < 0) { + result = -EINVAL; + goto out; + } result = smu_cmn_update_table(smu, SMU_TABLE_ACTIVITY_MONITOR_COEFF, workload_type, (void *)(&activity_monitor_external[i]), false); if (result) { dev_err(smu->adev->dev, "[%s] Failed to get activity monitor!", __func__); - return result; + goto out; } } @@ -1493,7 +1515,10 @@ do { \ PRINT_DPM_MONITOR(Fclk_BoosterFreq); #undef PRINT_DPM_MONITOR - return size; + result = size; +out: + kfree(activity_monitor_external); + return result; } static int smu_v13_0_7_set_power_profile_mode(struct smu_context *smu, long *input, uint32_t size) @@ -1688,6 +1713,7 @@ static const struct pptable_funcs smu_v13_0_7_ppt_funcs = { .mode1_reset = smu_v13_0_mode1_reset, .set_mp1_state = smu_v13_0_7_set_mp1_state, .set_df_cstate = smu_v13_0_7_set_df_cstate, + .gpo_control = smu_v13_0_gpo_control, }; void smu_v13_0_7_set_ppt_funcs(struct smu_context *smu) diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index 3593938dcd87..24ef36ec2d3d 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -673,8 +673,6 @@ static void intel_enable_dp(struct intel_atomic_state *state, intel_dp_pcon_dsc_configure(intel_dp, pipe_config); intel_dp_start_link_train(intel_dp, pipe_config); intel_dp_stop_link_train(intel_dp, pipe_config); - - intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void g4x_enable_dp(struct intel_atomic_state *state, @@ -683,6 +681,7 @@ static void g4x_enable_dp(struct intel_atomic_state *state, const struct drm_connector_state *conn_state) { intel_enable_dp(state, encoder, pipe_config, conn_state); + intel_audio_codec_enable(encoder, pipe_config, conn_state); intel_edp_backlight_on(pipe_config, conn_state); } @@ -691,6 +690,7 @@ static void vlv_enable_dp(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { + intel_audio_codec_enable(encoder, pipe_config, conn_state); intel_edp_backlight_on(pipe_config, conn_state); } diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index 121caeaa409b..c3580d96765c 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -157,10 +157,8 @@ static void intel_hdmi_get_config(struct intel_encoder *encoder, &pipe_config->infoframes.hdmi); } -static void g4x_enable_hdmi(struct intel_atomic_state *state, - struct intel_encoder *encoder, - const struct intel_crtc_state *pipe_config, - const struct drm_connector_state *conn_state) +static void g4x_hdmi_enable_port(struct intel_encoder *encoder, + const struct intel_crtc_state *pipe_config) { struct drm_device *dev = encoder->base.dev; struct drm_i915_private *dev_priv = to_i915(dev); @@ -175,6 +173,16 @@ static void g4x_enable_hdmi(struct intel_atomic_state *state, intel_de_write(dev_priv, intel_hdmi->hdmi_reg, temp); intel_de_posting_read(dev_priv, intel_hdmi->hdmi_reg); +} + +static void g4x_enable_hdmi(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *pipe_config, + const struct drm_connector_state *conn_state) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + + g4x_hdmi_enable_port(encoder, pipe_config); drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && !pipe_config->has_hdmi_sink); @@ -294,6 +302,11 @@ static void vlv_enable_hdmi(struct intel_atomic_state *state, const struct intel_crtc_state *pipe_config, const struct drm_connector_state *conn_state) { + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + + drm_WARN_ON(&dev_priv->drm, pipe_config->has_audio && + !pipe_config->has_hdmi_sink); + intel_audio_codec_enable(encoder, pipe_config, conn_state); } static void intel_disable_hdmi(struct intel_atomic_state *state, @@ -415,7 +428,7 @@ static void vlv_hdmi_pre_enable(struct intel_atomic_state *state, pipe_config->has_infoframe, pipe_config, conn_state); - g4x_enable_hdmi(state, encoder, pipe_config, conn_state); + g4x_hdmi_enable_port(encoder, pipe_config); vlv_wait_port_ready(dev_priv, dig_port, 0x0); } @@ -492,7 +505,7 @@ static void chv_hdmi_pre_enable(struct intel_atomic_state *state, pipe_config->has_infoframe, pipe_config, conn_state); - g4x_enable_hdmi(state, encoder, pipe_config, conn_state); + g4x_hdmi_enable_port(encoder, pipe_config); vlv_wait_port_ready(dev_priv, dig_port, 0x0); diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 67089711d9e2..75070eb07d4b 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3679,61 +3679,6 @@ static void intel_dp_phy_pattern_update(struct intel_dp *intel_dp, } } -static void -intel_dp_autotest_phy_ddi_disable(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_device *dev = dig_port->base.base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - struct intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc); - enum pipe pipe = crtc->pipe; - u32 trans_ddi_func_ctl_value, trans_conf_value, dp_tp_ctl_value; - - trans_ddi_func_ctl_value = intel_de_read(dev_priv, - TRANS_DDI_FUNC_CTL(pipe)); - trans_conf_value = intel_de_read(dev_priv, PIPECONF(pipe)); - dp_tp_ctl_value = intel_de_read(dev_priv, TGL_DP_TP_CTL(pipe)); - - trans_ddi_func_ctl_value &= ~(TRANS_DDI_FUNC_ENABLE | - TGL_TRANS_DDI_PORT_MASK); - trans_conf_value &= ~PIPECONF_ENABLE; - dp_tp_ctl_value &= ~DP_TP_CTL_ENABLE; - - intel_de_write(dev_priv, PIPECONF(pipe), trans_conf_value); - intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(pipe), - trans_ddi_func_ctl_value); - intel_de_write(dev_priv, TGL_DP_TP_CTL(pipe), dp_tp_ctl_value); -} - -static void -intel_dp_autotest_phy_ddi_enable(struct intel_dp *intel_dp, - const struct intel_crtc_state *crtc_state) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct drm_device *dev = dig_port->base.base.dev; - struct drm_i915_private *dev_priv = to_i915(dev); - enum port port = dig_port->base.port; - struct intel_crtc *crtc = to_intel_crtc(dig_port->base.base.crtc); - enum pipe pipe = crtc->pipe; - u32 trans_ddi_func_ctl_value, trans_conf_value, dp_tp_ctl_value; - - trans_ddi_func_ctl_value = intel_de_read(dev_priv, - TRANS_DDI_FUNC_CTL(pipe)); - trans_conf_value = intel_de_read(dev_priv, PIPECONF(pipe)); - dp_tp_ctl_value = intel_de_read(dev_priv, TGL_DP_TP_CTL(pipe)); - - trans_ddi_func_ctl_value |= TRANS_DDI_FUNC_ENABLE | - TGL_TRANS_DDI_SELECT_PORT(port); - trans_conf_value |= PIPECONF_ENABLE; - dp_tp_ctl_value |= DP_TP_CTL_ENABLE; - - intel_de_write(dev_priv, PIPECONF(pipe), trans_conf_value); - intel_de_write(dev_priv, TGL_DP_TP_CTL(pipe), dp_tp_ctl_value); - intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(pipe), - trans_ddi_func_ctl_value); -} - static void intel_dp_process_phy_request(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { @@ -3752,14 +3697,10 @@ static void intel_dp_process_phy_request(struct intel_dp *intel_dp, intel_dp_get_adjust_train(intel_dp, crtc_state, DP_PHY_DPRX, link_status); - intel_dp_autotest_phy_ddi_disable(intel_dp, crtc_state); - intel_dp_set_signal_levels(intel_dp, crtc_state, DP_PHY_DPRX); intel_dp_phy_pattern_update(intel_dp, crtc_state); - intel_dp_autotest_phy_ddi_enable(intel_dp, crtc_state); - drm_dp_dpcd_write(&intel_dp->aux, DP_TRAINING_LANE0_SET, intel_dp->train_set, crtc_state->lane_count); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.c b/drivers/gpu/drm/i915/gem/i915_gem_object.c index 733696057761..1a0886b8aaa1 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.c @@ -785,6 +785,9 @@ bool i915_gem_object_needs_ccs_pages(struct drm_i915_gem_object *obj) if (!HAS_FLAT_CCS(to_i915(obj->base.dev))) return false; + if (obj->flags & I915_BO_ALLOC_CCS_AUX) + return true; + for (i = 0; i < obj->mm.n_placements; i++) { /* Compression is not allowed for the objects with smem placement */ if (obj->mm.placements[i]->type == INTEL_MEMORY_SYSTEM) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h index d0d6772e6f36..ab4c2f90a564 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object_types.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object_types.h @@ -327,16 +327,18 @@ struct drm_i915_gem_object { * dealing with userspace objects the CPU fault handler is free to ignore this. */ #define I915_BO_ALLOC_GPU_ONLY BIT(6) +#define I915_BO_ALLOC_CCS_AUX BIT(7) #define I915_BO_ALLOC_FLAGS (I915_BO_ALLOC_CONTIGUOUS | \ I915_BO_ALLOC_VOLATILE | \ I915_BO_ALLOC_CPU_CLEAR | \ I915_BO_ALLOC_USER | \ I915_BO_ALLOC_PM_VOLATILE | \ I915_BO_ALLOC_PM_EARLY | \ - I915_BO_ALLOC_GPU_ONLY) -#define I915_BO_READONLY BIT(7) -#define I915_TILING_QUIRK_BIT 8 /* unknown swizzling; do not release! */ -#define I915_BO_PROTECTED BIT(9) + I915_BO_ALLOC_GPU_ONLY | \ + I915_BO_ALLOC_CCS_AUX) +#define I915_BO_READONLY BIT(8) +#define I915_TILING_QUIRK_BIT 9 /* unknown swizzling; do not release! */ +#define I915_BO_PROTECTED BIT(10) /** * @mem_flags - Mutable placement-related flags * diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c index 07e49f22f2de..7e67742bc65e 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm_pm.c @@ -50,6 +50,7 @@ static int i915_ttm_backup(struct i915_gem_apply_to_region *apply, container_of(bo->bdev, typeof(*i915), bdev); struct drm_i915_gem_object *backup; struct ttm_operation_ctx ctx = {}; + unsigned int flags; int err = 0; if (bo->resource->mem_type == I915_PL_SYSTEM || obj->ttm.backup) @@ -65,7 +66,22 @@ static int i915_ttm_backup(struct i915_gem_apply_to_region *apply, if (obj->flags & I915_BO_ALLOC_PM_VOLATILE) return 0; - backup = i915_gem_object_create_shmem(i915, obj->base.size); + /* + * It seems that we might have some framebuffers still pinned at this + * stage, but for such objects we might also need to deal with the CCS + * aux state. Make sure we force the save/restore of the CCS state, + * otherwise we might observe display corruption, when returning from + * suspend. + */ + flags = 0; + if (i915_gem_object_needs_ccs_pages(obj)) { + WARN_ON_ONCE(!i915_gem_object_is_framebuffer(obj)); + WARN_ON_ONCE(!pm_apply->allow_gpu); + + flags = I915_BO_ALLOC_CCS_AUX; + } + backup = i915_gem_object_create_region(i915->mm.regions[INTEL_REGION_SMEM], + obj->base.size, 0, flags); if (IS_ERR(backup)) return PTR_ERR(backup); diff --git a/drivers/gpu/drm/i915/gt/intel_gt_mcr.c b/drivers/gpu/drm/i915/gt/intel_gt_mcr.c index d9a8ff9e5e57..ea86c1ab5dc5 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_mcr.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_mcr.c @@ -702,7 +702,7 @@ void intel_gt_mcr_get_ss_steering(struct intel_gt *gt, unsigned int dss, } /** - * intel_gt_mcr_wait_for_reg_fw - wait until MCR register matches expected state + * intel_gt_mcr_wait_for_reg - wait until MCR register matches expected state * @gt: GT structure * @reg: the register to read * @mask: mask to apply to register value diff --git a/drivers/gpu/drm/i915/gt/intel_migrate.c b/drivers/gpu/drm/i915/gt/intel_migrate.c index b405a04135ca..5fb74e71f27b 100644 --- a/drivers/gpu/drm/i915/gt/intel_migrate.c +++ b/drivers/gpu/drm/i915/gt/intel_migrate.c @@ -342,6 +342,16 @@ static int emit_no_arbitration(struct i915_request *rq) return 0; } +static int max_pte_pkt_size(struct i915_request *rq, int pkt) +{ + struct intel_ring *ring = rq->ring; + + pkt = min_t(int, pkt, (ring->space - rq->reserved_space) / sizeof(u32) + 5); + pkt = min_t(int, pkt, (ring->size - ring->emit) / sizeof(u32) + 5); + + return pkt; +} + static int emit_pte(struct i915_request *rq, struct sgt_dma *it, enum i915_cache_level cache_level, @@ -388,8 +398,7 @@ static int emit_pte(struct i915_request *rq, return PTR_ERR(cs); /* Pack as many PTE updates as possible into a single MI command */ - pkt = min_t(int, dword_length, ring->space / sizeof(u32) + 5); - pkt = min_t(int, pkt, (ring->size - ring->emit) / sizeof(u32) + 5); + pkt = max_pte_pkt_size(rq, dword_length); hdr = cs; *cs++ = MI_STORE_DATA_IMM | REG_BIT(21); /* as qword elements */ @@ -422,8 +431,7 @@ static int emit_pte(struct i915_request *rq, } } - pkt = min_t(int, dword_rem, ring->space / sizeof(u32) + 5); - pkt = min_t(int, pkt, (ring->size - ring->emit) / sizeof(u32) + 5); + pkt = max_pte_pkt_size(rq, dword_rem); hdr = cs; *cs++ = MI_STORE_DATA_IMM | REG_BIT(21); @@ -829,14 +837,35 @@ intel_context_migrate_copy(struct intel_context *ce, if (err) goto out_rq; - /* - * While we can't always restore/manage the CCS state, - * we still need to ensure we don't leak the CCS state - * from the previous user, so make sure we overwrite it - * with something. - */ - err = emit_copy_ccs(rq, dst_offset, INDIRECT_ACCESS, - dst_offset, DIRECT_ACCESS, len); + if (src_is_lmem) { + /* + * If the src is already in lmem, then we must + * be doing an lmem -> lmem transfer, and so + * should be safe to directly copy the CCS + * state. In this case we have either + * initialised the CCS aux state when first + * clearing the pages (since it is already + * allocated in lmem), or the user has + * potentially populated it, in which case we + * need to copy the CCS state as-is. + */ + err = emit_copy_ccs(rq, + dst_offset, INDIRECT_ACCESS, + src_offset, INDIRECT_ACCESS, + len); + } else { + /* + * While we can't always restore/manage the CCS + * state, we still need to ensure we don't leak + * the CCS state from the previous user, so make + * sure we overwrite it with something. + */ + err = emit_copy_ccs(rq, + dst_offset, INDIRECT_ACCESS, + dst_offset, DIRECT_ACCESS, + len); + } + if (err) goto out_rq; diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 00e09bb18b13..125b6ca25a75 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -1383,6 +1383,9 @@ static u32 oa_context_image_offset(struct intel_context *ce, u32 reg) u32 offset, len = (ce->engine->context_size - PAGE_SIZE) / 4; u32 *state = ce->lrc_reg_state; + if (drm_WARN_ON(&ce->engine->i915->drm, !state)) + return U32_MAX; + for (offset = 0; offset < len; ) { if (IS_MI_LRI_CMD(state[offset])) { /* @@ -1447,7 +1450,8 @@ static int oa_get_render_ctx_id(struct i915_perf_stream *stream) if (IS_ERR(ce)) return PTR_ERR(ce); - if (engine_supports_mi_query(stream->engine)) { + if (engine_supports_mi_query(stream->engine) && + HAS_LOGICAL_RING_CONTEXTS(stream->perf->i915)) { /* * We are enabling perf query here. If we don't find the context * offset here, just return an error. diff --git a/drivers/gpu/drm/i915/intel_uncore.c b/drivers/gpu/drm/i915/intel_uncore.c index 8006a6c61466..614013745fca 100644 --- a/drivers/gpu/drm/i915/intel_uncore.c +++ b/drivers/gpu/drm/i915/intel_uncore.c @@ -824,9 +824,9 @@ void intel_uncore_forcewake_flush(struct intel_uncore *uncore, } /** - * intel_uncore_forcewake_put__locked - grab forcewake domain references + * intel_uncore_forcewake_put__locked - release forcewake domain references * @uncore: the intel_uncore structure - * @fw_domains: forcewake domains to get reference on + * @fw_domains: forcewake domains to put references * * See intel_uncore_forcewake_put(). This variant places the onus * on the caller to explicitly handle the dev_priv->uncore.lock spinlock. diff --git a/drivers/misc/lkdtm/cfi.c b/drivers/misc/lkdtm/cfi.c index 5245cf6013c9..fc28714ae3a6 100644 --- a/drivers/misc/lkdtm/cfi.c +++ b/drivers/misc/lkdtm/cfi.c @@ -54,7 +54,11 @@ static void lkdtm_CFI_FORWARD_PROTO(void) # ifdef CONFIG_ARM64_BTI_KERNEL # define __no_pac "branch-protection=bti" # else -# define __no_pac "branch-protection=none" +# ifdef CONFIG_CC_HAS_BRANCH_PROT_PAC_RET +# define __no_pac "branch-protection=none" +# else +# define __no_pac "sign-return-address=none" +# endif # endif # define __no_ret_protection __noscs __attribute__((__target__(__no_pac))) #else diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ace4ecc6d7c2..ae69e493913d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1002,7 +1002,7 @@ static int drms_uA_update(struct regulator_dev *rdev) /* get input voltage */ input_uV = 0; if (rdev->supply) - input_uV = regulator_get_voltage(rdev->supply); + input_uV = regulator_get_voltage_rdev(rdev->supply->rdev); if (input_uV <= 0) input_uV = rdev->constraints->input_uV; @@ -5540,7 +5540,7 @@ regulator_register(struct device *dev, /* register with sysfs */ rdev->dev.class = ®ulator_class; - rdev->dev.parent = dev; + rdev->dev.parent = config->dev; dev_set_name(&rdev->dev, "regulator.%lu", (unsigned long) atomic_inc_return(®ulator_no)); dev_set_drvdata(&rdev->dev, rdev); diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index 731624f157fc..93152144fd2e 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -333,13 +333,26 @@ static int fsl_spi_prepare_message(struct spi_controller *ctlr, { struct mpc8xxx_spi *mpc8xxx_spi = spi_controller_get_devdata(ctlr); struct spi_transfer *t; + struct spi_transfer *first; + + first = list_first_entry(&m->transfers, struct spi_transfer, + transfer_list); /* * In CPU mode, optimize large byte transfers to use larger * bits_per_word values to reduce number of interrupts taken. + * + * Some glitches can appear on the SPI clock when the mode changes. + * Check that there is no speed change during the transfer and set it up + * now to change the mode without having a chip-select asserted. */ - if (!(mpc8xxx_spi->flags & SPI_CPM_MODE)) { - list_for_each_entry(t, &m->transfers, transfer_list) { + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->speed_hz != first->speed_hz) { + dev_err(&m->spi->dev, + "speed_hz cannot change during message.\n"); + return -EINVAL; + } + if (!(mpc8xxx_spi->flags & SPI_CPM_MODE)) { if (t->len < 256 || t->bits_per_word != 8) continue; if ((t->len & 3) == 0) @@ -348,7 +361,7 @@ static int fsl_spi_prepare_message(struct spi_controller *ctlr, t->bits_per_word = 16; } } - return 0; + return fsl_spi_setup_transfer(m->spi, first); } static int fsl_spi_transfer_one(struct spi_controller *controller, diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 23cf9b2fbfe4..805151114e96 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 0129de2ea31a..3a9c4517265f 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_addr.c b/fs/9p/vfs_addr.c index a19891015f19..97599edbc300 100644 --- a/fs/9p/vfs_addr.c +++ b/fs/9p/vfs_addr.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index f89f01734587..65fa2df5e49b 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_dir.c b/fs/9p/vfs_dir.c index 3bb95adc9619..59b0e8948f78 100644 --- a/fs/9p/vfs_dir.c +++ b/fs/9p/vfs_dir.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_file.c b/fs/9p/vfs_file.c index aec43ba83799..b740017634ef 100644 --- a/fs/9p/vfs_file.c +++ b/fs/9p/vfs_file.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 4d1a4a8d9277..27a04a226d97 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_inode_dotl.c b/fs/9p/vfs_inode_dotl.c index 03c1743c4aff..f806b3f11649 100644 --- a/fs/9p/vfs_inode_dotl.c +++ b/fs/9p/vfs_inode_dotl.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 2d9ee073d12c..266c4693e20c 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/fs/pstore/Kconfig b/fs/pstore/Kconfig index 8adabde685f1..c49d554cc9ae 100644 --- a/fs/pstore/Kconfig +++ b/fs/pstore/Kconfig @@ -126,6 +126,7 @@ config PSTORE_CONSOLE config PSTORE_PMSG bool "Log user space messages" depends on PSTORE + select RT_MUTEXES help When the option is enabled, pstore will export a character interface /dev/pmsg0 to log user space messages. On reboot diff --git a/fs/pstore/pmsg.c b/fs/pstore/pmsg.c index b31c9c72d90b..ab82e5f05346 100644 --- a/fs/pstore/pmsg.c +++ b/fs/pstore/pmsg.c @@ -7,9 +7,10 @@ #include #include #include +#include #include "internal.h" -static DEFINE_MUTEX(pmsg_lock); +static DEFINE_RT_MUTEX(pmsg_lock); static ssize_t write_pmsg(struct file *file, const char __user *buf, size_t count, loff_t *ppos) @@ -28,9 +29,9 @@ static ssize_t write_pmsg(struct file *file, const char __user *buf, if (!access_ok(buf, count)) return -EFAULT; - mutex_lock(&pmsg_lock); + rt_mutex_lock(&pmsg_lock); ret = psinfo->write_user(&record, buf); - mutex_unlock(&pmsg_lock); + rt_mutex_unlock(&pmsg_lock); return ret ? ret : count; } diff --git a/fs/pstore/ram.c b/fs/pstore/ram.c index 9a5052431fd3..ade66dbe5f39 100644 --- a/fs/pstore/ram.c +++ b/fs/pstore/ram.c @@ -680,7 +680,7 @@ static int ramoops_parse_dt(struct platform_device *pdev, field = value; \ } - parse_u32("mem-type", pdata->record_size, pdata->mem_type); + parse_u32("mem-type", pdata->mem_type, pdata->mem_type); parse_u32("record-size", pdata->record_size, 0); parse_u32("console-size", pdata->console_size, 0); parse_u32("ftrace-size", pdata->ftrace_size, 0); diff --git a/include/net/9p/9p.h b/include/net/9p/9p.h index 13abe013af21..429adf6be29c 100644 --- a/include/net/9p/9p.h +++ b/include/net/9p/9p.h @@ -531,6 +531,7 @@ struct p9_rstatfs { * @offset: used by marshalling routines to track current position in buffer * @capacity: used by marshalling routines to track total malloc'd capacity * @sdata: payload + * @zc: whether zero-copy is used * * &p9_fcall represents the structure for all 9P RPC * transactions. Requests are packaged into fcalls, and reponses @@ -549,6 +550,7 @@ struct p9_fcall { struct kmem_cache *cache; u8 *sdata; + bool zc; }; int p9_errstr2errno(char *errstr, int len); diff --git a/include/sound/hda_codec.h b/include/sound/hda_codec.h index 25ec8c181688..eba23daf2c29 100644 --- a/include/sound/hda_codec.h +++ b/include/sound/hda_codec.h @@ -258,6 +258,7 @@ struct hda_codec { unsigned int link_down_at_suspend:1; /* link down at runtime suspend */ unsigned int relaxed_resume:1; /* don't resume forcibly for jack */ unsigned int forced_resume:1; /* forced resume for jack */ + unsigned int no_stream_clean_at_suspend:1; /* do not clean streams at suspend */ #ifdef CONFIG_PM unsigned long power_on_acct; diff --git a/include/sound/hdaudio_ext.h b/include/sound/hdaudio_ext.h index 68ab89211de2..511211f4a2b6 100644 --- a/include/sound/hdaudio_ext.h +++ b/include/sound/hdaudio_ext.h @@ -75,6 +75,8 @@ struct hdac_ext_stream *snd_hdac_ext_stream_assign(struct hdac_bus *bus, struct snd_pcm_substream *substream, int type); void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type); +struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus, + struct snd_compr_stream *cstream); void snd_hdac_ext_stream_decouple_locked(struct hdac_bus *bus, struct hdac_ext_stream *hext_stream, bool decouple); void snd_hdac_ext_stream_decouple(struct hdac_bus *bus, diff --git a/kernel/Makefile b/kernel/Makefile index e7fc37a68069..10ef068f598d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -41,9 +41,6 @@ UBSAN_SANITIZE_kcov.o := n KMSAN_SANITIZE_kcov.o := n CFLAGS_kcov.o := $(call cc-option, -fno-conserve-stack) -fno-stack-protector -# Don't instrument error handlers -CFLAGS_REMOVE_cfi.o := $(CC_FLAGS_CFI) - obj-y += sched/ obj-y += locking/ obj-y += power/ diff --git a/kernel/dma/mapping.c b/kernel/dma/mapping.c index c026a5a5e046..68106e3791f6 100644 --- a/kernel/dma/mapping.c +++ b/kernel/dma/mapping.c @@ -560,6 +560,8 @@ static struct page *__dma_alloc_pages(struct device *dev, size_t size, return NULL; if (WARN_ON_ONCE(gfp & (__GFP_DMA | __GFP_DMA32 | __GFP_HIGHMEM))) return NULL; + if (WARN_ON_ONCE(gfp & __GFP_COMP)) + return NULL; size = PAGE_ALIGN(size); if (dma_alloc_direct(dev, ops)) @@ -645,6 +647,8 @@ struct sg_table *dma_alloc_noncontiguous(struct device *dev, size_t size, if (WARN_ON_ONCE(attrs & ~DMA_ATTR_ALLOC_SINGLE_PAGES)) return NULL; + if (WARN_ON_ONCE(gfp & __GFP_COMP)) + return NULL; if (ops && ops->alloc_noncontiguous) sgt = ops->alloc_noncontiguous(dev, size, dir, gfp, attrs); diff --git a/kernel/exit.c b/kernel/exit.c index deffb8e4b1b2..15dc2ec80c46 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -931,6 +931,7 @@ void __noreturn make_task_dead(int signr) * Then do everything else. */ struct task_struct *tsk = current; + unsigned int limit; if (unlikely(in_interrupt())) panic("Aiee, killing interrupt handler!"); @@ -954,8 +955,9 @@ void __noreturn make_task_dead(int signr) * To make sure this can't happen, place an upper bound on how often the * kernel may oops without panic(). */ - if (atomic_inc_return(&oops_count) >= READ_ONCE(oops_limit) && oops_limit) - panic("Oopsed too often (kernel.oops_limit is %d)", oops_limit); + limit = READ_ONCE(oops_limit); + if (atomic_inc_return(&oops_count) >= limit && limit) + panic("Oopsed too often (kernel.oops_limit is %d)", limit); /* * We're taking recursive faults here in make_task_dead. Safest is to just diff --git a/kernel/panic.c b/kernel/panic.c index 326d91505f04..463c9295bc28 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -232,12 +232,15 @@ static void panic_print_sys_info(bool console_flush) void check_panic_on_warn(const char *origin) { + unsigned int limit; + if (panic_on_warn) panic("%s: panic_on_warn set ...\n", origin); - if (atomic_inc_return(&warn_count) >= READ_ONCE(warn_limit) && warn_limit) + limit = READ_ONCE(warn_limit); + if (atomic_inc_return(&warn_count) >= limit && limit) panic("%s: system warned too often (kernel.warn_limit is %d)", - origin, warn_limit); + origin, limit); } /** diff --git a/net/9p/client.c b/net/9p/client.c index 0638b12055ba..622ec6a586ee 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -297,6 +297,11 @@ p9_tag_alloc(struct p9_client *c, int8_t type, uint t_size, uint r_size, p9pdu_reset(&req->rc); req->t_err = 0; req->status = REQ_STATUS_ALLOC; + /* refcount needs to be set to 0 before inserting into the idr + * so p9_tag_lookup does not accept a request that is not fully + * initialized. refcount_set to 2 below will mark request ready. + */ + refcount_set(&req->refcount, 0); init_waitqueue_head(&req->wq); INIT_LIST_HEAD(&req->req_list); @@ -438,7 +443,7 @@ void p9_client_cb(struct p9_client *c, struct p9_req_t *req, int status) * the status change is visible to another thread */ smp_wmb(); - req->status = status; + WRITE_ONCE(req->status, status); wake_up(&req->wq); p9_debug(P9_DEBUG_MUX, "wakeup: %d\n", req->tc.tag); @@ -514,10 +519,9 @@ static int p9_check_errors(struct p9_client *c, struct p9_req_t *req) int ecode; err = p9_parse_header(&req->rc, NULL, &type, NULL, 0); - if (req->rc.size >= c->msize) { - p9_debug(P9_DEBUG_ERROR, - "requested packet size too big: %d\n", - req->rc.size); + if (req->rc.size > req->rc.capacity && !req->rc.zc) { + pr_err("requested packet size too big: %d does not fit %zu (type=%d)\n", + req->rc.size, req->rc.capacity, req->rc.id); return -EIO; } /* dump the response from server @@ -600,7 +604,7 @@ static int p9_client_flush(struct p9_client *c, struct p9_req_t *oldreq) /* if we haven't received a response for oldreq, * remove it from the list */ - if (oldreq->status == REQ_STATUS_SENT) { + if (READ_ONCE(oldreq->status) == REQ_STATUS_SENT) { if (c->trans_mod->cancelled) c->trans_mod->cancelled(c, oldreq); } @@ -680,6 +684,9 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) if (IS_ERR(req)) return req; + req->tc.zc = false; + req->rc.zc = false; + if (signal_pending(current)) { sigpending = 1; clear_thread_flag(TIF_SIGPENDING); @@ -697,7 +704,8 @@ p9_client_rpc(struct p9_client *c, int8_t type, const char *fmt, ...) } again: /* Wait for the response */ - err = wait_event_killable(req->wq, req->status >= REQ_STATUS_RCVD); + err = wait_event_killable(req->wq, + READ_ONCE(req->status) >= REQ_STATUS_RCVD); /* Make sure our req is coherent with regard to updates in other * threads - echoes to wmb() in the callback @@ -711,7 +719,7 @@ again: goto again; } - if (req->status == REQ_STATUS_ERROR) { + if (READ_ONCE(req->status) == REQ_STATUS_ERROR) { p9_debug(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err); err = req->t_err; } @@ -724,7 +732,7 @@ again: p9_client_flush(c, req); /* if we received the response anyway, don't signal error */ - if (req->status == REQ_STATUS_RCVD) + if (READ_ONCE(req->status) == REQ_STATUS_RCVD) err = 0; } recalc_sigpending: @@ -778,6 +786,9 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, if (IS_ERR(req)) return req; + req->tc.zc = true; + req->rc.zc = true; + if (signal_pending(current)) { sigpending = 1; clear_thread_flag(TIF_SIGPENDING); @@ -793,7 +804,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, if (err != -ERESTARTSYS) goto recalc_sigpending; } - if (req->status == REQ_STATUS_ERROR) { + if (READ_ONCE(req->status) == REQ_STATUS_ERROR) { p9_debug(P9_DEBUG_ERROR, "req_status error %d\n", req->t_err); err = req->t_err; } @@ -806,7 +817,7 @@ static struct p9_req_t *p9_client_zc_rpc(struct p9_client *c, int8_t type, p9_client_flush(c, req); /* if we received the response anyway, don't signal error */ - if (req->status == REQ_STATUS_RCVD) + if (READ_ONCE(req->status) == REQ_STATUS_RCVD) err = 0; } recalc_sigpending: diff --git a/net/9p/trans_fd.c b/net/9p/trans_fd.c index d9120f14684b..00b684616e8d 100644 --- a/net/9p/trans_fd.c +++ b/net/9p/trans_fd.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -202,11 +201,11 @@ static void p9_conn_cancel(struct p9_conn *m, int err) list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) { list_move(&req->req_list, &cancel_list); - req->status = REQ_STATUS_ERROR; + WRITE_ONCE(req->status, REQ_STATUS_ERROR); } list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) { list_move(&req->req_list, &cancel_list); - req->status = REQ_STATUS_ERROR; + WRITE_ONCE(req->status, REQ_STATUS_ERROR); } spin_unlock(&m->req_lock); @@ -467,7 +466,7 @@ static void p9_write_work(struct work_struct *work) req = list_entry(m->unsent_req_list.next, struct p9_req_t, req_list); - req->status = REQ_STATUS_SENT; + WRITE_ONCE(req->status, REQ_STATUS_SENT); p9_debug(P9_DEBUG_TRANS, "move req %p\n", req); list_move_tail(&req->req_list, &m->req_list); @@ -676,7 +675,7 @@ static int p9_fd_request(struct p9_client *client, struct p9_req_t *req) return m->err; spin_lock(&m->req_lock); - req->status = REQ_STATUS_UNSENT; + WRITE_ONCE(req->status, REQ_STATUS_UNSENT); list_add_tail(&req->req_list, &m->unsent_req_list); spin_unlock(&m->req_lock); @@ -703,7 +702,7 @@ static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req) if (req->status == REQ_STATUS_UNSENT) { list_del(&req->req_list); - req->status = REQ_STATUS_FLSHD; + WRITE_ONCE(req->status, REQ_STATUS_FLSHD); p9_req_put(client, req); ret = 0; } @@ -732,7 +731,7 @@ static int p9_fd_cancelled(struct p9_client *client, struct p9_req_t *req) * remove it from the list. */ list_del(&req->req_list); - req->status = REQ_STATUS_FLSHD; + WRITE_ONCE(req->status, REQ_STATUS_FLSHD); spin_unlock(&m->req_lock); p9_req_put(client, req); diff --git a/net/9p/trans_rdma.c b/net/9p/trans_rdma.c index 6ff706760676..83f9100d46bf 100644 --- a/net/9p/trans_rdma.c +++ b/net/9p/trans_rdma.c @@ -21,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -507,7 +506,7 @@ dont_need_post_recv: * because doing if after could erase the REQ_STATUS_RCVD * status in case of a very fast reply. */ - req->status = REQ_STATUS_SENT; + WRITE_ONCE(req->status, REQ_STATUS_SENT); err = ib_post_send(rdma->qp, &wr, NULL); if (err) goto send_error; @@ -517,7 +516,7 @@ dont_need_post_recv: /* Handle errors that happened during or while preparing the send: */ send_error: - req->status = REQ_STATUS_ERROR; + WRITE_ONCE(req->status, REQ_STATUS_ERROR); kfree(c); p9_debug(P9_DEBUG_ERROR, "Error %d in rdma_request()\n", err); diff --git a/net/9p/trans_virtio.c b/net/9p/trans_virtio.c index e757f0601304..3c27ffb781e3 100644 --- a/net/9p/trans_virtio.c +++ b/net/9p/trans_virtio.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include #include @@ -263,7 +262,7 @@ p9_virtio_request(struct p9_client *client, struct p9_req_t *req) p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n"); - req->status = REQ_STATUS_SENT; + WRITE_ONCE(req->status, REQ_STATUS_SENT); req_retry: spin_lock_irqsave(&chan->lock, flags); @@ -469,7 +468,7 @@ p9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, inlen = n; } } - req->status = REQ_STATUS_SENT; + WRITE_ONCE(req->status, REQ_STATUS_SENT); req_retry_pinned: spin_lock_irqsave(&chan->lock, flags); @@ -532,9 +531,10 @@ req_retry_pinned: spin_unlock_irqrestore(&chan->lock, flags); kicked = 1; p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); - err = wait_event_killable(req->wq, req->status >= REQ_STATUS_RCVD); + err = wait_event_killable(req->wq, + READ_ONCE(req->status) >= REQ_STATUS_RCVD); // RERROR needs reply (== error string) in static data - if (req->status == REQ_STATUS_RCVD && + if (READ_ONCE(req->status) == REQ_STATUS_RCVD && unlikely(req->rc.sdata[4] == P9_RERROR)) handle_rerror(req, in_hdr_len, offs, in_pages); diff --git a/net/9p/trans_xen.c b/net/9p/trans_xen.c index aaa5fd364691..9630b1275557 100644 --- a/net/9p/trans_xen.c +++ b/net/9p/trans_xen.c @@ -157,7 +157,7 @@ again: &masked_prod, masked_cons, XEN_9PFS_RING_SIZE(ring)); - p9_req->status = REQ_STATUS_SENT; + WRITE_ONCE(p9_req->status, REQ_STATUS_SENT); virt_wmb(); /* write ring before updating pointer */ prod += size; ring->intf->out_prod = prod; @@ -212,11 +212,13 @@ static void p9_xen_response(struct work_struct *work) dev_warn(&priv->dev->dev, "requested packet size too big: %d for tag %d with capacity %zd\n", h.size, h.tag, req->rc.capacity); - req->status = REQ_STATUS_ERROR; + WRITE_ONCE(req->status, REQ_STATUS_ERROR); goto recv_error; } - memcpy(&req->rc, &h, sizeof(h)); + req->rc.size = h.size; + req->rc.id = h.id; + req->rc.tag = h.tag; req->rc.offset = 0; masked_cons = xen_9pfs_mask(cons, XEN_9PFS_RING_SIZE(ring)); diff --git a/scripts/coccicheck b/scripts/coccicheck index caba0bff6da7..2956fce8fa4f 100755 --- a/scripts/coccicheck +++ b/scripts/coccicheck @@ -47,7 +47,7 @@ FLAGS="--very-quiet" # inspected there. # # --profile will not output if --very-quiet is used, so avoid it. -echo $SPFLAGS | egrep -e "--profile|--show-trying" 2>&1 > /dev/null +echo $SPFLAGS | grep -E -e "--profile|--show-trying" 2>&1 > /dev/null if [ $? -eq 0 ]; then FLAGS="--quiet" fi diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index d766b7d0ffd1..53baa95cb644 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -257,6 +257,9 @@ config INIT_ON_FREE_DEFAULT_ON config CC_HAS_ZERO_CALL_USED_REGS def_bool $(cc-option,-fzero-call-used-regs=used-gpr) + # https://github.com/ClangBuiltLinux/linux/issues/1766 + # https://github.com/llvm/llvm-project/issues/59242 + depends on !CC_IS_CLANG || CLANG_VERSION > 150006 config ZERO_CALL_USED_REGS bool "Enable register zeroing on function exit" diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index de41621f4998..110a5ab2b46b 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -122,21 +122,11 @@ static void loadpin_sb_free_security(struct super_block *mnt_sb) } } -static int loadpin_read_file(struct file *file, enum kernel_read_file_id id, - bool contents) +static int loadpin_check(struct file *file, enum kernel_read_file_id id) { struct super_block *load_root; const char *origin = kernel_read_file_id_str(id); - /* - * If we will not know that we'll be seeing the full contents - * then we cannot trust a load will be complete and unchanged - * off disk. Treat all contents=false hooks as if there were - * no associated file struct. - */ - if (!contents) - file = NULL; - /* If the file id is excluded, ignore the pinning. */ if ((unsigned int)id < ARRAY_SIZE(ignore_read_file_id) && ignore_read_file_id[id]) { @@ -192,9 +182,25 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id, return 0; } +static int loadpin_read_file(struct file *file, enum kernel_read_file_id id, + bool contents) +{ + /* + * LoadPin only cares about the _origin_ of a file, not its + * contents, so we can ignore the "are full contents available" + * argument here. + */ + return loadpin_check(file, id); +} + static int loadpin_load_data(enum kernel_load_data_id id, bool contents) { - return loadpin_read_file(NULL, (enum kernel_read_file_id) id, contents); + /* + * LoadPin only cares about the _origin_ of a file, not its + * contents, so a NULL file is passed, and we can ignore the + * state of "contents". + */ + return loadpin_check(NULL, (enum kernel_read_file_id) id); } static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = { diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c index 3cf5a87d69ea..81025f50a542 100644 --- a/sound/core/memalloc.c +++ b/sound/core/memalloc.c @@ -542,7 +542,7 @@ static void *snd_dma_noncontig_alloc(struct snd_dma_buffer *dmab, size_t size) void *p; sgt = dma_alloc_noncontiguous(dmab->dev.dev, size, dmab->dev.dir, - DEFAULT_GFP | __GFP_COMP, 0); + DEFAULT_GFP, 0); #ifdef CONFIG_SND_DMA_SGBUF if (!sgt && !get_dma_ops(dmab->dev.dev)) { if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_WC_SG) @@ -820,7 +820,7 @@ static void *snd_dma_noncoherent_alloc(struct snd_dma_buffer *dmab, size_t size) void *p; p = dma_alloc_noncoherent(dmab->dev.dev, size, &dmab->addr, - dmab->dev.dir, DEFAULT_GFP | __GFP_COMP); + dmab->dev.dir, DEFAULT_GFP); if (p) dmab->dev.need_sync = dma_need_sync(dmab->dev.dev, dmab->addr); return p; diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c index 2a071a09224d..11b7119cc47e 100644 --- a/sound/hda/ext/hdac_ext_stream.c +++ b/sound/hda/ext/hdac_ext_stream.c @@ -14,6 +14,7 @@ #include #include #include +#include /** * snd_hdac_ext_stream_init - initialize each stream (aka device) @@ -367,3 +368,43 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *hext_stream, int type) } EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_release); + +/** + * snd_hdac_ext_cstream_assign - assign a host stream for compress + * @bus: HD-audio core bus + * @cstream: Compress stream to assign + * + * Assign an unused host stream for the given compress stream. + * If no stream is free, NULL is returned. Stream is decoupled + * before assignment. + */ +struct hdac_ext_stream *snd_hdac_ext_cstream_assign(struct hdac_bus *bus, + struct snd_compr_stream *cstream) +{ + struct hdac_ext_stream *res = NULL; + struct hdac_stream *hstream; + + spin_lock_irq(&bus->reg_lock); + list_for_each_entry(hstream, &bus->stream_list, list) { + struct hdac_ext_stream *hext_stream = stream_to_hdac_ext_stream(hstream); + + if (hstream->direction != cstream->direction) + continue; + + if (!hstream->opened) { + res = hext_stream; + break; + } + } + + if (res) { + snd_hdac_ext_stream_decouple_locked(bus, res, true); + res->hstream.opened = 1; + res->hstream.running = 0; + res->hstream.cstream = cstream; + } + spin_unlock_irq(&bus->reg_lock); + + return res; +} +EXPORT_SYMBOL_GPL(snd_hdac_ext_cstream_assign); diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c index 9a60bfdb39ba..3c7af6558249 100644 --- a/sound/hda/hdac_controller.c +++ b/sound/hda/hdac_controller.c @@ -578,8 +578,8 @@ int snd_hdac_bus_handle_stream_irq(struct hdac_bus *bus, unsigned int status, sd_status = snd_hdac_stream_readb(azx_dev, SD_STS); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); handled |= 1 << azx_dev->index; - if (!azx_dev->substream || !azx_dev->running || - !(sd_status & SD_INT_COMPLETE)) + if ((!azx_dev->substream && !azx_dev->cstream) || + !azx_dev->running || !(sd_status & SD_INT_COMPLETE)) continue; if (ack) ack(bus, azx_dev); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c index 3b250ee7f6a7..547adbc22590 100644 --- a/sound/hda/hdac_stream.c +++ b/sound/hda/hdac_stream.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -487,11 +488,23 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) { struct hdac_bus *bus = azx_dev->bus; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_compr_stream *cstream = azx_dev->cstream; + struct snd_pcm_runtime *runtime = NULL; + struct snd_dma_buffer *dmab; __le32 *bdl; int i, ofs, periods, period_bytes; int pos_adj, pos_align; + if (substream) { + runtime = substream->runtime; + dmab = snd_pcm_get_dma_buf(substream); + } else if (cstream) { + dmab = snd_pcm_get_dma_buf(cstream); + } else { + WARN(1, "No substream or cstream assigned\n"); + return -EINVAL; + } + /* reset BDL address */ snd_hdac_stream_writel(azx_dev, SD_BDLPL, 0); snd_hdac_stream_writel(azx_dev, SD_BDLPU, 0); @@ -505,7 +518,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) azx_dev->frags = 0; pos_adj = bus->bdl_pos_adj; - if (!azx_dev->no_period_wakeup && pos_adj > 0) { + if (runtime && !azx_dev->no_period_wakeup && pos_adj > 0) { pos_align = pos_adj; pos_adj = DIV_ROUND_UP(pos_adj * runtime->rate, 48000); if (!pos_adj) @@ -518,8 +531,7 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) pos_adj); pos_adj = 0; } else { - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, + ofs = setup_bdle(bus, dmab, azx_dev, &bdl, ofs, pos_adj, true); if (ofs < 0) goto error; @@ -529,13 +541,11 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev) for (i = 0; i < periods; i++) { if (i == periods - 1 && pos_adj) - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes - pos_adj, 0); + ofs = setup_bdle(bus, dmab, azx_dev, + &bdl, ofs, period_bytes - pos_adj, 0); else - ofs = setup_bdle(bus, snd_pcm_get_dma_buf(substream), - azx_dev, &bdl, ofs, - period_bytes, + ofs = setup_bdle(bus, dmab, azx_dev, + &bdl, ofs, period_bytes, !azx_dev->no_period_wakeup); if (ofs < 0) goto error; @@ -560,26 +570,32 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods); int snd_hdac_stream_set_params(struct hdac_stream *azx_dev, unsigned int format_val) { - - unsigned int bufsize, period_bytes; struct snd_pcm_substream *substream = azx_dev->substream; - struct snd_pcm_runtime *runtime; + struct snd_compr_stream *cstream = azx_dev->cstream; + unsigned int bufsize, period_bytes; + unsigned int no_period_wakeup; int err; - if (!substream) + if (substream) { + bufsize = snd_pcm_lib_buffer_bytes(substream); + period_bytes = snd_pcm_lib_period_bytes(substream); + no_period_wakeup = substream->runtime->no_period_wakeup; + } else if (cstream) { + bufsize = cstream->runtime->buffer_size; + period_bytes = cstream->runtime->fragment_size; + no_period_wakeup = 0; + } else { return -EINVAL; - runtime = substream->runtime; - bufsize = snd_pcm_lib_buffer_bytes(substream); - period_bytes = snd_pcm_lib_period_bytes(substream); + } if (bufsize != azx_dev->bufsize || period_bytes != azx_dev->period_bytes || format_val != azx_dev->format_val || - runtime->no_period_wakeup != azx_dev->no_period_wakeup) { + no_period_wakeup != azx_dev->no_period_wakeup) { azx_dev->bufsize = bufsize; azx_dev->period_bytes = period_bytes; azx_dev->format_val = format_val; - azx_dev->no_period_wakeup = runtime->no_period_wakeup; + azx_dev->no_period_wakeup = no_period_wakeup; err = snd_hdac_stream_setup_periods(azx_dev); if (err < 0) return err; diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c index 7f329dfc5404..0c6754bf9455 100644 --- a/sound/pci/azt3328.c +++ b/sound/pci/azt3328.c @@ -363,15 +363,6 @@ snd_azf3328_codec_inw(const struct snd_azf3328_codec_data *codec, unsigned reg) return inw(codec->io_base + reg); } -static inline void -snd_azf3328_codec_outl(const struct snd_azf3328_codec_data *codec, - unsigned reg, - u32 value -) -{ - outl(value, codec->io_base + reg); -} - static inline void snd_azf3328_codec_outl_multi(const struct snd_azf3328_codec_data *codec, unsigned reg, const void *buffer, int count diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index b4d1e658c556..edd653ece70d 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2886,7 +2886,8 @@ static unsigned int hda_call_codec_suspend(struct hda_codec *codec) snd_hdac_enter_pm(&codec->core); if (codec->patch_ops.suspend) codec->patch_ops.suspend(codec); - hda_cleanup_all_streams(codec); + if (!codec->no_stream_clean_at_suspend) + hda_cleanup_all_streams(codec); state = hda_set_power_state(codec, AC_PWRST_D3); update_power_acct(codec, true); snd_hdac_leave_pm(&codec->core); diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c index 21edf7a619f0..8015e4471267 100644 --- a/sound/pci/hda/patch_hdmi.c +++ b/sound/pci/hda/patch_hdmi.c @@ -1738,6 +1738,7 @@ static void silent_stream_enable(struct hda_codec *codec, switch (spec->silent_stream_type) { case SILENT_STREAM_KAE: + silent_stream_enable_i915(codec, per_pin); silent_stream_set_kae(codec, per_pin, true); break; case SILENT_STREAM_I915: @@ -1975,6 +1976,7 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid) static const struct snd_pci_quirk force_connect_list[] = { SND_PCI_QUIRK(0x103c, 0x870f, "HP", 1), SND_PCI_QUIRK(0x103c, 0x871a, "HP", 1), + SND_PCI_QUIRK(0x103c, 0x8711, "HP", 1), SND_PCI_QUIRK(0x1462, 0xec94, "MS-7C94", 1), SND_PCI_QUIRK(0x8086, 0x2081, "Intel NUC 10", 1), {} @@ -2878,9 +2880,33 @@ static int i915_hsw_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid, hda_nid_t pin_nid, int dev_id, u32 stream_tag, int format) { + struct hdmi_spec *spec = codec->spec; + int pin_idx = pin_id_to_pin_index(codec, pin_nid, dev_id); + struct hdmi_spec_per_pin *per_pin; + int res; + + if (pin_idx < 0) + per_pin = NULL; + else + per_pin = get_pin(spec, pin_idx); + haswell_verify_D0(codec, cvt_nid, pin_nid); - return hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, - stream_tag, format); + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + silent_stream_set_kae(codec, per_pin, false); + /* wait for pending transfers in codec to clear */ + usleep_range(100, 200); + } + + res = hdmi_setup_stream(codec, cvt_nid, pin_nid, dev_id, + stream_tag, format); + + if (spec->silent_stream_type == SILENT_STREAM_KAE && per_pin && per_pin->silent_stream) { + usleep_range(100, 200); + silent_stream_set_kae(codec, per_pin, true); + } + + return res; } /* pin_cvt_fixup ops override for HSW+ and VLV+ */ @@ -2900,6 +2926,88 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec, } } +#ifdef CONFIG_PM +static int i915_adlp_hdmi_suspend(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + bool silent_streams = false; + int pin_idx, res; + + res = generic_hdmi_suspend(codec); + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + if (per_pin->silent_stream) { + silent_streams = true; + break; + } + } + + if (silent_streams && spec->silent_stream_type == SILENT_STREAM_KAE) { + /* + * stream-id should remain programmed when codec goes + * to runtime suspend + */ + codec->no_stream_clean_at_suspend = 1; + + /* + * the system might go to S3, in which case keep-alive + * must be reprogrammed upon resume + */ + codec->forced_resume = 1; + + codec_dbg(codec, "HDMI: KAE active at suspend\n"); + } else { + codec->no_stream_clean_at_suspend = 0; + codec->forced_resume = 0; + } + + return res; +} + +static int i915_adlp_hdmi_resume(struct hda_codec *codec) +{ + struct hdmi_spec *spec = codec->spec; + int pin_idx, res; + + res = generic_hdmi_resume(codec); + + /* KAE not programmed at suspend, nothing to do here */ + if (!codec->no_stream_clean_at_suspend) + return res; + + for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) { + struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx); + + /* + * If system was in suspend with monitor connected, + * the codec setting may have been lost. Re-enable + * keep-alive. + */ + if (per_pin->silent_stream) { + unsigned int param; + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, + AC_VERB_GET_CONV, 0); + if (!param) { + codec_dbg(codec, "HDMI: KAE: restore stream id\n"); + silent_stream_enable_i915(codec, per_pin); + } + + param = snd_hda_codec_read(codec, per_pin->cvt_nid, 0, + AC_VERB_GET_DIGI_CONVERT_1, 0); + if (!(param & (AC_DIG3_KAE << 16))) { + codec_dbg(codec, "HDMI: KAE: restore DIG3_KAE\n"); + silent_stream_set_kae(codec, per_pin, true); + } + } + } + + return res; +} +#endif + /* precondition and allocation for Intel codecs */ static int alloc_intel_hdmi(struct hda_codec *codec) { @@ -3030,8 +3138,14 @@ static int patch_i915_adlp_hdmi(struct hda_codec *codec) if (!res) { spec = codec->spec; - if (spec->silent_stream_type) + if (spec->silent_stream_type) { spec->silent_stream_type = SILENT_STREAM_KAE; + +#ifdef CONFIG_PM + codec->patch_ops.resume = i915_adlp_hdmi_resume; + codec->patch_ops.suspend = i915_adlp_hdmi_suspend; +#endif + } } return res; diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 5f51f8fc7fdc..e443d88f627f 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -10999,6 +10999,17 @@ static void alc897_fixup_lenovo_headset_mic(struct hda_codec *codec, } } +static void alc897_fixup_lenovo_headset_mode(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct alc_spec *spec = codec->spec; + + if (action == HDA_FIXUP_ACT_PRE_PROBE) { + spec->parse_flags |= HDA_PINCFG_HEADSET_MIC; + spec->gen.hp_automute_hook = alc897_hp_automute_hook; + } +} + static const struct coef_fw alc668_coefs[] = { WRITE_COEF(0x01, 0xbebe), WRITE_COEF(0x02, 0xaaaa), WRITE_COEF(0x03, 0x0), WRITE_COEF(0x04, 0x0180), WRITE_COEF(0x06, 0x0), WRITE_COEF(0x07, 0x0f80), @@ -11082,6 +11093,8 @@ enum { ALC897_FIXUP_LENOVO_HEADSET_MIC, ALC897_FIXUP_HEADSET_MIC_PIN, ALC897_FIXUP_HP_HSMIC_VERB, + ALC897_FIXUP_LENOVO_HEADSET_MODE, + ALC897_FIXUP_HEADSET_MIC_PIN2, }; static const struct hda_fixup alc662_fixups[] = { @@ -11508,6 +11521,19 @@ static const struct hda_fixup alc662_fixups[] = { { } }, }, + [ALC897_FIXUP_LENOVO_HEADSET_MODE] = { + .type = HDA_FIXUP_FUNC, + .v.func = alc897_fixup_lenovo_headset_mode, + }, + [ALC897_FIXUP_HEADSET_MIC_PIN2] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x1a, 0x01a11140 }, /* use as headset mic, without its own jack detect */ + { } + }, + .chained = true, + .chain_id = ALC897_FIXUP_LENOVO_HEADSET_MODE + }, }; static const struct snd_pci_quirk alc662_fixup_tbl[] = { @@ -11560,6 +11586,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x32cb, "Lenovo ThinkCentre M70", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32cf, "Lenovo ThinkCentre M950", ALC897_FIXUP_HEADSET_MIC_PIN), SND_PCI_QUIRK(0x17aa, 0x32f7, "Lenovo ThinkCentre M90", ALC897_FIXUP_HEADSET_MIC_PIN), + SND_PCI_QUIRK(0x17aa, 0x3742, "Lenovo TianYi510Pro-14IOB", ALC897_FIXUP_HEADSET_MIC_PIN2), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD), SND_PCI_QUIRK(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO), diff --git a/sound/soc/codecs/lochnagar-sc.c b/sound/soc/codecs/lochnagar-sc.c index 13fbd8830b09..5e0bd0d24ed3 100644 --- a/sound/soc/codecs/lochnagar-sc.c +++ b/sound/soc/codecs/lochnagar-sc.c @@ -253,7 +253,7 @@ MODULE_DEVICE_TABLE(of, lochnagar_of_match); static struct platform_driver lochnagar_sc_codec_driver = { .driver = { .name = "lochnagar-soundcard", - .of_match_table = of_match_ptr(lochnagar_of_match), + .of_match_table = lochnagar_of_match, }, .probe = lochnagar_sc_probe, diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index ebac6caeb40a..a230f441559a 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -3311,8 +3311,6 @@ static int rt5670_i2c_probe(struct i2c_client *i2c) if (ret < 0) goto err; - pm_runtime_put(&i2c->dev); - return 0; err: pm_runtime_disable(&i2c->dev); diff --git a/sound/soc/codecs/wcd-clsh-v2.c b/sound/soc/codecs/wcd-clsh-v2.c index 4c7ebc7fb400..a75db27e5205 100644 --- a/sound/soc/codecs/wcd-clsh-v2.c +++ b/sound/soc/codecs/wcd-clsh-v2.c @@ -130,12 +130,6 @@ static inline void wcd_enable_clsh_block(struct wcd_clsh_ctrl *ctrl, ctrl->clsh_users = 0; } -static inline bool wcd_clsh_enable_status(struct snd_soc_component *comp) -{ - return snd_soc_component_read(comp, WCD9XXX_A_CDC_CLSH_CRC) & - WCD9XXX_A_CDC_CLSH_CRC_CLK_EN_MASK; -} - static inline void wcd_clsh_set_buck_mode(struct snd_soc_component *comp, int mode) { diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c index d3cfd3788f2a..8fe9a75d1235 100644 --- a/sound/soc/codecs/wm8994.c +++ b/sound/soc/codecs/wm8994.c @@ -3853,7 +3853,12 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data) } else { dev_dbg(component->dev, "Jack not detected\n"); + /* Release wm8994->accdet_lock to avoid deadlock: + * cancel_delayed_work_sync() takes wm8994->mic_work internal + * lock and wm1811_mic_work takes wm8994->accdet_lock */ + mutex_unlock(&wm8994->accdet_lock); cancel_delayed_work_sync(&wm8994->mic_work); + mutex_lock(&wm8994->accdet_lock); snd_soc_component_update_bits(component, WM8958_MICBIAS2, WM8958_MICB2_DISCH, WM8958_MICB2_DISCH); diff --git a/sound/soc/fsl/imx-audmux.c b/sound/soc/fsl/imx-audmux.c index 50b71e5d4589..582f1e2431ee 100644 --- a/sound/soc/fsl/imx-audmux.c +++ b/sound/soc/fsl/imx-audmux.c @@ -75,8 +75,7 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf, if (!buf) return -ENOMEM; - ret = scnprintf(buf, PAGE_SIZE, "PDCR: %08x\nPTCR: %08x\n", - pdcr, ptcr); + ret = sysfs_emit(buf, "PDCR: %08x\nPTCR: %08x\n", pdcr, ptcr); if (ptcr & IMX_AUDMUX_V2_PTCR_TFSDIR) ret += scnprintf(buf + ret, PAGE_SIZE - ret, diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c index fe7cf972d44c..5daa824a4ffc 100644 --- a/sound/soc/generic/audio-graph-card.c +++ b/sound/soc/generic/audio-graph-card.c @@ -485,8 +485,10 @@ static int __graph_for_each_link(struct asoc_simple_priv *priv, of_node_put(codec_ep); of_node_put(codec_port); - if (ret < 0) + if (ret < 0) { + of_node_put(cpu_ep); return ret; + } codec_port_old = codec_port; } diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig index ac799de4f7fd..4b9e498e3303 100644 --- a/sound/soc/intel/Kconfig +++ b/sound/soc/intel/Kconfig @@ -217,6 +217,7 @@ config SND_SOC_INTEL_AVS select SND_SOC_ACPI if ACPI select SND_SOC_TOPOLOGY select SND_SOC_HDA + select SND_SOC_COMPRESS if DEBUG_FS select SND_HDA_EXT_CORE select SND_HDA_DSP_LOADER select SND_INTEL_DSP_CONFIG diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile index 919212825f21..1c6924a1ebca 100644 --- a/sound/soc/intel/avs/Makefile +++ b/sound/soc/intel/avs/Makefile @@ -9,6 +9,10 @@ snd-soc-avs-objs += trace.o # tell define_trace.h where to find the trace header CFLAGS_trace.o := -I$(src) +ifneq ($(CONFIG_DEBUG_FS),) +snd-soc-avs-objs += probes.o debugfs.o +endif + obj-$(CONFIG_SND_SOC_INTEL_AVS) += snd-soc-avs.o # Machine support diff --git a/sound/soc/intel/avs/apl.c b/sound/soc/intel/avs/apl.c index 7c8ce98eda9d..02683dce277a 100644 --- a/sound/soc/intel/avs/apl.c +++ b/sound/soc/intel/avs/apl.c @@ -13,8 +13,9 @@ #include "path.h" #include "topology.h" -static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +static int __maybe_unused +apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { struct apl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; @@ -50,7 +51,6 @@ static int apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { struct apl_log_buffer_layout layout; - unsigned long flags; void __iomem *addr, *buf; addr = avs_log_buffer_addr(adev, msg->log.core); @@ -59,26 +59,20 @@ static int apl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg memcpy_fromio(&layout, addr, sizeof(layout)); - spin_lock_irqsave(&adev->dbg.trace_lock, flags); - if (!kfifo_initialized(&adev->dbg.trace_fifo)) + if (!avs_logging_fw(adev)) /* consume the logs regardless of consumer presence */ goto update_read_ptr; buf = apl_log_payload_addr(addr); if (layout.read_ptr > layout.write_ptr) { - __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, - apl_log_payload_size(adev) - layout.read_ptr, - &adev->dbg.fifo_lock); + avs_dump_fw_log(adev, buf + layout.read_ptr, + apl_log_payload_size(adev) - layout.read_ptr); layout.read_ptr = 0; } - __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf + layout.read_ptr, - layout.write_ptr - layout.read_ptr, &adev->dbg.fifo_lock); - - wake_up(&adev->dbg.trace_waitq); + avs_dump_fw_log_wakeup(adev, buf + layout.read_ptr, layout.write_ptr - layout.read_ptr); update_read_ptr: - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); writel(layout.write_ptr, addr); return 0; } @@ -140,7 +134,7 @@ static int apl_coredump(struct avs_dev *adev, union avs_notify_msg *msg) * gathered before dumping stack */ lbs_msg.log.core = msg->ext.coredump.core_id; - avs_dsp_op(adev, log_buffer_status, &lbs_msg); + avs_log_buffer_status_locked(adev, &lbs_msg); } pos = dump + AVS_FW_REGS_SIZE; @@ -243,10 +237,10 @@ const struct avs_dsp_ops apl_dsp_ops = { .load_basefw = avs_hda_load_basefw, .load_lib = avs_hda_load_library, .transfer_mods = avs_hda_transfer_modules, - .enable_logs = apl_enable_logs, .log_buffer_offset = skl_log_buffer_offset, .log_buffer_status = apl_log_buffer_status, .coredump = apl_coredump, .d0ix_toggle = apl_d0ix_toggle, .set_d0ix = apl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(apl) }; diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h index 8d05b27608fe..d7fccdcb9c16 100644 --- a/sound/soc/intel/avs/avs.h +++ b/sound/soc/intel/avs/avs.h @@ -9,6 +9,7 @@ #ifndef __SOUND_SOC_INTEL_AVS_H #define __SOUND_SOC_INTEL_AVS_H +#include #include #include #include @@ -93,16 +94,6 @@ struct avs_fw_entry { struct list_head node; }; -struct avs_debug { - struct kfifo trace_fifo; - spinlock_t fifo_lock; /* serialize I/O for trace_fifo */ - spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */ - wait_queue_head_t trace_waitq; - u32 aging_timer_period; - u32 fifo_full_timer_period; - u32 logged_resources; /* context dependent: core or library */ -}; - /* * struct avs_dev - Intel HD-Audio driver data * @@ -146,7 +137,18 @@ struct avs_dev { spinlock_t path_list_lock; struct mutex path_mutex; - struct avs_debug dbg; + spinlock_t trace_lock; /* serialize debug window I/O between each LOG_BUFFER_STATUS */ +#ifdef CONFIG_DEBUG_FS + struct kfifo trace_fifo; + wait_queue_head_t trace_waitq; + u32 aging_timer_period; + u32 fifo_full_timer_period; + u32 logged_resources; /* context dependent: core or library */ + struct dentry *debugfs_root; + /* probes */ + struct hdac_ext_stream *extractor; + unsigned int num_probe_streams; +#endif }; /* from hda_bus to avs_dev */ @@ -321,6 +323,9 @@ struct avs_soc_component { extern const struct snd_soc_dai_ops avs_dai_fe_ops; +int avs_soc_component_register(struct device *dev, const char *name, + const struct snd_soc_component_driver *drv, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais); int avs_dmic_platform_register(struct avs_dev *adev, const char *name); int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned long port_mask, unsigned long *tdms); @@ -331,9 +336,6 @@ void avs_unregister_all_boards(struct avs_dev *adev); /* Firmware tracing helpers */ -unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, - spinlock_t *lock); - #define avs_log_buffer_size(adev) \ ((adev)->fw_cfg.trace_log_bytes / (adev)->hw_cfg.dsp_cores) @@ -344,6 +346,18 @@ unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, (avs_sram_addr(adev, AVS_DEBUG_WINDOW) + __offset); \ }) +static inline int avs_log_buffer_status_locked(struct avs_dev *adev, union avs_notify_msg *msg) +{ + unsigned long flags; + int ret; + + spin_lock_irqsave(&adev->trace_lock, flags); + ret = avs_dsp_op(adev, log_buffer_status, msg); + spin_unlock_irqrestore(&adev->trace_lock, flags); + + return ret; +} + struct apl_log_buffer_layout { u32 read_ptr; u32 write_ptr; @@ -356,4 +370,42 @@ struct apl_log_buffer_layout { #define apl_log_payload_addr(addr) \ (addr + sizeof(struct apl_log_buffer_layout)) +#ifdef CONFIG_DEBUG_FS +#define AVS_SET_ENABLE_LOGS_OP(name) \ + .enable_logs = name##_enable_logs + +bool avs_logging_fw(struct avs_dev *adev); +void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len); +void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len); + +int avs_probe_platform_register(struct avs_dev *adev, const char *name); + +void avs_debugfs_init(struct avs_dev *adev); +void avs_debugfs_exit(struct avs_dev *adev); +#else +#define AVS_SET_ENABLE_LOGS_OP(name) + +static inline bool avs_logging_fw(struct avs_dev *adev) +{ + return false; +} + +static inline void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ +} + +static inline void +avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ +} + +static inline int avs_probe_platform_register(struct avs_dev *adev, const char *name) +{ + return 0; +} + +static inline void avs_debugfs_init(struct avs_dev *adev) { } +static inline void avs_debugfs_exit(struct avs_dev *adev) { } +#endif + #endif /* __SOUND_SOC_INTEL_AVS_H */ diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c index 02cc1ce8f5f5..b2823c2107f7 100644 --- a/sound/soc/intel/avs/board_selection.c +++ b/sound/soc/intel/avs/board_selection.c @@ -291,6 +291,33 @@ static void board_pdev_unregister(void *data) platform_device_unregister(data); } +static int __maybe_unused avs_register_probe_board(struct avs_dev *adev) +{ + struct platform_device *board; + struct snd_soc_acpi_mach mach = {{0}}; + int ret; + + ret = avs_probe_platform_register(adev, "probe-platform"); + if (ret < 0) + return ret; + + mach.mach_params.platform = "probe-platform"; + + board = platform_device_register_data(NULL, "avs_probe_mb", PLATFORM_DEVID_NONE, + (const void *)&mach, sizeof(mach)); + if (IS_ERR(board)) { + dev_err(adev->dev, "probe board register failed\n"); + return PTR_ERR(board); + } + + ret = devm_add_action(adev->dev, board_pdev_unregister, board); + if (ret < 0) { + platform_device_unregister(board); + return ret; + } + return 0; +} + static int avs_register_dmic_board(struct avs_dev *adev) { struct platform_device *codec, *board; @@ -500,6 +527,12 @@ int avs_register_all_boards(struct avs_dev *adev) { int ret; +#ifdef CONFIG_DEBUG_FS + ret = avs_register_probe_board(adev); + if (ret < 0) + dev_warn(adev->dev, "enumerate PROBE endpoints failed: %d\n", ret); +#endif + ret = avs_register_dmic_board(adev); if (ret < 0) dev_warn(adev->dev, "enumerate DMIC endpoints failed: %d\n", diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig index 9bd40fdd9028..e4c230efe8d7 100644 --- a/sound/soc/intel/avs/boards/Kconfig +++ b/sound/soc/intel/avs/boards/Kconfig @@ -77,6 +77,14 @@ config SND_SOC_INTEL_AVS_MACH_NAU8825 Say Y or m if you have such a device. This is a recommended option. If unsure select "N". +config SND_SOC_INTEL_AVS_MACH_PROBE + tristate "Probing (data) board" + depends on DEBUG_FS + select SND_HWDEP + help + This adds support for data probing board which can be used to + gather data from runtime stream over compress operations. + config SND_SOC_INTEL_AVS_MACH_RT274 tristate "rt274 in I2S mode" depends on I2C diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile index 4d70b8d09ce5..b81343420370 100644 --- a/sound/soc/intel/avs/boards/Makefile +++ b/sound/soc/intel/avs/boards/Makefile @@ -8,6 +8,7 @@ snd-soc-avs-max98927-objs := max98927.o snd-soc-avs-max98357a-objs := max98357a.o snd-soc-avs-max98373-objs := max98373.o snd-soc-avs-nau8825-objs := nau8825.o +snd-soc-avs-probe-objs := probe.o snd-soc-avs-rt274-objs := rt274.o snd-soc-avs-rt286-objs := rt286.o snd-soc-avs-rt298-objs := rt298.o @@ -22,6 +23,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98927) += snd-soc-avs-max98927.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98357A) += snd-soc-avs-max98357a.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_MAX98373) += snd-soc-avs-max98373.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_NAU8825) += snd-soc-avs-nau8825.o +obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_PROBE) += snd-soc-avs-probe.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c new file mode 100644 index 000000000000..411acaee74f9 --- /dev/null +++ b/sound/soc/intel/avs/boards/probe.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include + +SND_SOC_DAILINK_DEF(dummy, DAILINK_COMP_ARRAY(COMP_DUMMY())); +SND_SOC_DAILINK_DEF(probe_cp, DAILINK_COMP_ARRAY(COMP_CPU("Probe Extraction CPU DAI"))); +SND_SOC_DAILINK_DEF(platform, DAILINK_COMP_ARRAY(COMP_PLATFORM("probe-platform"))); + +static struct snd_soc_dai_link probe_mb_dai_links[] = { + { + .name = "Compress Probe Capture", + .nonatomic = 1, + SND_SOC_DAILINK_REG(probe_cp, dummy, platform), + }, +}; + +static int avs_probe_mb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct snd_soc_acpi_mach *mach; + struct snd_soc_card *card; + int ret; + + mach = dev_get_platdata(dev); + + card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL); + if (!card) + return -ENOMEM; + + card->name = "avs_probe_mb"; + card->dev = dev; + card->owner = THIS_MODULE; + card->dai_link = probe_mb_dai_links; + card->num_links = ARRAY_SIZE(probe_mb_dai_links); + card->fully_routed = true; + + ret = snd_soc_fixup_dai_links_platform_name(card, mach->mach_params.platform); + if (ret) + return ret; + + return devm_snd_soc_register_card(dev, card); +} + +static struct platform_driver avs_probe_mb_driver = { + .probe = avs_probe_mb_probe, + .driver = { + .name = "avs_probe_mb", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(avs_probe_mb_driver); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:avs_probe_mb"); diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c index f7bc06404dbc..2ca24273c491 100644 --- a/sound/soc/intel/avs/core.c +++ b/sound/soc/intel/avs/core.c @@ -214,6 +214,7 @@ static void avs_hda_probe_work(struct work_struct *work) adev->nhlt = intel_nhlt_init(adev->dev); if (!adev->nhlt) dev_info(bus->dev, "platform has no NHLT\n"); + avs_debugfs_init(adev); avs_register_all_boards(adev); @@ -491,6 +492,7 @@ static void avs_pci_remove(struct pci_dev *pci) avs_unregister_all_boards(adev); + avs_debugfs_exit(adev); if (adev->nhlt) intel_nhlt_free(adev->nhlt); diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c new file mode 100644 index 000000000000..bdd388ec01ea --- /dev/null +++ b/sound/soc/intel/avs/debugfs.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include +#include +#include "avs.h" +#include "messages.h" + +static unsigned int __kfifo_fromio(struct kfifo *fifo, const void __iomem *src, unsigned int len) +{ + struct __kfifo *__fifo = &fifo->kfifo; + unsigned int l, off; + + len = min(len, kfifo_avail(fifo)); + off = __fifo->in & __fifo->mask; + l = min(len, kfifo_size(fifo) - off); + + memcpy_fromio(__fifo->data + off, src, l); + memcpy_fromio(__fifo->data, src + l, len - l); + /* Make sure data copied from SRAM is visible to all CPUs. */ + smp_mb(); + __fifo->in += len; + + return len; +} + +bool avs_logging_fw(struct avs_dev *adev) +{ + return kfifo_initialized(&adev->trace_fifo); +} + +void avs_dump_fw_log(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ + __kfifo_fromio(&adev->trace_fifo, src, len); +} + +void avs_dump_fw_log_wakeup(struct avs_dev *adev, const void __iomem *src, unsigned int len) +{ + avs_dump_fw_log(adev, src, len); + wake_up(&adev->trace_waitq); +} + +static ssize_t fw_regs_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + char *buf; + int ret; + + buf = kzalloc(AVS_FW_REGS_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy_fromio(buf, avs_sram_addr(adev, AVS_FW_REGS_WINDOW), AVS_FW_REGS_SIZE); + + ret = simple_read_from_buffer(to, count, ppos, buf, AVS_FW_REGS_SIZE); + kfree(buf); + return ret; +} + +static const struct file_operations fw_regs_fops = { + .open = simple_open, + .read = fw_regs_read, + .llseek = no_llseek, +}; + +static ssize_t debug_window_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + size_t size; + char *buf; + int ret; + + size = adev->hw_cfg.dsp_cores * AVS_WINDOW_CHUNK_SIZE; + buf = kzalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy_fromio(buf, avs_sram_addr(adev, AVS_DEBUG_WINDOW), size); + + ret = simple_read_from_buffer(to, count, ppos, buf, size); + kfree(buf); + return ret; +} + +static const struct file_operations debug_window_fops = { + .open = simple_open, + .read = debug_window_read, + .llseek = no_llseek, +}; + +static ssize_t probe_points_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + struct avs_probe_point_desc *desc; + size_t num_desc, len = 0; + char *buf; + int i, ret; + + /* Prevent chaining, send and dump IPC value just once. */ + if (*ppos) + return 0; + + buf = kzalloc(PAGE_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = avs_ipc_probe_get_points(adev, &desc, &num_desc); + if (ret) { + ret = AVS_IPC_RET(ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) { + ret = snprintf(buf + len, PAGE_SIZE - len, + "Id: %#010x Purpose: %d Node id: %#x\n", + desc[i].id.value, desc[i].purpose, desc[i].node_id.val); + if (ret < 0) + goto free_desc; + len += ret; + } + + ret = simple_read_from_buffer(to, count, ppos, buf, len); +free_desc: + kfree(desc); +exit: + kfree(buf); + return ret; +} + +static ssize_t probe_points_write(struct file *file, const char __user *from, size_t count, + loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + struct avs_probe_point_desc *desc; + u32 *array, num_elems; + size_t bytes; + int ret; + + ret = parse_int_array_user(from, count, (int **)&array); + if (ret < 0) + return ret; + + num_elems = *array; + bytes = sizeof(*array) * num_elems; + if (bytes % sizeof(*desc)) { + ret = -EINVAL; + goto exit; + } + + desc = (struct avs_probe_point_desc *)&array[1]; + ret = avs_ipc_probe_connect_points(adev, desc, bytes / sizeof(*desc)); + if (ret) + ret = AVS_IPC_RET(ret); + else + ret = count; +exit: + kfree(array); + return ret; +} + +static const struct file_operations probe_points_fops = { + .open = simple_open, + .read = probe_points_read, + .write = probe_points_write, + .llseek = no_llseek, +}; + +static ssize_t probe_points_disconnect_write(struct file *file, const char __user *from, + size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + union avs_probe_point_id *id; + u32 *array, num_elems; + size_t bytes; + int ret; + + ret = parse_int_array_user(from, count, (int **)&array); + if (ret < 0) + return ret; + + num_elems = *array; + bytes = sizeof(*array) * num_elems; + if (bytes % sizeof(*id)) { + ret = -EINVAL; + goto exit; + } + + id = (union avs_probe_point_id *)&array[1]; + ret = avs_ipc_probe_disconnect_points(adev, id, bytes / sizeof(*id)); + if (ret) + ret = AVS_IPC_RET(ret); + else + ret = count; +exit: + kfree(array); + return ret; +} + +static const struct file_operations probe_points_disconnect_fops = { + .open = simple_open, + .write = probe_points_disconnect_write, + .llseek = default_llseek, +}; + +static ssize_t strace_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + struct kfifo *fifo = &adev->trace_fifo; + unsigned int copied; + + if (kfifo_is_empty(fifo)) { + DEFINE_WAIT(wait); + + prepare_to_wait(&adev->trace_waitq, &wait, TASK_INTERRUPTIBLE); + if (!signal_pending(current)) + schedule(); + finish_wait(&adev->trace_waitq, &wait); + } + + if (kfifo_to_user(fifo, to, count, &copied)) + return -EFAULT; + *ppos += copied; + return copied; +} + +static int strace_open(struct inode *inode, struct file *file) +{ + struct avs_dev *adev = inode->i_private; + int ret; + + if (kfifo_initialized(&adev->trace_fifo)) + return -EBUSY; + + ret = kfifo_alloc(&adev->trace_fifo, PAGE_SIZE, GFP_KERNEL); + if (ret < 0) + return ret; + + file->private_data = adev; + return 0; +} + +static int strace_release(struct inode *inode, struct file *file) +{ + union avs_notify_msg msg = AVS_NOTIFICATION(LOG_BUFFER_STATUS); + struct avs_dev *adev = file->private_data; + unsigned long resource_mask; + unsigned long flags, i; + u32 num_cores; + + resource_mask = adev->logged_resources; + num_cores = adev->hw_cfg.dsp_cores; + + spin_lock_irqsave(&adev->trace_lock, flags); + + /* Gather any remaining logs. */ + for_each_set_bit(i, &resource_mask, num_cores) { + msg.log.core = i; + avs_dsp_op(adev, log_buffer_status, &msg); + } + + kfifo_free(&adev->trace_fifo); + + spin_unlock_irqrestore(&adev->trace_lock, flags); + + return 0; +} + +static const struct file_operations strace_fops = { + .llseek = default_llseek, + .read = strace_read, + .open = strace_open, + .release = strace_release, +}; + +#define DISABLE_TIMERS UINT_MAX + +static int enable_logs(struct avs_dev *adev, u32 resource_mask, u32 *priorities) +{ + int ret; + + /* Logging demands D0i0 state from DSP. */ + if (!adev->logged_resources) { + pm_runtime_get_sync(adev->dev); + + ret = avs_dsp_disable_d0ix(adev); + if (ret) + goto err_d0ix; + } + + ret = avs_ipc_set_system_time(adev); + if (ret && ret != AVS_IPC_NOT_SUPPORTED) { + ret = AVS_IPC_RET(ret); + goto err_ipc; + } + + ret = avs_dsp_op(adev, enable_logs, AVS_LOG_ENABLE, adev->aging_timer_period, + adev->fifo_full_timer_period, resource_mask, priorities); + if (ret) + goto err_ipc; + + adev->logged_resources |= resource_mask; + return 0; + +err_ipc: + if (!adev->logged_resources) { + avs_dsp_enable_d0ix(adev); +err_d0ix: + pm_runtime_mark_last_busy(adev->dev); + pm_runtime_put_autosuspend(adev->dev); + } + + return ret; +} + +static int disable_logs(struct avs_dev *adev, u32 resource_mask) +{ + int ret; + + /* Check if there's anything to do. */ + if (!adev->logged_resources) + return 0; + + ret = avs_dsp_op(adev, enable_logs, AVS_LOG_DISABLE, DISABLE_TIMERS, DISABLE_TIMERS, + resource_mask, NULL); + + /* + * If IPC fails causing recovery, logged_resources is already zero + * so unsetting bits is still safe. + */ + adev->logged_resources &= ~resource_mask; + + /* If that's the last resource, allow for D3. */ + if (!adev->logged_resources) { + avs_dsp_enable_d0ix(adev); + pm_runtime_mark_last_busy(adev->dev); + pm_runtime_put_autosuspend(adev->dev); + } + + return ret; +} + +static ssize_t trace_control_read(struct file *file, char __user *to, size_t count, loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + char buf[64]; + int len; + + len = snprintf(buf, sizeof(buf), "0x%08x\n", adev->logged_resources); + + return simple_read_from_buffer(to, count, ppos, buf, len); +} + +static ssize_t trace_control_write(struct file *file, const char __user *from, size_t count, + loff_t *ppos) +{ + struct avs_dev *adev = file->private_data; + u32 *array, num_elems; + u32 resource_mask; + int ret; + + ret = parse_int_array_user(from, count, (int **)&array); + if (ret < 0) + return ret; + + num_elems = *array; + resource_mask = array[1]; + + /* + * Disable if just resource mask is provided - no log priority flags. + * + * Enable input format: mask, prio1, .., prioN + * Where 'N' equals number of bits set in the 'mask'. + */ + if (num_elems == 1) { + ret = disable_logs(adev, resource_mask); + } else { + if (num_elems != (hweight_long(resource_mask) + 1)) { + ret = -EINVAL; + goto free_array; + } + + ret = enable_logs(adev, resource_mask, &array[2]); + } + + if (!ret) + ret = count; +free_array: + kfree(array); + return ret; +} + +static const struct file_operations trace_control_fops = { + .llseek = default_llseek, + .read = trace_control_read, + .write = trace_control_write, + .open = simple_open, +}; + +void avs_debugfs_init(struct avs_dev *adev) +{ + init_waitqueue_head(&adev->trace_waitq); + spin_lock_init(&adev->trace_lock); + + adev->debugfs_root = debugfs_create_dir("avs", snd_soc_debugfs_root); + + /* Initialize timer periods with recommended defaults. */ + adev->aging_timer_period = 10; + adev->fifo_full_timer_period = 10; + + debugfs_create_file("strace", 0444, adev->debugfs_root, adev, &strace_fops); + debugfs_create_file("trace_control", 0644, adev->debugfs_root, adev, &trace_control_fops); + debugfs_create_file("fw_regs", 0444, adev->debugfs_root, adev, &fw_regs_fops); + debugfs_create_file("debug_window", 0444, adev->debugfs_root, adev, &debug_window_fops); + + debugfs_create_u32("trace_aging_period", 0644, adev->debugfs_root, + &adev->aging_timer_period); + debugfs_create_u32("trace_fifo_full_period", 0644, adev->debugfs_root, + &adev->fifo_full_timer_period); + + debugfs_create_file("probe_points", 0644, adev->debugfs_root, adev, &probe_points_fops); + debugfs_create_file("probe_points_disconnect", 0200, adev->debugfs_root, adev, + &probe_points_disconnect_fops); +} + +void avs_debugfs_exit(struct avs_dev *adev) +{ + debugfs_remove_recursive(adev->debugfs_root); +} diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c index af8a260093f4..bdf013c3dd12 100644 --- a/sound/soc/intel/avs/ipc.c +++ b/sound/soc/intel/avs/ipc.c @@ -266,7 +266,7 @@ static void avs_dsp_process_notification(struct avs_dev *adev, u64 header) break; case AVS_NOTIFY_LOG_BUFFER_STATUS: - avs_dsp_op(adev, log_buffer_status, &msg); + avs_log_buffer_status_locked(adev, &msg); break; case AVS_NOTIFY_EXCEPTION_CAUGHT: diff --git a/sound/soc/intel/avs/messages.c b/sound/soc/intel/avs/messages.c index 6b0fecbf07c3..e11ae4246416 100644 --- a/sound/soc/intel/avs/messages.c +++ b/sound/soc/intel/avs/messages.c @@ -685,6 +685,24 @@ int avs_ipc_get_modules_info(struct avs_dev *adev, struct avs_mods_info **info) return 0; } +int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, + u8 instance_id, u32 sink_id, + const struct avs_audio_format *src_fmt, + const struct avs_audio_format *sink_fmt) +{ + struct avs_copier_sink_format cpr_fmt; + + cpr_fmt.sink_id = sink_id; + /* Firmware expects driver to resend copier's input format. */ + cpr_fmt.src_fmt = *src_fmt; + cpr_fmt.sink_fmt = *sink_fmt; + + return avs_ipc_set_large_config(adev, module_id, instance_id, + AVS_COPIER_SET_SINK_FORMAT, + (u8 *)&cpr_fmt, sizeof(cpr_fmt)); +} + +#ifdef CONFIG_DEBUG_FS int avs_ipc_set_enable_logs(struct avs_dev *adev, u8 *log_info, size_t size) { return avs_ipc_set_large_config(adev, AVS_BASEFW_MOD_ID, AVS_BASEFW_INST_ID, @@ -705,19 +723,81 @@ int avs_ipc_set_system_time(struct avs_dev *adev) AVS_BASEFW_SYSTEM_TIME, (u8 *)&sys_time, sizeof(sys_time)); } -int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, - u8 instance_id, u32 sink_id, - const struct avs_audio_format *src_fmt, - const struct avs_audio_format *sink_fmt) +int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas) { - struct avs_copier_sink_format cpr_fmt; + size_t payload_size; + u32 module_id; + u8 *payload; + int ret; - cpr_fmt.sink_id = sink_id; - /* Firmware expects driver to resend copier's input format. */ - cpr_fmt.src_fmt = *src_fmt; - cpr_fmt.sink_fmt = *sink_fmt; + module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); - return avs_ipc_set_large_config(adev, module_id, instance_id, - AVS_COPIER_SET_SINK_FORMAT, - (u8 *)&cpr_fmt, sizeof(cpr_fmt)); + ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA, + NULL, 0, &payload, &payload_size); + if (ret) + return ret; + + *dmas = (struct avs_probe_dma *)payload; + *num_dmas = payload_size / sizeof(**dmas); + + return 0; } + +int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_INJECTION_DMA, + (u8 *)dmas, array_size(sizeof(*dmas), num_dmas)); +} + +int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids, + size_t num_node_ids) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, + AVS_PROBE_INJECTION_DMA_DETACH, (u8 *)node_ids, + array_size(sizeof(*node_ids), num_node_ids)); +} + +int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs, + size_t *num_descs) +{ + size_t payload_size; + u32 module_id; + u8 *payload; + int ret; + + module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + ret = avs_ipc_get_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, NULL, + 0, &payload, &payload_size); + if (ret) + return ret; + + *descs = (struct avs_probe_point_desc *)payload; + *num_descs = payload_size / sizeof(**descs); + + return 0; +} + +int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs, + size_t num_descs) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, AVS_PROBE_POINTS, + (u8 *)descs, array_size(sizeof(*descs), num_descs)); +} + +int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids, + size_t num_ids) +{ + u32 module_id = avs_get_module_id(adev, &AVS_PROBE_MOD_UUID); + + return avs_ipc_set_large_config(adev, module_id, AVS_PROBE_INST_ID, + AVS_PROBE_POINTS_DISCONNECT, (u8 *)ids, + array_size(sizeof(*ids), num_ids)); +} +#endif diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h index 02b3b7a74783..9dd835527e02 100644 --- a/sound/soc/intel/avs/messages.h +++ b/sound/soc/intel/avs/messages.h @@ -802,4 +802,57 @@ int avs_ipc_copier_set_sink_format(struct avs_dev *adev, u16 module_id, const struct avs_audio_format *src_fmt, const struct avs_audio_format *sink_fmt); +#define AVS_PROBE_INST_ID 0 + +enum avs_probe_runtime_param { + AVS_PROBE_INJECTION_DMA = 1, + AVS_PROBE_INJECTION_DMA_DETACH, + AVS_PROBE_POINTS, + AVS_PROBE_POINTS_DISCONNECT, +}; + +struct avs_probe_dma { + union avs_connector_node_id node_id; + u32 dma_buffer_size; +} __packed; + +enum avs_probe_type { + AVS_PROBE_TYPE_INPUT = 0, + AVS_PROBE_TYPE_OUTPUT, + AVS_PROBE_TYPE_INTERNAL +}; + +union avs_probe_point_id { + u32 value; + struct { + u32 module_id:16; + u32 instance_id:8; + u32 type:2; + u32 index:6; + } id; +} __packed; + +enum avs_connection_purpose { + AVS_CONNECTION_PURPOSE_EXTRACT = 0, + AVS_CONNECTION_PURPOSE_INJECT, + AVS_CONNECTION_PURPOSE_INJECT_REEXTRACT, +}; + +struct avs_probe_point_desc { + union avs_probe_point_id id; + u32 purpose; + union avs_connector_node_id node_id; +} __packed; + +int avs_ipc_probe_get_dma(struct avs_dev *adev, struct avs_probe_dma **dmas, size_t *num_dmas); +int avs_ipc_probe_attach_dma(struct avs_dev *adev, struct avs_probe_dma *dmas, size_t num_dmas); +int avs_ipc_probe_detach_dma(struct avs_dev *adev, union avs_connector_node_id *node_ids, + size_t num_node_ids); +int avs_ipc_probe_get_points(struct avs_dev *adev, struct avs_probe_point_desc **descs, + size_t *num_descs); +int avs_ipc_probe_connect_points(struct avs_dev *adev, struct avs_probe_point_desc *descs, + size_t num_descs); +int avs_ipc_probe_disconnect_points(struct avs_dev *adev, union avs_probe_point_id *ids, + size_t num_ids); + #endif /* __SOUND_SOC_INTEL_AVS_MSGS_H */ diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c index 70d687fa9923..f930c5e86a84 100644 --- a/sound/soc/intel/avs/pcm.c +++ b/sound/soc/intel/avs/pcm.c @@ -1126,9 +1126,9 @@ static const struct snd_soc_component_driver avs_component_driver = { .topology_name_prefix = "intel/avs", }; -static int avs_soc_component_register(struct device *dev, const char *name, - const struct snd_soc_component_driver *drv, - struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) +int avs_soc_component_register(struct device *dev, const char *name, + const struct snd_soc_component_driver *drv, + struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais) { struct avs_soc_component *acomp; int ret; diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c new file mode 100644 index 000000000000..29d63f2a9616 --- /dev/null +++ b/sound/soc/intel/avs/probes.c @@ -0,0 +1,313 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright(c) 2021-2022 Intel Corporation. All rights reserved. +// +// Authors: Cezary Rojewski +// Amadeusz Slawinski +// + +#include +#include +#include +#include +#include "avs.h" +#include "messages.h" + +static int avs_dsp_init_probe(struct avs_dev *adev, union avs_connector_node_id node_id, + size_t buffer_size) +{ + struct avs_probe_cfg cfg = {{0}}; + struct avs_module_entry mentry; + u16 dummy; + + avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + + /* + * Probe module uses no cycles, audio data format and input and output + * frame sizes are unused. It is also not owned by any pipeline. + */ + cfg.base.ibs = 1; + /* BSS module descriptor is always segment of index=2. */ + cfg.base.is_pages = mentry.segments[2].flags.length; + cfg.gtw_cfg.node_id = node_id; + cfg.gtw_cfg.dma_buffer_size = buffer_size; + + return avs_dsp_init_module(adev, mentry.module_id, INVALID_PIPELINE_ID, 0, 0, &cfg, + sizeof(cfg), &dummy); +} + +static void avs_dsp_delete_probe(struct avs_dev *adev) +{ + struct avs_module_entry mentry; + + avs_get_module_entry(adev, &AVS_PROBE_MOD_UUID, &mentry); + + /* There is only ever one probe module instance. */ + avs_dsp_delete_module(adev, mentry.module_id, 0, INVALID_PIPELINE_ID, 0); +} + +static inline struct hdac_ext_stream *avs_compr_get_host_stream(struct snd_compr_stream *cstream) +{ + return cstream->runtime->private_data; +} + +static int avs_probe_compr_open(struct snd_compr_stream *cstream, struct snd_soc_dai *dai) +{ + struct avs_dev *adev = to_avs_dev(dai->dev); + struct hdac_bus *bus = &adev->base.core; + struct hdac_ext_stream *host_stream; + + if (adev->extractor) { + dev_err(dai->dev, "Cannot open more than one extractor stream\n"); + return -EEXIST; + } + + host_stream = snd_hdac_ext_cstream_assign(bus, cstream); + if (!host_stream) { + dev_err(dai->dev, "Failed to assign HDAudio stream for extraction\n"); + return -EBUSY; + } + + adev->extractor = host_stream; + hdac_stream(host_stream)->curr_pos = 0; + cstream->runtime->private_data = host_stream; + + return 0; +} + +static int avs_probe_compr_free(struct snd_compr_stream *cstream, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct avs_dev *adev = to_avs_dev(dai->dev); + struct avs_probe_point_desc *desc; + /* Extractor node identifier. */ + unsigned int vindex = INVALID_NODE_ID.vindex; + size_t num_desc; + int i, ret; + + /* Disconnect all probe points. */ + ret = avs_ipc_probe_get_points(adev, &desc, &num_desc); + if (ret) { + dev_err(dai->dev, "get probe points failed: %d\n", ret); + ret = AVS_IPC_RET(ret); + goto exit; + } + + for (i = 0; i < num_desc; i++) + if (desc[i].node_id.vindex == vindex) + avs_ipc_probe_disconnect_points(adev, &desc[i].id, 1); + kfree(desc); + +exit: + if (adev->num_probe_streams) { + adev->num_probe_streams--; + if (!adev->num_probe_streams) { + avs_dsp_delete_probe(adev); + avs_dsp_enable_d0ix(adev); + } + } + + snd_hdac_stream_cleanup(hdac_stream(host_stream)); + hdac_stream(host_stream)->prepared = 0; + snd_hdac_ext_stream_release(host_stream, HDAC_EXT_STREAM_TYPE_HOST); + + snd_compr_free_pages(cstream); + adev->extractor = NULL; + + return ret; +} + +static int avs_probe_compr_set_params(struct snd_compr_stream *cstream, + struct snd_compr_params *params, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct snd_compr_runtime *rtd = cstream->runtime; + struct avs_dev *adev = to_avs_dev(dai->dev); + /* compr params do not store bit depth, default to S32_LE. */ + snd_pcm_format_t format = SNDRV_PCM_FORMAT_S32_LE; + unsigned int format_val; + int bps, ret; + + hdac_stream(host_stream)->bufsize = 0; + hdac_stream(host_stream)->period_bytes = 0; + hdac_stream(host_stream)->format_val = 0; + cstream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV_SG; + cstream->dma_buffer.dev.dev = adev->dev; + + ret = snd_compr_malloc_pages(cstream, rtd->buffer_size); + if (ret < 0) + return ret; + bps = snd_pcm_format_physical_width(format); + if (bps < 0) + return bps; + format_val = snd_hdac_calc_stream_format(params->codec.sample_rate, params->codec.ch_out, + format, bps, 0); + ret = snd_hdac_stream_set_params(hdac_stream(host_stream), format_val); + if (ret < 0) + return ret; + ret = snd_hdac_stream_setup(hdac_stream(host_stream)); + if (ret < 0) + return ret; + + hdac_stream(host_stream)->prepared = 1; + + if (!adev->num_probe_streams) { + union avs_connector_node_id node_id; + + /* D0ix not allowed during probing. */ + ret = avs_dsp_disable_d0ix(adev); + if (ret) + return ret; + + node_id.vindex = hdac_stream(host_stream)->stream_tag - 1; + node_id.dma_type = AVS_DMA_HDA_HOST_INPUT; + + ret = avs_dsp_init_probe(adev, node_id, rtd->dma_bytes); + if (ret < 0) { + dev_err(dai->dev, "probe init failed: %d\n", ret); + avs_dsp_enable_d0ix(adev); + return ret; + } + } + + adev->num_probe_streams++; + return 0; +} + +static int avs_probe_compr_trigger(struct snd_compr_stream *cstream, int cmd, + struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct avs_dev *adev = to_avs_dev(dai->dev); + struct hdac_bus *bus = &adev->base.core; + unsigned long cookie; + + if (!hdac_stream(host_stream)->prepared) + return -EPIPE; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + case SNDRV_PCM_TRIGGER_RESUME: + spin_lock_irqsave(&bus->reg_lock, cookie); + snd_hdac_stream_start(hdac_stream(host_stream), true); + spin_unlock_irqrestore(&bus->reg_lock, cookie); + break; + + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + case SNDRV_PCM_TRIGGER_SUSPEND: + case SNDRV_PCM_TRIGGER_STOP: + spin_lock_irqsave(&bus->reg_lock, cookie); + snd_hdac_stream_stop(hdac_stream(host_stream)); + spin_unlock_irqrestore(&bus->reg_lock, cookie); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int avs_probe_compr_pointer(struct snd_compr_stream *cstream, + struct snd_compr_tstamp *tstamp, struct snd_soc_dai *dai) +{ + struct hdac_ext_stream *host_stream = avs_compr_get_host_stream(cstream); + struct snd_soc_pcm_stream *pstream; + + pstream = &dai->driver->capture; + tstamp->copied_total = hdac_stream(host_stream)->curr_pos; + tstamp->sampling_rate = snd_pcm_rate_bit_to_rate(pstream->rates); + + return 0; +} + +static int avs_probe_compr_copy(struct snd_soc_component *comp, struct snd_compr_stream *cstream, + char __user *buf, size_t count) +{ + struct snd_compr_runtime *rtd = cstream->runtime; + unsigned int offset, n; + void *ptr; + int ret; + + if (count > rtd->buffer_size) + count = rtd->buffer_size; + + div_u64_rem(rtd->total_bytes_transferred, rtd->buffer_size, &offset); + ptr = rtd->dma_area + offset; + n = rtd->buffer_size - offset; + + if (count < n) { + ret = copy_to_user(buf, ptr, count); + } else { + ret = copy_to_user(buf, ptr, n); + ret += copy_to_user(buf + n, rtd->dma_area, count - n); + } + + if (ret) + return count - ret; + return count; +} + +static const struct snd_soc_cdai_ops avs_probe_dai_ops = { + .startup = avs_probe_compr_open, + .shutdown = avs_probe_compr_free, + .set_params = avs_probe_compr_set_params, + .trigger = avs_probe_compr_trigger, + .pointer = avs_probe_compr_pointer, +}; + +static const struct snd_compress_ops avs_probe_compress_ops = { + .copy = avs_probe_compr_copy, +}; + +static struct snd_soc_dai_driver probe_cpu_dais[] = { +{ + .name = "Probe Extraction CPU DAI", + .compress_new = snd_soc_new_compress, + .cops = &avs_probe_dai_ops, + .capture = { + .stream_name = "Probe Extraction", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .rate_min = 48000, + .rate_max = 48000, + }, +}, +}; + +static int avs_probe_component_probe(struct snd_soc_component *component) +{ + struct avs_soc_component *acomp = to_avs_soc_component(component); + struct avs_dev *adev = to_avs_dev(component->dev); + + mutex_lock(&adev->comp_list_mutex); + list_add_tail(&acomp->node, &adev->comp_list); + mutex_unlock(&adev->comp_list_mutex); + return 0; +} + +static void avs_probe_component_remove(struct snd_soc_component *component) +{ + struct avs_soc_component *acomp = to_avs_soc_component(component); + struct avs_dev *adev = to_avs_dev(component->dev); + + mutex_lock(&adev->comp_list_mutex); + list_del(&acomp->node); + mutex_unlock(&adev->comp_list_mutex); +} + +static const struct snd_soc_component_driver avs_probe_component_driver = { + .name = "avs-probe-compr", + .probe = avs_probe_component_probe, + .remove = avs_probe_component_remove, + .compress_ops = &avs_probe_compress_ops, + .module_get_upon_open = 1, /* increment refcount when a stream is opened */ +}; + +int avs_probe_platform_register(struct avs_dev *adev, const char *name) +{ + return avs_soc_component_register(adev->dev, name, &avs_probe_component_driver, + probe_cpu_dais, ARRAY_SIZE(probe_cpu_dais)); +} diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h index 95be86148cf3..2b464e466ed5 100644 --- a/sound/soc/intel/avs/registers.h +++ b/sound/soc/intel/avs/registers.h @@ -59,7 +59,8 @@ #define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0) #define AVS_FW_REG_ERROR_CODE(adev) (AVS_FW_REG_BASE(adev) + 0x4) -#define AVS_FW_REGS_SIZE PAGE_SIZE +#define AVS_WINDOW_CHUNK_SIZE PAGE_SIZE +#define AVS_FW_REGS_SIZE AVS_WINDOW_CHUNK_SIZE #define AVS_FW_REGS_WINDOW 0 /* DSP -> HOST communication window */ #define AVS_UPLINK_WINDOW AVS_FW_REGS_WINDOW diff --git a/sound/soc/intel/avs/skl.c b/sound/soc/intel/avs/skl.c index dc98b5cf900f..6bb8bbc70442 100644 --- a/sound/soc/intel/avs/skl.c +++ b/sound/soc/intel/avs/skl.c @@ -12,8 +12,9 @@ #include "avs.h" #include "messages.h" -static int skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, - u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) +static int __maybe_unused +skl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period, + u32 fifo_full_period, unsigned long resource_mask, u32 *priorities) { struct skl_log_state_info *info; u32 size, num_cores = adev->hw_cfg.dsp_cores; @@ -55,15 +56,11 @@ int skl_log_buffer_offset(struct avs_dev *adev, u32 core) static int skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) { - unsigned long flags; void __iomem *buf; u16 size, write, offset; - spin_lock_irqsave(&adev->dbg.trace_lock, flags); - if (!kfifo_initialized(&adev->dbg.trace_fifo)) { - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + if (!avs_logging_fw(adev)) return 0; - } size = avs_log_buffer_size(adev) / 2; write = readl(avs_sram_addr(adev, AVS_FW_REGS_WINDOW) + FW_REGS_DBG_LOG_WP(msg->log.core)); @@ -72,9 +69,7 @@ skl_log_buffer_status(struct avs_dev *adev, union avs_notify_msg *msg) /* Address is guaranteed to exist in SRAM2. */ buf = avs_log_buffer_addr(adev, msg->log.core) + offset; - __kfifo_fromio_locked(&adev->dbg.trace_fifo, buf, size, &adev->dbg.fifo_lock); - wake_up(&adev->dbg.trace_waitq); - spin_unlock_irqrestore(&adev->dbg.trace_lock, flags); + avs_dump_fw_log_wakeup(adev, buf, size); return 0; } @@ -116,10 +111,10 @@ const struct avs_dsp_ops skl_dsp_ops = { .load_basefw = avs_cldma_load_basefw, .load_lib = avs_cldma_load_library, .transfer_mods = avs_cldma_transfer_modules, - .enable_logs = skl_enable_logs, .log_buffer_offset = skl_log_buffer_offset, .log_buffer_status = skl_log_buffer_status, .coredump = skl_coredump, .d0ix_toggle = skl_d0ix_toggle, .set_d0ix = skl_set_d0ix, + AVS_SET_ENABLE_LOGS_OP(skl) }; diff --git a/sound/soc/intel/avs/utils.c b/sound/soc/intel/avs/utils.c index 13611dee9787..82416b86662d 100644 --- a/sound/soc/intel/avs/utils.c +++ b/sound/soc/intel/avs/utils.c @@ -300,25 +300,3 @@ void avs_release_firmwares(struct avs_dev *adev) kfree(entry); } } - -unsigned int __kfifo_fromio_locked(struct kfifo *fifo, const void __iomem *src, unsigned int len, - spinlock_t *lock) -{ - struct __kfifo *__fifo = &fifo->kfifo; - unsigned long flags; - unsigned int l, off; - - spin_lock_irqsave(lock, flags); - len = min(len, kfifo_avail(fifo)); - off = __fifo->in & __fifo->mask; - l = min(len, kfifo_size(fifo) - off); - - memcpy_fromio(__fifo->data + off, src, l); - memcpy_fromio(__fifo->data, src + l, len - l); - /* Make sure data copied from SRAM is visible to all CPUs. */ - smp_mb(); - __fifo->in += len; - spin_unlock_irqrestore(lock, flags); - - return len; -} diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile index 7e1a4ff77ac3..d1fd7a2b32db 100644 --- a/sound/soc/intel/boards/Makefile +++ b/sound/soc/intel/boards/Makefile @@ -37,8 +37,7 @@ snd-soc-sof_da7219_max98373-objs := sof_da7219_max98373.o snd-soc-ehl-rt5660-objs := ehl_rt5660.o snd-soc-sof-ssp-amp-objs := sof_ssp_amp.o snd-soc-sof-sdw-objs += sof_sdw.o \ - sof_sdw_max98373.o sof_sdw_rt1308.o \ - sof_sdw_rt1316.o sof_sdw_rt1318.o \ + sof_sdw_max98373.o sof_sdw_rt_amp.o \ sof_sdw_rt5682.o sof_sdw_rt700.o \ sof_sdw_rt711.o sof_sdw_rt711_sdca.o \ sof_sdw_rt715.o sof_sdw_rt715_sdca.o \ diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c index 6db07b2417ca..4699ca79f3ea 100644 --- a/sound/soc/intel/boards/bytcr_rt5640.c +++ b/sound/soc/intel/boards/bytcr_rt5640.c @@ -570,6 +570,21 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), }, + { + /* Advantech MICA-071 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Advantech"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "MICA-071"), + }, + /* OVCD Th = 1500uA to reliable detect head-phones vs -set */ + .driver_data = (void *)(BYT_RT5640_IN3_MAP | + BYT_RT5640_JD_SRC_JD2_IN4N | + BYT_RT5640_OVCD_TH_1500UA | + BYT_RT5640_OVCD_SF_0P75 | + BYT_RT5640_MONO_SPEAKER | + BYT_RT5640_DIFF_MIC | + BYT_RT5640_MCLK_EN), + }, { .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ARCHOS"), @@ -796,6 +811,16 @@ static const struct dmi_system_id byt_rt5640_quirk_table[] = { BYT_RT5640_SSP0_AIF1 | BYT_RT5640_MCLK_EN), }, + { /* HP Stream 8 */ + .matches = { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP Stream 8 Tablet"), + }, + .driver_data = (void *)(BYTCR_INPUT_DEFAULTS | + BYT_RT5640_JD_NOT_INV | + BYT_RT5640_SSP0_AIF1 | + BYT_RT5640_MCLK_EN), + }, { /* I.T.Works TW891 */ .matches = { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "To be filled by O.E.M."), diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c index 70713e4b07dc..773e5d1d87d4 100644 --- a/sound/soc/intel/boards/sof_es8336.c +++ b/sound/soc/intel/boards/sof_es8336.c @@ -783,7 +783,7 @@ static int sof_es8336_remove(struct platform_device *pdev) struct snd_soc_card *card = platform_get_drvdata(pdev); struct sof_es8336_private *priv = snd_soc_card_get_drvdata(card); - cancel_delayed_work(&priv->pcm_pop_work); + cancel_delayed_work_sync(&priv->pcm_pop_work); gpiod_put(priv->gpio_speakers); device_remove_software_node(priv->codec_dev); put_device(priv->codec_dev); diff --git a/sound/soc/intel/boards/sof_realtek_common.c b/sound/soc/intel/boards/sof_realtek_common.c index ff2851fc8930..6c12ca92f371 100644 --- a/sound/soc/intel/boards/sof_realtek_common.c +++ b/sound/soc/intel/boards/sof_realtek_common.c @@ -267,7 +267,8 @@ static int rt1015_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); struct snd_soc_dai_link *dai_link = rtd->dai_link; struct snd_soc_dai *codec_dai; - int i, clk_freq, ret; + int i, clk_freq; + int ret = 0; clk_freq = sof_dai_get_bclk(rtd); diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c index 4a2f91249b10..2eabc4b0fafa 100644 --- a/sound/soc/intel/boards/sof_rt5682.c +++ b/sound/soc/intel/boards/sof_rt5682.c @@ -1104,6 +1104,12 @@ static const struct platform_device_id board_ids[] = { SOF_RT5682_SSP_AMP(1) | SOF_RT5682_NUM_HDMIDEV(4)), }, + { + .name = "jsl_rt5682", + .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN | + SOF_RT5682_MCLK_24MHZ | + SOF_RT5682_SSP_CODEC(0)), + }, { } }; MODULE_DEVICE_TABLE(platform, board_ids); diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c index b58c7b35599d..d2ed807abde9 100644 --- a/sound/soc/intel/boards/sof_sdw.c +++ b/sound/soc/intel/boards/sof_sdw.c @@ -550,23 +550,23 @@ static struct sof_sdw_codec_info codec_info_list[] = { .direction = {true, false}, .dai_name = "rt1308-aif", .ops = &sof_sdw_rt1308_i2s_ops, - .init = sof_sdw_rt1308_init, - .exit = sof_sdw_rt1308_exit, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1316, .direction = {true, true}, .dai_name = "rt1316-aif", - .init = sof_sdw_rt1316_init, - .exit = sof_sdw_rt1316_exit, + .init = sof_sdw_rt_amp_init, + .exit = sof_sdw_rt_amp_exit, .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { .part_id = 0x1318, .direction = {true, true}, .dai_name = "rt1318-aif", - .init = sof_sdw_rt1318_init, + .init = sof_sdw_rt_amp_init, .codec_type = SOF_SDW_CODEC_TYPE_AMP, }, { diff --git a/sound/soc/intel/boards/sof_sdw_common.h b/sound/soc/intel/boards/sof_sdw_common.h index 54a50f7da4da..350010b0e5f4 100644 --- a/sound/soc/intel/boards/sof_sdw_common.h +++ b/sound/soc/intel/boards/sof_sdw_common.h @@ -125,30 +125,18 @@ int sof_sdw_rt700_init(struct snd_soc_card *card, struct sof_sdw_codec_info *info, bool playback); -/* RT1308 support */ +/* RT1308 I2S support */ extern struct snd_soc_ops sof_sdw_rt1308_i2s_ops; -int sof_sdw_rt1308_init(struct snd_soc_card *card, +/* generic amp support */ +int sof_sdw_rt_amp_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, bool playback); -int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); +int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); /* RT1316 support */ -int sof_sdw_rt1316_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); -int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link); - -/* RT1318 support */ -int sof_sdw_rt1318_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback); /* RT715 support */ int sof_sdw_rt715_init(struct snd_soc_card *card, diff --git a/sound/soc/intel/boards/sof_sdw_rt1316.c b/sound/soc/intel/boards/sof_sdw_rt1316.c deleted file mode 100644 index f6bbea0d3810..000000000000 --- a/sound/soc/intel/boards/sof_sdw_rt1316.c +++ /dev/null @@ -1,239 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// Copyright (c) 2020 Intel Corporation - -/* - * sof_sdw_rt1316 - Helpers to handle RT1316 from generic machine driver - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sof_sdw_common.h" -#include "sof_sdw_amp_coeff_tables.h" - -struct rt1316_platform_data { - const unsigned char *bq_params; - const unsigned int bq_params_cnt; -}; - -static const struct rt1316_platform_data dell_0b00_platform_data = { - .bq_params = dell_0b00_bq_params, - .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params), -}; - -static const struct dmi_system_id dmi_platform_data[] = { - /* AlderLake devices */ - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), - DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") - }, - .driver_data = (void *)&dell_0b00_platform_data, - }, -}; - -static int rt1316_add_device_props(struct device *sdw_dev) -{ - struct property_entry props[3] = {}; - struct fwnode_handle *fwnode; - const struct dmi_system_id *dmi_data; - const struct rt1316_platform_data *pdata; - unsigned char params[RT1316_MAX_BQ_REG]; - int ret; - - dmi_data = dmi_first_match(dmi_platform_data); - if (!dmi_data) - return 0; - - pdata = dmi_data->driver_data; - memcpy(¶ms, pdata->bq_params, sizeof(unsigned char) * pdata->bq_params_cnt); - - props[0] = PROPERTY_ENTRY_U8_ARRAY("realtek,bq-params", params); - props[1] = PROPERTY_ENTRY_U32("realtek,bq-params-cnt", pdata->bq_params_cnt); - - fwnode = fwnode_create_software_node(props, NULL); - if (IS_ERR(fwnode)) - return PTR_ERR(fwnode); - - ret = device_add_software_node(sdw_dev, to_software_node(fwnode)); - - fwnode_handle_put(fwnode); - - return ret; -} - -static const struct snd_soc_dapm_widget rt1316_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), -}; - -/* - * dapm routes for rt1316 will be registered dynamically according - * to the number of rt1316 used. The first two entries will be registered - * for one codec case, and the last two entries are also registered - * if two 1316s are used. - */ -static const struct snd_soc_dapm_route rt1316_map[] = { - { "Speaker", NULL, "rt1316-1 SPOL" }, - { "Speaker", NULL, "rt1316-1 SPOR" }, - { "Speaker", NULL, "rt1316-2 SPOL" }, - { "Speaker", NULL, "rt1316-2 SPOR" }, -}; - -static const struct snd_kcontrol_new rt1316_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), -}; - -static int first_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - int ret; - - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:rt1316", - card->components); - if (!card->components) - return -ENOMEM; - - ret = snd_soc_add_card_controls(card, rt1316_controls, - ARRAY_SIZE(rt1316_controls)); - if (ret) { - dev_err(card->dev, "rt1316 controls addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_new_controls(&card->dapm, rt1316_widgets, - ARRAY_SIZE(rt1316_widgets)); - if (ret) { - dev_err(card->dev, "rt1316 widgets addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map, 2); - if (ret) - dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); - - return ret; -} - -static int second_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - int ret; - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1316_map + 2, 2); - if (ret) - dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); - - return ret; -} - -static int all_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - int ret; - - ret = first_spk_init(rtd); - if (ret) - return ret; - - return second_spk_init(rtd); -} - -int sof_sdw_rt1316_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) -{ - struct mc_private *ctx = snd_soc_card_get_drvdata(card); - - if (ctx->amp_dev1) { - device_remove_software_node(ctx->amp_dev1); - put_device(ctx->amp_dev1); - } - - if (ctx->amp_dev2) { - device_remove_software_node(ctx->amp_dev2); - put_device(ctx->amp_dev2); - } - - return 0; -} - -int sof_sdw_rt1316_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - struct mc_private *ctx = snd_soc_card_get_drvdata(card); - struct device *sdw_dev1, *sdw_dev2; - int ret; - - /* Count amp number and do init on playback link only. */ - if (!playback) - return 0; - - info->amp_num++; - if (info->amp_num == 1) - dai_links->init = first_spk_init; - - if (info->amp_num == 2) { - sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name); - if (!sdw_dev1) - return -EPROBE_DEFER; - - ret = rt1316_add_device_props(sdw_dev1); - if (ret < 0) { - put_device(sdw_dev1); - return ret; - } - ctx->amp_dev1 = sdw_dev1; - - sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name); - if (!sdw_dev2) - return -EPROBE_DEFER; - - ret = rt1316_add_device_props(sdw_dev2); - if (ret < 0) { - put_device(sdw_dev2); - return ret; - } - ctx->amp_dev2 = sdw_dev2; - - /* - * if two 1316s are in one dai link, the init function - * in this dai link will be first set for the first speaker, - * and it should be reset to initialize all speakers when - * the second speaker is found. - */ - if (dai_links->init) - dai_links->init = all_spk_init; - else - dai_links->init = second_spk_init; - } - - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt1318.c b/sound/soc/intel/boards/sof_sdw_rt1318.c deleted file mode 100644 index dbee4bf5c814..000000000000 --- a/sound/soc/intel/boards/sof_sdw_rt1318.c +++ /dev/null @@ -1,120 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -// Copyright (c) 2022 Intel Corporation - -/* - * sof_sdw_rt1318 - Helpers to handle RT1318 from generic machine driver - */ - -#include -#include -#include -#include -#include -#include -#include "sof_sdw_common.h" - -static const struct snd_soc_dapm_widget rt1318_widgets[] = { - SND_SOC_DAPM_SPK("Speaker", NULL), -}; - -/* - * dapm routes for rt1318 will be registered dynamically according - * to the number of rt1318 used. The first two entries will be registered - * for one codec case, and the last two entries are also registered - * if two 1318s are used. - */ -static const struct snd_soc_dapm_route rt1318_map[] = { - { "Speaker", NULL, "rt1318-1 SPOL" }, - { "Speaker", NULL, "rt1318-1 SPOR" }, - { "Speaker", NULL, "rt1318-2 SPOL" }, - { "Speaker", NULL, "rt1318-2 SPOR" }, -}; - -static const struct snd_kcontrol_new rt1318_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), -}; - -static int first_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - int ret; - - card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:rt1318", - card->components); - if (!card->components) - return -ENOMEM; - - ret = snd_soc_add_card_controls(card, rt1318_controls, - ARRAY_SIZE(rt1318_controls)); - if (ret) { - dev_err(card->dev, "rt1318 controls addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_new_controls(&card->dapm, rt1318_widgets, - ARRAY_SIZE(rt1318_widgets)); - if (ret) { - dev_err(card->dev, "rt1318 widgets addition failed: %d\n", ret); - return ret; - } - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map, 2); - if (ret) - dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); - - return ret; -} - -static int second_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - struct snd_soc_card *card = rtd->card; - int ret; - - ret = snd_soc_dapm_add_routes(&card->dapm, rt1318_map + 2, 2); - if (ret) - dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); - - return ret; -} - -static int all_spk_init(struct snd_soc_pcm_runtime *rtd) -{ - int ret; - - ret = first_spk_init(rtd); - if (ret) - return ret; - - return second_spk_init(rtd); -} - -int sof_sdw_rt1318_init(struct snd_soc_card *card, - const struct snd_soc_acpi_link_adr *link, - struct snd_soc_dai_link *dai_links, - struct sof_sdw_codec_info *info, - bool playback) -{ - /* Count amp number and do init on playback link only. */ - if (!playback) - return 0; - - info->amp_num++; - if (info->amp_num == 1) - dai_links->init = first_spk_init; - - if (info->amp_num == 2) { - /* - * if two 1318s are in one dai link, the init function - * in this dai link will be first set for the first speaker, - * and it should be reset to initialize all speakers when - * the second speaker is found. - */ - if (dai_links->init) - dai_links->init = all_spk_init; - else - dai_links->init = second_spk_init; - } - - return 0; -} diff --git a/sound/soc/intel/boards/sof_sdw_rt1308.c b/sound/soc/intel/boards/sof_sdw_rt_amp.c similarity index 59% rename from sound/soc/intel/boards/sof_sdw_rt1308.c rename to sound/soc/intel/boards/sof_sdw_rt_amp.c index a19b055b9c6f..26bf9e0dd3d2 100644 --- a/sound/soc/intel/boards/sof_sdw_rt1308.c +++ b/sound/soc/intel/boards/sof_sdw_rt_amp.c @@ -1,8 +1,8 @@ // SPDX-License-Identifier: GPL-2.0-only -// Copyright (c) 2020 Intel Corporation +// Copyright (c) 2022 Intel Corporation /* - * sof_sdw_rt1308 - Helpers to handle RT1308 from generic machine driver + * sof_sdw_rt_amp - Helpers to handle RT1308/RT1316/RT1318 from generic machine driver */ #include @@ -18,16 +18,26 @@ #include "sof_sdw_amp_coeff_tables.h" #include "../../codecs/rt1308.h" -struct rt1308_platform_data { +#define CODEC_NAME_SIZE 7 + +/* choose a larger value to resolve compatibility issues */ +#define RT_AMP_MAX_BQ_REG RT1316_MAX_BQ_REG + +struct rt_amp_platform_data { const unsigned char *bq_params; const unsigned int bq_params_cnt; }; -static const struct rt1308_platform_data dell_0a5d_platform_data = { +static const struct rt_amp_platform_data dell_0a5d_platform_data = { .bq_params = dell_0a5d_bq_params, .bq_params_cnt = ARRAY_SIZE(dell_0a5d_bq_params), }; +static const struct rt_amp_platform_data dell_0b00_platform_data = { + .bq_params = dell_0b00_bq_params, + .bq_params_cnt = ARRAY_SIZE(dell_0b00_bq_params), +}; + static const struct dmi_system_id dmi_platform_data[] = { /* CometLake devices */ { @@ -59,15 +69,45 @@ static const struct dmi_system_id dmi_platform_data[] = { }, .driver_data = (void *)&dell_0a5d_platform_data, }, + /* AlderLake devices */ + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B00") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0B01") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFF") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + { + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "0AFE") + }, + .driver_data = (void *)&dell_0b00_platform_data, + }, + {}, }; -static int rt1308_add_device_props(struct device *sdw_dev) +static int rt_amp_add_device_props(struct device *sdw_dev) { struct property_entry props[3] = {}; struct fwnode_handle *fwnode; const struct dmi_system_id *dmi_data; - const struct rt1308_platform_data *pdata; - unsigned char params[RT1308_MAX_BQ_REG]; + const struct rt_amp_platform_data *pdata; + unsigned char params[RT_AMP_MAX_BQ_REG]; int ret; dmi_data = dmi_first_match(dmi_platform_data); @@ -91,15 +131,19 @@ static int rt1308_add_device_props(struct device *sdw_dev) return ret; } -static const struct snd_soc_dapm_widget rt1308_widgets[] = { +static const struct snd_kcontrol_new rt_amp_controls[] = { + SOC_DAPM_PIN_SWITCH("Speaker"), +}; + +static const struct snd_soc_dapm_widget rt_amp_widgets[] = { SND_SOC_DAPM_SPK("Speaker", NULL), }; /* - * dapm routes for rt1308 will be registered dynamically according - * to the number of rt1308 used. The first two entries will be registered - * for one codec case, and the last two entries are also registered - * if two 1308s are used. + * dapm routes for rt1308/rt1316/rt1318 will be registered dynamically + * according to the number of rt1308/rt1316/rt1318 used. The first two + * entries will be registered for one codec case, and the last two entries + * are also registered if two 1308s/1316s/1318s are used. */ static const struct snd_soc_dapm_route rt1308_map[] = { { "Speaker", NULL, "rt1308-1 SPOL" }, @@ -108,36 +152,69 @@ static const struct snd_soc_dapm_route rt1308_map[] = { { "Speaker", NULL, "rt1308-2 SPOR" }, }; -static const struct snd_kcontrol_new rt1308_controls[] = { - SOC_DAPM_PIN_SWITCH("Speaker"), +static const struct snd_soc_dapm_route rt1316_map[] = { + { "Speaker", NULL, "rt1316-1 SPOL" }, + { "Speaker", NULL, "rt1316-1 SPOR" }, + { "Speaker", NULL, "rt1316-2 SPOL" }, + { "Speaker", NULL, "rt1316-2 SPOR" }, }; +static const struct snd_soc_dapm_route rt1318_map[] = { + { "Speaker", NULL, "rt1318-1 SPOL" }, + { "Speaker", NULL, "rt1318-1 SPOR" }, + { "Speaker", NULL, "rt1318-2 SPOL" }, + { "Speaker", NULL, "rt1318-2 SPOR" }, +}; + +static const struct snd_soc_dapm_route *get_codec_name_and_route(struct snd_soc_pcm_runtime *rtd, + char *codec_name) +{ + const char *dai_name; + + dai_name = rtd->dai_link->codecs->dai_name; + + /* get the codec name */ + snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name); + + /* choose the right codec's map */ + if (strcmp(codec_name, "rt1308") == 0) + return rt1308_map; + else if (strcmp(codec_name, "rt1316") == 0) + return rt1316_map; + else + return rt1318_map; +} + static int first_spk_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + const struct snd_soc_dapm_route *rt_amp_map; + char codec_name[CODEC_NAME_SIZE]; int ret; + rt_amp_map = get_codec_name_and_route(rtd, codec_name); + card->components = devm_kasprintf(card->dev, GFP_KERNEL, - "%s spk:rt1308", - card->components); + "%s spk:%s", + card->components, codec_name); if (!card->components) return -ENOMEM; - ret = snd_soc_add_card_controls(card, rt1308_controls, - ARRAY_SIZE(rt1308_controls)); + ret = snd_soc_add_card_controls(card, rt_amp_controls, + ARRAY_SIZE(rt_amp_controls)); if (ret) { - dev_err(card->dev, "rt1308 controls addition failed: %d\n", ret); + dev_err(card->dev, "%s controls addition failed: %d\n", codec_name, ret); return ret; } - ret = snd_soc_dapm_new_controls(&card->dapm, rt1308_widgets, - ARRAY_SIZE(rt1308_widgets)); + ret = snd_soc_dapm_new_controls(&card->dapm, rt_amp_widgets, + ARRAY_SIZE(rt_amp_widgets)); if (ret) { - dev_err(card->dev, "rt1308 widgets addition failed: %d\n", ret); + dev_err(card->dev, "%s widgets addition failed: %d\n", codec_name, ret); return ret; } - ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map, 2); + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2); if (ret) dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret); @@ -147,9 +224,13 @@ static int first_spk_init(struct snd_soc_pcm_runtime *rtd) static int second_spk_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; + const struct snd_soc_dapm_route *rt_amp_map; + char codec_name[CODEC_NAME_SIZE]; int ret; - ret = snd_soc_dapm_add_routes(&card->dapm, rt1308_map + 2, 2); + rt_amp_map = get_codec_name_and_route(rtd, codec_name); + + ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map + 2, 2); if (ret) dev_err(rtd->dev, "failed to add second SPK map: %d\n", ret); @@ -204,7 +285,7 @@ struct snd_soc_ops sof_sdw_rt1308_i2s_ops = { .hw_params = rt1308_i2s_hw_params, }; -int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) +int sof_sdw_rt_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link) { struct mc_private *ctx = snd_soc_card_get_drvdata(card); @@ -221,7 +302,7 @@ int sof_sdw_rt1308_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_ return 0; } -int sof_sdw_rt1308_init(struct snd_soc_card *card, +int sof_sdw_rt_amp_init(struct snd_soc_card *card, const struct snd_soc_acpi_link_adr *link, struct snd_soc_dai_link *dai_links, struct sof_sdw_codec_info *info, @@ -244,7 +325,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card, if (!sdw_dev1) return -EPROBE_DEFER; - ret = rt1308_add_device_props(sdw_dev1); + ret = rt_amp_add_device_props(sdw_dev1); if (ret < 0) { put_device(sdw_dev1); return ret; @@ -255,7 +336,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card, if (!sdw_dev2) return -EPROBE_DEFER; - ret = rt1308_add_device_props(sdw_dev2); + ret = rt_amp_add_device_props(sdw_dev2); if (ret < 0) { put_device(sdw_dev2); return ret; @@ -263,7 +344,7 @@ int sof_sdw_rt1308_init(struct snd_soc_card *card, ctx->amp_dev2 = sdw_dev2; /* - * if two 1308s are in one dai link, the init function + * if two amps are in one dai link, the init function * in this dai link will be first set for the first speaker, * and it should be reset to initialize all speakers when * the second speaker is found. diff --git a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c index b95c4b2cda94..f5c7e1bbded0 100644 --- a/sound/soc/intel/common/soc-acpi-intel-jsl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-jsl-match.c @@ -78,6 +78,11 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_jsl_machines[] = { .quirk_data = &mx98360a_spk, .sof_tplg_filename = "sof-jsl-rt5682-mx98360a.tplg", }, + { + .comp_ids = &rt5682_rt5682s_hp, + .drv_name = "jsl_rt5682", + .sof_tplg_filename = "sof-jsl-rt5682.tplg", + }, { .id = "10134242", .drv_name = "jsl_cs4242_mx98360a", diff --git a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c index 3c5229f41bb0..31b43116e3d8 100644 --- a/sound/soc/intel/common/soc-acpi-intel-rpl-match.c +++ b/sound/soc/intel/common/soc-acpi-intel-rpl-match.c @@ -112,7 +112,7 @@ static const struct snd_soc_acpi_adr_device rt1316_1_group2_adr[] = { static const struct snd_soc_acpi_adr_device rt1318_1_group1_adr[] = { { - .adr = 0x000131025D131801ull, + .adr = 0x000132025D131801ull, .num_endpoints = 1, .endpoints = &spk_l_endpoint, .name_prefix = "rt1318-1" diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c index b91f7a652a2b..b0204ea00f07 100644 --- a/sound/soc/intel/skylake/skl-sst-cldma.c +++ b/sound/soc/intel/skylake/skl-sst-cldma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "../common/sst-dsp.h" #include "../common/sst-dsp-priv.h" @@ -79,21 +80,25 @@ static void skl_cldma_setup_bdle(struct sst_dsp *ctx, __le32 **bdlp, int size, int with_ioc) { __le32 *bdl = *bdlp; + int remaining = ctx->cl_dev.bufsize; + int offset = 0; ctx->cl_dev.frags = 0; - while (size > 0) { - phys_addr_t addr = virt_to_phys(dmab_data->area + - (ctx->cl_dev.frags * ctx->cl_dev.bufsize)); + while (remaining > 0) { + phys_addr_t addr; + int chunk; + addr = snd_sgbuf_get_addr(dmab_data, offset); bdl[0] = cpu_to_le32(lower_32_bits(addr)); bdl[1] = cpu_to_le32(upper_32_bits(addr)); + chunk = snd_sgbuf_get_chunk_size(dmab_data, offset, size); + bdl[2] = cpu_to_le32(chunk); - bdl[2] = cpu_to_le32(ctx->cl_dev.bufsize); - - size -= ctx->cl_dev.bufsize; - bdl[3] = (size || !with_ioc) ? 0 : cpu_to_le32(0x01); + remaining -= chunk; + bdl[3] = (remaining > 0) ? 0 : cpu_to_le32(0x01); bdl += 4; + offset += chunk; ctx->cl_dev.frags++; } } @@ -338,15 +343,15 @@ int skl_cldma_prepare(struct sst_dsp *ctx) ctx->cl_dev.ops.cl_stop_dma = skl_cldma_stop; /* Allocate buffer*/ - ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, - &ctx->cl_dev.dmab_data, ctx->cl_dev.bufsize); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV_SG, ctx->dev, ctx->cl_dev.bufsize, + &ctx->cl_dev.dmab_data); if (ret < 0) { dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", ret); return ret; } + /* Setup Code loader BDL */ - ret = ctx->dsp_ops.alloc_dma_buf(ctx->dev, - &ctx->cl_dev.dmab_bdl, PAGE_SIZE); + ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, ctx->dev, BDL_SIZE, &ctx->cl_dev.dmab_bdl); if (ret < 0) { dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret); ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data); diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c index e06eac592da1..b20643b83401 100644 --- a/sound/soc/intel/skylake/skl-topology.c +++ b/sound/soc/intel/skylake/skl-topology.c @@ -582,36 +582,10 @@ static int skl_tplg_unload_pipe_modules(struct skl_dev *skl, return ret; } -static bool skl_tplg_is_multi_fmt(struct skl_dev *skl, struct skl_pipe *pipe) +static void skl_tplg_set_pipe_config_idx(struct skl_pipe *pipe, int idx) { - struct skl_pipe_fmt *cur_fmt; - struct skl_pipe_fmt *next_fmt; - int i; - - if (pipe->nr_cfgs <= 1) - return false; - - if (pipe->conn_type != SKL_PIPE_CONN_TYPE_FE) - return true; - - for (i = 0; i < pipe->nr_cfgs - 1; i++) { - if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) { - cur_fmt = &pipe->configs[i].out_fmt; - next_fmt = &pipe->configs[i + 1].out_fmt; - } else { - cur_fmt = &pipe->configs[i].in_fmt; - next_fmt = &pipe->configs[i + 1].in_fmt; - } - - if (!CHECK_HW_PARAMS(cur_fmt->channels, cur_fmt->freq, - cur_fmt->bps, - next_fmt->channels, - next_fmt->freq, - next_fmt->bps)) - return true; - } - - return false; + pipe->cur_config_idx = idx; + pipe->memory_pages = pipe->configs[idx].mem_pages; } /* @@ -632,24 +606,14 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig) int i; if (pipe->nr_cfgs == 0) { - pipe->cur_config_idx = 0; - return 0; - } - - if (skl_tplg_is_multi_fmt(skl, pipe)) { - pipe->cur_config_idx = pipe->pipe_config_idx; - pipe->memory_pages = pconfig->mem_pages; - dev_dbg(skl->dev, "found pipe config idx:%d\n", - pipe->cur_config_idx); + skl_tplg_set_pipe_config_idx(pipe, 0); return 0; } if (pipe->conn_type == SKL_PIPE_CONN_TYPE_NONE || pipe->nr_cfgs == 1) { dev_dbg(skl->dev, "No conn_type or just 1 pathcfg, taking 0th for %d\n", pipe->ppl_id); - pipe->cur_config_idx = 0; - pipe->memory_pages = pconfig->mem_pages; - + skl_tplg_set_pipe_config_idx(pipe, 0); return 0; } @@ -668,10 +632,8 @@ skl_tplg_get_pipe_config(struct skl_dev *skl, struct skl_module_cfg *mconfig) if (CHECK_HW_PARAMS(params->ch, params->s_freq, params->s_fmt, fmt->channels, fmt->freq, fmt->bps)) { - pipe->cur_config_idx = i; - pipe->memory_pages = pconfig->mem_pages; + skl_tplg_set_pipe_config_idx(pipe, i); dev_dbg(skl->dev, "Using pipe config: %d\n", i); - return 0; } } @@ -1391,9 +1353,9 @@ static int skl_tplg_multi_config_set_get(struct snd_kcontrol *kcontrol, return -EIO; if (is_set) - pipe->pipe_config_idx = ucontrol->value.enumerated.item[0]; + skl_tplg_set_pipe_config_idx(pipe, ucontrol->value.enumerated.item[0]); else - ucontrol->value.enumerated.item[0] = pipe->pipe_config_idx; + ucontrol->value.enumerated.item[0] = pipe->cur_config_idx; return 0; } @@ -1837,20 +1799,28 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, { struct nhlt_specific_cfg *cfg; struct skl_pipe *pipe = mconfig->pipe; + struct skl_pipe_params save = *pipe->p_params; struct skl_pipe_fmt *pipe_fmt; struct skl_dev *skl = get_skl_ctx(dai->dev); int link_type = skl_tplg_be_link_type(mconfig->dev_type); u8 dev_type = skl_tplg_be_dev_type(mconfig->dev_type); + int ret; skl_tplg_fill_dma_id(mconfig, params); if (link_type == NHLT_LINK_HDA) return 0; + *pipe->p_params = *params; + ret = skl_tplg_get_pipe_config(skl, mconfig); + if (ret) + goto err; + + dev_dbg(skl->dev, "%s using pipe config: %d\n", __func__, pipe->cur_config_idx); if (pipe->direction == SNDRV_PCM_STREAM_PLAYBACK) - pipe_fmt = &pipe->configs[pipe->pipe_config_idx].out_fmt; + pipe_fmt = &pipe->configs[pipe->cur_config_idx].out_fmt; else - pipe_fmt = &pipe->configs[pipe->pipe_config_idx].in_fmt; + pipe_fmt = &pipe->configs[pipe->cur_config_idx].in_fmt; /* update the blob based on virtual bus_id*/ cfg = intel_nhlt_get_endpoint_blob(dai->dev, skl->nhlt, @@ -1865,10 +1835,15 @@ static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai, dev_err(dai->dev, "Blob NULL for id:%d type:%d dirn:%d ch:%d, freq:%d, fmt:%d\n", mconfig->vbus_id, link_type, params->stream, params->ch, params->s_freq, params->s_fmt); - return -EINVAL; + ret = -EINVAL; + goto err; } return 0; + +err: + *pipe->p_params = save; + return ret; } static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai, diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h index 017ac0ef324d..6db0fd7bad49 100644 --- a/sound/soc/intel/skylake/skl-topology.h +++ b/sound/soc/intel/skylake/skl-topology.h @@ -324,7 +324,6 @@ struct skl_pipe { struct skl_path_config configs[SKL_MAX_PATH_CONFIGS]; struct list_head w_list; bool passthru; - u32 pipe_config_idx; }; enum skl_module_state { diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c index 9bd9f9866898..998bd0232cf1 100644 --- a/sound/soc/intel/skylake/skl.c +++ b/sound/soc/intel/skylake/skl.c @@ -1107,7 +1107,10 @@ static void skl_shutdown(struct pci_dev *pci) if (!skl->init_done) return; - snd_hdac_stop_streams_and_chip(bus); + snd_hdac_stop_streams(bus); + snd_hdac_ext_bus_link_power_down_all(bus); + skl_dsp_sleep(skl->dsp); + list_for_each_entry(s, &bus->stream_list, list) { stream = stream_to_hdac_ext_stream(s); snd_hdac_ext_stream_decouple(bus, stream, false); diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c index 12f40c81b101..f803f121659d 100644 --- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c +++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -200,14 +200,16 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[0].of_node) { dev_err(&pdev->dev, "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node = of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1); if (!mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node) { dev_err(&pdev->dev, "Property 'audio-codec' missing or invalid\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } mt8173_rt5650_rt5514_codec_conf[0].dlc.of_node = mt8173_rt5650_rt5514_dais[DAI_LINK_CODEC_I2S].codecs[1].of_node; @@ -216,6 +218,7 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); +out: of_node_put(platform_node); return ret; } diff --git a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c index 8fb473543cf9..ce9aedde7e1e 100644 --- a/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c +++ b/sound/soc/mediatek/mt8183/mt8183-mt6358-ts3a227-max98357.c @@ -677,8 +677,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) } card = (struct snd_soc_card *)of_device_get_match_data(&pdev->dev); - if (!card) + if (!card) { + of_node_put(platform_node); return -EINVAL; + } card->dev = &pdev->dev; ec_codec = of_parse_phandle(pdev->dev.of_node, "mediatek,ec-codec", 0); @@ -767,8 +769,10 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) } priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; + if (!priv) { + ret = -ENOMEM; + goto out; + } snd_soc_card_set_drvdata(card, priv); @@ -776,7 +780,8 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) if (IS_ERR(priv->pinctrl)) { dev_err(&pdev->dev, "%s devm_pinctrl_get failed\n", __func__); - return PTR_ERR(priv->pinctrl); + ret = PTR_ERR(priv->pinctrl); + goto out; } for (i = 0; i < PIN_STATE_MAX; i++) { @@ -809,6 +814,7 @@ mt8183_mt6358_ts3a227_max98357_dev_probe(struct platform_device *pdev) ret = devm_snd_soc_register_card(&pdev->dev, card); +out: of_node_put(platform_node); of_node_put(ec_codec); of_node_put(hdmi_codec); diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c index 61be66f47723..4682748d82be 100644 --- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c +++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c @@ -633,6 +633,32 @@ static const struct snd_soc_ops mt8195_rt1011_etdm_ops = { .hw_params = mt8195_rt1011_etdm_hw_params, }; +static int mt8195_sof_be_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); + struct snd_soc_component *cmpnt_afe = NULL; + struct snd_soc_pcm_runtime *runtime; + + /* find afe component */ + for_each_card_rtds(rtd->card, runtime) { + cmpnt_afe = snd_soc_rtdcom_lookup(runtime, AFE_PCM_NAME); + if (cmpnt_afe) + break; + } + + if (cmpnt_afe && !pm_runtime_active(cmpnt_afe->dev)) { + dev_err(rtd->dev, "afe pm runtime is not active!!\n"); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_ops mt8195_sof_be_ops = { + .hw_params = mt8195_sof_be_hw_params, +}; + static int mt8195_rt1011_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_card *card = rtd->card; @@ -1272,24 +1298,28 @@ static struct snd_soc_dai_link mt8195_mt6359_dai_links[] = { .name = "AFE_SOF_DL2", .no_pcm = 1, .dpcm_playback = 1, + .ops = &mt8195_sof_be_ops, SND_SOC_DAILINK_REG(AFE_SOF_DL2), }, [DAI_LINK_SOF_DL3_BE] = { .name = "AFE_SOF_DL3", .no_pcm = 1, .dpcm_playback = 1, + .ops = &mt8195_sof_be_ops, SND_SOC_DAILINK_REG(AFE_SOF_DL3), }, [DAI_LINK_SOF_UL4_BE] = { .name = "AFE_SOF_UL4", .no_pcm = 1, .dpcm_capture = 1, + .ops = &mt8195_sof_be_ops, SND_SOC_DAILINK_REG(AFE_SOF_UL4), }, [DAI_LINK_SOF_UL5_BE] = { .name = "AFE_SOF_UL5", .no_pcm = 1, .dpcm_capture = 1, + .ops = &mt8195_sof_be_ops, SND_SOC_DAILINK_REG(AFE_SOF_UL5), }, }; diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c index a7549f827235..5b1e47bdc376 100644 --- a/sound/soc/rockchip/rockchip_pdm.c +++ b/sound/soc/rockchip/rockchip_pdm.c @@ -431,6 +431,7 @@ static int rockchip_pdm_runtime_resume(struct device *dev) ret = clk_prepare_enable(pdm->hclk); if (ret) { + clk_disable_unprepare(pdm->clk); dev_err(pdm->dev, "hclock enable failed %d\n", ret); return ret; } diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c index 8bef572d3cbc..5b4f00457587 100644 --- a/sound/soc/rockchip/rockchip_spdif.c +++ b/sound/soc/rockchip/rockchip_spdif.c @@ -88,6 +88,7 @@ static int __maybe_unused rk_spdif_runtime_resume(struct device *dev) ret = clk_prepare_enable(spdif->hclk); if (ret) { + clk_disable_unprepare(spdif->mclk); dev_err(spdif->dev, "hclk clock enable failed %d\n", ret); return ret; } diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c index a7810c78ffa1..579a44d81d9a 100644 --- a/sound/soc/soc-pcm.c +++ b/sound/soc/soc-pcm.c @@ -709,8 +709,17 @@ static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd, snd_soc_dpcm_mutex_assert_held(rtd); - if (!rollback) + if (!rollback) { snd_soc_runtime_deactivate(rtd, substream->stream); + /* clear the corresponding DAIs parameters when going to be inactive */ + for_each_rtd_dais(rtd, i, dai) { + if (snd_soc_dai_active(dai) == 0) + soc_pcm_set_dai_params(dai, NULL); + + if (snd_soc_dai_stream_active(dai, substream->stream) == 0) + snd_soc_dai_digital_mute(dai, 1, substream->stream); + } + } for_each_rtd_dais(rtd, i, dai) snd_soc_dai_shutdown(dai, substream, rollback); @@ -940,15 +949,6 @@ static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd, snd_soc_dpcm_mutex_assert_held(rtd); - /* clear the corresponding DAIs parameters when going to be inactive */ - for_each_rtd_dais(rtd, i, dai) { - if (snd_soc_dai_active(dai) == 1) - soc_pcm_set_dai_params(dai, NULL); - - if (snd_soc_dai_stream_active(dai, substream->stream) == 1) - snd_soc_dai_digital_mute(dai, 1, substream->stream); - } - /* run the stream event */ snd_soc_dapm_stream_stop(rtd, substream->stream); diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c index 3e6141d03770..625977a29d8a 100644 --- a/sound/soc/sof/core.c +++ b/sound/soc/sof/core.c @@ -475,19 +475,10 @@ EXPORT_SYMBOL(snd_sof_device_remove); int snd_sof_device_shutdown(struct device *dev) { struct snd_sof_dev *sdev = dev_get_drvdata(dev); - struct snd_sof_pdata *pdata = sdev->pdata; if (IS_ENABLED(CONFIG_SND_SOC_SOF_PROBE_WORK_QUEUE)) cancel_work_sync(&sdev->probe_work); - /* - * make sure clients and machine driver(s) are unregistered to force - * all userspace devices to be closed prior to the DSP shutdown sequence - */ - sof_unregister_clients(sdev); - - snd_sof_machine_unregister(sdev, pdata); - if (sdev->fw_state == SOF_FW_BOOT_COMPLETE) return snd_sof_shutdown(sdev); diff --git a/sound/soc/sof/intel/hda-dsp.c b/sound/soc/sof/intel/hda-dsp.c index 5fa29df54b42..b4eacae8564c 100644 --- a/sound/soc/sof/intel/hda-dsp.c +++ b/sound/soc/sof/intel/hda-dsp.c @@ -878,6 +878,78 @@ int hda_dsp_suspend(struct snd_sof_dev *sdev, u32 target_state) return snd_sof_dsp_set_power_state(sdev, &target_dsp_state); } +static unsigned int hda_dsp_check_for_dma_streams(struct snd_sof_dev *sdev) +{ + struct hdac_bus *bus = sof_to_bus(sdev); + struct hdac_stream *s; + unsigned int active_streams = 0; + int sd_offset; + u32 val; + + list_for_each_entry(s, &bus->stream_list, list) { + sd_offset = SOF_STREAM_SD_OFFSET(s); + val = snd_sof_dsp_read(sdev, HDA_DSP_HDA_BAR, + sd_offset); + if (val & SOF_HDA_SD_CTL_DMA_START) + active_streams |= BIT(s->index); + } + + return active_streams; +} + +static int hda_dsp_s5_quirk(struct snd_sof_dev *sdev) +{ + int ret; + + /* + * Do not assume a certain timing between the prior + * suspend flow, and running of this quirk function. + * This is needed if the controller was just put + * to reset before calling this function. + */ + usleep_range(500, 1000); + + /* + * Take controller out of reset to flush DMA + * transactions. + */ + ret = hda_dsp_ctrl_link_reset(sdev, false); + if (ret < 0) + return ret; + + usleep_range(500, 1000); + + /* Restore state for shutdown, back to reset */ + ret = hda_dsp_ctrl_link_reset(sdev, true); + if (ret < 0) + return ret; + + return ret; +} + +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev) +{ + unsigned int active_streams; + int ret, ret2; + + /* check if DMA cleanup has been successful */ + active_streams = hda_dsp_check_for_dma_streams(sdev); + + sdev->system_suspend_target = SOF_SUSPEND_S3; + ret = snd_sof_suspend(sdev->dev); + + if (active_streams) { + dev_warn(sdev->dev, + "There were active DSP streams (%#x) at shutdown, trying to recover\n", + active_streams); + ret2 = hda_dsp_s5_quirk(sdev); + if (ret2 < 0) + dev_err(sdev->dev, "shutdown recovery failed (%d)\n", ret2); + } + + return ret; +} + int hda_dsp_shutdown(struct snd_sof_dev *sdev) { sdev->system_suspend_target = SOF_SUSPEND_S3; diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h index 022ce80968dd..caccaf8fba9c 100644 --- a/sound/soc/sof/intel/hda.h +++ b/sound/soc/sof/intel/hda.h @@ -592,6 +592,7 @@ int hda_dsp_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_suspend(struct snd_sof_dev *sdev); int hda_dsp_runtime_resume(struct snd_sof_dev *sdev); int hda_dsp_runtime_idle(struct snd_sof_dev *sdev); +int hda_dsp_shutdown_dma_flush(struct snd_sof_dev *sdev); int hda_dsp_shutdown(struct snd_sof_dev *sdev); int hda_dsp_set_hw_params_upon_resume(struct snd_sof_dev *sdev); void hda_dsp_dump(struct snd_sof_dev *sdev, u32 flags); diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c index 30f2f49ee149..58ac3a46e6a7 100644 --- a/sound/soc/sof/intel/tgl.c +++ b/sound/soc/sof/intel/tgl.c @@ -60,7 +60,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev) memcpy(&sof_tgl_ops, &sof_hda_common_ops, sizeof(struct snd_sof_dsp_ops)); /* probe/remove/shutdown */ - sof_tgl_ops.shutdown = hda_dsp_shutdown; + sof_tgl_ops.shutdown = hda_dsp_shutdown_dma_flush; if (sdev->pdata->ipc_type == SOF_IPC) { /* doorbell */ diff --git a/sound/soc/sof/mediatek/mtk-adsp-common.c b/sound/soc/sof/mediatek/mtk-adsp-common.c index 1e0769c668a7..de8dbe27cd0d 100644 --- a/sound/soc/sof/mediatek/mtk-adsp-common.c +++ b/sound/soc/sof/mediatek/mtk-adsp-common.c @@ -60,7 +60,7 @@ void mtk_adsp_dump(struct snd_sof_dev *sdev, u32 flags) { char *level = (flags & SOF_DBG_DUMP_OPTIONAL) ? KERN_DEBUG : KERN_ERR; struct sof_ipc_dsp_oops_xtensa xoops; - struct sof_ipc_panic_info panic_info; + struct sof_ipc_panic_info panic_info = {}; u32 stack[MTK_ADSP_STACK_DUMP_SIZE]; u32 status; diff --git a/sound/usb/card.h b/sound/usb/card.h index 40061550105a..6ec95b2edf86 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -131,6 +131,7 @@ struct snd_usb_endpoint { bool lowlatency_playback; /* low-latency playback mode */ bool need_setup; /* (re-)need for hw_params? */ bool need_prepare; /* (re-)need for prepare? */ + bool fixed_rate; /* skip rate setup */ /* for hw constraints */ const struct audioformat *cur_audiofmt; diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 4aaf0784940b..419302e2057e 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -769,7 +769,8 @@ struct snd_usb_endpoint * snd_usb_endpoint_open(struct snd_usb_audio *chip, const struct audioformat *fp, const struct snd_pcm_hw_params *params, - bool is_sync_ep) + bool is_sync_ep, + bool fixed_rate) { struct snd_usb_endpoint *ep; int ep_num = is_sync_ep ? fp->sync_ep : fp->endpoint; @@ -825,6 +826,7 @@ snd_usb_endpoint_open(struct snd_usb_audio *chip, ep->implicit_fb_sync = fp->implicit_fb; ep->need_setup = true; ep->need_prepare = true; + ep->fixed_rate = fixed_rate; usb_audio_dbg(chip, " channels=%d, rate=%d, format=%s, period_bytes=%d, periods=%d, implicit_fb=%d\n", ep->cur_channels, ep->cur_rate, @@ -1413,11 +1415,13 @@ static int init_sample_rate(struct snd_usb_audio *chip, if (clock && !clock->need_setup) return 0; - err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate); - if (err < 0) { - if (clock) - clock->rate = 0; /* reset rate */ - return err; + if (!ep->fixed_rate) { + err = snd_usb_init_sample_rate(chip, ep->cur_audiofmt, rate); + if (err < 0) { + if (clock) + clock->rate = 0; /* reset rate */ + return err; + } } if (clock) diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index e67ea28faa54..924f4351588c 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -14,7 +14,8 @@ struct snd_usb_endpoint * snd_usb_endpoint_open(struct snd_usb_audio *chip, const struct audioformat *fp, const struct snd_pcm_hw_params *params, - bool is_sync_ep); + bool is_sync_ep, + bool fixed_rate); void snd_usb_endpoint_close(struct snd_usb_audio *chip, struct snd_usb_endpoint *ep); int snd_usb_endpoint_set_params(struct snd_usb_audio *chip, diff --git a/sound/usb/implicit.c b/sound/usb/implicit.c index f3e8484b3d9c..41ac7185b42b 100644 --- a/sound/usb/implicit.c +++ b/sound/usb/implicit.c @@ -15,6 +15,7 @@ #include "usbaudio.h" #include "card.h" #include "helper.h" +#include "pcm.h" #include "implicit.h" enum { @@ -455,7 +456,8 @@ const struct audioformat * snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, const struct audioformat *target, const struct snd_pcm_hw_params *params, - int stream) + int stream, + bool *fixed_rate) { struct snd_usb_substream *subs; const struct audioformat *fp, *sync_fmt = NULL; @@ -483,6 +485,8 @@ snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, } } + if (fixed_rate) + *fixed_rate = snd_usb_pcm_has_fixed_rate(subs); return sync_fmt; } diff --git a/sound/usb/implicit.h b/sound/usb/implicit.h index ccb415a0ea86..7f1577b6c4d3 100644 --- a/sound/usb/implicit.h +++ b/sound/usb/implicit.h @@ -9,6 +9,6 @@ const struct audioformat * snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, const struct audioformat *target, const struct snd_pcm_hw_params *params, - int stream); + int stream, bool *fixed_rate); #endif /* __USBAUDIO_IMPLICIT_H */ diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index 9557bd4d1bbc..99a66d0ef5b2 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -157,6 +157,31 @@ find_substream_format(struct snd_usb_substream *subs, true, subs); } +bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *subs) +{ + const struct audioformat *fp; + struct snd_usb_audio *chip = subs->stream->chip; + int rate = -1; + + if (!(chip->quirk_flags & QUIRK_FLAG_FIXED_RATE)) + return false; + list_for_each_entry(fp, &subs->fmt_list, list) { + if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) + return false; + if (fp->nr_rates < 1) + continue; + if (fp->nr_rates > 1) + return false; + if (rate < 0) { + rate = fp->rate_table[0]; + continue; + } + if (rate != fp->rate_table[0]) + return false; + } + return true; +} + static int init_pitch_v1(struct snd_usb_audio *chip, int ep) { struct usb_device *dev = chip->dev; @@ -450,12 +475,14 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, struct snd_usb_audio *chip = subs->stream->chip; const struct audioformat *fmt; const struct audioformat *sync_fmt; + bool fixed_rate, sync_fixed_rate; int ret; ret = snd_media_start_pipeline(subs); if (ret) return ret; + fixed_rate = snd_usb_pcm_has_fixed_rate(subs); fmt = find_substream_format(subs, hw_params); if (!fmt) { usb_audio_dbg(chip, @@ -469,7 +496,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (fmt->implicit_fb) { sync_fmt = snd_usb_find_implicit_fb_sync_format(chip, fmt, hw_params, - !substream->stream); + !substream->stream, + &sync_fixed_rate); if (!sync_fmt) { usb_audio_dbg(chip, "cannot find sync format: ep=0x%x, iface=%d:%d, format=%s, rate=%d, channels=%d\n", @@ -482,6 +510,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, } } else { sync_fmt = fmt; + sync_fixed_rate = fixed_rate; } ret = snd_usb_lock_shutdown(chip); @@ -499,7 +528,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, close_endpoints(chip, subs); } - subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false); + subs->data_endpoint = snd_usb_endpoint_open(chip, fmt, hw_params, false, fixed_rate); if (!subs->data_endpoint) { ret = -EINVAL; goto unlock; @@ -508,7 +537,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream, if (fmt->sync_ep) { subs->sync_endpoint = snd_usb_endpoint_open(chip, sync_fmt, hw_params, - fmt == sync_fmt); + fmt == sync_fmt, + sync_fixed_rate); if (!subs->sync_endpoint) { ret = -EINVAL; goto unlock; diff --git a/sound/usb/pcm.h b/sound/usb/pcm.h index 493a4e34d78d..388fe2ba346d 100644 --- a/sound/usb/pcm.h +++ b/sound/usb/pcm.h @@ -6,6 +6,8 @@ void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream); int snd_usb_pcm_suspend(struct snd_usb_stream *as); int snd_usb_pcm_resume(struct snd_usb_stream *as); +bool snd_usb_pcm_has_fixed_rate(struct snd_usb_substream *as); + int snd_usb_init_pitch(struct snd_usb_audio *chip, const struct audioformat *fmt); void snd_usb_preallocate_buffer(struct snd_usb_substream *subs); diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 874fcf245747..271884e35003 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -76,6 +76,8 @@ { USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f0a) }, /* E-Mu 0204 USB */ { USB_DEVICE_VENDOR_SPEC(0x041e, 0x3f19) }, +/* Ktmicro Usb_audio device */ +{ USB_DEVICE_VENDOR_SPEC(0x31b2, 0x0011) }, /* * Creative Technology, Ltd Live! Cam Sync HD [VF0770] diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 58b37bfc885c..3d13fdf7590c 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -2152,6 +2152,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { QUIRK_FLAG_GENERIC_IMPLICIT_FB), DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */ QUIRK_FLAG_IFACE_SKIP_CLOSE), + DEVICE_FLG(0x0ecb, 0x2069, /* JBL Quantum810 Wireless */ + QUIRK_FLAG_FIXED_RATE), /* Vendor matches */ VENDOR_FLG(0x045e, /* MS Lifecam */ diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index 2aba508a4831..f5a8dca66457 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -175,6 +175,9 @@ extern bool snd_usb_skip_validation; * QUIRK_FLAG_FORCE_IFACE_RESET * Force an interface reset whenever stopping & restarting a stream * (e.g. after xrun) + * QUIRK_FLAG_FIXED_RATE + * Do not set PCM rate (frequency) when only one rate is available + * for the given endpoint. */ #define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0) @@ -198,5 +201,6 @@ extern bool snd_usb_skip_validation; #define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18) #define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19) #define QUIRK_FLAG_FORCE_IFACE_RESET (1U << 20) +#define QUIRK_FLAG_FIXED_RATE (1U << 21) #endif /* __USBAUDIO_H */