diff options
author | Jani Nikula <jani.nikula@intel.com> | 2023-10-04 18:06:27 +0300 |
---|---|---|
committer | Jani Nikula <jani.nikula@intel.com> | 2023-10-04 18:06:27 +0300 |
commit | 7824a88b4286980512de2a46763646100274a5ac (patch) | |
tree | fc10f5509fa12f85b72ba14c9bdf62641278f303 /drivers/gpu | |
parent | 25591b66d0a4f9277241cebe1a74b4f985bc27a9 (diff) | |
parent | caacbdc28f545744770fb2caf347b3c4be9a6299 (diff) |
Merge drm/drm-next into drm-intel-next
Backmerge to sync up with drm-intel-gt-next and drm-misc-next.
Signed-off-by: Jani Nikula <jani.nikula@intel.com>
Diffstat (limited to 'drivers/gpu')
294 files changed, 6574 insertions, 3417 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index ab9ef1c20349..48ca28a2e4ff 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -216,6 +216,13 @@ config DRM_EXEC help Execution context for command submissions +config DRM_GPUVM + tristate + depends on DRM + help + GPU-VM representation providing helpers to manage a GPUs virtual + address space + config DRM_BUDDY tristate depends on DRM diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 215e78e79125..8e1bde059170 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -45,7 +45,6 @@ drm-y := \ drm_vblank.o \ drm_vblank_work.o \ drm_vma_manager.o \ - drm_gpuva_mgr.o \ drm_writeback.o drm-$(CONFIG_DRM_LEGACY) += \ drm_agpsupport.o \ @@ -81,6 +80,7 @@ obj-$(CONFIG_DRM_PANEL_ORIENTATION_QUIRKS) += drm_panel_orientation_quirks.o # # obj-$(CONFIG_DRM_EXEC) += drm_exec.o +obj-$(CONFIG_DRM_GPUVM) += drm_gpuvm.o obj-$(CONFIG_DRM_BUDDY) += drm_buddy.o diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index dc2d53081e80..a79d53bdbe13 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1293,7 +1293,6 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, void amdgpu_device_pci_config_reset(struct amdgpu_device *adev); int amdgpu_device_pci_reset(struct amdgpu_device *adev); bool amdgpu_device_need_post(struct amdgpu_device *adev); -bool amdgpu_sg_display_supported(struct amdgpu_device *adev); bool amdgpu_device_pcie_dynamic_switching_supported(void); bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev); bool amdgpu_device_aspm_support_quirk(void); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index cdf6087706aa..25d5fda5b243 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -478,7 +478,7 @@ void amdgpu_amdkfd_get_cu_info(struct amdgpu_device *adev, struct kfd_cu_info *c cu_info->cu_active_number = acu_info.number; cu_info->cu_ao_mask = acu_info.ao_cu_mask; memcpy(&cu_info->cu_bitmap[0], &acu_info.bitmap[0], - sizeof(acu_info.bitmap)); + sizeof(cu_info->cu_bitmap)); cu_info->num_shader_engines = adev->gfx.config.max_shader_engines; cu_info->num_shader_arrays_per_engine = adev->gfx.config.max_sh_per_se; cu_info->num_cu_per_sh = adev->gfx.config.max_cu_per_sh; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c index f1f2c24de081..69810b3f1c63 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c @@ -980,8 +980,7 @@ void kgd_gfx_v10_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst) + uint32_t *reg_data) { *reg_data = wait_times; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h index ecaead24e8c9..67bcaa3d4226 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h @@ -55,5 +55,4 @@ void kgd_gfx_v10_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c index fa5ee96f8845..3c45a188b701 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c @@ -1103,8 +1103,7 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst) + uint32_t *reg_data) { *reg_data = wait_times; @@ -1120,8 +1119,7 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, SCH_WAVE, grace_period); - *reg_offset = SOC15_REG_OFFSET(GC, GET_INST(GC, inst), - mmCP_IQ_WAIT_TIME2); + *reg_offset = SOC15_REG_OFFSET(GC, 0, mmCP_IQ_WAIT_TIME2); } void kgd_gfx_v9_program_trap_handler_settings(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h index 936e501908ce..ce424615f59b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h @@ -100,5 +100,4 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 3f001a50b34a..30c4f5cca02c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1245,32 +1245,6 @@ bool amdgpu_device_need_post(struct amdgpu_device *adev) } /* - * On APUs with >= 64GB white flickering has been observed w/ SG enabled. - * Disable S/G on such systems until we have a proper fix. - * https://gitlab.freedesktop.org/drm/amd/-/issues/2354 - * https://gitlab.freedesktop.org/drm/amd/-/issues/2735 - */ -bool amdgpu_sg_display_supported(struct amdgpu_device *adev) -{ - switch (amdgpu_sg_display) { - case -1: - break; - case 0: - return false; - case 1: - return true; - default: - return false; - } - if ((totalram_pages() << (PAGE_SHIFT - 10)) + - (adev->gmc.real_vram_size / 1024) >= 64000000) { - DRM_WARN("Disabling S/G due to >=64GB RAM\n"); - return false; - } - return true; -} - -/* * Intel hosts such as Raptor Lake and Sapphire Rapids don't support dynamic * speed switching. Until we have confirmation from Intel that a specific host * supports it, it's safer that we keep it disabled for all. diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c index ca4d2d430e28..a1b15d0d6c48 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gem.c @@ -962,6 +962,7 @@ static int amdgpu_debugfs_gem_info_show(struct seq_file *m, void *unused) list_for_each_entry(file, &dev->filelist, lhead) { struct task_struct *task; struct drm_gem_object *gobj; + struct pid *pid; int id; /* @@ -971,8 +972,9 @@ static int amdgpu_debugfs_gem_info_show(struct seq_file *m, void *unused) * Therefore, we need to protect this ->comm access using RCU. */ rcu_read_lock(); - task = pid_task(file->pid, PIDTYPE_TGID); - seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid), + pid = rcu_dereference(file->pid); + task = pid_task(pid, PIDTYPE_TGID); + seq_printf(m, "pid %8d command %s:\n", pid_nr(pid), task ? task->comm : "<unknown>"); rcu_read_unlock(); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index 395c1768b9fc..0ca95c4d4bfb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -43,6 +43,7 @@ #define AMDGPU_GFX_LBPW_DISABLED_MODE 0x00000008L #define AMDGPU_MAX_GC_INSTANCES 8 +#define KGD_MAX_QUEUES 128 #define AMDGPU_MAX_GFX_QUEUES KGD_MAX_QUEUES #define AMDGPU_MAX_COMPUTE_QUEUES KGD_MAX_QUEUES @@ -257,7 +258,7 @@ struct amdgpu_cu_info { uint32_t number; uint32_t ao_cu_mask; uint32_t ao_cu_bitmap[4][4]; - uint32_t bitmap[4][4]; + uint32_t bitmap[AMDGPU_MAX_GC_INSTANCES][4][4]; }; struct amdgpu_gfx_ras { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 99f4df133ed3..d30dc0b718c7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -839,7 +839,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) memcpy(&dev_info->cu_ao_bitmap[0], &adev->gfx.cu_info.ao_cu_bitmap[0], sizeof(adev->gfx.cu_info.ao_cu_bitmap)); memcpy(&dev_info->cu_bitmap[0], &adev->gfx.cu_info.bitmap[0], - sizeof(adev->gfx.cu_info.bitmap)); + sizeof(dev_info->cu_bitmap)); dev_info->vram_type = adev->gmc.vram_type; dev_info->vram_bit_width = adev->gmc.vram_width; dev_info->vce_harvest_config = adev->vce.harvest_config; @@ -940,12 +940,17 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) struct atom_context *atom_context; atom_context = adev->mode_info.atom_context; - memcpy(vbios_info.name, atom_context->name, sizeof(atom_context->name)); - memcpy(vbios_info.vbios_pn, atom_context->vbios_pn, sizeof(atom_context->vbios_pn)); - vbios_info.version = atom_context->version; - memcpy(vbios_info.vbios_ver_str, atom_context->vbios_ver_str, - sizeof(atom_context->vbios_ver_str)); - memcpy(vbios_info.date, atom_context->date, sizeof(atom_context->date)); + if (atom_context) { + memcpy(vbios_info.name, atom_context->name, + sizeof(atom_context->name)); + memcpy(vbios_info.vbios_pn, atom_context->vbios_pn, + sizeof(atom_context->vbios_pn)); + vbios_info.version = atom_context->version; + memcpy(vbios_info.vbios_ver_str, atom_context->vbios_ver_str, + sizeof(atom_context->vbios_ver_str)); + memcpy(vbios_info.date, atom_context->date, + sizeof(atom_context->date)); + } return copy_to_user(out, &vbios_info, min((size_t)size, sizeof(vbios_info))) ? -EFAULT : 0; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 3c4600e15b86..937c54fc7174 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -1052,7 +1052,8 @@ int amdgpu_ras_query_error_status(struct amdgpu_device *adev, info->ce_count = obj->err_data.ce_count; if (err_data.ce_count) { - if (adev->smuio.funcs && + if (!adev->aid_mask && + adev->smuio.funcs && adev->smuio.funcs->get_socket_id && adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " @@ -1072,7 +1073,8 @@ int amdgpu_ras_query_error_status(struct amdgpu_device *adev, } } if (err_data.ue_count) { - if (adev->smuio.funcs && + if (!adev->aid_mask && + adev->smuio.funcs && adev->smuio.funcs->get_socket_id && adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index c6b4337eb20c..10df731998b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -81,7 +81,7 @@ int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, unsigned int size) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, true, 0); + GFP_KERNEL, false, 0); if (IS_ERR(sa)) { *sa_bo = NULL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c index c7085a747b03..18f58efc9dc7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vram_mgr.c @@ -424,9 +424,9 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, const struct ttm_place *place, struct ttm_resource **res) { - u64 vis_usage = 0, max_bytes, cur_size, min_block_size; struct amdgpu_vram_mgr *mgr = to_vram_mgr(man); struct amdgpu_device *adev = to_amdgpu_device(mgr); + u64 vis_usage = 0, max_bytes, min_block_size; struct amdgpu_vram_mgr_resource *vres; u64 size, remaining_size, lpfn, fpfn; struct drm_buddy *mm = &mgr->mm; @@ -474,6 +474,9 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, if (place->flags & TTM_PL_FLAG_TOPDOWN) vres->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + if (place->flags & TTM_PL_FLAG_CONTIGUOUS) + vres->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION; + if (fpfn || lpfn != mgr->mm.size) /* Allocate blocks in desired range */ vres->flags |= DRM_BUDDY_RANGE_ALLOCATION; @@ -496,25 +499,6 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, !(size & (((u64)pages_per_block << PAGE_SHIFT) - 1))) min_block_size = (u64)pages_per_block << PAGE_SHIFT; - cur_size = size; - - if (fpfn + size != (u64)place->lpfn << PAGE_SHIFT) { - /* - * Except for actual range allocation, modify the size and - * min_block_size conforming to continuous flag enablement - */ - if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { - size = roundup_pow_of_two(size); - min_block_size = size; - /* - * Modify the size value if size is not - * aligned with min_block_size - */ - } else if (!IS_ALIGNED(size, min_block_size)) { - size = round_up(size, min_block_size); - } - } - r = drm_buddy_alloc_blocks(mm, fpfn, lpfn, size, @@ -531,40 +515,6 @@ static int amdgpu_vram_mgr_new(struct ttm_resource_manager *man, } mutex_unlock(&mgr->lock); - if (cur_size != size) { - struct drm_buddy_block *block; - struct list_head *trim_list; - u64 original_size; - LIST_HEAD(temp); - - trim_list = &vres->blocks; - original_size = (u64)vres->base.size; - - /* - * If size value is rounded up to min_block_size, trim the last - * block to the required size - */ - if (!list_is_singular(&vres->blocks)) { - block = list_last_entry(&vres->blocks, typeof(*block), link); - list_move_tail(&block->link, &temp); - trim_list = &temp; - /* - * Compute the original_size value by subtracting the - * last block size with (aligned size - original size) - */ - original_size = amdgpu_vram_mgr_block_size(block) - (size - cur_size); - } - - mutex_lock(&mgr->lock); - drm_buddy_block_trim(mm, - original_size, - trim_list); - mutex_unlock(&mgr->lock); - - if (!list_empty(&temp)) - list_splice_tail(trim_list, &vres->blocks); - } - vres->base.start = 0; list_for_each_entry(block, &vres->blocks, link) { unsigned long start; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 0aee9c8288a2..9032d7a24d7c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -9449,7 +9449,7 @@ static int gfx_v10_0_get_cu_info(struct amdgpu_device *adev, gfx_v10_0_set_user_wgp_inactive_bitmap_per_sh( adev, disable_masks[i * 2 + j]); bitmap = gfx_v10_0_get_cu_active_bitmap_per_sh(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 5c3db694afa8..762d7a19f1be 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -6368,7 +6368,7 @@ static int gfx_v11_0_get_cu_info(struct amdgpu_device *adev, * SE6: {SH0,SH1} --> {bitmap[2][2], bitmap[2][3]} * SE7: {SH0,SH1} --> {bitmap[3][2], bitmap[3][3]} */ - cu_info->bitmap[i % 4][j + (i / 4) * 2] = bitmap; + cu_info->bitmap[0][i % 4][j + (i / 4) * 2] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index da6caff78c22..34f9211b2679 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -3577,7 +3577,7 @@ static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev) gfx_v6_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v6_0_get_cu_enabled(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 90b034b173c1..c2faf6b4c2fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -5119,7 +5119,7 @@ static void gfx_v7_0_get_cu_info(struct amdgpu_device *adev) gfx_v7_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v7_0_get_cu_active_bitmap(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 51c1745c8369..885ebd703260 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -7121,7 +7121,7 @@ static void gfx_v8_0_get_cu_info(struct amdgpu_device *adev) gfx_v8_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v8_0_get_cu_active_bitmap(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 458faf657042..fd61574a737c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1499,7 +1499,7 @@ static void gfx_v9_0_init_always_on_cu_mask(struct amdgpu_device *adev) amdgpu_gfx_select_se_sh(adev, i, j, 0xffffffff, 0); for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { - if (cu_info->bitmap[i][j] & mask) { + if (cu_info->bitmap[0][i][j] & mask) { if (counter == pg_always_on_cu_num) WREG32_SOC15(GC, 0, mmRLC_PG_ALWAYS_ON_CU_MASK, cu_bitmap); if (counter < always_on_cu_num) @@ -7233,7 +7233,7 @@ static int gfx_v9_0_get_cu_info(struct amdgpu_device *adev, * SE6,SH0 --> bitmap[2][1] * SE7,SH0 --> bitmap[3][1] */ - cu_info->bitmap[i % 4][j + i / 4] = bitmap; + cu_info->bitmap[0][i % 4][j + i / 4] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 0a26a00074a6..18ce5fe45f6f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -4259,7 +4259,7 @@ static void gfx_v9_4_3_set_gds_init(struct amdgpu_device *adev) } static void gfx_v9_4_3_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, - u32 bitmap) + u32 bitmap, int xcc_id) { u32 data; @@ -4269,15 +4269,15 @@ static void gfx_v9_4_3_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, data = bitmap << GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; data &= GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; - WREG32_SOC15(GC, GET_INST(GC, 0), regGC_USER_SHADER_ARRAY_CONFIG, data); + WREG32_SOC15(GC, GET_INST(GC, xcc_id), regGC_USER_SHADER_ARRAY_CONFIG, data); } -static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev) +static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev, int xcc_id) { u32 data, mask; - data = RREG32_SOC15(GC, GET_INST(GC, 0), regCC_GC_SHADER_ARRAY_CONFIG); - data |= RREG32_SOC15(GC, GET_INST(GC, 0), regGC_USER_SHADER_ARRAY_CONFIG); + data = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCC_GC_SHADER_ARRAY_CONFIG); + data |= RREG32_SOC15(GC, GET_INST(GC, xcc_id), regGC_USER_SHADER_ARRAY_CONFIG); data &= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; data >>= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; @@ -4290,7 +4290,7 @@ static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev) static int gfx_v9_4_3_get_cu_info(struct amdgpu_device *adev, struct amdgpu_cu_info *cu_info) { - int i, j, k, counter, active_cu_number = 0; + int i, j, k, counter, xcc_id, active_cu_number = 0; u32 mask, bitmap, ao_bitmap, ao_cu_mask = 0; unsigned disable_masks[4 * 4]; @@ -4309,46 +4309,38 @@ static int gfx_v9_4_3_get_cu_info(struct amdgpu_device *adev, adev->gfx.config.max_sh_per_se); mutex_lock(&adev->grbm_idx_mutex); - for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { - for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { - mask = 1; - ao_bitmap = 0; - counter = 0; - gfx_v9_4_3_xcc_select_se_sh(adev, i, j, 0xffffffff, 0); - gfx_v9_4_3_set_user_cu_inactive_bitmap( - adev, disable_masks[i * adev->gfx.config.max_sh_per_se + j]); - bitmap = gfx_v9_4_3_get_cu_active_bitmap(adev); - - /* - * The bitmap(and ao_cu_bitmap) in cu_info structure is - * 4x4 size array, and it's usually suitable for Vega - * ASICs which has 4*2 SE/SH layout. - * But for Arcturus, SE/SH layout is changed to 8*1. - * To mostly reduce the impact, we make it compatible - * with current bitmap array as below: - * SE4,SH0 --> bitmap[0][1] - * SE5,SH0 --> bitmap[1][1] - * SE6,SH0 --> bitmap[2][1] - * SE7,SH0 --> bitmap[3][1] - */ - cu_info->bitmap[i % 4][j + i / 4] = bitmap; - - for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { - if (bitmap & mask) { - if (counter < adev->gfx.config.max_cu_per_sh) - ao_bitmap |= mask; - counter++; + for (xcc_id = 0; xcc_id < NUM_XCC(adev->gfx.xcc_mask); xcc_id++) { + for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { + for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { + mask = 1; + ao_bitmap = 0; + counter = 0; + gfx_v9_4_3_xcc_select_se_sh(adev, i, j, 0xffffffff, xcc_id); + gfx_v9_4_3_set_user_cu_inactive_bitmap( + adev, + disable_masks[i * adev->gfx.config.max_sh_per_se + j], + xcc_id); + bitmap = gfx_v9_4_3_get_cu_active_bitmap(adev, xcc_id); + + cu_info->bitmap[xcc_id][i][j] = bitmap; + + for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { + if (bitmap & mask) { + if (counter < adev->gfx.config.max_cu_per_sh) + ao_bitmap |= mask; + counter++; + } + mask <<= 1; } - mask <<= 1; + active_cu_number += counter; + if (i < 2 && j < 2) + ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8)); + cu_info->ao_cu_bitmap[i][j] = ao_bitmap; } - active_cu_number += counter; - if (i < 2 && j < 2) - ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8)); - cu_info->ao_cu_bitmap[i % 4][j + i / 4] = ao_bitmap; } + gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, + xcc_id); } - gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, - 0); mutex_unlock(&adev->grbm_idx_mutex); cu_info->number = active_cu_number; diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c index d5ed9e0e1a5f..e5b5b0f4940f 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c @@ -345,6 +345,9 @@ static void nbio_v4_3_init_registers(struct amdgpu_device *adev) data &= ~RCC_DEV0_EPF2_STRAP2__STRAP_NO_SOFT_RESET_DEV0_F2_MASK; WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF2_STRAP2, data); } + if (amdgpu_sriov_vf(adev)) + adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, + regBIF_BX_DEV0_EPF0_VF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2; } static u32 nbio_v4_3_get_rom_offset(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 40d23738ee4e..8b2ff2b281b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -766,7 +766,7 @@ static int soc21_common_hw_init(void *handle) * for the purpose of expose those registers * to process space */ - if (adev->nbio.funcs->remap_hdp_registers) + if (adev->nbio.funcs->remap_hdp_registers && !amdgpu_sriov_vf(adev)) adev->nbio.funcs->remap_hdp_registers(adev); /* enable the doorbell aperture */ adev->nbio.funcs->enable_doorbell_aperture(adev, true); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 86fb7ac7982a..f76b7aee5c0a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -2087,7 +2087,8 @@ static int kfd_create_vcrat_image_gpu(void *pcrat_image, amdgpu_amdkfd_get_cu_info(kdev->adev, &cu_info); cu->num_simd_per_cu = cu_info.simd_per_cu; - cu->num_simd_cores = cu_info.simd_per_cu * cu_info.cu_active_number; + cu->num_simd_cores = cu_info.simd_per_cu * + (cu_info.cu_active_number / kdev->kfd->num_nodes); cu->max_waves_simd = cu_info.max_waves_per_simd; cu->wave_front_size = cu_info.wave_front_size; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.h b/drivers/gpu/drm/amd/amdkfd/kfd_crat.h index 387a8ef49385..74c2d7a0d628 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.h @@ -79,6 +79,10 @@ struct crat_header { #define CRAT_SUBTYPE_IOLINK_AFFINITY 5 #define CRAT_SUBTYPE_MAX 6 +/* + * Do not change the value of CRAT_SIBLINGMAP_SIZE from 32 + * as it breaks the ABI. + */ #define CRAT_SIBLINGMAP_SIZE 32 /* diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index b166f30f083e..8a6cb41444a4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -1677,8 +1677,7 @@ static int start_cpsch(struct device_queue_manager *dqm) dqm->dev->kfd2kgd->build_grace_period_packet_info( dqm->dev->adev, dqm->wait_times, grace_period, ®_offset, - &dqm->wait_times, - ffs(dqm->dev->xcc_mask) - 1); + &dqm->wait_times); } dqm_unlock(dqm); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c index c2e0b79dcc6d..7b38537c7c99 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c @@ -162,6 +162,7 @@ void __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd, return NULL; *doorbell_off = amdgpu_doorbell_index_on_bar(kfd->adev, kfd->doorbells, inx); + inx *= 2; pr_debug("Get kernel queue doorbell\n" " doorbell offset == 0x%08X\n" @@ -176,6 +177,7 @@ void kfd_release_kernel_doorbell(struct kfd_dev *kfd, u32 __iomem *db_addr) unsigned int inx; inx = (unsigned int)(db_addr - kfd->doorbell_kernel_ptr); + inx /= 2; mutex_lock(&kfd->doorbell_mutex); __clear_bit(inx, kfd->doorbell_bitmap); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c index d01bb57733b3..447829c22295 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c @@ -97,18 +97,22 @@ void free_mqd_hiq_sdma(struct mqd_manager *mm, void *mqd, void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, const uint32_t *cu_mask, uint32_t cu_mask_count, - uint32_t *se_mask) + uint32_t *se_mask, uint32_t inst) { struct kfd_cu_info cu_info; uint32_t cu_per_sh[KFD_MAX_NUM_SE][KFD_MAX_NUM_SH_PER_SE] = {0}; bool wgp_mode_req = KFD_GC_VERSION(mm->dev) >= IP_VERSION(10, 0, 0); uint32_t en_mask = wgp_mode_req ? 0x3 : 0x1; - int i, se, sh, cu, cu_bitmap_sh_mul, inc = wgp_mode_req ? 2 : 1; + int i, se, sh, cu, cu_bitmap_sh_mul, cu_inc = wgp_mode_req ? 2 : 1; + uint32_t cu_active_per_node; + int inc = cu_inc * NUM_XCC(mm->dev->xcc_mask); + int xcc_inst = inst + ffs(mm->dev->xcc_mask) - 1; amdgpu_amdkfd_get_cu_info(mm->dev->adev, &cu_info); - if (cu_mask_count > cu_info.cu_active_number) - cu_mask_count = cu_info.cu_active_number; + cu_active_per_node = cu_info.cu_active_number / mm->dev->kfd->num_nodes; + if (cu_mask_count > cu_active_per_node) + cu_mask_count = cu_active_per_node; /* Exceeding these bounds corrupts the stack and indicates a coding error. * Returning with no CU's enabled will hang the queue, which should be @@ -141,7 +145,8 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, for (se = 0; se < cu_info.num_shader_engines; se++) for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) cu_per_sh[se][sh] = hweight32( - cu_info.cu_bitmap[se % 4][sh + (se / 4) * cu_bitmap_sh_mul]); + cu_info.cu_bitmap[xcc_inst][se % 4][sh + (se / 4) * + cu_bitmap_sh_mul]); /* Symmetrically map cu_mask to all SEs & SHs: * se_mask programs up to 2 SH in the upper and lower 16 bits. @@ -164,20 +169,33 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, * cu_mask[0] bit8 -> se_mask[0] bit1 (SE0,SH0,CU1) * ... * + * For GFX 9.4.3, the following code only looks at a + * subset of the cu_mask corresponding to the inst parameter. + * If we have n XCCs under one GPU node + * cu_mask[0] bit0 -> XCC0 se_mask[0] bit0 (XCC0,SE0,SH0,CU0) + * cu_mask[0] bit1 -> XCC1 se_mask[0] bit0 (XCC1,SE0,SH0,CU0) + * .. + * cu_mask[0] bitn -> XCCn se_mask[0] bit0 (XCCn,SE0,SH0,CU0) + * cu_mask[0] bit n+1 -> XCC0 se_mask[1] bit0 (XCC0,SE1,SH0,CU0) + * + * For example, if there are 6 XCCs under 1 KFD node, this code + * running for each inst, will look at the bits as: + * inst, inst + 6, inst + 12... + * * First ensure all CUs are disabled, then enable user specified CUs. */ for (i = 0; i < cu_info.num_shader_engines; i++) se_mask[i] = 0; - i = 0; - for (cu = 0; cu < 16; cu += inc) { + i = inst; + for (cu = 0; cu < 16; cu += cu_inc) { for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) { for (se = 0; se < cu_info.num_shader_engines; se++) { if (cu_per_sh[se][sh] > cu) { if (cu_mask[i / 32] & (en_mask << (i % 32))) se_mask[se] |= en_mask << (cu + sh * 16); i += inc; - if (i == cu_mask_count) + if (i >= cu_mask_count) return; } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h index 23158db7da03..57bf5e513f4d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h @@ -138,7 +138,7 @@ void free_mqd_hiq_sdma(struct mqd_manager *mm, void *mqd, void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, const uint32_t *cu_mask, uint32_t cu_mask_count, - uint32_t *se_mask); + uint32_t *se_mask, uint32_t inst); int kfd_hiq_load_mqd_kiq(struct mqd_manager *mm, void *mqd, uint32_t pipe_id, uint32_t queue_id, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c index ee1d32d957f2..1a4a69943c71 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c @@ -52,7 +52,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index 83699392c808..8b7fed913526 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -52,7 +52,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index 0bbf0edbabd4..15277f1d5cf0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -71,7 +71,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, } mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m->compute_static_thread_mgmt_se0 = se_mask[0]; m->compute_static_thread_mgmt_se1 = se_mask[1]; @@ -321,6 +321,43 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, return 0; } +static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst) +{ + struct v11_compute_mqd *m; + + m = get_mqd(mqd); + + memcpy(mqd_dst, m, sizeof(struct v11_compute_mqd)); +} + +static void restore_mqd(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, const u32 ctl_stack_size) +{ + uint64_t addr; + struct v11_compute_mqd *m; + + m = (struct v11_compute_mqd *) mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memcpy(m, mqd_src, sizeof(*m)); + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + m->cp_hqd_pq_doorbell_control = + qp->doorbell_off << + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", + m->cp_hqd_pq_doorbell_control); + + qp->is_active = 0; +} + + static void init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, struct queue_properties *q) @@ -458,6 +495,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->mqd_size = sizeof(struct v11_compute_mqd); mqd->get_wave_state = get_wave_state; mqd->mqd_stride = kfd_mqd_stride; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; #endif @@ -502,6 +541,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->update_mqd = update_mqd_sdma; mqd->destroy_mqd = kfd_destroy_mqd_sdma; mqd->is_occupied = kfd_is_occupied_sdma; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; mqd->mqd_size = sizeof(struct v11_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index e23d32f35607..42d881809dc7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -60,7 +60,7 @@ static inline struct v9_sdma_mqd *get_sdma_mqd(void *mqd) } static void update_cu_mask(struct mqd_manager *mm, void *mqd, - struct mqd_update_info *minfo) + struct mqd_update_info *minfo, uint32_t inst) { struct v9_mqd *m; uint32_t se_mask[KFD_MAX_NUM_SE] = {0}; @@ -69,27 +69,36 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, inst); m = get_mqd(mqd); + m->compute_static_thread_mgmt_se0 = se_mask[0]; m->compute_static_thread_mgmt_se1 = se_mask[1]; m->compute_static_thread_mgmt_se2 = se_mask[2]; m->compute_static_thread_mgmt_se3 = se_mask[3]; - m->compute_static_thread_mgmt_se4 = se_mask[4]; - m->compute_static_thread_mgmt_se5 = se_mask[5]; - m->compute_static_thread_mgmt_se6 = se_mask[6]; - m->compute_static_thread_mgmt_se7 = se_mask[7]; - - pr_debug("update cu mask to %#x %#x %#x %#x %#x %#x %#x %#x\n", - m->compute_static_thread_mgmt_se0, - m->compute_static_thread_mgmt_se1, - m->compute_static_thread_mgmt_se2, - m->compute_static_thread_mgmt_se3, - m->compute_static_thread_mgmt_se4, - m->compute_static_thread_mgmt_se5, - m->compute_static_thread_mgmt_se6, - m->compute_static_thread_mgmt_se7); + if (KFD_GC_VERSION(mm->dev) != IP_VERSION(9, 4, 3)) { + m->compute_static_thread_mgmt_se4 = se_mask[4]; + m->compute_static_thread_mgmt_se5 = se_mask[5]; + m->compute_static_thread_mgmt_se6 = se_mask[6]; + m->compute_static_thread_mgmt_se7 = se_mask[7]; + + pr_debug("update cu mask to %#x %#x %#x %#x %#x %#x %#x %#x\n", + m->compute_static_thread_mgmt_se0, + m->compute_static_thread_mgmt_se1, + m->compute_static_thread_mgmt_se2, + m->compute_static_thread_mgmt_se3, + m->compute_static_thread_mgmt_se4, + m->compute_static_thread_mgmt_se5, + m->compute_static_thread_mgmt_se6, + m->compute_static_thread_mgmt_se7); + } else { + pr_debug("inst: %u, update cu mask to %#x %#x %#x %#x\n", + inst, m->compute_static_thread_mgmt_se0, + m->compute_static_thread_mgmt_se1, + m->compute_static_thread_mgmt_se2, + m->compute_static_thread_mgmt_se3); + } } static void set_priority(struct v9_mqd *m, struct queue_properties *q) @@ -290,7 +299,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, if (mm->dev->kfd->cwsr_enabled && q->ctx_save_restore_area_address) m->cp_hqd_ctx_save_control = 0; - update_cu_mask(mm, mqd, minfo); + if (KFD_GC_VERSION(mm->dev) != IP_VERSION(9, 4, 3)) + update_cu_mask(mm, mqd, minfo, 0); set_priority(m, q); q->is_active = QUEUE_IS_ACTIVE(*q); @@ -676,6 +686,8 @@ static void update_mqd_v9_4_3(struct mqd_manager *mm, void *mqd, m = get_mqd(mqd + size * xcc); update_mqd(mm, m, q, minfo); + update_cu_mask(mm, mqd, minfo, xcc); + if (q->format == KFD_QUEUE_FORMAT_AQL) { switch (xcc) { case 0: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index 657c37822980..3e1a574d4ea6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -55,7 +55,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c index 8ce6f5200905..1a03173e2313 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c @@ -299,8 +299,7 @@ static int pm_set_grace_period_v9(struct packet_manager *pm, pm->dqm->wait_times, grace_period, ®_offset, - ®_data, - 0); + ®_data); if (grace_period == USE_DEFAULT_GRACE_PERIOD) reg_data = pm->dqm->wait_times; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 3d9ce44d88da..fa24e1852493 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -1466,8 +1466,7 @@ void kfd_flush_tlb(struct kfd_process_device *pdd, enum TLB_FLUSH_TYPE type); static inline bool kfd_flush_tlb_after_unmap(struct kfd_dev *dev) { - return KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 3) || - KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 2) || + return KFD_GC_VERSION(dev) > IP_VERSION(9, 4, 2) || (KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 1) && dev->sdma_fw_version >= 18) || KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 0); } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index ff98fded9534..c8c75ff7cea8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -450,8 +450,7 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr, sysfs_show_32bit_prop(buffer, offs, "cpu_cores_count", dev->node_props.cpu_cores_count); sysfs_show_32bit_prop(buffer, offs, "simd_count", - dev->gpu ? (dev->node_props.simd_count * - NUM_XCC(dev->gpu->xcc_mask)) : 0); + dev->gpu ? dev->node_props.simd_count : 0); sysfs_show_32bit_prop(buffer, offs, "mem_banks_count", dev->node_props.mem_banks_count); sysfs_show_32bit_prop(buffer, offs, "caches_count", @@ -1597,14 +1596,17 @@ static int fill_in_l1_pcache(struct kfd_cache_properties **props_ext, static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, struct kfd_gpu_cache_info *pcache_info, struct kfd_cu_info *cu_info, - int cache_type, unsigned int cu_processor_id) + int cache_type, unsigned int cu_processor_id, + struct kfd_node *knode) { unsigned int cu_sibling_map_mask; int first_active_cu; - int i, j, k; + int i, j, k, xcc, start, end; struct kfd_cache_properties *pcache = NULL; - cu_sibling_map_mask = cu_info->cu_bitmap[0][0]; + start = ffs(knode->xcc_mask) - 1; + end = start + NUM_XCC(knode->xcc_mask); + cu_sibling_map_mask = cu_info->cu_bitmap[start][0][0]; cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); first_active_cu = ffs(cu_sibling_map_mask); @@ -1639,16 +1641,18 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, cu_sibling_map_mask = cu_sibling_map_mask >> (first_active_cu - 1); k = 0; - for (i = 0; i < cu_info->num_shader_engines; i++) { - for (j = 0; j < cu_info->num_shader_arrays_per_engine; j++) { - pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF); - pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF); - pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF); - pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); - k += 4; - - cu_sibling_map_mask = cu_info->cu_bitmap[i % 4][j + i / 4]; - cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); + for (xcc = start; xcc < end; xcc++) { + for (i = 0; i < cu_info->num_shader_engines; i++) { + for (j = 0; j < cu_info->num_shader_arrays_per_engine; j++) { + pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF); + pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF); + pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF); + pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); + k += 4; + + cu_sibling_map_mask = cu_info->cu_bitmap[xcc][i % 4][j + i / 4]; + cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); + } } } pcache->sibling_map_size = k; @@ -1666,7 +1670,7 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct kfd_node *kdev) { struct kfd_gpu_cache_info *pcache_info = NULL; - int i, j, k; + int i, j, k, xcc, start, end; int ct = 0; unsigned int cu_processor_id; int ret; @@ -1700,37 +1704,42 @@ static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct * then it will consider only one CU from * the shared unit */ + start = ffs(kdev->xcc_mask) - 1; + end = start + NUM_XCC(kdev->xcc_mask); + for (ct = 0; ct < num_of_cache_types; ct++) { cu_processor_id = gpu_processor_id; if (pcache_info[ct].cache_level == 1) { - for (i = 0; i < pcu_info->num_shader_engines; i++) { - for (j = 0; j < pcu_info->num_shader_arrays_per_engine; j++) { - for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { + for (xcc = start; xcc < end; xcc++) { + for (i = 0; i < pcu_info->num_shader_engines; i++) { + for (j = 0; j < pcu_info->num_shader_arrays_per_engine; j++) { + for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { - ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, - pcu_info->cu_bitmap[i % 4][j + i / 4], ct, + ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, + pcu_info->cu_bitmap[xcc][i % 4][j + i / 4], ct, cu_processor_id, k); - if (ret < 0) - break; + if (ret < 0) + break; - if (!ret) { - num_of_entries++; - list_add_tail(&props_ext->list, &dev->cache_props); - } + if (!ret) { + num_of_entries++; + list_add_tail(&props_ext->list, &dev->cache_props); + } - /* Move to next CU block */ - num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <= - pcu_info->num_cu_per_sh) ? - pcache_info[ct].num_cu_shared : - (pcu_info->num_cu_per_sh - k); - cu_processor_id += num_cu_shared; + /* Move to next CU block */ + num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <= + pcu_info->num_cu_per_sh) ? + pcache_info[ct].num_cu_shared : + (pcu_info->num_cu_per_sh - k); + cu_processor_id += num_cu_shared; + } } } } } else { ret = fill_in_l2_l3_pcache(&props_ext, pcache_info, - pcu_info, ct, cu_processor_id); + pcu_info, ct, cu_processor_id, kdev); if (ret < 0) break; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.h b/drivers/gpu/drm/amd/amdkfd/kfd_topology.h index dea32a9e5506..27386ce9a021 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.h @@ -89,7 +89,7 @@ struct kfd_mem_properties { struct attribute attr; }; -#define CACHE_SIBLINGMAP_SIZE 64 +#define CACHE_SIBLINGMAP_SIZE 128 struct kfd_cache_properties { struct list_head list; 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 88ba8b66de1f..c6fd34bab358 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1274,11 +1274,15 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_ pt_base = amdgpu_gmc_pd_addr(adev->gart.bo); - page_table_start.high_part = (u32)(adev->gmc.gart_start >> 44) & 0xF; - page_table_start.low_part = (u32)(adev->gmc.gart_start >> 12); - page_table_end.high_part = (u32)(adev->gmc.gart_end >> 44) & 0xF; - page_table_end.low_part = (u32)(adev->gmc.gart_end >> 12); - page_table_base.high_part = upper_32_bits(pt_base) & 0xF; + page_table_start.high_part = upper_32_bits(adev->gmc.gart_start >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_start.low_part = lower_32_bits(adev->gmc.gart_start >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_end.high_part = upper_32_bits(adev->gmc.gart_end >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_end.low_part = lower_32_bits(adev->gmc.gart_end >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_base.high_part = upper_32_bits(pt_base); page_table_base.low_part = lower_32_bits(pt_base); pa_config->system_aperture.start_addr = (uint64_t)logical_addr_low << 18; @@ -1640,8 +1644,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) } break; } - if (init_data.flags.gpu_vm_support) - init_data.flags.gpu_vm_support = amdgpu_sg_display_supported(adev); + if (init_data.flags.gpu_vm_support && + (amdgpu_sg_display == 0)) + init_data.flags.gpu_vm_support = false; if (init_data.flags.gpu_vm_support) adev->mode_info.gpu_vm_support = true; @@ -2335,14 +2340,62 @@ static int dm_late_init(void *handle) return detect_mst_link_for_all_connectors(adev_to_drm(adev)); } +static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret; + u8 guid[16]; + u64 tmp64; + + mutex_lock(&mgr->lock); + if (!mgr->mst_primary) + goto out_fail; + + if (drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd) < 0) { + drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); + goto out_fail; + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | + DP_UP_REQ_EN | + DP_UPSTREAM_IS_SRC); + if (ret < 0) { + drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n"); + goto out_fail; + } + + /* Some hubs forget their guids after they resume */ + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); + if (ret != 16) { + drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); + goto out_fail; + } + + if (memchr_inv(guid, 0, 16) == NULL) { + tmp64 = get_jiffies_64(); + memcpy(&guid[0], &tmp64, sizeof(u64)); + memcpy(&guid[8], &tmp64, sizeof(u64)); + + ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16); + + if (ret != 16) { + drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n"); + goto out_fail; + } + } + + memcpy(mgr->mst_primary->guid, guid, 16); + +out_fail: + mutex_unlock(&mgr->lock); +} + static void s3_handle_mst(struct drm_device *dev, bool suspend) { struct amdgpu_dm_connector *aconnector; struct drm_connector *connector; struct drm_connector_list_iter iter; struct drm_dp_mst_topology_mgr *mgr; - int ret; - bool need_hotplug = false; drm_connector_list_iter_begin(dev, &iter); drm_for_each_connector_iter(connector, &iter) { @@ -2364,18 +2417,15 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend) if (!dp_is_lttpr_present(aconnector->dc_link)) try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD); - ret = drm_dp_mst_topology_mgr_resume(mgr, true); - if (ret < 0) { - dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, - aconnector->dc_link); - need_hotplug = true; - } + /* TODO: move resume_mst_branch_status() into drm mst resume again + * once topology probing work is pulled out from mst resume into mst + * resume 2nd step. mst resume 2nd step should be called after old + * state getting restored (i.e. drm_atomic_helper_resume()). + */ + resume_mst_branch_status(mgr); } } drm_connector_list_iter_end(&iter); - - if (need_hotplug) - drm_kms_helper_hotplug_event(dev); } static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev) @@ -2769,7 +2819,8 @@ static int dm_resume(void *handle) struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state); enum dc_connection_type new_connection_type = dc_connection_none; struct dc_state *dc_state; - int i, r, j; + int i, r, j, ret; + bool need_hotplug = false; if (amdgpu_in_reset(adev)) { dc_state = dm->cached_dc_state; @@ -2867,7 +2918,7 @@ static int dm_resume(void *handle) continue; /* - * this is the case when traversing through already created + * this is the case when traversing through already created end sink * MST connectors, should be skipped */ if (aconnector && aconnector->mst_root) @@ -2927,6 +2978,27 @@ static int dm_resume(void *handle) dm->cached_state = NULL; + /* Do mst topology probing after resuming cached state*/ + drm_connector_list_iter_begin(ddev, &iter); + drm_for_each_connector_iter(connector, &iter) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type != dc_connection_mst_branch || + aconnector->mst_root) + continue; + + ret = drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr, true); + + if (ret < 0) { + dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, + aconnector->dc_link); + need_hotplug = true; + } + } + drm_connector_list_iter_end(&iter); + + if (need_hotplug) + drm_kms_helper_hotplug_event(ddev); + amdgpu_dm_irq_resume_late(adev); amdgpu_dm_smu_write_watermarks_table(adev); @@ -8073,7 +8145,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bundle->surface_updates[planes_count].plane_info = &bundle->plane_infos[planes_count]; - if (acrtc_state->stream->link->psr_settings.psr_feature_enabled) { + if (acrtc_state->stream->link->psr_settings.psr_feature_enabled || + acrtc_state->stream->link->replay_settings.replay_feature_enabled) { fill_dc_dirty_rects(plane, old_plane_state, new_plane_state, new_crtc_state, &bundle->flip_addrs[planes_count], diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index a2d34be82613..9e4cc5eeda76 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -620,7 +620,7 @@ struct amdgpu_hdmi_vsdb_info { unsigned int max_refresh_rate_hz; /** - * @replay mode: Replay supported + * @replay_mode: Replay supported */ bool replay_mode; }; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index 4b230933b28e..baf7e5254fb3 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -219,7 +219,7 @@ static void dm_helpers_construct_old_payload( /* Set correct time_slots/PBN of old payload. * other fields (delete & dsc_enabled) in * struct drm_dp_mst_atomic_payload are don't care fields - * while calling drm_dp_remove_payload() + * while calling drm_dp_remove_payload_part2() */ for (i = 0; i < current_link_table.stream_count; i++) { dc_alloc = @@ -263,13 +263,12 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( mst_mgr = &aconnector->mst_root->mst_mgr; mst_state = to_drm_dp_mst_topology_state(mst_mgr->base.state); - - /* It's OK for this to fail */ new_payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->mst_output_port); if (enable) { target_payload = new_payload; + /* It's OK for this to fail */ drm_dp_add_payload_part1(mst_mgr, mst_state, new_payload); } else { /* construct old payload by VCPI*/ @@ -277,7 +276,7 @@ bool dm_helpers_dp_mst_write_payload_allocation_table( new_payload, &old_payload); target_payload = &old_payload; - drm_dp_remove_payload(mst_mgr, mst_state, &old_payload, new_payload); + drm_dp_remove_payload_part1(mst_mgr, mst_state, new_payload); } /* mst_mgr->->payloads are VC payload notify MST branch using DPCD or @@ -344,7 +343,7 @@ bool dm_helpers_dp_mst_send_payload_allocation( struct amdgpu_dm_connector *aconnector; struct drm_dp_mst_topology_state *mst_state; struct drm_dp_mst_topology_mgr *mst_mgr; - struct drm_dp_mst_atomic_payload *payload; + struct drm_dp_mst_atomic_payload *new_payload, old_payload; enum mst_progress_status set_flag = MST_ALLOCATE_NEW_PAYLOAD; enum mst_progress_status clr_flag = MST_CLEAR_ALLOCATED_PAYLOAD; int ret = 0; @@ -357,15 +356,20 @@ bool dm_helpers_dp_mst_send_payload_allocation( mst_mgr = &aconnector->mst_root->mst_mgr; mst_state = to_drm_dp_mst_topology_state(mst_mgr->base.state); - payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->mst_output_port); + new_payload = drm_atomic_get_mst_payload_state(mst_state, aconnector->mst_output_port); if (!enable) { set_flag = MST_CLEAR_ALLOCATED_PAYLOAD; clr_flag = MST_ALLOCATE_NEW_PAYLOAD; } - if (enable) - ret = drm_dp_add_payload_part2(mst_mgr, mst_state->base.state, payload); + if (enable) { + ret = drm_dp_add_payload_part2(mst_mgr, mst_state->base.state, new_payload); + } else { + dm_helpers_construct_old_payload(stream->link, mst_state->pbn_div, + new_payload, &old_payload); + drm_dp_remove_payload_part2(mst_mgr, mst_state, &old_payload, new_payload); + } if (ret) { amdgpu_dm_set_mst_status(&aconnector->mst_status, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c index 30c0644d4418..be5a6d008b29 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c @@ -169,11 +169,23 @@ static void add_link_enc_assignment( /* Return first available DIG link encoder. */ static enum engine_id find_first_avail_link_enc( const struct dc_context *ctx, - const struct dc_state *state) + const struct dc_state *state, + enum engine_id eng_id_requested) { enum engine_id eng_id = ENGINE_ID_UNKNOWN; int i; + if (eng_id_requested != ENGINE_ID_UNKNOWN) { + + for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { + eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i]; + if (eng_id == eng_id_requested) + return eng_id; + } + } + + eng_id = ENGINE_ID_UNKNOWN; + for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i]; if (eng_id != ENGINE_ID_UNKNOWN) @@ -287,7 +299,7 @@ void link_enc_cfg_link_encs_assign( struct dc_stream_state *streams[], uint8_t stream_count) { - enum engine_id eng_id = ENGINE_ID_UNKNOWN; + enum engine_id eng_id = ENGINE_ID_UNKNOWN, eng_id_req = ENGINE_ID_UNKNOWN; int i; int j; @@ -377,8 +389,14 @@ void link_enc_cfg_link_encs_assign( * assigned to that endpoint. */ link_enc = get_link_enc_used_by_link(state, stream->link); - if (link_enc == NULL) - eng_id = find_first_avail_link_enc(stream->ctx, state); + if (link_enc == NULL) { + + if (stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + stream->link->dpia_preferred_eng_id != ENGINE_ID_UNKNOWN) + eng_id_req = stream->link->dpia_preferred_eng_id; + + eng_id = find_first_avail_link_enc(stream->ctx, state, eng_id_req); + } else eng_id = link_enc->preferred_engine; @@ -402,7 +420,9 @@ void link_enc_cfg_link_encs_assign( DC_LOG_DEBUG("%s: CUR %s(%d) - enc_id(%d)\n", __func__, assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA", - assignment.ep_id.link_id.enum_id - 1, + assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? + assignment.ep_id.link_id.enum_id : + assignment.ep_id.link_id.enum_id - 1, assignment.eng_id); } for (i = 0; i < MAX_PIPES; i++) { @@ -413,7 +433,9 @@ void link_enc_cfg_link_encs_assign( DC_LOG_DEBUG("%s: NEW %s(%d) - enc_id(%d)\n", __func__, assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA", - assignment.ep_id.link_id.enum_id - 1, + assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? + assignment.ep_id.link_id.enum_id : + assignment.ep_id.link_id.enum_id - 1, assignment.eng_id); } @@ -478,7 +500,6 @@ struct dc_link *link_enc_cfg_get_link_using_link_enc( if (stream) link = stream->link; - // dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id); return link; } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 0d0bef8eb331..31e3183497a7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1496,6 +1496,7 @@ struct dc_link { * object creation. */ enum engine_id eng_id; + enum engine_id dpia_preferred_eng_id; bool test_pattern_enabled; enum dp_test_pattern current_test_pattern; diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index ad967b58d7be..478281f2a5ba 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -964,7 +964,9 @@ void dce110_edp_backlight_control( return; } - if (link->panel_cntl) { + if (link->panel_cntl && !(link->dpcd_sink_ext_caps.bits.oled || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) { bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl); if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) { diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c index 1c1fb2fa0822..004beed9bd44 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c @@ -1032,6 +1032,28 @@ static const struct dce_i2c_mask i2c_masks = { I2C_COMMON_MASK_SH_LIST_DCN30(_MASK) }; +/* ========================================================== */ + +/* + * DPIA index | Preferred Encoder | Host Router + * 0 | C | 0 + * 1 | First Available | 0 + * 2 | D | 1 + * 3 | First Available | 1 + */ +/* ========================================================== */ +static const enum engine_id dpia_to_preferred_enc_id_table[] = { + ENGINE_ID_DIGC, + ENGINE_ID_DIGC, + ENGINE_ID_DIGD, + ENGINE_ID_DIGD +}; + +static enum engine_id dcn314_get_preferred_eng_id_dpia(unsigned int dpia_index) +{ + return dpia_to_preferred_enc_id_table[dpia_index]; +} + static struct dce_i2c_hw *dcn31_i2c_hw_create( struct dc_context *ctx, uint32_t inst) @@ -1785,6 +1807,7 @@ static struct resource_funcs dcn314_res_pool_funcs = { .update_bw_bounding_box = dcn314_update_bw_bounding_box, .patch_unknown_plane_state = dcn20_patch_unknown_plane_state, .get_panel_config_defaults = dcn314_get_panel_config_defaults, + .get_preferred_eng_id_dpia = dcn314_get_preferred_eng_id_dpia, }; static struct clock_source *dcn30_clock_source_create( diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index 027aec70c070..eaad1260bfd1 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -65,6 +65,7 @@ struct resource_context; struct clk_bw_params; struct resource_funcs { + enum engine_id (*get_preferred_eng_id_dpia)(unsigned int dpia_index); void (*destroy)(struct resource_pool **pool); void (*link_init)(struct dc_link *link); struct panel_cntl*(*panel_cntl_create)( diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index 195ca9e52eda..0895742a3102 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -791,6 +791,10 @@ static bool construct_dpia(struct dc_link *link, /* Set dpia port index : 0 to number of dpia ports */ link->ddc_hw_inst = init_params->connector_index; + // Assign Dpia preferred eng_id + if (link->dc->res_pool->funcs->get_preferred_eng_id_dpia) + link->dpia_preferred_eng_id = link->dc->res_pool->funcs->get_preferred_eng_id_dpia(link->ddc_hw_inst); + /* TODO: Create link encoder */ link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 8433f99f6667..3b5a56585c4b 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -31,12 +31,12 @@ #include <linux/types.h> #include <linux/bitmap.h> #include <linux/dma-fence.h> +#include "amdgpu_irq.h" +#include "amdgpu_gfx.h" struct pci_dev; struct amdgpu_device; -#define KGD_MAX_QUEUES 128 - struct kfd_dev; struct kgd_mem; @@ -68,7 +68,7 @@ struct kfd_cu_info { uint32_t wave_front_size; uint32_t max_scratch_slots_per_cu; uint32_t lds_size; - uint32_t cu_bitmap[4][4]; + uint32_t cu_bitmap[AMDGPU_MAX_GC_INSTANCES][4][4]; }; /* For getting GPU local memory information from KGD */ @@ -326,8 +326,7 @@ struct kfd2kgd_calls { uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); void (*get_cu_occupancy)(struct amdgpu_device *adev, int pasid, int *wave_cnt, int *max_waves_per_cu, uint32_t inst); void (*program_trap_handler_settings)(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 199a673b8120..de80e191a92c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -336,7 +336,7 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) /* Store one-time values in driver PPTable */ if (!pptable->Init) { - while (retry--) { + while (--retry) { ret = smu_v13_0_6_get_metrics_table(smu, NULL, true); if (ret) return ret; diff --git a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c index 6c56f5662bc7..80973975bfdb 100644 --- a/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c +++ b/drivers/gpu/drm/arm/display/komeda/d71/d71_dev.c @@ -521,7 +521,7 @@ static struct komeda_format_caps d71_format_caps_table[] = { {__HW_ID(5, 1), DRM_FORMAT_YUYV, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ {__HW_ID(5, 2), DRM_FORMAT_YUYV, RICH, Flip_H_V, 0, 0}, {__HW_ID(5, 3), DRM_FORMAT_UYVY, RICH, Flip_H_V, 0, 0}, - {__HW_ID(5, 6), DRM_FORMAT_NV12, RICH, Flip_H_V, 0, 0}, + {__HW_ID(5, 6), DRM_FORMAT_NV12, RICH_WB, Flip_H_V, 0, 0}, {__HW_ID(5, 6), DRM_FORMAT_YUV420_8BIT, RICH, Rot_ALL_H_V, LYT_NM, AFB_TH}, /* afbc */ {__HW_ID(5, 7), DRM_FORMAT_YUV420, RICH, Flip_H_V, 0, 0}, /* YUV 10bit*/ diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c index cb2a2be24c5f..cc57ea4e13ae 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_drv.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_drv.c @@ -45,6 +45,14 @@ static void komeda_platform_remove(struct platform_device *pdev) devm_kfree(dev, mdrv); } +static void komeda_platform_shutdown(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct komeda_drv *mdrv = dev_get_drvdata(dev); + + komeda_kms_shutdown(mdrv->kms); +} + static int komeda_platform_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -142,6 +150,7 @@ static const struct dev_pm_ops komeda_pm_ops = { static struct platform_driver komeda_platform_driver = { .probe = komeda_platform_probe, .remove_new = komeda_platform_remove, + .shutdown = komeda_platform_shutdown, .driver = { .name = "komeda", .of_match_table = komeda_of_match, diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c index 9299026701f3..fe46b0ebefea 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.c @@ -340,3 +340,10 @@ void komeda_kms_detach(struct komeda_kms_dev *kms) komeda_kms_cleanup_private_objs(kms); drm->dev_private = NULL; } + +void komeda_kms_shutdown(struct komeda_kms_dev *kms) +{ + struct drm_device *drm = &kms->base; + + drm_atomic_helper_shutdown(drm); +} diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h index 6ef655326357..a4048724564d 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_kms.h +++ b/drivers/gpu/drm/arm/display/komeda/komeda_kms.h @@ -190,5 +190,6 @@ void komeda_crtc_flush_and_wait_for_flip_done(struct komeda_crtc *kcrtc, struct komeda_kms_dev *komeda_kms_attach(struct komeda_dev *mdev); void komeda_kms_detach(struct komeda_kms_dev *kms); +void komeda_kms_shutdown(struct komeda_kms_dev *kms); #endif /*_KOMEDA_KMS_H_*/ diff --git a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c index 4618687a8f4d..f3e744172673 100644 --- a/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c +++ b/drivers/gpu/drm/arm/display/komeda/komeda_pipeline_state.c @@ -1223,7 +1223,7 @@ int komeda_build_display_data_flow(struct komeda_crtc *kcrtc, return 0; } -static void +static int komeda_pipeline_unbound_components(struct komeda_pipeline *pipe, struct komeda_pipeline_state *new) { @@ -1243,8 +1243,12 @@ komeda_pipeline_unbound_components(struct komeda_pipeline *pipe, c = komeda_pipeline_get_component(pipe, id); c_st = komeda_component_get_state_and_set_user(c, drm_st, NULL, new->crtc); + if (PTR_ERR(c_st) == -EDEADLK) + return -EDEADLK; WARN_ON(IS_ERR(c_st)); } + + return 0; } /* release unclaimed pipeline resource */ @@ -1266,9 +1270,8 @@ int komeda_release_unclaimed_resources(struct komeda_pipeline *pipe, if (WARN_ON(IS_ERR_OR_NULL(st))) return -EINVAL; - komeda_pipeline_unbound_components(pipe, st); + return komeda_pipeline_unbound_components(pipe, st); - return 0; } /* Since standalone disabled components must be disabled separately and in the diff --git a/drivers/gpu/drm/arm/hdlcd_drv.c b/drivers/gpu/drm/arm/hdlcd_drv.c index aa06f9838015..32be9e370049 100644 --- a/drivers/gpu/drm/arm/hdlcd_drv.c +++ b/drivers/gpu/drm/arm/hdlcd_drv.c @@ -372,6 +372,11 @@ static void hdlcd_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &hdlcd_master_ops); } +static void hdlcd_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id hdlcd_of_match[] = { { .compatible = "arm,hdlcd" }, {}, @@ -399,6 +404,7 @@ static SIMPLE_DEV_PM_OPS(hdlcd_pm_ops, hdlcd_pm_suspend, hdlcd_pm_resume); static struct platform_driver hdlcd_platform_driver = { .probe = hdlcd_probe, .remove_new = hdlcd_remove, + .shutdown = hdlcd_shutdown, .driver = { .name = "hdlcd", .pm = &hdlcd_pm_ops, diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 62329d5dd992..6682131d2910 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -941,6 +941,11 @@ static void malidp_platform_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &malidp_master_ops); } +static void malidp_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static int __maybe_unused malidp_pm_suspend(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); @@ -982,6 +987,7 @@ static const struct dev_pm_ops malidp_pm_ops = { static struct platform_driver malidp_platform_driver = { .probe = malidp_platform_probe, .remove_new = malidp_platform_remove, + .shutdown = malidp_platform_shutdown, .driver = { .name = "mali-dp", .pm = &malidp_pm_ops, diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index e8d2fe955909..fa1c67598706 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -148,6 +148,7 @@ static int armada_drm_bind(struct device *dev) err_kms: drm_mode_config_cleanup(&priv->drm); drm_mm_takedown(&priv->linear); + dev_set_drvdata(dev, NULL); return ret; } @@ -166,6 +167,7 @@ static void armada_drm_unbind(struct device *dev) drm_mode_config_cleanup(&priv->drm); drm_mm_takedown(&priv->linear); + dev_set_drvdata(dev, NULL); } static void armada_add_endpoints(struct device *dev, @@ -230,6 +232,11 @@ static int armada_drm_remove(struct platform_device *pdev) return 0; } +static void armada_drm_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct platform_device_id armada_drm_platform_ids[] = { { .name = "armada-drm", @@ -243,6 +250,7 @@ MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids); static struct platform_driver armada_drm_platform_driver = { .probe = armada_drm_probe, .remove = armada_drm_remove, + .shutdown = armada_drm_shutdown, .driver = { .name = "armada-drm", }, diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c index d207b03f8357..78122b35a0cb 100644 --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c @@ -358,11 +358,18 @@ static void aspeed_gfx_remove(struct platform_device *pdev) sysfs_remove_group(&pdev->dev.kobj, &aspeed_sysfs_attr_group); drm_dev_unregister(drm); aspeed_gfx_unload(drm); + drm_atomic_helper_shutdown(drm); +} + +static void aspeed_gfx_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); } static struct platform_driver aspeed_gfx_platform_driver = { .probe = aspeed_gfx_probe, .remove_new = aspeed_gfx_remove, + .shutdown = aspeed_gfx_shutdown, .driver = { .name = "aspeed_gfx", .of_match_table = aspeed_gfx_match, diff --git a/drivers/gpu/drm/ast/ast_drv.c b/drivers/gpu/drm/ast/ast_drv.c index e1224ef4ad83..cf5b754f044c 100644 --- a/drivers/gpu/drm/ast/ast_drv.c +++ b/drivers/gpu/drm/ast/ast_drv.c @@ -125,6 +125,11 @@ static void ast_pci_remove(struct pci_dev *pdev) drm_atomic_helper_shutdown(dev); } +static void ast_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + static int ast_drm_freeze(struct drm_device *dev) { int error; @@ -209,6 +214,7 @@ static struct pci_driver ast_pci_driver = { .id_table = ast_pciidlist, .probe = ast_pci_probe, .remove = ast_pci_remove, + .shutdown = ast_pci_shutdown, .driver.pm = &ast_pm_ops, }; diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index fa0f9a93d50d..84c54e8622d1 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -782,6 +782,11 @@ static void atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) drm_dev_put(ddev); } +static void atmel_hlcdc_dc_drm_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static int atmel_hlcdc_dc_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); @@ -825,6 +830,7 @@ static const struct of_device_id atmel_hlcdc_dc_of_match[] = { static struct platform_driver atmel_hlcdc_dc_platform_driver = { .probe = atmel_hlcdc_dc_drm_probe, .remove_new = atmel_hlcdc_dc_drm_remove, + .shutdown = atmel_hlcdc_dc_drm_shutdown, .driver = { .name = "atmel-hlcdc-display-controller", .pm = pm_sleep_ptr(&atmel_hlcdc_dc_drm_pm_ops), diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 44a660a4bdbf..ba82a1142adf 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -181,6 +181,7 @@ config DRM_NWL_MIPI_DSI select DRM_KMS_HELPER select DRM_MIPI_DSI select DRM_PANEL_BRIDGE + select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY select MFD_SYSCON select MULTIPLEXER @@ -227,6 +228,7 @@ config DRM_SAMSUNG_DSIM select DRM_KMS_HELPER select DRM_MIPI_DSI select DRM_PANEL_BRIDGE + select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY help The Samsung MIPI DSIM bridge controller driver. diff --git a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c index 800555aef97f..5748a8581af4 100644 --- a/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c +++ b/drivers/gpu/drm/bridge/analogix/analogix-anx78xx.c @@ -1211,6 +1211,7 @@ static const u16 anx78xx_chipid_list[] = { 0x7808, 0x7812, 0x7814, + 0x7816, 0x7818, }; @@ -1231,9 +1232,7 @@ static int anx78xx_i2c_probe(struct i2c_client *client) mutex_init(&anx78xx->lock); -#if IS_ENABLED(CONFIG_OF) anx78xx->bridge.of_node = client->dev.of_node; -#endif anx78xx->client = client; i2c_set_clientdata(client, anx78xx); @@ -1367,16 +1366,11 @@ static void anx78xx_i2c_remove(struct i2c_client *client) kfree(anx78xx->edid); } -static const struct i2c_device_id anx78xx_id[] = { - { "anx7814", 0 }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(i2c, anx78xx_id); - static const struct of_device_id anx78xx_match_table[] = { { .compatible = "analogix,anx7808", .data = anx7808_i2c_addresses }, { .compatible = "analogix,anx7812", .data = anx781x_i2c_addresses }, { .compatible = "analogix,anx7814", .data = anx781x_i2c_addresses }, + { .compatible = "analogix,anx7816", .data = anx781x_i2c_addresses }, { .compatible = "analogix,anx7818", .data = anx781x_i2c_addresses }, { /* sentinel */ }, }; @@ -1389,7 +1383,6 @@ static struct i2c_driver anx78xx_driver = { }, .probe = anx78xx_i2c_probe, .remove = anx78xx_i2c_remove, - .id_table = anx78xx_id, }; module_i2c_driver(anx78xx_driver); diff --git a/drivers/gpu/drm/bridge/analogix/anx7625.c b/drivers/gpu/drm/bridge/analogix/anx7625.c index 51abe42c639e..8f740154707d 100644 --- a/drivers/gpu/drm/bridge/analogix/anx7625.c +++ b/drivers/gpu/drm/bridge/analogix/anx7625.c @@ -1464,6 +1464,9 @@ static int _anx7625_hpd_polling(struct anx7625_data *ctx, if (ctx->pdata.intp_irq) return 0; + /* Delay 200ms for FW HPD de-bounce */ + msleep(200); + ret = readx_poll_timeout(anx7625_read_hpd_status_p0, ctx, val, ((val & HPD_STATUS) || (val < 0)), diff --git a/drivers/gpu/drm/bridge/cadence/Kconfig b/drivers/gpu/drm/bridge/cadence/Kconfig index ec35215a2003..cced81633ddc 100644 --- a/drivers/gpu/drm/bridge/cadence/Kconfig +++ b/drivers/gpu/drm/bridge/cadence/Kconfig @@ -4,6 +4,7 @@ config DRM_CDNS_DSI select DRM_KMS_HELPER select DRM_MIPI_DSI select DRM_PANEL_BRIDGE + select GENERIC_PHY select GENERIC_PHY_MIPI_DPHY depends on OF help diff --git a/drivers/gpu/drm/bridge/ite-it66121.c b/drivers/gpu/drm/bridge/ite-it66121.c index 466641c77fe9..1cf3fb1f13dc 100644 --- a/drivers/gpu/drm/bridge/ite-it66121.c +++ b/drivers/gpu/drm/bridge/ite-it66121.c @@ -884,14 +884,14 @@ static struct edid *it66121_bridge_get_edid(struct drm_bridge *bridge, mutex_lock(&ctx->lock); ret = it66121_preamble_ddc(ctx); if (ret) { - edid = ERR_PTR(ret); + edid = NULL; goto out_unlock; } ret = regmap_write(ctx->regmap, IT66121_DDC_HEADER_REG, IT66121_DDC_HEADER_EDID); if (ret) { - edid = ERR_PTR(ret); + edid = NULL; goto out_unlock; } @@ -1447,10 +1447,14 @@ static int it66121_audio_get_eld(struct device *dev, void *data, struct it66121_ctx *ctx = dev_get_drvdata(dev); mutex_lock(&ctx->lock); - - memcpy(buf, ctx->connector->eld, - min(sizeof(ctx->connector->eld), len)); - + if (!ctx->connector) { + /* Pass en empty ELD if connector not available */ + dev_dbg(dev, "No connector present, passing empty EDID data"); + memset(buf, 0, len); + } else { + memcpy(buf, ctx->connector->eld, + min(sizeof(ctx->connector->eld), len)); + } mutex_unlock(&ctx->lock); return 0; @@ -1501,7 +1505,6 @@ static const char * const it66121_supplies[] = { static int it66121_probe(struct i2c_client *client) { - const struct i2c_device_id *id = i2c_client_get_device_id(client); u32 revision_id, vendor_ids[2] = { 0 }, device_ids[2] = { 0 }; struct device_node *ep; int ret; @@ -1523,7 +1526,7 @@ static int it66121_probe(struct i2c_client *client) ctx->dev = dev; ctx->client = client; - ctx->info = (const struct it66121_chip_info *) id->driver_data; + ctx->info = i2c_get_match_data(client); of_property_read_u32(ep, "bus-width", &ctx->bus_width); of_node_put(ep); @@ -1609,13 +1612,6 @@ static void it66121_remove(struct i2c_client *client) mutex_destroy(&ctx->lock); } -static const struct of_device_id it66121_dt_match[] = { - { .compatible = "ite,it66121" }, - { .compatible = "ite,it6610" }, - { } -}; -MODULE_DEVICE_TABLE(of, it66121_dt_match); - static const struct it66121_chip_info it66121_chip_info = { .id = ID_IT66121, .vid = 0x4954, @@ -1628,6 +1624,13 @@ static const struct it66121_chip_info it6610_chip_info = { .pid = 0x0611, }; +static const struct of_device_id it66121_dt_match[] = { + { .compatible = "ite,it66121", &it66121_chip_info }, + { .compatible = "ite,it6610", &it6610_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, it66121_dt_match); + static const struct i2c_device_id it66121_id[] = { { "it66121", (kernel_ulong_t) &it66121_chip_info }, { "it6610", (kernel_ulong_t) &it6610_chip_info }, diff --git a/drivers/gpu/drm/bridge/lontium-lt8912b.c b/drivers/gpu/drm/bridge/lontium-lt8912b.c index 4eaea67fb71c..03532efb893b 100644 --- a/drivers/gpu/drm/bridge/lontium-lt8912b.c +++ b/drivers/gpu/drm/bridge/lontium-lt8912b.c @@ -45,7 +45,6 @@ struct lt8912 { u8 data_lanes; bool is_power_on; - bool is_attached; }; static int lt8912_write_init_config(struct lt8912 *lt) @@ -559,6 +558,13 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge, struct lt8912 *lt = bridge_to_lt8912(bridge); int ret; + ret = drm_bridge_attach(bridge->encoder, lt->hdmi_port, bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); + if (ret < 0) { + dev_err(lt->dev, "Failed to attach next bridge (%d)\n", ret); + return ret; + } + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) { ret = lt8912_bridge_connector_init(bridge); if (ret) { @@ -575,8 +581,6 @@ static int lt8912_bridge_attach(struct drm_bridge *bridge, if (ret) goto error; - lt->is_attached = true; - return 0; error: @@ -588,15 +592,10 @@ static void lt8912_bridge_detach(struct drm_bridge *bridge) { struct lt8912 *lt = bridge_to_lt8912(bridge); - if (lt->is_attached) { - lt8912_hard_power_off(lt); - - if (lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) - drm_bridge_hpd_disable(lt->hdmi_port); + lt8912_hard_power_off(lt); - drm_connector_unregister(<->connector); - drm_connector_cleanup(<->connector); - } + if (lt->connector.dev && lt->hdmi_port->ops & DRM_BRIDGE_OP_HPD) + drm_bridge_hpd_disable(lt->hdmi_port); } static enum drm_connector_status @@ -750,7 +749,6 @@ static void lt8912_remove(struct i2c_client *client) { struct lt8912 *lt = i2c_get_clientdata(client); - lt8912_bridge_detach(<->bridge); drm_bridge_remove(<->bridge); lt8912_free_i2c(lt); lt8912_put_dt(lt); diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c index 8c5668dca0c4..991732c4b629 100644 --- a/drivers/gpu/drm/bridge/lvds-codec.c +++ b/drivers/gpu/drm/bridge/lvds-codec.c @@ -5,6 +5,7 @@ */ #include <linux/gpio/consumer.h> +#include <linux/media-bus-format.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_graph.h> @@ -71,12 +72,6 @@ static void lvds_codec_disable(struct drm_bridge *bridge) "Failed to disable regulator \"vcc\": %d\n", ret); } -static const struct drm_bridge_funcs funcs = { - .attach = lvds_codec_attach, - .enable = lvds_codec_enable, - .disable = lvds_codec_disable, -}; - #define MAX_INPUT_SEL_FORMATS 1 static u32 * lvds_codec_atomic_get_input_bus_fmts(struct drm_bridge *bridge, @@ -102,7 +97,7 @@ lvds_codec_atomic_get_input_bus_fmts(struct drm_bridge *bridge, return input_fmts; } -static const struct drm_bridge_funcs funcs_decoder = { +static const struct drm_bridge_funcs funcs = { .attach = lvds_codec_attach, .enable = lvds_codec_enable, .disable = lvds_codec_disable, @@ -184,8 +179,9 @@ static int lvds_codec_probe(struct platform_device *pdev) return ret; } else { lvds_codec->bus_format = ret; - lvds_codec->bridge.funcs = &funcs_decoder; } + } else { + lvds_codec->bus_format = MEDIA_BUS_FMT_RGB888_1X24; } /* diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c index 9316384b4474..e48823a4f1ed 100644 --- a/drivers/gpu/drm/bridge/panel.c +++ b/drivers/gpu/drm/bridge/panel.c @@ -4,6 +4,8 @@ * Copyright (C) 2017 Broadcom */ +#include <linux/device.h> + #include <drm/drm_atomic_helper.h> #include <drm/drm_bridge.h> #include <drm/drm_connector.h> @@ -19,6 +21,7 @@ struct panel_bridge { struct drm_bridge bridge; struct drm_connector connector; struct drm_panel *panel; + struct device_link *link; u32 connector_type; }; @@ -60,13 +63,24 @@ static int panel_bridge_attach(struct drm_bridge *bridge, { struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); struct drm_connector *connector = &panel_bridge->connector; + struct drm_panel *panel = panel_bridge->panel; + struct drm_device *drm_dev = bridge->dev; int ret; + panel_bridge->link = device_link_add(drm_dev->dev, panel->dev, + DL_FLAG_STATELESS); + if (!panel_bridge->link) { + DRM_ERROR("Failed to add device link between %s and %s\n", + dev_name(drm_dev->dev), dev_name(panel->dev)); + return -EINVAL; + } + if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR) return 0; if (!bridge->encoder) { DRM_ERROR("Missing encoder\n"); + device_link_del(panel_bridge->link); return -ENODEV; } @@ -78,6 +92,7 @@ static int panel_bridge_attach(struct drm_bridge *bridge, panel_bridge->connector_type); if (ret) { DRM_ERROR("Failed to initialize connector\n"); + device_link_del(panel_bridge->link); return ret; } @@ -100,6 +115,8 @@ static void panel_bridge_detach(struct drm_bridge *bridge) struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge); struct drm_connector *connector = &panel_bridge->connector; + device_link_del(panel_bridge->link); + /* * Cleanup the connector if we know it was initialized. * @@ -302,9 +319,7 @@ struct drm_bridge *drm_panel_bridge_add_typed(struct drm_panel *panel, panel_bridge->panel = panel; panel_bridge->bridge.funcs = &panel_bridge_bridge_funcs; -#ifdef CONFIG_OF panel_bridge->bridge.of_node = panel->dev->of_node; -#endif panel_bridge->bridge.ops = DRM_BRIDGE_OP_MODES; panel_bridge->bridge.type = connector_type; diff --git a/drivers/gpu/drm/bridge/samsung-dsim.c b/drivers/gpu/drm/bridge/samsung-dsim.c index cf777bdb25d2..19bdb32dbc9a 100644 --- a/drivers/gpu/drm/bridge/samsung-dsim.c +++ b/drivers/gpu/drm/bridge/samsung-dsim.c @@ -385,7 +385,7 @@ static const unsigned int imx8mm_dsim_reg_values[] = { [RESET_TYPE] = DSIM_SWRST, [PLL_TIMER] = 500, [STOP_STATE_CNT] = 0xf, - [PHYCTRL_ULPS_EXIT] = 0, + [PHYCTRL_ULPS_EXIT] = DSIM_PHYCTRL_ULPS_EXIT(0xaf), [PHYCTRL_VREG_LP] = 0, [PHYCTRL_SLEW_UP] = 0, [PHYTIMING_LPX] = DSIM_PHYTIMING_LPX(0x06), @@ -413,6 +413,7 @@ static const struct samsung_dsim_driver_data exynos3_dsi_driver_data = { .m_min = 41, .m_max = 125, .min_freq = 500, + .has_broken_fifoctrl_emptyhdr = 1, }; static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = { @@ -429,6 +430,7 @@ static const struct samsung_dsim_driver_data exynos4_dsi_driver_data = { .m_min = 41, .m_max = 125, .min_freq = 500, + .has_broken_fifoctrl_emptyhdr = 1, }; static const struct samsung_dsim_driver_data exynos5_dsi_driver_data = { @@ -1010,8 +1012,20 @@ static int samsung_dsim_wait_for_hdr_fifo(struct samsung_dsim *dsi) do { u32 reg = samsung_dsim_read(dsi, DSIM_FIFOCTRL_REG); - if (reg & DSIM_SFR_HEADER_EMPTY) - return 0; + if (!dsi->driver_data->has_broken_fifoctrl_emptyhdr) { + if (reg & DSIM_SFR_HEADER_EMPTY) + return 0; + } else { + if (!(reg & DSIM_SFR_HEADER_FULL)) { + /* + * Wait a little bit, so the pending data can + * actually leave the FIFO to avoid overflow. + */ + if (!cond_resched()) + usleep_range(950, 1050); + return 0; + } + } if (!cond_resched()) usleep_range(950, 1050); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c index be21c11de1f2..673661160e54 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c @@ -145,6 +145,10 @@ static irqreturn_t dw_hdmi_cec_hardirq(int irq, void *data) cec->tx_status = CEC_TX_STATUS_NACK; cec->tx_done = true; ret = IRQ_WAKE_THREAD; + } else if (stat & CEC_STAT_ARBLOST) { + cec->tx_status = CEC_TX_STATUS_ARB_LOST; + cec->tx_done = true; + ret = IRQ_WAKE_THREAD; } if (stat & CEC_STAT_EOM) { @@ -209,7 +213,7 @@ static int dw_hdmi_cec_enable(struct cec_adapter *adap, bool enable) cec->ops->enable(cec->hdmi); irqs = CEC_STAT_ERROR_INIT | CEC_STAT_NACK | CEC_STAT_EOM | - CEC_STAT_DONE; + CEC_STAT_ARBLOST | CEC_STAT_DONE; dw_hdmi_write(cec, irqs, HDMI_CEC_POLARITY); dw_hdmi_write(cec, ~irqs, HDMI_CEC_MASK); dw_hdmi_write(cec, ~irqs, HDMI_IH_MUTE_CEC_STAT0); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 6c1d79474505..52d91a0df85e 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -3541,9 +3541,7 @@ struct dw_hdmi *dw_hdmi_probe(struct platform_device *pdev, | DRM_BRIDGE_OP_HPD; hdmi->bridge.interlace_allowed = true; hdmi->bridge.ddc = hdmi->ddc; -#ifdef CONFIG_OF hdmi->bridge.of_node = pdev->dev.of_node; -#endif memset(&pdevinfo, 0, sizeof(pdevinfo)); pdevinfo.parent = dev; diff --git a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c index 04d4a1a10698..a8dd2a2e7c7b 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c @@ -1182,9 +1182,7 @@ __dw_mipi_dsi_probe(struct platform_device *pdev, dsi->bridge.driver_private = dsi; dsi->bridge.funcs = &dw_mipi_dsi_bridge_funcs; -#ifdef CONFIG_OF dsi->bridge.of_node = pdev->dev.of_node; -#endif return dsi; } diff --git a/drivers/gpu/drm/bridge/tc358768.c b/drivers/gpu/drm/bridge/tc358768.c index 819a4b6ec2a0..0e8813278a2f 100644 --- a/drivers/gpu/drm/bridge/tc358768.c +++ b/drivers/gpu/drm/bridge/tc358768.c @@ -9,12 +9,14 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/kernel.h> +#include <linux/math64.h> #include <linux/media-bus-format.h> #include <linux/minmax.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> +#include <linux/units.h> #include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> @@ -156,6 +158,7 @@ struct tc358768_priv { u32 frs; /* PLL Freqency range for HSCK (post divider) */ u32 dsiclk; /* pll_clk / 2 */ + u32 pclk; /* incoming pclk rate */ }; static inline struct tc358768_priv *dsi_host_to_tc358768(struct mipi_dsi_host @@ -216,6 +219,10 @@ static void tc358768_update_bits(struct tc358768_priv *priv, u32 reg, u32 mask, u32 tmp, orig; tc358768_read(priv, reg, &orig); + + if (priv->error) + return; + tmp = orig & ~mask; tmp |= val & mask; if (tmp != orig) @@ -312,7 +319,7 @@ static int tc358768_calc_pll(struct tc358768_priv *priv, target_pll = tc358768_pclk_to_pll(priv, mode->clock * 1000); - /* pll_clk = RefClk * [(FBD + 1)/ (PRD + 1)] * [1 / (2^FRS)] */ + /* pll_clk = RefClk * FBD / PRD * (1 / (2^FRS)) */ for (i = 0; i < ARRAY_SIZE(frs_limits); i++) if (target_pll >= frs_limits[i]) @@ -332,19 +339,19 @@ static int tc358768_calc_pll(struct tc358768_priv *priv, best_prd = 0; best_fbd = 0; - for (prd = 0; prd < 16; ++prd) { - u32 divisor = (prd + 1) * (1 << frs); + for (prd = 1; prd <= 16; ++prd) { + u32 divisor = prd * (1 << frs); u32 fbd; - for (fbd = 0; fbd < 512; ++fbd) { + for (fbd = 1; fbd <= 512; ++fbd) { u32 pll, diff, pll_in; - pll = (u32)div_u64((u64)refclk * (fbd + 1), divisor); + pll = (u32)div_u64((u64)refclk * fbd, divisor); if (pll >= max_pll || pll < min_pll) continue; - pll_in = (u32)div_u64((u64)refclk, prd + 1); + pll_in = (u32)div_u64((u64)refclk, prd); if (pll_in < 4000000) continue; @@ -375,6 +382,7 @@ found: priv->prd = best_prd; priv->frs = frs; priv->dsiclk = best_pll / 2; + priv->pclk = mode->clock * 1000; return 0; } @@ -600,14 +608,14 @@ static int tc358768_setup_pll(struct tc358768_priv *priv, dev_dbg(priv->dev, "PLL: refclk %lu, fbd %u, prd %u, frs %u\n", clk_get_rate(priv->refclk), fbd, prd, frs); - dev_dbg(priv->dev, "PLL: pll_clk: %u, DSIClk %u, DSIByteClk %u\n", + dev_dbg(priv->dev, "PLL: pll_clk: %u, DSIClk %u, HSByteClk %u\n", priv->dsiclk * 2, priv->dsiclk, priv->dsiclk / 4); dev_dbg(priv->dev, "PLL: pclk %u (panel: %u)\n", tc358768_pll_to_pclk(priv, priv->dsiclk * 2), mode->clock * 1000); /* PRD[15:12] FBD[8:0] */ - tc358768_write(priv, TC358768_PLLCTL0, (prd << 12) | fbd); + tc358768_write(priv, TC358768_PLLCTL0, ((prd - 1) << 12) | (fbd - 1)); /* FRS[11:10] LBWS[9:8] CKEN[4] RESETB[1] EN[0] */ tc358768_write(priv, TC358768_PLLCTL1, @@ -623,15 +631,36 @@ static int tc358768_setup_pll(struct tc358768_priv *priv, return tc358768_clear_error(priv); } -#define TC358768_PRECISION 1000 -static u32 tc358768_ns_to_cnt(u32 ns, u32 period_nsk) +static u32 tc358768_ns_to_cnt(u32 ns, u32 period_ps) +{ + return DIV_ROUND_UP(ns * 1000, period_ps); +} + +static u32 tc358768_ps_to_ns(u32 ps) { - return (ns * TC358768_PRECISION + period_nsk) / period_nsk; + return ps / 1000; +} + +static u32 tc358768_dpi_to_ns(u32 val, u32 pclk) +{ + return (u32)div_u64((u64)val * NANO, pclk); +} + +/* Convert value in DPI pixel clock units to DSI byte count */ +static u32 tc358768_dpi_to_dsi_bytes(struct tc358768_priv *priv, u32 val) +{ + u64 m = (u64)val * priv->dsiclk / 4 * priv->dsi_lanes; + u64 n = priv->pclk; + + return (u32)div_u64(m + n - 1, n); } -static u32 tc358768_to_ns(u32 nsk) +static u32 tc358768_dsi_bytes_to_ns(struct tc358768_priv *priv, u32 val) { - return (nsk / TC358768_PRECISION); + u64 m = (u64)val * NANO; + u64 n = priv->dsiclk / 4 * priv->dsi_lanes; + + return (u32)div_u64(m, n); } static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) @@ -642,13 +671,23 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) u32 val, val2, lptxcnt, hact, data_type; s32 raw_val; const struct drm_display_mode *mode; - u32 dsibclk_nsk, dsiclk_nsk, ui_nsk; - u32 dsiclk, dsibclk, video_start; - const u32 internal_delay = 40; + u32 hsbyteclk_ps, dsiclk_ps, ui_ps; + u32 dsiclk, hsbyteclk; int ret, i; + struct videomode vm; + struct device *dev = priv->dev; + /* In pixelclock units */ + u32 dpi_htot, dpi_data_start; + /* In byte units */ + u32 dsi_dpi_htot, dsi_dpi_data_start; + u32 dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp; + const u32 dsi_hss = 4; /* HSS is a short packet (4 bytes) */ + /* In hsbyteclk units */ + u32 dsi_vsdly; + const u32 internal_dly = 40; if (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) { - dev_warn_once(priv->dev, "Non-continuous mode unimplemented, falling back to continuous\n"); + dev_warn_once(dev, "Non-continuous mode unimplemented, falling back to continuous\n"); mode_flags &= ~MIPI_DSI_CLOCK_NON_CONTINUOUS; } @@ -656,7 +695,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) ret = tc358768_sw_reset(priv); if (ret) { - dev_err(priv->dev, "Software reset failed: %d\n", ret); + dev_err(dev, "Software reset failed: %d\n", ret); tc358768_hw_disable(priv); return; } @@ -664,53 +703,194 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) mode = &bridge->encoder->crtc->state->adjusted_mode; ret = tc358768_setup_pll(priv, mode); if (ret) { - dev_err(priv->dev, "PLL setup failed: %d\n", ret); + dev_err(dev, "PLL setup failed: %d\n", ret); tc358768_hw_disable(priv); return; } + drm_display_mode_to_videomode(mode, &vm); + dsiclk = priv->dsiclk; - dsibclk = dsiclk / 4; + hsbyteclk = dsiclk / 4; /* Data Format Control Register */ val = BIT(2) | BIT(1) | BIT(0); /* rdswap_en | dsitx_en | txdt_en */ switch (dsi_dev->format) { case MIPI_DSI_FMT_RGB888: val |= (0x3 << 4); - hact = mode->hdisplay * 3; - video_start = (mode->htotal - mode->hsync_start) * 3; + hact = vm.hactive * 3; data_type = MIPI_DSI_PACKED_PIXEL_STREAM_24; break; case MIPI_DSI_FMT_RGB666: val |= (0x4 << 4); - hact = mode->hdisplay * 3; - video_start = (mode->htotal - mode->hsync_start) * 3; + hact = vm.hactive * 3; data_type = MIPI_DSI_PACKED_PIXEL_STREAM_18; break; case MIPI_DSI_FMT_RGB666_PACKED: val |= (0x4 << 4) | BIT(3); - hact = mode->hdisplay * 18 / 8; - video_start = (mode->htotal - mode->hsync_start) * 18 / 8; + hact = vm.hactive * 18 / 8; data_type = MIPI_DSI_PIXEL_STREAM_3BYTE_18; break; case MIPI_DSI_FMT_RGB565: val |= (0x5 << 4); - hact = mode->hdisplay * 2; - video_start = (mode->htotal - mode->hsync_start) * 2; + hact = vm.hactive * 2; data_type = MIPI_DSI_PACKED_PIXEL_STREAM_16; break; default: - dev_err(priv->dev, "Invalid data format (%u)\n", + dev_err(dev, "Invalid data format (%u)\n", dsi_dev->format); tc358768_hw_disable(priv); return; } + /* + * There are three important things to make TC358768 work correctly, + * which are not trivial to manage: + * + * 1. Keep the DPI line-time and the DSI line-time as close to each + * other as possible. + * 2. TC358768 goes to LP mode after each line's active area. The DSI + * HFP period has to be long enough for entering and exiting LP mode. + * But it is not clear how to calculate this. + * 3. VSDly (video start delay) has to be long enough to ensure that the + * DSI TX does not start transmitting until we have started receiving + * pixel data from the DPI input. It is not clear how to calculate + * this either. + */ + + dpi_htot = vm.hactive + vm.hfront_porch + vm.hsync_len + vm.hback_porch; + dpi_data_start = vm.hsync_len + vm.hback_porch; + + dev_dbg(dev, "dpi horiz timing (pclk): %u + %u + %u + %u = %u\n", + vm.hsync_len, vm.hback_porch, vm.hactive, vm.hfront_porch, + dpi_htot); + + dev_dbg(dev, "dpi horiz timing (ns): %u + %u + %u + %u = %u\n", + tc358768_dpi_to_ns(vm.hsync_len, vm.pixelclock), + tc358768_dpi_to_ns(vm.hback_porch, vm.pixelclock), + tc358768_dpi_to_ns(vm.hactive, vm.pixelclock), + tc358768_dpi_to_ns(vm.hfront_porch, vm.pixelclock), + tc358768_dpi_to_ns(dpi_htot, vm.pixelclock)); + + dev_dbg(dev, "dpi data start (ns): %u + %u = %u\n", + tc358768_dpi_to_ns(vm.hsync_len, vm.pixelclock), + tc358768_dpi_to_ns(vm.hback_porch, vm.pixelclock), + tc358768_dpi_to_ns(dpi_data_start, vm.pixelclock)); + + dsi_dpi_htot = tc358768_dpi_to_dsi_bytes(priv, dpi_htot); + dsi_dpi_data_start = tc358768_dpi_to_dsi_bytes(priv, dpi_data_start); + + if (dsi_dev->mode_flags & MIPI_DSI_MODE_VIDEO_SYNC_PULSE) { + dsi_hsw = tc358768_dpi_to_dsi_bytes(priv, vm.hsync_len); + dsi_hbp = tc358768_dpi_to_dsi_bytes(priv, vm.hback_porch); + } else { + /* HBP is included in HSW in event mode */ + dsi_hbp = 0; + dsi_hsw = tc358768_dpi_to_dsi_bytes(priv, + vm.hsync_len + + vm.hback_porch); + + /* + * The pixel packet includes the actual pixel data, and: + * DSI packet header = 4 bytes + * DCS code = 1 byte + * DSI packet footer = 2 bytes + */ + dsi_hact = hact + 4 + 1 + 2; + + dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss; + + /* + * Here we should check if HFP is long enough for entering LP + * and exiting LP, but it's not clear how to calculate that. + * Instead, this is a naive algorithm that just adjusts the HFP + * and HSW so that HFP is (at least) roughly 2/3 of the total + * blanking time. + */ + if (dsi_hfp < (dsi_hfp + dsi_hsw + dsi_hss) * 2 / 3) { + u32 old_hfp = dsi_hfp; + u32 old_hsw = dsi_hsw; + u32 tot = dsi_hfp + dsi_hsw + dsi_hss; + + dsi_hsw = tot / 3; + + /* + * Seems like sometimes HSW has to be divisible by num-lanes, but + * not always... + */ + dsi_hsw = roundup(dsi_hsw, priv->dsi_lanes); + + dsi_hfp = dsi_dpi_htot - dsi_hact - dsi_hsw - dsi_hss; + + dev_dbg(dev, + "hfp too short, adjusting dsi hfp and dsi hsw from %u, %u to %u, %u\n", + old_hfp, old_hsw, dsi_hfp, dsi_hsw); + } + + dev_dbg(dev, + "dsi horiz timing (bytes): %u, %u + %u + %u + %u = %u\n", + dsi_hss, dsi_hsw, dsi_hbp, dsi_hact, dsi_hfp, + dsi_hss + dsi_hsw + dsi_hbp + dsi_hact + dsi_hfp); + + dev_dbg(dev, "dsi horiz timing (ns): %u + %u + %u + %u + %u = %u\n", + tc358768_dsi_bytes_to_ns(priv, dsi_hss), + tc358768_dsi_bytes_to_ns(priv, dsi_hsw), + tc358768_dsi_bytes_to_ns(priv, dsi_hbp), + tc358768_dsi_bytes_to_ns(priv, dsi_hact), + tc358768_dsi_bytes_to_ns(priv, dsi_hfp), + tc358768_dsi_bytes_to_ns(priv, dsi_hss + dsi_hsw + + dsi_hbp + dsi_hact + dsi_hfp)); + } + + /* VSDly calculation */ + + /* Start with the HW internal delay */ + dsi_vsdly = internal_dly; + + /* Convert to byte units as the other variables are in byte units */ + dsi_vsdly *= priv->dsi_lanes; + + /* Do we need more delay, in addition to the internal? */ + if (dsi_dpi_data_start > dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp) { + dsi_vsdly = dsi_dpi_data_start - dsi_hss - dsi_hsw - dsi_hbp; + dsi_vsdly = roundup(dsi_vsdly, priv->dsi_lanes); + } + + dev_dbg(dev, "dsi data start (bytes) %u + %u + %u + %u = %u\n", + dsi_vsdly, dsi_hss, dsi_hsw, dsi_hbp, + dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp); + + dev_dbg(dev, "dsi data start (ns) %u + %u + %u + %u = %u\n", + tc358768_dsi_bytes_to_ns(priv, dsi_vsdly), + tc358768_dsi_bytes_to_ns(priv, dsi_hss), + tc358768_dsi_bytes_to_ns(priv, dsi_hsw), + tc358768_dsi_bytes_to_ns(priv, dsi_hbp), + tc358768_dsi_bytes_to_ns(priv, dsi_vsdly + dsi_hss + dsi_hsw + dsi_hbp)); + + /* Convert back to hsbyteclk */ + dsi_vsdly /= priv->dsi_lanes; + + /* + * The docs say that there is an internal delay of 40 cycles. + * However, we get underflows if we follow that rule. If we + * instead ignore the internal delay, things work. So either + * the docs are wrong or the calculations are wrong. + * + * As a temporary fix, add the internal delay here, to counter + * the subtraction when writing the register. + */ + dsi_vsdly += internal_dly; + + /* Clamp to the register max */ + if (dsi_vsdly - internal_dly > 0x3ff) { + dev_warn(dev, "VSDly too high, underflows likely\n"); + dsi_vsdly = 0x3ff + internal_dly; + } + /* VSDly[9:0] */ - video_start = max(video_start, internal_delay + 1) - internal_delay; - tc358768_write(priv, TC358768_VSDLY, video_start); + tc358768_write(priv, TC358768_VSDLY, dsi_vsdly - internal_dly); tc358768_write(priv, TC358768_DATAFMT, val); tc358768_write(priv, TC358768_DSITX_DT, data_type); @@ -722,67 +902,67 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) tc358768_write(priv, TC358768_D0W_CNTRL + i * 4, 0x0000); /* DSI Timings */ - dsibclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION, - dsibclk); - dsiclk_nsk = (u32)div_u64((u64)1000000000 * TC358768_PRECISION, dsiclk); - ui_nsk = dsiclk_nsk / 2; - dev_dbg(priv->dev, "dsiclk_nsk: %u\n", dsiclk_nsk); - dev_dbg(priv->dev, "ui_nsk: %u\n", ui_nsk); - dev_dbg(priv->dev, "dsibclk_nsk: %u\n", dsibclk_nsk); + hsbyteclk_ps = (u32)div_u64(PICO, hsbyteclk); + dsiclk_ps = (u32)div_u64(PICO, dsiclk); + ui_ps = dsiclk_ps / 2; + dev_dbg(dev, "dsiclk: %u ps, ui %u ps, hsbyteclk %u ps\n", dsiclk_ps, + ui_ps, hsbyteclk_ps); /* LP11 > 100us for D-PHY Rx Init */ - val = tc358768_ns_to_cnt(100 * 1000, dsibclk_nsk) - 1; - dev_dbg(priv->dev, "LINEINITCNT: 0x%x\n", val); + val = tc358768_ns_to_cnt(100 * 1000, hsbyteclk_ps) - 1; + dev_dbg(dev, "LINEINITCNT: %u\n", val); tc358768_write(priv, TC358768_LINEINITCNT, val); /* LPTimeCnt > 50ns */ - val = tc358768_ns_to_cnt(50, dsibclk_nsk) - 1; + val = tc358768_ns_to_cnt(50, hsbyteclk_ps) - 1; lptxcnt = val; - dev_dbg(priv->dev, "LPTXTIMECNT: 0x%x\n", val); + dev_dbg(dev, "LPTXTIMECNT: %u\n", val); tc358768_write(priv, TC358768_LPTXTIMECNT, val); /* 38ns < TCLK_PREPARE < 95ns */ - val = tc358768_ns_to_cnt(65, dsibclk_nsk) - 1; + val = tc358768_ns_to_cnt(65, hsbyteclk_ps) - 1; + dev_dbg(dev, "TCLK_PREPARECNT %u\n", val); /* TCLK_PREPARE + TCLK_ZERO > 300ns */ - val2 = tc358768_ns_to_cnt(300 - tc358768_to_ns(2 * ui_nsk), - dsibclk_nsk) - 2; + val2 = tc358768_ns_to_cnt(300 - tc358768_ps_to_ns(2 * ui_ps), + hsbyteclk_ps) - 2; + dev_dbg(dev, "TCLK_ZEROCNT %u\n", val2); val |= val2 << 8; - dev_dbg(priv->dev, "TCLK_HEADERCNT: 0x%x\n", val); tc358768_write(priv, TC358768_TCLK_HEADERCNT, val); /* TCLK_TRAIL > 60ns AND TEOT <= 105 ns + 12*UI */ - raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(2 * ui_nsk), dsibclk_nsk) - 5; + raw_val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(2 * ui_ps), hsbyteclk_ps) - 5; val = clamp(raw_val, 0, 127); - dev_dbg(priv->dev, "TCLK_TRAILCNT: 0x%x\n", val); + dev_dbg(dev, "TCLK_TRAILCNT: %u\n", val); tc358768_write(priv, TC358768_TCLK_TRAILCNT, val); /* 40ns + 4*UI < THS_PREPARE < 85ns + 6*UI */ - val = 50 + tc358768_to_ns(4 * ui_nsk); - val = tc358768_ns_to_cnt(val, dsibclk_nsk) - 1; + val = 50 + tc358768_ps_to_ns(4 * ui_ps); + val = tc358768_ns_to_cnt(val, hsbyteclk_ps) - 1; + dev_dbg(dev, "THS_PREPARECNT %u\n", val); /* THS_PREPARE + THS_ZERO > 145ns + 10*UI */ - raw_val = tc358768_ns_to_cnt(145 - tc358768_to_ns(3 * ui_nsk), dsibclk_nsk) - 10; + raw_val = tc358768_ns_to_cnt(145 - tc358768_ps_to_ns(3 * ui_ps), hsbyteclk_ps) - 10; val2 = clamp(raw_val, 0, 127); + dev_dbg(dev, "THS_ZEROCNT %u\n", val2); val |= val2 << 8; - dev_dbg(priv->dev, "THS_HEADERCNT: 0x%x\n", val); tc358768_write(priv, TC358768_THS_HEADERCNT, val); /* TWAKEUP > 1ms in lptxcnt steps */ - val = tc358768_ns_to_cnt(1020000, dsibclk_nsk); + val = tc358768_ns_to_cnt(1020000, hsbyteclk_ps); val = val / (lptxcnt + 1) - 1; - dev_dbg(priv->dev, "TWAKEUP: 0x%x\n", val); + dev_dbg(dev, "TWAKEUP: %u\n", val); tc358768_write(priv, TC358768_TWAKEUP, val); /* TCLK_POSTCNT > 60ns + 52*UI */ - val = tc358768_ns_to_cnt(60 + tc358768_to_ns(52 * ui_nsk), - dsibclk_nsk) - 3; - dev_dbg(priv->dev, "TCLK_POSTCNT: 0x%x\n", val); + val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(52 * ui_ps), + hsbyteclk_ps) - 3; + dev_dbg(dev, "TCLK_POSTCNT: %u\n", val); tc358768_write(priv, TC358768_TCLK_POSTCNT, val); /* max(60ns + 4*UI, 8*UI) < THS_TRAILCNT < 105ns + 12*UI */ - raw_val = tc358768_ns_to_cnt(60 + tc358768_to_ns(18 * ui_nsk), - dsibclk_nsk) - 4; + raw_val = tc358768_ns_to_cnt(60 + tc358768_ps_to_ns(18 * ui_ps), + hsbyteclk_ps) - 4; val = clamp(raw_val, 0, 15); - dev_dbg(priv->dev, "THS_TRAILCNT: 0x%x\n", val); + dev_dbg(dev, "THS_TRAILCNT: %u\n", val); tc358768_write(priv, TC358768_THS_TRAILCNT, val); val = BIT(0); @@ -790,16 +970,17 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) val |= BIT(i + 1); tc358768_write(priv, TC358768_HSTXVREGEN, val); - if (!(mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS)) - tc358768_write(priv, TC358768_TXOPTIONCNTRL, 0x1); + tc358768_write(priv, TC358768_TXOPTIONCNTRL, + (mode_flags & MIPI_DSI_CLOCK_NON_CONTINUOUS) ? 0 : BIT(0)); /* TXTAGOCNT[26:16] RXTASURECNT[10:0] */ - val = tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk * 4); - val = tc358768_ns_to_cnt(val, dsibclk_nsk) / 4 - 1; - val2 = tc358768_ns_to_cnt(tc358768_to_ns((lptxcnt + 1) * dsibclk_nsk), - dsibclk_nsk) - 2; + val = tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps * 4); + val = tc358768_ns_to_cnt(val, hsbyteclk_ps) / 4 - 1; + dev_dbg(dev, "TXTAGOCNT: %u\n", val); + val2 = tc358768_ns_to_cnt(tc358768_ps_to_ns((lptxcnt + 1) * hsbyteclk_ps), + hsbyteclk_ps) - 2; + dev_dbg(dev, "RXTASURECNT: %u\n", val2); val = val << 16 | val2; - dev_dbg(priv->dev, "BTACNTRL1: 0x%x\n", val); tc358768_write(priv, TC358768_BTACNTRL1, val); /* START[0] */ @@ -810,58 +991,44 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) tc358768_write(priv, TC358768_DSI_EVENT, 0); /* vact */ - tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); + tc358768_write(priv, TC358768_DSI_VACT, vm.vactive); /* vsw */ - tc358768_write(priv, TC358768_DSI_VSW, - mode->vsync_end - mode->vsync_start); + tc358768_write(priv, TC358768_DSI_VSW, vm.vsync_len); + /* vbp */ - tc358768_write(priv, TC358768_DSI_VBPR, - mode->vtotal - mode->vsync_end); - - /* hsw * byteclk * ndl / pclk */ - val = (u32)div_u64((mode->hsync_end - mode->hsync_start) * - ((u64)priv->dsiclk / 4) * priv->dsi_lanes, - mode->clock * 1000); - tc358768_write(priv, TC358768_DSI_HSW, val); - - /* hbp * byteclk * ndl / pclk */ - val = (u32)div_u64((mode->htotal - mode->hsync_end) * - ((u64)priv->dsiclk / 4) * priv->dsi_lanes, - mode->clock * 1000); - tc358768_write(priv, TC358768_DSI_HBPR, val); + tc358768_write(priv, TC358768_DSI_VBPR, vm.vback_porch); } else { /* Set event mode */ tc358768_write(priv, TC358768_DSI_EVENT, 1); /* vact */ - tc358768_write(priv, TC358768_DSI_VACT, mode->vdisplay); + tc358768_write(priv, TC358768_DSI_VACT, vm.vactive); /* vsw (+ vbp) */ tc358768_write(priv, TC358768_DSI_VSW, - mode->vtotal - mode->vsync_start); + vm.vsync_len + vm.vback_porch); + /* vbp (not used in event mode) */ tc358768_write(priv, TC358768_DSI_VBPR, 0); + } - /* (hsw + hbp) * byteclk * ndl / pclk */ - val = (u32)div_u64((mode->htotal - mode->hsync_start) * - ((u64)priv->dsiclk / 4) * priv->dsi_lanes, - mode->clock * 1000); - tc358768_write(priv, TC358768_DSI_HSW, val); + /* hsw (bytes) */ + tc358768_write(priv, TC358768_DSI_HSW, dsi_hsw); - /* hbp (not used in event mode) */ - tc358768_write(priv, TC358768_DSI_HBPR, 0); - } + /* hbp (bytes) */ + tc358768_write(priv, TC358768_DSI_HBPR, dsi_hbp); /* hact (bytes) */ tc358768_write(priv, TC358768_DSI_HACT, hact); /* VSYNC polarity */ - if (!(mode->flags & DRM_MODE_FLAG_NVSYNC)) - tc358768_update_bits(priv, TC358768_CONFCTL, BIT(5), BIT(5)); + tc358768_update_bits(priv, TC358768_CONFCTL, BIT(5), + (mode->flags & DRM_MODE_FLAG_PVSYNC) ? BIT(5) : 0); + /* HSYNC polarity */ - if (mode->flags & DRM_MODE_FLAG_PHSYNC) - tc358768_update_bits(priv, TC358768_PP_MISC, BIT(0), BIT(0)); + tc358768_update_bits(priv, TC358768_PP_MISC, BIT(0), + (mode->flags & DRM_MODE_FLAG_PHSYNC) ? BIT(0) : 0); /* Start DSI Tx */ tc358768_write(priv, TC358768_DSI_START, 0x1); @@ -891,7 +1058,7 @@ static void tc358768_bridge_pre_enable(struct drm_bridge *bridge) ret = tc358768_clear_error(priv); if (ret) { - dev_err(priv->dev, "Bridge pre_enable failed: %d\n", ret); + dev_err(dev, "Bridge pre_enable failed: %d\n", ret); tc358768_bridge_disable(bridge); tc358768_bridge_post_disable(bridge); } @@ -959,9 +1126,27 @@ tc358768_atomic_get_input_bus_fmts(struct drm_bridge *bridge, return input_fmts; } +static bool tc358768_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* Default to positive sync */ + + if (!(adjusted_mode->flags & + (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC))) + adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; + + if (!(adjusted_mode->flags & + (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) + adjusted_mode->flags |= DRM_MODE_FLAG_PVSYNC; + + return true; +} + static const struct drm_bridge_funcs tc358768_bridge_funcs = { .attach = tc358768_bridge_attach, .mode_valid = tc358768_bridge_mode_valid, + .mode_fixup = tc358768_mode_fixup, .pre_enable = tc358768_bridge_pre_enable, .enable = tc358768_bridge_enable, .disable = tc358768_bridge_disable, diff --git a/drivers/gpu/drm/display/drm_dp_mst_topology.c b/drivers/gpu/drm/display/drm_dp_mst_topology.c index ed96cfcfa304..c490e8befc2f 100644 --- a/drivers/gpu/drm/display/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c @@ -3255,15 +3255,15 @@ out_get_port: } EXPORT_SYMBOL(drm_dp_send_query_stream_enc_status); -static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_atomic_payload *payload) +static int drm_dp_create_payload_at_dfp(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_atomic_payload *payload) { return drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot, payload->time_slots); } -static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_atomic_payload *payload) +static int drm_dp_create_payload_to_remote(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_atomic_payload *payload) { int ret; struct drm_dp_mst_port *port = drm_dp_mst_topology_get_port_validated(mgr, payload->port); @@ -3276,17 +3276,20 @@ static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr, return ret; } -static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_topology_state *mst_state, - struct drm_dp_mst_atomic_payload *payload) +static void drm_dp_destroy_payload_at_remote_and_dfp(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_atomic_payload *payload) { drm_dbg_kms(mgr->dev, "\n"); /* it's okay for these to fail */ - drm_dp_payload_send_msg(mgr, payload->port, payload->vcpi, 0); - drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot, 0); + if (payload->payload_allocation_status == DRM_DP_MST_PAYLOAD_ALLOCATION_REMOTE) { + drm_dp_payload_send_msg(mgr, payload->port, payload->vcpi, 0); + payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_DFP; + } - return 0; + if (payload->payload_allocation_status == DRM_DP_MST_PAYLOAD_ALLOCATION_DFP) + drm_dp_dpcd_write_payload(mgr, payload->vcpi, payload->vc_start_slot, 0); } /** @@ -3296,10 +3299,9 @@ static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr, * @payload: The payload to write * * Determines the starting time slot for the given payload, and programs the VCPI for this payload - * into hardware. After calling this, the driver should generate ACT and payload packets. + * into the DPCD of DPRX. After calling this, the driver should generate ACT and payload packets. * - * Returns: 0 on success, error code on failure. In the event that this fails, - * @payload.vc_start_slot will also be set to -1. + * Returns: 0 on success, error code on failure. */ int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_topology_state *mst_state, @@ -3308,69 +3310,93 @@ int drm_dp_add_payload_part1(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port; int ret; + /* Update mst mgr info */ + if (mgr->payload_count == 0) + mgr->next_start_slot = mst_state->start_slot; + + payload->vc_start_slot = mgr->next_start_slot; + + mgr->payload_count++; + mgr->next_start_slot += payload->time_slots; + + payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_LOCAL; + + /* Allocate payload to immediate downstream facing port */ port = drm_dp_mst_topology_get_port_validated(mgr, payload->port); if (!port) { drm_dbg_kms(mgr->dev, - "VCPI %d for port %p not in topology, not creating a payload\n", + "VCPI %d for port %p not in topology, not creating a payload to remote\n", payload->vcpi, payload->port); - payload->vc_start_slot = -1; - return 0; + return -EIO; } - if (mgr->payload_count == 0) - mgr->next_start_slot = mst_state->start_slot; - - payload->vc_start_slot = mgr->next_start_slot; - - ret = drm_dp_create_payload_step1(mgr, payload); - drm_dp_mst_topology_put_port(port); + ret = drm_dp_create_payload_at_dfp(mgr, payload); if (ret < 0) { - drm_warn(mgr->dev, "Failed to create MST payload for port %p: %d\n", - payload->port, ret); - payload->vc_start_slot = -1; - return ret; + drm_dbg_kms(mgr->dev, "Failed to create MST payload for port %p: %d\n", + payload->port, ret); + goto put_port; } - mgr->payload_count++; - mgr->next_start_slot += payload->time_slots; + payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_DFP; - return 0; +put_port: + drm_dp_mst_topology_put_port(port); + + return ret; } EXPORT_SYMBOL(drm_dp_add_payload_part1); /** - * drm_dp_remove_payload() - Remove an MST payload + * drm_dp_remove_payload_part1() - Remove an MST payload along the virtual channel * @mgr: Manager to use. * @mst_state: The MST atomic state - * @old_payload: The payload with its old state - * @new_payload: The payload to write + * @payload: The payload to remove * - * Removes a payload from an MST topology if it was successfully assigned a start slot. Also updates - * the starting time slots of all other payloads which would have been shifted towards the start of - * the VC table as a result. After calling this, the driver should generate ACT and payload packets. + * Removes a payload along the virtual channel if it was successfully allocated. + * After calling this, the driver should set HW to generate ACT and then switch to new + * payload allocation state. */ -void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, - struct drm_dp_mst_topology_state *mst_state, - const struct drm_dp_mst_atomic_payload *old_payload, - struct drm_dp_mst_atomic_payload *new_payload) +void drm_dp_remove_payload_part1(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_atomic_payload *payload) { - struct drm_dp_mst_atomic_payload *pos; + /* Remove remote payload allocation */ bool send_remove = false; - /* We failed to make the payload, so nothing to do */ - if (new_payload->vc_start_slot == -1) - return; - mutex_lock(&mgr->lock); - send_remove = drm_dp_mst_port_downstream_of_branch(new_payload->port, mgr->mst_primary); + send_remove = drm_dp_mst_port_downstream_of_branch(payload->port, mgr->mst_primary); mutex_unlock(&mgr->lock); if (send_remove) - drm_dp_destroy_payload_step1(mgr, mst_state, new_payload); + drm_dp_destroy_payload_at_remote_and_dfp(mgr, mst_state, payload); else drm_dbg_kms(mgr->dev, "Payload for VCPI %d not in topology, not sending remove\n", - new_payload->vcpi); + payload->vcpi); + payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_LOCAL; +} +EXPORT_SYMBOL(drm_dp_remove_payload_part1); + +/** + * drm_dp_remove_payload_part2() - Remove an MST payload locally + * @mgr: Manager to use. + * @mst_state: The MST atomic state + * @old_payload: The payload with its old state + * @new_payload: The payload with its latest state + * + * Updates the starting time slots of all other payloads which would have been shifted towards + * the start of the payload ID table as a result of removing a payload. Driver should call this + * function whenever it removes a payload in its HW. It's independent to the result of payload + * allocation/deallocation at branch devices along the virtual channel. + */ +void drm_dp_remove_payload_part2(struct drm_dp_mst_topology_mgr *mgr, + struct drm_dp_mst_topology_state *mst_state, + const struct drm_dp_mst_atomic_payload *old_payload, + struct drm_dp_mst_atomic_payload *new_payload) +{ + struct drm_dp_mst_atomic_payload *pos; + + /* Remove local payload allocation */ list_for_each_entry(pos, &mst_state->payloads, next) { if (pos != new_payload && pos->vc_start_slot > new_payload->vc_start_slot) pos->vc_start_slot -= old_payload->time_slots; @@ -3382,9 +3408,10 @@ void drm_dp_remove_payload(struct drm_dp_mst_topology_mgr *mgr, if (new_payload->delete) drm_dp_mst_put_port_malloc(new_payload->port); -} -EXPORT_SYMBOL(drm_dp_remove_payload); + new_payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_NONE; +} +EXPORT_SYMBOL(drm_dp_remove_payload_part2); /** * drm_dp_add_payload_part2() - Execute payload update part 2 * @mgr: Manager to use. @@ -3403,21 +3430,19 @@ int drm_dp_add_payload_part2(struct drm_dp_mst_topology_mgr *mgr, int ret = 0; /* Skip failed payloads */ - if (payload->vc_start_slot == -1) { - drm_dbg_kms(mgr->dev, "Part 1 of payload creation for %s failed, skipping part 2\n", + if (payload->payload_allocation_status != DRM_DP_MST_PAYLOAD_ALLOCATION_DFP) { + drm_dbg_kms(state->dev, "Part 1 of payload creation for %s failed, skipping part 2\n", payload->port->connector->name); return -EIO; } - ret = drm_dp_create_payload_step2(mgr, payload); - if (ret < 0) { - if (!payload->delete) - drm_err(mgr->dev, "Step 2 of creating MST payload for %p failed: %d\n", - payload->port, ret); - else - drm_dbg_kms(mgr->dev, "Step 2 of removing MST payload for %p failed: %d\n", - payload->port, ret); - } + /* Allocate payload to remote end */ + ret = drm_dp_create_payload_to_remote(mgr, payload); + if (ret < 0) + drm_err(mgr->dev, "Step 2 of creating MST payload for %p failed: %d\n", + payload->port, ret); + else + payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_REMOTE; return ret; } @@ -4328,6 +4353,7 @@ int drm_dp_atomic_find_time_slots(struct drm_atomic_state *state, drm_dp_mst_get_port_malloc(port); payload->port = port; payload->vc_start_slot = -1; + payload->payload_allocation_status = DRM_DP_MST_PAYLOAD_ALLOCATION_NONE; list_add(&payload->next, &topology_state->payloads); } payload->time_slots = req_slots; @@ -4497,7 +4523,7 @@ void drm_dp_mst_atomic_wait_for_dependencies(struct drm_atomic_state *state) } /* Now that previous state is committed, it's safe to copy over the start slot - * assignments + * and allocation status assignments */ list_for_each_entry(old_payload, &old_mst_state->payloads, next) { if (old_payload->delete) @@ -4506,6 +4532,8 @@ void drm_dp_mst_atomic_wait_for_dependencies(struct drm_atomic_state *state) new_payload = drm_atomic_get_mst_payload_state(new_mst_state, old_payload->port); new_payload->vc_start_slot = old_payload->vc_start_slot; + new_payload->payload_allocation_status = + old_payload->payload_allocation_status; } } } @@ -4822,6 +4850,13 @@ void drm_dp_mst_dump_topology(struct seq_file *m, struct drm_dp_mst_atomic_payload *payload; int i, ret; + static const char *const status[] = { + "None", + "Local", + "DFP", + "Remote", + }; + mutex_lock(&mgr->lock); if (mgr->mst_primary) drm_dp_mst_dump_mstb(m, mgr->mst_primary); @@ -4838,7 +4873,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, seq_printf(m, "payload_mask: %x, max_payloads: %d, start_slot: %u, pbn_div: %d\n", state->payload_mask, mgr->max_payloads, state->start_slot, state->pbn_div); - seq_printf(m, "\n| idx | port | vcpi | slots | pbn | dsc | sink name |\n"); + seq_printf(m, "\n| idx | port | vcpi | slots | pbn | dsc | status | sink name |\n"); for (i = 0; i < mgr->max_payloads; i++) { list_for_each_entry(payload, &state->payloads, next) { char name[14]; @@ -4847,7 +4882,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, continue; fetch_monitor_name(mgr, payload->port, name, sizeof(name)); - seq_printf(m, " %5d %6d %6d %02d - %02d %5d %5s %19s\n", + seq_printf(m, " %5d %6d %6d %02d - %02d %5d %5s %8s %19s\n", i, payload->port->port_num, payload->vcpi, @@ -4855,6 +4890,7 @@ void drm_dp_mst_dump_topology(struct seq_file *m, payload->vc_start_slot + payload->time_slots - 1, payload->pbn, payload->dsc_enabled ? "Y" : "N", + status[payload->payload_allocation_status], (*name != 0) ? name : "Unknown"); } } diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index c277b198fa3f..f1a503aafe5a 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1841,9 +1841,9 @@ static const struct drm_debugfs_info drm_atomic_debugfs_list[] = { {"state", drm_state_info, 0}, }; -void drm_atomic_debugfs_init(struct drm_minor *minor) +void drm_atomic_debugfs_init(struct drm_device *dev) { - drm_debugfs_add_files(minor->dev, drm_atomic_debugfs_list, + drm_debugfs_add_files(dev, drm_atomic_debugfs_list, ARRAY_SIZE(drm_atomic_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 292e38eb6218..71d399397107 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -3339,6 +3339,9 @@ void drm_atomic_helper_shutdown(struct drm_device *dev) struct drm_modeset_acquire_ctx ctx; int ret; + if (dev == NULL) + return; + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); ret = drm_atomic_helper_disable_all(dev, &ctx); diff --git a/drivers/gpu/drm/drm_auth.c b/drivers/gpu/drm/drm_auth.c index cf92a9ae8034..2ed2585ded37 100644 --- a/drivers/gpu/drm/drm_auth.c +++ b/drivers/gpu/drm/drm_auth.c @@ -235,7 +235,8 @@ static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) static int drm_master_check_perm(struct drm_device *dev, struct drm_file *file_priv) { - if (file_priv->pid == task_pid(current) && file_priv->was_master) + if (file_priv->was_master && + rcu_access_pointer(file_priv->pid) == task_pid(current)) return 0; if (!capable(CAP_SYS_ADMIN)) diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index 39e68e45bb12..30d66bee0ec6 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -1384,9 +1384,9 @@ static const struct drm_debugfs_info drm_bridge_debugfs_list[] = { { "bridge_chains", drm_bridge_chains_info, 0 }, }; -void drm_bridge_debugfs_init(struct drm_minor *minor) +void drm_bridge_debugfs_init(struct drm_device *dev) { - drm_debugfs_add_files(minor->dev, drm_bridge_debugfs_list, + drm_debugfs_add_files(dev, drm_bridge_debugfs_list, ARRAY_SIZE(drm_bridge_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_buddy.c b/drivers/gpu/drm/drm_buddy.c index e6f5ba5f4baf..f57e6d74fb0e 100644 --- a/drivers/gpu/drm/drm_buddy.c +++ b/drivers/gpu/drm/drm_buddy.c @@ -480,10 +480,12 @@ err_undo: static int __alloc_range(struct drm_buddy *mm, struct list_head *dfs, u64 start, u64 size, - struct list_head *blocks) + struct list_head *blocks, + u64 *total_allocated_on_err) { struct drm_buddy_block *block; struct drm_buddy_block *buddy; + u64 total_allocated = 0; LIST_HEAD(allocated); u64 end; int err; @@ -520,6 +522,7 @@ static int __alloc_range(struct drm_buddy *mm, } mark_allocated(block); + total_allocated += drm_buddy_block_size(mm, block); mm->avail -= drm_buddy_block_size(mm, block); list_add_tail(&block->link, &allocated); continue; @@ -551,13 +554,20 @@ err_undo: __drm_buddy_free(mm, block); err_free: - drm_buddy_free_list(mm, &allocated); + if (err == -ENOSPC && total_allocated_on_err) { + list_splice_tail(&allocated, blocks); + *total_allocated_on_err = total_allocated; + } else { + drm_buddy_free_list(mm, &allocated); + } + return err; } static int __drm_buddy_alloc_range(struct drm_buddy *mm, u64 start, u64 size, + u64 *total_allocated_on_err, struct list_head *blocks) { LIST_HEAD(dfs); @@ -566,7 +576,62 @@ static int __drm_buddy_alloc_range(struct drm_buddy *mm, for (i = 0; i < mm->n_roots; ++i) list_add_tail(&mm->roots[i]->tmp_link, &dfs); - return __alloc_range(mm, &dfs, start, size, blocks); + return __alloc_range(mm, &dfs, start, size, + blocks, total_allocated_on_err); +} + +static int __alloc_contig_try_harder(struct drm_buddy *mm, + u64 size, + u64 min_block_size, + struct list_head *blocks) +{ + u64 rhs_offset, lhs_offset, lhs_size, filled; + struct drm_buddy_block *block; + struct list_head *list; + LIST_HEAD(blocks_lhs); + unsigned long pages; + unsigned int order; + u64 modify_size; + int err; + + modify_size = rounddown_pow_of_two(size); + pages = modify_size >> ilog2(mm->chunk_size); + order = fls(pages) - 1; + if (order == 0) + return -ENOSPC; + + list = &mm->free_list[order]; + if (list_empty(list)) + return -ENOSPC; + + list_for_each_entry_reverse(block, list, link) { + /* Allocate blocks traversing RHS */ + rhs_offset = drm_buddy_block_offset(block); + err = __drm_buddy_alloc_range(mm, rhs_offset, size, + &filled, blocks); + if (!err || err != -ENOSPC) + return err; + + lhs_size = max((size - filled), min_block_size); + if (!IS_ALIGNED(lhs_size, min_block_size)) + lhs_size = round_up(lhs_size, min_block_size); + + /* Allocate blocks traversing LHS */ + lhs_offset = drm_buddy_block_offset(block) - lhs_size; + err = __drm_buddy_alloc_range(mm, lhs_offset, lhs_size, + NULL, &blocks_lhs); + if (!err) { + list_splice(&blocks_lhs, blocks); + return 0; + } else if (err != -ENOSPC) { + drm_buddy_free_list(mm, blocks); + return err; + } + /* Free blocks for the next iteration */ + drm_buddy_free_list(mm, blocks); + } + + return -ENOSPC; } /** @@ -626,7 +691,7 @@ int drm_buddy_block_trim(struct drm_buddy *mm, new_start = drm_buddy_block_offset(block); list_add(&block->tmp_link, &dfs); - err = __alloc_range(mm, &dfs, new_start, new_size, blocks); + err = __alloc_range(mm, &dfs, new_start, new_size, blocks, NULL); if (err) { mark_allocated(block); mm->avail -= drm_buddy_block_size(mm, block); @@ -645,7 +710,7 @@ EXPORT_SYMBOL(drm_buddy_block_trim); * @start: start of the allowed range for this block * @end: end of the allowed range for this block * @size: size of the allocation - * @min_page_size: alignment of the allocation + * @min_block_size: alignment of the allocation * @blocks: output list head to add allocated blocks * @flags: DRM_BUDDY_*_ALLOCATION flags * @@ -660,23 +725,24 @@ EXPORT_SYMBOL(drm_buddy_block_trim); */ int drm_buddy_alloc_blocks(struct drm_buddy *mm, u64 start, u64 end, u64 size, - u64 min_page_size, + u64 min_block_size, struct list_head *blocks, unsigned long flags) { struct drm_buddy_block *block = NULL; + u64 original_size, original_min_size; unsigned int min_order, order; - unsigned long pages; LIST_HEAD(allocated); + unsigned long pages; int err; if (size < mm->chunk_size) return -EINVAL; - if (min_page_size < mm->chunk_size) + if (min_block_size < mm->chunk_size) return -EINVAL; - if (!is_power_of_2(min_page_size)) + if (!is_power_of_2(min_block_size)) return -EINVAL; if (!IS_ALIGNED(start | end | size, mm->chunk_size)) @@ -690,14 +756,23 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm, /* Actual range allocation */ if (start + size == end) - return __drm_buddy_alloc_range(mm, start, size, blocks); - - if (!IS_ALIGNED(size, min_page_size)) - return -EINVAL; + return __drm_buddy_alloc_range(mm, start, size, NULL, blocks); + + original_size = size; + original_min_size = min_block_size; + + /* Roundup the size to power of 2 */ + if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION) { + size = roundup_pow_of_two(size); + min_block_size = size; + /* Align size value to min_block_size */ + } else if (!IS_ALIGNED(size, min_block_size)) { + size = round_up(size, min_block_size); + } pages = size >> ilog2(mm->chunk_size); order = fls(pages) - 1; - min_order = ilog2(min_page_size) - ilog2(mm->chunk_size); + min_order = ilog2(min_block_size) - ilog2(mm->chunk_size); do { order = min(order, (unsigned int)fls(pages) - 1); @@ -716,6 +791,16 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm, break; if (order-- == min_order) { + if (flags & DRM_BUDDY_CONTIGUOUS_ALLOCATION && + !(flags & DRM_BUDDY_RANGE_ALLOCATION)) + /* + * Try contiguous block allocation through + * try harder method + */ + return __alloc_contig_try_harder(mm, + original_size, + original_min_size, + blocks); err = -ENOSPC; goto err_free; } @@ -732,6 +817,31 @@ int drm_buddy_alloc_blocks(struct drm_buddy *mm, break; } while (1); + /* Trim the allocated block to the required size */ + if (original_size != size) { + struct list_head *trim_list; + LIST_HEAD(temp); + u64 trim_size; + + trim_list = &allocated; + trim_size = original_size; + + if (!list_is_singular(&allocated)) { + block = list_last_entry(&allocated, typeof(*block), link); + list_move(&block->link, &temp); + trim_list = &temp; + trim_size = drm_buddy_block_size(mm, block) - + (size - original_size); + } + + drm_buddy_block_trim(mm, + trim_size, + trim_list); + + if (!list_empty(&temp)) + list_splice_tail(trim_list, &allocated); + } + list_splice_tail(&allocated, blocks); return 0; diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c index 037e36f2049c..2762572f286e 100644 --- a/drivers/gpu/drm/drm_client.c +++ b/drivers/gpu/drm/drm_client.c @@ -535,9 +535,9 @@ static const struct drm_debugfs_info drm_client_debugfs_list[] = { { "internal_clients", drm_client_debugfs_internal_clients, 0 }, }; -void drm_client_debugfs_init(struct drm_minor *minor) +void drm_client_debugfs_init(struct drm_device *dev) { - drm_debugfs_add_files(minor->dev, drm_client_debugfs_list, + drm_debugfs_add_files(dev, drm_client_debugfs_list, ARRAY_SIZE(drm_client_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index f239de0085c3..9d4c7b0c5c05 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -2212,6 +2212,7 @@ static int drm_mode_create_colorspace_property(struct drm_connector *connector, /** * drm_mode_create_hdmi_colorspace_property - create hdmi colorspace property * @connector: connector to create the Colorspace property on. + * @supported_colorspaces: bitmap of supported color spaces * * Called by a driver the first time it's needed, must be attached to desired * HDMI connectors. @@ -2236,6 +2237,7 @@ EXPORT_SYMBOL(drm_mode_create_hdmi_colorspace_property); /** * drm_mode_create_dp_colorspace_property - create dp colorspace property * @connector: connector to create the Colorspace property on. + * @supported_colorspaces: bitmap of supported color spaces * * Called by a driver the first time it's needed, must be attached to desired * DP connectors. diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h index 501a10edd0e1..8556c3b3ff88 100644 --- a/drivers/gpu/drm/drm_crtc_internal.h +++ b/drivers/gpu/drm/drm_crtc_internal.h @@ -232,7 +232,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, /* drm_atomic.c */ #ifdef CONFIG_DEBUG_FS struct drm_minor; -void drm_atomic_debugfs_init(struct drm_minor *minor); +void drm_atomic_debugfs_init(struct drm_device *dev); #endif int __drm_atomic_helper_disable_plane(struct drm_plane *plane, diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c index 2de43ff3ce0a..f291fb4b359f 100644 --- a/drivers/gpu/drm/drm_debugfs.c +++ b/drivers/gpu/drm/drm_debugfs.c @@ -40,7 +40,7 @@ #include <drm/drm_file.h> #include <drm/drm_gem.h> #include <drm/drm_managed.h> -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> #include "drm_crtc_internal.h" #include "drm_internal.h" @@ -92,15 +92,17 @@ static int drm_clients_info(struct seq_file *m, void *data) */ mutex_lock(&dev->filelist_mutex); list_for_each_entry_reverse(priv, &dev->filelist, lhead) { - struct task_struct *task; bool is_current_master = drm_is_current_master(priv); + struct task_struct *task; + struct pid *pid; - rcu_read_lock(); /* locks pid_task()->comm */ - task = pid_task(priv->pid, PIDTYPE_TGID); + rcu_read_lock(); /* Locks priv->pid and pid_task()->comm! */ + pid = rcu_dereference(priv->pid); + task = pid_task(pid, PIDTYPE_TGID); uid = task ? __task_cred(task)->euid : GLOBAL_ROOT_UID; seq_printf(m, "%20s %5d %3d %c %c %5d %10u\n", task ? task->comm : "<unknown>", - pid_vnr(priv->pid), + pid_vnr(pid), priv->minor->index, is_current_master ? 'y' : 'n', priv->authenticated ? 'y' : 'n', @@ -150,6 +152,9 @@ static int drm_debugfs_open(struct inode *inode, struct file *file) { struct drm_info_node *node = inode->i_private; + if (!device_is_registered(node->minor->kdev)) + return -ENODEV; + return single_open(file, node->info_ent->show, node); } @@ -157,6 +162,10 @@ static int drm_debugfs_entry_open(struct inode *inode, struct file *file) { struct drm_debugfs_entry *entry = inode->i_private; struct drm_debugfs_info *node = &entry->file; + struct drm_minor *minor = entry->dev->primary ?: entry->dev->accel; + + if (!device_is_registered(minor->kdev)) + return -ENODEV; return single_open(file, node->show, entry); } @@ -180,31 +189,31 @@ static const struct file_operations drm_debugfs_fops = { /** * drm_debugfs_gpuva_info - dump the given DRM GPU VA space * @m: pointer to the &seq_file to write - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * @gpuvm: the &drm_gpuvm representing the GPU VA space * * Dumps the GPU VA mappings of a given DRM GPU VA manager. * * For each DRM GPU VA space drivers should call this function from their * &drm_info_list's show callback. * - * Returns: 0 on success, -ENODEV if the &mgr is not initialized + * Returns: 0 on success, -ENODEV if the &gpuvm is not initialized */ int drm_debugfs_gpuva_info(struct seq_file *m, - struct drm_gpuva_manager *mgr) + struct drm_gpuvm *gpuvm) { - struct drm_gpuva *va, *kva = &mgr->kernel_alloc_node; + struct drm_gpuva *va, *kva = &gpuvm->kernel_alloc_node; - if (!mgr->name) + if (!gpuvm->name) return -ENODEV; seq_printf(m, "DRM GPU VA space (%s) [0x%016llx;0x%016llx]\n", - mgr->name, mgr->mm_start, mgr->mm_start + mgr->mm_range); + gpuvm->name, gpuvm->mm_start, gpuvm->mm_start + gpuvm->mm_range); seq_printf(m, "Kernel reserved node [0x%016llx;0x%016llx]\n", kva->va.addr, kva->va.addr + kva->va.range); seq_puts(m, "\n"); seq_puts(m, " VAs | start | range | end | object | object offset\n"); seq_puts(m, "-------------------------------------------------------------------------------------------------------------\n"); - drm_gpuva_for_each_va(va, mgr) { + drm_gpuvm_for_each_va(va, gpuvm) { if (unlikely(va == kva)) continue; @@ -227,7 +236,7 @@ EXPORT_SYMBOL(drm_debugfs_gpuva_info); * * Create a given set of debugfs files represented by an array of * &struct drm_info_list in the given root directory. These files will be removed - * automatically on drm_debugfs_cleanup(). + * automatically on drm_debugfs_dev_fini(). */ void drm_debugfs_create_files(const struct drm_info_list *files, int count, struct dentry *root, struct drm_minor *minor) @@ -242,7 +251,7 @@ void drm_debugfs_create_files(const struct drm_info_list *files, int count, if (features && !drm_core_check_all_features(dev, features)) continue; - tmp = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL); + tmp = drmm_kzalloc(dev, sizeof(*tmp), GFP_KERNEL); if (tmp == NULL) continue; @@ -251,111 +260,89 @@ void drm_debugfs_create_files(const struct drm_info_list *files, int count, 0444, root, tmp, &drm_debugfs_fops); tmp->info_ent = &files[i]; - - mutex_lock(&minor->debugfs_lock); - list_add(&tmp->list, &minor->debugfs_list); - mutex_unlock(&minor->debugfs_lock); } } EXPORT_SYMBOL(drm_debugfs_create_files); -int drm_debugfs_init(struct drm_minor *minor, int minor_id, - struct dentry *root) +int drm_debugfs_remove_files(const struct drm_info_list *files, int count, + struct dentry *root, struct drm_minor *minor) { - struct drm_device *dev = minor->dev; - struct drm_debugfs_entry *entry, *tmp; - char name[64]; - - INIT_LIST_HEAD(&minor->debugfs_list); - mutex_init(&minor->debugfs_lock); - sprintf(name, "%d", minor_id); - minor->debugfs_root = debugfs_create_dir(name, root); - - drm_debugfs_add_files(minor->dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES); - - if (drm_drv_uses_atomic_modeset(dev)) { - drm_atomic_debugfs_init(minor); - drm_bridge_debugfs_init(minor); - } - - if (drm_core_check_feature(dev, DRIVER_MODESET)) { - drm_framebuffer_debugfs_init(minor); + int i; - drm_client_debugfs_init(minor); - } + for (i = 0; i < count; i++) { + struct dentry *dent = debugfs_lookup(files[i].name, root); - if (dev->driver->debugfs_init) - dev->driver->debugfs_init(minor); + if (!dent) + continue; - list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) { - debugfs_create_file(entry->file.name, 0444, - minor->debugfs_root, entry, &drm_debugfs_entry_fops); - list_del(&entry->list); + drmm_kfree(minor->dev, d_inode(dent)->i_private); + debugfs_remove(dent); } - return 0; } +EXPORT_SYMBOL(drm_debugfs_remove_files); -void drm_debugfs_late_register(struct drm_device *dev) +/** + * drm_debugfs_dev_init - create debugfs directory for the device + * @dev: the device which we want to create the directory for + * @root: the parent directory depending on the device type + * + * Creates the debugfs directory for the device under the given root directory. + */ +void drm_debugfs_dev_init(struct drm_device *dev, struct dentry *root) { - struct drm_minor *minor = dev->primary; - struct drm_debugfs_entry *entry, *tmp; - - if (!minor) - return; - - list_for_each_entry_safe(entry, tmp, &dev->debugfs_list, list) { - debugfs_create_file(entry->file.name, 0444, - minor->debugfs_root, entry, &drm_debugfs_entry_fops); - list_del(&entry->list); - } + dev->debugfs_root = debugfs_create_dir(dev->unique, root); } -int drm_debugfs_remove_files(const struct drm_info_list *files, int count, - struct drm_minor *minor) +/** + * drm_debugfs_dev_fini - cleanup debugfs directory + * @dev: the device to cleanup the debugfs stuff + * + * Remove the debugfs directory, might be called multiple times. + */ +void drm_debugfs_dev_fini(struct drm_device *dev) { - struct list_head *pos, *q; - struct drm_info_node *tmp; - int i; - - mutex_lock(&minor->debugfs_lock); - for (i = 0; i < count; i++) { - list_for_each_safe(pos, q, &minor->debugfs_list) { - tmp = list_entry(pos, struct drm_info_node, list); - if (tmp->info_ent == &files[i]) { - debugfs_remove(tmp->dent); - list_del(pos); - kfree(tmp); - } - } - } - mutex_unlock(&minor->debugfs_lock); - return 0; + debugfs_remove_recursive(dev->debugfs_root); + dev->debugfs_root = NULL; } -EXPORT_SYMBOL(drm_debugfs_remove_files); -static void drm_debugfs_remove_all_files(struct drm_minor *minor) +void drm_debugfs_dev_register(struct drm_device *dev) { - struct drm_info_node *node, *tmp; + drm_debugfs_add_files(dev, drm_debugfs_list, DRM_DEBUGFS_ENTRIES); - mutex_lock(&minor->debugfs_lock); - list_for_each_entry_safe(node, tmp, &minor->debugfs_list, list) { - debugfs_remove(node->dent); - list_del(&node->list); - kfree(node); + if (drm_core_check_feature(dev, DRIVER_MODESET)) { + drm_framebuffer_debugfs_init(dev); + drm_client_debugfs_init(dev); + } + if (drm_drv_uses_atomic_modeset(dev)) { + drm_atomic_debugfs_init(dev); + drm_bridge_debugfs_init(dev); } - mutex_unlock(&minor->debugfs_lock); } -void drm_debugfs_cleanup(struct drm_minor *minor) +int drm_debugfs_register(struct drm_minor *minor, int minor_id, + struct dentry *root) { - if (!minor->debugfs_root) - return; + struct drm_device *dev = minor->dev; + char name[64]; + + sprintf(name, "%d", minor_id); + minor->debugfs_symlink = debugfs_create_symlink(name, root, + dev->unique); + + /* TODO: Only for compatibility with drivers */ + minor->debugfs_root = dev->debugfs_root; - drm_debugfs_remove_all_files(minor); + if (dev->driver->debugfs_init && dev->render != minor) + dev->driver->debugfs_init(minor); - debugfs_remove_recursive(minor->debugfs_root); - minor->debugfs_root = NULL; + return 0; +} + +void drm_debugfs_unregister(struct drm_minor *minor) +{ + debugfs_remove(minor->debugfs_symlink); + minor->debugfs_symlink = NULL; } /** @@ -381,9 +368,8 @@ void drm_debugfs_add_file(struct drm_device *dev, const char *name, entry->file.data = data; entry->dev = dev; - mutex_lock(&dev->debugfs_mutex); - list_add(&entry->list, &dev->debugfs_list); - mutex_unlock(&dev->debugfs_mutex); + debugfs_create_file(name, 0444, dev->debugfs_root, entry, + &drm_debugfs_entry_fops); } EXPORT_SYMBOL(drm_debugfs_add_file); @@ -540,13 +526,13 @@ static const struct file_operations drm_connector_fops = { void drm_debugfs_connector_add(struct drm_connector *connector) { - struct drm_minor *minor = connector->dev->primary; + struct drm_device *dev = connector->dev; struct dentry *root; - if (!minor->debugfs_root) + if (!dev->debugfs_root) return; - root = debugfs_create_dir(connector->name, minor->debugfs_root); + root = debugfs_create_dir(connector->name, dev->debugfs_root); connector->debugfs_entry = root; /* force */ @@ -581,7 +567,7 @@ void drm_debugfs_connector_remove(struct drm_connector *connector) void drm_debugfs_crtc_add(struct drm_crtc *crtc) { - struct drm_minor *minor = crtc->dev->primary; + struct drm_device *dev = crtc->dev; struct dentry *root; char *name; @@ -589,7 +575,7 @@ void drm_debugfs_crtc_add(struct drm_crtc *crtc) if (!name) return; - root = debugfs_create_dir(name, minor->debugfs_root); + root = debugfs_create_dir(name, dev->debugfs_root); kfree(name); crtc->debugfs_entry = root; diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 3eda026ffac6..535f16e7882e 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -172,10 +172,9 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type) if (!minor) return 0; - if (minor->type == DRM_MINOR_ACCEL) { - accel_debugfs_init(minor, minor->index); - } else { - ret = drm_debugfs_init(minor, minor->index, drm_debugfs_root); + if (minor->type != DRM_MINOR_ACCEL) { + ret = drm_debugfs_register(minor, minor->index, + drm_debugfs_root); if (ret) { DRM_ERROR("DRM: Failed to initialize /sys/kernel/debug/dri.\n"); goto err_debugfs; @@ -199,7 +198,7 @@ static int drm_minor_register(struct drm_device *dev, enum drm_minor_type type) return 0; err_debugfs: - drm_debugfs_cleanup(minor); + drm_debugfs_unregister(minor); return ret; } @@ -223,7 +222,7 @@ static void drm_minor_unregister(struct drm_device *dev, enum drm_minor_type typ device_del(minor->kdev); dev_set_drvdata(minor->kdev, NULL); /* safety belt */ - drm_debugfs_cleanup(minor); + drm_debugfs_unregister(minor); } /* @@ -598,7 +597,6 @@ static void drm_dev_init_release(struct drm_device *dev, void *res) mutex_destroy(&dev->clientlist_mutex); mutex_destroy(&dev->filelist_mutex); mutex_destroy(&dev->struct_mutex); - mutex_destroy(&dev->debugfs_mutex); drm_legacy_destroy_members(dev); } @@ -639,14 +637,12 @@ static int drm_dev_init(struct drm_device *dev, INIT_LIST_HEAD(&dev->filelist_internal); INIT_LIST_HEAD(&dev->clientlist); INIT_LIST_HEAD(&dev->vblank_event_list); - INIT_LIST_HEAD(&dev->debugfs_list); spin_lock_init(&dev->event_lock); mutex_init(&dev->struct_mutex); mutex_init(&dev->filelist_mutex); mutex_init(&dev->clientlist_mutex); mutex_init(&dev->master_mutex); - mutex_init(&dev->debugfs_mutex); ret = drmm_add_action_or_reset(dev, drm_dev_init_release, NULL); if (ret) @@ -697,6 +693,11 @@ static int drm_dev_init(struct drm_device *dev, goto err; } + if (drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) + accel_debugfs_init(dev); + else + drm_debugfs_dev_init(dev, drm_debugfs_root); + return 0; err: @@ -786,6 +787,9 @@ static void drm_dev_release(struct kref *ref) { struct drm_device *dev = container_of(ref, struct drm_device, ref); + /* Just in case register/unregister was never called */ + drm_debugfs_dev_fini(dev); + if (dev->driver->release) dev->driver->release(dev); @@ -916,6 +920,11 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) if (drm_dev_needs_global_mutex(dev)) mutex_lock(&drm_global_mutex); + if (drm_core_check_feature(dev, DRIVER_COMPUTE_ACCEL)) + accel_debugfs_register(dev); + else + drm_debugfs_dev_register(dev); + ret = drm_minor_register(dev, DRM_MINOR_RENDER); if (ret) goto err_minors; @@ -1001,6 +1010,7 @@ void drm_dev_unregister(struct drm_device *dev) drm_minor_unregister(dev, DRM_MINOR_ACCEL); drm_minor_unregister(dev, DRM_MINOR_PRIMARY); drm_minor_unregister(dev, DRM_MINOR_RENDER); + drm_debugfs_dev_fini(dev); } EXPORT_SYMBOL(drm_dev_unregister); diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 39dd3f694544..ec1cb4890acb 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3497,11 +3497,19 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_connector *connecto mode->vsync_end = mode->vsync_start + vsync_pulse_width; mode->vtotal = mode->vdisplay + vblank; - /* Some EDIDs have bogus h/vtotal values */ - if (mode->hsync_end > mode->htotal) - mode->htotal = mode->hsync_end + 1; - if (mode->vsync_end > mode->vtotal) - mode->vtotal = mode->vsync_end + 1; + /* Some EDIDs have bogus h/vsync_end values */ + if (mode->hsync_end > mode->htotal) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing hsync_end %d->%d\n", + connector->base.id, connector->name, + mode->hsync_end, mode->htotal); + mode->hsync_end = mode->htotal; + } + if (mode->vsync_end > mode->vtotal) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] reducing vsync_end %d->%d\n", + connector->base.id, connector->name, + mode->vsync_end, mode->vtotal); + mode->vsync_end = mode->vtotal; + } drm_mode_do_interlace_quirk(mode, pt); diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index ff69cf0fb42a..5d2809de4517 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec) struct drm_gem_object *obj; unsigned long index; - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object_reverse(exec, index, obj) { dma_resv_unlock(obj->resv); drm_gem_object_put(obj); } diff --git a/drivers/gpu/drm/drm_file.c b/drivers/gpu/drm/drm_file.c index 883d83bc0e3d..e692770ef6d3 100644 --- a/drivers/gpu/drm/drm_file.c +++ b/drivers/gpu/drm/drm_file.c @@ -160,7 +160,7 @@ struct drm_file *drm_file_alloc(struct drm_minor *minor) /* Get a unique identifier for fdinfo: */ file->client_id = atomic64_inc_return(&ident); - file->pid = get_pid(task_tgid(current)); + rcu_assign_pointer(file->pid, get_pid(task_tgid(current))); file->minor = minor; /* for compatibility root is always authenticated */ @@ -200,7 +200,7 @@ out_prime_destroy: drm_syncobj_release(file); if (drm_core_check_feature(dev, DRIVER_GEM)) drm_gem_release(dev, file); - put_pid(file->pid); + put_pid(rcu_access_pointer(file->pid)); kfree(file); return ERR_PTR(ret); @@ -291,7 +291,7 @@ void drm_file_free(struct drm_file *file) WARN_ON(!list_empty(&file->event_list)); - put_pid(file->pid); + put_pid(rcu_access_pointer(file->pid)); kfree(file); } @@ -505,6 +505,40 @@ int drm_release(struct inode *inode, struct file *filp) } EXPORT_SYMBOL(drm_release); +void drm_file_update_pid(struct drm_file *filp) +{ + struct drm_device *dev; + struct pid *pid, *old; + + /* + * Master nodes need to keep the original ownership in order for + * drm_master_check_perm to keep working correctly. (See comment in + * drm_auth.c.) + */ + if (filp->was_master) + return; + + pid = task_tgid(current); + + /* + * Quick unlocked check since the model is a single handover followed by + * exclusive repeated use. + */ + if (pid == rcu_access_pointer(filp->pid)) + return; + + dev = filp->minor->dev; + mutex_lock(&dev->filelist_mutex); + old = rcu_replace_pointer(filp->pid, pid, 1); + mutex_unlock(&dev->filelist_mutex); + + if (pid != old) { + get_pid(pid); + synchronize_rcu(); + put_pid(old); + } +} + /** * drm_release_noglobal - release method for DRM file * @inode: device inode diff --git a/drivers/gpu/drm/drm_framebuffer.c b/drivers/gpu/drm/drm_framebuffer.c index aff3746dedfb..ba51deb6d042 100644 --- a/drivers/gpu/drm/drm_framebuffer.c +++ b/drivers/gpu/drm/drm_framebuffer.c @@ -1222,9 +1222,9 @@ static const struct drm_debugfs_info drm_framebuffer_debugfs_list[] = { { "framebuffer", drm_framebuffer_info, 0 }, }; -void drm_framebuffer_debugfs_init(struct drm_minor *minor) +void drm_framebuffer_debugfs_init(struct drm_device *dev) { - drm_debugfs_add_files(minor->dev, drm_framebuffer_debugfs_list, + drm_debugfs_add_files(dev, drm_framebuffer_debugfs_list, ARRAY_SIZE(drm_framebuffer_debugfs_list)); } #endif diff --git a/drivers/gpu/drm/drm_gpuva_mgr.c b/drivers/gpu/drm/drm_gpuvm.c index ad99c9cfedac..02ce6baacdad 100644 --- a/drivers/gpu/drm/drm_gpuva_mgr.c +++ b/drivers/gpu/drm/drm_gpuvm.c @@ -25,7 +25,7 @@ * */ -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> #include <linux/interval_tree_generic.h> #include <linux/mm.h> @@ -33,8 +33,8 @@ /** * DOC: Overview * - * The DRM GPU VA Manager, represented by struct drm_gpuva_manager keeps track - * of a GPU's virtual address (VA) space and manages the corresponding virtual + * The DRM GPU VA Manager, represented by struct drm_gpuvm keeps track of a + * GPU's virtual address (VA) space and manages the corresponding virtual * mappings represented by &drm_gpuva objects. It also keeps track of the * mapping's backing &drm_gem_object buffers. * @@ -47,28 +47,28 @@ * The GPU VA manager internally uses a rb-tree to manage the * &drm_gpuva mappings within a GPU's virtual address space. * - * The &drm_gpuva_manager contains a special &drm_gpuva representing the + * The &drm_gpuvm structure contains a special &drm_gpuva representing the * portion of VA space reserved by the kernel. This node is initialized together * with the GPU VA manager instance and removed when the GPU VA manager is * destroyed. * - * In a typical application drivers would embed struct drm_gpuva_manager and + * In a typical application drivers would embed struct drm_gpuvm and * struct drm_gpuva within their own driver specific structures, there won't be * any memory allocations of its own nor memory allocations of &drm_gpuva * entries. * - * The data structures needed to store &drm_gpuvas within the &drm_gpuva_manager - * are contained within struct drm_gpuva already. Hence, for inserting - * &drm_gpuva entries from within dma-fence signalling critical sections it is - * enough to pre-allocate the &drm_gpuva structures. + * The data structures needed to store &drm_gpuvas within the &drm_gpuvm are + * contained within struct drm_gpuva already. Hence, for inserting &drm_gpuva + * entries from within dma-fence signalling critical sections it is enough to + * pre-allocate the &drm_gpuva structures. */ /** * DOC: Split and Merge * * Besides its capability to manage and represent a GPU VA space, the - * &drm_gpuva_manager also provides functions to let the &drm_gpuva_manager - * calculate a sequence of operations to satisfy a given map or unmap request. + * GPU VA manager also provides functions to let the &drm_gpuvm calculate a + * sequence of operations to satisfy a given map or unmap request. * * Therefore the DRM GPU VA manager provides an algorithm implementing splitting * and merging of existent GPU VA mappings with the ones that are requested to @@ -76,16 +76,16 @@ * implement Vulkan 'Sparse Memory Bindings' - drivers UAPIs often refer to this * as VM BIND. * - * Drivers can call drm_gpuva_sm_map() to receive a sequence of callbacks + * Drivers can call drm_gpuvm_sm_map() to receive a sequence of callbacks * containing map, unmap and remap operations for a given newly requested * mapping. The sequence of callbacks represents the set of operations to * execute in order to integrate the new mapping cleanly into the current state * of the GPU VA space. * * Depending on how the new GPU VA mapping intersects with the existent mappings - * of the GPU VA space the &drm_gpuva_fn_ops callbacks contain an arbitrary - * amount of unmap operations, a maximum of two remap operations and a single - * map operation. The caller might receive no callback at all if no operation is + * of the GPU VA space the &drm_gpuvm_ops callbacks contain an arbitrary amount + * of unmap operations, a maximum of two remap operations and a single map + * operation. The caller might receive no callback at all if no operation is * required, e.g. if the requested mapping already exists in the exact same way. * * The single map operation represents the original map operation requested by @@ -95,7 +95,7 @@ * &drm_gpuva to unmap is physically contiguous with the original mapping * request. Optionally, if 'keep' is set, drivers may keep the actual page table * entries for this &drm_gpuva, adding the missing page table entries only and - * update the &drm_gpuva_manager's view of things accordingly. + * update the &drm_gpuvm's view of things accordingly. * * Drivers may do the same optimization, namely delta page table updates, also * for remap operations. This is possible since &drm_gpuva_op_remap consists of @@ -106,34 +106,34 @@ * the beginning and one at the end of the new mapping, hence there is a * maximum of two remap operations. * - * Analogous to drm_gpuva_sm_map() drm_gpuva_sm_unmap() uses &drm_gpuva_fn_ops - * to call back into the driver in order to unmap a range of GPU VA space. The + * Analogous to drm_gpuvm_sm_map() drm_gpuvm_sm_unmap() uses &drm_gpuvm_ops to + * call back into the driver in order to unmap a range of GPU VA space. The * logic behind this function is way simpler though: For all existent mappings * enclosed by the given range unmap operations are created. For mappings which * are only partically located within the given range, remap operations are * created such that those mappings are split up and re-mapped partically. * - * As an alternative to drm_gpuva_sm_map() and drm_gpuva_sm_unmap(), - * drm_gpuva_sm_map_ops_create() and drm_gpuva_sm_unmap_ops_create() can be used + * As an alternative to drm_gpuvm_sm_map() and drm_gpuvm_sm_unmap(), + * drm_gpuvm_sm_map_ops_create() and drm_gpuvm_sm_unmap_ops_create() can be used * to directly obtain an instance of struct drm_gpuva_ops containing a list of * &drm_gpuva_op, which can be iterated with drm_gpuva_for_each_op(). This list * contains the &drm_gpuva_ops analogous to the callbacks one would receive when - * calling drm_gpuva_sm_map() or drm_gpuva_sm_unmap(). While this way requires + * calling drm_gpuvm_sm_map() or drm_gpuvm_sm_unmap(). While this way requires * more memory (to allocate the &drm_gpuva_ops), it provides drivers a way to * iterate the &drm_gpuva_op multiple times, e.g. once in a context where memory * allocations are possible (e.g. to allocate GPU page tables) and once in the * dma-fence signalling critical path. * - * To update the &drm_gpuva_manager's view of the GPU VA space - * drm_gpuva_insert() and drm_gpuva_remove() may be used. These functions can - * safely be used from &drm_gpuva_fn_ops callbacks originating from - * drm_gpuva_sm_map() or drm_gpuva_sm_unmap(). However, it might be more - * convenient to use the provided helper functions drm_gpuva_map(), - * drm_gpuva_remap() and drm_gpuva_unmap() instead. + * To update the &drm_gpuvm's view of the GPU VA space drm_gpuva_insert() and + * drm_gpuva_remove() may be used. These functions can safely be used from + * &drm_gpuvm_ops callbacks originating from drm_gpuvm_sm_map() or + * drm_gpuvm_sm_unmap(). However, it might be more convenient to use the + * provided helper functions drm_gpuva_map(), drm_gpuva_remap() and + * drm_gpuva_unmap() instead. * * The following diagram depicts the basic relationships of existent GPU VA * mappings, a newly requested mapping and the resulting mappings as implemented - * by drm_gpuva_sm_map() - it doesn't cover any arbitrary combinations of these. + * by drm_gpuvm_sm_map() - it doesn't cover any arbitrary combinations of these. * * 1) Requested mapping is identical. Replace it, but indicate the backing PTEs * could be kept. @@ -421,10 +421,10 @@ * // Allocates a new &drm_gpuva. * struct drm_gpuva * driver_gpuva_alloc(void); * - * // Typically drivers would embedd the &drm_gpuva_manager and &drm_gpuva + * // Typically drivers would embedd the &drm_gpuvm and &drm_gpuva * // structure in individual driver structures and lock the dma-resv with * // drm_exec or similar helpers. - * int driver_mapping_create(struct drm_gpuva_manager *mgr, + * int driver_mapping_create(struct drm_gpuvm *gpuvm, * u64 addr, u64 range, * struct drm_gem_object *obj, u64 offset) * { @@ -432,7 +432,7 @@ * struct drm_gpuva_op *op * * driver_lock_va_space(); - * ops = drm_gpuva_sm_map_ops_create(mgr, addr, range, + * ops = drm_gpuvm_sm_map_ops_create(gpuvm, addr, range, * obj, offset); * if (IS_ERR(ops)) * return PTR_ERR(ops); @@ -448,7 +448,7 @@ * // free memory and unlock * * driver_vm_map(); - * drm_gpuva_map(mgr, va, &op->map); + * drm_gpuva_map(gpuvm, va, &op->map); * drm_gpuva_link(va); * * break; @@ -504,23 +504,23 @@ * 2) Receive a callback for each &drm_gpuva_op to create a new mapping:: * * struct driver_context { - * struct drm_gpuva_manager *mgr; + * struct drm_gpuvm *gpuvm; * struct drm_gpuva *new_va; * struct drm_gpuva *prev_va; * struct drm_gpuva *next_va; * }; * - * // ops to pass to drm_gpuva_manager_init() - * static const struct drm_gpuva_fn_ops driver_gpuva_ops = { + * // ops to pass to drm_gpuvm_init() + * static const struct drm_gpuvm_ops driver_gpuvm_ops = { * .sm_step_map = driver_gpuva_map, * .sm_step_remap = driver_gpuva_remap, * .sm_step_unmap = driver_gpuva_unmap, * }; * - * // Typically drivers would embedd the &drm_gpuva_manager and &drm_gpuva + * // Typically drivers would embedd the &drm_gpuvm and &drm_gpuva * // structure in individual driver structures and lock the dma-resv with * // drm_exec or similar helpers. - * int driver_mapping_create(struct drm_gpuva_manager *mgr, + * int driver_mapping_create(struct drm_gpuvm *gpuvm, * u64 addr, u64 range, * struct drm_gem_object *obj, u64 offset) * { @@ -529,7 +529,7 @@ * struct drm_gpuva_op *op; * int ret = 0; * - * ctx.mgr = mgr; + * ctx.gpuvm = gpuvm; * * ctx.new_va = kzalloc(sizeof(*ctx.new_va), GFP_KERNEL); * ctx.prev_va = kzalloc(sizeof(*ctx.prev_va), GFP_KERNEL); @@ -540,7 +540,7 @@ * } * * driver_lock_va_space(); - * ret = drm_gpuva_sm_map(mgr, &ctx, addr, range, obj, offset); + * ret = drm_gpuvm_sm_map(gpuvm, &ctx, addr, range, obj, offset); * driver_unlock_va_space(); * * out: @@ -554,7 +554,7 @@ * { * struct driver_context *ctx = __ctx; * - * drm_gpuva_map(ctx->mgr, ctx->new_va, &op->map); + * drm_gpuva_map(ctx->vm, ctx->new_va, &op->map); * * drm_gpuva_link(ctx->new_va); * @@ -609,12 +609,12 @@ INTERVAL_TREE_DEFINE(struct drm_gpuva, rb.node, u64, rb.__subtree_last, GPUVA_START, GPUVA_LAST, static __maybe_unused, drm_gpuva_it) -static int __drm_gpuva_insert(struct drm_gpuva_manager *mgr, +static int __drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va); static void __drm_gpuva_remove(struct drm_gpuva *va); static bool -drm_gpuva_check_overflow(u64 addr, u64 range) +drm_gpuvm_check_overflow(u64 addr, u64 range) { u64 end; @@ -623,121 +623,121 @@ drm_gpuva_check_overflow(u64 addr, u64 range) } static bool -drm_gpuva_in_mm_range(struct drm_gpuva_manager *mgr, u64 addr, u64 range) +drm_gpuvm_in_mm_range(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { u64 end = addr + range; - u64 mm_start = mgr->mm_start; - u64 mm_end = mm_start + mgr->mm_range; + u64 mm_start = gpuvm->mm_start; + u64 mm_end = mm_start + gpuvm->mm_range; return addr >= mm_start && end <= mm_end; } static bool -drm_gpuva_in_kernel_node(struct drm_gpuva_manager *mgr, u64 addr, u64 range) +drm_gpuvm_in_kernel_node(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { u64 end = addr + range; - u64 kstart = mgr->kernel_alloc_node.va.addr; - u64 krange = mgr->kernel_alloc_node.va.range; + u64 kstart = gpuvm->kernel_alloc_node.va.addr; + u64 krange = gpuvm->kernel_alloc_node.va.range; u64 kend = kstart + krange; return krange && addr < kend && kstart < end; } static bool -drm_gpuva_range_valid(struct drm_gpuva_manager *mgr, +drm_gpuvm_range_valid(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { - return !drm_gpuva_check_overflow(addr, range) && - drm_gpuva_in_mm_range(mgr, addr, range) && - !drm_gpuva_in_kernel_node(mgr, addr, range); + return !drm_gpuvm_check_overflow(addr, range) && + drm_gpuvm_in_mm_range(gpuvm, addr, range) && + !drm_gpuvm_in_kernel_node(gpuvm, addr, range); } /** - * drm_gpuva_manager_init() - initialize a &drm_gpuva_manager - * @mgr: pointer to the &drm_gpuva_manager to initialize + * drm_gpuvm_init() - initialize a &drm_gpuvm + * @gpuvm: pointer to the &drm_gpuvm to initialize * @name: the name of the GPU VA space * @start_offset: the start offset of the GPU VA space * @range: the size of the GPU VA space * @reserve_offset: the start of the kernel reserved GPU VA area * @reserve_range: the size of the kernel reserved GPU VA area - * @ops: &drm_gpuva_fn_ops called on &drm_gpuva_sm_map / &drm_gpuva_sm_unmap + * @ops: &drm_gpuvm_ops called on &drm_gpuvm_sm_map / &drm_gpuvm_sm_unmap * - * The &drm_gpuva_manager must be initialized with this function before use. + * The &drm_gpuvm must be initialized with this function before use. * - * Note that @mgr must be cleared to 0 before calling this function. The given + * Note that @gpuvm must be cleared to 0 before calling this function. The given * &name is expected to be managed by the surrounding driver structures. */ void -drm_gpuva_manager_init(struct drm_gpuva_manager *mgr, - const char *name, - u64 start_offset, u64 range, - u64 reserve_offset, u64 reserve_range, - const struct drm_gpuva_fn_ops *ops) +drm_gpuvm_init(struct drm_gpuvm *gpuvm, + const char *name, + u64 start_offset, u64 range, + u64 reserve_offset, u64 reserve_range, + const struct drm_gpuvm_ops *ops) { - mgr->rb.tree = RB_ROOT_CACHED; - INIT_LIST_HEAD(&mgr->rb.list); + gpuvm->rb.tree = RB_ROOT_CACHED; + INIT_LIST_HEAD(&gpuvm->rb.list); - drm_gpuva_check_overflow(start_offset, range); - mgr->mm_start = start_offset; - mgr->mm_range = range; + drm_gpuvm_check_overflow(start_offset, range); + gpuvm->mm_start = start_offset; + gpuvm->mm_range = range; - mgr->name = name ? name : "unknown"; - mgr->ops = ops; + gpuvm->name = name ? name : "unknown"; + gpuvm->ops = ops; - memset(&mgr->kernel_alloc_node, 0, sizeof(struct drm_gpuva)); + memset(&gpuvm->kernel_alloc_node, 0, sizeof(struct drm_gpuva)); if (reserve_range) { - mgr->kernel_alloc_node.va.addr = reserve_offset; - mgr->kernel_alloc_node.va.range = reserve_range; + gpuvm->kernel_alloc_node.va.addr = reserve_offset; + gpuvm->kernel_alloc_node.va.range = reserve_range; - if (likely(!drm_gpuva_check_overflow(reserve_offset, + if (likely(!drm_gpuvm_check_overflow(reserve_offset, reserve_range))) - __drm_gpuva_insert(mgr, &mgr->kernel_alloc_node); + __drm_gpuva_insert(gpuvm, &gpuvm->kernel_alloc_node); } } -EXPORT_SYMBOL_GPL(drm_gpuva_manager_init); +EXPORT_SYMBOL_GPL(drm_gpuvm_init); /** - * drm_gpuva_manager_destroy() - cleanup a &drm_gpuva_manager - * @mgr: pointer to the &drm_gpuva_manager to clean up + * drm_gpuvm_destroy() - cleanup a &drm_gpuvm + * @gpuvm: pointer to the &drm_gpuvm to clean up * * Note that it is a bug to call this function on a manager that still * holds GPU VA mappings. */ void -drm_gpuva_manager_destroy(struct drm_gpuva_manager *mgr) +drm_gpuvm_destroy(struct drm_gpuvm *gpuvm) { - mgr->name = NULL; + gpuvm->name = NULL; - if (mgr->kernel_alloc_node.va.range) - __drm_gpuva_remove(&mgr->kernel_alloc_node); + if (gpuvm->kernel_alloc_node.va.range) + __drm_gpuva_remove(&gpuvm->kernel_alloc_node); - WARN(!RB_EMPTY_ROOT(&mgr->rb.tree.rb_root), + WARN(!RB_EMPTY_ROOT(&gpuvm->rb.tree.rb_root), "GPUVA tree is not empty, potentially leaking memory."); } -EXPORT_SYMBOL_GPL(drm_gpuva_manager_destroy); +EXPORT_SYMBOL_GPL(drm_gpuvm_destroy); static int -__drm_gpuva_insert(struct drm_gpuva_manager *mgr, +__drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va) { struct rb_node *node; struct list_head *head; - if (drm_gpuva_it_iter_first(&mgr->rb.tree, + if (drm_gpuva_it_iter_first(&gpuvm->rb.tree, GPUVA_START(va), GPUVA_LAST(va))) return -EEXIST; - va->mgr = mgr; + va->vm = gpuvm; - drm_gpuva_it_insert(va, &mgr->rb.tree); + drm_gpuva_it_insert(va, &gpuvm->rb.tree); node = rb_prev(&va->rb.node); if (node) head = &(to_drm_gpuva(node))->rb.entry; else - head = &mgr->rb.list; + head = &gpuvm->rb.list; list_add(&va->rb.entry, head); @@ -746,36 +746,36 @@ __drm_gpuva_insert(struct drm_gpuva_manager *mgr, /** * drm_gpuva_insert() - insert a &drm_gpuva - * @mgr: the &drm_gpuva_manager to insert the &drm_gpuva in + * @gpuvm: the &drm_gpuvm to insert the &drm_gpuva in * @va: the &drm_gpuva to insert * * Insert a &drm_gpuva with a given address and range into a - * &drm_gpuva_manager. + * &drm_gpuvm. * * It is safe to use this function using the safe versions of iterating the GPU - * VA space, such as drm_gpuva_for_each_va_safe() and - * drm_gpuva_for_each_va_range_safe(). + * VA space, such as drm_gpuvm_for_each_va_safe() and + * drm_gpuvm_for_each_va_range_safe(). * * Returns: 0 on success, negative error code on failure. */ int -drm_gpuva_insert(struct drm_gpuva_manager *mgr, +drm_gpuva_insert(struct drm_gpuvm *gpuvm, struct drm_gpuva *va) { u64 addr = va->va.addr; u64 range = va->va.range; - if (unlikely(!drm_gpuva_range_valid(mgr, addr, range))) + if (unlikely(!drm_gpuvm_range_valid(gpuvm, addr, range))) return -EINVAL; - return __drm_gpuva_insert(mgr, va); + return __drm_gpuva_insert(gpuvm, va); } EXPORT_SYMBOL_GPL(drm_gpuva_insert); static void __drm_gpuva_remove(struct drm_gpuva *va) { - drm_gpuva_it_remove(va, &va->mgr->rb.tree); + drm_gpuva_it_remove(va, &va->vm->rb.tree); list_del_init(&va->rb.entry); } @@ -786,15 +786,15 @@ __drm_gpuva_remove(struct drm_gpuva *va) * This removes the given &va from the underlaying tree. * * It is safe to use this function using the safe versions of iterating the GPU - * VA space, such as drm_gpuva_for_each_va_safe() and - * drm_gpuva_for_each_va_range_safe(). + * VA space, such as drm_gpuvm_for_each_va_safe() and + * drm_gpuvm_for_each_va_range_safe(). */ void drm_gpuva_remove(struct drm_gpuva *va) { - struct drm_gpuva_manager *mgr = va->mgr; + struct drm_gpuvm *gpuvm = va->vm; - if (unlikely(va == &mgr->kernel_alloc_node)) { + if (unlikely(va == &gpuvm->kernel_alloc_node)) { WARN(1, "Can't destroy kernel reserved node.\n"); return; } @@ -853,37 +853,37 @@ EXPORT_SYMBOL_GPL(drm_gpuva_unlink); /** * drm_gpuva_find_first() - find the first &drm_gpuva in the given range - * @mgr: the &drm_gpuva_manager to search in + * @gpuvm: the &drm_gpuvm to search in * @addr: the &drm_gpuvas address * @range: the &drm_gpuvas range * * Returns: the first &drm_gpuva within the given range */ struct drm_gpuva * -drm_gpuva_find_first(struct drm_gpuva_manager *mgr, +drm_gpuva_find_first(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { u64 last = addr + range - 1; - return drm_gpuva_it_iter_first(&mgr->rb.tree, addr, last); + return drm_gpuva_it_iter_first(&gpuvm->rb.tree, addr, last); } EXPORT_SYMBOL_GPL(drm_gpuva_find_first); /** * drm_gpuva_find() - find a &drm_gpuva - * @mgr: the &drm_gpuva_manager to search in + * @gpuvm: the &drm_gpuvm to search in * @addr: the &drm_gpuvas address * @range: the &drm_gpuvas range * * Returns: the &drm_gpuva at a given &addr and with a given &range */ struct drm_gpuva * -drm_gpuva_find(struct drm_gpuva_manager *mgr, +drm_gpuva_find(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { struct drm_gpuva *va; - va = drm_gpuva_find_first(mgr, addr, range); + va = drm_gpuva_find_first(gpuvm, addr, range); if (!va) goto out; @@ -900,7 +900,7 @@ EXPORT_SYMBOL_GPL(drm_gpuva_find); /** * drm_gpuva_find_prev() - find the &drm_gpuva before the given address - * @mgr: the &drm_gpuva_manager to search in + * @gpuvm: the &drm_gpuvm to search in * @start: the given GPU VA's start address * * Find the adjacent &drm_gpuva before the GPU VA with given &start address. @@ -911,18 +911,18 @@ EXPORT_SYMBOL_GPL(drm_gpuva_find); * Returns: a pointer to the found &drm_gpuva or NULL if none was found */ struct drm_gpuva * -drm_gpuva_find_prev(struct drm_gpuva_manager *mgr, u64 start) +drm_gpuva_find_prev(struct drm_gpuvm *gpuvm, u64 start) { - if (!drm_gpuva_range_valid(mgr, start - 1, 1)) + if (!drm_gpuvm_range_valid(gpuvm, start - 1, 1)) return NULL; - return drm_gpuva_it_iter_first(&mgr->rb.tree, start - 1, start); + return drm_gpuva_it_iter_first(&gpuvm->rb.tree, start - 1, start); } EXPORT_SYMBOL_GPL(drm_gpuva_find_prev); /** * drm_gpuva_find_next() - find the &drm_gpuva after the given address - * @mgr: the &drm_gpuva_manager to search in + * @gpuvm: the &drm_gpuvm to search in * @end: the given GPU VA's end address * * Find the adjacent &drm_gpuva after the GPU VA with given &end address. @@ -933,47 +933,47 @@ EXPORT_SYMBOL_GPL(drm_gpuva_find_prev); * Returns: a pointer to the found &drm_gpuva or NULL if none was found */ struct drm_gpuva * -drm_gpuva_find_next(struct drm_gpuva_manager *mgr, u64 end) +drm_gpuva_find_next(struct drm_gpuvm *gpuvm, u64 end) { - if (!drm_gpuva_range_valid(mgr, end, 1)) + if (!drm_gpuvm_range_valid(gpuvm, end, 1)) return NULL; - return drm_gpuva_it_iter_first(&mgr->rb.tree, end, end + 1); + return drm_gpuva_it_iter_first(&gpuvm->rb.tree, end, end + 1); } EXPORT_SYMBOL_GPL(drm_gpuva_find_next); /** - * drm_gpuva_interval_empty() - indicate whether a given interval of the VA space + * drm_gpuvm_interval_empty() - indicate whether a given interval of the VA space * is empty - * @mgr: the &drm_gpuva_manager to check the range for + * @gpuvm: the &drm_gpuvm to check the range for * @addr: the start address of the range * @range: the range of the interval * * Returns: true if the interval is empty, false otherwise */ bool -drm_gpuva_interval_empty(struct drm_gpuva_manager *mgr, u64 addr, u64 range) +drm_gpuvm_interval_empty(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { - return !drm_gpuva_find_first(mgr, addr, range); + return !drm_gpuva_find_first(gpuvm, addr, range); } -EXPORT_SYMBOL_GPL(drm_gpuva_interval_empty); +EXPORT_SYMBOL_GPL(drm_gpuvm_interval_empty); /** * drm_gpuva_map() - helper to insert a &drm_gpuva according to a * &drm_gpuva_op_map - * @mgr: the &drm_gpuva_manager + * @gpuvm: the &drm_gpuvm * @va: the &drm_gpuva to insert * @op: the &drm_gpuva_op_map to initialize @va with * - * Initializes the @va from the @op and inserts it into the given @mgr. + * Initializes the @va from the @op and inserts it into the given @gpuvm. */ void -drm_gpuva_map(struct drm_gpuva_manager *mgr, +drm_gpuva_map(struct drm_gpuvm *gpuvm, struct drm_gpuva *va, struct drm_gpuva_op_map *op) { drm_gpuva_init_from_op(va, op); - drm_gpuva_insert(mgr, va); + drm_gpuva_insert(gpuvm, va); } EXPORT_SYMBOL_GPL(drm_gpuva_map); @@ -993,18 +993,18 @@ drm_gpuva_remap(struct drm_gpuva *prev, struct drm_gpuva_op_remap *op) { struct drm_gpuva *curr = op->unmap->va; - struct drm_gpuva_manager *mgr = curr->mgr; + struct drm_gpuvm *gpuvm = curr->vm; drm_gpuva_remove(curr); if (op->prev) { drm_gpuva_init_from_op(prev, op->prev); - drm_gpuva_insert(mgr, prev); + drm_gpuva_insert(gpuvm, prev); } if (op->next) { drm_gpuva_init_from_op(next, op->next); - drm_gpuva_insert(mgr, next); + drm_gpuva_insert(gpuvm, next); } } EXPORT_SYMBOL_GPL(drm_gpuva_remap); @@ -1024,7 +1024,7 @@ drm_gpuva_unmap(struct drm_gpuva_op_unmap *op) EXPORT_SYMBOL_GPL(drm_gpuva_unmap); static int -op_map_cb(const struct drm_gpuva_fn_ops *fn, void *priv, +op_map_cb(const struct drm_gpuvm_ops *fn, void *priv, u64 addr, u64 range, struct drm_gem_object *obj, u64 offset) { @@ -1040,7 +1040,7 @@ op_map_cb(const struct drm_gpuva_fn_ops *fn, void *priv, } static int -op_remap_cb(const struct drm_gpuva_fn_ops *fn, void *priv, +op_remap_cb(const struct drm_gpuvm_ops *fn, void *priv, struct drm_gpuva_op_map *prev, struct drm_gpuva_op_map *next, struct drm_gpuva_op_unmap *unmap) @@ -1058,7 +1058,7 @@ op_remap_cb(const struct drm_gpuva_fn_ops *fn, void *priv, } static int -op_unmap_cb(const struct drm_gpuva_fn_ops *fn, void *priv, +op_unmap_cb(const struct drm_gpuvm_ops *fn, void *priv, struct drm_gpuva *va, bool merge) { struct drm_gpuva_op op = {}; @@ -1071,8 +1071,8 @@ op_unmap_cb(const struct drm_gpuva_fn_ops *fn, void *priv, } static int -__drm_gpuva_sm_map(struct drm_gpuva_manager *mgr, - const struct drm_gpuva_fn_ops *ops, void *priv, +__drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, + const struct drm_gpuvm_ops *ops, void *priv, u64 req_addr, u64 req_range, struct drm_gem_object *req_obj, u64 req_offset) { @@ -1080,10 +1080,10 @@ __drm_gpuva_sm_map(struct drm_gpuva_manager *mgr, u64 req_end = req_addr + req_range; int ret; - if (unlikely(!drm_gpuva_range_valid(mgr, req_addr, req_range))) + if (unlikely(!drm_gpuvm_range_valid(gpuvm, req_addr, req_range))) return -EINVAL; - drm_gpuva_for_each_va_range_safe(va, next, mgr, req_addr, req_end) { + drm_gpuvm_for_each_va_range_safe(va, next, gpuvm, req_addr, req_end) { struct drm_gem_object *obj = va->gem.obj; u64 offset = va->gem.offset; u64 addr = va->va.addr; @@ -1213,18 +1213,18 @@ __drm_gpuva_sm_map(struct drm_gpuva_manager *mgr, } static int -__drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, - const struct drm_gpuva_fn_ops *ops, void *priv, +__drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, + const struct drm_gpuvm_ops *ops, void *priv, u64 req_addr, u64 req_range) { struct drm_gpuva *va, *next; u64 req_end = req_addr + req_range; int ret; - if (unlikely(!drm_gpuva_range_valid(mgr, req_addr, req_range))) + if (unlikely(!drm_gpuvm_range_valid(gpuvm, req_addr, req_range))) return -EINVAL; - drm_gpuva_for_each_va_range_safe(va, next, mgr, req_addr, req_end) { + drm_gpuvm_for_each_va_range_safe(va, next, gpuvm, req_addr, req_end) { struct drm_gpuva_op_map prev = {}, next = {}; bool prev_split = false, next_split = false; struct drm_gem_object *obj = va->gem.obj; @@ -1271,8 +1271,8 @@ __drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, } /** - * drm_gpuva_sm_map() - creates the &drm_gpuva_op split/merge steps - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * drm_gpuvm_sm_map() - creates the &drm_gpuva_op split/merge steps + * @gpuvm: the &drm_gpuvm representing the GPU VA space * @req_addr: the start address of the new mapping * @req_range: the range of the new mapping * @req_obj: the &drm_gem_object to map @@ -1280,15 +1280,15 @@ __drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, * @priv: pointer to a driver private data structure * * This function iterates the given range of the GPU VA space. It utilizes the - * &drm_gpuva_fn_ops to call back into the driver providing the split and merge + * &drm_gpuvm_ops to call back into the driver providing the split and merge * steps. * * Drivers may use these callbacks to update the GPU VA space right away within * the callback. In case the driver decides to copy and store the operations for - * later processing neither this function nor &drm_gpuva_sm_unmap is allowed to - * be called before the &drm_gpuva_manager's view of the GPU VA space was + * later processing neither this function nor &drm_gpuvm_sm_unmap is allowed to + * be called before the &drm_gpuvm's view of the GPU VA space was * updated with the previous set of operations. To update the - * &drm_gpuva_manager's view of the GPU VA space drm_gpuva_insert(), + * &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(), * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be * used. * @@ -1303,39 +1303,39 @@ __drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, * Returns: 0 on success or a negative error code */ int -drm_gpuva_sm_map(struct drm_gpuva_manager *mgr, void *priv, +drm_gpuvm_sm_map(struct drm_gpuvm *gpuvm, void *priv, u64 req_addr, u64 req_range, struct drm_gem_object *req_obj, u64 req_offset) { - const struct drm_gpuva_fn_ops *ops = mgr->ops; + const struct drm_gpuvm_ops *ops = gpuvm->ops; if (unlikely(!(ops && ops->sm_step_map && ops->sm_step_remap && ops->sm_step_unmap))) return -EINVAL; - return __drm_gpuva_sm_map(mgr, ops, priv, + return __drm_gpuvm_sm_map(gpuvm, ops, priv, req_addr, req_range, req_obj, req_offset); } -EXPORT_SYMBOL_GPL(drm_gpuva_sm_map); +EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map); /** - * drm_gpuva_sm_unmap() - creates the &drm_gpuva_ops to split on unmap - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * drm_gpuvm_sm_unmap() - creates the &drm_gpuva_ops to split on unmap + * @gpuvm: the &drm_gpuvm representing the GPU VA space * @priv: pointer to a driver private data structure * @req_addr: the start address of the range to unmap * @req_range: the range of the mappings to unmap * * This function iterates the given range of the GPU VA space. It utilizes the - * &drm_gpuva_fn_ops to call back into the driver providing the operations to + * &drm_gpuvm_ops to call back into the driver providing the operations to * unmap and, if required, split existent mappings. * * Drivers may use these callbacks to update the GPU VA space right away within * the callback. In case the driver decides to copy and store the operations for - * later processing neither this function nor &drm_gpuva_sm_map is allowed to be - * called before the &drm_gpuva_manager's view of the GPU VA space was updated - * with the previous set of operations. To update the &drm_gpuva_manager's view + * later processing neither this function nor &drm_gpuvm_sm_map is allowed to be + * called before the &drm_gpuvm's view of the GPU VA space was updated + * with the previous set of operations. To update the &drm_gpuvm's view * of the GPU VA space drm_gpuva_insert(), drm_gpuva_destroy_locked() and/or * drm_gpuva_destroy_unlocked() should be used. * @@ -1348,24 +1348,24 @@ EXPORT_SYMBOL_GPL(drm_gpuva_sm_map); * Returns: 0 on success or a negative error code */ int -drm_gpuva_sm_unmap(struct drm_gpuva_manager *mgr, void *priv, +drm_gpuvm_sm_unmap(struct drm_gpuvm *gpuvm, void *priv, u64 req_addr, u64 req_range) { - const struct drm_gpuva_fn_ops *ops = mgr->ops; + const struct drm_gpuvm_ops *ops = gpuvm->ops; if (unlikely(!(ops && ops->sm_step_remap && ops->sm_step_unmap))) return -EINVAL; - return __drm_gpuva_sm_unmap(mgr, ops, priv, + return __drm_gpuvm_sm_unmap(gpuvm, ops, priv, req_addr, req_range); } -EXPORT_SYMBOL_GPL(drm_gpuva_sm_unmap); +EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap); static struct drm_gpuva_op * -gpuva_op_alloc(struct drm_gpuva_manager *mgr) +gpuva_op_alloc(struct drm_gpuvm *gpuvm) { - const struct drm_gpuva_fn_ops *fn = mgr->ops; + const struct drm_gpuvm_ops *fn = gpuvm->ops; struct drm_gpuva_op *op; if (fn && fn->op_alloc) @@ -1380,10 +1380,10 @@ gpuva_op_alloc(struct drm_gpuva_manager *mgr) } static void -gpuva_op_free(struct drm_gpuva_manager *mgr, +gpuva_op_free(struct drm_gpuvm *gpuvm, struct drm_gpuva_op *op) { - const struct drm_gpuva_fn_ops *fn = mgr->ops; + const struct drm_gpuvm_ops *fn = gpuvm->ops; if (fn && fn->op_free) fn->op_free(op); @@ -1396,14 +1396,14 @@ drm_gpuva_sm_step(struct drm_gpuva_op *__op, void *priv) { struct { - struct drm_gpuva_manager *mgr; + struct drm_gpuvm *vm; struct drm_gpuva_ops *ops; } *args = priv; - struct drm_gpuva_manager *mgr = args->mgr; + struct drm_gpuvm *gpuvm = args->vm; struct drm_gpuva_ops *ops = args->ops; struct drm_gpuva_op *op; - op = gpuva_op_alloc(mgr); + op = gpuva_op_alloc(gpuvm); if (unlikely(!op)) goto err; @@ -1442,20 +1442,20 @@ err_free_unmap: err_free_prev: kfree(op->remap.prev); err_free_op: - gpuva_op_free(mgr, op); + gpuva_op_free(gpuvm, op); err: return -ENOMEM; } -static const struct drm_gpuva_fn_ops gpuva_list_ops = { +static const struct drm_gpuvm_ops gpuvm_list_ops = { .sm_step_map = drm_gpuva_sm_step, .sm_step_remap = drm_gpuva_sm_step, .sm_step_unmap = drm_gpuva_sm_step, }; /** - * drm_gpuva_sm_map_ops_create() - creates the &drm_gpuva_ops to split and merge - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * drm_gpuvm_sm_map_ops_create() - creates the &drm_gpuva_ops to split and merge + * @gpuvm: the &drm_gpuvm representing the GPU VA space * @req_addr: the start address of the new mapping * @req_range: the range of the new mapping * @req_obj: the &drm_gem_object to map @@ -1474,9 +1474,9 @@ static const struct drm_gpuva_fn_ops gpuva_list_ops = { * map operation requested by the caller. * * Note that before calling this function again with another mapping request it - * is necessary to update the &drm_gpuva_manager's view of the GPU VA space. The + * is necessary to update the &drm_gpuvm's view of the GPU VA space. The * previously obtained operations must be either processed or abandoned. To - * update the &drm_gpuva_manager's view of the GPU VA space drm_gpuva_insert(), + * update the &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(), * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be * used. * @@ -1486,13 +1486,13 @@ static const struct drm_gpuva_fn_ops gpuva_list_ops = { * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure */ struct drm_gpuva_ops * -drm_gpuva_sm_map_ops_create(struct drm_gpuva_manager *mgr, +drm_gpuvm_sm_map_ops_create(struct drm_gpuvm *gpuvm, u64 req_addr, u64 req_range, struct drm_gem_object *req_obj, u64 req_offset) { struct drm_gpuva_ops *ops; struct { - struct drm_gpuva_manager *mgr; + struct drm_gpuvm *vm; struct drm_gpuva_ops *ops; } args; int ret; @@ -1503,10 +1503,10 @@ drm_gpuva_sm_map_ops_create(struct drm_gpuva_manager *mgr, INIT_LIST_HEAD(&ops->list); - args.mgr = mgr; + args.vm = gpuvm; args.ops = ops; - ret = __drm_gpuva_sm_map(mgr, &gpuva_list_ops, &args, + ret = __drm_gpuvm_sm_map(gpuvm, &gpuvm_list_ops, &args, req_addr, req_range, req_obj, req_offset); if (ret) @@ -1515,15 +1515,15 @@ drm_gpuva_sm_map_ops_create(struct drm_gpuva_manager *mgr, return ops; err_free_ops: - drm_gpuva_ops_free(mgr, ops); + drm_gpuva_ops_free(gpuvm, ops); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(drm_gpuva_sm_map_ops_create); +EXPORT_SYMBOL_GPL(drm_gpuvm_sm_map_ops_create); /** - * drm_gpuva_sm_unmap_ops_create() - creates the &drm_gpuva_ops to split on + * drm_gpuvm_sm_unmap_ops_create() - creates the &drm_gpuva_ops to split on * unmap - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * @gpuvm: the &drm_gpuvm representing the GPU VA space * @req_addr: the start address of the range to unmap * @req_range: the range of the mappings to unmap * @@ -1538,9 +1538,9 @@ EXPORT_SYMBOL_GPL(drm_gpuva_sm_map_ops_create); * remap operations. * * Note that before calling this function again with another range to unmap it - * is necessary to update the &drm_gpuva_manager's view of the GPU VA space. The + * is necessary to update the &drm_gpuvm's view of the GPU VA space. The * previously obtained operations must be processed or abandoned. To update the - * &drm_gpuva_manager's view of the GPU VA space drm_gpuva_insert(), + * &drm_gpuvm's view of the GPU VA space drm_gpuva_insert(), * drm_gpuva_destroy_locked() and/or drm_gpuva_destroy_unlocked() should be * used. * @@ -1550,12 +1550,12 @@ EXPORT_SYMBOL_GPL(drm_gpuva_sm_map_ops_create); * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure */ struct drm_gpuva_ops * -drm_gpuva_sm_unmap_ops_create(struct drm_gpuva_manager *mgr, +drm_gpuvm_sm_unmap_ops_create(struct drm_gpuvm *gpuvm, u64 req_addr, u64 req_range) { struct drm_gpuva_ops *ops; struct { - struct drm_gpuva_manager *mgr; + struct drm_gpuvm *vm; struct drm_gpuva_ops *ops; } args; int ret; @@ -1566,10 +1566,10 @@ drm_gpuva_sm_unmap_ops_create(struct drm_gpuva_manager *mgr, INIT_LIST_HEAD(&ops->list); - args.mgr = mgr; + args.vm = gpuvm; args.ops = ops; - ret = __drm_gpuva_sm_unmap(mgr, &gpuva_list_ops, &args, + ret = __drm_gpuvm_sm_unmap(gpuvm, &gpuvm_list_ops, &args, req_addr, req_range); if (ret) goto err_free_ops; @@ -1577,14 +1577,14 @@ drm_gpuva_sm_unmap_ops_create(struct drm_gpuva_manager *mgr, return ops; err_free_ops: - drm_gpuva_ops_free(mgr, ops); + drm_gpuva_ops_free(gpuvm, ops); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(drm_gpuva_sm_unmap_ops_create); +EXPORT_SYMBOL_GPL(drm_gpuvm_sm_unmap_ops_create); /** - * drm_gpuva_prefetch_ops_create() - creates the &drm_gpuva_ops to prefetch - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * drm_gpuvm_prefetch_ops_create() - creates the &drm_gpuva_ops to prefetch + * @gpuvm: the &drm_gpuvm representing the GPU VA space * @addr: the start address of the range to prefetch * @range: the range of the mappings to prefetch * @@ -1601,7 +1601,7 @@ EXPORT_SYMBOL_GPL(drm_gpuva_sm_unmap_ops_create); * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure */ struct drm_gpuva_ops * -drm_gpuva_prefetch_ops_create(struct drm_gpuva_manager *mgr, +drm_gpuvm_prefetch_ops_create(struct drm_gpuvm *gpuvm, u64 addr, u64 range) { struct drm_gpuva_ops *ops; @@ -1616,8 +1616,8 @@ drm_gpuva_prefetch_ops_create(struct drm_gpuva_manager *mgr, INIT_LIST_HEAD(&ops->list); - drm_gpuva_for_each_va_range(va, mgr, addr, end) { - op = gpuva_op_alloc(mgr); + drm_gpuvm_for_each_va_range(va, gpuvm, addr, end) { + op = gpuva_op_alloc(gpuvm); if (!op) { ret = -ENOMEM; goto err_free_ops; @@ -1631,14 +1631,14 @@ drm_gpuva_prefetch_ops_create(struct drm_gpuva_manager *mgr, return ops; err_free_ops: - drm_gpuva_ops_free(mgr, ops); + drm_gpuva_ops_free(gpuvm, ops); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(drm_gpuva_prefetch_ops_create); +EXPORT_SYMBOL_GPL(drm_gpuvm_prefetch_ops_create); /** - * drm_gpuva_gem_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM - * @mgr: the &drm_gpuva_manager representing the GPU VA space + * drm_gpuvm_gem_unmap_ops_create() - creates the &drm_gpuva_ops to unmap a GEM + * @gpuvm: the &drm_gpuvm representing the GPU VA space * @obj: the &drm_gem_object to unmap * * This function creates a list of operations to perform unmapping for every @@ -1656,7 +1656,7 @@ EXPORT_SYMBOL_GPL(drm_gpuva_prefetch_ops_create); * Returns: a pointer to the &drm_gpuva_ops on success, an ERR_PTR on failure */ struct drm_gpuva_ops * -drm_gpuva_gem_unmap_ops_create(struct drm_gpuva_manager *mgr, +drm_gpuvm_gem_unmap_ops_create(struct drm_gpuvm *gpuvm, struct drm_gem_object *obj) { struct drm_gpuva_ops *ops; @@ -1673,7 +1673,7 @@ drm_gpuva_gem_unmap_ops_create(struct drm_gpuva_manager *mgr, INIT_LIST_HEAD(&ops->list); drm_gem_for_each_gpuva(va, obj) { - op = gpuva_op_alloc(mgr); + op = gpuva_op_alloc(gpuvm); if (!op) { ret = -ENOMEM; goto err_free_ops; @@ -1687,21 +1687,21 @@ drm_gpuva_gem_unmap_ops_create(struct drm_gpuva_manager *mgr, return ops; err_free_ops: - drm_gpuva_ops_free(mgr, ops); + drm_gpuva_ops_free(gpuvm, ops); return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(drm_gpuva_gem_unmap_ops_create); +EXPORT_SYMBOL_GPL(drm_gpuvm_gem_unmap_ops_create); /** * drm_gpuva_ops_free() - free the given &drm_gpuva_ops - * @mgr: the &drm_gpuva_manager the ops were created for + * @gpuvm: the &drm_gpuvm the ops were created for * @ops: the &drm_gpuva_ops to free * * Frees the given &drm_gpuva_ops structure including all the ops associated * with it. */ void -drm_gpuva_ops_free(struct drm_gpuva_manager *mgr, +drm_gpuva_ops_free(struct drm_gpuvm *gpuvm, struct drm_gpuva_ops *ops) { struct drm_gpuva_op *op, *next; @@ -1715,9 +1715,12 @@ drm_gpuva_ops_free(struct drm_gpuva_manager *mgr, kfree(op->remap.unmap); } - gpuva_op_free(mgr, op); + gpuva_op_free(gpuvm, op); } kfree(ops); } EXPORT_SYMBOL_GPL(drm_gpuva_ops_free); + +MODULE_DESCRIPTION("DRM GPUVM"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/drm_internal.h b/drivers/gpu/drm/drm_internal.h index 4053cf8105ce..8462b657c375 100644 --- a/drivers/gpu/drm/drm_internal.h +++ b/drivers/gpu/drm/drm_internal.h @@ -182,27 +182,32 @@ void drm_gem_vunmap(struct drm_gem_object *obj, struct iosys_map *map); /* drm_debugfs.c drm_debugfs_crc.c */ #if defined(CONFIG_DEBUG_FS) -int drm_debugfs_init(struct drm_minor *minor, int minor_id, - struct dentry *root); -void drm_debugfs_cleanup(struct drm_minor *minor); -void drm_debugfs_late_register(struct drm_device *dev); +void drm_debugfs_dev_fini(struct drm_device *dev); +void drm_debugfs_dev_register(struct drm_device *dev); +int drm_debugfs_register(struct drm_minor *minor, int minor_id, + struct dentry *root); +void drm_debugfs_unregister(struct drm_minor *minor); void drm_debugfs_connector_add(struct drm_connector *connector); void drm_debugfs_connector_remove(struct drm_connector *connector); void drm_debugfs_crtc_add(struct drm_crtc *crtc); void drm_debugfs_crtc_remove(struct drm_crtc *crtc); void drm_debugfs_crtc_crc_add(struct drm_crtc *crtc); #else -static inline int drm_debugfs_init(struct drm_minor *minor, int minor_id, - struct dentry *root) +static inline void drm_debugfs_dev_fini(struct drm_device *dev) { - return 0; } -static inline void drm_debugfs_cleanup(struct drm_minor *minor) +static inline void drm_debugfs_dev_register(struct drm_device *dev) { } -static inline void drm_debugfs_late_register(struct drm_device *dev) +static inline int drm_debugfs_register(struct drm_minor *minor, int minor_id, + struct dentry *root) +{ + return 0; +} + +static inline void drm_debugfs_unregister(struct drm_minor *minor) { } @@ -261,4 +266,4 @@ int drm_syncobj_query_ioctl(struct drm_device *dev, void *data, /* drm_framebuffer.c */ void drm_framebuffer_print_info(struct drm_printer *p, unsigned int indent, const struct drm_framebuffer *fb); -void drm_framebuffer_debugfs_init(struct drm_minor *minor); +void drm_framebuffer_debugfs_init(struct drm_device *dev); diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c index f03ffbacfe9b..77590b0f38fa 100644 --- a/drivers/gpu/drm/drm_ioctl.c +++ b/drivers/gpu/drm/drm_ioctl.c @@ -776,6 +776,9 @@ long drm_ioctl_kernel(struct file *file, drm_ioctl_t *func, void *kdata, struct drm_device *dev = file_priv->minor->dev; int retcode; + /* Update drm_file owner if fd was passed along. */ + drm_file_update_pid(file_priv); + if (drm_dev_is_unplugged(dev)) return -ENODEV; diff --git a/drivers/gpu/drm/drm_mode_config.c b/drivers/gpu/drm/drm_mode_config.c index 87eb591fe9b5..8525ef851540 100644 --- a/drivers/gpu/drm/drm_mode_config.c +++ b/drivers/gpu/drm/drm_mode_config.c @@ -54,8 +54,6 @@ int drm_modeset_register_all(struct drm_device *dev) if (ret) goto err_connector; - drm_debugfs_late_register(dev); - return 0; err_connector: diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c index a395f93449f3..ab6c0c6cd0e2 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -356,9 +356,17 @@ static void fsl_dcu_drm_remove(struct platform_device *pdev) clk_unregister(fsl_dev->pix_clk); } +static void fsl_dcu_drm_shutdown(struct platform_device *pdev) +{ + struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); + + drm_atomic_helper_shutdown(fsl_dev->drm); +} + static struct platform_driver fsl_dcu_drm_platform_driver = { .probe = fsl_dcu_drm_probe, .remove_new = fsl_dcu_drm_remove, + .shutdown = fsl_dcu_drm_shutdown, .driver = { .name = "fsl-dcu", .pm = &fsl_dcu_drm_pm_ops, diff --git a/drivers/gpu/drm/gma500/gma_display.h b/drivers/gpu/drm/gma500/gma_display.h index c8b611a2f6c6..1188a8d61caf 100644 --- a/drivers/gpu/drm/gma500/gma_display.h +++ b/drivers/gpu/drm/gma500/gma_display.h @@ -81,7 +81,6 @@ extern void gma_encoder_destroy(struct drm_encoder *encoder); /* Common clock related functions */ extern const struct gma_limit_t *gma_limit(struct drm_crtc *crtc, int refclk); -extern void gma_clock(int refclk, struct gma_clock_t *clock); extern bool gma_pll_is_valid(struct drm_crtc *crtc, const struct gma_limit_t *limit, struct gma_clock_t *clock); diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h index f7f709df99b4..c5edfa4aa4cc 100644 --- a/drivers/gpu/drm/gma500/psb_drv.h +++ b/drivers/gpu/drm/gma500/psb_drv.h @@ -161,14 +161,6 @@ #define PSB_NUM_VBLANKS 2 - -#define PSB_2D_SIZE (256*1024*1024) -#define PSB_MAX_RELOC_PAGES 1024 - -#define PSB_LOW_REG_OFFS 0x0204 -#define PSB_HIGH_REG_OFFS 0x0600 - -#define PSB_NUM_VBLANKS 2 #define PSB_WATCHDOG_DELAY (HZ * 2) #define PSB_LID_DELAY (HZ / 10) @@ -424,6 +416,7 @@ struct drm_psb_private { uint32_t pipestat[PSB_NUM_PIPE]; spinlock_t irqmask_lock; + bool irq_enabled; /* Power */ bool pm_initialized; diff --git a/drivers/gpu/drm/gma500/psb_intel_drv.h b/drivers/gpu/drm/gma500/psb_intel_drv.h index 0bb85494e3da..c111e933e1ed 100644 --- a/drivers/gpu/drm/gma500/psb_intel_drv.h +++ b/drivers/gpu/drm/gma500/psb_intel_drv.h @@ -186,19 +186,13 @@ extern bool psb_intel_ddc_probe(struct i2c_adapter *adapter); extern void psb_intel_crtc_init(struct drm_device *dev, int pipe, struct psb_intel_mode_device *mode_dev); -extern void psb_intel_crt_init(struct drm_device *dev); extern bool psb_intel_sdvo_init(struct drm_device *dev, int output_device); -extern void psb_intel_dvo_init(struct drm_device *dev); -extern void psb_intel_tv_init(struct drm_device *dev); extern void psb_intel_lvds_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); extern void psb_intel_lvds_set_brightness(struct drm_device *dev, int level); extern void oaktrail_lvds_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev); -extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev); struct gma_i2c_chan *oaktrail_lvds_i2c_init(struct drm_device *dev); -extern void mid_dsi_init(struct drm_device *dev, - struct psb_intel_mode_device *mode_dev, int dsi_num); extern struct drm_encoder *gma_best_encoder(struct drm_connector *connector); extern void gma_connector_attach_encoder(struct gma_connector *connector, @@ -214,11 +208,6 @@ extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, struct drm_crtc *crtc); extern struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe); -extern struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev, - int sdvoB); -extern int intelfb_probe(struct drm_device *dev); -extern int intelfb_remove(struct drm_device *dev, - struct drm_framebuffer *fb); extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); @@ -242,9 +231,6 @@ extern void cdv_intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode); -extern void psb_intel_attach_force_audio_property(struct drm_connector *connector); -extern void psb_intel_attach_broadcast_rgb_property(struct drm_connector *connector); - extern int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val); extern int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val); extern void cdv_sb_reset(struct drm_device *dev); diff --git a/drivers/gpu/drm/gma500/psb_irq.c b/drivers/gpu/drm/gma500/psb_irq.c index 343c51250207..7bbb79b0497d 100644 --- a/drivers/gpu/drm/gma500/psb_irq.c +++ b/drivers/gpu/drm/gma500/psb_irq.c @@ -327,6 +327,8 @@ int gma_irq_install(struct drm_device *dev) gma_irq_postinstall(dev); + dev_priv->irq_enabled = true; + return 0; } @@ -337,6 +339,9 @@ void gma_irq_uninstall(struct drm_device *dev) unsigned long irqflags; unsigned int i; + if (!dev_priv->irq_enabled) + return; + spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags); if (dev_priv->ops->hotplug_enable) diff --git a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c index 8a98fa276e8a..57c21ec452b7 100644 --- a/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/hibmc/hibmc_drm_drv.c @@ -357,6 +357,11 @@ static void hibmc_pci_remove(struct pci_dev *pdev) hibmc_unload(dev); } +static void hibmc_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + static const struct pci_device_id hibmc_pci_table[] = { { PCI_VDEVICE(HUAWEI, 0x1711) }, {0,} @@ -367,6 +372,7 @@ static struct pci_driver hibmc_pci_driver = { .id_table = hibmc_pci_table, .probe = hibmc_pci_probe, .remove = hibmc_pci_remove, + .shutdown = hibmc_pci_shutdown, .driver.pm = &hibmc_pm_ops, }; diff --git a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c index e8c77bcc6dae..75292a2f4644 100644 --- a/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c +++ b/drivers/gpu/drm/hisilicon/kirin/kirin_drm_drv.c @@ -206,6 +206,7 @@ err_mode_config_cleanup: static int kirin_drm_kms_cleanup(struct drm_device *dev) { drm_kms_helper_poll_fini(dev); + drm_atomic_helper_shutdown(dev); kirin_drm_private_cleanup(dev); drm_mode_config_cleanup(dev); @@ -244,6 +245,7 @@ err_kms_cleanup: kirin_drm_kms_cleanup(drm_dev); err_drm_dev_put: drm_dev_put(drm_dev); + dev_set_drvdata(dev, NULL); return ret; } @@ -255,6 +257,7 @@ static void kirin_drm_unbind(struct device *dev) drm_dev_unregister(drm_dev); kirin_drm_kms_cleanup(drm_dev); drm_dev_put(drm_dev); + dev_set_drvdata(dev, NULL); } static const struct component_master_ops kirin_drm_ops = { @@ -284,6 +287,11 @@ static void kirin_drm_platform_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &kirin_drm_ops); } +static void kirin_drm_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id kirin_drm_dt_ids[] = { { .compatible = "hisilicon,hi6220-ade", .data = &ade_driver_data, @@ -295,6 +303,7 @@ MODULE_DEVICE_TABLE(of, kirin_drm_dt_ids); static struct platform_driver kirin_drm_platform_driver = { .probe = kirin_drm_platform_probe, .remove_new = kirin_drm_platform_remove, + .shutdown = kirin_drm_platform_shutdown, .driver = { .name = "kirin-drm", .of_match_table = kirin_drm_dt_ids, diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c index 8026118c6e03..58b0b46a21e6 100644 --- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c +++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c @@ -178,6 +178,11 @@ static void hyperv_vmbus_remove(struct hv_device *hdev) vmbus_free_mmio(hv->mem->start, hv->fb_size); } +static void hyperv_vmbus_shutdown(struct hv_device *hdev) +{ + drm_atomic_helper_shutdown(hv_get_drvdata(hdev)); +} + static int hyperv_vmbus_suspend(struct hv_device *hdev) { struct drm_device *dev = hv_get_drvdata(hdev); @@ -220,6 +225,7 @@ static struct hv_driver hyperv_hv_driver = { .id_table = hyperv_vmbus_tbl, .probe = hyperv_vmbus_probe, .remove = hyperv_vmbus_remove, + .shutdown = hyperv_vmbus_shutdown, .suspend = hyperv_vmbus_suspend, .resume = hyperv_vmbus_resume, .driver = { diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index c27f82872c55..a21cc9d0689e 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -592,12 +592,8 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state, struct intel_dp *intel_dp = &dig_port->dp; struct intel_connector *connector = to_intel_connector(old_conn_state->connector); - struct drm_dp_mst_topology_state *old_mst_state = - drm_atomic_get_old_mst_topology_state(&state->base, &intel_dp->mst_mgr); struct drm_dp_mst_topology_state *new_mst_state = drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr); - const struct drm_dp_mst_atomic_payload *old_payload = - drm_atomic_get_mst_payload_state(old_mst_state, connector->port); struct drm_dp_mst_atomic_payload *new_payload = drm_atomic_get_mst_payload_state(new_mst_state, connector->port); struct drm_i915_private *i915 = to_i915(connector->base.dev); @@ -607,8 +603,7 @@ static void intel_mst_disable_dp(struct intel_atomic_state *state, intel_hdcp_disable(intel_mst->connector); - drm_dp_remove_payload(&intel_dp->mst_mgr, new_mst_state, - old_payload, new_payload); + drm_dp_remove_payload_part1(&intel_dp->mst_mgr, new_mst_state, new_payload); intel_audio_codec_disable(encoder, old_crtc_state, old_conn_state); } @@ -623,6 +618,14 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, struct intel_dp *intel_dp = &dig_port->dp; struct intel_connector *connector = to_intel_connector(old_conn_state->connector); + struct drm_dp_mst_topology_state *old_mst_state = + drm_atomic_get_old_mst_topology_state(&state->base, &intel_dp->mst_mgr); + struct drm_dp_mst_topology_state *new_mst_state = + drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr); + const struct drm_dp_mst_atomic_payload *old_payload = + drm_atomic_get_mst_payload_state(old_mst_state, connector->port); + struct drm_dp_mst_atomic_payload *new_payload = + drm_atomic_get_mst_payload_state(new_mst_state, connector->port); struct drm_i915_private *dev_priv = to_i915(connector->base.dev); bool last_mst_stream; @@ -643,6 +646,9 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state, wait_for_act_sent(encoder, old_crtc_state); + drm_dp_remove_payload_part2(&intel_dp->mst_mgr, new_mst_state, + old_payload, new_payload); + intel_ddi_disable_transcoder_func(old_crtc_state); if (DISPLAY_VER(dev_priv) >= 9) diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 9cbf4686df1d..245a64332cc7 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -21,6 +21,7 @@ #include "skl_scaler.h" #include "skl_universal_plane.h" #include "skl_watermark.h" +#include "gt/intel_gt.h" #include "pxp/intel_pxp.h" static const u32 skl_plane_formats[] = { @@ -2172,11 +2173,6 @@ skl_plane_disable_flip_done(struct intel_plane *plane) static bool skl_plane_has_rc_ccs(struct drm_i915_private *i915, enum pipe pipe, enum plane_id plane_id) { - /* Wa_14017240301 */ - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) - return false; - /* Wa_22011186057 */ if (IS_ALDERLAKE_P(i915) && IS_DISPLAY_STEP(i915, STEP_A0, STEP_B0)) return false; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_create.c b/drivers/gpu/drm/i915/gem/i915_gem_create.c index d24c0ce8805c..19156ba4b9ef 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_create.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_create.c @@ -405,8 +405,8 @@ static int ext_set_pat(struct i915_user_extension __user *base, void *data) BUILD_BUG_ON(sizeof(struct drm_i915_gem_create_ext_set_pat) != offsetofend(struct drm_i915_gem_create_ext_set_pat, rsvd)); - /* Limiting the extension only to Meteor Lake */ - if (!IS_METEORLAKE(i915)) + /* Limiting the extension only to Xe_LPG and beyond */ + if (GRAPHICS_VER_FULL(i915) < IP_VER(12, 70)) return -ENODEV; if (copy_from_user(&ext, base, sizeof(ext))) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c index 7d04ec740fd1..683fd8d3151c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_execbuffer.c @@ -321,7 +321,7 @@ static int eb_pin_engine(struct i915_execbuffer *eb, bool throttle); static void eb_unpin_engine(struct i915_execbuffer *eb); static void eb_capture_release(struct i915_execbuffer *eb); -static inline bool eb_use_cmdparser(const struct i915_execbuffer *eb) +static bool eb_use_cmdparser(const struct i915_execbuffer *eb) { return intel_engine_requires_cmd_parser(eb->context->engine) || (intel_engine_using_cmd_parser(eb->context->engine) && @@ -433,7 +433,7 @@ static u64 eb_pin_flags(const struct drm_i915_gem_exec_object2 *entry, return pin_flags; } -static inline int +static int eb_pin_vma(struct i915_execbuffer *eb, const struct drm_i915_gem_exec_object2 *entry, struct eb_vma *ev) @@ -486,7 +486,7 @@ eb_pin_vma(struct i915_execbuffer *eb, return 0; } -static inline void +static void eb_unreserve_vma(struct eb_vma *ev) { if (unlikely(ev->flags & __EXEC_OBJECT_HAS_FENCE)) @@ -548,7 +548,7 @@ eb_validate_vma(struct i915_execbuffer *eb, return 0; } -static inline bool +static bool is_batch_buffer(struct i915_execbuffer *eb, unsigned int buffer_idx) { return eb->args->flags & I915_EXEC_BATCH_FIRST ? @@ -628,8 +628,8 @@ eb_add_vma(struct i915_execbuffer *eb, return 0; } -static inline int use_cpu_reloc(const struct reloc_cache *cache, - const struct drm_i915_gem_object *obj) +static int use_cpu_reloc(const struct reloc_cache *cache, + const struct drm_i915_gem_object *obj) { if (!i915_gem_object_has_struct_page(obj)) return false; @@ -1107,7 +1107,7 @@ static void eb_destroy(const struct i915_execbuffer *eb) kfree(eb->buckets); } -static inline u64 +static u64 relocation_target(const struct drm_i915_gem_relocation_entry *reloc, const struct i915_vma *target) { @@ -1128,19 +1128,19 @@ static void reloc_cache_init(struct reloc_cache *cache, cache->node.flags = 0; } -static inline void *unmask_page(unsigned long p) +static void *unmask_page(unsigned long p) { return (void *)(uintptr_t)(p & PAGE_MASK); } -static inline unsigned int unmask_flags(unsigned long p) +static unsigned int unmask_flags(unsigned long p) { return p & ~PAGE_MASK; } #define KMAP 0x4 /* after CLFLUSH_FLAGS */ -static inline struct i915_ggtt *cache_to_ggtt(struct reloc_cache *cache) +static struct i915_ggtt *cache_to_ggtt(struct reloc_cache *cache) { struct drm_i915_private *i915 = container_of(cache, struct i915_execbuffer, reloc_cache)->i915; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c index 8f1633c3fb93..73a4a4eb29e0 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c @@ -100,6 +100,7 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st, st->nents = 0; for (i = 0; i < page_count; i++) { struct folio *folio; + unsigned long nr_pages; const unsigned int shrink[] = { I915_SHRINK_BOUND | I915_SHRINK_UNBOUND, 0, @@ -150,6 +151,8 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st, } } while (1); + nr_pages = min_t(unsigned long, + folio_nr_pages(folio), page_count - i); if (!i || sg->length >= max_segment || folio_pfn(folio) != next_pfn) { @@ -157,13 +160,13 @@ int shmem_sg_alloc_table(struct drm_i915_private *i915, struct sg_table *st, sg = sg_next(sg); st->nents++; - sg_set_folio(sg, folio, folio_size(folio), 0); + sg_set_folio(sg, folio, nr_pages * PAGE_SIZE, 0); } else { /* XXX: could overflow? */ - sg->length += folio_size(folio); + sg->length += nr_pages * PAGE_SIZE; } - next_pfn = folio_pfn(folio) + folio_nr_pages(folio); - i += folio_nr_pages(folio) - 1; + next_pfn = folio_pfn(folio) + nr_pages; + i += nr_pages - 1; /* Check that the i965g/gm workaround works. */ GEM_BUG_ON(gfp & __GFP_DMA32 && next_pfn >= 0x00100000UL); diff --git a/drivers/gpu/drm/i915/gem/selftests/mock_context.c b/drivers/gpu/drm/i915/gem/selftests/mock_context.c index 8ac6726ec16b..e199d7dbb876 100644 --- a/drivers/gpu/drm/i915/gem/selftests/mock_context.c +++ b/drivers/gpu/drm/i915/gem/selftests/mock_context.c @@ -36,7 +36,7 @@ mock_context(struct drm_i915_private *i915, if (name) { struct i915_ppgtt *ppgtt; - strncpy(ctx->name, name, sizeof(ctx->name) - 1); + strscpy(ctx->name, name, sizeof(ctx->name)); ppgtt = mock_ppgtt(i915, name); if (!ppgtt) diff --git a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c index a4ff55aa5e55..ba4c2422b340 100644 --- a/drivers/gpu/drm/i915/gt/gen8_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/gen8_engine_cs.c @@ -4,9 +4,9 @@ */ #include "gen8_engine_cs.h" -#include "i915_drv.h" #include "intel_engine_regs.h" #include "intel_gpu_commands.h" +#include "intel_gt.h" #include "intel_lrc.h" #include "intel_ring.h" @@ -226,8 +226,8 @@ u32 *gen12_emit_aux_table_inv(struct intel_engine_cs *engine, u32 *cs) static int mtl_dummy_pipe_control(struct i915_request *rq) { /* Wa_14016712196 */ - if (IS_MTL_GRAPHICS_STEP(rq->i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(rq->i915, P, STEP_A0, STEP_B0)) { + if (IS_GFX_GT_IP_RANGE(rq->engine->gt, IP_VER(12, 70), IP_VER(12, 71)) || + IS_DG2(rq->i915)) { u32 *cs; /* dummy PIPE_CONTROL + depth flush */ @@ -271,8 +271,17 @@ int gen12_emit_flush_rcs(struct i915_request *rq, u32 mode) if (GRAPHICS_VER_FULL(rq->i915) >= IP_VER(12, 70)) bit_group_0 |= PIPE_CONTROL_CCS_FLUSH; + /* + * L3 fabric flush is needed for AUX CCS invalidation + * which happens as part of pipe-control so we can + * ignore PIPE_CONTROL_FLUSH_L3. Also PIPE_CONTROL_FLUSH_L3 + * deals with Protected Memory which is not needed for + * AUX CCS invalidation and lead to unwanted side effects. + */ + if (mode & EMIT_FLUSH) + bit_group_1 |= PIPE_CONTROL_FLUSH_L3; + bit_group_1 |= PIPE_CONTROL_TILE_CACHE_FLUSH; - bit_group_1 |= PIPE_CONTROL_FLUSH_L3; bit_group_1 |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH; bit_group_1 |= PIPE_CONTROL_DEPTH_CACHE_FLUSH; /* Wa_1409600907:tgl,adl-p */ @@ -799,6 +808,7 @@ u32 *gen12_emit_fini_breadcrumb_xcs(struct i915_request *rq, u32 *cs) u32 *gen12_emit_fini_breadcrumb_rcs(struct i915_request *rq, u32 *cs) { struct drm_i915_private *i915 = rq->i915; + struct intel_gt *gt = rq->engine->gt; u32 flags = (PIPE_CONTROL_CS_STALL | PIPE_CONTROL_TLB_INVALIDATE | PIPE_CONTROL_TILE_CACHE_FLUSH | @@ -809,8 +819,7 @@ u32 *gen12_emit_fini_breadcrumb_rcs(struct i915_request *rq, u32 *cs) PIPE_CONTROL_FLUSH_ENABLE); /* Wa_14016712196 */ - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) + if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71)) || IS_DG2(i915)) /* dummy PIPE_CONTROL + depth flush */ cs = gen12_emit_pipe_control(cs, 0, PIPE_CONTROL_DEPTH_CACHE_FLUSH, 0); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_cs.c b/drivers/gpu/drm/i915/gt/intel_engine_cs.c index ee15486fed0d..84a75c95f3f7 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_cs.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_cs.c @@ -558,7 +558,6 @@ static int intel_engine_setup(struct intel_gt *gt, enum intel_engine_id id, DRIVER_CAPS(i915)->has_logical_contexts = true; ewma__engine_latency_init(&engine->latency); - seqcount_init(&engine->stats.execlists.lock); ATOMIC_INIT_NOTIFIER_HEAD(&engine->context_status_notifier); @@ -1617,9 +1616,7 @@ static int __intel_engine_stop_cs(struct intel_engine_cs *engine, * Wa_22011802037: Prior to doing a reset, ensure CS is * stopped, set ring stop bit and prefetch disable bit to halt CS */ - if (IS_MTL_GRAPHICS_STEP(engine->i915, M, STEP_A0, STEP_B0) || - (GRAPHICS_VER(engine->i915) >= 11 && - GRAPHICS_VER_FULL(engine->i915) < IP_VER(12, 70))) + if (intel_engine_reset_needs_wa_22011802037(engine->gt)) intel_uncore_write_fw(uncore, RING_MODE_GEN7(engine->mmio_base), _MASKED_BIT_ENABLE(GEN12_GFX_PREFETCH_DISABLE)); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_pm.c b/drivers/gpu/drm/i915/gt/intel_engine_pm.c index b538b5c04948..e91fc881dbf1 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_pm.c +++ b/drivers/gpu/drm/i915/gt/intel_engine_pm.c @@ -21,7 +21,7 @@ static void intel_gsc_idle_msg_enable(struct intel_engine_cs *engine) { struct drm_i915_private *i915 = engine->i915; - if (IS_METEORLAKE(i915) && engine->id == GSC0) { + if (MEDIA_VER(i915) >= 13 && engine->id == GSC0) { intel_uncore_write(engine->gt->uncore, RC_PSMI_CTRL_GSCCS, _MASKED_BIT_DISABLE(IDLE_MSG_DISABLE)); diff --git a/drivers/gpu/drm/i915/gt/intel_engine_regs.h b/drivers/gpu/drm/i915/gt/intel_engine_regs.h index 6b9d9f837669..fdd4ddd3a978 100644 --- a/drivers/gpu/drm/i915/gt/intel_engine_regs.h +++ b/drivers/gpu/drm/i915/gt/intel_engine_regs.h @@ -177,6 +177,7 @@ #define CTX_CTRL_RS_CTX_ENABLE REG_BIT(1) #define CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT REG_BIT(2) #define CTX_CTRL_INHIBIT_SYN_CTX_SWITCH REG_BIT(3) +#define GEN12_CTX_CTRL_RUNALONE_MODE REG_BIT(7) #define GEN12_CTX_CTRL_OAR_CONTEXT_ENABLE REG_BIT(8) #define RING_CTX_SR_CTL(base) _MMIO((base) + 0x244) #define RING_SEMA_WAIT_POLL(base) _MMIO((base) + 0x24c) diff --git a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c index 8a641bcf777c..e8f42ec6b1b4 100644 --- a/drivers/gpu/drm/i915/gt/intel_execlists_submission.c +++ b/drivers/gpu/drm/i915/gt/intel_execlists_submission.c @@ -3001,9 +3001,7 @@ static void execlists_reset_prepare(struct intel_engine_cs *engine) * Wa_22011802037: In addition to stopping the cs, we need * to wait for any pending mi force wakeups */ - if (IS_MTL_GRAPHICS_STEP(engine->i915, M, STEP_A0, STEP_B0) || - (GRAPHICS_VER(engine->i915) >= 11 && - GRAPHICS_VER_FULL(engine->i915) < IP_VER(12, 70))) + if (intel_engine_reset_needs_wa_22011802037(engine->gt)) intel_engine_wait_for_pending_mi_fw(engine); engine->execlists.reset_ccid = active_ccid(engine); @@ -3550,6 +3548,8 @@ int intel_execlists_submission_setup(struct intel_engine_cs *engine) logical_ring_default_vfuncs(engine); logical_ring_default_irqs(engine); + seqcount_init(&engine->stats.execlists.lock); + if (engine->flags & I915_ENGINE_HAS_RCS_REG_STATE) rcs_submission_override(engine); diff --git a/drivers/gpu/drm/i915/gt/intel_ggtt.c b/drivers/gpu/drm/i915/gt/intel_ggtt.c index dd0ed941441a..da21f2786b5d 100644 --- a/drivers/gpu/drm/i915/gt/intel_ggtt.c +++ b/drivers/gpu/drm/i915/gt/intel_ggtt.c @@ -511,20 +511,31 @@ void intel_ggtt_unbind_vma(struct i915_address_space *vm, vm->clear_range(vm, vma_res->start, vma_res->vma_size); } +/* + * Reserve the top of the GuC address space for firmware images. Addresses + * beyond GUC_GGTT_TOP in the GuC address space are inaccessible by GuC, + * which makes for a suitable range to hold GuC/HuC firmware images if the + * size of the GGTT is 4G. However, on a 32-bit platform the size of the GGTT + * is limited to 2G, which is less than GUC_GGTT_TOP, but we reserve a chunk + * of the same size anyway, which is far more than needed, to keep the logic + * in uc_fw_ggtt_offset() simple. + */ +#define GUC_TOP_RESERVE_SIZE (SZ_4G - GUC_GGTT_TOP) + static int ggtt_reserve_guc_top(struct i915_ggtt *ggtt) { - u64 size; + u64 offset; int ret; if (!intel_uc_uses_guc(&ggtt->vm.gt->uc)) return 0; - GEM_BUG_ON(ggtt->vm.total <= GUC_GGTT_TOP); - size = ggtt->vm.total - GUC_GGTT_TOP; + GEM_BUG_ON(ggtt->vm.total <= GUC_TOP_RESERVE_SIZE); + offset = ggtt->vm.total - GUC_TOP_RESERVE_SIZE; - ret = i915_gem_gtt_reserve(&ggtt->vm, NULL, &ggtt->uc_fw, size, - GUC_GGTT_TOP, I915_COLOR_UNEVICTABLE, - PIN_NOEVICT); + ret = i915_gem_gtt_reserve(&ggtt->vm, NULL, &ggtt->uc_fw, + GUC_TOP_RESERVE_SIZE, offset, + I915_COLOR_UNEVICTABLE, PIN_NOEVICT); if (ret) drm_dbg(&ggtt->vm.i915->drm, "Failed to reserve top of GGTT for GuC\n"); diff --git a/drivers/gpu/drm/i915/gt/intel_gt.c b/drivers/gpu/drm/i915/gt/intel_gt.c index 449f0b7fc843..93062c35e072 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt.c +++ b/drivers/gpu/drm/i915/gt/intel_gt.c @@ -1019,3 +1019,8 @@ enum i915_map_type intel_gt_coherent_map_type(struct intel_gt *gt, else return I915_MAP_WC; } + +bool intel_gt_needs_wa_22016122933(struct intel_gt *gt) +{ + return MEDIA_VER_FULL(gt->i915) == IP_VER(13, 0) && gt->type == GT_MEDIA; +} diff --git a/drivers/gpu/drm/i915/gt/intel_gt.h b/drivers/gpu/drm/i915/gt/intel_gt.h index 6c34547b58b5..2cac499d5aa3 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt.h +++ b/drivers/gpu/drm/i915/gt/intel_gt.h @@ -6,7 +6,6 @@ #ifndef __INTEL_GT__ #define __INTEL_GT__ -#include "i915_drv.h" #include "intel_engine_types.h" #include "intel_gt_types.h" #include "intel_reset.h" @@ -14,6 +13,69 @@ struct drm_i915_private; struct drm_printer; +/* + * Check that the GT is a graphics GT and has an IP version within the + * specified range (inclusive). + */ +#define IS_GFX_GT_IP_RANGE(gt, from, until) ( \ + BUILD_BUG_ON_ZERO((from) < IP_VER(2, 0)) + \ + BUILD_BUG_ON_ZERO((until) < (from)) + \ + ((gt)->type != GT_MEDIA && \ + GRAPHICS_VER_FULL((gt)->i915) >= (from) && \ + GRAPHICS_VER_FULL((gt)->i915) <= (until))) + +/* + * Check that the GT is a media GT and has an IP version within the + * specified range (inclusive). + * + * Only usable on platforms with a standalone media design (i.e., IP version 13 + * and higher). + */ +#define IS_MEDIA_GT_IP_RANGE(gt, from, until) ( \ + BUILD_BUG_ON_ZERO((from) < IP_VER(13, 0)) + \ + BUILD_BUG_ON_ZERO((until) < (from)) + \ + ((gt) && (gt)->type == GT_MEDIA && \ + MEDIA_VER_FULL((gt)->i915) >= (from) && \ + MEDIA_VER_FULL((gt)->i915) <= (until))) + +/* + * Check that the GT is a graphics GT with a specific IP version and has + * a stepping in the range [from, until). The lower stepping bound is + * inclusive, the upper bound is exclusive. The most common use-case of this + * macro is for checking bounds for workarounds, which usually have a stepping + * ("from") at which the hardware issue is first present and another stepping + * ("until") at which a hardware fix is present and the software workaround is + * no longer necessary. E.g., + * + * IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) + * IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_B1, STEP_FOREVER) + * + * "STEP_FOREVER" can be passed as "until" for workarounds that have no upper + * stepping bound for the specified IP version. + */ +#define IS_GFX_GT_IP_STEP(gt, ipver, from, until) ( \ + BUILD_BUG_ON_ZERO((until) <= (from)) + \ + (IS_GFX_GT_IP_RANGE((gt), (ipver), (ipver)) && \ + IS_GRAPHICS_STEP((gt)->i915, (from), (until)))) + +/* + * Check that the GT is a media GT with a specific IP version and has + * a stepping in the range [from, until). The lower stepping bound is + * inclusive, the upper bound is exclusive. The most common use-case of this + * macro is for checking bounds for workarounds, which usually have a stepping + * ("from") at which the hardware issue is first present and another stepping + * ("until") at which a hardware fix is present and the software workaround is + * no longer necessary. "STEP_FOREVER" can be passed as "until" for + * workarounds that have no upper stepping bound for the specified IP version. + * + * This macro may only be used to match on platforms that have a standalone + * media design (i.e., media version 13 or higher). + */ +#define IS_MEDIA_GT_IP_STEP(gt, ipver, from, until) ( \ + BUILD_BUG_ON_ZERO((until) <= (from)) + \ + (IS_MEDIA_GT_IP_RANGE((gt), (ipver), (ipver)) && \ + IS_MEDIA_STEP((gt)->i915, (from), (until)))) + #define GT_TRACE(gt, fmt, ...) do { \ const struct intel_gt *gt__ __maybe_unused = (gt); \ GEM_TRACE("%s " fmt, dev_name(gt__->i915->drm.dev), \ @@ -25,10 +87,7 @@ static inline bool gt_is_root(struct intel_gt *gt) return !gt->info.id; } -static inline bool intel_gt_needs_wa_22016122933(struct intel_gt *gt) -{ - return MEDIA_VER_FULL(gt->i915) == IP_VER(13, 0) && gt->type == GT_MEDIA; -} +bool intel_gt_needs_wa_22016122933(struct intel_gt *gt); static inline struct intel_gt *uc_to_gt(struct intel_uc *uc) { diff --git a/drivers/gpu/drm/i915/gt/intel_gt_mcr.c b/drivers/gpu/drm/i915/gt/intel_gt_mcr.c index 0b414eae1683..bf4a933de03a 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_mcr.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_mcr.c @@ -4,7 +4,7 @@ */ #include "i915_drv.h" - +#include "intel_gt.h" #include "intel_gt_mcr.h" #include "intel_gt_print.h" #include "intel_gt_regs.h" @@ -166,8 +166,8 @@ void intel_gt_mcr_init(struct intel_gt *gt) gt->steering_table[OADDRM] = xelpmp_oaddrm_steering_table; } else if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 70)) { /* Wa_14016747170 */ - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) fuse = REG_FIELD_GET(MTL_GT_L3_EXC_MASK, intel_uncore_read(gt->uncore, MTL_GT_ACTIVITY_FACTOR)); diff --git a/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c b/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c index 357e2f865727..f900cc68d6d9 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c +++ b/drivers/gpu/drm/i915/gt/intel_gt_pm_debugfs.c @@ -290,7 +290,6 @@ static int mtl_drpc(struct seq_file *m) seq_puts(m, "RC6\n"); break; default: - MISSING_CASE(REG_FIELD_GET(MTL_CC_MASK, gt_core_status)); seq_puts(m, "Unknown\n"); break; } diff --git a/drivers/gpu/drm/i915/gt/intel_gt_regs.h b/drivers/gpu/drm/i915/gt/intel_gt_regs.h index 2cdfb2f713d0..cca4bac8f8b0 100644 --- a/drivers/gpu/drm/i915/gt/intel_gt_regs.h +++ b/drivers/gpu/drm/i915/gt/intel_gt_regs.h @@ -26,7 +26,7 @@ #define MTL_CAGF_MASK REG_GENMASK(8, 0) #define MTL_CC0 0x0 #define MTL_CC6 0x3 -#define MTL_CC_MASK REG_GENMASK(12, 9) +#define MTL_CC_MASK REG_GENMASK(10, 9) /* RPM unit config (Gen8+) */ #define RPM_CONFIG0 _MMIO(0xd00) @@ -164,6 +164,8 @@ #define GEN9_CSFE_CHICKEN1_RCS _MMIO(0x20d4) #define GEN9_PREEMPT_GPGPU_SYNC_SWITCH_DISABLE (1 << 2) #define GEN11_ENABLE_32_PLANE_MODE (1 << 7) +#define GEN12_CS_DEBUG_MODE2 _MMIO(0x20d8) +#define INSTRUCTION_STATE_CACHE_INVALIDATE REG_BIT(6) #define GEN7_FF_SLICE_CS_CHICKEN1 _MMIO(0x20e0) #define GEN9_FFSC_PERCTX_PREEMPT_CTRL (1 << 14) @@ -412,9 +414,6 @@ #define XEHP_CULLBIT1 MCR_REG(0x6100) -#define CHICKEN_RASTER_1 MCR_REG(0x6204) -#define DIS_SF_ROUND_NEAREST_EVEN REG_BIT(8) - #define CHICKEN_RASTER_2 MCR_REG(0x6208) #define TBIMR_FAST_CLIP REG_BIT(5) @@ -1221,6 +1220,8 @@ #define XEHP_HDC_CHICKEN0 MCR_REG(0xe5f0) #define LSC_L1_FLUSH_CTL_3D_DATAPORT_FLUSH_EVENTS_MASK REG_GENMASK(13, 11) +#define DIS_ATOMIC_CHAINING_TYPED_WRITES REG_BIT(3) + #define ICL_HDC_MODE MCR_REG(0xe5f4) #define EU_PERF_CNTL2 PERF_REG(0xe658) @@ -1231,6 +1232,7 @@ #define DISABLE_D8_D16_COASLESCE REG_BIT(30) #define FORCE_1_SUB_MESSAGE_PER_FRAGMENT REG_BIT(15) #define LSC_CHICKEN_BIT_0_UDW MCR_REG(0xe7c8 + 4) +#define UGM_FRAGMENT_THRESHOLD_TO_3 REG_BIT(58 - 32) #define DIS_CHAIN_2XSIMD8 REG_BIT(55 - 32) #define FORCE_SLM_FENCE_SCOPE_TO_TILE REG_BIT(42 - 32) #define FORCE_UGM_FENCE_SCOPE_TO_TILE REG_BIT(41 - 32) diff --git a/drivers/gpu/drm/i915/gt/intel_lrc.c b/drivers/gpu/drm/i915/gt/intel_lrc.c index 957d0aeb0c02..eaf66d903166 100644 --- a/drivers/gpu/drm/i915/gt/intel_lrc.c +++ b/drivers/gpu/drm/i915/gt/intel_lrc.c @@ -845,6 +845,27 @@ lrc_setup_indirect_ctx(u32 *regs, lrc_ring_indirect_offset_default(engine) << 6; } +static bool ctx_needs_runalone(const struct intel_context *ce) +{ + struct i915_gem_context *gem_ctx; + bool ctx_is_protected = false; + + /* + * On MTL and newer platforms, protected contexts require setting + * the LRC run-alone bit or else the encryption will not happen. + */ + if (GRAPHICS_VER_FULL(ce->engine->i915) >= IP_VER(12, 70) && + (ce->engine->class == COMPUTE_CLASS || ce->engine->class == RENDER_CLASS)) { + rcu_read_lock(); + gem_ctx = rcu_dereference(ce->gem_context); + if (gem_ctx) + ctx_is_protected = gem_ctx->uses_protected_content; + rcu_read_unlock(); + } + + return ctx_is_protected; +} + static void init_common_regs(u32 * const regs, const struct intel_context *ce, const struct intel_engine_cs *engine, @@ -860,6 +881,8 @@ static void init_common_regs(u32 * const regs, if (GRAPHICS_VER(engine->i915) < 11) ctl |= _MASKED_BIT_DISABLE(CTX_CTRL_ENGINE_CTX_SAVE_INHIBIT | CTX_CTRL_RS_CTX_ENABLE); + if (ctx_needs_runalone(ce)) + ctl |= _MASKED_BIT_ENABLE(GEN12_CTX_CTRL_RUNALONE_MODE); regs[CTX_CONTEXT_CONTROL] = ctl; regs[CTX_TIMESTAMP] = ce->stats.runtime.last; @@ -1094,6 +1117,9 @@ __lrc_alloc_state(struct intel_context *ce, struct intel_engine_cs *engine) I915_BO_ALLOC_PM_VOLATILE); if (IS_ERR(obj)) { obj = i915_gem_object_create_shmem(engine->i915, context_size); + if (IS_ERR(obj)) + return ERR_CAST(obj); + /* * Wa_22016122933: For Media version 13.0, all Media GT shared * memory needs to be mapped as WC on CPU side and UC (PAT @@ -1102,8 +1128,6 @@ __lrc_alloc_state(struct intel_context *ce, struct intel_engine_cs *engine) if (intel_gt_needs_wa_22016122933(engine->gt)) i915_gem_object_set_cache_coherency(obj, I915_CACHE_NONE); } - if (IS_ERR(obj)) - return ERR_CAST(obj); vma = i915_vma_instance(obj, &engine->gt->ggtt->vm, NULL); if (IS_ERR(vma)) { @@ -1316,29 +1340,6 @@ gen12_emit_cmd_buf_wa(const struct intel_context *ce, u32 *cs) } /* - * On DG2 during context restore of a preempted context in GPGPU mode, - * RCS restore hang is detected. This is extremely timing dependent. - * To address this below sw wabb is implemented for DG2 A steppings. - */ -static u32 * -dg2_emit_rcs_hang_wabb(const struct intel_context *ce, u32 *cs) -{ - *cs++ = MI_LOAD_REGISTER_IMM(1); - *cs++ = i915_mmio_reg_offset(GEN12_STATE_ACK_DEBUG(ce->engine->mmio_base)); - *cs++ = 0x21; - - *cs++ = MI_LOAD_REGISTER_REG; - *cs++ = i915_mmio_reg_offset(RING_NOPID(ce->engine->mmio_base)); - *cs++ = i915_mmio_reg_offset(XEHP_CULLBIT1); - - *cs++ = MI_LOAD_REGISTER_REG; - *cs++ = i915_mmio_reg_offset(RING_NOPID(ce->engine->mmio_base)); - *cs++ = i915_mmio_reg_offset(XEHP_CULLBIT2); - - return cs; -} - -/* * The bspec's tuning guide asks us to program a vertical watermark value of * 0x3FF. However this register is not saved/restored properly by the * hardware, so we're required to apply the desired value via INDIRECT_CTX @@ -1356,27 +1357,34 @@ dg2_emit_draw_watermark_setting(u32 *cs) } static u32 * +gen12_invalidate_state_cache(u32 *cs) +{ + *cs++ = MI_LOAD_REGISTER_IMM(1); + *cs++ = i915_mmio_reg_offset(GEN12_CS_DEBUG_MODE2); + *cs++ = _MASKED_BIT_ENABLE(INSTRUCTION_STATE_CACHE_INVALIDATE); + return cs; +} + +static u32 * gen12_emit_indirect_ctx_rcs(const struct intel_context *ce, u32 *cs) { cs = gen12_emit_timestamp_wa(ce, cs); cs = gen12_emit_cmd_buf_wa(ce, cs); cs = gen12_emit_restore_scratch(ce, cs); - /* Wa_22011450934:dg2 */ - if (IS_DG2_GRAPHICS_STEP(ce->engine->i915, G10, STEP_A0, STEP_B0) || - IS_DG2_GRAPHICS_STEP(ce->engine->i915, G11, STEP_A0, STEP_B0)) - cs = dg2_emit_rcs_hang_wabb(ce, cs); - /* Wa_16013000631:dg2 */ - if (IS_DG2_GRAPHICS_STEP(ce->engine->i915, G10, STEP_B0, STEP_C0) || - IS_DG2_G11(ce->engine->i915)) + if (IS_DG2_G11(ce->engine->i915)) cs = gen8_emit_pipe_control(cs, PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE, 0); cs = gen12_emit_aux_table_inv(ce->engine, cs); + /* Wa_18022495364 */ + if (IS_GFX_GT_IP_RANGE(ce->engine->gt, IP_VER(12, 0), IP_VER(12, 10))) + cs = gen12_invalidate_state_cache(cs); + /* Wa_16014892111 */ - if (IS_MTL_GRAPHICS_STEP(ce->engine->i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(ce->engine->i915, P, STEP_A0, STEP_B0) || + if (IS_GFX_GT_IP_STEP(ce->engine->gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(ce->engine->gt, IP_VER(12, 71), STEP_A0, STEP_B0) || IS_DG2(ce->engine->i915)) cs = dg2_emit_draw_watermark_setting(cs); @@ -1390,8 +1398,7 @@ gen12_emit_indirect_ctx_xcs(const struct intel_context *ce, u32 *cs) cs = gen12_emit_restore_scratch(ce, cs); /* Wa_16013000631:dg2 */ - if (IS_DG2_GRAPHICS_STEP(ce->engine->i915, G10, STEP_B0, STEP_C0) || - IS_DG2_G11(ce->engine->i915)) + if (IS_DG2_G11(ce->engine->i915)) if (ce->engine->class == COMPUTE_CLASS) cs = gen8_emit_pipe_control(cs, PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE, diff --git a/drivers/gpu/drm/i915/gt/intel_mocs.c b/drivers/gpu/drm/i915/gt/intel_mocs.c index 2c014407225c..07269ff3be13 100644 --- a/drivers/gpu/drm/i915/gt/intel_mocs.c +++ b/drivers/gpu/drm/i915/gt/intel_mocs.c @@ -404,18 +404,6 @@ static const struct drm_i915_mocs_entry dg2_mocs_table[] = { MOCS_ENTRY(3, 0, L3_3_WB | L3_LKUP(1)), }; -static const struct drm_i915_mocs_entry dg2_mocs_table_g10_ax[] = { - /* Wa_14011441408: Set Go to Memory for MOCS#0 */ - MOCS_ENTRY(0, 0, L3_1_UC | L3_GLBGO(1) | L3_LKUP(1)), - /* UC - Coherent; GO:Memory */ - MOCS_ENTRY(1, 0, L3_1_UC | L3_GLBGO(1) | L3_LKUP(1)), - /* UC - Non-Coherent; GO:Memory */ - MOCS_ENTRY(2, 0, L3_1_UC | L3_GLBGO(1)), - - /* WB - LC */ - MOCS_ENTRY(3, 0, L3_3_WB | L3_LKUP(1)), -}; - static const struct drm_i915_mocs_entry pvc_mocs_table[] = { /* Error */ MOCS_ENTRY(0, 0, L3_3_WB), @@ -507,7 +495,7 @@ static unsigned int get_mocs_settings(const struct drm_i915_private *i915, memset(table, 0, sizeof(struct drm_i915_mocs_table)); table->unused_entries_index = I915_MOCS_PTE; - if (IS_METEORLAKE(i915)) { + if (IS_GFX_GT_IP_RANGE(&i915->gt0, IP_VER(12, 70), IP_VER(12, 71))) { table->size = ARRAY_SIZE(mtl_mocs_table); table->table = mtl_mocs_table; table->n_entries = MTL_NUM_MOCS_ENTRIES; @@ -521,13 +509,8 @@ static unsigned int get_mocs_settings(const struct drm_i915_private *i915, table->wb_index = 2; table->unused_entries_index = 2; } else if (IS_DG2(i915)) { - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_B0)) { - table->size = ARRAY_SIZE(dg2_mocs_table_g10_ax); - table->table = dg2_mocs_table_g10_ax; - } else { - table->size = ARRAY_SIZE(dg2_mocs_table); - table->table = dg2_mocs_table; - } + table->size = ARRAY_SIZE(dg2_mocs_table); + table->table = dg2_mocs_table; table->uc_index = 1; table->n_entries = GEN9_NUM_MOCS_ENTRIES; table->unused_entries_index = 3; diff --git a/drivers/gpu/drm/i915/gt/intel_rc6.c b/drivers/gpu/drm/i915/gt/intel_rc6.c index 58bb1c55294c..8b67abd720be 100644 --- a/drivers/gpu/drm/i915/gt/intel_rc6.c +++ b/drivers/gpu/drm/i915/gt/intel_rc6.c @@ -118,14 +118,12 @@ static void gen11_rc6_enable(struct intel_rc6 *rc6) GEN6_RC_CTL_EI_MODE(1); /* - * Wa_16011777198 and BSpec 52698 - Render powergating must be off. + * BSpec 52698 - Render powergating must be off. * FIXME BSpec is outdated, disabling powergating for MTL is just * temporary wa and should be removed after fixing real cause * of forcewake timeouts. */ - if (IS_METEORLAKE(gt->i915) || - IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_C0) || - IS_DG2_GRAPHICS_STEP(gt->i915, G11, STEP_A0, STEP_B0)) + if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71))) pg_enable = GEN9_MEDIA_PG_ENABLE | GEN11_MEDIA_SAMPLER_PG_ENABLE; @@ -526,8 +524,7 @@ static bool rc6_supported(struct intel_rc6 *rc6) return false; } - if (IS_MTL_MEDIA_STEP(gt->i915, STEP_A0, STEP_B0) && - gt->type == GT_MEDIA) { + if (IS_MEDIA_GT_IP_STEP(gt, IP_VER(13, 0), STEP_A0, STEP_B0)) { drm_notice(&i915->drm, "Media RC6 disabled on A step\n"); return false; diff --git a/drivers/gpu/drm/i915/gt/intel_reset.c b/drivers/gpu/drm/i915/gt/intel_reset.c index cc6bd21a3e51..a21e939fdbf6 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.c +++ b/drivers/gpu/drm/i915/gt/intel_reset.c @@ -161,16 +161,16 @@ static int i915_do_reset(struct intel_gt *gt, struct pci_dev *pdev = to_pci_dev(gt->i915->drm.dev); int err; - /* Assert reset for at least 20 usec, and wait for acknowledgement. */ + /* Assert reset for at least 50 usec, and wait for acknowledgement. */ pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE); udelay(50); - err = wait_for_atomic(i915_in_reset(pdev), 50); + err = _wait_for_atomic(i915_in_reset(pdev), 50000, 0); /* Clear the reset request. */ pci_write_config_byte(pdev, I915_GDRST, 0); udelay(50); if (!err) - err = wait_for_atomic(!i915_in_reset(pdev), 50); + err = _wait_for_atomic(!i915_in_reset(pdev), 50000, 0); return err; } @@ -190,7 +190,7 @@ static int g33_do_reset(struct intel_gt *gt, struct pci_dev *pdev = to_pci_dev(gt->i915->drm.dev); pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE); - return wait_for_atomic(g4x_reset_complete(pdev), 50); + return _wait_for_atomic(g4x_reset_complete(pdev), 50000, 0); } static int g4x_do_reset(struct intel_gt *gt, @@ -207,7 +207,7 @@ static int g4x_do_reset(struct intel_gt *gt, pci_write_config_byte(pdev, I915_GDRST, GRDOM_MEDIA | GRDOM_RESET_ENABLE); - ret = wait_for_atomic(g4x_reset_complete(pdev), 50); + ret = _wait_for_atomic(g4x_reset_complete(pdev), 50000, 0); if (ret) { GT_TRACE(gt, "Wait for media reset failed\n"); goto out; @@ -215,7 +215,7 @@ static int g4x_do_reset(struct intel_gt *gt, pci_write_config_byte(pdev, I915_GDRST, GRDOM_RENDER | GRDOM_RESET_ENABLE); - ret = wait_for_atomic(g4x_reset_complete(pdev), 50); + ret = _wait_for_atomic(g4x_reset_complete(pdev), 50000, 0); if (ret) { GT_TRACE(gt, "Wait for render reset failed\n"); goto out; @@ -705,7 +705,7 @@ static int __reset_guc(struct intel_gt *gt) static bool needs_wa_14015076503(struct intel_gt *gt, intel_engine_mask_t engine_mask) { - if (!IS_METEORLAKE(gt->i915) || !HAS_ENGINE(gt, GSC0)) + if (MEDIA_VER_FULL(gt->i915) != IP_VER(13, 0) || !HAS_ENGINE(gt, GSC0)) return false; if (!__HAS_ENGINE(engine_mask, GSC0)) @@ -785,9 +785,7 @@ int __intel_gt_reset(struct intel_gt *gt, intel_engine_mask_t engine_mask) reset_mask = wa_14015076503_start(gt, engine_mask, !retry); GT_TRACE(gt, "engine_mask=%x\n", reset_mask); - preempt_disable(); ret = reset(gt, reset_mask, retry); - preempt_enable(); wa_14015076503_end(gt, reset_mask); } @@ -1632,6 +1630,24 @@ void __intel_fini_wedge(struct intel_wedge_me *w) w->gt = NULL; } +/* + * Wa_22011802037 requires that we (or the GuC) ensure that no command + * streamers are executing MI_FORCE_WAKE while an engine reset is initiated. + */ +bool intel_engine_reset_needs_wa_22011802037(struct intel_gt *gt) +{ + if (GRAPHICS_VER(gt->i915) < 11) + return false; + + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0)) + return true; + + if (GRAPHICS_VER_FULL(gt->i915) >= IP_VER(12, 70)) + return false; + + return true; +} + #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST) #include "selftest_reset.c" #include "selftest_hangcheck.c" diff --git a/drivers/gpu/drm/i915/gt/intel_reset.h b/drivers/gpu/drm/i915/gt/intel_reset.h index 25c975b6e8fc..f615b30b81c5 100644 --- a/drivers/gpu/drm/i915/gt/intel_reset.h +++ b/drivers/gpu/drm/i915/gt/intel_reset.h @@ -78,4 +78,6 @@ void __intel_fini_wedge(struct intel_wedge_me *w); bool intel_has_gpu_reset(const struct intel_gt *gt); bool intel_has_reset_engine(const struct intel_gt *gt); +bool intel_engine_reset_needs_wa_22011802037(struct intel_gt *gt); + #endif /* I915_RESET_H */ diff --git a/drivers/gpu/drm/i915/gt/intel_rps.c b/drivers/gpu/drm/i915/gt/intel_rps.c index 092542f53aad..4feef874e6d6 100644 --- a/drivers/gpu/drm/i915/gt/intel_rps.c +++ b/drivers/gpu/drm/i915/gt/intel_rps.c @@ -1161,7 +1161,7 @@ void gen6_rps_get_freq_caps(struct intel_rps *rps, struct intel_rps_freq_caps *c { struct drm_i915_private *i915 = rps_to_i915(rps); - if (IS_METEORLAKE(i915)) + if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 70)) return mtl_get_freq_caps(rps, caps); else return __gen6_rps_get_freq_caps(rps, caps); diff --git a/drivers/gpu/drm/i915/gt/intel_workarounds.c b/drivers/gpu/drm/i915/gt/intel_workarounds.c index 3ae0dbd39eaa..b86a10b1f534 100644 --- a/drivers/gpu/drm/i915/gt/intel_workarounds.c +++ b/drivers/gpu/drm/i915/gt/intel_workarounds.c @@ -764,68 +764,41 @@ static void dg2_ctx_workarounds_init(struct intel_engine_cs *engine, { dg2_ctx_gt_tuning_init(engine, wal); - /* Wa_16011186671:dg2_g11 */ - if (IS_DG2_GRAPHICS_STEP(engine->i915, G11, STEP_A0, STEP_B0)) { - wa_mcr_masked_dis(wal, VFLSKPD, DIS_MULT_MISS_RD_SQUASH); - wa_mcr_masked_en(wal, VFLSKPD, DIS_OVER_FETCH_CACHE); - } - - if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_A0, STEP_B0)) { - /* Wa_14010469329:dg2_g10 */ - wa_mcr_masked_en(wal, XEHP_COMMON_SLICE_CHICKEN3, - XEHP_DUAL_SIMD8_SEQ_MERGE_DISABLE); - - /* - * Wa_22010465075:dg2_g10 - * Wa_22010613112:dg2_g10 - * Wa_14010698770:dg2_g10 - */ - wa_mcr_masked_en(wal, XEHP_COMMON_SLICE_CHICKEN3, - GEN12_DISABLE_CPS_AWARE_COLOR_PIPE); - } - /* Wa_16013271637:dg2 */ wa_mcr_masked_en(wal, XEHP_SLICE_COMMON_ECO_CHICKEN1, MSC_MSAA_REODER_BUF_BYPASS_DISABLE); /* Wa_14014947963:dg2 */ - if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_B0, STEP_FOREVER) || - IS_DG2_G11(engine->i915) || IS_DG2_G12(engine->i915)) - wa_masked_field_set(wal, VF_PREEMPTION, PREEMPTION_VERTEX_COUNT, 0x4000); + wa_masked_field_set(wal, VF_PREEMPTION, PREEMPTION_VERTEX_COUNT, 0x4000); /* Wa_18018764978:dg2 */ - if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_C0, STEP_FOREVER) || - IS_DG2_G11(engine->i915) || IS_DG2_G12(engine->i915)) - wa_mcr_masked_en(wal, XEHP_PSS_MODE2, SCOREBOARD_STALL_FLUSH_CONTROL); - - /* Wa_15010599737:dg2 */ - wa_mcr_masked_en(wal, CHICKEN_RASTER_1, DIS_SF_ROUND_NEAREST_EVEN); + wa_mcr_masked_en(wal, XEHP_PSS_MODE2, SCOREBOARD_STALL_FLUSH_CONTROL); /* Wa_18019271663:dg2 */ wa_masked_en(wal, CACHE_MODE_1, MSAA_OPTIMIZATION_REDUC_DISABLE); } -static void mtl_ctx_gt_tuning_init(struct intel_engine_cs *engine, - struct i915_wa_list *wal) +static void xelpg_ctx_gt_tuning_init(struct intel_engine_cs *engine, + struct i915_wa_list *wal) { - struct drm_i915_private *i915 = engine->i915; + struct intel_gt *gt = engine->gt; dg2_ctx_gt_tuning_init(engine, wal); - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_B0, STEP_FOREVER) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_B0, STEP_FOREVER)) + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_B0, STEP_FOREVER) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_B0, STEP_FOREVER)) wa_add(wal, DRAW_WATERMARK, VERT_WM_VAL, 0x3FF, 0, false); } -static void mtl_ctx_workarounds_init(struct intel_engine_cs *engine, - struct i915_wa_list *wal) +static void xelpg_ctx_workarounds_init(struct intel_engine_cs *engine, + struct i915_wa_list *wal) { - struct drm_i915_private *i915 = engine->i915; + struct intel_gt *gt = engine->gt; - mtl_ctx_gt_tuning_init(engine, wal); + xelpg_ctx_gt_tuning_init(engine, wal); - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) { + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) { /* Wa_14014947963 */ wa_masked_field_set(wal, VF_PREEMPTION, PREEMPTION_VERTEX_COUNT, 0x4000); @@ -931,8 +904,8 @@ __intel_engine_init_ctx_wa(struct intel_engine_cs *engine, if (engine->class != RENDER_CLASS) goto done; - if (IS_METEORLAKE(i915)) - mtl_ctx_workarounds_init(engine, wal); + if (IS_GFX_GT_IP_RANGE(engine->gt, IP_VER(12, 70), IP_VER(12, 71))) + xelpg_ctx_workarounds_init(engine, wal); else if (IS_PONTEVECCHIO(i915)) ; /* noop; none at this time */ else if (IS_DG2(i915)) @@ -1606,31 +1579,11 @@ xehpsdv_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal) static void dg2_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal) { - struct intel_engine_cs *engine; - int id; - xehp_init_mcr(gt, wal); /* Wa_14011060649:dg2 */ wa_14011060649(gt, wal); - /* - * Although there are per-engine instances of these registers, - * they technically exist outside the engine itself and are not - * impacted by engine resets. Furthermore, they're part of the - * GuC blacklist so trying to treat them as engine workarounds - * will result in GuC initialization failure and a wedged GPU. - */ - for_each_engine(engine, gt, id) { - if (engine->class != VIDEO_DECODE_CLASS) - continue; - - /* Wa_16010515920:dg2_g10 */ - if (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_B0)) - wa_write_or(wal, VDBOX_CGCTL3F18(engine->mmio_base), - ALNUNIT_CLKGATE_DIS); - } - if (IS_DG2_G10(gt->i915)) { /* Wa_22010523718:dg2 */ wa_write_or(wal, UNSLICE_UNIT_LEVEL_CLKGATE, @@ -1641,70 +1594,15 @@ dg2_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal) DSS_ROUTER_CLKGATE_DIS); } - if (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_B0) || - IS_DG2_GRAPHICS_STEP(gt->i915, G11, STEP_A0, STEP_B0)) { - /* Wa_14012362059:dg2 */ - wa_mcr_write_or(wal, XEHP_MERT_MOD_CTRL, FORCE_MISS_FTLB); - } - - if (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_B0)) { - /* Wa_14010948348:dg2_g10 */ - wa_write_or(wal, UNSLCGCTL9430, MSQDUNIT_CLKGATE_DIS); - - /* Wa_14011037102:dg2_g10 */ - wa_write_or(wal, UNSLCGCTL9444, LTCDD_CLKGATE_DIS); - - /* Wa_14011371254:dg2_g10 */ - wa_mcr_write_or(wal, XEHP_SLICE_UNIT_LEVEL_CLKGATE, NODEDSS_CLKGATE_DIS); - - /* Wa_14011431319:dg2_g10 */ - wa_write_or(wal, UNSLCGCTL9440, GAMTLBOACS_CLKGATE_DIS | - GAMTLBVDBOX7_CLKGATE_DIS | - GAMTLBVDBOX6_CLKGATE_DIS | - GAMTLBVDBOX5_CLKGATE_DIS | - GAMTLBVDBOX4_CLKGATE_DIS | - GAMTLBVDBOX3_CLKGATE_DIS | - GAMTLBVDBOX2_CLKGATE_DIS | - GAMTLBVDBOX1_CLKGATE_DIS | - GAMTLBVDBOX0_CLKGATE_DIS | - GAMTLBKCR_CLKGATE_DIS | - GAMTLBGUC_CLKGATE_DIS | - GAMTLBBLT_CLKGATE_DIS); - wa_write_or(wal, UNSLCGCTL9444, GAMTLBGFXA0_CLKGATE_DIS | - GAMTLBGFXA1_CLKGATE_DIS | - GAMTLBCOMPA0_CLKGATE_DIS | - GAMTLBCOMPA1_CLKGATE_DIS | - GAMTLBCOMPB0_CLKGATE_DIS | - GAMTLBCOMPB1_CLKGATE_DIS | - GAMTLBCOMPC0_CLKGATE_DIS | - GAMTLBCOMPC1_CLKGATE_DIS | - GAMTLBCOMPD0_CLKGATE_DIS | - GAMTLBCOMPD1_CLKGATE_DIS | - GAMTLBMERT_CLKGATE_DIS | - GAMTLBVEBOX3_CLKGATE_DIS | - GAMTLBVEBOX2_CLKGATE_DIS | - GAMTLBVEBOX1_CLKGATE_DIS | - GAMTLBVEBOX0_CLKGATE_DIS); - - /* Wa_14010569222:dg2_g10 */ - wa_write_or(wal, UNSLICE_UNIT_LEVEL_CLKGATE, - GAMEDIA_CLKGATE_DIS); - - /* Wa_14011028019:dg2_g10 */ - wa_mcr_write_or(wal, SSMCGCTL9530, RTFUNIT_CLKGATE_DIS); - - /* Wa_14010680813:dg2_g10 */ - wa_mcr_write_or(wal, XEHP_GAMSTLB_CTRL, - CONTROL_BLOCK_CLKGATE_DIS | - EGRESS_BLOCK_CLKGATE_DIS | - TAG_BLOCK_CLKGATE_DIS); - } - /* Wa_14014830051:dg2 */ wa_mcr_write_clr(wal, SARB_CHICKEN1, COMP_CKN_IN); - /* Wa_14015795083 */ - wa_write_clr(wal, GEN7_MISCCPCTL, GEN12_DOP_CLOCK_GATE_RENDER_ENABLE); + /* + * Wa_14015795083 + * Skip verification for possibly locked register. + */ + wa_add(wal, GEN7_MISCCPCTL, GEN12_DOP_CLOCK_GATE_RENDER_ENABLE, + 0, 0, false); /* Wa_18018781329 */ wa_mcr_write_or(wal, RENDER_MOD_CTRL, FORCE_MISS_FTLB); @@ -1747,8 +1645,8 @@ xelpg_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal) /* Wa_22016670082 */ wa_write_or(wal, GEN12_SQCNT1, GEN12_STRICT_RAR_ENABLE); - if (IS_MTL_GRAPHICS_STEP(gt->i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(gt->i915, P, STEP_A0, STEP_B0)) { + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) { /* Wa_14014830051 */ wa_mcr_write_clr(wal, SARB_CHICKEN1, COMP_CKN_IN); @@ -1791,10 +1689,8 @@ xelpmp_gt_workarounds_init(struct intel_gt *gt, struct i915_wa_list *wal) */ static void gt_tuning_settings(struct intel_gt *gt, struct i915_wa_list *wal) { - if (IS_METEORLAKE(gt->i915)) { - if (gt->type != GT_MEDIA) - wa_mcr_write_or(wal, XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS); - + if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71))) { + wa_mcr_write_or(wal, XEHP_L3SCQREG7, BLEND_FILL_CACHING_OPT_DIS); wa_mcr_write_or(wal, XEHP_SQCM, EN_32B_ACCESS); } @@ -1818,15 +1714,15 @@ gt_init_workarounds(struct intel_gt *gt, struct i915_wa_list *wal) gt_tuning_settings(gt, wal); if (gt->type == GT_MEDIA) { - if (MEDIA_VER(i915) >= 13) + if (MEDIA_VER_FULL(i915) == IP_VER(13, 0)) xelpmp_gt_workarounds_init(gt, wal); else - MISSING_CASE(MEDIA_VER(i915)); + MISSING_CASE(MEDIA_VER_FULL(i915)); return; } - if (GRAPHICS_VER_FULL(i915) >= IP_VER(12, 70)) + if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71))) xelpg_gt_workarounds_init(gt, wal); else if (IS_PONTEVECCHIO(i915)) pvc_gt_workarounds_init(gt, wal); @@ -2242,29 +2138,10 @@ static void dg2_whitelist_build(struct intel_engine_cs *engine) switch (engine->class) { case RENDER_CLASS: - /* - * Wa_1507100340:dg2_g10 - * - * This covers 4 registers which are next to one another : - * - PS_INVOCATION_COUNT - * - PS_INVOCATION_COUNT_UDW - * - PS_DEPTH_COUNT - * - PS_DEPTH_COUNT_UDW - */ - if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_A0, STEP_B0)) - whitelist_reg_ext(w, PS_INVOCATION_COUNT, - RING_FORCE_TO_NONPRIV_ACCESS_RD | - RING_FORCE_TO_NONPRIV_RANGE_4); - /* Required by recommended tuning setting (not a workaround) */ whitelist_mcr_reg(w, XEHP_COMMON_SLICE_CHICKEN3); break; - case COMPUTE_CLASS: - /* Wa_16011157294:dg2_g10 */ - if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_A0, STEP_B0)) - whitelist_reg(w, GEN9_CTX_PREEMPT_REG); - break; default: break; } @@ -2294,7 +2171,7 @@ static void pvc_whitelist_build(struct intel_engine_cs *engine) blacklist_trtt(engine); } -static void mtl_whitelist_build(struct intel_engine_cs *engine) +static void xelpg_whitelist_build(struct intel_engine_cs *engine) { struct i915_wa_list *w = &engine->whitelist; @@ -2316,8 +2193,10 @@ void intel_engine_init_whitelist(struct intel_engine_cs *engine) wa_init_start(w, engine->gt, "whitelist", engine->name); - if (IS_METEORLAKE(i915)) - mtl_whitelist_build(engine); + if (engine->gt->type == GT_MEDIA) + ; /* none yet */ + else if (IS_GFX_GT_IP_RANGE(engine->gt, IP_VER(12, 70), IP_VER(12, 71))) + xelpg_whitelist_build(engine); else if (IS_PONTEVECCHIO(i915)) pvc_whitelist_build(engine); else if (IS_DG2(i915)) @@ -2415,62 +2294,35 @@ engine_fake_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) } } -static bool needs_wa_1308578152(struct intel_engine_cs *engine) -{ - return intel_sseu_find_first_xehp_dss(&engine->gt->info.sseu, 0, 0) >= - GEN_DSS_PER_GSLICE; -} - static void rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) { struct drm_i915_private *i915 = engine->i915; + struct intel_gt *gt = engine->gt; - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) { + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) { /* Wa_22014600077 */ wa_mcr_masked_en(wal, GEN10_CACHE_MODE_SS, ENABLE_EU_COUNT_FOR_TDL_FLUSH); } - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0) || - IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_FOREVER) || - IS_DG2_G11(i915) || IS_DG2_G12(i915)) { + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0) || + IS_DG2(i915)) { /* Wa_1509727124 */ wa_mcr_masked_en(wal, GEN10_SAMPLER_MODE, SC_DISABLE_POWER_OPTIMIZATION_EBB); } - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_FOREVER) || - IS_DG2_G11(i915) || IS_DG2_G12(i915) || - IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0)) { + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_DG2(i915)) { /* Wa_22012856258 */ wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN2, GEN12_DISABLE_READ_SUPPRESSION); } - if (IS_DG2_GRAPHICS_STEP(i915, G11, STEP_A0, STEP_B0)) { - /* Wa_14013392000:dg2_g11 */ - wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN2, GEN12_ENABLE_LARGE_GRF_MODE); - } - - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_B0) || - IS_DG2_GRAPHICS_STEP(i915, G11, STEP_A0, STEP_B0)) { - /* Wa_14012419201:dg2 */ - wa_mcr_masked_en(wal, GEN9_ROW_CHICKEN4, - GEN12_DISABLE_HDR_PAST_PAYLOAD_HOLD_FIX); - } - - /* Wa_1308578152:dg2_g10 when first gslice is fused off */ - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_C0) && - needs_wa_1308578152(engine)) { - wa_masked_dis(wal, GEN12_CS_DEBUG_MODE1_CCCSUNIT_BE_COMMON, - GEN12_REPLAY_MODE_GRANULARITY); - } - - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_FOREVER) || - IS_DG2_G11(i915) || IS_DG2_G12(i915)) { + if (IS_DG2(i915)) { /* * Wa_22010960976:dg2 * Wa_14013347512:dg2 @@ -2479,34 +2331,15 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) LSC_L1_FLUSH_CTL_3D_DATAPORT_FLUSH_EVENTS_MASK); } - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_B0)) { - /* - * Wa_1608949956:dg2_g10 - * Wa_14010198302:dg2_g10 - */ - wa_mcr_masked_en(wal, GEN8_ROW_CHICKEN, - MDQ_ARBITRATION_MODE | UGM_BACKUP_MODE); + if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71)) || + IS_DG2(i915)) { + /* Wa_14015150844 */ + wa_mcr_add(wal, XEHP_HDC_CHICKEN0, 0, + _MASKED_BIT_ENABLE(DIS_ATOMIC_CHAINING_TYPED_WRITES), + 0, true); } - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_B0)) - /* Wa_22010430635:dg2 */ - wa_mcr_masked_en(wal, - GEN9_ROW_CHICKEN4, - GEN12_DISABLE_GRF_CLEAR); - - /* Wa_14013202645:dg2 */ - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_C0) || - IS_DG2_GRAPHICS_STEP(i915, G11, STEP_A0, STEP_B0)) - wa_mcr_write_or(wal, RT_CTRL, DIS_NULL_QUERY); - - /* Wa_22012532006:dg2 */ - if (IS_DG2_GRAPHICS_STEP(engine->i915, G10, STEP_A0, STEP_C0) || - IS_DG2_GRAPHICS_STEP(engine->i915, G11, STEP_A0, STEP_B0)) - wa_mcr_masked_en(wal, GEN9_HALF_SLICE_CHICKEN7, - DG2_DISABLE_ROUND_ENABLE_ALLOW_FOR_SSLA); - - if (IS_DG2_GRAPHICS_STEP(i915, G11, STEP_B0, STEP_FOREVER) || - IS_DG2_G10(i915)) { + if (IS_DG2_G11(i915) || IS_DG2_G10(i915)) { /* Wa_22014600077:dg2 */ wa_mcr_add(wal, GEN10_CACHE_MODE_SS, 0, _MASKED_BIT_ENABLE(ENABLE_EU_COUNT_FOR_TDL_FLUSH), @@ -2514,6 +2347,19 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) true); } + if (IS_DG2(i915) || IS_ALDERLAKE_P(i915) || IS_ALDERLAKE_S(i915) || + IS_DG1(i915) || IS_ROCKETLAKE(i915) || IS_TIGERLAKE(i915)) { + /* + * Wa_1606700617:tgl,dg1,adl-p + * Wa_22010271021:tgl,rkl,dg1,adl-s,adl-p + * Wa_14010826681:tgl,dg1,rkl,adl-p + * Wa_18019627453:dg2 + */ + wa_masked_en(wal, + GEN9_CS_DEBUG_MODE1, + FF_DOP_CLOCK_GATE_DISABLE); + } + if (IS_ALDERLAKE_P(i915) || IS_ALDERLAKE_S(i915) || IS_DG1(i915) || IS_ROCKETLAKE(i915) || IS_TIGERLAKE(i915)) { /* Wa_1606931601:tgl,rkl,dg1,adl-s,adl-p */ @@ -2527,19 +2373,11 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) */ wa_write_or(wal, GEN7_FF_THREAD_MODE, GEN12_FF_TESSELATION_DOP_GATE_DISABLE); - } - if (IS_ALDERLAKE_P(i915) || IS_DG2(i915) || IS_ALDERLAKE_S(i915) || - IS_DG1(i915) || IS_ROCKETLAKE(i915) || IS_TIGERLAKE(i915)) { - /* - * Wa_1606700617:tgl,dg1,adl-p - * Wa_22010271021:tgl,rkl,dg1,adl-s,adl-p - * Wa_14010826681:tgl,dg1,rkl,adl-p - * Wa_18019627453:dg2 - */ - wa_masked_en(wal, - GEN9_CS_DEBUG_MODE1, - FF_DOP_CLOCK_GATE_DISABLE); + /* Wa_1406941453:tgl,rkl,dg1,adl-s,adl-p */ + wa_mcr_masked_en(wal, + GEN10_SAMPLER_MODE, + ENABLE_SMALLPL); } if (IS_ALDERLAKE_P(i915) || IS_ALDERLAKE_S(i915) || @@ -2566,14 +2404,6 @@ rcs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) GEN8_RC_SEMA_IDLE_MSG_DISABLE); } - if (IS_DG1(i915) || IS_ROCKETLAKE(i915) || IS_TIGERLAKE(i915) || - IS_ALDERLAKE_S(i915) || IS_ALDERLAKE_P(i915)) { - /* Wa_1406941453:tgl,rkl,dg1,adl-s,adl-p */ - wa_mcr_masked_en(wal, - GEN10_SAMPLER_MODE, - ENABLE_SMALLPL); - } - if (GRAPHICS_VER(i915) == 11) { /* This is not an Wa. Enable for better image quality */ wa_masked_en(wal, @@ -2975,10 +2805,12 @@ ccs_engine_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) * function invoked by __intel_engine_init_ctx_wa(). */ static void -add_render_compute_tuning_settings(struct drm_i915_private *i915, +add_render_compute_tuning_settings(struct intel_gt *gt, struct i915_wa_list *wal) { - if (IS_METEORLAKE(i915) || IS_DG2(i915)) + struct drm_i915_private *i915 = gt->i915; + + if (IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71)) || IS_DG2(i915)) wa_mcr_write_clr_set(wal, RT_CTRL, STACKID_CTRL, STACKID_CTRL_512); /* @@ -3007,8 +2839,9 @@ static void general_render_compute_wa_init(struct intel_engine_cs *engine, struct i915_wa_list *wal) { struct drm_i915_private *i915 = engine->i915; + struct intel_gt *gt = engine->gt; - add_render_compute_tuning_settings(i915, wal); + add_render_compute_tuning_settings(gt, wal); if (GRAPHICS_VER(i915) >= 11) { /* This is not a Wa (although referred to as @@ -3029,13 +2862,13 @@ general_render_compute_wa_init(struct intel_engine_cs *engine, struct i915_wa_li GEN11_INDIRECT_STATE_BASE_ADDR_OVERRIDE); } - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_B0, STEP_FOREVER) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_B0, STEP_FOREVER)) + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_B0, STEP_FOREVER) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_B0, STEP_FOREVER)) /* Wa_14017856879 */ wa_mcr_masked_en(wal, GEN9_ROW_CHICKEN3, MTL_DISABLE_FIX_FOR_EOT_FLUSH); - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) /* * Wa_14017066071 * Wa_14017654203 @@ -3043,37 +2876,47 @@ general_render_compute_wa_init(struct intel_engine_cs *engine, struct i915_wa_li wa_mcr_masked_en(wal, GEN10_SAMPLER_MODE, MTL_DISABLE_SAMPLER_SC_OOO); - if (IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0)) + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0)) /* Wa_22015279794 */ wa_mcr_masked_en(wal, GEN10_CACHE_MODE_SS, DISABLE_PREFETCH_INTO_IC); - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0) || - IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_FOREVER) || - IS_DG2_G11(i915) || IS_DG2_G12(i915)) { + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0) || + IS_DG2(i915)) { /* Wa_22013037850 */ wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0_UDW, DISABLE_128B_EVICTION_COMMAND_UDW); + + /* Wa_18017747507 */ + wa_masked_en(wal, VFG_PREEMPTION_CHICKEN, POLYGON_TRIFAN_LINELOOP_DISABLE); } - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0) || + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || + IS_GFX_GT_IP_STEP(gt, IP_VER(12, 71), STEP_A0, STEP_B0) || IS_PONTEVECCHIO(i915) || IS_DG2(i915)) { /* Wa_22014226127 */ wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0, DISABLE_D8_D16_COASLESCE); } - if (IS_MTL_GRAPHICS_STEP(i915, M, STEP_A0, STEP_B0) || - IS_MTL_GRAPHICS_STEP(i915, P, STEP_A0, STEP_B0) || - IS_DG2(i915)) { - /* Wa_18017747507 */ - wa_masked_en(wal, VFG_PREEMPTION_CHICKEN, POLYGON_TRIFAN_LINELOOP_DISABLE); + if (IS_PONTEVECCHIO(i915) || IS_DG2(i915)) { + /* Wa_14015227452:dg2,pvc */ + wa_mcr_masked_en(wal, GEN9_ROW_CHICKEN4, XEHP_DIS_BBL_SYSPIPE); + + /* Wa_16015675438:dg2,pvc */ + wa_masked_en(wal, FF_SLICE_CS_CHICKEN2, GEN12_PERF_FIX_BALANCING_CFE_DISABLE); } - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_B0, STEP_C0) || - IS_DG2_G11(i915)) { + if (IS_DG2(i915)) { + /* + * Wa_16011620976:dg2_g11 + * Wa_22015475538:dg2 + */ + wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0_UDW, DIS_CHAIN_2XSIMD8); + } + + if (IS_DG2_G11(i915)) { /* * Wa_22012826095:dg2 * Wa_22013059131:dg2 @@ -3085,18 +2928,23 @@ general_render_compute_wa_init(struct intel_engine_cs *engine, struct i915_wa_li /* Wa_22013059131:dg2 */ wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0, FORCE_1_SUB_MESSAGE_PER_FRAGMENT); - } - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_B0)) { /* - * Wa_14010918519:dg2_g10 + * Wa_22012654132 * - * LSC_CHICKEN_BIT_0 always reads back as 0 is this stepping, - * so ignoring verification. + * Note that register 0xE420 is write-only and cannot be read + * back for verification on DG2 (due to Wa_14012342262), so + * we need to explicitly skip the readback. */ - wa_mcr_add(wal, LSC_CHICKEN_BIT_0_UDW, 0, - FORCE_SLM_FENCE_SCOPE_TO_TILE | FORCE_UGM_FENCE_SCOPE_TO_TILE, - 0, false); + wa_mcr_add(wal, GEN10_CACHE_MODE_SS, 0, + _MASKED_BIT_ENABLE(ENABLE_PREFETCH_INTO_IC), + 0 /* write-only, so skip validation */, + true); + } + + if (IS_DG2_G10(i915) || IS_DG2_G12(i915)) { + /* Wa_18028616096 */ + wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0_UDW, UGM_FRAGMENT_THRESHOLD_TO_3); } if (IS_XEHPSDV(i915)) { @@ -3114,35 +2962,6 @@ general_render_compute_wa_init(struct intel_engine_cs *engine, struct i915_wa_li wa_mcr_masked_en(wal, GEN8_HALF_SLICE_CHICKEN1, GEN7_PSD_SINGLE_PORT_DISPATCH_ENABLE); } - - if (IS_DG2(i915) || IS_PONTEVECCHIO(i915)) { - /* Wa_14015227452:dg2,pvc */ - wa_mcr_masked_en(wal, GEN9_ROW_CHICKEN4, XEHP_DIS_BBL_SYSPIPE); - - /* Wa_16015675438:dg2,pvc */ - wa_masked_en(wal, FF_SLICE_CS_CHICKEN2, GEN12_PERF_FIX_BALANCING_CFE_DISABLE); - } - - if (IS_DG2(i915)) { - /* - * Wa_16011620976:dg2_g11 - * Wa_22015475538:dg2 - */ - wa_mcr_write_or(wal, LSC_CHICKEN_BIT_0_UDW, DIS_CHAIN_2XSIMD8); - } - - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_C0) || IS_DG2_G11(i915)) - /* - * Wa_22012654132 - * - * Note that register 0xE420 is write-only and cannot be read - * back for verification on DG2 (due to Wa_14012342262), so - * we need to explicitly skip the readback. - */ - wa_mcr_add(wal, GEN10_CACHE_MODE_SS, 0, - _MASKED_BIT_ENABLE(ENABLE_PREFETCH_INTO_IC), - 0 /* write-only, so skip validation */, - true); } static void diff --git a/drivers/gpu/drm/i915/gt/selftest_migrate.c b/drivers/gpu/drm/i915/gt/selftest_migrate.c index 3def5ca72dec..1a34cbe04fb6 100644 --- a/drivers/gpu/drm/i915/gt/selftest_migrate.c +++ b/drivers/gpu/drm/i915/gt/selftest_migrate.c @@ -710,7 +710,7 @@ static int threaded_migrate(struct intel_migrate *migrate, thread[i].tsk = tsk; } - msleep(10); /* start all threads before we kthread_stop() */ + msleep(10 * n_cpus); /* start all threads before we kthread_stop() */ for (i = 0; i < n_cpus; ++i) { struct task_struct *tsk = thread[i].tsk; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c b/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c index 89ed5ee9cded..2fde5c360cff 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.c @@ -81,8 +81,17 @@ out_rq: i915_request_add(rq); - if (!err && i915_request_wait(rq, 0, msecs_to_jiffies(500)) < 0) - err = -ETIME; + if (!err) { + /* + * Start timeout for i915_request_wait only after considering one possible + * pending GSC-HECI submission cycle on the other (non-privileged) path. + */ + if (wait_for(i915_request_started(rq), GSC_HECI_REPLY_LATENCY_MS)) + drm_dbg(&gsc_uc_to_gt(gsc)->i915->drm, + "Delay in gsc-heci-priv submission to gsccs-hw"); + if (i915_request_wait(rq, 0, msecs_to_jiffies(GSC_HECI_REPLY_LATENCY_MS)) < 0) + err = -ETIME; + } i915_request_put(rq); @@ -186,6 +195,13 @@ out_rq: i915_request_add(rq); if (!err) { + /* + * Start timeout for i915_request_wait only after considering one possible + * pending GSC-HECI submission cycle on the other (privileged) path. + */ + if (wait_for(i915_request_started(rq), GSC_HECI_REPLY_LATENCY_MS)) + drm_dbg(&gsc_uc_to_gt(gsc)->i915->drm, + "Delay in gsc-heci-non-priv submission to gsccs-hw"); if (i915_request_wait(rq, I915_WAIT_INTERRUPTIBLE, msecs_to_jiffies(timeout_ms)) < 0) err = -ETIME; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.h b/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.h index 09d3fbdad05a..c4308291c003 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_gsc_uc_heci_cmd_submit.h @@ -12,6 +12,12 @@ struct i915_vma; struct intel_context; struct intel_gsc_uc; +#define GSC_HECI_REPLY_LATENCY_MS 500 +/* + * Max FW response time is 500ms, but this should be counted from the time the + * command has hit the GSC-CS hardware, not the preceding handoff to GuC CTB. + */ + struct intel_gsc_mtl_header { u32 validity_marker; #define GSC_HECI_VALIDITY_MARKER 0xA578875A diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc.c index 569b5fe94c41..27df41c53b89 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.c @@ -159,6 +159,21 @@ static void gen11_disable_guc_interrupts(struct intel_guc *guc) gen11_reset_guc_interrupts(guc); } +static void guc_dead_worker_func(struct work_struct *w) +{ + struct intel_guc *guc = container_of(w, struct intel_guc, dead_guc_worker); + struct intel_gt *gt = guc_to_gt(guc); + unsigned long last = guc->last_dead_guc_jiffies; + unsigned long delta = jiffies_to_msecs(jiffies - last); + + if (delta < 500) { + intel_gt_set_wedged(gt); + } else { + intel_gt_handle_error(gt, ALL_ENGINES, I915_ERROR_CAPTURE, "dead GuC"); + guc->last_dead_guc_jiffies = jiffies; + } +} + void intel_guc_init_early(struct intel_guc *guc) { struct intel_gt *gt = guc_to_gt(guc); @@ -171,6 +186,8 @@ void intel_guc_init_early(struct intel_guc *guc) intel_guc_slpc_init_early(&guc->slpc); intel_guc_rc_init_early(guc); + INIT_WORK(&guc->dead_guc_worker, guc_dead_worker_func); + mutex_init(&guc->send_mutex); spin_lock_init(&guc->irq_lock); if (GRAPHICS_VER(i915) >= 11) { @@ -272,18 +289,14 @@ static u32 guc_ctl_wa_flags(struct intel_guc *guc) GRAPHICS_VER_FULL(gt->i915) < IP_VER(12, 50)) flags |= GUC_WA_POLLCS; - /* Wa_16011759253:dg2_g10:a0 */ - if (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_B0)) - flags |= GUC_WA_GAM_CREDITS; - /* Wa_14014475959 */ - if (IS_MTL_GRAPHICS_STEP(gt->i915, M, STEP_A0, STEP_B0) || + if (IS_GFX_GT_IP_STEP(gt, IP_VER(12, 70), STEP_A0, STEP_B0) || IS_DG2(gt->i915)) flags |= GUC_WA_HOLD_CCS_SWITCHOUT; /* - * Wa_14012197797:dg2_g10:a0,dg2_g11:a0 - * Wa_22011391025:dg2_g10,dg2_g11,dg2_g12 + * Wa_14012197797 + * Wa_22011391025 * * The same WA bit is used for both and 22011391025 is applicable to * all DG2. @@ -292,22 +305,14 @@ static u32 guc_ctl_wa_flags(struct intel_guc *guc) flags |= GUC_WA_DUAL_QUEUE; /* Wa_22011802037: graphics version 11/12 */ - if (IS_MTL_GRAPHICS_STEP(gt->i915, M, STEP_A0, STEP_B0) || - (GRAPHICS_VER(gt->i915) >= 11 && - GRAPHICS_VER_FULL(gt->i915) < IP_VER(12, 70))) + if (intel_engine_reset_needs_wa_22011802037(gt)) flags |= GUC_WA_PRE_PARSER; - /* Wa_16011777198:dg2 */ - if (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_C0) || - IS_DG2_GRAPHICS_STEP(gt->i915, G11, STEP_A0, STEP_B0)) - flags |= GUC_WA_RCS_RESET_BEFORE_RC6; - /* - * Wa_22012727170:dg2_g10[a0-c0), dg2_g11[a0..) - * Wa_22012727685:dg2_g11[a0..) + * Wa_22012727170 + * Wa_22012727685 */ - if (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_C0) || - IS_DG2_GRAPHICS_STEP(gt->i915, G11, STEP_A0, STEP_FOREVER)) + if (IS_DG2_G11(gt->i915)) flags |= GUC_WA_CONTEXT_ISOLATION; /* Wa_16015675438 */ @@ -461,6 +466,8 @@ void intel_guc_fini(struct intel_guc *guc) if (!intel_uc_fw_is_loadable(&guc->fw)) return; + flush_work(&guc->dead_guc_worker); + if (intel_guc_slpc_is_used(guc)) intel_guc_slpc_fini(&guc->slpc); @@ -585,6 +592,20 @@ out: return ret; } +int intel_guc_crash_process_msg(struct intel_guc *guc, u32 action) +{ + if (action == INTEL_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED) + guc_err(guc, "Crash dump notification\n"); + else if (action == INTEL_GUC_ACTION_NOTIFY_EXCEPTION) + guc_err(guc, "Exception notification\n"); + else + guc_err(guc, "Unknown crash notification: 0x%04X\n", action); + + queue_work(system_unbound_wq, &guc->dead_guc_worker); + + return 0; +} + int intel_guc_to_host_process_recv_msg(struct intel_guc *guc, const u32 *payload, u32 len) { @@ -601,6 +622,9 @@ int intel_guc_to_host_process_recv_msg(struct intel_guc *guc, if (msg & INTEL_GUC_RECV_MSG_EXCEPTION) guc_err(guc, "Received early exception notification!\n"); + if (msg & (INTEL_GUC_RECV_MSG_CRASH_DUMP_POSTED | INTEL_GUC_RECV_MSG_EXCEPTION)) + queue_work(system_unbound_wq, &guc->dead_guc_worker); + return 0; } @@ -640,6 +664,8 @@ int intel_guc_suspend(struct intel_guc *guc) return 0; if (intel_guc_submission_is_used(guc)) { + flush_work(&guc->dead_guc_worker); + /* * This H2G MMIO command tears down the GuC in two steps. First it will * generate a G2H CTB for every active context indicating a reset. In diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index 8dc291ff0093..6c392bad29c1 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -266,6 +266,20 @@ struct intel_guc { unsigned long last_stat_jiffies; } timestamp; + /** + * @dead_guc_worker: Asynchronous worker thread for forcing a GuC reset. + * Specifically used when the G2H handler wants to issue a reset. Resets + * require flushing the G2H queue. So, the G2H processing itself must not + * trigger a reset directly. Instead, go via this worker. + */ + struct work_struct dead_guc_worker; + /** + * @last_dead_guc_jiffies: timestamp of previous 'dead guc' occurrance + * used to prevent a fundamentally broken system from continuously + * reloading the GuC. + */ + unsigned long last_dead_guc_jiffies; + #ifdef CONFIG_DRM_I915_SELFTEST /** * @number_guc_id_stolen: The number of guc_ids that have been stolen @@ -476,6 +490,7 @@ int intel_guc_engine_failure_process_msg(struct intel_guc *guc, const u32 *msg, u32 len); int intel_guc_error_capture_process_msg(struct intel_guc *guc, const u32 *msg, u32 len); +int intel_guc_crash_process_msg(struct intel_guc *guc, u32 action); struct intel_engine_cs * intel_guc_lookup_engine(struct intel_guc *guc, u8 guc_class, u8 instance); diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c index 97eadd08181d..6e22af31513a 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_ct.c @@ -1112,12 +1112,8 @@ static int ct_process_request(struct intel_guc_ct *ct, struct ct_incoming_msg *r ret = 0; break; case INTEL_GUC_ACTION_NOTIFY_CRASH_DUMP_POSTED: - CT_ERROR(ct, "Received GuC crash dump notification!\n"); - ret = 0; - break; case INTEL_GUC_ACTION_NOTIFY_EXCEPTION: - CT_ERROR(ct, "Received GuC exception notification!\n"); - ret = 0; + ret = intel_guc_crash_process_msg(guc, action); break; default: ret = -EOPNOTSUPP; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c index 477df260ae3a..2dfb07cc4b33 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c @@ -138,17 +138,6 @@ static int guc_action_slpc_set_param(struct intel_guc *guc, u8 id, u32 value) return ret > 0 ? -EPROTO : ret; } -static int guc_action_slpc_unset_param(struct intel_guc *guc, u8 id) -{ - u32 request[] = { - GUC_ACTION_HOST2GUC_PC_SLPC_REQUEST, - SLPC_EVENT(SLPC_EVENT_PARAMETER_UNSET, 1), - id, - }; - - return intel_guc_send(guc, request, ARRAY_SIZE(request)); -} - static bool slpc_is_running(struct intel_guc_slpc *slpc) { return slpc_get_state(slpc) == SLPC_GLOBAL_STATE_RUNNING; @@ -199,15 +188,6 @@ static int slpc_set_param(struct intel_guc_slpc *slpc, u8 id, u32 value) return ret; } -static int slpc_unset_param(struct intel_guc_slpc *slpc, u8 id) -{ - struct intel_guc *guc = slpc_to_guc(slpc); - - GEM_BUG_ON(id >= SLPC_MAX_PARAM); - - return guc_action_slpc_unset_param(guc, id); -} - static int slpc_force_min_freq(struct intel_guc_slpc *slpc, u32 freq) { struct intel_guc *guc = slpc_to_guc(slpc); @@ -672,49 +652,6 @@ static void slpc_get_rp_values(struct intel_guc_slpc *slpc) slpc->boost_freq = slpc->rp0_freq; } -/** - * intel_guc_slpc_override_gucrc_mode() - override GUCRC mode - * @slpc: pointer to intel_guc_slpc. - * @mode: new value of the mode. - * - * This function will override the GUCRC mode. - * - * Return: 0 on success, non-zero error code on failure. - */ -int intel_guc_slpc_override_gucrc_mode(struct intel_guc_slpc *slpc, u32 mode) -{ - int ret; - struct drm_i915_private *i915 = slpc_to_i915(slpc); - intel_wakeref_t wakeref; - - if (mode >= SLPC_GUCRC_MODE_MAX) - return -EINVAL; - - with_intel_runtime_pm(&i915->runtime_pm, wakeref) { - ret = slpc_set_param(slpc, SLPC_PARAM_PWRGATE_RC_MODE, mode); - if (ret) - guc_err(slpc_to_guc(slpc), "Override RC mode %d failed: %pe\n", - mode, ERR_PTR(ret)); - } - - return ret; -} - -int intel_guc_slpc_unset_gucrc_mode(struct intel_guc_slpc *slpc) -{ - struct drm_i915_private *i915 = slpc_to_i915(slpc); - intel_wakeref_t wakeref; - int ret = 0; - - with_intel_runtime_pm(&i915->runtime_pm, wakeref) { - ret = slpc_unset_param(slpc, SLPC_PARAM_PWRGATE_RC_MODE); - if (ret) - guc_err(slpc_to_guc(slpc), "Unsetting RC mode failed: %pe\n", ERR_PTR(ret)); - } - - return ret; -} - /* * intel_guc_slpc_enable() - Start SLPC * @slpc: pointer to intel_guc_slpc. diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.h index 597eb5413ddf..6ac6503c39d4 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.h @@ -44,8 +44,6 @@ int intel_guc_slpc_set_media_ratio_mode(struct intel_guc_slpc *slpc, u32 val); void intel_guc_pm_intrmsk_enable(struct intel_gt *gt); void intel_guc_slpc_boost(struct intel_guc_slpc *slpc); void intel_guc_slpc_dec_waiters(struct intel_guc_slpc *slpc); -int intel_guc_slpc_unset_gucrc_mode(struct intel_guc_slpc *slpc); -int intel_guc_slpc_override_gucrc_mode(struct intel_guc_slpc *slpc, u32 mode); int intel_guc_slpc_set_ignore_eff_freq(struct intel_guc_slpc *slpc, bool val); #endif diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c index b5b7f2fe8c78..ae3495a9c814 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_submission.c @@ -1433,6 +1433,36 @@ static void guc_timestamp_ping(struct work_struct *wrk) int srcu, ret; /* + * Ideally the busyness worker should take a gt pm wakeref because the + * worker only needs to be active while gt is awake. However, the + * gt_park path cancels the worker synchronously and this complicates + * the flow if the worker is also running at the same time. The cancel + * waits for the worker and when the worker releases the wakeref, that + * would call gt_park and would lead to a deadlock. + * + * The resolution is to take the global pm wakeref if runtime pm is + * already active. If not, we don't need to update the busyness stats as + * the stats would already be updated when the gt was parked. + * + * Note: + * - We do not requeue the worker if we cannot take a reference to runtime + * pm since intel_guc_busyness_unpark would requeue the worker in the + * resume path. + * + * - If the gt was parked longer than time taken for GT timestamp to roll + * over, we ignore those rollovers since we don't care about tracking + * the exact GT time. We only care about roll overs when the gt is + * active and running workloads. + * + * - There is a window of time between gt_park and runtime suspend, + * where the worker may run. This is acceptable since the worker will + * not find any new data to update busyness. + */ + wakeref = intel_runtime_pm_get_if_active(>->i915->runtime_pm); + if (!wakeref) + return; + + /* * Synchronize with gt reset to make sure the worker does not * corrupt the engine/guc stats. NB: can't actually block waiting * for a reset to complete as the reset requires flushing out @@ -1440,10 +1470,9 @@ static void guc_timestamp_ping(struct work_struct *wrk) */ ret = intel_gt_reset_trylock(gt, &srcu); if (ret) - return; + goto err_trylock; - with_intel_runtime_pm(>->i915->runtime_pm, wakeref) - __update_guc_busyness_stats(guc); + __update_guc_busyness_stats(guc); /* adjust context stats for overflow */ xa_for_each(&guc->context_lookup, index, ce) @@ -1452,6 +1481,9 @@ static void guc_timestamp_ping(struct work_struct *wrk) intel_gt_reset_unlock(gt, srcu); guc_enable_busyness_worker(guc); + +err_trylock: + intel_runtime_pm_put(>->i915->runtime_pm, wakeref); } static int guc_action_enable_usage_stats(struct intel_guc *guc) @@ -1658,9 +1690,7 @@ static void guc_engine_reset_prepare(struct intel_engine_cs *engine) * Wa_22011802037: In addition to stopping the cs, we need * to wait for any pending mi force wakeups */ - if (IS_MTL_GRAPHICS_STEP(engine->i915, M, STEP_A0, STEP_B0) || - (GRAPHICS_VER(engine->i915) >= 11 && - GRAPHICS_VER_FULL(engine->i915) < IP_VER(12, 70))) { + if (intel_engine_reset_needs_wa_22011802037(engine->gt)) { intel_engine_stop_cs(engine); intel_engine_wait_for_pending_mi_fw(engine); } @@ -4267,7 +4297,7 @@ static void guc_default_vfuncs(struct intel_engine_cs *engine) /* Wa_14014475959:dg2 */ if (engine->class == COMPUTE_CLASS) - if (IS_MTL_GRAPHICS_STEP(engine->i915, M, STEP_A0, STEP_B0) || + if (IS_GFX_GT_IP_STEP(engine->gt, IP_VER(12, 70), STEP_A0, STEP_B0) || IS_DG2(engine->i915)) engine->flags |= I915_ENGINE_USES_WA_HOLD_CCS_SWITCHOUT; diff --git a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c index 8be005de1d28..32e27e9a2490 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_uc_fw.c @@ -132,6 +132,17 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, fw_def(SKYLAKE, 0, huc_mmp(skl, 2, 0, 0)) /* + * The GSC FW has multiple version (see intel_gsc_uc.h for details); since what + * we care about is the interface, we use the compatibility version in the + * binary names. + * Same as with the GuC, a major version bump indicate a + * backward-incompatible change, while a minor version bump indicates a + * backward-compatible one, so we use only the former in the file name. + */ +#define INTEL_GSC_FIRMWARE_DEFS(fw_def, gsc_def) \ + fw_def(METEORLAKE, 0, gsc_def(mtl, 1, 0)) + +/* * Set of macros for producing a list of filenames from the above table. */ #define __MAKE_UC_FW_PATH_BLANK(prefix_, name_) \ @@ -166,6 +177,9 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, #define MAKE_HUC_FW_PATH_MMP(prefix_, major_, minor_, patch_) \ __MAKE_UC_FW_PATH_MMP(prefix_, "huc", major_, minor_, patch_) +#define MAKE_GSC_FW_PATH(prefix_, major_, minor_) \ + __MAKE_UC_FW_PATH_MAJOR(prefix_, "gsc", major_) + /* * All blobs need to be declared via MODULE_FIRMWARE(). * This first expansion of the table macros is solely to provide @@ -176,6 +190,7 @@ void intel_uc_fw_change_status(struct intel_uc_fw *uc_fw, INTEL_GUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GUC_FW_PATH_MAJOR, MAKE_GUC_FW_PATH_MMP) INTEL_HUC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_HUC_FW_PATH_BLANK, MAKE_HUC_FW_PATH_MMP, MAKE_HUC_FW_PATH_GSC) +INTEL_GSC_FIRMWARE_DEFS(INTEL_UC_MODULE_FW, MAKE_GSC_FW_PATH) /* * The next expansion of the table macros (in __uc_fw_auto_select below) provides @@ -225,6 +240,10 @@ struct __packed uc_fw_blob { #define HUC_FW_BLOB_GSC(prefix_) \ UC_FW_BLOB_NEW(0, 0, 0, true, MAKE_HUC_FW_PATH_GSC(prefix_)) +#define GSC_FW_BLOB(prefix_, major_, minor_) \ + UC_FW_BLOB_NEW(major_, minor_, 0, true, \ + MAKE_GSC_FW_PATH(prefix_, major_, minor_)) + struct __packed uc_fw_platform_requirement { enum intel_platform p; u8 rev; /* first platform rev using this FW */ @@ -251,9 +270,14 @@ static const struct uc_fw_platform_requirement blobs_huc[] = { INTEL_HUC_FIRMWARE_DEFS(MAKE_FW_LIST, HUC_FW_BLOB, HUC_FW_BLOB_MMP, HUC_FW_BLOB_GSC) }; +static const struct uc_fw_platform_requirement blobs_gsc[] = { + INTEL_GSC_FIRMWARE_DEFS(MAKE_FW_LIST, GSC_FW_BLOB) +}; + static const struct fw_blobs_by_type blobs_all[INTEL_UC_FW_NUM_TYPES] = { [INTEL_UC_FW_TYPE_GUC] = { blobs_guc, ARRAY_SIZE(blobs_guc) }, [INTEL_UC_FW_TYPE_HUC] = { blobs_huc, ARRAY_SIZE(blobs_huc) }, + [INTEL_UC_FW_TYPE_GSC] = { blobs_gsc, ARRAY_SIZE(blobs_gsc) }, }; static void @@ -267,14 +291,6 @@ __uc_fw_auto_select(struct drm_i915_private *i915, struct intel_uc_fw *uc_fw) bool found; /* - * GSC FW support is still not fully in place, so we're not defining - * the FW blob yet because we don't want the driver to attempt to load - * it until we're ready for it. - */ - if (uc_fw->type == INTEL_UC_FW_TYPE_GSC) - return; - - /* * The only difference between the ADL GuC FWs is the HWConfig support. * ADL-N does not support HWConfig, so we should use the same binary as * ADL-S, otherwise the GuC might attempt to fetch a config table that diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index 4de44cf1026d..e9b79c2c37d8 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -144,7 +144,7 @@ static const char *i915_cache_level_str(struct drm_i915_gem_object *obj) { struct drm_i915_private *i915 = obj_to_i915(obj); - if (IS_METEORLAKE(i915)) { + if (IS_GFX_GT_IP_RANGE(to_gt(i915), IP_VER(12, 70), IP_VER(12, 71))) { switch (obj->pat_index) { case 0: return " WB"; case 1: return " WT"; @@ -740,15 +740,19 @@ static int i915_drop_caches_set(void *data, u64 val) { struct drm_i915_private *i915 = data; + struct intel_gt *gt; unsigned int flags; + unsigned int i; int ret; drm_dbg(&i915->drm, "Dropping caches: 0x%08llx [0x%08llx]\n", val, val & DROP_ALL); - ret = gt_drop_caches(to_gt(i915), val); - if (ret) - return ret; + for_each_gt(gt, i915, i) { + ret = gt_drop_caches(gt, val); + if (ret) + return ret; + } fs_reclaim_acquire(GFP_KERNEL); flags = memalloc_noreclaim_save(); diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e4d791ad6751..da2217c16153 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -567,10 +567,6 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define IS_PONTEVECCHIO(i915) IS_PLATFORM(i915, INTEL_PONTEVECCHIO) #define IS_METEORLAKE(i915) IS_PLATFORM(i915, INTEL_METEORLAKE) -#define IS_METEORLAKE_M(i915) \ - IS_SUBPLATFORM(i915, INTEL_METEORLAKE, INTEL_SUBPLATFORM_M) -#define IS_METEORLAKE_P(i915) \ - IS_SUBPLATFORM(i915, INTEL_METEORLAKE, INTEL_SUBPLATFORM_P) #define IS_DG2_G10(i915) \ IS_SUBPLATFORM(i915, INTEL_DG2, INTEL_SUBPLATFORM_G10) #define IS_DG2_G11(i915) \ @@ -645,33 +641,6 @@ IS_SUBPLATFORM(const struct drm_i915_private *i915, #define IS_XEHPSDV_GRAPHICS_STEP(__i915, since, until) \ (IS_XEHPSDV(__i915) && IS_GRAPHICS_STEP(__i915, since, until)) -#define IS_MTL_GRAPHICS_STEP(__i915, variant, since, until) \ - (IS_SUBPLATFORM(__i915, INTEL_METEORLAKE, INTEL_SUBPLATFORM_##variant) && \ - IS_GRAPHICS_STEP(__i915, since, until)) - -#define IS_MTL_MEDIA_STEP(__i915, since, until) \ - (IS_METEORLAKE(__i915) && \ - IS_MEDIA_STEP(__i915, since, until)) - -/* - * DG2 hardware steppings are a bit unusual. The hardware design was forked to - * create three variants (G10, G11, and G12) which each have distinct - * workaround sets. The G11 and G12 forks of the DG2 design reset the GT - * stepping back to "A0" for their first iterations, even though they're more - * similar to a G10 B0 stepping and G10 C0 stepping respectively in terms of - * functionality and workarounds. However the display stepping does not reset - * in the same manner --- a specific stepping like "B0" has a consistent - * meaning regardless of whether it belongs to a G10, G11, or G12 DG2. - * - * TLDR: All GT workarounds and stepping-specific logic must be applied in - * relation to a specific subplatform (G10/G11/G12), whereas display workarounds - * and stepping-specific logic will be applied with a general DG2-wide stepping - * number. - */ -#define IS_DG2_GRAPHICS_STEP(__i915, variant, since, until) \ - (IS_SUBPLATFORM(__i915, INTEL_DG2, INTEL_SUBPLATFORM_##variant) && \ - IS_GRAPHICS_STEP(__i915, since, until)) - #define IS_PVC_BD_STEP(__i915, since, until) \ (IS_PONTEVECCHIO(__i915) && \ IS_BASEDIE_STEP(__i915, since, until)) diff --git a/drivers/gpu/drm/i915/i915_perf.c b/drivers/gpu/drm/i915/i915_perf.c index 04bc1f4a1115..1347e4ec9dd5 100644 --- a/drivers/gpu/drm/i915/i915_perf.c +++ b/drivers/gpu/drm/i915/i915_perf.c @@ -543,10 +543,9 @@ static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream) { u32 gtt_offset = i915_ggtt_offset(stream->oa_buffer.vma); int report_size = stream->oa_buffer.format->size; - u32 head, tail, read_tail; + u32 tail, hw_tail; unsigned long flags; bool pollin; - u32 hw_tail; u32 partial_report_size; /* We have to consider the (unlikely) possibility that read() errors @@ -556,6 +555,7 @@ static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream) spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags); hw_tail = stream->perf->ops.oa_hw_tail_read(stream); + hw_tail -= gtt_offset; /* The tail pointer increases in 64 byte increments, not in report_size * steps. Also the report size may not be a power of 2. Compute @@ -567,13 +567,6 @@ static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream) /* Subtract partial amount off the tail */ hw_tail = OA_TAKEN(hw_tail, partial_report_size); - /* NB: The head we observe here might effectively be a little - * out of date. If a read() is in progress, the head could be - * anywhere between this head and stream->oa_buffer.tail. - */ - head = stream->oa_buffer.head - gtt_offset; - read_tail = stream->oa_buffer.tail - gtt_offset; - tail = hw_tail; /* Walk the stream backward until we find a report with report @@ -587,7 +580,7 @@ static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream) * memory in the order they were written to. * If not : (╯°□°)╯︵ ┻━┻ */ - while (OA_TAKEN(tail, read_tail) >= report_size) { + while (OA_TAKEN(tail, stream->oa_buffer.tail) >= report_size) { void *report = stream->oa_buffer.vaddr + tail; if (oa_report_id(stream, report) || @@ -601,9 +594,9 @@ static bool oa_buffer_check_unlocked(struct i915_perf_stream *stream) __ratelimit(&stream->perf->tail_pointer_race)) drm_notice(&stream->uncore->i915->drm, "unlanded report(s) head=0x%x tail=0x%x hw_tail=0x%x\n", - head, tail, hw_tail); + stream->oa_buffer.head, tail, hw_tail); - stream->oa_buffer.tail = gtt_offset + tail; + stream->oa_buffer.tail = tail; pollin = OA_TAKEN(stream->oa_buffer.tail, stream->oa_buffer.head) >= report_size; @@ -754,13 +747,6 @@ static int gen8_append_oa_reports(struct i915_perf_stream *stream, spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags); /* - * NB: oa_buffer.head/tail include the gtt_offset which we don't want - * while indexing relative to oa_buf_base. - */ - head -= gtt_offset; - tail -= gtt_offset; - - /* * An out of bounds or misaligned head or tail pointer implies a driver * bug since we validate + align the tail pointers we read from the * hardware and we are in full control of the head pointer which should @@ -895,9 +881,8 @@ static int gen8_append_oa_reports(struct i915_perf_stream *stream, * We removed the gtt_offset for the copy loop above, indexing * relative to oa_buf_base so put back here... */ - head += gtt_offset; intel_uncore_write(uncore, oaheadptr, - head & GEN12_OAG_OAHEADPTR_MASK); + (head + gtt_offset) & GEN12_OAG_OAHEADPTR_MASK); stream->oa_buffer.head = head; spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags); @@ -1042,12 +1027,6 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream, spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags); - /* NB: oa_buffer.head/tail include the gtt_offset which we don't want - * while indexing relative to oa_buf_base. - */ - head -= gtt_offset; - tail -= gtt_offset; - /* An out of bounds or misaligned head or tail pointer implies a driver * bug since we validate + align the tail pointers we read from the * hardware and we are in full control of the head pointer which should @@ -1110,13 +1089,8 @@ static int gen7_append_oa_reports(struct i915_perf_stream *stream, if (start_offset != *offset) { spin_lock_irqsave(&stream->oa_buffer.ptr_lock, flags); - /* We removed the gtt_offset for the copy loop above, indexing - * relative to oa_buf_base so put back here... - */ - head += gtt_offset; - intel_uncore_write(uncore, GEN7_OASTATUS2, - (head & GEN7_OASTATUS2_HEAD_MASK) | + ((head + gtt_offset) & GEN7_OASTATUS2_HEAD_MASK) | GEN7_OASTATUS2_MEM_SELECT_GGTT); stream->oa_buffer.head = head; @@ -1675,13 +1649,6 @@ static void i915_oa_stream_destroy(struct i915_perf_stream *stream) free_oa_buffer(stream); - /* - * Wa_16011777198:dg2: Unset the override of GUCRC mode to enable rc6. - */ - if (stream->override_gucrc) - drm_WARN_ON(>->i915->drm, - intel_guc_slpc_unset_gucrc_mode(>->uc.guc.slpc)); - intel_uncore_forcewake_put(stream->uncore, FORCEWAKE_ALL); intel_engine_pm_put(stream->engine); @@ -1711,7 +1678,7 @@ static void gen7_init_oa_buffer(struct i915_perf_stream *stream) */ intel_uncore_write(uncore, GEN7_OASTATUS2, /* head */ gtt_offset | GEN7_OASTATUS2_MEM_SELECT_GGTT); - stream->oa_buffer.head = gtt_offset; + stream->oa_buffer.head = 0; intel_uncore_write(uncore, GEN7_OABUFFER, gtt_offset); @@ -1719,7 +1686,7 @@ static void gen7_init_oa_buffer(struct i915_perf_stream *stream) gtt_offset | OABUFFER_SIZE_16M); /* Mark that we need updated tail pointers to read from... */ - stream->oa_buffer.tail = gtt_offset; + stream->oa_buffer.tail = 0; spin_unlock_irqrestore(&stream->oa_buffer.ptr_lock, flags); @@ -1753,7 +1720,7 @@ static void gen8_init_oa_buffer(struct i915_perf_stream *stream) intel_uncore_write(uncore, GEN8_OASTATUS, 0); intel_uncore_write(uncore, GEN8_OAHEADPTR, gtt_offset); - stream->oa_buffer.head = gtt_offset; + stream->oa_buffer.head = 0; intel_uncore_write(uncore, GEN8_OABUFFER_UDW, 0); @@ -1770,7 +1737,7 @@ static void gen8_init_oa_buffer(struct i915_perf_stream *stream) intel_uncore_write(uncore, GEN8_OATAILPTR, gtt_offset & GEN8_OATAILPTR_MASK); /* Mark that we need updated tail pointers to read from... */ - stream->oa_buffer.tail = gtt_offset; + stream->oa_buffer.tail = 0; /* * Reset state used to recognise context switches, affecting which @@ -1807,7 +1774,7 @@ static void gen12_init_oa_buffer(struct i915_perf_stream *stream) intel_uncore_write(uncore, __oa_regs(stream)->oa_status, 0); intel_uncore_write(uncore, __oa_regs(stream)->oa_head_ptr, gtt_offset & GEN12_OAG_OAHEADPTR_MASK); - stream->oa_buffer.head = gtt_offset; + stream->oa_buffer.head = 0; /* * PRM says: @@ -1823,7 +1790,7 @@ static void gen12_init_oa_buffer(struct i915_perf_stream *stream) gtt_offset & GEN12_OAG_OATAILPTR_MASK); /* Mark that we need updated tail pointers to read from... */ - stream->oa_buffer.tail = gtt_offset; + stream->oa_buffer.tail = 0; /* * Reset state used to recognise context switches, affecting which @@ -3227,11 +3194,10 @@ get_sseu_config(struct intel_sseu *out_sseu, */ u32 i915_perf_oa_timestamp_frequency(struct drm_i915_private *i915) { - /* - * Wa_18013179988:dg2 - * Wa_14015846243:mtl - */ - if (IS_DG2(i915) || IS_METEORLAKE(i915)) { + struct intel_gt *gt = to_gt(i915); + + /* Wa_18013179988 */ + if (IS_DG2(i915) || IS_GFX_GT_IP_RANGE(gt, IP_VER(12, 70), IP_VER(12, 71))) { intel_wakeref_t wakeref; u32 reg, shift; @@ -3272,7 +3238,6 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream, struct drm_i915_private *i915 = stream->perf->i915; struct i915_perf *perf = stream->perf; struct i915_perf_group *g; - struct intel_gt *gt; int ret; if (!props->engine) { @@ -3280,7 +3245,6 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream, "OA engine not specified\n"); return -EINVAL; } - gt = props->engine->gt; g = props->engine->oa_group; /* @@ -3381,25 +3345,6 @@ static int i915_oa_stream_init(struct i915_perf_stream *stream, intel_engine_pm_get(stream->engine); intel_uncore_forcewake_get(stream->uncore, FORCEWAKE_ALL); - /* - * Wa_16011777198:dg2: GuC resets render as part of the Wa. This causes - * OA to lose the configuration state. Prevent this by overriding GUCRC - * mode. - */ - if (intel_uc_uses_guc_rc(>->uc) && - (IS_DG2_GRAPHICS_STEP(gt->i915, G10, STEP_A0, STEP_C0) || - IS_DG2_GRAPHICS_STEP(gt->i915, G11, STEP_A0, STEP_B0))) { - ret = intel_guc_slpc_override_gucrc_mode(>->uc.guc.slpc, - SLPC_GUCRC_MODE_GUCRC_NO_RC6); - if (ret) { - drm_dbg(&stream->perf->i915->drm, - "Unable to override gucrc mode\n"); - goto err_gucrc; - } - - stream->override_gucrc = true; - } - ret = alloc_oa_buffer(stream); if (ret) goto err_oa_buf_alloc; @@ -3436,10 +3381,6 @@ err_enable: free_oa_buffer(stream); err_oa_buf_alloc: - if (stream->override_gucrc) - intel_guc_slpc_unset_gucrc_mode(>->uc.guc.slpc); - -err_gucrc: intel_uncore_forcewake_put(stream->uncore, FORCEWAKE_ALL); intel_engine_pm_put(stream->engine); @@ -4223,7 +4164,7 @@ static int read_properties_unlocked(struct i915_perf *perf, * C6 disable in BIOS. Fail if Media C6 is enabled on steppings where OAM * does not work as expected. */ - if (IS_MTL_MEDIA_STEP(props->engine->i915, STEP_A0, STEP_C0) && + if (IS_MEDIA_GT_IP_STEP(props->engine->gt, IP_VER(13, 0), STEP_A0, STEP_C0) && props->engine->oa_group->type == TYPE_OAM && intel_check_bios_c6_setup(&props->engine->gt->rc6)) { drm_dbg(&perf->i915->drm, @@ -4539,7 +4480,7 @@ static bool xehp_is_valid_b_counter_addr(struct i915_perf *perf, u32 addr) static bool gen12_is_valid_mux_addr(struct i915_perf *perf, u32 addr) { - if (IS_METEORLAKE(perf->i915)) + if (GRAPHICS_VER_FULL(perf->i915) >= IP_VER(12, 70)) return reg_in_range_table(addr, mtl_oa_mux_regs); else return reg_in_range_table(addr, gen12_oa_mux_regs); @@ -5332,16 +5273,9 @@ int i915_perf_ioctl_version(struct drm_i915_private *i915) * C6 disable in BIOS. If Media C6 is enabled in BIOS, return version 6 * to indicate that OA media is not supported. */ - if (IS_MTL_MEDIA_STEP(i915, STEP_A0, STEP_C0)) { - struct intel_gt *gt; - int i; - - for_each_gt(gt, i915, i) { - if (gt->type == GT_MEDIA && - intel_check_bios_c6_setup(>->rc6)) - return 6; - } - } + if (IS_MEDIA_GT_IP_STEP(i915->media_gt, IP_VER(13, 0), STEP_A0, STEP_C0) && + intel_check_bios_c6_setup(&i915->media_gt->rc6)) + return 6; return 7; } diff --git a/drivers/gpu/drm/i915/i915_perf_types.h b/drivers/gpu/drm/i915/i915_perf_types.h index fe3a5dae8c22..13b1ae9b96c7 100644 --- a/drivers/gpu/drm/i915/i915_perf_types.h +++ b/drivers/gpu/drm/i915/i915_perf_types.h @@ -338,12 +338,6 @@ struct i915_perf_stream { * buffer should be checked for available data. */ u64 poll_oa_period; - - /** - * @override_gucrc: GuC RC has been overridden for the perf stream, - * and we need to restore the default configuration on release. - */ - bool override_gucrc; }; /** diff --git a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c index a1bc804cfa15..0d735d5c2b35 100644 --- a/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c +++ b/drivers/gpu/drm/i915/i915_ttm_buddy_manager.c @@ -59,6 +59,9 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, if (place->flags & TTM_PL_FLAG_TOPDOWN) bman_res->flags |= DRM_BUDDY_TOPDOWN_ALLOCATION; + if (place->flags & TTM_PL_FLAG_CONTIGUOUS) + bman_res->flags |= DRM_BUDDY_CONTIGUOUS_ALLOCATION; + if (place->fpfn || lpfn != man->size) bman_res->flags |= DRM_BUDDY_RANGE_ALLOCATION; @@ -72,18 +75,6 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, GEM_BUG_ON(min_page_size < mm->chunk_size); GEM_BUG_ON(!IS_ALIGNED(size, min_page_size)); - if (place->fpfn + PFN_UP(bman_res->base.size) != place->lpfn && - place->flags & TTM_PL_FLAG_CONTIGUOUS) { - unsigned long pages; - - size = roundup_pow_of_two(size); - min_page_size = size; - - pages = size >> ilog2(mm->chunk_size); - if (pages > lpfn) - lpfn = pages; - } - if (size > lpfn << PAGE_SHIFT) { err = -E2BIG; goto err_free_res; @@ -107,14 +98,6 @@ static int i915_ttm_buddy_man_alloc(struct ttm_resource_manager *man, if (unlikely(err)) goto err_free_blocks; - if (place->flags & TTM_PL_FLAG_CONTIGUOUS) { - u64 original_size = (u64)bman_res->base.size; - - drm_buddy_block_trim(mm, - original_size, - &bman_res->blocks); - } - if (lpfn <= bman->visible_size) { bman_res->used_visible_size = PFN_UP(bman_res->base.size); } else { diff --git a/drivers/gpu/drm/i915/intel_clock_gating.c b/drivers/gpu/drm/i915/intel_clock_gating.c index 6589bb04e788..9c21ce69bd98 100644 --- a/drivers/gpu/drm/i915/intel_clock_gating.c +++ b/drivers/gpu/drm/i915/intel_clock_gating.c @@ -361,14 +361,6 @@ static void dg2_init_clock_gating(struct drm_i915_private *i915) /* Wa_22010954014:dg2 */ intel_uncore_rmw(&i915->uncore, XEHP_CLOCK_GATE_DIS, 0, SGSI_SIDECLK_DIS); - - /* - * Wa_14010733611:dg2_g10 - * Wa_22010146351:dg2_g10 - */ - if (IS_DG2_GRAPHICS_STEP(i915, G10, STEP_A0, STEP_B0)) - intel_uncore_rmw(&i915->uncore, XEHP_CLOCK_GATE_DIS, 0, - SGR_DIS | SGGI_DIS); } static void pvc_init_clock_gating(struct drm_i915_private *i915) diff --git a/drivers/gpu/drm/i915/intel_device_info.c b/drivers/gpu/drm/i915/intel_device_info.c index 64295e6702b0..59bea1398c91 100644 --- a/drivers/gpu/drm/i915/intel_device_info.c +++ b/drivers/gpu/drm/i915/intel_device_info.c @@ -205,14 +205,6 @@ static const u16 subplatform_g12_ids[] = { INTEL_DG2_G12_IDS(0), }; -static const u16 subplatform_m_ids[] = { - INTEL_MTL_M_IDS(0), -}; - -static const u16 subplatform_p_ids[] = { - INTEL_MTL_P_IDS(0), -}; - static bool find_devid(u16 id, const u16 *p, unsigned int num) { for (; num; num--, p++) { @@ -270,12 +262,6 @@ static void intel_device_info_subplatform_init(struct drm_i915_private *i915) } else if (find_devid(devid, subplatform_g12_ids, ARRAY_SIZE(subplatform_g12_ids))) { mask = BIT(INTEL_SUBPLATFORM_G12); - } else if (find_devid(devid, subplatform_m_ids, - ARRAY_SIZE(subplatform_m_ids))) { - mask = BIT(INTEL_SUBPLATFORM_M); - } else if (find_devid(devid, subplatform_p_ids, - ARRAY_SIZE(subplatform_p_ids))) { - mask = BIT(INTEL_SUBPLATFORM_P); } GEM_BUG_ON(mask & ~INTEL_SUBPLATFORM_MASK); diff --git a/drivers/gpu/drm/i915/intel_device_info.h b/drivers/gpu/drm/i915/intel_device_info.h index 19d120728ff1..39817490b13f 100644 --- a/drivers/gpu/drm/i915/intel_device_info.h +++ b/drivers/gpu/drm/i915/intel_device_info.h @@ -129,10 +129,6 @@ enum intel_platform { #define INTEL_SUBPLATFORM_N 1 #define INTEL_SUBPLATFORM_RPLU 2 -/* MTL */ -#define INTEL_SUBPLATFORM_M 0 -#define INTEL_SUBPLATFORM_P 1 - enum intel_ppgtt_type { INTEL_PPGTT_NONE = I915_GEM_PPGTT_NONE, INTEL_PPGTT_ALIASING = I915_GEM_PPGTT_ALIASING, diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_43.h b/drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_43.h index 0165d38fbead..329b4fcdc040 100644 --- a/drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_43.h +++ b/drivers/gpu/drm/i915/pxp/intel_pxp_cmd_interface_43.h @@ -14,8 +14,8 @@ #define PXP43_CMDID_NEW_HUC_AUTH 0x0000003F /* MTL+ */ #define PXP43_CMDID_INIT_SESSION 0x00000036 -/* PXP-Packet sizes for MTL's GSCCS-HECI instruction */ -#define PXP43_MAX_HECI_INOUT_SIZE (SZ_32K) +/* PXP-Packet sizes for MTL's GSCCS-HECI instruction is spec'd at 65K before page alignment*/ +#define PXP43_MAX_HECI_INOUT_SIZE (PAGE_ALIGN(SZ_64K + SZ_1K)) /* PXP-Packet size for MTL's NEW_HUC_AUTH instruction */ #define PXP43_HUC_AUTH_INOUT_SIZE (SZ_4K) diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c b/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c index b4ce7ca9b49d..27402ecf0457 100644 --- a/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c +++ b/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.c @@ -112,7 +112,7 @@ gsccs_send_message(struct intel_pxp *pxp, ret = intel_gsc_uc_heci_cmd_submit_nonpriv(>->uc.gsc, exec_res->ce, &pkt, exec_res->bb_vaddr, - GSC_REPLY_LATENCY_MS); + GSC_HECI_REPLY_LATENCY_MS); if (ret) { drm_err(&i915->drm, "failed to send gsc PXP msg (%d)\n", ret); goto unlock; diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.h b/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.h index 298ad38e6c7d..9aae779c4da3 100644 --- a/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.h +++ b/drivers/gpu/drm/i915/pxp/intel_pxp_gsccs.h @@ -8,16 +8,14 @@ #include <linux/types.h> +#include "gt/uc/intel_gsc_uc_heci_cmd_submit.h" + struct intel_pxp; -#define GSC_REPLY_LATENCY_MS 210 -/* - * Max FW response time is 200ms, to which we add 10ms to account for overhead - * such as request preparation, GuC submission to hw and pipeline completion times. - */ #define GSC_PENDING_RETRY_MAXCOUNT 40 #define GSC_PENDING_RETRY_PAUSE_MS 50 -#define GSCFW_MAX_ROUND_TRIP_LATENCY_MS (GSC_PENDING_RETRY_MAXCOUNT * GSC_PENDING_RETRY_PAUSE_MS) +#define GSCFW_MAX_ROUND_TRIP_LATENCY_MS (GSC_HECI_REPLY_LATENCY_MS + \ + (GSC_PENDING_RETRY_MAXCOUNT * GSC_PENDING_RETRY_PAUSE_MS)) #ifdef CONFIG_DRM_I915_PXP void intel_pxp_gsccs_fini(struct intel_pxp *pxp); diff --git a/drivers/gpu/drm/i915/pxp/intel_pxp_tee.c b/drivers/gpu/drm/i915/pxp/intel_pxp_tee.c index f89a1f80f50e..bb58fa9579b8 100644 --- a/drivers/gpu/drm/i915/pxp/intel_pxp_tee.c +++ b/drivers/gpu/drm/i915/pxp/intel_pxp_tee.c @@ -9,6 +9,7 @@ #include <drm/i915_component.h> #include "gem/i915_gem_lmem.h" +#include "gt/intel_gt_print.h" #include "i915_drv.h" #include "gt/intel_gt.h" @@ -156,7 +157,8 @@ static int i915_pxp_tee_component_bind(struct device *i915_kdev, { struct drm_i915_private *i915 = kdev_to_i915(i915_kdev); struct intel_pxp *pxp = i915->pxp; - struct intel_uc *uc = &pxp->ctrl_gt->uc; + struct intel_gt *gt = pxp->ctrl_gt; + struct intel_uc *uc = >->uc; intel_wakeref_t wakeref; int ret = 0; @@ -176,7 +178,7 @@ static int i915_pxp_tee_component_bind(struct device *i915_kdev, /* load huc via pxp */ ret = intel_huc_fw_load_and_auth_via_gsc(&uc->huc); if (ret < 0) - drm_err(&i915->drm, "failed to load huc via gsc %d\n", ret); + gt_probe_error(gt, "failed to load huc via gsc %d\n", ret); } } diff --git a/drivers/gpu/drm/i915/selftests/igt_spinner.c b/drivers/gpu/drm/i915/selftests/igt_spinner.c index 0f064930ef11..8c3e1f20e5a1 100644 --- a/drivers/gpu/drm/i915/selftests/igt_spinner.c +++ b/drivers/gpu/drm/i915/selftests/igt_spinner.c @@ -179,6 +179,9 @@ igt_spinner_create_request(struct igt_spinner *spin, *batch++ = arbitration_command; + memset32(batch, MI_NOOP, 128); + batch += 128; + if (GRAPHICS_VER(rq->i915) >= 8) *batch++ = MI_BATCH_BUFFER_START | BIT(8) | 1; else if (IS_HASWELL(rq->i915)) diff --git a/drivers/gpu/drm/imx/dcss/dcss-drv.c b/drivers/gpu/drm/imx/dcss/dcss-drv.c index c68b0d93ae9e..b61cec0cc79d 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-drv.c +++ b/drivers/gpu/drm/imx/dcss/dcss-drv.c @@ -92,6 +92,13 @@ static int dcss_drv_platform_remove(struct platform_device *pdev) return 0; } +static void dcss_drv_platform_shutdown(struct platform_device *pdev) +{ + struct dcss_drv *mdrv = dev_get_drvdata(&pdev->dev); + + dcss_kms_shutdown(mdrv->kms); +} + static struct dcss_type_data dcss_types[] = { [DCSS_IMX8MQ] = { .name = "DCSS_IMX8MQ", @@ -114,6 +121,7 @@ MODULE_DEVICE_TABLE(of, dcss_of_match); static struct platform_driver dcss_platform_driver = { .probe = dcss_drv_platform_probe, .remove = dcss_drv_platform_remove, + .shutdown = dcss_drv_platform_shutdown, .driver = { .name = "imx-dcss", .of_match_table = dcss_of_match, diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.c b/drivers/gpu/drm/imx/dcss/dcss-kms.c index 896de946f8df..d0ea4e97cded 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-kms.c +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.c @@ -172,3 +172,10 @@ void dcss_kms_detach(struct dcss_kms_dev *kms) dcss_crtc_deinit(&kms->crtc, drm); drm->dev_private = NULL; } + +void dcss_kms_shutdown(struct dcss_kms_dev *kms) +{ + struct drm_device *drm = &kms->base; + + drm_atomic_helper_shutdown(drm); +} diff --git a/drivers/gpu/drm/imx/dcss/dcss-kms.h b/drivers/gpu/drm/imx/dcss/dcss-kms.h index dfe5dd99eea3..62521c1fd6d2 100644 --- a/drivers/gpu/drm/imx/dcss/dcss-kms.h +++ b/drivers/gpu/drm/imx/dcss/dcss-kms.h @@ -34,6 +34,7 @@ struct dcss_kms_dev { struct dcss_kms_dev *dcss_kms_attach(struct dcss_dev *dcss); void dcss_kms_detach(struct dcss_kms_dev *kms); +void dcss_kms_shutdown(struct dcss_kms_dev *kms); int dcss_crtc_init(struct dcss_crtc *crtc, struct drm_device *drm); void dcss_crtc_deinit(struct dcss_crtc *crtc, struct drm_device *drm); struct dcss_plane *dcss_plane_init(struct drm_device *drm, diff --git a/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c index a2277a0d6d06..0006ea52b83c 100644 --- a/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c +++ b/drivers/gpu/drm/imx/ipuv3/dw_hdmi-imx.c @@ -255,19 +255,17 @@ static int dw_hdmi_imx_probe(struct platform_device *pdev) return ret; } -static int dw_hdmi_imx_remove(struct platform_device *pdev) +static void dw_hdmi_imx_remove(struct platform_device *pdev) { struct imx_hdmi *hdmi = platform_get_drvdata(pdev); component_del(&pdev->dev, &dw_hdmi_imx_ops); dw_hdmi_remove(hdmi->hdmi); - - return 0; } static struct platform_driver dw_hdmi_imx_platform_driver = { .probe = dw_hdmi_imx_probe, - .remove = dw_hdmi_imx_remove, + .remove_new = dw_hdmi_imx_remove, .driver = { .name = "dwhdmi-imx", .of_match_table = dw_hdmi_imx_dt_ids, diff --git a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c index 4a866ac60fff..4cfabcf7375a 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-drm-core.c @@ -257,6 +257,7 @@ err_poll_fini: drm_kms_helper_poll_fini(drm); component_unbind_all(drm->dev, drm); err_kms: + dev_set_drvdata(dev, NULL); drm_dev_put(drm); return ret; @@ -269,6 +270,7 @@ static void imx_drm_unbind(struct device *dev) drm_dev_unregister(drm); drm_kms_helper_poll_fini(drm); + drm_atomic_helper_shutdown(drm); component_unbind_all(drm->dev, drm); @@ -292,10 +294,14 @@ static int imx_drm_platform_probe(struct platform_device *pdev) return ret; } -static int imx_drm_platform_remove(struct platform_device *pdev) +static void imx_drm_platform_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &imx_drm_ops); - return 0; +} + +static void imx_drm_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); } #ifdef CONFIG_PM_SLEEP @@ -324,7 +330,8 @@ MODULE_DEVICE_TABLE(of, imx_drm_dt_ids); static struct platform_driver imx_drm_pdrv = { .probe = imx_drm_platform_probe, - .remove = imx_drm_platform_remove, + .remove_new = imx_drm_platform_remove, + .shutdown = imx_drm_platform_shutdown, .driver = { .name = "imx-drm", .pm = &imx_drm_pm_ops, diff --git a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c index c45fc8f4744d..989eca32d325 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-ldb.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-ldb.c @@ -737,7 +737,7 @@ free_child: return ret; } -static int imx_ldb_remove(struct platform_device *pdev) +static void imx_ldb_remove(struct platform_device *pdev) { struct imx_ldb *imx_ldb = platform_get_drvdata(pdev); int i; @@ -750,12 +750,11 @@ static int imx_ldb_remove(struct platform_device *pdev) } component_del(&pdev->dev, &imx_ldb_ops); - return 0; } static struct platform_driver imx_ldb_driver = { .probe = imx_ldb_probe, - .remove = imx_ldb_remove, + .remove_new = imx_ldb_remove, .driver = { .of_match_table = imx_ldb_dt_ids, .name = DRIVER_NAME, diff --git a/drivers/gpu/drm/imx/ipuv3/imx-tve.c b/drivers/gpu/drm/imx/ipuv3/imx-tve.c index d6832f506322..b49bddb85535 100644 --- a/drivers/gpu/drm/imx/ipuv3/imx-tve.c +++ b/drivers/gpu/drm/imx/ipuv3/imx-tve.c @@ -645,10 +645,9 @@ static int imx_tve_probe(struct platform_device *pdev) return component_add(dev, &imx_tve_ops); } -static int imx_tve_remove(struct platform_device *pdev) +static void imx_tve_remove(struct platform_device *pdev) { component_del(&pdev->dev, &imx_tve_ops); - return 0; } static const struct of_device_id imx_tve_dt_ids[] = { @@ -659,7 +658,7 @@ MODULE_DEVICE_TABLE(of, imx_tve_dt_ids); static struct platform_driver imx_tve_driver = { .probe = imx_tve_probe, - .remove = imx_tve_remove, + .remove_new = imx_tve_remove, .driver = { .of_match_table = imx_tve_dt_ids, .name = "imx-tve", diff --git a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c index 89585b31b985..ef29c9a61a46 100644 --- a/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c +++ b/drivers/gpu/drm/imx/ipuv3/ipuv3-crtc.c @@ -441,10 +441,9 @@ static int ipu_drm_probe(struct platform_device *pdev) return component_add(dev, &ipu_crtc_ops); } -static int ipu_drm_remove(struct platform_device *pdev) +static void ipu_drm_remove(struct platform_device *pdev) { component_del(&pdev->dev, &ipu_crtc_ops); - return 0; } struct platform_driver ipu_drm_driver = { @@ -452,5 +451,5 @@ struct platform_driver ipu_drm_driver = { .name = "imx-ipuv3-crtc", }, .probe = ipu_drm_probe, - .remove = ipu_drm_remove, + .remove_new = ipu_drm_remove, }; diff --git a/drivers/gpu/drm/imx/ipuv3/parallel-display.c b/drivers/gpu/drm/imx/ipuv3/parallel-display.c index 0fa0b590830b..70349739dd89 100644 --- a/drivers/gpu/drm/imx/ipuv3/parallel-display.c +++ b/drivers/gpu/drm/imx/ipuv3/parallel-display.c @@ -353,11 +353,9 @@ static int imx_pd_probe(struct platform_device *pdev) return component_add(dev, &imx_pd_ops); } -static int imx_pd_remove(struct platform_device *pdev) +static void imx_pd_remove(struct platform_device *pdev) { component_del(&pdev->dev, &imx_pd_ops); - - return 0; } static const struct of_device_id imx_pd_dt_ids[] = { @@ -368,7 +366,7 @@ MODULE_DEVICE_TABLE(of, imx_pd_dt_ids); static struct platform_driver imx_pd_driver = { .probe = imx_pd_probe, - .remove = imx_pd_remove, + .remove_new = imx_pd_remove, .driver = { .of_match_table = imx_pd_dt_ids, .name = "imx-parallel-display", diff --git a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c index 8dbd4847d3a6..0751235007a7 100644 --- a/drivers/gpu/drm/ingenic/ingenic-drm-drv.c +++ b/drivers/gpu/drm/ingenic/ingenic-drm-drv.c @@ -1130,7 +1130,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) ret = drmm_mode_config_init(drm); if (ret) - return ret; + goto err_drvdata; drm->mode_config.min_width = 0; drm->mode_config.min_height = 0; @@ -1142,7 +1142,8 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) { dev_err(dev, "Failed to get memory resource\n"); - return PTR_ERR(base); + ret = PTR_ERR(base); + goto err_drvdata; } regmap_config = ingenic_drm_regmap_config; @@ -1151,33 +1152,40 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) ®map_config); if (IS_ERR(priv->map)) { dev_err(dev, "Failed to create regmap\n"); - return PTR_ERR(priv->map); + ret = PTR_ERR(priv->map); + goto err_drvdata; } irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; + if (irq < 0) { + ret = irq; + goto err_drvdata; + } if (soc_info->needs_dev_clk) { priv->lcd_clk = devm_clk_get(dev, "lcd"); if (IS_ERR(priv->lcd_clk)) { dev_err(dev, "Failed to get lcd clock\n"); - return PTR_ERR(priv->lcd_clk); + ret = PTR_ERR(priv->lcd_clk); + goto err_drvdata; } } priv->pix_clk = devm_clk_get(dev, "lcd_pclk"); if (IS_ERR(priv->pix_clk)) { dev_err(dev, "Failed to get pixel clock\n"); - return PTR_ERR(priv->pix_clk); + ret = PTR_ERR(priv->pix_clk); + goto err_drvdata; } priv->dma_hwdescs = dmam_alloc_coherent(dev, sizeof(*priv->dma_hwdescs), &priv->dma_hwdescs_phys, GFP_KERNEL); - if (!priv->dma_hwdescs) - return -ENOMEM; + if (!priv->dma_hwdescs) { + ret = -ENOMEM; + goto err_drvdata; + } /* Configure DMA hwdesc for foreground0 plane */ ingenic_drm_configure_hwdesc_plane(priv, 0); @@ -1199,7 +1207,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) NULL, DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) { dev_err(dev, "Failed to register plane: %i\n", ret); - return ret; + goto err_drvdata; } if (soc_info->map_noncoherent) @@ -1211,7 +1219,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) NULL, &ingenic_drm_crtc_funcs, NULL); if (ret) { dev_err(dev, "Failed to init CRTC: %i\n", ret); - return ret; + goto err_drvdata; } drm_crtc_enable_color_mgmt(&priv->crtc, 0, false, @@ -1230,7 +1238,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) if (ret) { dev_err(dev, "Failed to register overlay plane: %i\n", ret); - return ret; + goto err_drvdata; } if (soc_info->map_noncoherent) @@ -1241,17 +1249,18 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) if (ret) { if (ret != -EPROBE_DEFER) dev_err(dev, "Failed to bind components: %i\n", ret); - return ret; + goto err_drvdata; } ret = devm_add_action_or_reset(dev, ingenic_drm_unbind_all, priv); if (ret) - return ret; + goto err_drvdata; priv->ipu_plane = drm_plane_from_index(drm, 2); if (!priv->ipu_plane) { dev_err(dev, "Failed to retrieve IPU plane\n"); - return -EINVAL; + ret = -EINVAL; + goto err_drvdata; } } } @@ -1263,7 +1272,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) break; /* we're done */ if (ret != -EPROBE_DEFER) dev_err(dev, "Failed to get bridge handle\n"); - return ret; + goto err_drvdata; } if (panel) @@ -1275,7 +1284,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) if (IS_ERR(ib)) { ret = PTR_ERR(ib); dev_err(dev, "Failed to init encoder: %d\n", ret); - return ret; + goto err_drvdata; } encoder = &ib->encoder; @@ -1290,13 +1299,14 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) DRM_BRIDGE_ATTACH_NO_CONNECTOR); if (ret) { dev_err(dev, "Unable to attach bridge\n"); - return ret; + goto err_drvdata; } connector = drm_bridge_connector_init(drm, encoder); if (IS_ERR(connector)) { dev_err(dev, "Unable to init connector\n"); - return PTR_ERR(connector); + ret = PTR_ERR(connector); + goto err_drvdata; } drm_connector_attach_encoder(connector, encoder); @@ -1313,13 +1323,13 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) ret = devm_request_irq(dev, irq, ingenic_drm_irq_handler, 0, drm->driver->name, drm); if (ret) { dev_err(dev, "Unable to install IRQ handler\n"); - return ret; + goto err_drvdata; } ret = drm_vblank_init(drm, 1); if (ret) { dev_err(dev, "Failed calling drm_vblank_init()\n"); - return ret; + goto err_drvdata; } drm_mode_config_reset(drm); @@ -1327,7 +1337,7 @@ static int ingenic_drm_bind(struct device *dev, bool has_components) ret = clk_prepare_enable(priv->pix_clk); if (ret) { dev_err(dev, "Unable to start pixel clock\n"); - return ret; + goto err_drvdata; } if (priv->lcd_clk) { @@ -1402,6 +1412,8 @@ err_devclk_disable: clk_disable_unprepare(priv->lcd_clk); err_pixclk_disable: clk_disable_unprepare(priv->pix_clk); +err_drvdata: + platform_set_drvdata(pdev, NULL); return ret; } @@ -1422,6 +1434,7 @@ static void ingenic_drm_unbind(struct device *dev) drm_dev_unregister(&priv->drm); drm_atomic_helper_shutdown(&priv->drm); + dev_set_drvdata(dev, NULL); } static const struct component_master_ops ingenic_master_ops = { @@ -1449,7 +1462,7 @@ static int ingenic_drm_probe(struct platform_device *pdev) return component_master_add_with_match(dev, &ingenic_master_ops, match); } -static int ingenic_drm_remove(struct platform_device *pdev) +static void ingenic_drm_remove(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1457,8 +1470,14 @@ static int ingenic_drm_remove(struct platform_device *pdev) ingenic_drm_unbind(dev); else component_master_del(dev, &ingenic_master_ops); +} - return 0; +static void ingenic_drm_shutdown(struct platform_device *pdev) +{ + struct ingenic_drm *priv = platform_get_drvdata(pdev); + + if (priv) + drm_atomic_helper_shutdown(&priv->drm); } static int ingenic_drm_suspend(struct device *dev) @@ -1611,7 +1630,8 @@ static struct platform_driver ingenic_drm_driver = { .of_match_table = of_match_ptr(ingenic_drm_of_match), }, .probe = ingenic_drm_probe, - .remove = ingenic_drm_remove, + .remove_new = ingenic_drm_remove, + .shutdown = ingenic_drm_shutdown, }; static int ingenic_drm_init(void) diff --git a/drivers/gpu/drm/ingenic/ingenic-ipu.c b/drivers/gpu/drm/ingenic/ingenic-ipu.c index 6d236547f611..5bd9072352b5 100644 --- a/drivers/gpu/drm/ingenic/ingenic-ipu.c +++ b/drivers/gpu/drm/ingenic/ingenic-ipu.c @@ -922,10 +922,9 @@ static int ingenic_ipu_probe(struct platform_device *pdev) return component_add(&pdev->dev, &ingenic_ipu_ops); } -static int ingenic_ipu_remove(struct platform_device *pdev) +static void ingenic_ipu_remove(struct platform_device *pdev) { component_del(&pdev->dev, &ingenic_ipu_ops); - return 0; } static const u32 jz4725b_ipu_formats[] = { @@ -992,7 +991,7 @@ static struct platform_driver ingenic_ipu_driver = { .of_match_table = ingenic_ipu_of_match, }, .probe = ingenic_ipu_probe, - .remove = ingenic_ipu_remove, + .remove_new = ingenic_ipu_remove, }; struct platform_driver *ingenic_ipu_driver_ptr = &ingenic_ipu_driver; diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.c b/drivers/gpu/drm/logicvc/logicvc_drm.c index 749debd3d6a5..01a37e28c080 100644 --- a/drivers/gpu/drm/logicvc/logicvc_drm.c +++ b/drivers/gpu/drm/logicvc/logicvc_drm.c @@ -482,6 +482,14 @@ static void logicvc_drm_remove(struct platform_device *pdev) of_reserved_mem_device_release(dev); } +static void logicvc_drm_shutdown(struct platform_device *pdev) +{ + struct logicvc_drm *logicvc = platform_get_drvdata(pdev); + struct drm_device *drm_dev = &logicvc->drm_dev; + + drm_atomic_helper_shutdown(drm_dev); +} + static const struct of_device_id logicvc_drm_of_table[] = { { .compatible = "xylon,logicvc-3.02.a-display" }, { .compatible = "xylon,logicvc-4.01.a-display" }, @@ -492,6 +500,7 @@ MODULE_DEVICE_TABLE(of, logicvc_drm_of_table); static struct platform_driver logicvc_drm_platform_driver = { .probe = logicvc_drm_probe, .remove_new = logicvc_drm_remove, + .shutdown = logicvc_drm_shutdown, .driver = { .name = "logicvc-drm", .of_match_table = logicvc_drm_of_table, diff --git a/drivers/gpu/drm/loongson/lsdc_drv.c b/drivers/gpu/drm/loongson/lsdc_drv.c index 188ec82afcfb..89ccc0c43169 100644 --- a/drivers/gpu/drm/loongson/lsdc_drv.c +++ b/drivers/gpu/drm/loongson/lsdc_drv.c @@ -327,6 +327,11 @@ static void lsdc_pci_remove(struct pci_dev *pdev) drm_atomic_helper_shutdown(ddev); } +static void lsdc_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + static int lsdc_drm_freeze(struct drm_device *ddev) { struct lsdc_device *ldev = to_lsdc(ddev); @@ -447,6 +452,7 @@ struct pci_driver lsdc_pci_driver = { .id_table = lsdc_pciid_list, .probe = lsdc_pci_probe, .remove = lsdc_pci_remove, + .shutdown = lsdc_pci_shutdown, .driver.pm = &lsdc_pm_ops, }; diff --git a/drivers/gpu/drm/loongson/lsdc_pixpll.c b/drivers/gpu/drm/loongson/lsdc_pixpll.c index 04c15b4697e2..2609a2256da4 100644 --- a/drivers/gpu/drm/loongson/lsdc_pixpll.c +++ b/drivers/gpu/drm/loongson/lsdc_pixpll.c @@ -120,12 +120,14 @@ static int lsdc_pixel_pll_setup(struct lsdc_pixpll * const this) struct lsdc_pixpll_parms *pparms; this->mmio = ioremap(this->reg_base, this->reg_size); - if (IS_ERR_OR_NULL(this->mmio)) + if (!this->mmio) return -ENOMEM; pparms = kzalloc(sizeof(*pparms), GFP_KERNEL); - if (IS_ERR_OR_NULL(pparms)) + if (!pparms) { + iounmap(this->mmio); return -ENOMEM; + } pparms->ref_clock = LSDC_PLL_REF_CLK_KHZ; diff --git a/drivers/gpu/drm/mcde/mcde_drv.c b/drivers/gpu/drm/mcde/mcde_drv.c index a2572fb311f0..10c06440c7e7 100644 --- a/drivers/gpu/drm/mcde/mcde_drv.c +++ b/drivers/gpu/drm/mcde/mcde_drv.c @@ -459,6 +459,14 @@ static void mcde_remove(struct platform_device *pdev) regulator_disable(mcde->epod); } +static void mcde_shutdown(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + if (drm->registered) + drm_atomic_helper_shutdown(drm); +} + static const struct of_device_id mcde_of_match[] = { { .compatible = "ste,mcde", @@ -473,6 +481,7 @@ static struct platform_driver mcde_driver = { }, .probe = mcde_probe, .remove_new = mcde_remove, + .shutdown = mcde_shutdown, }; static struct platform_driver *const component_drivers[] = { diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c index abddf37f0ea1..2fb18b782b05 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.c +++ b/drivers/gpu/drm/mgag200/mgag200_drv.c @@ -10,6 +10,7 @@ #include <linux/pci.h> #include <drm/drm_aperture.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> #include <drm/drm_fbdev_generic.h> #include <drm/drm_file.h> @@ -278,6 +279,12 @@ static void mgag200_pci_remove(struct pci_dev *pdev) struct drm_device *dev = pci_get_drvdata(pdev); drm_dev_unregister(dev); + drm_atomic_helper_shutdown(dev); +} + +static void mgag200_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); } static struct pci_driver mgag200_pci_driver = { @@ -285,6 +292,7 @@ static struct pci_driver mgag200_pci_driver = { .id_table = mgag200_pciidlist, .probe = mgag200_pci_probe, .remove = mgag200_pci_remove, + .shutdown = mgag200_pci_shutdown, }; drm_module_pci_driver_if_modeset(mgag200_pci_driver, mgag200_modeset); diff --git a/drivers/gpu/drm/msm/adreno/adreno_device.c b/drivers/gpu/drm/msm/adreno/adreno_device.c index 575e7c56219f..fa527935ffd4 100644 --- a/drivers/gpu/drm/msm/adreno/adreno_device.c +++ b/drivers/gpu/drm/msm/adreno/adreno_device.c @@ -751,10 +751,9 @@ static int adreno_probe(struct platform_device *pdev) return 0; } -static int adreno_remove(struct platform_device *pdev) +static void adreno_remove(struct platform_device *pdev) { component_del(&pdev->dev, &a3xx_ops); - return 0; } static void adreno_shutdown(struct platform_device *pdev) @@ -869,7 +868,7 @@ static const struct dev_pm_ops adreno_pm_ops = { static struct platform_driver adreno_driver = { .probe = adreno_probe, - .remove = adreno_remove, + .remove_new = adreno_remove, .shutdown = adreno_shutdown, .driver = { .name = "adreno", diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c index aa6ba2cf4b84..82381d12414d 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c @@ -1302,11 +1302,9 @@ static int dpu_dev_probe(struct platform_device *pdev) return msm_drv_probe(&pdev->dev, dpu_kms_init); } -static int dpu_dev_remove(struct platform_device *pdev) +static void dpu_dev_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &msm_drm_ops); - - return 0; } static int __maybe_unused dpu_runtime_suspend(struct device *dev) @@ -1382,7 +1380,7 @@ MODULE_DEVICE_TABLE(of, dpu_dt_match); static struct platform_driver dpu_driver = { .probe = dpu_dev_probe, - .remove = dpu_dev_remove, + .remove_new = dpu_dev_remove, .shutdown = msm_drv_shutdown, .driver = { .name = "msm_dpu", diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c index 700df4040e9a..e5012fa6771f 100644 --- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c @@ -560,11 +560,9 @@ static int mdp4_probe(struct platform_device *pdev) return msm_drv_probe(&pdev->dev, mdp4_kms_init); } -static int mdp4_remove(struct platform_device *pdev) +static void mdp4_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &msm_drm_ops); - - return 0; } static const struct of_device_id mdp4_dt_match[] = { @@ -575,7 +573,7 @@ MODULE_DEVICE_TABLE(of, mdp4_dt_match); static struct platform_driver mdp4_platform_driver = { .probe = mdp4_probe, - .remove = mdp4_remove, + .remove_new = mdp4_remove, .shutdown = msm_drv_shutdown, .driver = { .name = "mdp4", diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c index 92bf9d949d09..8a7b44376bc6 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c @@ -942,11 +942,10 @@ static int mdp5_dev_probe(struct platform_device *pdev) return msm_drv_probe(&pdev->dev, mdp5_kms_init); } -static int mdp5_dev_remove(struct platform_device *pdev) +static void mdp5_dev_remove(struct platform_device *pdev) { DBG(""); component_master_del(&pdev->dev, &msm_drm_ops); - return 0; } static __maybe_unused int mdp5_runtime_suspend(struct device *dev) @@ -987,7 +986,7 @@ MODULE_DEVICE_TABLE(of, mdp5_dt_match); static struct platform_driver mdp5_driver = { .probe = mdp5_dev_probe, - .remove = mdp5_dev_remove, + .remove_new = mdp5_dev_remove, .shutdown = msm_drv_shutdown, .driver = { .name = "msm_mdp", diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index 76f13954015b..01784e9e7127 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -1296,7 +1296,7 @@ static int dp_display_probe(struct platform_device *pdev) return rc; } -static int dp_display_remove(struct platform_device *pdev) +static void dp_display_remove(struct platform_device *pdev) { struct dp_display_private *dp = dev_get_dp_display_private(&pdev->dev); @@ -1304,8 +1304,6 @@ static int dp_display_remove(struct platform_device *pdev) dp_display_deinit_sub_modules(dp); platform_set_drvdata(pdev, NULL); - - return 0; } static int dp_pm_resume(struct device *dev) @@ -1415,7 +1413,7 @@ static const struct dev_pm_ops dp_pm_ops = { static struct platform_driver dp_display_driver = { .probe = dp_display_probe, - .remove = dp_display_remove, + .remove_new = dp_display_remove, .driver = { .name = "msm-dp-display", .of_match_table = dp_dt_match, diff --git a/drivers/gpu/drm/msm/dsi/dsi.c b/drivers/gpu/drm/msm/dsi/dsi.c index baab79ab6e74..7a8208cd6649 100644 --- a/drivers/gpu/drm/msm/dsi/dsi.c +++ b/drivers/gpu/drm/msm/dsi/dsi.c @@ -161,14 +161,12 @@ static int dsi_dev_probe(struct platform_device *pdev) return 0; } -static int dsi_dev_remove(struct platform_device *pdev) +static void dsi_dev_remove(struct platform_device *pdev) { struct msm_dsi *msm_dsi = platform_get_drvdata(pdev); DBG(""); dsi_destroy(msm_dsi); - - return 0; } static const struct of_device_id dt_match[] = { @@ -187,7 +185,7 @@ static const struct dev_pm_ops dsi_pm_ops = { static struct platform_driver dsi_driver = { .probe = dsi_dev_probe, - .remove = dsi_dev_remove, + .remove_new = dsi_dev_remove, .driver = { .name = "msm_dsi", .of_match_table = dt_match, diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c index 60509fb39710..b6bcb9f675fe 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi.c @@ -551,15 +551,13 @@ err_put_phy: return ret; } -static int msm_hdmi_dev_remove(struct platform_device *pdev) +static void msm_hdmi_dev_remove(struct platform_device *pdev) { struct hdmi *hdmi = dev_get_drvdata(&pdev->dev); component_del(&pdev->dev, &msm_hdmi_ops); msm_hdmi_put_phy(hdmi); - - return 0; } static const struct of_device_id msm_hdmi_dt_match[] = { @@ -574,7 +572,7 @@ static const struct of_device_id msm_hdmi_dt_match[] = { static struct platform_driver msm_hdmi_driver = { .probe = msm_hdmi_dev_probe, - .remove = msm_hdmi_dev_remove, + .remove_new = msm_hdmi_dev_remove, .driver = { .name = "hdmi_msm", .of_match_table = msm_hdmi_dt_match, diff --git a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c index 3e00fb8190b2..88a3423b7f24 100644 --- a/drivers/gpu/drm/msm/hdmi/hdmi_phy.c +++ b/drivers/gpu/drm/msm/hdmi/hdmi_phy.c @@ -177,11 +177,9 @@ static int msm_hdmi_phy_probe(struct platform_device *pdev) return 0; } -static int msm_hdmi_phy_remove(struct platform_device *pdev) +static void msm_hdmi_phy_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - - return 0; } static const struct of_device_id msm_hdmi_phy_dt_match[] = { @@ -200,7 +198,7 @@ static const struct of_device_id msm_hdmi_phy_dt_match[] = { static struct platform_driver msm_hdmi_phy_platform_driver = { .probe = msm_hdmi_phy_probe, - .remove = msm_hdmi_phy_remove, + .remove_new = msm_hdmi_phy_remove, .driver = { .name = "msm_hdmi_phy", .of_match_table = msm_hdmi_phy_dt_match, diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index 4bd028fa7500..a428951ee539 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -1278,11 +1278,9 @@ static int msm_pdev_probe(struct platform_device *pdev) return msm_drv_probe(&pdev->dev, NULL); } -static int msm_pdev_remove(struct platform_device *pdev) +static void msm_pdev_remove(struct platform_device *pdev) { component_master_del(&pdev->dev, &msm_drm_ops); - - return 0; } void msm_drv_shutdown(struct platform_device *pdev) @@ -1303,7 +1301,7 @@ void msm_drv_shutdown(struct platform_device *pdev) static struct platform_driver msm_platform_driver = { .probe = msm_pdev_probe, - .remove = msm_pdev_remove, + .remove_new = msm_pdev_remove, .shutdown = msm_drv_shutdown, .driver = { .name = "msm", diff --git a/drivers/gpu/drm/msm/msm_mdss.c b/drivers/gpu/drm/msm/msm_mdss.c index 2e87dd6cb17b..a429b704059e 100644 --- a/drivers/gpu/drm/msm/msm_mdss.c +++ b/drivers/gpu/drm/msm/msm_mdss.c @@ -497,15 +497,13 @@ static int mdss_probe(struct platform_device *pdev) return 0; } -static int mdss_remove(struct platform_device *pdev) +static void mdss_remove(struct platform_device *pdev) { struct msm_mdss *mdss = platform_get_drvdata(pdev); of_platform_depopulate(&pdev->dev); msm_mdss_destroy(mdss); - - return 0; } static const struct msm_mdss_data msm8998_data = { @@ -629,7 +627,7 @@ MODULE_DEVICE_TABLE(of, mdss_dt_match); static struct platform_driver mdss_platform_driver = { .probe = mdss_probe, - .remove = mdss_remove, + .remove_new = mdss_remove, .driver = { .name = "msm-mdss", .of_match_table = mdss_dt_match, diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index c52e8096cca4..1e6aaf95ff7c 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -11,6 +11,7 @@ config DRM_NOUVEAU select DRM_TTM select DRM_TTM_HELPER select DRM_EXEC + select DRM_GPUVM select DRM_SCHED select I2C select I2C_ALGOBIT diff --git a/drivers/gpu/drm/nouveau/dispnv04/disp.c b/drivers/gpu/drm/nouveau/dispnv04/disp.c index e9ac3fb27ff7..13705c5f1497 100644 --- a/drivers/gpu/drm/nouveau/dispnv04/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv04/disp.c @@ -256,7 +256,7 @@ nv04_display_create(struct drm_device *dev) for (i = 0; i < dcb->entries; i++) { struct dcb_output *dcbent = &dcb->entry[i]; - connector = nouveau_connector_create(dev, dcbent); + connector = nouveau_connector_create(dev, dcbent->connector); if (IS_ERR(connector)) continue; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 4e7c9c353c51..52f1569ee37c 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -66,8 +66,6 @@ #include "nouveau_fence.h" #include "nv50_display.h" -#include <subdev/bios/dp.h> - /****************************************************************************** * EVO channel *****************************************************************************/ @@ -477,7 +475,6 @@ nv50_dac_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st core->func->dac->ctrl(core, nv_encoder->outp.or.id, ctrl, NULL); nv_encoder->crtc = NULL; - nvif_outp_release(&nv_encoder->outp); } static void @@ -502,7 +499,8 @@ nv50_dac_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta ctrl |= NVDEF(NV507D, DAC_SET_CONTROL, PROTOCOL, RGB_CRT); - nvif_outp_acquire_rgb_crt(&nv_encoder->outp); + if (!nvif_outp_acquired(&nv_encoder->outp)) + nvif_outp_acquire_dac(&nv_encoder->outp); core->func->dac->ctrl(core, nv_encoder->outp.or.id, ctrl, asyh); asyh->or.depth = 0; @@ -553,34 +551,27 @@ nv50_dac_func = { }; static int -nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) +nv50_dac_create(struct nouveau_encoder *nv_encoder) { + struct drm_connector *connector = &nv_encoder->conn->base; struct nouveau_drm *drm = nouveau_drm(connector->dev); - struct nv50_disp *disp = nv50_disp(connector->dev); struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus; - struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct dcb_output *dcbe = nv_encoder->dcb; int type = DRM_MODE_ENCODER_DAC; - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; - bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) nv_encoder->i2c = &bus->i2c; encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; drm_encoder_init(connector->dev, encoder, &nv50_dac_func, type, "dac-%04x-%04x", dcbe->hasht, dcbe->hashm); drm_encoder_helper_add(encoder, &nv50_dac_help); drm_connector_attach_encoder(connector, encoder); - return nvif_outp_ctor(disp->disp, nv_encoder->base.base.name, dcbe->id, &nv_encoder->outp); + return 0; } /* @@ -617,7 +608,7 @@ nv50_audio_component_get_eld(struct device *kdev, int port, int dev_id, continue; /* TODO */ nv_encoder = nouveau_encoder(encoder); - nv_connector = nouveau_connector(nv_encoder->audio.connector); + nv_connector = nv_encoder->conn; nv_crtc = nouveau_crtc(nv_encoder->crtc); if (!nv_crtc || nv_encoder->outp.or.id != port || nv_crtc->index != dev_id) @@ -713,6 +704,18 @@ nv50_audio_supported(struct drm_encoder *encoder) disp->disp->object.oclass == GT206_DISP) return false; + if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_DP: + break; + default: + return false; + } + } + return true; } @@ -729,7 +732,6 @@ nv50_audio_disable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc) mutex_lock(&drm->audio.lock); if (nv_encoder->audio.enabled) { nv_encoder->audio.enabled = false; - nv_encoder->audio.connector = NULL; nvif_outp_hda_eld(&nv_encoder->outp, nv_crtc->index, NULL, 0); } mutex_unlock(&drm->audio.lock); @@ -754,7 +756,6 @@ nv50_audio_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, nvif_outp_hda_eld(&nv_encoder->outp, nv_crtc->index, nv_connector->base.eld, drm_eld_size(nv_connector->base.eld)); nv_encoder->audio.enabled = true; - nv_encoder->audio.connector = &nv_connector->base; mutex_unlock(&drm->audio.lock); @@ -774,7 +775,6 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, struct drm_hdmi_info *hdmi = &nv_connector->base.display_info.hdmi; union hdmi_infoframe infoframe = { 0 }; const u8 rekey = 56; /* binary driver, and tegra, constant */ - u8 scdc = 0; u32 max_ac_packet; struct { struct nvif_outp_infoframe_v0 infoframe; @@ -787,8 +787,9 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, max_ac_packet -= 18; /* constant from tegra */ max_ac_packet /= 32; - if (hdmi->scdc.scrambling.supported) { + if (nv_encoder->i2c && hdmi->scdc.scrambling.supported) { const bool high_tmds_clock_ratio = mode->clock > 340000; + u8 scdc; ret = drm_scdc_readb(nv_encoder->i2c, SCDC_TMDS_CONFIG, &scdc); if (ret < 0) { @@ -808,8 +809,9 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, scdc, ret); } - ret = nvif_outp_acquire_tmds(&nv_encoder->outp, nv_crtc->index, true, - max_ac_packet, rekey, scdc, hda); + ret = nvif_outp_hdmi(&nv_encoder->outp, nv_crtc->index, true, max_ac_packet, rekey, + mode->clock, hdmi->scdc.supported, hdmi->scdc.scrambling.supported, + hdmi->scdc.scrambling.low_rates); if (ret) return; @@ -838,7 +840,7 @@ nv50_hdmi_enable(struct drm_encoder *encoder, struct nouveau_crtc *nv_crtc, nvif_outp_infoframe(&nv_encoder->outp, NVIF_OUTP_INFOFRAME_V0_VSI, &args.infoframe, size); - nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode); + nv_encoder->hdmi.enabled = true; } /****************************************************************************** @@ -865,6 +867,8 @@ struct nv50_msto { struct nv50_mstc *mstc; bool disabled; bool enabled; + + u32 display_id; }; struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) @@ -882,21 +886,33 @@ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder) static void nv50_msto_cleanup(struct drm_atomic_state *state, - struct drm_dp_mst_topology_state *mst_state, + struct drm_dp_mst_topology_state *new_mst_state, struct drm_dp_mst_topology_mgr *mgr, struct nv50_msto *msto) { struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); - struct drm_dp_mst_atomic_payload *payload = - drm_atomic_get_mst_payload_state(mst_state, msto->mstc->port); + struct drm_dp_mst_atomic_payload *new_payload = + drm_atomic_get_mst_payload_state(new_mst_state, msto->mstc->port); + struct drm_dp_mst_topology_state *old_mst_state = + drm_atomic_get_old_mst_topology_state(state, mgr); + const struct drm_dp_mst_atomic_payload *old_payload = + drm_atomic_get_mst_payload_state(old_mst_state, msto->mstc->port); + struct nv50_mstc *mstc = msto->mstc; + struct nv50_mstm *mstm = mstc->mstm; NV_ATOMIC(drm, "%s: msto cleanup\n", msto->encoder.name); if (msto->disabled) { + if (msto->head->func->display_id) { + nvif_outp_dp_mst_id_put(&mstm->outp->outp, msto->display_id); + msto->display_id = 0; + } + msto->mstc = NULL; msto->disabled = false; + drm_dp_remove_payload_part2(mgr, new_mst_state, old_payload, new_payload); } else if (msto->enabled) { - drm_dp_add_payload_part2(mgr, state, payload); + drm_dp_add_payload_part2(mgr, state, new_payload); msto->enabled = false; } } @@ -910,28 +926,28 @@ nv50_msto_prepare(struct drm_atomic_state *state, struct nouveau_drm *drm = nouveau_drm(msto->encoder.dev); struct nv50_mstc *mstc = msto->mstc; struct nv50_mstm *mstm = mstc->mstm; - struct drm_dp_mst_topology_state *old_mst_state; - struct drm_dp_mst_atomic_payload *payload, *old_payload; + struct drm_dp_mst_atomic_payload *payload; + int ret = 0; NV_ATOMIC(drm, "%s: msto prepare\n", msto->encoder.name); - old_mst_state = drm_atomic_get_old_mst_topology_state(state, mgr); - payload = drm_atomic_get_mst_payload_state(mst_state, mstc->port); - old_payload = drm_atomic_get_mst_payload_state(old_mst_state, mstc->port); - // TODO: Figure out if we want to do a better job of handling VCPI allocation failures here? if (msto->disabled) { - drm_dp_remove_payload(mgr, mst_state, old_payload, payload); - + drm_dp_remove_payload_part1(mgr, mst_state, payload); nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); + ret = 1; } else { if (msto->enabled) - drm_dp_add_payload_part1(mgr, mst_state, payload); + ret = drm_dp_add_payload_part1(mgr, mst_state, payload); + } + if (ret == 0) { nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, payload->vc_start_slot, payload->time_slots, payload->pbn, payload->time_slots * mst_state->pbn_div); + } else { + nvif_outp_dp_mst_vcpi(&mstm->outp->outp, msto->head->base.index, 0, 0, 0, 0); } } @@ -1028,8 +1044,13 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st return; if (!mstm->links++) { - /*XXX: MST audio. */ - nvif_outp_acquire_dp(&mstm->outp->outp, mstm->outp->dp.dpcd, 0, 0, false, true); + nvif_outp_acquire_sor(&mstm->outp->outp, false /*TODO: MST audio... */); + nouveau_dp_train(mstm->outp, true, 0, 0); + } + + if (head->func->display_id) { + if (!WARN_ON(nvif_outp_dp_mst_id_get(&mstm->outp->outp, &msto->display_id))) + head->func->display_id(head, msto->display_id); } if (mstm->outp->outp.or.link & 1) @@ -1052,6 +1073,9 @@ nv50_msto_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *s struct nv50_mstc *mstc = msto->mstc; struct nv50_mstm *mstm = mstc->mstm; + if (msto->head->func->display_id) + msto->head->func->display_id(msto->head, 0); + mstm->outp->update(mstm->outp, msto->head->base.index, NULL, 0, 0); mstm->modified = true; if (!--mstm->links) @@ -1290,6 +1314,12 @@ nv50_mstm_cleanup(struct drm_atomic_state *state, } } + if (mstm->disabled) { + nouveau_dp_power_down(mstm->outp); + nvif_outp_release(&mstm->outp->outp); + mstm->disabled = false; + } + mstm->modified = false; } @@ -1324,12 +1354,6 @@ nv50_mstm_prepare(struct drm_atomic_state *state, nv50_msto_prepare(state, mst_state, &mstm->mgr, msto); } } - - if (mstm->disabled) { - if (!mstm->links) - nvif_outp_release(&mstm->outp->outp); - mstm->disabled = false; - } } static struct drm_connector * @@ -1535,7 +1559,7 @@ static void nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *state) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + struct nv50_head *head = nv50_head(nv_encoder->crtc); struct nouveau_connector *nv_connector = nv50_outp_get_old_connector(state, nv_encoder); #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); @@ -1543,7 +1567,6 @@ nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st #endif struct drm_dp_aux *aux = &nv_connector->aux; int ret; - u8 pwr; #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT if (backlight && backlight->uses_dpcd) { @@ -1554,19 +1577,20 @@ nv50_sor_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *st } #endif - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); - - if (ret == 0) { - pwr &= ~DP_SET_POWER_MASK; - pwr |= DP_SET_POWER_D3; - drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); - } + if (nv_encoder->dcb->type == DCB_OUTPUT_TMDS && nv_encoder->hdmi.enabled) { + nvif_outp_hdmi(&nv_encoder->outp, head->base.index, + false, 0, 0, 0, false, false, false); + nv_encoder->hdmi.enabled = false; } - nv_encoder->update(nv_encoder, nv_crtc->index, NULL, 0, 0); - nv50_audio_disable(encoder, nv_crtc); - nvif_outp_release(&nv_encoder->outp); + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) + nouveau_dp_power_down(nv_encoder); + + if (head->func->display_id) + head->func->display_id(head, 0); + + nv_encoder->update(nv_encoder, head->base.index, NULL, 0, 0); + nv50_audio_disable(encoder, &head->base); nv_encoder->crtc = NULL; } @@ -1579,6 +1603,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta nv50_head_atom(drm_atomic_get_new_crtc_state(state, &nv_crtc->base)); struct drm_display_mode *mode = &asyh->state.adjusted_mode; struct nv50_disp *disp = nv50_disp(encoder->dev); + struct nv50_head *head = nv50_head(&nv_crtc->base); struct nvif_outp *outp = &nv_encoder->outp; struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); @@ -1596,15 +1621,17 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta if ((disp->disp->object.oclass == GT214_DISP || disp->disp->object.oclass >= GF110_DISP) && + nv_encoder->dcb->type != DCB_OUTPUT_LVDS && drm_detect_monitor_audio(nv_connector->edid)) hda = true; + if (!nvif_outp_acquired(outp)) + nvif_outp_acquire_sor(outp, hda); + switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: - if (disp->disp->object.oclass == NV50_DISP || - !drm_detect_hdmi_monitor(nv_connector->edid)) - nvif_outp_acquire_tmds(outp, nv_crtc->index, false, 0, 0, 0, false); - else + if (disp->disp->object.oclass != NV50_DISP && + drm_detect_hdmi_monitor(nv_connector->edid)) nv50_hdmi_enable(encoder, nv_crtc, nv_connector, state, mode, hda); if (nv_encoder->outp.or.link & 1) { @@ -1650,10 +1677,10 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta lvds_8bpc = true; } - nvif_outp_acquire_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc); + nvif_outp_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc); break; case DCB_OUTPUT_DP: - nvif_outp_acquire_dp(&nv_encoder->outp, nv_encoder->dp.dpcd, 0, 0, hda, false); + nouveau_dp_train(nv_encoder, false, mode->clock, asyh->or.bpc); depth = nv50_dp_bpc_to_depth(asyh->or.bpc); if (nv_encoder->outp.or.link & 1) @@ -1661,8 +1688,6 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta else proto = NV887D_SOR_SET_CONTROL_PROTOCOL_DP_B; - nv50_audio_enable(encoder, nv_crtc, nv_connector, state, mode); - #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT backlight = nv_connector->backlight; if (backlight && backlight->uses_dpcd) @@ -1676,6 +1701,9 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta break; } + if (head->func->display_id) + head->func->display_id(head, BIT(nv_encoder->outp.id)); + nv_encoder->update(nv_encoder, nv_crtc->index, asyh, proto, depth); } @@ -1691,14 +1719,13 @@ nv50_sor_destroy(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - nvif_outp_dtor(&nv_encoder->outp); - nv50_mstm_del(&nv_encoder->dp.mstm); drm_encoder_cleanup(encoder); if (nv_encoder->dcb->type == DCB_OUTPUT_DP) mutex_destroy(&nv_encoder->dp.hpd_irq_lock); + nvif_outp_dtor(&nv_encoder->outp); kfree(encoder); } @@ -1707,24 +1734,15 @@ nv50_sor_func = { .destroy = nv50_sor_destroy, }; -bool nv50_has_mst(struct nouveau_drm *drm) -{ - struct nvkm_bios *bios = nvxx_bios(&drm->client.device); - u32 data; - u8 ver, hdr, cnt, len; - - data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len); - return data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04); -} - static int -nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) +nv50_sor_create(struct nouveau_encoder *nv_encoder) { + struct drm_connector *connector = &nv_encoder->conn->base; struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_drm *drm = nouveau_drm(connector->dev); struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); - struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct dcb_output *dcbe = nv_encoder->dcb; struct nv50_disp *disp = nv50_disp(connector->dev); int type, ret; @@ -1737,15 +1755,9 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) break; } - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; nv_encoder->update = nv50_sor_update; encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; drm_encoder_init(connector->dev, encoder, &nv50_sor_func, type, "sor-%04x-%04x", dcbe->hasht, dcbe->hashm); drm_encoder_helper_add(encoder, &nv50_sor_help); @@ -1756,40 +1768,40 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) nv50_outp_dump_caps(drm, nv_encoder); if (dcbe->type == DCB_OUTPUT_DP) { - struct nvkm_i2c_aux *aux = - nvkm_i2c_aux_find(i2c, dcbe->i2c_index); - mutex_init(&nv_encoder->dp.hpd_irq_lock); - if (aux) { - if (disp->disp->object.oclass < GF110_DISP) { - /* HW has no support for address-only - * transactions, so we're required to - * use custom I2C-over-AUX code. - */ - nv_encoder->i2c = &aux->i2c; - } else { - nv_encoder->i2c = &nv_connector->aux.ddc; - } - nv_encoder->aux = aux; + if (disp->disp->object.oclass < GF110_DISP) { + /* HW has no support for address-only + * transactions, so we're required to + * use custom I2C-over-AUX code. + */ + struct nvkm_i2c_aux *aux; + + aux = nvkm_i2c_aux_find(i2c, dcbe->i2c_index); + if (!aux) + return -EINVAL; + + nv_encoder->i2c = &aux->i2c; + } else { + nv_encoder->i2c = &nv_connector->aux.ddc; } - if (nv_connector->type != DCB_CONNECTOR_eDP && - nv50_has_mst(drm)) { + if (nv_connector->type != DCB_CONNECTOR_eDP && nv_encoder->outp.info.dp.mst) { ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 16, nv_connector->base.base.id, &nv_encoder->dp.mstm); if (ret) return ret; } - } else { + } else + if (nv_encoder->outp.info.ddc != NVIF_OUTP_DDC_INVALID) { struct nvkm_i2c_bus *bus = nvkm_i2c_bus_find(i2c, dcbe->i2c_index); if (bus) nv_encoder->i2c = &bus->i2c; } - return nvif_outp_ctor(disp->disp, nv_encoder->base.base.name, dcbe->id, &nv_encoder->outp); + return 0; } /****************************************************************************** @@ -1816,7 +1828,6 @@ nv50_pior_atomic_disable(struct drm_encoder *encoder, struct drm_atomic_state *s core->func->pior->ctrl(core, nv_encoder->outp.or.id, ctrl, NULL); nv_encoder->crtc = NULL; - nvif_outp_release(&nv_encoder->outp); } static void @@ -1844,14 +1855,16 @@ nv50_pior_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st default: asyh->or.depth = NV837D_PIOR_SET_CONTROL_PIXEL_DEPTH_DEFAULT; break; } + if (!nvif_outp_acquired(&nv_encoder->outp)) + nvif_outp_acquire_pior(&nv_encoder->outp); + switch (nv_encoder->dcb->type) { case DCB_OUTPUT_TMDS: ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC); - nvif_outp_acquire_tmds(&nv_encoder->outp, false, false, 0, 0, 0, false); break; case DCB_OUTPUT_DP: ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC); - nvif_outp_acquire_dp(&nv_encoder->outp, nv_encoder->dp.dpcd, 0, 0, false, false); + nouveau_dp_train(nv_encoder, false, asyh->state.adjusted_mode.clock, 6); break; default: BUG(); @@ -1888,8 +1901,9 @@ nv50_pior_func = { }; static int -nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) +nv50_pior_create(struct nouveau_encoder *nv_encoder) { + struct drm_connector *connector = &nv_encoder->conn->base; struct drm_device *dev = connector->dev; struct nouveau_drm *drm = nouveau_drm(dev); struct nv50_disp *disp = nv50_disp(dev); @@ -1897,18 +1911,18 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) struct nvkm_i2c_bus *bus = NULL; struct nvkm_i2c_aux *aux = NULL; struct i2c_adapter *ddc; - struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct dcb_output *dcbe = nv_encoder->dcb; int type; switch (dcbe->type) { case DCB_OUTPUT_TMDS: - bus = nvkm_i2c_bus_find(i2c, NVKM_I2C_BUS_EXT(dcbe->extdev)); + bus = nvkm_i2c_bus_find(i2c, nv_encoder->outp.info.ddc); ddc = bus ? &bus->i2c : NULL; type = DRM_MODE_ENCODER_TMDS; break; case DCB_OUTPUT_DP: - aux = nvkm_i2c_aux_find(i2c, NVKM_I2C_AUX_EXT(dcbe->extdev)); + aux = nvkm_i2c_aux_find(i2c, nv_encoder->outp.info.dp.aux); ddc = aux ? &aux->i2c : NULL; type = DRM_MODE_ENCODER_TMDS; break; @@ -1916,18 +1930,11 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) return -ENODEV; } - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; nv_encoder->i2c = ddc; - nv_encoder->aux = aux; mutex_init(&nv_encoder->dp.hpd_irq_lock); encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; drm_encoder_init(connector->dev, encoder, &nv50_pior_func, type, "pior-%04x-%04x", dcbe->hasht, dcbe->hashm); drm_encoder_helper_add(encoder, &nv50_pior_help); @@ -1937,7 +1944,7 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) disp->core->func->pior->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); nv50_outp_dump_caps(drm, nv_encoder); - return nvif_outp_ctor(disp->disp, nv_encoder->base.base.name, dcbe->id, &nv_encoder->outp); + return 0; } /****************************************************************************** @@ -1951,7 +1958,9 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) struct drm_dp_mst_topology_state *mst_state; struct nouveau_drm *drm = nouveau_drm(state->dev); struct nv50_disp *disp = nv50_disp(drm->dev); + struct nv50_atom *atom = nv50_atom(state); struct nv50_core *core = disp->core; + struct nv50_outp_atom *outp; struct nv50_mstm *mstm; int i; @@ -1974,6 +1983,23 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) if (mstm->modified) nv50_mstm_cleanup(state, mst_state, mstm); } + + list_for_each_entry(outp, &atom->outp, head) { + if (outp->encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { + struct nouveau_encoder *nv_encoder = nouveau_encoder(outp->encoder); + + if (outp->enabled) { + nv50_audio_enable(outp->encoder, nouveau_crtc(nv_encoder->crtc), + nv_encoder->conn, NULL, NULL); + outp->enabled = outp->disabled = false; + } else { + if (outp->disabled) { + nvif_outp_release(&nv_encoder->outp); + outp->disabled = false; + } + } + } + } } static void @@ -2065,14 +2091,8 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (outp->clr.mask) { help->atomic_disable(encoder, state); + outp->disabled = true; interlock[NV50_DISP_INTERLOCK_CORE] |= 1; - if (outp->flush_disable) { - nv50_disp_atomic_commit_wndw(state, interlock); - nv50_disp_atomic_commit_core(state, interlock); - memset(interlock, 0x00, sizeof(interlock)); - - flushed = true; - } } } @@ -2092,7 +2112,7 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) nv50_crc_atomic_init_notifier_contexts(state); /* Update output path(s). */ - list_for_each_entry_safe(outp, outt, &atom->outp, head) { + list_for_each_entry(outp, &atom->outp, head) { const struct drm_encoder_helper_funcs *help; struct drm_encoder *encoder; @@ -2104,11 +2124,9 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (outp->set.mask) { help->atomic_enable(encoder, state); + outp->enabled = true; interlock[NV50_DISP_INTERLOCK_CORE] = 1; } - - list_del(&outp->head); - kfree(outp); } /* Update head(s). */ @@ -2206,6 +2224,11 @@ nv50_disp_atomic_commit_tail(struct drm_atomic_state *state) if (atom->lock_core) mutex_unlock(&disp->mutex); + list_for_each_entry_safe(outp, outt, &atom->outp, head) { + list_del(&outp->head); + kfree(outp); + } + /* Wait for HW to signal completion. */ for_each_new_plane_in_state(state, plane, new_plane_state, i) { struct nv50_wndw_atom *asyw = nv50_wndw_atom(new_plane_state); @@ -2354,10 +2377,9 @@ nv50_disp_outp_atomic_check_clr(struct nv50_atom *atom, if (IS_ERR(outp)) return PTR_ERR(outp); - if (outp->encoder->encoder_type == DRM_MODE_ENCODER_DPMST) { - outp->flush_disable = true; + if (outp->encoder->encoder_type == DRM_MODE_ENCODER_DPMST || + nouveau_encoder(outp->encoder)->dcb->type == DCB_OUTPUT_DP) atom->flush_disable = true; - } outp->clr.ctrl = true; atom->lock_core = true; } @@ -2518,6 +2540,104 @@ nv50_display_fini(struct drm_device *dev, bool runtime, bool suspend) cancel_work_sync(&drm->hpd_work); } +static inline void +nv50_display_read_hw_or_state(struct drm_device *dev, struct nv50_disp *disp, + struct nouveau_encoder *outp) +{ + struct drm_crtc *crtc; + struct drm_connector_list_iter conn_iter; + struct drm_connector *conn; + struct nv50_head_atom *armh; + const u32 encoder_mask = drm_encoder_mask(&outp->base.base); + bool found_conn = false, found_head = false; + u8 proto; + int head_idx; + int ret; + + switch (outp->dcb->type) { + case DCB_OUTPUT_TMDS: + ret = nvif_outp_inherit_tmds(&outp->outp, &proto); + break; + case DCB_OUTPUT_DP: + ret = nvif_outp_inherit_dp(&outp->outp, &proto); + break; + case DCB_OUTPUT_LVDS: + ret = nvif_outp_inherit_lvds(&outp->outp, &proto); + break; + case DCB_OUTPUT_ANALOG: + ret = nvif_outp_inherit_rgb_crt(&outp->outp, &proto); + break; + default: + drm_dbg_kms(dev, "Readback for %s not implemented yet, skipping\n", + outp->base.base.name); + drm_WARN_ON(dev, true); + return; + } + + if (ret < 0) + return; + + head_idx = ret; + + drm_for_each_crtc(crtc, dev) { + if (crtc->index != head_idx) + continue; + + armh = nv50_head_atom(crtc->state); + found_head = true; + break; + } + if (drm_WARN_ON(dev, !found_head)) + return; + + /* Figure out which connector is being used by this encoder */ + drm_connector_list_iter_begin(dev, &conn_iter); + nouveau_for_each_non_mst_connector_iter(conn, &conn_iter) { + if (nouveau_connector(conn)->index == outp->dcb->connector) { + found_conn = true; + break; + } + } + drm_connector_list_iter_end(&conn_iter); + if (drm_WARN_ON(dev, !found_conn)) + return; + + armh->state.encoder_mask = encoder_mask; + armh->state.connector_mask = drm_connector_mask(conn); + armh->state.active = true; + armh->state.enable = true; + pm_runtime_get_noresume(dev->dev); + + outp->crtc = crtc; + outp->ctrl = NVVAL(NV507D, SOR_SET_CONTROL, PROTOCOL, proto) | BIT(crtc->index); + + drm_connector_get(conn); + conn->state->crtc = crtc; + conn->state->best_encoder = &outp->base.base; +} + +/* Read back the currently programmed display state */ +static void +nv50_display_read_hw_state(struct nouveau_drm *drm) +{ + struct drm_device *dev = drm->dev; + struct drm_encoder *encoder; + struct drm_modeset_acquire_ctx ctx; + struct nv50_disp *disp = nv50_disp(dev); + int ret; + + DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret); + + drm_for_each_encoder(encoder, dev) { + if (encoder->encoder_type == DRM_MODE_ENCODER_DPMST) + continue; + + nv50_display_read_hw_or_state(dev, disp, nouveau_encoder(encoder)); + } + + DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); +} + static int nv50_display_init(struct drm_device *dev, bool resume, bool runtime) { @@ -2535,6 +2655,9 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime) } } + if (!resume) + nv50_display_read_hw_state(nouveau_drm(dev)); + return 0; } @@ -2561,14 +2684,11 @@ nv50_display_destroy(struct drm_device *dev) int nv50_display_create(struct drm_device *dev) { - struct nvif_device *device = &nouveau_drm(dev)->client.device; struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_table *dcb = &drm->vbios.dcb; struct drm_connector *connector, *tmp; struct nv50_disp *disp; - struct dcb_output *dcbe; - int crtcs, ret, i; - bool has_mst = nv50_has_mst(drm); + int ret, i; + bool has_mst = false; disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) @@ -2644,20 +2764,92 @@ nv50_display_create(struct drm_device *dev) dev->mode_config.cursor_height = 64; } - /* create crtc objects to represent the hw heads */ - if (disp->disp->object.oclass >= GV100_DISP) - crtcs = nvif_rd32(&device->object, 0x610060) & 0xff; - else - if (disp->disp->object.oclass >= GF110_DISP) - crtcs = nvif_rd32(&device->object, 0x612004) & 0xf; - else - crtcs = 0x3; + /* create encoder/connector objects based on VBIOS DCB table */ + for_each_set_bit(i, &disp->disp->outp_mask, sizeof(disp->disp->outp_mask) * 8) { + struct nouveau_encoder *outp; - for (i = 0; i < fls(crtcs); i++) { - struct nv50_head *head; + outp = kzalloc(sizeof(*outp), GFP_KERNEL); + if (!outp) + break; + + ret = nvif_outp_ctor(disp->disp, "kmsOutp", i, &outp->outp); + if (ret) { + kfree(outp); + continue; + } + + connector = nouveau_connector_create(dev, outp->outp.info.conn); + if (IS_ERR(connector)) { + nvif_outp_dtor(&outp->outp); + kfree(outp); + continue; + } + + outp->base.base.possible_crtcs = outp->outp.info.heads; + outp->base.base.possible_clones = 0; + outp->conn = nouveau_connector(connector); + + outp->dcb = kzalloc(sizeof(*outp->dcb), GFP_KERNEL); + if (!outp->dcb) + break; + + switch (outp->outp.info.proto) { + case NVIF_OUTP_RGB_CRT: + outp->dcb->type = DCB_OUTPUT_ANALOG; + outp->dcb->crtconf.maxfreq = outp->outp.info.rgb_crt.freq_max; + break; + case NVIF_OUTP_TMDS: + outp->dcb->type = DCB_OUTPUT_TMDS; + outp->dcb->duallink_possible = outp->outp.info.tmds.dual; + break; + case NVIF_OUTP_LVDS: + outp->dcb->type = DCB_OUTPUT_LVDS; + outp->dcb->lvdsconf.use_acpi_for_edid = outp->outp.info.lvds.acpi_edid; + break; + case NVIF_OUTP_DP: + outp->dcb->type = DCB_OUTPUT_DP; + outp->dcb->dpconf.link_nr = outp->outp.info.dp.link_nr; + outp->dcb->dpconf.link_bw = outp->outp.info.dp.link_bw; + if (outp->outp.info.dp.mst) + has_mst = true; + break; + default: + WARN_ON(1); + continue; + } - if (!(crtcs & (1 << i))) + outp->dcb->heads = outp->outp.info.heads; + outp->dcb->connector = outp->outp.info.conn; + outp->dcb->i2c_index = outp->outp.info.ddc; + + switch (outp->outp.info.type) { + case NVIF_OUTP_DAC : ret = nv50_dac_create(outp); break; + case NVIF_OUTP_SOR : ret = nv50_sor_create(outp); break; + case NVIF_OUTP_PIOR: ret = nv50_pior_create(outp); break; + default: + WARN_ON(1); continue; + } + + if (ret) { + NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", + i, outp->outp.info.type, outp->outp.info.proto, ret); + } + } + + /* cull any connectors we created that don't have an encoder */ + list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { + if (connector->possible_encoders) + continue; + + NV_WARN(drm, "%s has no encoders, removing\n", + connector->name); + connector->funcs->destroy(connector); + } + + /* create crtc objects to represent the hw heads */ + for_each_set_bit(i, &disp->disp->head_mask, sizeof(disp->disp->head_mask) * 8) { + struct nv50_head *head; head = nv50_head_create(dev, i); if (IS_ERR(head)) { @@ -2683,52 +2875,10 @@ nv50_display_create(struct drm_device *dev) * Once these issues are closed, this should be * removed */ - head->msto->encoder.possible_crtcs = crtcs; - } - } - - /* create encoder/connector objects based on VBIOS DCB table */ - for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { - connector = nouveau_connector_create(dev, dcbe); - if (IS_ERR(connector)) - continue; - - if (dcbe->location == DCB_LOC_ON_CHIP) { - switch (dcbe->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - ret = nv50_sor_create(connector, dcbe); - break; - case DCB_OUTPUT_ANALOG: - ret = nv50_dac_create(connector, dcbe); - break; - default: - ret = -ENODEV; - break; - } - } else { - ret = nv50_pior_create(connector, dcbe); - } - - if (ret) { - NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n", - dcbe->location, dcbe->type, - ffs(dcbe->or) - 1, ret); - ret = 0; + head->msto->encoder.possible_crtcs = disp->disp->head_mask; } } - /* cull any connectors we created that don't have an encoder */ - list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { - if (connector->possible_encoders) - continue; - - NV_WARN(drm, "%s has no encoders, removing\n", - connector->name); - connector->funcs->destroy(connector); - } - /* Disable vblank irqs aggressively for power-saving, safe on nv50+ */ dev->vblank_disable_immediate = true; diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index 9d66c9c726c3..5508a7cfd492 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -83,7 +83,9 @@ struct nv50_outp_atom { struct list_head head; struct drm_encoder *encoder; - bool flush_disable; + + bool disabled; + bool enabled; union nv50_outp_atom_mask { struct { @@ -106,8 +108,6 @@ void nv50_dmac_destroy(struct nv50_dmac *); */ struct nouveau_encoder *nv50_real_outp(struct drm_encoder *encoder); -bool nv50_has_mst(struct nouveau_drm *drm); - u32 *evo_wait(struct nv50_dmac *, int nr); void evo_kick(u32 *, struct nv50_dmac *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/head.h b/drivers/gpu/drm/nouveau/dispnv50/head.h index 41c8788dfb31..e9d17037ffcf 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/head.h +++ b/drivers/gpu/drm/nouveau/dispnv50/head.h @@ -49,6 +49,7 @@ struct nv50_head_func { int (*procamp)(struct nv50_head *, struct nv50_head_atom *); int (*or)(struct nv50_head *, struct nv50_head_atom *); void (*static_wndw_map)(struct nv50_head *, struct nv50_head_atom *); + int (*display_id)(struct nv50_head *, u32 display_id); }; extern const struct nv50_head_func head507d; diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 543f08ceaad6..53b1248c40ec 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -28,6 +28,19 @@ #include <nvhw/class/clc57d.h> static int +headc57d_display_id(struct nv50_head *head, u32 display_id) +{ + struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; + int ret; + + if ((ret = PUSH_WAIT(push, 2))) + return ret; + + PUSH_NVSQ(push, NVC57D, 0x2020 + (head->base.index * 0x400), display_id); + return 0; +} + +static int headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) { struct nvif_push *push = nv50_disp(head->base.base.dev)->core->chan.push; @@ -250,4 +263,5 @@ headc57d = { .or = headc57d_or, /* TODO: flexible window mappings */ .static_wndw_map = headc37d_static_wndw_map, + .display_id = headc57d_display_id, }; diff --git a/drivers/gpu/drm/nouveau/include/nvif/conn.h b/drivers/gpu/drm/nouveau/include/nvif/conn.h index dc355e1dfafa..406c12a111f9 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/conn.h +++ b/drivers/gpu/drm/nouveau/include/nvif/conn.h @@ -7,6 +7,21 @@ struct nvif_disp; struct nvif_conn { struct nvif_object object; + u32 id; + + struct { + enum { + NVIF_CONN_VGA, + NVIF_CONN_TV, + NVIF_CONN_DVI_I, + NVIF_CONN_DVI_D, + NVIF_CONN_LVDS, + NVIF_CONN_LVDS_SPWG, + NVIF_CONN_HDMI, + NVIF_CONN_DP, + NVIF_CONN_EDP, + } type; + } info; }; int nvif_conn_ctor(struct nvif_disp *, const char *name, int id, struct nvif_conn *); @@ -18,11 +33,6 @@ nvif_conn_id(struct nvif_conn *conn) return conn->object.handle; } -#define NVIF_CONN_HPD_STATUS_UNSUPPORTED 0 /* negative if query fails */ -#define NVIF_CONN_HPD_STATUS_NOT_PRESENT 1 -#define NVIF_CONN_HPD_STATUS_PRESENT 2 -int nvif_conn_hpd_status(struct nvif_conn *); - int nvif_conn_event_ctor(struct nvif_conn *, const char *name, nvif_event_func, u8 types, struct nvif_event *); #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0011.h b/drivers/gpu/drm/nouveau/include/nvif/if0011.h index 69b0b779f942..3ed0ddd75bd8 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/if0011.h +++ b/drivers/gpu/drm/nouveau/include/nvif/if0011.h @@ -7,6 +7,16 @@ union nvif_conn_args { __u8 version; __u8 id; /* DCB connector table index. */ __u8 pad02[6]; +#define NVIF_CONN_V0_VGA 0x00 +#define NVIF_CONN_V0_TV 0x01 +#define NVIF_CONN_V0_DVI_I 0x02 +#define NVIF_CONN_V0_DVI_D 0x03 +#define NVIF_CONN_V0_LVDS 0x04 +#define NVIF_CONN_V0_LVDS_SPWG 0x05 +#define NVIF_CONN_V0_HDMI 0x06 +#define NVIF_CONN_V0_DP 0x07 +#define NVIF_CONN_V0_EDP 0x08 + __u8 type; } v0; }; @@ -20,15 +30,4 @@ union nvif_conn_event_args { __u8 pad02[6]; } v0; }; - -#define NVIF_CONN_V0_HPD_STATUS 0x00000000 - -union nvif_conn_hpd_status_args { - struct nvif_conn_hpd_status_v0 { - __u8 version; - __u8 support; - __u8 present; - __u8 pad03[5]; - } v0; -}; #endif diff --git a/drivers/gpu/drm/nouveau/include/nvif/if0012.h b/drivers/gpu/drm/nouveau/include/nvif/if0012.h index 16d4ad5023a3..bde9bfae8d11 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/if0012.h +++ b/drivers/gpu/drm/nouveau/include/nvif/if0012.h @@ -8,18 +8,86 @@ union nvif_outp_args { struct nvif_outp_v0 { __u8 version; __u8 id; /* DCB device index. */ - __u8 pad02[6]; +#define NVIF_OUTP_V0_TYPE_DAC 0x00 +#define NVIF_OUTP_V0_TYPE_SOR 0x01 +#define NVIF_OUTP_V0_TYPE_PIOR 0x02 + __u8 type; +#define NVIF_OUTP_V0_PROTO_RGB_CRT 0x00 +#define NVIF_OUTP_V0_PROTO_TMDS 0x01 +#define NVIF_OUTP_V0_PROTO_LVDS 0x02 +#define NVIF_OUTP_V0_PROTO_DP 0x03 + __u8 proto; + __u8 heads; + __u8 ddc; + __u8 conn; + union { + struct { + __u32 freq_max; + } rgb_crt; + struct { + __u8 dual; + } tmds; + struct { + __u8 acpi_edid; + } lvds; + struct { + __u8 aux; + __u8 mst; + __u8 increased_wm; + __u8 link_nr; + __u32 link_bw; + } dp; + }; } v0; }; -#define NVIF_OUTP_V0_LOAD_DETECT 0x00 -#define NVIF_OUTP_V0_ACQUIRE 0x01 -#define NVIF_OUTP_V0_RELEASE 0x02 -#define NVIF_OUTP_V0_INFOFRAME 0x03 -#define NVIF_OUTP_V0_HDA_ELD 0x04 -#define NVIF_OUTP_V0_DP_AUX_PWR 0x05 -#define NVIF_OUTP_V0_DP_RETRAIN 0x06 -#define NVIF_OUTP_V0_DP_MST_VCPI 0x07 +#define NVIF_OUTP_V0_DETECT 0x00 +#define NVIF_OUTP_V0_EDID_GET 0x01 + +#define NVIF_OUTP_V0_INHERIT 0x10 +#define NVIF_OUTP_V0_ACQUIRE 0x11 +#define NVIF_OUTP_V0_RELEASE 0x12 + +#define NVIF_OUTP_V0_LOAD_DETECT 0x20 + +#define NVIF_OUTP_V0_BL_GET 0x30 +#define NVIF_OUTP_V0_BL_SET 0x31 + +#define NVIF_OUTP_V0_LVDS 0x40 + +#define NVIF_OUTP_V0_HDMI 0x50 + +#define NVIF_OUTP_V0_INFOFRAME 0x60 +#define NVIF_OUTP_V0_HDA_ELD 0x61 + +#define NVIF_OUTP_V0_DP_AUX_PWR 0x70 +#define NVIF_OUTP_V0_DP_AUX_XFER 0x71 +#define NVIF_OUTP_V0_DP_RATES 0x72 +#define NVIF_OUTP_V0_DP_TRAIN 0x73 +#define NVIF_OUTP_V0_DP_DRIVE 0x74 +#define NVIF_OUTP_V0_DP_SST 0x75 +#define NVIF_OUTP_V0_DP_MST_ID_GET 0x76 +#define NVIF_OUTP_V0_DP_MST_ID_PUT 0x77 +#define NVIF_OUTP_V0_DP_MST_VCPI 0x78 + +union nvif_outp_detect_args { + struct nvif_outp_detect_v0 { + __u8 version; +#define NVIF_OUTP_DETECT_V0_NOT_PRESENT 0x00 +#define NVIF_OUTP_DETECT_V0_PRESENT 0x01 +#define NVIF_OUTP_DETECT_V0_UNKNOWN 0x02 + __u8 status; + } v0; +}; + +union nvif_outp_edid_get_args { + struct nvif_outp_edid_get_v0 { + __u8 version; + __u8 pad01; + __u16 size; + __u8 data[2048]; + } v0; +}; union nvif_outp_load_detect_args { struct nvif_outp_load_detect_v0 { @@ -33,40 +101,39 @@ union nvif_outp_load_detect_args { union nvif_outp_acquire_args { struct nvif_outp_acquire_v0 { __u8 version; -#define NVIF_OUTP_ACQUIRE_V0_RGB_CRT 0x00 -#define NVIF_OUTP_ACQUIRE_V0_TV 0x01 -#define NVIF_OUTP_ACQUIRE_V0_TMDS 0x02 -#define NVIF_OUTP_ACQUIRE_V0_LVDS 0x03 -#define NVIF_OUTP_ACQUIRE_V0_DP 0x04 - __u8 proto; +#define NVIF_OUTP_ACQUIRE_V0_DAC 0x00 +#define NVIF_OUTP_ACQUIRE_V0_SOR 0x01 +#define NVIF_OUTP_ACQUIRE_V0_PIOR 0x02 + __u8 type; __u8 or; __u8 link; __u8 pad04[4]; union { struct { - __u8 head; - __u8 hdmi; - __u8 hdmi_max_ac_packet; - __u8 hdmi_rekey; -#define NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_SCRAMBLE (1 << 0) -#define NVIF_OUTP_ACQUIRE_V0_TMDS_HDMI_SCDC_DIV_BY_4 (1 << 1) - __u8 hdmi_scdc; - __u8 hdmi_hda; - __u8 pad06[2]; - } tmds; - struct { - __u8 dual; - __u8 bpc8; - __u8 pad02[6]; - } lvds; + __u8 hda; + } sor; + }; + } v0; +}; + +union nvif_outp_inherit_args { + struct nvif_outp_inherit_v0 { + __u8 version; +#define NVIF_OUTP_INHERIT_V0_RGB_CRT 0x00 +#define NVIF_OUTP_INHERIT_V0_TV 0x01 +#define NVIF_OUTP_INHERIT_V0_TMDS 0x02 +#define NVIF_OUTP_INHERIT_V0_LVDS 0x03 +#define NVIF_OUTP_INHERIT_V0_DP 0x04 + // In/out. Input is one of the above values, output is the actual hw protocol + __u8 proto; + __u8 or; + __u8 link; + __u8 head; + union { struct { - __u8 link_nr; /* 0 = highest possible. */ - __u8 link_bw; /* 0 = highest possible, DP BW code otherwise. */ + // TODO: Figure out padding, and whether we even want this field __u8 hda; - __u8 mst; - __u8 pad04[4]; - __u8 dpcd[DP_RECEIVER_CAP_SIZE]; - } dp; + } tmds; }; } v0; }; @@ -76,6 +143,42 @@ union nvif_outp_release_args { } vn; }; +union nvif_outp_bl_get_args { + struct nvif_outp_bl_get_v0 { + __u8 version; + __u8 level; + } v0; +}; + +union nvif_outp_bl_set_args { + struct nvif_outp_bl_set_v0 { + __u8 version; + __u8 level; + } v0; +}; + +union nvif_outp_lvds_args { + struct nvif_outp_lvds_v0 { + __u8 version; + __u8 dual; + __u8 bpc8; + } v0; +}; + +union nvif_outp_hdmi_args { + struct nvif_outp_hdmi_v0 { + __u8 version; + __u8 head; + __u8 enable; + __u8 max_ac_packet; + __u8 rekey; + __u8 scdc; + __u8 scdc_scrambling; + __u8 scdc_low_rates; + __u32 khz; + } v0; +}; + union nvif_outp_infoframe_args { struct nvif_outp_infoframe_v0 { __u8 version; @@ -105,9 +208,77 @@ union nvif_outp_dp_aux_pwr_args { } v0; }; -union nvif_outp_dp_retrain_args { - struct nvif_outp_dp_retrain_vn { - } vn; +union nvif_outp_dp_aux_xfer_args { + struct nvif_outp_dp_aux_xfer_v0 { + __u8 version; + __u8 pad01; + __u8 type; + __u8 size; + __u32 addr; + __u8 data[16]; + } v0; +}; + +union nvif_outp_dp_rates_args { + struct nvif_outp_dp_rates_v0 { + __u8 version; + __u8 pad01[6]; + __u8 rates; + struct { + __s8 dpcd; + __u32 rate; + } rate[8]; + } v0; +}; + +union nvif_outp_dp_train_args { + struct nvif_outp_dp_train_v0 { + __u8 version; + __u8 retrain; + __u8 mst; + __u8 lttprs; + __u8 post_lt_adj; + __u8 link_nr; + __u32 link_bw; + __u8 dpcd[DP_RECEIVER_CAP_SIZE]; + } v0; +}; + +union nvif_outp_dp_drive_args { + struct nvif_outp_dp_drive_v0 { + __u8 version; + __u8 pad01[2]; + __u8 lanes; + __u8 pe[4]; + __u8 vs[4]; + } v0; +}; + +union nvif_outp_dp_sst_args { + struct nvif_outp_dp_sst_v0 { + __u8 version; + __u8 head; + __u8 pad02[2]; + __u32 watermark; + __u32 hblanksym; + __u32 vblanksym; + } v0; +}; + +union nvif_outp_dp_mst_id_put_args { + struct nvif_outp_dp_mst_id_put_v0 { + __u8 version; + __u8 pad01[3]; + __u32 id; + } v0; +}; + +union nvif_outp_dp_mst_id_get_args { + struct nvif_outp_dp_mst_id_get_v0 { + __u8 version; + __u8 pad01[3]; + __u32 id; + } v0; }; union nvif_outp_dp_mst_vcpi_args { diff --git a/drivers/gpu/drm/nouveau/include/nvif/outp.h b/drivers/gpu/drm/nouveau/include/nvif/outp.h index fa76a7b5e4b3..bc122a5ba7df 100644 --- a/drivers/gpu/drm/nouveau/include/nvif/outp.h +++ b/drivers/gpu/drm/nouveau/include/nvif/outp.h @@ -8,6 +8,46 @@ struct nvif_disp; struct nvif_outp { struct nvif_object object; + u32 id; + + struct { + enum { + NVIF_OUTP_DAC, + NVIF_OUTP_SOR, + NVIF_OUTP_PIOR, + } type; + + enum { + NVIF_OUTP_RGB_CRT, + NVIF_OUTP_TMDS, + NVIF_OUTP_LVDS, + NVIF_OUTP_DP, + } proto; + + u8 heads; +#define NVIF_OUTP_DDC_INVALID 0xff + u8 ddc; + u8 conn; + + union { + struct { + u32 freq_max; + } rgb_crt; + struct { + bool dual; + } tmds; + struct { + bool acpi_edid; + } lvds; + struct { + u8 aux; + bool mst; + bool increased_wm; + u8 link_nr; + u32 link_bw; + } dp; + }; + } info; struct { int id; @@ -17,18 +57,60 @@ struct nvif_outp { int nvif_outp_ctor(struct nvif_disp *, const char *name, int id, struct nvif_outp *); void nvif_outp_dtor(struct nvif_outp *); + +enum nvif_outp_detect_status { + NOT_PRESENT, + PRESENT, + UNKNOWN, +}; + +enum nvif_outp_detect_status nvif_outp_detect(struct nvif_outp *); +int nvif_outp_edid_get(struct nvif_outp *, u8 **pedid); + int nvif_outp_load_detect(struct nvif_outp *, u32 loadval); -int nvif_outp_acquire_rgb_crt(struct nvif_outp *); -int nvif_outp_acquire_tmds(struct nvif_outp *, int head, - bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda); -int nvif_outp_acquire_lvds(struct nvif_outp *, bool dual, bool bpc8); -int nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], - int link_nr, int link_bw, bool hda, bool mst); +int nvif_outp_acquire_dac(struct nvif_outp *); +int nvif_outp_acquire_sor(struct nvif_outp *, bool hda); +int nvif_outp_acquire_pior(struct nvif_outp *); +int nvif_outp_inherit_rgb_crt(struct nvif_outp *outp, u8 *proto_out); +int nvif_outp_inherit_lvds(struct nvif_outp *outp, u8 *proto_out); +int nvif_outp_inherit_tmds(struct nvif_outp *outp, u8 *proto_out); +int nvif_outp_inherit_dp(struct nvif_outp *outp, u8 *proto_out); + void nvif_outp_release(struct nvif_outp *); + +static inline bool +nvif_outp_acquired(struct nvif_outp *outp) +{ + return outp->or.id >= 0; +} + +int nvif_outp_bl_get(struct nvif_outp *); +int nvif_outp_bl_set(struct nvif_outp *, int level); + +int nvif_outp_lvds(struct nvif_outp *, bool dual, bool bpc8); + +int nvif_outp_hdmi(struct nvif_outp *, int head, bool enable, u8 max_ac_packet, u8 rekey, u32 khz, + bool scdc, bool scdc_scrambling, bool scdc_low_rates); + int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size); int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size); + int nvif_outp_dp_aux_pwr(struct nvif_outp *, bool enable); -int nvif_outp_dp_retrain(struct nvif_outp *); +int nvif_outp_dp_aux_xfer(struct nvif_outp *, u8 type, u8 *size, u32 addr, u8 *data); + +struct nvif_outp_dp_rate { + int dpcd; /* -1 for non-indexed rates */ + u32 rate; +}; + +int nvif_outp_dp_rates(struct nvif_outp *, struct nvif_outp_dp_rate *rate, int rate_nr); +int nvif_outp_dp_train(struct nvif_outp *, u8 dpcd[DP_RECEIVER_CAP_SIZE], + u8 lttprs, u8 link_nr, u32 link_bw, bool mst, bool post_lt_adj, + bool retrain); +int nvif_outp_dp_drive(struct nvif_outp *, u8 link_nr, u8 pe[4], u8 vs[4]); +int nvif_outp_dp_sst(struct nvif_outp *, int head, u32 watermark, u32 hblanksym, u32 vblanksym); +int nvif_outp_dp_mst_id_get(struct nvif_outp *, u32 *id); +int nvif_outp_dp_mst_id_put(struct nvif_outp *, u32 id); int nvif_outp_dp_mst_vcpi(struct nvif_outp *, int head, u8 start_slot, u8 num_slots, u16 pbn, u16 aligned_pbn); #endif diff --git a/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h b/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h index d3b6a68ddda3..fc0f38981391 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/core/memory.h @@ -12,6 +12,7 @@ struct nvkm_tags { }; enum nvkm_memory_target { + NVKM_MEM_TARGET_INST_SR_LOST, /* instance memory - not preserved across suspend */ NVKM_MEM_TARGET_INST, /* instance memory */ NVKM_MEM_TARGET_VRAM, /* video memory */ NVKM_MEM_TARGET_HOST, /* coherent system memory */ diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h index fcdaefc99fe8..92a36ddfc29f 100644 --- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h +++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/instmem.h @@ -26,7 +26,7 @@ struct nvkm_instmem { u32 nvkm_instmem_rd32(struct nvkm_instmem *, u32 addr); void nvkm_instmem_wr32(struct nvkm_instmem *, u32 addr, u32 data); -int nvkm_instobj_new(struct nvkm_instmem *, u32 size, u32 align, bool zero, +int nvkm_instobj_new(struct nvkm_instmem *, u32 size, u32 align, bool zero, bool preserve, struct nvkm_memory **); int nvkm_instobj_wrap(struct nvkm_device *, struct nvkm_memory *, struct nvkm_memory **); diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 91b5ecc57538..d47442125fa1 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -109,42 +109,6 @@ nv40_backlight_init(struct nouveau_encoder *encoder, return 0; } -static int -nv50_get_intensity(struct backlight_device *bd) -{ - struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div = 1025; - u32 val; - - val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); - val &= NV50_PDISP_SOR_PWM_CTL_VAL; - return ((val * 100) + (div / 2)) / div; -} - -static int -nv50_set_intensity(struct backlight_device *bd) -{ - struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div = 1025; - u32 val = (bd->props.brightness * div) / 100; - - nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), - NV50_PDISP_SOR_PWM_CTL_NEW | val); - return 0; -} - -static const struct backlight_ops nv50_bl_ops = { - .options = BL_CORE_SUSPENDRESUME, - .get_brightness = nv50_get_intensity, - .update_status = nv50_set_intensity, -}; - /* * eDP brightness callbacks need to happen under lock, since we need to * enable/disable the backlight ourselves for modesets @@ -238,53 +202,25 @@ static const struct backlight_ops nv50_edp_bl_ops = { }; static int -nva3_get_intensity(struct backlight_device *bd) +nv50_get_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div, val; - div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); - val = nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(or)); - val &= NVA3_PDISP_SOR_PWM_CTL_VAL; - if (div && div >= val) - return ((val * 100) + (div / 2)) / div; - - return 100; + return nvif_outp_bl_get(&nv_encoder->outp); } static int -nva3_set_intensity(struct backlight_device *bd) +nv50_set_intensity(struct backlight_device *bd) { struct nouveau_encoder *nv_encoder = bl_get_data(bd); - struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; - int or = ffs(nv_encoder->dcb->or) - 1; - u32 div, val; - - div = nvif_rd32(device, NV50_PDISP_SOR_PWM_DIV(or)); - val = backlight_get_brightness(bd); - if (val) - val = (val * div) / 100; - - if (div) { - nvif_wr32(device, NV50_PDISP_SOR_PWM_CTL(or), - val | - NV50_PDISP_SOR_PWM_CTL_NEW | - NVA3_PDISP_SOR_PWM_CTL_UNK); - return 0; - } - - return -EINVAL; + return nvif_outp_bl_set(&nv_encoder->outp, backlight_get_brightness(bd)); } -static const struct backlight_ops nva3_bl_ops = { +static const struct backlight_ops nv50_bl_ops = { .options = BL_CORE_SUSPENDRESUME, - .get_brightness = nva3_get_intensity, - .update_status = nva3_set_intensity, + .get_brightness = nv50_get_intensity, + .update_status = nv50_set_intensity, }; /* FIXME: perform backlight probing for eDP _before_ this, this only gets called after connector @@ -298,13 +234,12 @@ nv50_backlight_init(struct nouveau_backlight *bl, const struct backlight_ops **ops) { struct nouveau_drm *drm = nouveau_drm(nv_encoder->base.base.dev); - struct nvif_object *device = &drm->client.device.object; /* * Note when this runs the connectors have not been probed yet, * so nv_conn->base.status is not set yet. */ - if (!nvif_rd32(device, NV50_PDISP_SOR_PWM_CTL(ffs(nv_encoder->dcb->or) - 1)) || + if (nvif_outp_bl_get(&nv_encoder->outp) < 0 || drm_helper_probe_detect(&nv_conn->base, NULL, false) != connector_status_connected) return -ENODEV; @@ -346,15 +281,8 @@ nv50_backlight_init(struct nouveau_backlight *bl, } } - if (drm->client.device.info.chipset <= 0xa0 || - drm->client.device.info.chipset == 0xaa || - drm->client.device.info.chipset == 0xac) - *ops = &nv50_bl_ops; - else - *ops = &nva3_bl_ops; - + *ops = &nv50_bl_ops; props->max_brightness = 100; - return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 189903b65edc..9e878cdc8e38 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -2093,9 +2093,11 @@ nouveau_bios_init(struct drm_device *dev) if (!NVInitVBIOS(dev)) return -ENODEV; - ret = parse_dcb_table(dev, bios); - if (ret) - return ret; + if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) { + ret = parse_dcb_table(dev, bios); + if (ret) + return ret; + } if (!bios->major_version) /* we don't run version 0 bios */ return 0; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 79ea30aac31f..856b3ef5edb8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -400,10 +400,8 @@ nouveau_connector_destroy(struct drm_connector *connector) kfree(nv_connector->edid); drm_connector_unregister(connector); drm_connector_cleanup(connector); - if (nv_connector->aux.transfer) { + if (nv_connector->aux.transfer) drm_dp_cec_unregister_connector(&nv_connector->aux); - kfree(nv_connector->aux.name); - } nvif_conn_dtor(&nv_connector->conn); kfree(connector); } @@ -413,6 +411,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct pci_dev *pdev = to_pci_dev(dev->dev); + struct nouveau_connector *conn = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL, *found = NULL; struct drm_encoder *encoder; int ret; @@ -421,33 +420,48 @@ nouveau_connector_ddc_detect(struct drm_connector *connector) drm_connector_for_each_possible_encoder(connector, encoder) { nv_encoder = nouveau_encoder(encoder); - switch (nv_encoder->dcb->type) { - case DCB_OUTPUT_DP: - ret = nouveau_dp_detect(nouveau_connector(connector), - nv_encoder); - if (ret == NOUVEAU_DP_MST) - return NULL; - else if (ret == NOUVEAU_DP_SST) - found = nv_encoder; + if (nvif_object_constructed(&nv_encoder->outp.object)) { + enum nvif_outp_detect_status status; + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { + ret = nouveau_dp_detect(conn, nv_encoder); + if (ret == NOUVEAU_DP_MST) + return NULL; + if (ret != NOUVEAU_DP_SST) + continue; + + return nv_encoder; + } else { + status = nvif_outp_detect(&nv_encoder->outp); + switch (status) { + case PRESENT: + return nv_encoder; + case NOT_PRESENT: + continue; + case UNKNOWN: + break; + default: + WARN_ON(1); + break; + } + } + } - break; - case DCB_OUTPUT_LVDS: + if (!nv_encoder->i2c) + continue; + + if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS) { switcheroo_ddc = !!(vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC); - fallthrough; - default: - if (!nv_encoder->i2c) - break; + } - if (switcheroo_ddc) - vga_switcheroo_lock_ddc(pdev); - if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) - found = nv_encoder; - if (switcheroo_ddc) - vga_switcheroo_unlock_ddc(pdev); + if (switcheroo_ddc) + vga_switcheroo_lock_ddc(pdev); + if (nvkm_probe_i2c(nv_encoder->i2c, 0x50)) + found = nv_encoder; + if (switcheroo_ddc) + vga_switcheroo_unlock_ddc(pdev); - break; - } if (found) break; } @@ -554,7 +568,6 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) struct nouveau_connector *nv_connector = nouveau_connector(connector); struct nouveau_encoder *nv_encoder = NULL; struct nouveau_encoder *nv_partner; - struct i2c_adapter *i2c; int type; int ret; enum drm_connector_status conn_status = connector_status_disconnected; @@ -577,15 +590,20 @@ nouveau_connector_detect(struct drm_connector *connector, bool force) } nv_encoder = nouveau_connector_ddc_detect(connector); - if (nv_encoder && (i2c = nv_encoder->i2c) != NULL) { - struct edid *new_edid; + if (nv_encoder) { + struct edid *new_edid = NULL; - if ((vga_switcheroo_handler_flags() & - VGA_SWITCHEROO_CAN_SWITCH_DDC) && - nv_connector->type == DCB_CONNECTOR_LVDS) - new_edid = drm_get_edid_switcheroo(connector, i2c); - else - new_edid = drm_get_edid(connector, i2c); + if (nv_encoder->i2c) { + if ((vga_switcheroo_handler_flags() & VGA_SWITCHEROO_CAN_SWITCH_DDC) && + nv_connector->type == DCB_CONNECTOR_LVDS) + new_edid = drm_get_edid_switcheroo(connector, nv_encoder->i2c); + else + new_edid = drm_get_edid(connector, nv_encoder->i2c); + } else { + ret = nvif_outp_edid_get(&nv_encoder->outp, (u8 **)&new_edid); + if (ret < 0) + return connector_status_disconnected; + } nouveau_connector_set_edid(nv_connector, new_edid); if (!nv_connector->edid) { @@ -1117,7 +1135,7 @@ nouveau_connector_atomic_check(struct drm_connector *connector, struct drm_atomi struct drm_connector_state *conn_state = drm_atomic_get_new_connector_state(state, connector); - if (!nv_conn->dp_encoder || !nv50_has_mst(nouveau_drm(connector->dev))) + if (!nv_conn->dp_encoder || !nv_conn->dp_encoder->dp.mstm) return 0; return drm_dp_mst_root_conn_atomic_check(conn_state, &nv_conn->dp_encoder->dp.mstm->mgr); @@ -1206,23 +1224,17 @@ nouveau_connector_aux_xfer(struct drm_dp_aux *obj, struct drm_dp_aux_msg *msg) struct nouveau_connector *nv_connector = container_of(obj, typeof(*nv_connector), aux); struct nouveau_encoder *nv_encoder; - struct nvkm_i2c_aux *aux; u8 size = msg->size; int ret; nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); - if (!nv_encoder || !(aux = nv_encoder->aux)) + if (!nv_encoder) return -ENODEV; if (WARN_ON(msg->size > 16)) return -E2BIG; - ret = nvkm_i2c_aux_acquire(aux); - if (ret) - return ret; - - ret = nvkm_i2c_aux_xfer(aux, false, msg->request, msg->address, - msg->buffer, &size); - nvkm_i2c_aux_release(aux); + ret = nvif_outp_dp_aux_xfer(&nv_encoder->outp, + msg->request, &size, msg->address, msg->buffer); if (ret >= 0) { msg->reply = ret; return size; @@ -1263,17 +1275,13 @@ drm_conntype_from_dcb(enum dcb_connector_type dcb) } struct drm_connector * -nouveau_connector_create(struct drm_device *dev, - const struct dcb_output *dcbe) +nouveau_connector_create(struct drm_device *dev, int index) { - const struct drm_connector_funcs *funcs = &nouveau_connector_funcs; struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_display *disp = nouveau_display(dev); struct nouveau_connector *nv_connector = NULL; struct drm_connector *connector; struct drm_connector_list_iter conn_iter; - char aux_name[48] = {0}; - int index = dcbe->connector; int type, ret = 0; bool dummy; @@ -1295,74 +1303,86 @@ nouveau_connector_create(struct drm_device *dev, nv_connector->index = index; INIT_WORK(&nv_connector->irq_work, nouveau_dp_irq); - /* attempt to parse vbios connector type and hotplug gpio */ - nv_connector->dcb = olddcb_conn(dev, index); - if (nv_connector->dcb) { - u32 entry = ROM16(nv_connector->dcb[0]); - if (olddcb_conntab(dev)[3] >= 4) - entry |= (u32)ROM16(nv_connector->dcb[2]) << 16; - - nv_connector->type = nv_connector->dcb[0]; - if (drm_conntype_from_dcb(nv_connector->type) == - DRM_MODE_CONNECTOR_Unknown) { - NV_WARN(drm, "unknown connector type %02x\n", - nv_connector->type); - nv_connector->type = DCB_CONNECTOR_NONE; + if (disp->disp.conn_mask & BIT(nv_connector->index)) { + ret = nvif_conn_ctor(&disp->disp, nv_connector->base.name, nv_connector->index, + &nv_connector->conn); + if (ret) { + kfree(nv_connector); + return ERR_PTR(ret); } - /* Gigabyte NX85T */ - if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) { - if (nv_connector->type == DCB_CONNECTOR_HDMI_1) - nv_connector->type = DCB_CONNECTOR_DVI_I; + switch (nv_connector->conn.info.type) { + case NVIF_CONN_VGA : type = DCB_CONNECTOR_VGA; break; + case NVIF_CONN_DVI_I : type = DCB_CONNECTOR_DVI_I; break; + case NVIF_CONN_DVI_D : type = DCB_CONNECTOR_DVI_D; break; + case NVIF_CONN_LVDS : type = DCB_CONNECTOR_LVDS; break; + case NVIF_CONN_LVDS_SPWG: type = DCB_CONNECTOR_LVDS_SPWG; break; + case NVIF_CONN_DP : type = DCB_CONNECTOR_DP; break; + case NVIF_CONN_EDP : type = DCB_CONNECTOR_eDP; break; + case NVIF_CONN_HDMI : type = DCB_CONNECTOR_HDMI_0; break; + default: + WARN_ON(1); + return NULL; } - /* Gigabyte GV-NX86T512H */ - if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) { - if (nv_connector->type == DCB_CONNECTOR_HDMI_1) - nv_connector->type = DCB_CONNECTOR_DVI_I; - } + nv_connector->type = type; } else { - nv_connector->type = DCB_CONNECTOR_NONE; - } + u8 *dcb = olddcb_conn(dev, nv_connector->index); - /* no vbios data, or an unknown dcb connector type - attempt to - * figure out something suitable ourselves - */ - if (nv_connector->type == DCB_CONNECTOR_NONE) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_table *dcbt = &drm->vbios.dcb; - u32 encoders = 0; - int i; - - for (i = 0; i < dcbt->entries; i++) { - if (dcbt->entry[i].connector == nv_connector->index) - encoders |= (1 << dcbt->entry[i].type); + if (dcb) + nv_connector->type = dcb[0]; + else + nv_connector->type = DCB_CONNECTOR_NONE; + + /* attempt to parse vbios connector type and hotplug gpio */ + if (nv_connector->type != DCB_CONNECTOR_NONE) { + if (drm_conntype_from_dcb(nv_connector->type) == + DRM_MODE_CONNECTOR_Unknown) { + NV_WARN(drm, "unknown connector type %02x\n", + nv_connector->type); + nv_connector->type = DCB_CONNECTOR_NONE; + } } - if (encoders & (1 << DCB_OUTPUT_DP)) { - if (encoders & (1 << DCB_OUTPUT_TMDS)) - nv_connector->type = DCB_CONNECTOR_DP; - else - nv_connector->type = DCB_CONNECTOR_eDP; - } else - if (encoders & (1 << DCB_OUTPUT_TMDS)) { - if (encoders & (1 << DCB_OUTPUT_ANALOG)) - nv_connector->type = DCB_CONNECTOR_DVI_I; - else - nv_connector->type = DCB_CONNECTOR_DVI_D; - } else - if (encoders & (1 << DCB_OUTPUT_ANALOG)) { - nv_connector->type = DCB_CONNECTOR_VGA; - } else - if (encoders & (1 << DCB_OUTPUT_LVDS)) { - nv_connector->type = DCB_CONNECTOR_LVDS; - } else - if (encoders & (1 << DCB_OUTPUT_TV)) { - nv_connector->type = DCB_CONNECTOR_TV_0; + /* no vbios data, or an unknown dcb connector type - attempt to + * figure out something suitable ourselves + */ + if (nv_connector->type == DCB_CONNECTOR_NONE && + !WARN_ON(drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA)) { + struct dcb_table *dcbt = &drm->vbios.dcb; + u32 encoders = 0; + int i; + + for (i = 0; i < dcbt->entries; i++) { + if (dcbt->entry[i].connector == nv_connector->index) + encoders |= (1 << dcbt->entry[i].type); + } + + if (encoders & (1 << DCB_OUTPUT_TMDS)) { + if (encoders & (1 << DCB_OUTPUT_ANALOG)) + nv_connector->type = DCB_CONNECTOR_DVI_I; + else + nv_connector->type = DCB_CONNECTOR_DVI_D; + } else + if (encoders & (1 << DCB_OUTPUT_ANALOG)) { + nv_connector->type = DCB_CONNECTOR_VGA; + } else + if (encoders & (1 << DCB_OUTPUT_LVDS)) { + nv_connector->type = DCB_CONNECTOR_LVDS; + } else + if (encoders & (1 << DCB_OUTPUT_TV)) { + nv_connector->type = DCB_CONNECTOR_TV_0; + } } } - switch ((type = drm_conntype_from_dcb(nv_connector->type))) { + type = drm_conntype_from_dcb(nv_connector->type); + if (type == DRM_MODE_CONNECTOR_LVDS) + drm_connector_init(dev, connector, &nouveau_connector_funcs_lvds, type); + else + drm_connector_init(dev, connector, &nouveau_connector_funcs, type); + + switch (type) { case DRM_MODE_CONNECTOR_LVDS: ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy); if (ret) { @@ -1371,24 +1391,16 @@ nouveau_connector_create(struct drm_device *dev, return ERR_PTR(ret); } - funcs = &nouveau_connector_funcs_lvds; break; case DRM_MODE_CONNECTOR_DisplayPort: case DRM_MODE_CONNECTOR_eDP: nv_connector->aux.dev = connector->kdev; nv_connector->aux.drm_dev = dev; nv_connector->aux.transfer = nouveau_connector_aux_xfer; - snprintf(aux_name, sizeof(aux_name), "sor-%04x-%04x", - dcbe->hasht, dcbe->hashm); - nv_connector->aux.name = kstrdup(aux_name, GFP_KERNEL); - if (!nv_connector->aux.name) { - kfree(nv_connector); - return ERR_PTR(-ENOMEM); - } + nv_connector->aux.name = connector->name; drm_dp_aux_init(&nv_connector->aux); break; default: - funcs = &nouveau_connector_funcs; break; } @@ -1403,17 +1415,10 @@ nouveau_connector_create(struct drm_device *dev, connector->interlace_allowed = false; connector->doublescan_allowed = false; - drm_connector_init(dev, connector, funcs, type); drm_connector_helper_add(connector, &nouveau_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_CONNECT; - if (nv_connector->dcb && (disp->disp.conn_mask & BIT(nv_connector->index))) { - ret = nvif_conn_ctor(&disp->disp, nv_connector->base.name, nv_connector->index, - &nv_connector->conn); - if (ret) { - goto drm_conn_err; - } - + if (nvif_object_constructed(&nv_connector->conn.object)) { ret = nvif_conn_event_ctor(&nv_connector->conn, "kmsHotplug", nouveau_connector_hotplug, NVIF_CONN_EVENT_V0_PLUG | NVIF_CONN_EVENT_V0_UNPLUG, diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index 35bcb541722b..a2df4918340c 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -121,7 +121,6 @@ struct nouveau_connector { struct drm_connector base; enum dcb_connector_type type; u8 index; - u8 *dcb; struct nvif_conn conn; u64 hpd_pending; @@ -200,7 +199,7 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) } struct drm_connector * -nouveau_connector_create(struct drm_device *, const struct dcb_output *); +nouveau_connector_create(struct drm_device *, int id); void nouveau_connector_hpd(struct nouveau_connector *, u64 bits); extern int nouveau_tv_disable; diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c index 053f703f2f68..e83db051e851 100644 --- a/drivers/gpu/drm/nouveau/nouveau_debugfs.c +++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c @@ -231,7 +231,7 @@ nouveau_debugfs_gpuva(struct seq_file *m, void *data) continue; nouveau_uvmm_lock(uvmm); - drm_debugfs_gpuva_info(m, &uvmm->umgr); + drm_debugfs_gpuva_info(m, &uvmm->base); seq_puts(m, "\n"); nouveau_debugfs_gpuva_regions(m, uvmm); nouveau_uvmm_unlock(uvmm); diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 99977e5fe716..d8c92521226d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -724,10 +724,10 @@ nouveau_display_create(struct drm_device *dev) drm_kms_helper_poll_init(dev); drm_kms_helper_poll_disable(dev); - if (nouveau_modeset != 2 && drm->vbios.dcb.entries) { - ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0, - &disp->disp); - if (ret == 0) { + if (nouveau_modeset != 2) { + ret = nvif_disp_ctor(&drm->client.device, "kmsDisp", 0, &disp->disp); + + if (!ret && (disp->disp.outp_mask || drm->vbios.dcb.entries)) { nouveau_display_create_properties(dev); if (disp->disp.object.oclass < NV50_DISP) { dev->mode_config.fb_modifiers_not_supported = true; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 6a4980b2d4d4..7de7707ec6a8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -42,6 +42,21 @@ nouveau_dp_has_sink_count(struct drm_connector *connector, return drm_dp_read_sink_count_cap(connector, outp->dp.dpcd, &outp->dp.desc); } +static bool +nouveau_dp_probe_lttpr(struct nouveau_encoder *outp) +{ + u8 rev, size = sizeof(rev); + int ret; + + ret = nvif_outp_dp_aux_xfer(&outp->outp, DP_AUX_NATIVE_READ, &size, + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV, + &rev); + if (ret || size < sizeof(rev) || rev < 0x14) + return false; + + return true; +} + static enum drm_connector_status nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, struct nouveau_encoder *outp) @@ -53,10 +68,112 @@ nouveau_dp_probe_dpcd(struct nouveau_connector *nv_connector, int ret; u8 *dpcd = outp->dp.dpcd; + outp->dp.lttpr.nr = 0; + outp->dp.rate_nr = 0; + outp->dp.link_nr = 0; + outp->dp.link_bw = 0; + + if (connector->connector_type != DRM_MODE_CONNECTOR_eDP && + nouveau_dp_probe_lttpr(outp) && + !drm_dp_read_dpcd_caps(aux, dpcd) && + !drm_dp_read_lttpr_common_caps(aux, dpcd, outp->dp.lttpr.caps)) { + int nr = drm_dp_lttpr_count(outp->dp.lttpr.caps); + + if (nr) { + drm_dp_dpcd_writeb(aux, DP_PHY_REPEATER_MODE, + DP_PHY_REPEATER_MODE_TRANSPARENT); + + if (nr > 0) { + ret = drm_dp_dpcd_writeb(aux, DP_PHY_REPEATER_MODE, + DP_PHY_REPEATER_MODE_NON_TRANSPARENT); + if (ret != 1) { + drm_dp_dpcd_writeb(aux, DP_PHY_REPEATER_MODE, + DP_PHY_REPEATER_MODE_TRANSPARENT); + } else { + outp->dp.lttpr.nr = nr; + } + } + } + } + ret = drm_dp_read_dpcd_caps(aux, dpcd); if (ret < 0) goto out; + outp->dp.link_nr = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; + if (outp->dcb->dpconf.link_nr < outp->dp.link_nr) + outp->dp.link_nr = outp->dcb->dpconf.link_nr; + + if (outp->dp.lttpr.nr) { + int links = drm_dp_lttpr_max_lane_count(outp->dp.lttpr.caps); + + if (links && links < outp->dp.link_nr) + outp->dp.link_nr = links; + } + + if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) { + __le16 rates[DP_MAX_SUPPORTED_RATES]; + + ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, rates, sizeof(rates)); + if (ret == sizeof(rates)) { + for (int i = 0; i < ARRAY_SIZE(rates); i++) { + u32 rate = (le16_to_cpu(rates[i]) * 200) / 10; + int j; + + if (!rate) + break; + + for (j = 0; j < outp->dp.rate_nr; j++) { + if (rate > outp->dp.rate[j].rate) { + for (int k = outp->dp.rate_nr; k > j; k--) + outp->dp.rate[k] = outp->dp.rate[k - 1]; + break; + } + } + + outp->dp.rate[j].dpcd = i; + outp->dp.rate[j].rate = rate; + outp->dp.rate_nr++; + } + } + } + + if (!outp->dp.rate_nr) { + const u32 rates[] = { 810000, 540000, 270000, 162000 }; + u32 max_rate = dpcd[DP_MAX_LINK_RATE] * 27000; + + if (outp->dp.lttpr.nr) { + int rate = drm_dp_lttpr_max_link_rate(outp->dp.lttpr.caps); + + if (rate && rate < max_rate) + max_rate = rate; + } + + max_rate = min_t(int, max_rate, outp->dcb->dpconf.link_bw); + + for (int i = 0; i < ARRAY_SIZE(rates); i++) { + if (rates[i] <= max_rate) { + outp->dp.rate[outp->dp.rate_nr].dpcd = -1; + outp->dp.rate[outp->dp.rate_nr].rate = rates[i]; + outp->dp.rate_nr++; + } + } + + if (WARN_ON(!outp->dp.rate_nr)) + goto out; + } + + ret = nvif_outp_dp_rates(&outp->outp, outp->dp.rate, outp->dp.rate_nr); + if (ret) + goto out; + + for (int i = 0; i < outp->dp.rate_nr; i++) { + u32 link_bw = outp->dp.rate[i].rate; + + if (link_bw > outp->dp.link_bw) + outp->dp.link_bw = link_bw; + } + ret = drm_dp_read_desc(aux, &outp->dp.desc, drm_dp_is_branch(dpcd)); if (ret < 0) goto out; @@ -132,14 +249,8 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, } } - /* Check status of HPD pin before attempting an AUX transaction that - * would result in a number of (futile) retries on a connector which - * has no display plugged. - * - * TODO: look into checking this before probing I2C to detect DVI/HDMI - */ - hpd = nvif_conn_hpd_status(&nv_connector->conn); - if (hpd == NVIF_CONN_HPD_STATUS_NOT_PRESENT) { + hpd = nvif_outp_detect(&nv_encoder->outp); + if (hpd == NOT_PRESENT) { nvif_outp_dp_aux_pwr(&nv_encoder->outp, false); goto out; } @@ -157,39 +268,14 @@ nouveau_dp_detect(struct nouveau_connector *nv_connector, goto out; } - nv_encoder->dp.link_bw = 27000 * dpcd[DP_MAX_LINK_RATE]; - nv_encoder->dp.link_nr = - dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK; - - if (connector->connector_type == DRM_MODE_CONNECTOR_eDP && dpcd[DP_DPCD_REV] >= 0x13) { - struct drm_dp_aux *aux = &nv_connector->aux; - int ret, i; - u8 sink_rates[16]; - - ret = drm_dp_dpcd_read(aux, DP_SUPPORTED_LINK_RATES, sink_rates, sizeof(sink_rates)); - if (ret == sizeof(sink_rates)) { - for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { - int val = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; - if (val && (i == 0 || val > nv_encoder->dp.link_bw)) - nv_encoder->dp.link_bw = val; - } - } - } - - NV_DEBUG(drm, "display: %dx%d dpcd 0x%02x\n", - nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, - dpcd[DP_DPCD_REV]); - NV_DEBUG(drm, "encoder: %dx%d\n", - nv_encoder->dcb->dpconf.link_nr, - nv_encoder->dcb->dpconf.link_bw); - - if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) - nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; - if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) - nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; + NV_DEBUG(drm, "sink dpcd version: 0x%02x\n", dpcd[DP_DPCD_REV]); + for (int i = 0; i < nv_encoder->dp.rate_nr; i++) + NV_DEBUG(drm, "sink rate %d: %d\n", i, nv_encoder->dp.rate[i].rate); - NV_DEBUG(drm, "maximum: %dx%d\n", - nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); + NV_DEBUG(drm, "encoder: %dx%d\n", nv_encoder->dcb->dpconf.link_nr, + nv_encoder->dcb->dpconf.link_bw); + NV_DEBUG(drm, "maximum: %dx%d\n", nv_encoder->dp.link_nr, + nv_encoder->dp.link_bw); if (mstm && mstm->can_mst) { ret = nv50_mstm_detect(nv_encoder); @@ -211,15 +297,186 @@ out: return ret; } +void +nouveau_dp_power_down(struct nouveau_encoder *outp) +{ + struct drm_dp_aux *aux = &outp->conn->aux; + int ret; + u8 pwr; + + mutex_lock(&outp->dp.hpd_irq_lock); + + ret = drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr); + if (ret == 1) { + pwr &= ~DP_SET_POWER_MASK; + pwr |= DP_SET_POWER_D3; + drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); + } + + outp->dp.lt.nr = 0; + mutex_unlock(&outp->dp.hpd_irq_lock); +} + +static bool +nouveau_dp_train_link(struct nouveau_encoder *outp, bool retrain) +{ + struct drm_dp_aux *aux = &outp->conn->aux; + bool post_lt = false; + int ret, retries = 0; + + if ( (outp->dp.dpcd[DP_MAX_LANE_COUNT] & 0x20) && + !(outp->dp.dpcd[DP_MAX_DOWNSPREAD] & DP_TPS4_SUPPORTED)) + post_lt = true; + +retry: + ret = nvif_outp_dp_train(&outp->outp, outp->dp.dpcd, + outp->dp.lttpr.nr, + outp->dp.lt.nr, + outp->dp.lt.bw, + outp->dp.lt.mst, + post_lt, + retrain); + if (ret) + return false; + + if (post_lt) { + u8 stat[DP_LINK_STATUS_SIZE]; + u8 prev[2]; + u8 time = 0, adjusts = 0, tmp; + + ret = drm_dp_dpcd_read_phy_link_status(aux, DP_PHY_DPRX, stat); + if (ret) + return false; + + for (;;) { + if (!drm_dp_channel_eq_ok(stat, outp->dp.lt.nr)) { + ret = 1; + break; + } + + if (!(stat[2] & 0x02)) + break; + + msleep(5); + time += 5; + + memcpy(prev, &stat[4], sizeof(prev)); + ret = drm_dp_dpcd_read_phy_link_status(aux, DP_PHY_DPRX, stat); + if (ret) + break; + + if (!memcmp(prev, &stat[4], sizeof(prev))) { + if (time > 200) + break; + } else { + u8 pe[4], vs[4]; + + if (adjusts++ == 6) + break; + + for (int i = 0; i < outp->dp.lt.nr; i++) { + pe[i] = drm_dp_get_adjust_request_pre_emphasis(stat, i) >> + DP_TRAIN_PRE_EMPHASIS_SHIFT; + vs[i] = drm_dp_get_adjust_request_voltage(stat, i) >> + DP_TRAIN_VOLTAGE_SWING_SHIFT; + } + + ret = nvif_outp_dp_drive(&outp->outp, outp->dp.lt.nr, pe, vs); + if (ret) + break; + + time = 0; + } + } + + if (drm_dp_dpcd_readb(aux, DP_LANE_COUNT_SET, &tmp) == 1) { + tmp &= ~0x20; + drm_dp_dpcd_writeb(aux, DP_LANE_COUNT_SET, tmp); + } + } + + if (ret == 1 && retries++ < 3) + goto retry; + + return ret == 0; +} + bool -nouveau_dp_link_check(struct nouveau_connector *nv_connector) +nouveau_dp_train(struct nouveau_encoder *outp, bool mst, u32 khz, u8 bpc) +{ + struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev); + struct drm_dp_aux *aux = &outp->conn->aux; + u32 min_rate; + u8 pwr; + bool ret = true; + + if (mst) + min_rate = outp->dp.link_nr * outp->dp.rate[0].rate; + else + min_rate = DIV_ROUND_UP(khz * bpc * 3, 8); + + NV_DEBUG(drm, "%s link training (mst:%d min_rate:%d)\n", + outp->base.base.name, mst, min_rate); + + mutex_lock(&outp->dp.hpd_irq_lock); + + if (drm_dp_dpcd_readb(aux, DP_SET_POWER, &pwr) == 1) { + if ((pwr & DP_SET_POWER_MASK) != DP_SET_POWER_D0) { + pwr &= ~DP_SET_POWER_MASK; + pwr |= DP_SET_POWER_D0; + drm_dp_dpcd_writeb(aux, DP_SET_POWER, pwr); + } + } + + for (int nr = outp->dp.link_nr; nr; nr >>= 1) { + for (int rate = 0; rate < outp->dp.rate_nr; rate++) { + if (outp->dp.rate[rate].rate * nr >= min_rate) { + outp->dp.lt.nr = nr; + outp->dp.lt.bw = outp->dp.rate[rate].rate; + outp->dp.lt.mst = mst; + if (nouveau_dp_train_link(outp, false)) + goto done; + } + } + } + + ret = false; +done: + mutex_unlock(&outp->dp.hpd_irq_lock); + return ret; +} + +static bool +nouveau_dp_link_check_locked(struct nouveau_encoder *outp) { - struct nouveau_encoder *nv_encoder = find_encoder(&nv_connector->base, DCB_OUTPUT_DP); + u8 link_status[DP_LINK_STATUS_SIZE]; - if (!nv_encoder || nv_encoder->outp.or.id < 0) + if (!outp || !outp->dp.lt.nr) return true; - return nvif_outp_dp_retrain(&nv_encoder->outp) == 0; + if (drm_dp_dpcd_read_phy_link_status(&outp->conn->aux, DP_PHY_DPRX, link_status) < 0) + return false; + + if (drm_dp_channel_eq_ok(link_status, outp->dp.lt.nr)) + return true; + + return nouveau_dp_train_link(outp, true); +} + +bool +nouveau_dp_link_check(struct nouveau_connector *nv_connector) +{ + struct nouveau_encoder *outp = nv_connector->dp_encoder; + bool link_ok = true; + + if (outp) { + mutex_lock(&outp->dp.hpd_irq_lock); + if (outp->dp.lt.nr) + link_ok = nouveau_dp_link_check_locked(outp); + mutex_unlock(&outp->dp.hpd_irq_lock); + } + + return link_ok; } void diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 4396f501b16a..50589f982d1a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1133,7 +1133,10 @@ nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) } get_task_comm(tmpname, current); - snprintf(name, sizeof(name), "%s[%d]", tmpname, pid_nr(fpriv->pid)); + rcu_read_lock(); + snprintf(name, sizeof(name), "%s[%d]", + tmpname, pid_nr(rcu_dereference(fpriv->pid))); + rcu_read_unlock(); if (!(cli = kzalloc(sizeof(*cli), GFP_KERNEL))) { ret = -ENOMEM; diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 1fe17ff95f5e..3666a7403e47 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -189,21 +189,12 @@ u_free(void *addr) static inline void * u_memcpya(uint64_t user, unsigned int nmemb, unsigned int size) { - void *mem; - void __user *userptr = (void __force __user *)(uintptr_t)user; + void __user *userptr = u64_to_user_ptr(user); + size_t bytes; - size *= nmemb; - - mem = kvmalloc(size, GFP_KERNEL); - if (!mem) - return ERR_PTR(-ENOMEM); - - if (copy_from_user(mem, userptr, size)) { - u_free(mem); - return ERR_PTR(-EFAULT); - } - - return mem; + if (unlikely(check_mul_overflow(nmemb, size, &bytes))) + return NULL; + return vmemdup_user(userptr, bytes); } #include <nvif/object.h> diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index bcba1a14cfab..333042fc493f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -49,8 +49,9 @@ struct nouveau_encoder { struct nvif_outp outp; int or; + struct nouveau_connector *conn; + struct i2c_adapter *i2c; - struct nvkm_i2c_aux *aux; /* different to drm_encoder.crtc, this reflects what's * actually programmed on the hw, not the proposed crtc */ @@ -60,7 +61,6 @@ struct nouveau_encoder { /* Protected by nouveau_drm.audio.lock */ struct { bool enabled; - struct drm_connector *connector; } audio; struct drm_display_mode mode; @@ -68,18 +68,38 @@ struct nouveau_encoder { struct nv04_output_reg restore; - union { + struct { + struct { + bool enabled; + } hdmi; + struct { struct nv50_mstm *mstm; + + struct { + u8 caps[DP_LTTPR_COMMON_CAP_SIZE]; + u8 nr; + } lttpr; + + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + + struct nvif_outp_dp_rate rate[8]; + int rate_nr; + int link_nr; int link_bw; + struct { + bool mst; + u8 nr; + u32 bw; + } lt; + /* Protects DP state that needs to be accessed outside * connector reprobing contexts */ struct mutex hpd_irq_lock; - u8 dpcd[DP_RECEIVER_CAP_SIZE]; u8 downstream_ports[DP_MAX_DOWNSTREAM_PORTS]; struct drm_dp_desc desc; @@ -141,6 +161,8 @@ enum nouveau_dp_status { }; int nouveau_dp_detect(struct nouveau_connector *, struct nouveau_encoder *); +bool nouveau_dp_train(struct nouveau_encoder *, bool mst, u32 khz, u8 bpc); +void nouveau_dp_power_down(struct nouveau_encoder *); bool nouveau_dp_link_check(struct nouveau_connector *); void nouveau_dp_irq(struct work_struct *); enum drm_mode_status nv50_dp_mode_valid(struct nouveau_encoder *, diff --git a/drivers/gpu/drm/nouveau/nouveau_exec.c b/drivers/gpu/drm/nouveau/nouveau_exec.c index 19024ce21fbb..bf6c12f4342a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_exec.c +++ b/drivers/gpu/drm/nouveau/nouveau_exec.c @@ -107,8 +107,8 @@ nouveau_exec_job_submit(struct nouveau_job *job) drm_exec_until_all_locked(exec) { struct drm_gpuva *va; - drm_gpuva_for_each_va(va, &uvmm->umgr) { - if (unlikely(va == &uvmm->umgr.kernel_alloc_node)) + drm_gpuvm_for_each_va(va, &uvmm->base) { + if (unlikely(va == &uvmm->base.kernel_alloc_node)) continue; ret = drm_exec_prepare_obj(exec, va->gem.obj, 1); diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.c b/drivers/gpu/drm/nouveau/nouveau_uvmm.c index aae780e4a4aa..5cf892c50f43 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.c +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.c @@ -329,7 +329,7 @@ nouveau_uvma_region_create(struct nouveau_uvmm *uvmm, struct nouveau_uvma_region *reg; int ret; - if (!drm_gpuva_interval_empty(&uvmm->umgr, addr, range)) + if (!drm_gpuvm_interval_empty(&uvmm->base, addr, range)) return -ENOSPC; ret = nouveau_uvma_region_alloc(®); @@ -384,7 +384,7 @@ nouveau_uvma_region_empty(struct nouveau_uvma_region *reg) { struct nouveau_uvmm *uvmm = reg->uvmm; - return drm_gpuva_interval_empty(&uvmm->umgr, + return drm_gpuvm_interval_empty(&uvmm->base, reg->va.addr, reg->va.range); } @@ -444,7 +444,7 @@ op_map_prepare_unwind(struct nouveau_uvma *uvma) static void op_unmap_prepare_unwind(struct drm_gpuva *va) { - drm_gpuva_insert(va->mgr, va); + drm_gpuva_insert(va->vm, va); } static void @@ -589,7 +589,7 @@ op_map_prepare(struct nouveau_uvmm *uvmm, uvma->region = args->region; uvma->kind = args->kind; - drm_gpuva_map(&uvmm->umgr, &uvma->va, op); + drm_gpuva_map(&uvmm->base, &uvma->va, op); /* Keep a reference until this uvma is destroyed. */ nouveau_uvma_gem_get(uvma); @@ -1194,7 +1194,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) goto unwind_continue; } - op->ops = drm_gpuva_sm_unmap_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_unmap_ops_create(&uvmm->base, op->va.addr, op->va.range); if (IS_ERR(op->ops)) { @@ -1205,7 +1205,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) ret = nouveau_uvmm_sm_unmap_prepare(uvmm, &op->new, op->ops); if (ret) { - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; op->reg = NULL; goto unwind_continue; @@ -1240,7 +1240,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) } } - op->ops = drm_gpuva_sm_map_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_map_ops_create(&uvmm->base, op->va.addr, op->va.range, op->gem.obj, @@ -1256,7 +1256,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) op->va.range, op->flags & 0xff); if (ret) { - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; goto unwind_continue; } @@ -1264,7 +1264,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) break; } case OP_UNMAP: - op->ops = drm_gpuva_sm_unmap_ops_create(&uvmm->umgr, + op->ops = drm_gpuvm_sm_unmap_ops_create(&uvmm->base, op->va.addr, op->va.range); if (IS_ERR(op->ops)) { @@ -1275,7 +1275,7 @@ nouveau_uvmm_bind_job_submit(struct nouveau_job *job) ret = nouveau_uvmm_sm_unmap_prepare(uvmm, &op->new, op->ops); if (ret) { - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; goto unwind_continue; } @@ -1404,7 +1404,7 @@ unwind: break; } - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); op->ops = NULL; op->reg = NULL; } @@ -1509,7 +1509,7 @@ nouveau_uvmm_bind_job_free_work_fn(struct work_struct *work) } if (!IS_ERR_OR_NULL(op->ops)) - drm_gpuva_ops_free(&uvmm->umgr, op->ops); + drm_gpuva_ops_free(&uvmm->base, op->ops); if (obj) drm_gem_object_put(obj); @@ -1836,11 +1836,11 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, uvmm->kernel_managed_addr = kernel_managed_addr; uvmm->kernel_managed_size = kernel_managed_size; - drm_gpuva_manager_init(&uvmm->umgr, cli->name, - NOUVEAU_VA_SPACE_START, - NOUVEAU_VA_SPACE_END, - kernel_managed_addr, kernel_managed_size, - NULL); + drm_gpuvm_init(&uvmm->base, cli->name, + NOUVEAU_VA_SPACE_START, + NOUVEAU_VA_SPACE_END, + kernel_managed_addr, kernel_managed_size, + NULL); ret = nvif_vmm_ctor(&cli->mmu, "uvmm", cli->vmm.vmm.object.oclass, RAW, @@ -1855,7 +1855,7 @@ nouveau_uvmm_init(struct nouveau_uvmm *uvmm, struct nouveau_cli *cli, return 0; out_free_gpuva_mgr: - drm_gpuva_manager_destroy(&uvmm->umgr); + drm_gpuvm_destroy(&uvmm->base); out_unlock: mutex_unlock(&cli->mutex); return ret; @@ -1877,11 +1877,11 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) wait_event(entity->job.wq, list_empty(&entity->job.list.head)); nouveau_uvmm_lock(uvmm); - drm_gpuva_for_each_va_safe(va, next, &uvmm->umgr) { + drm_gpuvm_for_each_va_safe(va, next, &uvmm->base) { struct nouveau_uvma *uvma = uvma_from_va(va); struct drm_gem_object *obj = va->gem.obj; - if (unlikely(va == &uvmm->umgr.kernel_alloc_node)) + if (unlikely(va == &uvmm->base.kernel_alloc_node)) continue; drm_gpuva_remove(va); @@ -1910,7 +1910,7 @@ nouveau_uvmm_fini(struct nouveau_uvmm *uvmm) mutex_lock(&cli->mutex); nouveau_vmm_fini(&uvmm->vmm); - drm_gpuva_manager_destroy(&uvmm->umgr); + drm_gpuvm_destroy(&uvmm->base); mutex_unlock(&cli->mutex); dma_resv_fini(&uvmm->resv); diff --git a/drivers/gpu/drm/nouveau/nouveau_uvmm.h b/drivers/gpu/drm/nouveau/nouveau_uvmm.h index fc7f6fd2a4e1..a308c59760a5 100644 --- a/drivers/gpu/drm/nouveau/nouveau_uvmm.h +++ b/drivers/gpu/drm/nouveau/nouveau_uvmm.h @@ -3,13 +3,13 @@ #ifndef __NOUVEAU_UVMM_H__ #define __NOUVEAU_UVMM_H__ -#include <drm/drm_gpuva_mgr.h> +#include <drm/drm_gpuvm.h> #include "nouveau_drv.h" struct nouveau_uvmm { + struct drm_gpuvm base; struct nouveau_vmm vmm; - struct drm_gpuva_manager umgr; struct maple_tree region_mt; struct mutex mutex; struct dma_resv resv; @@ -41,10 +41,10 @@ struct nouveau_uvma { u8 kind; }; -#define uvmm_from_mgr(x) container_of((x), struct nouveau_uvmm, umgr) +#define uvmm_from_gpuvm(x) container_of((x), struct nouveau_uvmm, base) #define uvma_from_va(x) container_of((x), struct nouveau_uvma, va) -#define to_uvmm(x) uvmm_from_mgr((x)->va.mgr) +#define to_uvmm(x) uvmm_from_gpuvm((x)->va.vm) struct nouveau_uvmm_bind_job { struct nouveau_job base; diff --git a/drivers/gpu/drm/nouveau/nvif/client.c b/drivers/gpu/drm/nouveau/nvif/client.c index a3264a0e933a..3a27245f467f 100644 --- a/drivers/gpu/drm/nouveau/nvif/client.c +++ b/drivers/gpu/drm/nouveau/nvif/client.c @@ -69,7 +69,7 @@ nvif_client_ctor(struct nvif_client *parent, const char *name, u64 device, } nop = {}; int ret; - strncpy(args.name, name, sizeof(args.name)); + strscpy_pad(args.name, name, sizeof(args.name)); ret = nvif_object_ctor(parent != client ? &parent->object : NULL, name ? name : "nvifClient", 0, NVIF_CLASS_CLIENT, &args, sizeof(args), diff --git a/drivers/gpu/drm/nouveau/nvif/conn.c b/drivers/gpu/drm/nouveau/nvif/conn.c index a3cf91aeae2d..9ee18cb99264 100644 --- a/drivers/gpu/drm/nouveau/nvif/conn.c +++ b/drivers/gpu/drm/nouveau/nvif/conn.c @@ -45,20 +45,6 @@ nvif_conn_event_ctor(struct nvif_conn *conn, const char *name, nvif_event_func f return ret; } -int -nvif_conn_hpd_status(struct nvif_conn *conn) -{ - struct nvif_conn_hpd_status_v0 args; - int ret; - - args.version = 0; - - ret = nvif_mthd(&conn->object, NVIF_CONN_V0_HPD_STATUS, &args, sizeof(args)); - NVIF_ERRON(ret, &conn->object, "[HPD_STATUS] support:%d present:%d", - args.support, args.present); - return ret ? ret : !!args.support + !!args.present; -} - void nvif_conn_dtor(struct nvif_conn *conn) { @@ -77,5 +63,25 @@ nvif_conn_ctor(struct nvif_disp *disp, const char *name, int id, struct nvif_con ret = nvif_object_ctor(&disp->object, name ?: "nvifConn", id, NVIF_CLASS_CONN, &args, sizeof(args), &conn->object); NVIF_ERRON(ret, &disp->object, "[NEW conn id:%d]", id); - return ret; + if (ret) + return ret; + + conn->id = id; + + switch (args.type) { + case NVIF_CONN_V0_VGA : conn->info.type = NVIF_CONN_VGA; break; + case NVIF_CONN_V0_TV : conn->info.type = NVIF_CONN_TV; break; + case NVIF_CONN_V0_DVI_I : conn->info.type = NVIF_CONN_DVI_I; break; + case NVIF_CONN_V0_DVI_D : conn->info.type = NVIF_CONN_DVI_D; break; + case NVIF_CONN_V0_LVDS : conn->info.type = NVIF_CONN_LVDS; break; + case NVIF_CONN_V0_LVDS_SPWG: conn->info.type = NVIF_CONN_LVDS_SPWG; break; + case NVIF_CONN_V0_HDMI : conn->info.type = NVIF_CONN_HDMI; break; + case NVIF_CONN_V0_DP : conn->info.type = NVIF_CONN_DP; break; + case NVIF_CONN_V0_EDP : conn->info.type = NVIF_CONN_EDP; break; + default: + break; + } + + return 0; + } diff --git a/drivers/gpu/drm/nouveau/nvif/disp.c b/drivers/gpu/drm/nouveau/nvif/disp.c index 09915f2715af..097246e10cdb 100644 --- a/drivers/gpu/drm/nouveau/nvif/disp.c +++ b/drivers/gpu/drm/nouveau/nvif/disp.c @@ -60,7 +60,7 @@ nvif_disp_ctor(struct nvif_device *device, const char *name, s32 oclass, struct cid = nvif_sclass(&device->object, disps, oclass); disp->object.client = NULL; if (cid < 0) { - NVIF_ERRON(cid, &device->object, "[NEW disp%04x] not supported", oclass); + NVIF_DEBUG(&device->object, "[NEW disp%04x] not supported", oclass); return cid; } diff --git a/drivers/gpu/drm/nouveau/nvif/outp.c b/drivers/gpu/drm/nouveau/nvif/outp.c index c24bc5eae3ec..5d3190c05250 100644 --- a/drivers/gpu/drm/nouveau/nvif/outp.c +++ b/drivers/gpu/drm/nouveau/nvif/outp.c @@ -47,10 +47,134 @@ nvif_outp_dp_mst_vcpi(struct nvif_outp *outp, int head, } int -nvif_outp_dp_retrain(struct nvif_outp *outp) +nvif_outp_dp_mst_id_put(struct nvif_outp *outp, u32 id) { - int ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_RETRAIN, NULL, 0); - NVIF_ERRON(ret, &outp->object, "[DP_RETRAIN]"); + struct nvif_outp_dp_mst_id_get_v0 args; + int ret; + + args.version = 0; + args.id = id; + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_MST_ID_PUT, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_MST_ID_PUT id:%08x]", args.id); + return ret; +} + +int +nvif_outp_dp_mst_id_get(struct nvif_outp *outp, u32 *id) +{ + struct nvif_outp_dp_mst_id_get_v0 args; + int ret; + + args.version = 0; + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_MST_ID_GET, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_MST_ID_GET] id:%08x", args.id); + if (ret) + return ret; + + *id = args.id; + return 0; +} + +int +nvif_outp_dp_sst(struct nvif_outp *outp, int head, u32 watermark, u32 hblanksym, u32 vblanksym) +{ + struct nvif_outp_dp_sst_v0 args; + int ret; + + args.version = 0; + args.head = head; + args.watermark = watermark; + args.hblanksym = hblanksym; + args.vblanksym = vblanksym; + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_SST, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, + "[DP_SST head:%d watermark:%d hblanksym:%d vblanksym:%d]", + args.head, args.watermark, args.hblanksym, args.vblanksym); + return ret; +} + +int +nvif_outp_dp_drive(struct nvif_outp *outp, u8 link_nr, u8 pe[4], u8 vs[4]) +{ + struct nvif_outp_dp_drive_v0 args; + int ret; + + args.version = 0; + args.lanes = link_nr; + memcpy(args.pe, pe, sizeof(args.pe)); + memcpy(args.vs, vs, sizeof(args.vs)); + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_DRIVE, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_DRIVE lanes:%d]", args.lanes); + return ret; +} + +int +nvif_outp_dp_train(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], u8 lttprs, + u8 link_nr, u32 link_bw, bool mst, bool post_lt_adj, bool retrain) +{ + struct nvif_outp_dp_train_v0 args; + int ret; + + args.version = 0; + args.retrain = retrain; + args.mst = mst; + args.lttprs = lttprs; + args.post_lt_adj = post_lt_adj; + args.link_nr = link_nr; + args.link_bw = link_bw; + memcpy(args.dpcd, dpcd, sizeof(args.dpcd)); + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_TRAIN, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, + "[DP_TRAIN retrain:%d mst:%d lttprs:%d post_lt_adj:%d nr:%d bw:%d]", + args.retrain, args.mst, args.lttprs, args.post_lt_adj, args.link_nr, + args.link_bw); + return ret; +} + +int +nvif_outp_dp_rates(struct nvif_outp *outp, struct nvif_outp_dp_rate *rate, int rate_nr) +{ + struct nvif_outp_dp_rates_v0 args; + int ret; + + if (rate_nr > ARRAY_SIZE(args.rate)) + return -EINVAL; + + args.version = 0; + args.rates = rate_nr; + for (int i = 0; i < args.rates; i++, rate++) { + args.rate[i].dpcd = rate->dpcd; + args.rate[i].rate = rate->rate; + } + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_RATES, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DP_RATES rates:%d]", args.rates); + return ret; +} + +int +nvif_outp_dp_aux_xfer(struct nvif_outp *outp, u8 type, u8 *psize, u32 addr, u8 *data) +{ + struct nvif_outp_dp_aux_xfer_v0 args; + u8 size = *psize; + int ret; + + args.version = 0; + args.type = type; + args.size = size; + args.addr = addr; + memcpy(args.data, data, size); + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_DP_AUX_XFER, &args, sizeof(args)); + NVIF_DEBUG(&outp->object, "[DP_AUX_XFER type:%d size:%d addr:%05x] %d size:%d (ret: %d)", + args.type, size, args.addr, ret, args.size, ret); + if (ret < 0) + return ret; + + *psize = args.size; + + memcpy(data, args.data, size); return ret; } @@ -101,6 +225,74 @@ nvif_outp_infoframe(struct nvif_outp *outp, u8 type, struct nvif_outp_infoframe_ return ret; } +int +nvif_outp_hdmi(struct nvif_outp *outp, int head, bool enable, u8 max_ac_packet, u8 rekey, + u32 khz, bool scdc, bool scdc_scrambling, bool scdc_low_rates) +{ + struct nvif_outp_hdmi_v0 args; + int ret; + + args.version = 0; + args.head = head; + args.enable = enable; + args.max_ac_packet = max_ac_packet; + args.rekey = rekey; + args.khz = khz; + args.scdc = scdc; + args.scdc_scrambling = scdc_scrambling; + args.scdc_low_rates = scdc_low_rates; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_HDMI, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, + "[HDMI head:%d enable:%d max_ac_packet:%d rekey:%d khz:%d scdc:%d " + "scdc_scrambling:%d scdc_low_rates:%d]", + args.head, args.enable, args.max_ac_packet, args.rekey, args.khz, + args.scdc, args.scdc_scrambling, args.scdc_low_rates); + return ret; +} + +int +nvif_outp_lvds(struct nvif_outp *outp, bool dual, bool bpc8) +{ + struct nvif_outp_lvds_v0 args; + int ret; + + args.version = 0; + args.dual = dual; + args.bpc8 = bpc8; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_LVDS, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[LVDS dual:%d 8bpc:%d]", args.dual, args.bpc8); + return ret; +} + +int +nvif_outp_bl_set(struct nvif_outp *outp, int level) +{ + struct nvif_outp_bl_set_v0 args; + int ret; + + args.version = 0; + args.level = level; + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_BL_SET, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[BL_SET level:%d]", args.level); + return ret; +} + +int +nvif_outp_bl_get(struct nvif_outp *outp) +{ + struct nvif_outp_bl_get_v0 args; + int ret; + + args.version = 0; + + ret = nvif_object_mthd(&outp->object, NVIF_OUTP_V0_BL_GET, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[BL_GET level:%d]", args.level); + return ret ? ret : args.level; +} + void nvif_outp_release(struct nvif_outp *outp) { @@ -110,12 +302,12 @@ nvif_outp_release(struct nvif_outp *outp) } static inline int -nvif_outp_acquire(struct nvif_outp *outp, u8 proto, struct nvif_outp_acquire_v0 *args) +nvif_outp_acquire(struct nvif_outp *outp, u8 type, struct nvif_outp_acquire_v0 *args) { int ret; args->version = 0; - args->proto = proto; + args->type = type; ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_ACQUIRE, args, sizeof(*args)); if (ret) @@ -127,73 +319,106 @@ nvif_outp_acquire(struct nvif_outp *outp, u8 proto, struct nvif_outp_acquire_v0 } int -nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], - int link_nr, int link_bw, bool hda, bool mst) +nvif_outp_acquire_pior(struct nvif_outp *outp) { struct nvif_outp_acquire_v0 args; int ret; - args.dp.link_nr = link_nr; - args.dp.link_bw = link_bw; - args.dp.hda = hda; - args.dp.mst = mst; - memcpy(args.dp.dpcd, dpcd, sizeof(args.dp.dpcd)); - - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_DP, &args); - NVIF_ERRON(ret, &outp->object, - "[ACQUIRE proto:DP link_nr:%d link_bw:%02x hda:%d mst:%d] or:%d link:%d", - args.dp.link_nr, args.dp.link_bw, args.dp.hda, args.dp.mst, args.or, args.link); + ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_PIOR, &args); + NVIF_ERRON(ret, &outp->object, "[ACQUIRE PIOR] or:%d", args.or); return ret; } int -nvif_outp_acquire_lvds(struct nvif_outp *outp, bool dual, bool bpc8) +nvif_outp_acquire_sor(struct nvif_outp *outp, bool hda) { struct nvif_outp_acquire_v0 args; int ret; - args.lvds.dual = dual; - args.lvds.bpc8 = bpc8; + args.sor.hda = hda; - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_LVDS, &args); - NVIF_ERRON(ret, &outp->object, - "[ACQUIRE proto:LVDS dual:%d 8bpc:%d] or:%d link:%d", - args.lvds.dual, args.lvds.bpc8, args.or, args.link); + ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_SOR, &args); + NVIF_ERRON(ret, &outp->object, "[ACQUIRE SOR] or:%d link:%d", args.or, args.link); return ret; } int -nvif_outp_acquire_tmds(struct nvif_outp *outp, int head, - bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda) +nvif_outp_acquire_dac(struct nvif_outp *outp) { struct nvif_outp_acquire_v0 args; int ret; - args.tmds.head = head; - args.tmds.hdmi = hdmi; - args.tmds.hdmi_max_ac_packet = max_ac_packet; - args.tmds.hdmi_rekey = rekey; - args.tmds.hdmi_scdc = scdc; - args.tmds.hdmi_hda = hda; - - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_TMDS, &args); - NVIF_ERRON(ret, &outp->object, - "[ACQUIRE proto:TMDS head:%d hdmi:%d max_ac_packet:%d rekey:%d scdc:%d hda:%d]" - " or:%d link:%d", args.tmds.head, args.tmds.hdmi, args.tmds.hdmi_max_ac_packet, - args.tmds.hdmi_rekey, args.tmds.hdmi_scdc, args.tmds.hdmi_hda, - args.or, args.link); + ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_DAC, &args); + NVIF_ERRON(ret, &outp->object, "[ACQUIRE DAC] or:%d", args.or); return ret; } +static int +nvif_outp_inherit(struct nvif_outp *outp, + u8 proto, + struct nvif_outp_inherit_v0 *args, + u8 *proto_out) +{ + int ret; + + args->version = 0; + args->proto = proto; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_INHERIT, args, sizeof(*args)); + if (ret) + return ret; + + outp->or.id = args->or; + outp->or.link = args->link; + *proto_out = args->proto; + return 0; +} + int -nvif_outp_acquire_rgb_crt(struct nvif_outp *outp) +nvif_outp_inherit_lvds(struct nvif_outp *outp, u8 *proto_out) { - struct nvif_outp_acquire_v0 args; + struct nvif_outp_inherit_v0 args; int ret; - ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_RGB_CRT, &args); - NVIF_ERRON(ret, &outp->object, "[ACQUIRE proto:RGB_CRT] or:%d", args.or); - return ret; + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_LVDS, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:LVDS] ret:%d", ret); + return ret ?: args.head; +} + +int +nvif_outp_inherit_tmds(struct nvif_outp *outp, u8 *proto_out) +{ + struct nvif_outp_inherit_v0 args; + int ret; + + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_TMDS, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:TMDS] ret:%d", ret); + return ret ?: args.head; +} + +int +nvif_outp_inherit_dp(struct nvif_outp *outp, u8 *proto_out) +{ + struct nvif_outp_inherit_v0 args; + int ret; + + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_DP, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:DP] ret:%d", ret); + + // TODO: Get current link info + + return ret ?: args.head; +} + +int +nvif_outp_inherit_rgb_crt(struct nvif_outp *outp, u8 *proto_out) +{ + struct nvif_outp_inherit_v0 args; + int ret; + + ret = nvif_outp_inherit(outp, NVIF_OUTP_INHERIT_V0_RGB_CRT, &args, proto_out); + NVIF_ERRON(ret && ret != -ENODEV, &outp->object, "[INHERIT proto:RGB_CRT] ret:%d", ret); + return ret ?: args.head; } int @@ -210,6 +435,61 @@ nvif_outp_load_detect(struct nvif_outp *outp, u32 loadval) return ret < 0 ? ret : args.load; } +int +nvif_outp_edid_get(struct nvif_outp *outp, u8 **pedid) +{ + struct nvif_outp_edid_get_v0 *args; + int ret; + + args = kmalloc(sizeof(*args), GFP_KERNEL); + if (!args) + return -ENOMEM; + + args->version = 0; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_EDID_GET, args, sizeof(*args)); + NVIF_ERRON(ret, &outp->object, "[EDID_GET] size:%d", args->size); + if (ret) + goto done; + + *pedid = kmalloc(args->size, GFP_KERNEL); + if (!*pedid) { + ret = -ENOMEM; + goto done; + } + + memcpy(*pedid, args->data, args->size); + ret = args->size; +done: + kfree(args); + return ret; +} + +enum nvif_outp_detect_status +nvif_outp_detect(struct nvif_outp *outp) +{ + struct nvif_outp_detect_v0 args; + int ret; + + args.version = 0; + + ret = nvif_mthd(&outp->object, NVIF_OUTP_V0_DETECT, &args, sizeof(args)); + NVIF_ERRON(ret, &outp->object, "[DETECT] status:%02x", args.status); + if (ret) + return UNKNOWN; + + switch (args.status) { + case NVIF_OUTP_DETECT_V0_NOT_PRESENT: return NOT_PRESENT; + case NVIF_OUTP_DETECT_V0_PRESENT: return PRESENT; + case NVIF_OUTP_DETECT_V0_UNKNOWN: return UNKNOWN; + default: + WARN_ON(1); + break; + } + + return UNKNOWN; +} + void nvif_outp_dtor(struct nvif_outp *outp) { @@ -231,6 +511,50 @@ nvif_outp_ctor(struct nvif_disp *disp, const char *name, int id, struct nvif_out if (ret) return ret; + outp->id = args.id; + + switch (args.type) { + case NVIF_OUTP_V0_TYPE_DAC : outp->info.type = NVIF_OUTP_DAC; break; + case NVIF_OUTP_V0_TYPE_SOR : outp->info.type = NVIF_OUTP_SOR; break; + case NVIF_OUTP_V0_TYPE_PIOR: outp->info.type = NVIF_OUTP_PIOR; break; + break; + default: + WARN_ON(1); + nvif_outp_dtor(outp); + return -EINVAL; + } + + switch (args.proto) { + case NVIF_OUTP_V0_PROTO_RGB_CRT: + outp->info.proto = NVIF_OUTP_RGB_CRT; + outp->info.rgb_crt.freq_max = args.rgb_crt.freq_max; + break; + case NVIF_OUTP_V0_PROTO_TMDS: + outp->info.proto = NVIF_OUTP_TMDS; + outp->info.tmds.dual = args.tmds.dual; + break; + case NVIF_OUTP_V0_PROTO_LVDS: + outp->info.proto = NVIF_OUTP_LVDS; + outp->info.lvds.acpi_edid = args.lvds.acpi_edid; + break; + case NVIF_OUTP_V0_PROTO_DP: + outp->info.proto = NVIF_OUTP_DP; + outp->info.dp.aux = args.dp.aux; + outp->info.dp.mst = args.dp.mst; + outp->info.dp.increased_wm = args.dp.increased_wm; + outp->info.dp.link_nr = args.dp.link_nr; + outp->info.dp.link_bw = args.dp.link_bw; + break; + default: + WARN_ON(1); + nvif_outp_dtor(outp); + return -EINVAL; + } + + outp->info.heads = args.heads; + outp->info.ddc = args.ddc; + outp->info.conn = args.conn; + outp->or.id = -1; return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c index 91fb494d4009..374212da9e95 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/firmware.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/firmware.c @@ -79,8 +79,7 @@ nvkm_firmware_get(const struct nvkm_subdev *subdev, const char *fwname, int ver, int i; /* Convert device name to lowercase */ - strncpy(cname, device->chip->name, sizeof(cname)); - cname[sizeof(cname) - 1] = '\0'; + strscpy(cname, device->chip->name, sizeof(cname)); i = strlen(cname); while (i) { --i; diff --git a/drivers/gpu/drm/nouveau/nvkm/core/memory.c b/drivers/gpu/drm/nouveau/nvkm/core/memory.c index c69daac9bac7..a705c2dfca80 100644 --- a/drivers/gpu/drm/nouveau/nvkm/core/memory.c +++ b/drivers/gpu/drm/nouveau/nvkm/core/memory.c @@ -140,12 +140,23 @@ nvkm_memory_new(struct nvkm_device *device, enum nvkm_memory_target target, { struct nvkm_instmem *imem = device->imem; struct nvkm_memory *memory; + bool preserve = true; int ret; - if (unlikely(target != NVKM_MEM_TARGET_INST || !imem)) + if (unlikely(!imem)) return -ENOSYS; - ret = nvkm_instobj_new(imem, size, align, zero, &memory); + switch (target) { + case NVKM_MEM_TARGET_INST_SR_LOST: + preserve = false; + break; + case NVKM_MEM_TARGET_INST: + break; + default: + return -ENOSYS; + } + + ret = nvkm_instobj_new(imem, size, align, zero, preserve, &memory); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c index 73104b59f97f..39f7e7ce9f4a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/base.c @@ -23,15 +23,12 @@ */ #include "priv.h" #include "conn.h" -#include "dp.h" #include "head.h" #include "ior.h" #include "outp.h" #include <core/client.h> #include <core/ramht.h> -#include <subdev/bios.h> -#include <subdev/bios/dcb.h> #include <nvif/class.h> #include <nvif/cl0046.h> @@ -105,18 +102,14 @@ static int nvkm_disp_fini(struct nvkm_engine *engine, bool suspend) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_conn *conn; struct nvkm_outp *outp; if (disp->func->fini) disp->func->fini(disp); list_for_each_entry(outp, &disp->outps, head) { - nvkm_outp_fini(outp); - } - - list_for_each_entry(conn, &disp->conns, head) { - nvkm_conn_fini(conn); + if (outp->func->fini) + outp->func->fini(outp); } return 0; @@ -126,16 +119,12 @@ static int nvkm_disp_init(struct nvkm_engine *engine) { struct nvkm_disp *disp = nvkm_disp(engine); - struct nvkm_conn *conn; struct nvkm_outp *outp; struct nvkm_ior *ior; - list_for_each_entry(conn, &disp->conns, head) { - nvkm_conn_init(conn); - } - list_for_each_entry(outp, &disp->outps, head) { - nvkm_outp_init(outp); + if (outp->func->init) + outp->func->init(outp); } if (disp->func->init) { @@ -159,142 +148,15 @@ nvkm_disp_oneinit(struct nvkm_engine *engine) { struct nvkm_disp *disp = nvkm_disp(engine); struct nvkm_subdev *subdev = &disp->engine.subdev; - struct nvkm_bios *bios = subdev->device->bios; - struct nvkm_outp *outp, *outt, *pair; - struct nvkm_conn *conn; struct nvkm_head *head; - struct nvkm_ior *ior; - struct nvbios_connE connE; - struct dcb_output dcbE; - u8 hpd = 0, ver, hdr; - u32 data; int ret, i; - /* Create output path objects for each VBIOS display path. */ - i = -1; - while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { - if (ver < 0x40) /* No support for chipsets prior to NV50. */ - break; - if (dcbE.type == DCB_OUTPUT_UNUSED) - continue; - if (dcbE.type == DCB_OUTPUT_EOL) - break; - outp = NULL; - - switch (dcbE.type) { - case DCB_OUTPUT_ANALOG: - case DCB_OUTPUT_TV: - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - ret = nvkm_outp_new(disp, i, &dcbE, &outp); - break; - case DCB_OUTPUT_DP: - ret = nvkm_dp_new(disp, i, &dcbE, &outp); - break; - case DCB_OUTPUT_WFD: - /* No support for WFD yet. */ - ret = -ENODEV; - continue; - default: - nvkm_warn(subdev, "dcb %d type %d unknown\n", - i, dcbE.type); - continue; - } - - if (ret) { - if (outp) { - if (ret != -ENODEV) - OUTP_ERR(outp, "ctor failed: %d", ret); - else - OUTP_DBG(outp, "not supported"); - nvkm_outp_del(&outp); - continue; - } - nvkm_error(subdev, "failed to create outp %d\n", i); - continue; - } - - list_add_tail(&outp->head, &disp->outps); - hpd = max(hpd, (u8)(dcbE.connector + 1)); - } - - /* Create connector objects based on available output paths. */ - list_for_each_entry_safe(outp, outt, &disp->outps, head) { - /* VBIOS data *should* give us the most useful information. */ - data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, - &connE); - - /* No bios connector data... */ - if (!data) { - /* Heuristic: anything with the same ccb index is - * considered to be on the same connector, any - * output path without an associated ccb entry will - * be put on its own connector. - */ - int ccb_index = outp->info.i2c_index; - if (ccb_index != 0xf) { - list_for_each_entry(pair, &disp->outps, head) { - if (pair->info.i2c_index == ccb_index) { - outp->conn = pair->conn; - break; - } - } - } - - /* Connector shared with another output path. */ - if (outp->conn) - continue; - - memset(&connE, 0x00, sizeof(connE)); - connE.type = DCB_CONNECTOR_NONE; - i = -1; - } else { - i = outp->info.connector; - } - - /* Check that we haven't already created this connector. */ - list_for_each_entry(conn, &disp->conns, head) { - if (conn->index == outp->info.connector) { - outp->conn = conn; - break; - } - } - - if (outp->conn) - continue; - - /* Apparently we need to create a new one! */ - ret = nvkm_conn_new(disp, i, &connE, &outp->conn); - if (ret) { - nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret); - nvkm_conn_del(&outp->conn); - list_del(&outp->head); - nvkm_outp_del(&outp); - continue; - } - - list_add_tail(&outp->conn->head, &disp->conns); - } - if (disp->func->oneinit) { ret = disp->func->oneinit(disp); if (ret) return ret; } - /* Enforce identity-mapped SOR assignment for panels, which have - * certain bits (ie. backlight controls) wired to a specific SOR. - */ - list_for_each_entry(outp, &disp->outps, head) { - if (outp->conn->info.type == DCB_CONNECTOR_LVDS || - outp->conn->info.type == DCB_CONNECTOR_eDP) { - ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); - if (!WARN_ON(!ior)) - ior->identity = true; - outp->identity = true; - } - } - i = 0; list_for_each_entry(head, &disp->heads, head) i = max(i, head->id + 1); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c index fbdae1137864..ff88a5a5253a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.c @@ -30,16 +30,6 @@ #include <nvif/event.h> void -nvkm_conn_fini(struct nvkm_conn *conn) -{ -} - -void -nvkm_conn_init(struct nvkm_conn *conn) -{ -} - -void nvkm_conn_del(struct nvkm_conn **pconn) { struct nvkm_conn *conn = *pconn; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h index a0600e72b0ec..01c3146c7066 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/conn.h @@ -19,8 +19,6 @@ struct nvkm_conn { int nvkm_conn_new(struct nvkm_disp *, int index, struct nvbios_connE *, struct nvkm_conn **); void nvkm_conn_del(struct nvkm_conn **); -void nvkm_conn_init(struct nvkm_conn *); -void nvkm_conn_fini(struct nvkm_conn *); #define CONN_MSG(c,l,f,a...) do { \ struct nvkm_conn *_conn = (c); \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c index b8ac66b4a2c4..a109348bd63b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -41,6 +41,40 @@ */ #define AMPERE_IED_HACK(disp) ((disp)->engine.subdev.device->card_type >= GA100) +static int +nvkm_dp_mst_id_put(struct nvkm_outp *outp, u32 id) +{ + return 0; +} + +static int +nvkm_dp_mst_id_get(struct nvkm_outp *outp, u32 *pid) +{ + *pid = BIT(outp->index); + return 0; +} + +static int +nvkm_dp_aux_xfer(struct nvkm_outp *outp, u8 type, u32 addr, u8 *data, u8 *size) +{ + int ret = nvkm_i2c_aux_acquire(outp->dp.aux); + + if (ret) + return ret; + + ret = nvkm_i2c_aux_xfer(outp->dp.aux, false, type, addr, data, size); + nvkm_i2c_aux_release(outp->dp.aux); + return ret; +} + +static int +nvkm_dp_aux_pwr(struct nvkm_outp *outp, bool pu) +{ + outp->dp.enabled = pu; + nvkm_dp_enable(outp, outp->dp.enabled); + return 0; +} + struct lt_state { struct nvkm_outp *outp; @@ -282,31 +316,20 @@ nvkm_dp_train_link(struct nvkm_outp *outp, int rate) struct lt_state lt = { .outp = outp, .pc2 = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_TPS3_SUPPORTED, + .repeaters = outp->dp.lttprs, }; - u8 sink[2], data; + u8 sink[2]; int ret; OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw); - /* Select LTTPR non-transparent mode if we have a valid configuration, - * use transparent mode otherwise. - */ - if (outp->dp.lttpr[0] >= 0x14) { - data = DPCD_LTTPR_MODE_TRANSPARENT; - nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); - - if (outp->dp.lttprs) { - data = DPCD_LTTPR_MODE_NON_TRANSPARENT; - nvkm_wraux(outp->dp.aux, DPCD_LTTPR_MODE, &data, sizeof(data)); - lt.repeaters = outp->dp.lttprs; - } - } - /* Set desired link configuration on the sink. */ sink[0] = (outp->dp.rate[rate].dpcd < 0) ? ior->dp.bw : 0; sink[1] = ior->dp.nr; if (ior->dp.ef) sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN; + if (outp->dp.lt.post_adj) + sink[1] |= 0x20; ret = nvkm_wraux(outp->dp.aux, DPCD_LC00_LINK_BW_SET, sink, 2); if (ret) @@ -447,71 +470,58 @@ nvkm_dp_train_init(struct nvkm_outp *outp) } static int -nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps) +nvkm_dp_drive(struct nvkm_outp *outp, u8 lanes, u8 pe[4], u8 vs[4]) +{ + struct lt_state lt = { + .outp = outp, + .stat[4] = (pe[0] << 2) | (vs[0] << 0) | + (pe[1] << 6) | (vs[1] << 4), + .stat[5] = (pe[2] << 2) | (vs[2] << 0) | + (pe[3] << 6) | (vs[3] << 4), + }; + + return nvkm_dp_train_drive(<, false); +} + +static int +nvkm_dp_train(struct nvkm_outp *outp, bool retrain) { struct nvkm_ior *ior = outp->ior; - int ret = -EINVAL, nr, rate; - u8 pwr; + int ret, rate; - /* Retraining link? Skip source configuration, it can mess up the active modeset. */ - if (atomic_read(&outp->dp.lt.done)) { - for (rate = 0; rate < outp->dp.rates; rate++) { - if (outp->dp.rate[rate].rate == ior->dp.bw * 27000) - return nvkm_dp_train_link(outp, ret); - } - WARN_ON(1); - return -EINVAL; + for (rate = 0; rate < outp->dp.rates; rate++) { + if (outp->dp.rate[rate].rate == (retrain ? ior->dp.bw : outp->dp.lt.bw) * 27000) + break; } - /* Ensure sink is not in a low-power state. */ - if (!nvkm_rdaux(outp->dp.aux, DPCD_SC00, &pwr, 1)) { - if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { - pwr &= ~DPCD_SC00_SET_POWER; - pwr |= DPCD_SC00_SET_POWER_D0; - nvkm_wraux(outp->dp.aux, DPCD_SC00, &pwr, 1); - } + if (WARN_ON(rate == outp->dp.rates)) + return -EINVAL; + + /* Retraining link? Skip source configuration, it can mess up the active modeset. */ + if (retrain) { + mutex_lock(&outp->dp.mutex); + ret = nvkm_dp_train_link(outp, rate); + mutex_unlock(&outp->dp.mutex); + return ret; } + mutex_lock(&outp->dp.mutex); + OUTP_DBG(outp, "training"); + ior->dp.mst = outp->dp.lt.mst; ior->dp.ef = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; - ior->dp.nr = 0; + ior->dp.bw = outp->dp.lt.bw; + ior->dp.nr = outp->dp.lt.nr; - /* Link training. */ - OUTP_DBG(outp, "training"); nvkm_dp_train_init(outp); - - /* Validate and train at configuration requested (if any) on ACQUIRE. */ - if (outp->dp.lt.nr) { - for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { - for (rate = 0; nr == outp->dp.lt.nr && rate < outp->dp.rates; rate++) { - if (outp->dp.rate[rate].rate / 27000 == outp->dp.lt.bw) { - ior->dp.bw = outp->dp.rate[rate].rate / 27000; - ior->dp.nr = nr; - ret = nvkm_dp_train_links(outp, rate); - } - } - } - } - - /* Otherwise, loop through all valid link configurations that support the data rate. */ - for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) { - for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) { - if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) { - /* Program selected link configuration. */ - ior->dp.bw = outp->dp.rate[rate].rate / 27000; - ior->dp.nr = nr; - ret = nvkm_dp_train_links(outp, rate); - } - } - } - - /* Finish up. */ + ret = nvkm_dp_train_links(outp, rate); nvkm_dp_train_fini(outp); if (ret < 0) OUTP_ERR(outp, "training failed"); else OUTP_DBG(outp, "training done"); - atomic_set(&outp->dp.lt.done, 1); + + mutex_unlock(&outp->dp.mutex); return ret; } @@ -529,155 +539,10 @@ nvkm_dp_disable(struct nvkm_outp *outp, struct nvkm_ior *ior) static void nvkm_dp_release(struct nvkm_outp *outp) { - /* Prevent link from being retrained if sink sends an IRQ. */ - atomic_set(&outp->dp.lt.done, 0); outp->ior->dp.nr = 0; -} - -static int -nvkm_dp_acquire(struct nvkm_outp *outp) -{ - struct nvkm_ior *ior = outp->ior; - struct nvkm_head *head; - bool retrain = true; - u32 datakbps = 0; - u32 dataKBps; - u32 linkKBps; - u8 stat[3]; - int ret, i; - - mutex_lock(&outp->dp.mutex); - - /* Check that link configuration meets current requirements. */ - list_for_each_entry(head, &outp->disp->heads, head) { - if (ior->asy.head & (1 << head->id)) { - u32 khz = (head->asy.hz >> ior->asy.rgdiv) / 1000; - datakbps += khz * head->asy.or.depth; - } - } - - linkKBps = ior->dp.bw * 27000 * ior->dp.nr; - dataKBps = DIV_ROUND_UP(datakbps, 8); - OUTP_DBG(outp, "data %d KB/s link %d KB/s mst %d->%d", - dataKBps, linkKBps, ior->dp.mst, outp->dp.lt.mst); - if (linkKBps < dataKBps || ior->dp.mst != outp->dp.lt.mst) { - OUTP_DBG(outp, "link requirements changed"); - goto done; - } - - /* Check that link is still trained. */ - ret = nvkm_rdaux(outp->dp.aux, DPCD_LS02, stat, 3); - if (ret) { - OUTP_DBG(outp, "failed to read link status, assuming no sink"); - goto done; - } - - if (stat[2] & DPCD_LS04_INTERLANE_ALIGN_DONE) { - for (i = 0; i < ior->dp.nr; i++) { - u8 lane = (stat[i >> 1] >> ((i & 1) * 4)) & 0x0f; - if (!(lane & DPCD_LS02_LANE0_CR_DONE) || - !(lane & DPCD_LS02_LANE0_CHANNEL_EQ_DONE) || - !(lane & DPCD_LS02_LANE0_SYMBOL_LOCKED)) { - OUTP_DBG(outp, "lane %d not equalised", lane); - goto done; - } - } - retrain = false; - } else { - OUTP_DBG(outp, "no inter-lane alignment"); - } - -done: - if (retrain || !atomic_read(&outp->dp.lt.done)) - ret = nvkm_dp_train(outp, dataKBps); - mutex_unlock(&outp->dp.mutex); - return ret; -} - -static bool -nvkm_dp_enable_supported_link_rates(struct nvkm_outp *outp) -{ - u8 sink_rates[DPCD_RC10_SUPPORTED_LINK_RATES__SIZE]; - int i, j, k; - - if (outp->conn->info.type != DCB_CONNECTOR_eDP || - outp->dp.dpcd[DPCD_RC00_DPCD_REV] < 0x13 || - nvkm_rdaux(outp->dp.aux, DPCD_RC10_SUPPORTED_LINK_RATES(0), - sink_rates, sizeof(sink_rates))) - return false; - - for (i = 0; i < ARRAY_SIZE(sink_rates); i += 2) { - const u32 rate = ((sink_rates[i + 1] << 8) | sink_rates[i]) * 200 / 10; - - if (!rate || WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) - break; - - if (rate > outp->info.dpconf.link_bw * 27000) { - OUTP_DBG(outp, "rate %d !outp", rate); - continue; - } - - for (j = 0; j < outp->dp.rates; j++) { - if (rate > outp->dp.rate[j].rate) { - for (k = outp->dp.rates; k > j; k--) - outp->dp.rate[k] = outp->dp.rate[k - 1]; - break; - } - } - - outp->dp.rate[j].dpcd = i / 2; - outp->dp.rate[j].rate = rate; - outp->dp.rates++; - } - - for (i = 0; i < outp->dp.rates; i++) - OUTP_DBG(outp, "link_rate[%d] = %d", outp->dp.rate[i].dpcd, outp->dp.rate[i].rate); + nvkm_dp_disable(outp, outp->ior); - return outp->dp.rates != 0; -} - -/* XXX: This is a big fat hack, and this is just drm_dp_read_dpcd_caps() - * converted to work inside nvkm. This is a temporary holdover until we start - * passing the drm_dp_aux device through NVKM - */ -static int -nvkm_dp_read_dpcd_caps(struct nvkm_outp *outp) -{ - struct nvkm_i2c_aux *aux = outp->dp.aux; - u8 dpcd_ext[DP_RECEIVER_CAP_SIZE]; - int ret; - - ret = nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, outp->dp.dpcd, DP_RECEIVER_CAP_SIZE); - if (ret < 0) - return ret; - - /* - * Prior to DP1.3 the bit represented by - * DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT was reserved. - * If it is set DP_DPCD_REV at 0000h could be at a value less than - * the true capability of the panel. The only way to check is to - * then compare 0000h and 2200h. - */ - if (!(outp->dp.dpcd[DP_TRAINING_AUX_RD_INTERVAL] & - DP_EXTENDED_RECEIVER_CAP_FIELD_PRESENT)) - return 0; - - ret = nvkm_rdaux(aux, DP_DP13_DPCD_REV, dpcd_ext, sizeof(dpcd_ext)); - if (ret < 0) - return ret; - - if (outp->dp.dpcd[DP_DPCD_REV] > dpcd_ext[DP_DPCD_REV]) { - OUTP_DBG(outp, "Extended DPCD rev less than base DPCD rev (%d > %d)\n", - outp->dp.dpcd[DP_DPCD_REV], dpcd_ext[DP_DPCD_REV]); - return 0; - } - - if (!memcmp(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext))) - return 0; - - memcpy(outp->dp.dpcd, dpcd_ext, sizeof(dpcd_ext)); - - return 0; + nvkm_outp_release(outp); } void @@ -711,66 +576,11 @@ nvkm_dp_enable(struct nvkm_outp *outp, bool auxpwr) OUTP_DBG(outp, "aux power -> always"); nvkm_i2c_aux_monitor(aux, true); outp->dp.aux_pwr = true; - - /* Detect any LTTPRs before reading DPCD receiver caps. */ - if (!nvkm_rdaux(aux, DPCD_LTTPR_REV, outp->dp.lttpr, sizeof(outp->dp.lttpr)) && - outp->dp.lttpr[0] >= 0x14 && outp->dp.lttpr[2]) { - switch (outp->dp.lttpr[2]) { - case 0x80: outp->dp.lttprs = 1; break; - case 0x40: outp->dp.lttprs = 2; break; - case 0x20: outp->dp.lttprs = 3; break; - case 0x10: outp->dp.lttprs = 4; break; - case 0x08: outp->dp.lttprs = 5; break; - case 0x04: outp->dp.lttprs = 6; break; - case 0x02: outp->dp.lttprs = 7; break; - case 0x01: outp->dp.lttprs = 8; break; - default: - /* Unknown LTTPR count, we'll switch to transparent mode. */ - WARN_ON(1); - outp->dp.lttprs = 0; - break; - } - } else { - /* No LTTPR support, or zero LTTPR count - don't touch it at all. */ - memset(outp->dp.lttpr, 0x00, sizeof(outp->dp.lttpr)); - } - - if (!nvkm_dp_read_dpcd_caps(outp)) { - const u8 rates[] = { 0x1e, 0x14, 0x0a, 0x06, 0 }; - const u8 *rate; - int rate_max; - - outp->dp.rates = 0; - outp->dp.links = outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT; - outp->dp.links = min(outp->dp.links, outp->info.dpconf.link_nr); - if (outp->dp.lttprs && outp->dp.lttpr[4]) - outp->dp.links = min_t(int, outp->dp.links, outp->dp.lttpr[4]); - - rate_max = outp->dp.dpcd[DPCD_RC01_MAX_LINK_RATE]; - rate_max = min(rate_max, outp->info.dpconf.link_bw); - if (outp->dp.lttprs && outp->dp.lttpr[1]) - rate_max = min_t(int, rate_max, outp->dp.lttpr[1]); - - if (!nvkm_dp_enable_supported_link_rates(outp)) { - for (rate = rates; *rate; rate++) { - if (*rate > rate_max) - continue; - - if (WARN_ON(outp->dp.rates == ARRAY_SIZE(outp->dp.rate))) - break; - - outp->dp.rate[outp->dp.rates].dpcd = -1; - outp->dp.rate[outp->dp.rates].rate = *rate * 27000; - outp->dp.rates++; - } - } - } } else if (!auxpwr && outp->dp.aux_pwr) { OUTP_DBG(outp, "aux power -> demand"); nvkm_i2c_aux_monitor(aux, false); outp->dp.aux_pwr = false; - atomic_set(&outp->dp.lt.done, 0); /* Restore eDP panel GPIO to its prior state if we changed it, as * it could potentially interfere with other outputs. @@ -793,6 +603,7 @@ nvkm_dp_fini(struct nvkm_outp *outp) static void nvkm_dp_init(struct nvkm_outp *outp) { + nvkm_outp_init(outp); nvkm_dp_enable(outp, outp->dp.enabled); } @@ -807,9 +618,18 @@ nvkm_dp_func = { .dtor = nvkm_dp_dtor, .init = nvkm_dp_init, .fini = nvkm_dp_fini, - .acquire = nvkm_dp_acquire, + .detect = nvkm_outp_detect, + .inherit = nvkm_outp_inherit, + .acquire = nvkm_outp_acquire, .release = nvkm_dp_release, - .disable = nvkm_dp_disable, + .bl.get = nvkm_outp_bl_get, + .bl.set = nvkm_outp_bl_set, + .dp.aux_pwr = nvkm_dp_aux_pwr, + .dp.aux_xfer = nvkm_dp_aux_xfer, + .dp.train = nvkm_dp_train, + .dp.drive = nvkm_dp_drive, + .dp.mst_id_get = nvkm_dp_mst_id_get, + .dp.mst_id_put = nvkm_dp_mst_id_put, }; int @@ -819,7 +639,7 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n struct nvkm_bios *bios = device->bios; struct nvkm_i2c *i2c = device->i2c; struct nvkm_outp *outp; - u8 hdr, cnt, len; + u8 ver, hdr, cnt, len; u32 data; int ret; @@ -847,7 +667,9 @@ nvkm_dp_new(struct nvkm_disp *disp, int index, struct dcb_output *dcbE, struct n OUTP_DBG(outp, "bios dp %02x %02x %02x %02x", outp->dp.version, hdr, cnt, len); + data = nvbios_dp_table(bios, &ver, &hdr, &cnt, &len); + outp->dp.mst = data && ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04); + mutex_init(&outp->dp.mutex); - atomic_set(&outp->dp.lt.done, 0); return 0; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c index 23ae451ba473..1be97a68a83e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g84.c @@ -124,6 +124,7 @@ g84_sor = { .state = nv50_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = &nv50_sor_bl, .hdmi = &g84_sor_hdmi, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c index 67ef889a0c5f..843a2661ce9d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/g94.c @@ -295,6 +295,7 @@ g94_sor = { .clock = nv50_sor_clock, .war_2 = g94_sor_war_2, .war_3 = g94_sor_war_3, + .bl = &nv50_sor_bl, .hdmi = &g84_sor_hdmi, .dp = &g94_sor_dp, }; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c index 52099b75f52a..efe66ba3c61f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ga102.c @@ -105,6 +105,7 @@ ga102_sor = { .state = gv100_sor_state, .power = nv50_sor_power, .clock = ga102_sor_clock, + .bl = >215_sor_bl, .hdmi = &gv100_sor_hdmi, .dp = &ga102_sor_dp, .hda = &gv100_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c index a48e9bdf4cd0..b48ead31da30 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c @@ -328,6 +328,7 @@ gf119_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gf119_sor_hdmi, .dp = &gf119_sor_dp, .hda = &gf119_sor_hda, @@ -1038,7 +1039,6 @@ gf119_disp_super(struct work_struct *work) continue; nv50_disp_super_2_0(disp, head); } - nvkm_outp_route(disp); list_for_each_entry(head, &disp->heads, head) { if (!(mask[head->id] & 0x00010000)) continue; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c index 876a21a0cebb..a3e2fbadade4 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gk104.c @@ -115,6 +115,7 @@ gk104_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gk104_sor_hdmi, .dp = &gf119_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c index b4d8e868616f..688e123ad482 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm107.c @@ -70,6 +70,7 @@ gm107_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gk104_sor_hdmi, .dp = &gm107_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c index 562ebae57d44..511e7831b2f5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gm200.c @@ -68,15 +68,23 @@ gm200_sor_dp = { }; void -gm200_sor_hdmi_scdc(struct nvkm_ior *ior, u8 scdc) +gm200_sor_hdmi_scdc(struct nvkm_ior *ior, u32 khz, bool support, bool scrambling, + bool scrambling_low_rates) { struct nvkm_device *device = ior->disp->engine.subdev.device; const u32 soff = nv50_ior_base(ior); - const u32 ctrl = scdc & 0x3; + u32 ctrl = 0; - nvkm_mask(device, 0x61c5bc + soff, 0x00000003, ctrl); + ior->tmds.high_speed = khz > 340000; + + if (support && scrambling) { + if (ior->tmds.high_speed) + ctrl |= 0x00000002; + if (ior->tmds.high_speed || scrambling_low_rates) + ctrl |= 0x00000001; + } - ior->tmds.high_speed = !!(scdc & 0x2); + nvkm_mask(device, 0x61c5bc + soff, 0x00000003, ctrl); } const struct nvkm_ior_func_hdmi @@ -139,6 +147,7 @@ gm200_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gm200_sor_hdmi, .dp = &gm200_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c index 7f1eb4332040..4070447bd800 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gp100.c @@ -37,6 +37,7 @@ gp100_sor = { .state = gf119_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gm200_sor_hdmi, .dp = &gm200_sor_dp, .hda = &gf119_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c index 506ffbe7b842..6318721b66f6 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gt215.c @@ -182,11 +182,49 @@ gt215_sor_hdmi = { .infoframe_vsi = gt215_sor_hdmi_infoframe_vsi, }; +static int +gt215_sor_bl_set(struct nvkm_ior *ior, int lvl) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div, val; + + div = nvkm_rd32(device, 0x61c080 + soff); + val = (lvl * div) / 100; + if (div) + nvkm_wr32(device, 0x61c084 + soff, 0xc0000000 | val); + + return 0; +} + +static int +gt215_sor_bl_get(struct nvkm_ior *ior) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div, val; + + div = nvkm_rd32(device, 0x61c080 + soff); + val = nvkm_rd32(device, 0x61c084 + soff); + val &= 0x00ffffff; + if (div && div >= val) + return ((val * 100) + (div / 2)) / div; + + return 100; +} + +const struct nvkm_ior_func_bl +gt215_sor_bl = { + .get = gt215_sor_bl_get, + .set = gt215_sor_bl_set, +}; + static const struct nvkm_ior_func gt215_sor = { .state = g94_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = >215_sor_bl, .hdmi = >215_sor_hdmi, .dp = >215_sor_dp, .hda = >215_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c index 4ebc030e40d1..e1634f7bca56 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gv100.c @@ -212,6 +212,7 @@ gv100_sor = { .state = gv100_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gv100_sor_hdmi, .dp = &gv100_sor_dp, .hda = &gv100_sor_hda, @@ -863,7 +864,6 @@ gv100_disp_super(struct work_struct *work) continue; nv50_disp_super_2_0(disp, head); } - nvkm_outp_route(disp); list_for_each_entry(head, &disp->heads, head) { if (!(mask[head->id] & 0x00010000)) continue; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h index da1b1a626ef2..9beb9d1e8633 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h @@ -63,11 +63,18 @@ struct nvkm_ior_func { void (*war_2)(struct nvkm_ior *); void (*war_3)(struct nvkm_ior *); + const struct nvkm_ior_func_bl { + int (*get)(struct nvkm_ior *); + int (*set)(struct nvkm_ior *, int lvl); + } *bl; + const struct nvkm_ior_func_hdmi { void (*ctrl)(struct nvkm_ior *, int head, bool enable, u8 max_ac_packet, u8 rekey); - void (*scdc)(struct nvkm_ior *, u8 scdc); + void (*scdc)(struct nvkm_ior *, u32 khz, bool support, bool scrambling, + bool scrambling_low_rates); void (*infoframe_avi)(struct nvkm_ior *, int head, void *data, u32 size); void (*infoframe_vsi)(struct nvkm_ior *, int head, void *data, u32 size); + void (*audio)(struct nvkm_ior *, int head, bool enable); } *hdmi; const struct nvkm_ior_func_dp { @@ -77,6 +84,8 @@ struct nvkm_ior_func { void (*pattern)(struct nvkm_ior *, int pattern); void (*drive)(struct nvkm_ior *, int ln, int pc, int dc, int pe, int tx_pu); + int (*sst)(struct nvkm_ior *, int head, bool ef, + u32 watermark, u32 hblanksym, u32 vblanksym); void (*vcpi)(struct nvkm_ior *, int head, u8 slot, u8 slot_nr, u16 pbn, u16 aligned); void (*audio)(struct nvkm_ior *, int head, bool enable); @@ -122,6 +131,7 @@ int nv50_sor_cnt(struct nvkm_disp *, unsigned long *); void nv50_sor_state(struct nvkm_ior *, struct nvkm_ior_state *); void nv50_sor_power(struct nvkm_ior *, bool, bool, bool, bool, bool); void nv50_sor_clock(struct nvkm_ior *); +extern const struct nvkm_ior_func_bl nv50_sor_bl; int g84_sor_new(struct nvkm_disp *, int); extern const struct nvkm_ior_func_hdmi g84_sor_hdmi; @@ -138,6 +148,7 @@ void g94_sor_dp_audio_sym(struct nvkm_ior *, int, u16, u32); void g94_sor_dp_activesym(struct nvkm_ior *, int, u8, u8, u8, u8); void g94_sor_dp_watermark(struct nvkm_ior *, int, u8); +extern const struct nvkm_ior_func_bl gt215_sor_bl; extern const struct nvkm_ior_func_hdmi gt215_sor_hdmi; void gt215_sor_dp_audio(struct nvkm_ior *, int, bool); extern const struct nvkm_ior_func_hda gt215_sor_hda; @@ -167,7 +178,7 @@ void gm107_sor_dp_pattern(struct nvkm_ior *, int); void gm200_sor_route_set(struct nvkm_outp *, struct nvkm_ior *); int gm200_sor_route_get(struct nvkm_outp *, int *); extern const struct nvkm_ior_func_hdmi gm200_sor_hdmi; -void gm200_sor_hdmi_scdc(struct nvkm_ior *, u8); +void gm200_sor_hdmi_scdc(struct nvkm_ior *, u32, bool, bool, bool); extern const struct nvkm_ior_func_dp gm200_sor_dp; void gm200_sor_dp_drive(struct nvkm_ior *, int, int, int, int, int); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c index f96ba4752655..e0c5fb6df3d7 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/mcp89.c @@ -44,6 +44,7 @@ mcp89_sor = { .state = g94_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = >215_sor_bl, .hdmi = >215_sor_hdmi, .dp = &mcp89_sor_dp, .hda = >215_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c index be8116802960..2d05e2f7e46b 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c @@ -23,7 +23,9 @@ */ #include "priv.h" #include "chan.h" +#include "conn.h" #include "head.h" +#include "dp.h" #include "ior.h" #include "outp.h" @@ -156,6 +158,37 @@ nv50_pior_cnt(struct nvkm_disp *disp, unsigned long *pmask) return 3; } +static int +nv50_sor_bl_set(struct nvkm_ior *ior, int lvl) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div = 1025; + u32 val = (lvl * div) / 100; + + nvkm_wr32(device, 0x61c084 + soff, 0x80000000 | val); + return 0; +} + +static int +nv50_sor_bl_get(struct nvkm_ior *ior) +{ + struct nvkm_device *device = ior->disp->engine.subdev.device; + const u32 soff = nv50_ior_base(ior); + u32 div = 1025; + u32 val; + + val = nvkm_rd32(device, 0x61c084 + soff); + val &= 0x000007ff; + return ((val * 100) + (div / 2)) / div; +} + +const struct nvkm_ior_func_bl +nv50_sor_bl = { + .get = nv50_sor_bl_get, + .set = nv50_sor_bl_set, +}; + void nv50_sor_clock(struct nvkm_ior *sor) { @@ -220,6 +253,7 @@ nv50_sor = { .state = nv50_sor_state, .power = nv50_sor_power, .clock = nv50_sor_clock, + .bl = &nv50_sor_bl, }; static int @@ -1254,10 +1288,6 @@ nv50_disp_super_2_2(struct nvkm_disp *disp, struct nvkm_head *head) ior->asy.link = outp->lvds.dual ? 3 : 1; } - /* Handle any link training, etc. */ - if (outp && outp->func->acquire) - outp->func->acquire(outp); - /* Execute OnInt2 IED script. */ nv50_disp_super_ied_on(head, ior, 0, khz); @@ -1287,7 +1317,6 @@ nv50_disp_super_2_1(struct nvkm_disp *disp, struct nvkm_head *head) void nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head) { - struct nvkm_outp *outp; struct nvkm_ior *ior; /* Determine which OR, if any, we're detaching from the head. */ @@ -1298,14 +1327,6 @@ nv50_disp_super_2_0(struct nvkm_disp *disp, struct nvkm_head *head) /* Execute OffInt2 IED script. */ nv50_disp_super_ied_off(head, ior, 2); - - /* If we're shutting down the OR's only active head, execute - * the output path's disable function. - */ - if (ior->arm.head == (1 << head->id)) { - if ((outp = ior->arm.outp) && outp->func->disable) - outp->func->disable(outp, ior); - } } void @@ -1371,7 +1392,6 @@ nv50_disp_super(struct work_struct *work) continue; nv50_disp_super_2_0(disp, head); } - nvkm_outp_route(disp); list_for_each_entry(head, &disp->heads, head) { if (!(super & (0x00000200 << head->id))) continue; @@ -1563,7 +1583,15 @@ nv50_disp_oneinit(struct nvkm_disp *disp) const struct nvkm_disp_func *func = disp->func; struct nvkm_subdev *subdev = &disp->engine.subdev; struct nvkm_device *device = subdev->device; + struct nvkm_bios *bios = device->bios; + struct nvkm_outp *outp, *outt, *pair; + struct nvkm_conn *conn; + struct nvkm_ior *ior; int ret, i; + u8 ver, hdr; + u32 data; + struct dcb_output dcbE; + struct nvbios_connE connE; if (func->wndw.cnt) { disp->wndw.nr = func->wndw.cnt(disp, &disp->wndw.mask); @@ -1610,8 +1638,130 @@ nv50_disp_oneinit(struct nvkm_disp *disp) if (ret) return ret; - return nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : - 0x1000, 0, disp->inst, &disp->ramht); + ret = nvkm_ramht_new(device, func->ramht_size ? func->ramht_size : 0x1000, 0, disp->inst, + &disp->ramht); + if (ret) + return ret; + + /* Create output path objects for each VBIOS display path. */ + i = -1; + while ((data = dcb_outp_parse(bios, ++i, &ver, &hdr, &dcbE))) { + if (WARN_ON((ver & 0xf0) != 0x40)) + return -EINVAL; + if (dcbE.type == DCB_OUTPUT_UNUSED) + continue; + if (dcbE.type == DCB_OUTPUT_EOL) + break; + outp = NULL; + + switch (dcbE.type) { + case DCB_OUTPUT_ANALOG: + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + ret = nvkm_outp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_DP: + ret = nvkm_dp_new(disp, i, &dcbE, &outp); + break; + case DCB_OUTPUT_TV: + case DCB_OUTPUT_WFD: + /* No support for WFD yet. */ + ret = -ENODEV; + continue; + default: + nvkm_warn(subdev, "dcb %d type %d unknown\n", + i, dcbE.type); + continue; + } + + if (ret) { + if (outp) { + if (ret != -ENODEV) + OUTP_ERR(outp, "ctor failed: %d", ret); + else + OUTP_DBG(outp, "not supported"); + nvkm_outp_del(&outp); + continue; + } + nvkm_error(subdev, "failed to create outp %d\n", i); + continue; + } + + list_add_tail(&outp->head, &disp->outps); + } + + /* Create connector objects based on available output paths. */ + list_for_each_entry_safe(outp, outt, &disp->outps, head) { + /* VBIOS data *should* give us the most useful information. */ + data = nvbios_connEp(bios, outp->info.connector, &ver, &hdr, + &connE); + + /* No bios connector data... */ + if (!data) { + /* Heuristic: anything with the same ccb index is + * considered to be on the same connector, any + * output path without an associated ccb entry will + * be put on its own connector. + */ + int ccb_index = outp->info.i2c_index; + if (ccb_index != 0xf) { + list_for_each_entry(pair, &disp->outps, head) { + if (pair->info.i2c_index == ccb_index) { + outp->conn = pair->conn; + break; + } + } + } + + /* Connector shared with another output path. */ + if (outp->conn) + continue; + + memset(&connE, 0x00, sizeof(connE)); + connE.type = DCB_CONNECTOR_NONE; + i = -1; + } else { + i = outp->info.connector; + } + + /* Check that we haven't already created this connector. */ + list_for_each_entry(conn, &disp->conns, head) { + if (conn->index == outp->info.connector) { + outp->conn = conn; + break; + } + } + + if (outp->conn) + continue; + + /* Apparently we need to create a new one! */ + ret = nvkm_conn_new(disp, i, &connE, &outp->conn); + if (ret) { + nvkm_error(subdev, "failed to create outp %d conn: %d\n", outp->index, ret); + nvkm_conn_del(&outp->conn); + list_del(&outp->head); + nvkm_outp_del(&outp); + continue; + } + + list_add_tail(&outp->conn->head, &disp->conns); + } + + /* Enforce identity-mapped SOR assignment for panels, which have + * certain bits (ie. backlight controls) wired to a specific SOR. + */ + list_for_each_entry(outp, &disp->outps, head) { + if (outp->conn->info.type == DCB_CONNECTOR_LVDS || + outp->conn->info.type == DCB_CONNECTOR_eDP) { + ior = nvkm_ior_find(disp, SOR, ffs(outp->info.or) - 1); + if (!WARN_ON(!ior)) + ior->identity = true; + outp->identity = true; + } + } + + return 0; } static const struct nvkm_disp_func diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c index 6094805fbd63..bfb2a4db8d64 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.c @@ -22,14 +22,16 @@ * Authors: Ben Skeggs */ #include "outp.h" +#include "conn.h" #include "dp.h" #include "ior.h" #include <subdev/bios.h> #include <subdev/bios/dcb.h> +#include <subdev/gpio.h> #include <subdev/i2c.h> -void +static void nvkm_outp_route(struct nvkm_disp *disp) { struct nvkm_outp *outp; @@ -46,8 +48,8 @@ nvkm_outp_route(struct nvkm_disp *disp) list_for_each_entry(ior, &disp->iors, head) { if ((outp = ior->asy.outp)) { - OUTP_DBG(outp, "acquire %s", ior->name); if (ior->asy.outp != ior->arm.outp) { + OUTP_DBG(outp, "acquire %s", ior->name); if (ior->func->route.set) ior->func->route.set(outp, ior); ior->arm.outp = ior->asy.outp; @@ -87,22 +89,20 @@ nvkm_outp_xlat(struct nvkm_outp *outp, enum nvkm_ior_type *type) } void -nvkm_outp_release(struct nvkm_outp *outp, u8 user) +nvkm_outp_release_or(struct nvkm_outp *outp, u8 user) { struct nvkm_ior *ior = outp->ior; OUTP_TRACE(outp, "release %02x &= %02x %p", outp->acquired, ~user, ior); if (ior) { outp->acquired &= ~user; if (!outp->acquired) { - if (outp->func->release && outp->ior) - outp->func->release(outp); outp->ior->asy.outp = NULL; outp->ior = NULL; } } } -static inline int +int nvkm_outp_acquire_ior(struct nvkm_outp *outp, u8 user, struct nvkm_ior *ior) { outp->ior = ior; @@ -140,7 +140,7 @@ nvkm_outp_acquire_hda(struct nvkm_outp *outp, enum nvkm_ior_type type, } int -nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda) +nvkm_outp_acquire_or(struct nvkm_outp *outp, u8 user, bool hda) { struct nvkm_ior *ior = outp->ior; enum nvkm_ior_proto proto; @@ -207,39 +207,110 @@ nvkm_outp_acquire(struct nvkm_outp *outp, u8 user, bool hda) return nvkm_outp_acquire_hda(outp, type, user, false); } +int +nvkm_outp_bl_set(struct nvkm_outp *outp, int level) +{ + int ret; + + ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false); + if (ret) + return ret; + + if (outp->ior->func->bl) + ret = outp->ior->func->bl->set(outp->ior, level); + else + ret = -EINVAL; + + nvkm_outp_release_or(outp, NVKM_OUTP_PRIV); + return ret; +} + +int +nvkm_outp_bl_get(struct nvkm_outp *outp) +{ + int ret; + + ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false); + if (ret) + return ret; + + if (outp->ior->func->bl) + ret = outp->ior->func->bl->get(outp->ior); + else + ret = -EINVAL; + + nvkm_outp_release_or(outp, NVKM_OUTP_PRIV); + return ret; +} + +int +nvkm_outp_detect(struct nvkm_outp *outp) +{ + struct nvkm_gpio *gpio = outp->disp->engine.subdev.device->gpio; + int ret = -EINVAL; + + if (outp->conn->info.hpd != DCB_GPIO_UNUSED) { + ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, outp->conn->info.hpd); + if (ret < 0) + return ret; + if (ret) + return 1; + + /*TODO: Look into returning NOT_PRESENT if !HPD on DVI/HDMI. + * + * It's uncertain whether this is accurate for all older chipsets, + * so we're returning UNKNOWN, and the DRM will probe DDC instead. + */ + if (outp->info.type == DCB_OUTPUT_DP) + return 0; + } + + return ret; +} + void -nvkm_outp_fini(struct nvkm_outp *outp) +nvkm_outp_release(struct nvkm_outp *outp) { - if (outp->func->fini) - outp->func->fini(outp); + nvkm_outp_release_or(outp, NVKM_OUTP_USER); + nvkm_outp_route(outp->disp); } -static void -nvkm_outp_init_route(struct nvkm_outp *outp) +int +nvkm_outp_acquire(struct nvkm_outp *outp, bool hda) +{ + int ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_USER, hda); + + if (ret) + return ret; + + nvkm_outp_route(outp->disp); + return 0; +} + +struct nvkm_ior * +nvkm_outp_inherit(struct nvkm_outp *outp) { struct nvkm_disp *disp = outp->disp; + struct nvkm_ior *ior; enum nvkm_ior_proto proto; enum nvkm_ior_type type; - struct nvkm_ior *ior; int id, link; /* Find any OR from the class that is able to support this device. */ proto = nvkm_outp_xlat(outp, &type); if (proto == UNKNOWN) - return; + return NULL; ior = nvkm_ior_find(disp, type, -1); - if (!ior) { - WARN_ON(1); - return; - } + if (WARN_ON(!ior)) + return NULL; /* Determine the specific OR, if any, this device is attached to. */ if (ior->func->route.get) { id = ior->func->route.get(outp, &link); if (id < 0) { OUTP_DBG(outp, "no route"); - return; + return NULL; } } else { /* Prior to DCB 4.1, this is hardwired like so. */ @@ -248,10 +319,24 @@ nvkm_outp_init_route(struct nvkm_outp *outp) } ior = nvkm_ior_find(disp, type, id); - if (!ior) { - WARN_ON(1); + if (WARN_ON(!ior)) + return NULL; + + return ior; +} + +void +nvkm_outp_init(struct nvkm_outp *outp) +{ + enum nvkm_ior_proto proto; + enum nvkm_ior_type type; + struct nvkm_ior *ior; + + /* Find any OR from the class that is able to support this device. */ + proto = nvkm_outp_xlat(outp, &type); + ior = outp->func->inherit(outp); + if (!ior) return; - } /* Determine if the OR is already configured for this device. */ ior->func->state(ior, &ior->arm); @@ -274,14 +359,6 @@ nvkm_outp_init_route(struct nvkm_outp *outp) } void -nvkm_outp_init(struct nvkm_outp *outp) -{ - nvkm_outp_init_route(outp); - if (outp->func->init) - outp->func->init(outp); -} - -void nvkm_outp_del(struct nvkm_outp **poutp) { struct nvkm_outp *outp = *poutp; @@ -328,6 +405,13 @@ nvkm_outp_new_(const struct nvkm_outp_func *func, struct nvkm_disp *disp, static const struct nvkm_outp_func nvkm_outp = { + .init = nvkm_outp_init, + .detect = nvkm_outp_detect, + .inherit = nvkm_outp_inherit, + .acquire = nvkm_outp_acquire, + .release = nvkm_outp_release, + .bl.get = nvkm_outp_bl_get, + .bl.set = nvkm_outp_bl_set, }; int diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h index 4e7f873f66e2..ebd2f499b4b1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h @@ -35,6 +35,8 @@ struct nvkm_outp { struct { struct nvbios_dpout info; u8 version; + bool mst; + bool increased_wm; struct nvkm_i2c_aux *aux; @@ -50,14 +52,13 @@ struct nvkm_outp { u32 rate; } rate[8]; int rates; - int links; struct mutex mutex; struct { - atomic_t done; u8 nr; u8 bw; bool mst; + bool post_adj; } lt; } dp; }; @@ -74,17 +75,45 @@ int nvkm_outp_new(struct nvkm_disp *, int index, struct dcb_output *, struct nvk void nvkm_outp_del(struct nvkm_outp **); void nvkm_outp_init(struct nvkm_outp *); void nvkm_outp_fini(struct nvkm_outp *); -int nvkm_outp_acquire(struct nvkm_outp *, u8 user, bool hda); -void nvkm_outp_release(struct nvkm_outp *, u8 user); -void nvkm_outp_route(struct nvkm_disp *); + +int nvkm_outp_detect(struct nvkm_outp *); + +struct nvkm_ior *nvkm_outp_inherit(struct nvkm_outp *); +int nvkm_outp_acquire(struct nvkm_outp *, bool hda); +int nvkm_outp_acquire_or(struct nvkm_outp *, u8 user, bool hda); +int nvkm_outp_acquire_ior(struct nvkm_outp *, u8 user, struct nvkm_ior *); +void nvkm_outp_release(struct nvkm_outp *); +void nvkm_outp_release_or(struct nvkm_outp *, u8 user); + +int nvkm_outp_bl_get(struct nvkm_outp *); +int nvkm_outp_bl_set(struct nvkm_outp *, int level); struct nvkm_outp_func { void *(*dtor)(struct nvkm_outp *); void (*init)(struct nvkm_outp *); void (*fini)(struct nvkm_outp *); - int (*acquire)(struct nvkm_outp *); + + int (*detect)(struct nvkm_outp *); + int (*edid_get)(struct nvkm_outp *, u8 *data, u16 *size); + + struct nvkm_ior *(*inherit)(struct nvkm_outp *); + int (*acquire)(struct nvkm_outp *, bool hda); void (*release)(struct nvkm_outp *); - void (*disable)(struct nvkm_outp *, struct nvkm_ior *); + + struct { + int (*get)(struct nvkm_outp *); + int (*set)(struct nvkm_outp *, int level); + } bl; + + struct { + int (*aux_pwr)(struct nvkm_outp *, bool pu); + int (*aux_xfer)(struct nvkm_outp *, u8 type, u32 addr, u8 *data, u8 *size); + int (*rates)(struct nvkm_outp *); + int (*train)(struct nvkm_outp *, bool retrain); + int (*drive)(struct nvkm_outp *, u8 lanes, u8 pe[4], u8 vs[4]); + int (*mst_id_get)(struct nvkm_outp *, u32 *id); + int (*mst_id_put)(struct nvkm_outp *, u32 id); + } dp; }; #define OUTP_MSG(o,l,f,a...) do { \ diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c index f5242a672279..19f5d3a6035e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/tu102.c @@ -88,6 +88,7 @@ tu102_sor = { .state = gv100_sor_state, .power = nv50_sor_power, .clock = gf119_sor_clock, + .bl = >215_sor_bl, .hdmi = &gv100_sor_hdmi, .dp = &tu102_sor_dp, .hda = &gv100_sor_hda, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c index 46b057fe1412..ff82bb248492 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uconn.c @@ -109,46 +109,6 @@ nvkm_uconn_uevent(struct nvkm_object *object, void *argv, u32 argc, struct nvkm_ nvkm_uconn_uevent_gpio); } -static int -nvkm_uconn_mthd_hpd_status(struct nvkm_conn *conn, void *argv, u32 argc) -{ - struct nvkm_gpio *gpio = conn->disp->engine.subdev.device->gpio; - union nvif_conn_hpd_status_args *args = argv; - - if (argc != sizeof(args->v0) || args->v0.version != 0) - return -ENOSYS; - - args->v0.support = gpio && conn->info.hpd != DCB_GPIO_UNUSED; - args->v0.present = 0; - - if (args->v0.support) { - int ret = nvkm_gpio_get(gpio, 0, DCB_GPIO_UNUSED, conn->info.hpd); - - if (WARN_ON(ret < 0)) { - args->v0.support = false; - return 0; - } - - args->v0.present = ret; - } - - return 0; -} - -static int -nvkm_uconn_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) -{ - struct nvkm_conn *conn = nvkm_uconn(object); - - switch (mthd) { - case NVIF_CONN_V0_HPD_STATUS: return nvkm_uconn_mthd_hpd_status(conn, argv, argc); - default: - break; - } - - return -EINVAL; -} - static void * nvkm_uconn_dtor(struct nvkm_object *object) { @@ -164,7 +124,6 @@ nvkm_uconn_dtor(struct nvkm_object *object) static const struct nvkm_object_func nvkm_uconn = { .dtor = nvkm_uconn_dtor, - .mthd = nvkm_uconn_mthd, .uevent = nvkm_uconn_uevent, }; @@ -192,6 +151,32 @@ nvkm_uconn_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv ret = -EBUSY; spin_lock(&disp->client.lock); if (!conn->object.func) { + switch (conn->info.type) { + case DCB_CONNECTOR_VGA : args->v0.type = NVIF_CONN_V0_VGA; break; + case DCB_CONNECTOR_TV_0 : + case DCB_CONNECTOR_TV_1 : + case DCB_CONNECTOR_TV_3 : args->v0.type = NVIF_CONN_V0_TV; break; + case DCB_CONNECTOR_DMS59_0 : + case DCB_CONNECTOR_DMS59_1 : + case DCB_CONNECTOR_DVI_I : args->v0.type = NVIF_CONN_V0_DVI_I; break; + case DCB_CONNECTOR_DVI_D : args->v0.type = NVIF_CONN_V0_DVI_D; break; + case DCB_CONNECTOR_LVDS : args->v0.type = NVIF_CONN_V0_LVDS; break; + case DCB_CONNECTOR_LVDS_SPWG: args->v0.type = NVIF_CONN_V0_LVDS_SPWG; break; + case DCB_CONNECTOR_DMS59_DP0: + case DCB_CONNECTOR_DMS59_DP1: + case DCB_CONNECTOR_DP : + case DCB_CONNECTOR_mDP : + case DCB_CONNECTOR_USB_C : args->v0.type = NVIF_CONN_V0_DP; break; + case DCB_CONNECTOR_eDP : args->v0.type = NVIF_CONN_V0_EDP; break; + case DCB_CONNECTOR_HDMI_0 : + case DCB_CONNECTOR_HDMI_1 : + case DCB_CONNECTOR_HDMI_C : args->v0.type = NVIF_CONN_V0_HDMI; break; + default: + WARN_ON(1); + ret = -EINVAL; + break; + } + nvkm_object_ctor(&nvkm_uconn, oclass, &conn->object); *pobject = &conn->object; ret = 0; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c index fc283a4a1522..e4279f1772a1 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/uoutp.c @@ -25,6 +25,8 @@ #include "head.h" #include "ior.h" +#include <subdev/i2c.h> + #include <nvif/if0012.h> static int @@ -44,17 +46,121 @@ nvkm_uoutp_mthd_dp_mst_vcpi(struct nvkm_outp *outp, void *argv, u32 argc) } static int -nvkm_uoutp_mthd_dp_retrain(struct nvkm_outp *outp, void *argv, u32 argc) +nvkm_uoutp_mthd_dp_mst_id_put(struct nvkm_outp *outp, void *argv, u32 argc) { - union nvif_outp_dp_retrain_args *args = argv; + union nvif_outp_dp_mst_id_put_args *args = argv; - if (argc != sizeof(args->vn)) + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.mst_id_put) + return -EINVAL; + + return outp->func->dp.mst_id_put(outp, args->v0.id); +} + +static int +nvkm_uoutp_mthd_dp_mst_id_get(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_mst_id_get_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.mst_id_get) + return -EINVAL; + + return outp->func->dp.mst_id_get(outp, &args->v0.id); +} + +static int +nvkm_uoutp_mthd_dp_sst(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_sst_args *args = argv; + struct nvkm_disp *disp = outp->disp; + struct nvkm_ior *ior = outp->ior; + + if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - if (!atomic_read(&outp->dp.lt.done)) + if (!ior->func->dp || !nvkm_head_find(disp, args->v0.head)) + return -EINVAL; + if (!ior->func->dp->sst) return 0; - return outp->func->acquire(outp); + return ior->func->dp->sst(ior, args->v0.head, + outp->dp.dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP, + args->v0.watermark, args->v0.hblanksym, args->v0.vblanksym); +} + +static int +nvkm_uoutp_mthd_dp_drive(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_drive_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.drive) + return -EINVAL; + + return outp->func->dp.drive(outp, args->v0.lanes, args->v0.pe, args->v0.vs); +} + +static int +nvkm_uoutp_mthd_dp_train(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_train_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.train) + return -EINVAL; + + if (!args->v0.retrain) { + memcpy(outp->dp.dpcd, args->v0.dpcd, sizeof(outp->dp.dpcd)); + outp->dp.lttprs = args->v0.lttprs; + outp->dp.lt.nr = args->v0.link_nr; + outp->dp.lt.bw = args->v0.link_bw / 27000; + outp->dp.lt.mst = args->v0.mst; + outp->dp.lt.post_adj = args->v0.post_lt_adj; + } + + return outp->func->dp.train(outp, args->v0.retrain); +} + +static int +nvkm_uoutp_mthd_dp_rates(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_rates_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (args->v0.rates > ARRAY_SIZE(outp->dp.rate)) + return -EINVAL; + + for (int i = 0; i < args->v0.rates; i++) { + outp->dp.rate[i].dpcd = args->v0.rate[i].dpcd; + outp->dp.rate[i].rate = args->v0.rate[i].rate; + } + + outp->dp.rates = args->v0.rates; + + if (outp->func->dp.rates) + outp->func->dp.rates(outp); + + return 0; +} + +static int +nvkm_uoutp_mthd_dp_aux_xfer(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_dp_aux_xfer_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->dp.aux_xfer) + return -EINVAL; + + return outp->func->dp.aux_xfer(outp, args->v0.type, args->v0.addr, + args->v0.data, &args->v0.size); } static int @@ -64,10 +170,10 @@ nvkm_uoutp_mthd_dp_aux_pwr(struct nvkm_outp *outp, void *argv, u32 argc) if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; + if (!outp->func->dp.aux_pwr) + return -EINVAL; - outp->dp.enabled = !!args->v0.state; - nvkm_dp_enable(outp, outp->dp.enabled); - return 0; + return outp->func->dp.aux_pwr(outp, !!args->v0.state); } static int @@ -88,12 +194,20 @@ nvkm_uoutp_mthd_hda_eld(struct nvkm_outp *outp, void *argv, u32 argc) if (argc && args->v0.data[0]) { if (outp->info.type == DCB_OUTPUT_DP) ior->func->dp->audio(ior, args->v0.head, true); + else + if (ior->func->hdmi->audio) + ior->func->hdmi->audio(ior, args->v0.head, true); + ior->func->hda->hpd(ior, args->v0.head, true); ior->func->hda->eld(ior, args->v0.head, args->v0.data, argc); } else { + ior->func->hda->hpd(ior, args->v0.head, false); + if (outp->info.type == DCB_OUTPUT_DP) ior->func->dp->audio(ior, args->v0.head, false); - ior->func->hda->hpd(ior, args->v0.head, false); + else + if (ior->func->hdmi->audio) + ior->func->hdmi->audio(ior, args->v0.head, false); } return 0; @@ -126,84 +240,105 @@ nvkm_uoutp_mthd_infoframe(struct nvkm_outp *outp, void *argv, u32 argc) } static int -nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc) +nvkm_uoutp_mthd_hdmi(struct nvkm_outp *outp, void *argv, u32 argc) { - struct nvkm_head *head = outp->asy.head; + union nvif_outp_hdmi_args *args = argv; struct nvkm_ior *ior = outp->ior; - union nvif_outp_release_args *args = argv; - if (argc != sizeof(args->vn)) + if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - if (ior->func->hdmi && head) { - ior->func->hdmi->infoframe_avi(ior, head->id, NULL, 0); - ior->func->hdmi->infoframe_vsi(ior, head->id, NULL, 0); - ior->func->hdmi->ctrl(ior, head->id, false, 0, 0); + if (!(outp->asy.head = nvkm_head_find(outp->disp, args->v0.head))) + return -EINVAL; + + if (!ior->func->hdmi || + args->v0.max_ac_packet > 0x1f || + args->v0.rekey > 0x7f || + (args->v0.scdc && !ior->func->hdmi->scdc)) + return -EINVAL; + + if (!args->v0.enable) { + ior->func->hdmi->infoframe_avi(ior, args->v0.head, NULL, 0); + ior->func->hdmi->infoframe_vsi(ior, args->v0.head, NULL, 0); + ior->func->hdmi->ctrl(ior, args->v0.head, false, 0, 0); + return 0; } - nvkm_outp_release(outp, NVKM_OUTP_USER); + ior->func->hdmi->ctrl(ior, args->v0.head, args->v0.enable, + args->v0.max_ac_packet, args->v0.rekey); + if (ior->func->hdmi->scdc) + ior->func->hdmi->scdc(ior, args->v0.khz, args->v0.scdc, args->v0.scdc_scrambling, + args->v0.scdc_low_rates); + return 0; } static int -nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[DP_RECEIVER_CAP_SIZE], - u8 link_nr, u8 link_bw, bool hda, bool mst) +nvkm_uoutp_mthd_lvds(struct nvkm_outp *outp, void *argv, u32 argc) { - int ret; + union nvif_outp_lvds_args *args = argv; - ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hda); - if (ret) - return ret; + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (outp->info.type != DCB_OUTPUT_LVDS) + return -EINVAL; - memcpy(outp->dp.dpcd, dpcd, sizeof(outp->dp.dpcd)); - outp->dp.lt.nr = link_nr; - outp->dp.lt.bw = link_bw; - outp->dp.lt.mst = mst; + outp->lvds.dual = !!args->v0.dual; + outp->lvds.bpc8 = !!args->v0.bpc8; return 0; } static int -nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet, - u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda) +nvkm_uoutp_mthd_bl_set(struct nvkm_outp *outp, void *argv, u32 argc) { - struct nvkm_ior *ior; + union nvif_outp_bl_get_args *args = argv; int ret; - if (!(outp->asy.head = nvkm_head_find(outp->disp, head))) - return -EINVAL; + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; - ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hdmi && hdmi_hda); - if (ret) - return ret; + if (outp->func->bl.set) + ret = outp->func->bl.set(outp, args->v0.level); + else + ret = -EINVAL; - ior = outp->ior; + return ret; +} - if (hdmi) { - if (!ior->func->hdmi || - hdmi_max_ac_packet > 0x1f || hdmi_rekey > 0x7f || - (hdmi_scdc && !ior->func->hdmi->scdc)) { - nvkm_outp_release(outp, NVKM_OUTP_USER); - return -EINVAL; - } +static int +nvkm_uoutp_mthd_bl_get(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_bl_get_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; - ior->func->hdmi->ctrl(ior, head, hdmi, hdmi_max_ac_packet, hdmi_rekey); - if (ior->func->hdmi->scdc) - ior->func->hdmi->scdc(ior, hdmi_scdc); + if (outp->func->bl.get) { + ret = outp->func->bl.get(outp); + if (ret >= 0) { + args->v0.level = ret; + ret = 0; + } + } else { + ret = -EINVAL; } - return 0; + return ret; } static int -nvkm_uoutp_mthd_acquire_lvds(struct nvkm_outp *outp, bool dual, bool bpc8) +nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc) { - if (outp->info.type != DCB_OUTPUT_LVDS) - return -EINVAL; + union nvif_outp_release_args *args = argv; - outp->lvds.dual = dual; - outp->lvds.bpc8 = bpc8; + if (argc != sizeof(args->vn)) + return -ENOSYS; + if (!outp->ior) + return -EINVAL; - return nvkm_outp_acquire(outp, NVKM_OUTP_USER, false); + outp->func->release(outp); + return 0; } static int @@ -214,30 +349,16 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc) if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - if (outp->ior) + if (outp->ior && args->v0.type <= NVIF_OUTP_ACQUIRE_V0_PIOR) return -EBUSY; - switch (args->v0.proto) { - case NVIF_OUTP_ACQUIRE_V0_RGB_CRT: - ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, false); + switch (args->v0.type) { + case NVIF_OUTP_ACQUIRE_V0_DAC: + case NVIF_OUTP_ACQUIRE_V0_PIOR: + ret = outp->func->acquire(outp, false); break; - case NVIF_OUTP_ACQUIRE_V0_TMDS: - ret = nvkm_uoutp_mthd_acquire_tmds(outp, args->v0.tmds.head, - args->v0.tmds.hdmi, - args->v0.tmds.hdmi_max_ac_packet, - args->v0.tmds.hdmi_rekey, - args->v0.tmds.hdmi_scdc, - args->v0.tmds.hdmi_hda); - break; - case NVIF_OUTP_ACQUIRE_V0_LVDS: - ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8); - break; - case NVIF_OUTP_ACQUIRE_V0_DP: - ret = nvkm_uoutp_mthd_acquire_dp(outp, args->v0.dp.dpcd, - args->v0.dp.link_nr, - args->v0.dp.link_bw, - args->v0.dp.hda != 0, - args->v0.dp.mst != 0); + case NVIF_OUTP_ACQUIRE_V0_SOR: + ret = outp->func->acquire(outp, args->v0.sor.hda); break; default: ret = -EINVAL; @@ -253,6 +374,69 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc) } static int +nvkm_uoutp_mthd_inherit(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_inherit_args *args = argv; + struct nvkm_ior *ior; + int ret = 0; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + + /* Ensure an ior is hooked up to this outp already */ + ior = outp->func->inherit(outp); + if (!ior) + return -ENODEV; + + /* With iors, there will be a separate output path for each type of connector - and all of + * them will appear to be hooked up. Figure out which one is actually the one we're using + * based on the protocol we were given over nvif + */ + switch (args->v0.proto) { + case NVIF_OUTP_INHERIT_V0_TMDS: + if (ior->arm.proto != TMDS) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_DP: + if (ior->arm.proto != DP) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_LVDS: + if (ior->arm.proto != LVDS) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_TV: + if (ior->arm.proto != TV) + return -ENODEV; + break; + case NVIF_OUTP_INHERIT_V0_RGB_CRT: + if (ior->arm.proto != CRT) + return -ENODEV; + break; + default: + ret = -EINVAL; + break; + } + + /* Make sure that userspace hasn't already acquired this */ + if (outp->acquired) { + OUTP_ERR(outp, "cannot inherit an already acquired (%02x) outp", outp->acquired); + return -EBUSY; + } + + /* Mark the outp acquired by userspace now that we've confirmed it's already active */ + OUTP_TRACE(outp, "inherit %02x |= %02x %p", outp->acquired, NVKM_OUTP_USER, ior); + nvkm_outp_acquire_ior(outp, NVKM_OUTP_USER, ior); + + args->v0.or = ior->id; + args->v0.link = ior->arm.link; + args->v0.head = ffs(ior->arm.head) - 1; + args->v0.proto = ior->arm.proto_evo; + + return ret; +} + +static int nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) { union nvif_outp_load_detect_args *args = argv; @@ -261,7 +445,7 @@ nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) if (argc != sizeof(args->v0) || args->v0.version != 0) return -ENOSYS; - ret = nvkm_outp_acquire(outp, NVKM_OUTP_PRIV, false); + ret = nvkm_outp_acquire_or(outp, NVKM_OUTP_PRIV, false); if (ret == 0) { if (outp->ior->func->sense) { ret = outp->ior->func->sense(outp->ior, args->v0.data); @@ -269,21 +453,64 @@ nvkm_uoutp_mthd_load_detect(struct nvkm_outp *outp, void *argv, u32 argc) } else { ret = -EINVAL; } - nvkm_outp_release(outp, NVKM_OUTP_PRIV); + nvkm_outp_release_or(outp, NVKM_OUTP_PRIV); } return ret; } static int +nvkm_uoutp_mthd_edid_get(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_edid_get_args *args = argv; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->edid_get) + return -EINVAL; + + args->v0.size = ARRAY_SIZE(args->v0.data); + return outp->func->edid_get(outp, args->v0.data, &args->v0.size); +} + +static int +nvkm_uoutp_mthd_detect(struct nvkm_outp *outp, void *argv, u32 argc) +{ + union nvif_outp_detect_args *args = argv; + int ret; + + if (argc != sizeof(args->v0) || args->v0.version != 0) + return -ENOSYS; + if (!outp->func->detect) + return -EINVAL; + + ret = outp->func->detect(outp); + switch (ret) { + case 0: args->v0.status = NVIF_OUTP_DETECT_V0_NOT_PRESENT; break; + case 1: args->v0.status = NVIF_OUTP_DETECT_V0_PRESENT; break; + default: + args->v0.status = NVIF_OUTP_DETECT_V0_UNKNOWN; + break; + } + + return 0; +} + +static int nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) { switch (mthd) { - case NVIF_OUTP_V0_RELEASE : return nvkm_uoutp_mthd_release (outp, argv, argc); - case NVIF_OUTP_V0_INFOFRAME : return nvkm_uoutp_mthd_infoframe (outp, argv, argc); - case NVIF_OUTP_V0_HDA_ELD : return nvkm_uoutp_mthd_hda_eld (outp, argv, argc); - case NVIF_OUTP_V0_DP_RETRAIN : return nvkm_uoutp_mthd_dp_retrain (outp, argv, argc); - case NVIF_OUTP_V0_DP_MST_VCPI: return nvkm_uoutp_mthd_dp_mst_vcpi(outp, argv, argc); + case NVIF_OUTP_V0_RELEASE : return nvkm_uoutp_mthd_release (outp, argv, argc); + case NVIF_OUTP_V0_LVDS : return nvkm_uoutp_mthd_lvds (outp, argv, argc); + case NVIF_OUTP_V0_HDMI : return nvkm_uoutp_mthd_hdmi (outp, argv, argc); + case NVIF_OUTP_V0_INFOFRAME : return nvkm_uoutp_mthd_infoframe (outp, argv, argc); + case NVIF_OUTP_V0_HDA_ELD : return nvkm_uoutp_mthd_hda_eld (outp, argv, argc); + case NVIF_OUTP_V0_DP_TRAIN : return nvkm_uoutp_mthd_dp_train (outp, argv, argc); + case NVIF_OUTP_V0_DP_DRIVE : return nvkm_uoutp_mthd_dp_drive (outp, argv, argc); + case NVIF_OUTP_V0_DP_SST : return nvkm_uoutp_mthd_dp_sst (outp, argv, argc); + case NVIF_OUTP_V0_DP_MST_ID_GET: return nvkm_uoutp_mthd_dp_mst_id_get(outp, argv, argc); + case NVIF_OUTP_V0_DP_MST_ID_PUT: return nvkm_uoutp_mthd_dp_mst_id_put(outp, argv, argc); + case NVIF_OUTP_V0_DP_MST_VCPI : return nvkm_uoutp_mthd_dp_mst_vcpi (outp, argv, argc); default: break; } @@ -292,17 +519,25 @@ nvkm_uoutp_mthd_acquired(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) } static int -nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc) +nvkm_uoutp_mthd_noacquire(struct nvkm_outp *outp, u32 mthd, void *argv, u32 argc, bool *invalid) { switch (mthd) { - case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc); + case NVIF_OUTP_V0_DETECT : return nvkm_uoutp_mthd_detect (outp, argv, argc); + case NVIF_OUTP_V0_EDID_GET : return nvkm_uoutp_mthd_edid_get (outp, argv, argc); + case NVIF_OUTP_V0_INHERIT : return nvkm_uoutp_mthd_inherit (outp, argv, argc); case NVIF_OUTP_V0_ACQUIRE : return nvkm_uoutp_mthd_acquire (outp, argv, argc); + case NVIF_OUTP_V0_LOAD_DETECT: return nvkm_uoutp_mthd_load_detect(outp, argv, argc); + case NVIF_OUTP_V0_BL_GET : return nvkm_uoutp_mthd_bl_get (outp, argv, argc); + case NVIF_OUTP_V0_BL_SET : return nvkm_uoutp_mthd_bl_set (outp, argv, argc); case NVIF_OUTP_V0_DP_AUX_PWR : return nvkm_uoutp_mthd_dp_aux_pwr (outp, argv, argc); + case NVIF_OUTP_V0_DP_AUX_XFER: return nvkm_uoutp_mthd_dp_aux_xfer(outp, argv, argc); + case NVIF_OUTP_V0_DP_RATES : return nvkm_uoutp_mthd_dp_rates (outp, argv, argc); default: break; } - return 1; + *invalid = true; + return 0; } static int @@ -310,12 +545,13 @@ nvkm_uoutp_mthd(struct nvkm_object *object, u32 mthd, void *argv, u32 argc) { struct nvkm_outp *outp = nvkm_uoutp(object); struct nvkm_disp *disp = outp->disp; + bool invalid = false; int ret; mutex_lock(&disp->super.mutex); - ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc); - if (ret <= 0) + ret = nvkm_uoutp_mthd_noacquire(outp, mthd, argv, argc, &invalid); + if (!invalid) goto done; if (outp->ior) @@ -370,10 +606,60 @@ nvkm_uoutp_new(const struct nvkm_oclass *oclass, void *argv, u32 argc, struct nv ret = -EBUSY; spin_lock(&disp->client.lock); if (!outp->object.func) { + switch (outp->info.type) { + case DCB_OUTPUT_ANALOG: + args->v0.type = NVIF_OUTP_V0_TYPE_DAC; + args->v0.proto = NVIF_OUTP_V0_PROTO_RGB_CRT; + args->v0.rgb_crt.freq_max = outp->info.crtconf.maxfreq; + break; + case DCB_OUTPUT_TMDS: + if (!outp->info.location) { + args->v0.type = NVIF_OUTP_V0_TYPE_SOR; + args->v0.tmds.dual = (outp->info.tmdsconf.sor.link == 3); + } else { + args->v0.type = NVIF_OUTP_V0_TYPE_PIOR; + args->v0.tmds.dual = 0; + } + args->v0.proto = NVIF_OUTP_V0_PROTO_TMDS; + break; + case DCB_OUTPUT_LVDS: + args->v0.type = NVIF_OUTP_V0_TYPE_SOR; + args->v0.proto = NVIF_OUTP_V0_PROTO_LVDS; + args->v0.lvds.acpi_edid = outp->info.lvdsconf.use_acpi_for_edid; + break; + case DCB_OUTPUT_DP: + if (!outp->info.location) { + args->v0.type = NVIF_OUTP_V0_TYPE_SOR; + args->v0.dp.aux = outp->info.i2c_index; + } else { + args->v0.type = NVIF_OUTP_V0_TYPE_PIOR; + args->v0.dp.aux = NVKM_I2C_AUX_EXT(outp->info.extdev); + } + args->v0.proto = NVIF_OUTP_V0_PROTO_DP; + args->v0.dp.mst = outp->dp.mst; + args->v0.dp.increased_wm = outp->dp.increased_wm; + args->v0.dp.link_nr = outp->info.dpconf.link_nr; + args->v0.dp.link_bw = outp->info.dpconf.link_bw * 27000; + break; + default: + WARN_ON(1); + ret = -EINVAL; + goto done; + } + + if (outp->info.location) + args->v0.ddc = NVKM_I2C_BUS_EXT(outp->info.extdev); + else + args->v0.ddc = outp->info.i2c_index; + args->v0.heads = outp->info.heads; + args->v0.conn = outp->info.connector; + nvkm_object_ctor(&nvkm_uoutp, oclass, &outp->object); *pobject = &outp->object; ret = 0; } + +done: spin_unlock(&disp->client.lock); return ret; } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index 3648868bb9fc..c494a1ff2d57 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -2032,18 +2032,18 @@ gf100_gr_oneinit(struct nvkm_gr *base) } /* Allocate global context buffers. */ - ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, gr->func->grctx->pagepool_size, - 0x100, false, &gr->pagepool); + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST_SR_LOST, + gr->func->grctx->pagepool_size, 0x100, false, &gr->pagepool); if (ret) return ret; - ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, gr->func->grctx->bundle_size, + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST_SR_LOST, gr->func->grctx->bundle_size, 0x100, false, &gr->bundle_cb); if (ret) return ret; - ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST, gr->func->grctx->attrib_cb_size(gr), - 0x1000, false, &gr->attrib_cb); + ret = nvkm_memory_new(device, NVKM_MEM_TARGET_INST_SR_LOST, + gr->func->grctx->attrib_cb_size(gr), 0x1000, false, &gr->attrib_cb); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c index 8fe0444f761e..131db2645f84 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/pm/base.c @@ -462,7 +462,7 @@ nvkm_perfmon_mthd_query_domain(struct nvkm_perfmon *perfmon, args->v0.id = di; args->v0.signal_nr = nvkm_perfdom_count_perfsig(dom); - strncpy(args->v0.name, dom->name, sizeof(args->v0.name) - 1); + strscpy(args->v0.name, dom->name, sizeof(args->v0.name)); /* Currently only global counters (PCOUNTER) are implemented * but this will be different for local counters (MP). */ @@ -513,8 +513,7 @@ nvkm_perfmon_mthd_query_signal(struct nvkm_perfmon *perfmon, snprintf(args->v0.name, sizeof(args->v0.name), "/%s/%02x", dom->name, si); } else { - strncpy(args->v0.name, sig->name, - sizeof(args->v0.name) - 1); + strscpy(args->v0.name, sig->name, sizeof(args->v0.name)); } args->v0.signal = si; @@ -572,7 +571,7 @@ nvkm_perfmon_mthd_query_source(struct nvkm_perfmon *perfmon, args->v0.source = sig->source[si]; args->v0.mask = src->mask; - strncpy(args->v0.name, src->name, sizeof(args->v0.name) - 1); + strscpy(args->v0.name, src->name, sizeof(args->v0.name)); } if (++si < source_nr) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c index 81a1ad2c88a7..40997ad1d101 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/devinit/tu102.c @@ -83,17 +83,9 @@ tu102_devinit_wait(struct nvkm_device *device) } int -tu102_devinit_post(struct nvkm_devinit *base, bool post) +tu102_devinit_post(struct nvkm_devinit *init, bool post) { - struct nv50_devinit *init = nv50_devinit(base); - int ret; - - ret = tu102_devinit_wait(init->base.subdev.device); - if (ret) - return ret; - - gm200_devinit_preos(init, post); - return 0; + return tu102_devinit_wait(init->subdev.device); } static const struct nvkm_devinit_func diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c index e0e4f97be029..24886eabe8dc 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/base.c @@ -94,15 +94,21 @@ nvkm_instobj_wrap(struct nvkm_device *device, struct nvkm_memory *memory, struct nvkm_memory **pmemory) { struct nvkm_instmem *imem = device->imem; + int ret; if (!imem->func->memory_wrap) return -ENOSYS; - return imem->func->memory_wrap(imem, memory, pmemory); + ret = imem->func->memory_wrap(imem, memory, pmemory); + if (ret) + return ret; + + container_of(*pmemory, struct nvkm_instobj, memory)->preserve = true; + return 0; } int -nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, +nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, bool preserve, struct nvkm_memory **pmemory) { struct nvkm_subdev *subdev = &imem->subdev; @@ -130,6 +136,7 @@ nvkm_instobj_new(struct nvkm_instmem *imem, u32 size, u32 align, bool zero, nvkm_done(memory); } + container_of(memory, struct nvkm_instobj, memory)->preserve = preserve; done: if (ret) nvkm_memory_unref(&memory); @@ -176,9 +183,11 @@ nvkm_instmem_fini(struct nvkm_subdev *subdev, bool suspend) if (suspend) { list_for_each_entry(iobj, &imem->list, head) { - int ret = nvkm_instobj_save(iobj); - if (ret) - return ret; + if (iobj->preserve) { + int ret = nvkm_instobj_save(iobj); + if (ret) + return ret; + } } nvkm_bar_bar2_fini(subdev->device); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h index fe92986a3885..390ca00ab567 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/instmem/priv.h @@ -25,6 +25,7 @@ void nvkm_instmem_boot(struct nvkm_instmem *); struct nvkm_instobj { struct nvkm_memory memory; struct list_head head; + bool preserve; u32 *suspend; }; diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c index f3630d0e0d55..bddac77f48f0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmgp100.c @@ -558,7 +558,7 @@ gp100_vmm_invalidate_pdb(struct nvkm_vmm *vmm, u64 addr) void gp100_vmm_flush(struct nvkm_vmm *vmm, int depth) { - u32 type = (5 /* CACHE_LEVEL_UP_TO_PDE3 */ - depth) << 24; + u32 type = 0; if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) type |= 0x00000004; /* HUB_ONLY */ type |= 0x00000001; /* PAGE_ALL */ diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c index 6cb5eefa45e9..0095d58d4d9a 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/mmu/vmmtu102.c @@ -27,7 +27,7 @@ static void tu102_vmm_flush(struct nvkm_vmm *vmm, int depth) { struct nvkm_device *device = vmm->mmu->subdev.device; - u32 type = (5 /* CACHE_LEVEL_UP_TO_PDE3 */ - depth) << 24; + u32 type = 0; type |= 0x00000001; /* PAGE_ALL */ if (atomic_read(&vmm->engref[NVKM_SUBDEV_BAR])) diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index afeeb7737552..b2835b3ea6f5 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -817,6 +817,13 @@ static void pdev_remove(struct platform_device *pdev) kfree(priv); } +static void pdev_shutdown(struct platform_device *pdev) +{ + struct omap_drm_private *priv = platform_get_drvdata(pdev); + + drm_atomic_helper_shutdown(priv->ddev); +} + #ifdef CONFIG_PM_SLEEP static int omap_drm_suspend(struct device *dev) { @@ -846,6 +853,7 @@ static struct platform_driver pdev = { }, .probe = pdev_probe, .remove_new = pdev_remove, + .shutdown = pdev_shutdown, }; static struct platform_driver * const drivers[] = { diff --git a/drivers/gpu/drm/panel/Kconfig b/drivers/gpu/drm/panel/Kconfig index 869e535faefa..2d6d96ee3547 100644 --- a/drivers/gpu/drm/panel/Kconfig +++ b/drivers/gpu/drm/panel/Kconfig @@ -244,6 +244,17 @@ config DRM_PANEL_JDI_LT070ME05000 The panel has a 1200(RGB)×1920 (WUXGA) resolution and uses 24 bit per pixel. +config DRM_PANEL_JDI_LPM102A188A + tristate "JDI LPM102A188A DSI panel" + depends on OF && GPIOLIB + depends on DRM_MIPI_DSI + depends on BACKLIGHT_CLASS_DEVICE + help + Say Y here if you want to enable support for JDI LPM102A188A DSI + command mode panel as found in Google Pixel C devices. + The panel has a 2560×1800 resolution. It provides a MIPI DSI interface + to the host. + config DRM_PANEL_JDI_R63452 tristate "JDI R63452 Full HD DSI panel" depends on OF diff --git a/drivers/gpu/drm/panel/Makefile b/drivers/gpu/drm/panel/Makefile index 433e93d57949..157c77ff157f 100644 --- a/drivers/gpu/drm/panel/Makefile +++ b/drivers/gpu/drm/panel/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_DRM_PANEL_INNOLUX_EJ030NA) += panel-innolux-ej030na.o obj-$(CONFIG_DRM_PANEL_INNOLUX_P079ZCA) += panel-innolux-p079zca.o obj-$(CONFIG_DRM_PANEL_JADARD_JD9365DA_H3) += panel-jadard-jd9365da-h3.o obj-$(CONFIG_DRM_PANEL_JDI_LT070ME05000) += panel-jdi-lt070me05000.o +obj-$(CONFIG_DRM_PANEL_JDI_LPM102A188A) += panel-jdi-lpm102a188a.o obj-$(CONFIG_DRM_PANEL_JDI_R63452) += panel-jdi-fhd-r63452.o obj-$(CONFIG_DRM_PANEL_KHADAS_TS050) += panel-khadas-ts050.o obj-$(CONFIG_DRM_PANEL_KINGDISPLAY_KD097D04) += panel-kingdisplay-kd097d04.o diff --git a/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c b/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c index 075a7af81eff..bcaa63d1955f 100644 --- a/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c +++ b/drivers/gpu/drm/panel/panel-asus-z00t-tm5p5-n35596.c @@ -16,7 +16,6 @@ struct tm5p5_nt35596 { struct mipi_dsi_device *dsi; struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; - bool prepared; }; static inline struct tm5p5_nt35596 *to_tm5p5_nt35596(struct drm_panel *panel) @@ -112,9 +111,6 @@ static int tm5p5_nt35596_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) { dev_err(dev, "Failed to enable regulators: %d\n", ret); @@ -132,7 +128,6 @@ static int tm5p5_nt35596_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -142,9 +137,6 @@ static int tm5p5_nt35596_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = tm5p5_nt35596_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -153,7 +145,6 @@ static int tm5p5_nt35596_unprepare(struct drm_panel *panel) regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c index 90098b753e3b..e77db8597eb7 100644 --- a/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c +++ b/drivers/gpu/drm/panel/panel-boe-bf060y8m-aj0.c @@ -34,7 +34,6 @@ struct boe_bf060y8m_aj0 { struct mipi_dsi_device *dsi; struct regulator_bulk_data vregs[BF060Y8M_VREG_MAX]; struct gpio_desc *reset_gpio; - bool prepared; }; static inline @@ -129,9 +128,6 @@ static int boe_bf060y8m_aj0_prepare(struct drm_panel *panel) struct device *dev = &boe->dsi->dev; int ret; - if (boe->prepared) - return 0; - /* * Enable EL Driving Voltage first - doing that at the beginning * or at the end of the power sequence doesn't matter, so enable @@ -166,7 +162,6 @@ static int boe_bf060y8m_aj0_prepare(struct drm_panel *panel) return ret; } - boe->prepared = true; return 0; err_vci: @@ -186,9 +181,6 @@ static int boe_bf060y8m_aj0_unprepare(struct drm_panel *panel) struct device *dev = &boe->dsi->dev; int ret; - if (!boe->prepared) - return 0; - ret = boe_bf060y8m_aj0_off(boe); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -196,7 +188,6 @@ static int boe_bf060y8m_aj0_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(boe->reset_gpio, 1); ret = regulator_bulk_disable(ARRAY_SIZE(boe->vregs), boe->vregs); - boe->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c index 8912757a6f42..3e0a8e0d58a0 100644 --- a/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c +++ b/drivers/gpu/drm/panel/panel-jdi-fhd-r63452.c @@ -21,7 +21,6 @@ struct jdi_fhd_r63452 { struct drm_panel panel; struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; - bool prepared; }; static inline struct jdi_fhd_r63452 *to_jdi_fhd_r63452(struct drm_panel *panel) @@ -157,9 +156,6 @@ static int jdi_fhd_r63452_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - jdi_fhd_r63452_reset(ctx); ret = jdi_fhd_r63452_on(ctx); @@ -169,7 +165,6 @@ static int jdi_fhd_r63452_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -179,16 +174,12 @@ static int jdi_fhd_r63452_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = jdi_fhd_r63452_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); gpiod_set_value_cansleep(ctx->reset_gpio, 1); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-jdi-lpm102a188a.c b/drivers/gpu/drm/panel/panel-jdi-lpm102a188a.c new file mode 100644 index 000000000000..5b5082efb282 --- /dev/null +++ b/drivers/gpu/drm/panel/panel-jdi-lpm102a188a.c @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Google, Inc. + * + * Copyright (C) 2022 Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt> + * + * Adapted from the downstream Pixel C driver written by Sean Paul + */ + +#include <linux/backlight.h> +#include <linux/delay.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regulator/consumer.h> + +#include <video/mipi_display.h> + +#include <drm/drm_crtc.h> +#include <drm/drm_mipi_dsi.h> +#include <drm/drm_panel.h> + +#define MCS_CMD_ACS_PROT 0xB0 +#define MCS_CMD_ACS_PROT_OFF (0 << 0) + +#define MCS_PWR_CTRL_FUNC 0xD0 +#define MCS_PWR_CTRL_PARAM1_DEFAULT (2 << 0) +#define MCS_PWR_CTRL_PARAM1_VGH_210_DIV (1 << 4) +#define MCS_PWR_CTRL_PARAM1_VGH_240_DIV (2 << 4) +#define MCS_PWR_CTRL_PARAM1_VGH_280_DIV (3 << 4) +#define MCS_PWR_CTRL_PARAM1_VGH_330_DIV (4 << 4) +#define MCS_PWR_CTRL_PARAM1_VGH_410_DIV (5 << 4) +#define MCS_PWR_CTRL_PARAM2_DEFAULT (9 << 4) +#define MCS_PWR_CTRL_PARAM2_VGL_210_DIV (1 << 0) +#define MCS_PWR_CTRL_PARAM2_VGL_240_DIV (2 << 0) +#define MCS_PWR_CTRL_PARAM2_VGL_280_DIV (3 << 0) +#define MCS_PWR_CTRL_PARAM2_VGL_330_DIV (4 << 0) +#define MCS_PWR_CTRL_PARAM2_VGL_410_DIV (5 << 0) + +struct jdi_panel { + struct drm_panel base; + struct mipi_dsi_device *link1; + struct mipi_dsi_device *link2; + + struct regulator *supply; + struct regulator *ddi_supply; + struct backlight_device *backlight; + + struct gpio_desc *enable_gpio; + struct gpio_desc *reset_gpio; + + const struct drm_display_mode *mode; +}; + +static inline struct jdi_panel *to_panel_jdi(struct drm_panel *panel) +{ + return container_of(panel, struct jdi_panel, base); +} + +static void jdi_wait_frames(struct jdi_panel *jdi, unsigned int frames) +{ + unsigned int refresh = drm_mode_vrefresh(jdi->mode); + + if (WARN_ON(frames > refresh)) + return; + + msleep(1000 / (refresh / frames)); +} + +static int jdi_panel_disable(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_panel_jdi(panel); + + backlight_disable(jdi->backlight); + + jdi_wait_frames(jdi, 2); + + return 0; +} + +static int jdi_panel_unprepare(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_panel_jdi(panel); + int ret; + + ret = mipi_dsi_dcs_set_display_off(jdi->link1); + if (ret < 0) + dev_err(panel->dev, "failed to set display off: %d\n", ret); + + ret = mipi_dsi_dcs_set_display_off(jdi->link2); + if (ret < 0) + dev_err(panel->dev, "failed to set display off: %d\n", ret); + + /* Specified by JDI @ 50ms, subject to change */ + msleep(50); + + ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link1); + if (ret < 0) + dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret); + ret = mipi_dsi_dcs_enter_sleep_mode(jdi->link2); + if (ret < 0) + dev_err(panel->dev, "failed to enter sleep mode: %d\n", ret); + + /* Specified by JDI @ 150ms, subject to change */ + msleep(150); + + gpiod_set_value(jdi->reset_gpio, 1); + + /* T4 = 1ms */ + usleep_range(1000, 3000); + + gpiod_set_value(jdi->enable_gpio, 0); + + /* T5 = 2ms */ + usleep_range(2000, 4000); + + regulator_disable(jdi->ddi_supply); + + /* T6 = 2ms plus some time to discharge capacitors */ + usleep_range(7000, 9000); + + regulator_disable(jdi->supply); + /* Specified by JDI @ 20ms, subject to change */ + msleep(20); + + return ret; +} + +static int jdi_setup_symmetrical_split(struct mipi_dsi_device *left, + struct mipi_dsi_device *right, + const struct drm_display_mode *mode) +{ + int err; + + err = mipi_dsi_dcs_set_column_address(left, 0, mode->hdisplay / 2 - 1); + if (err < 0) { + dev_err(&left->dev, "failed to set column address: %d\n", err); + return err; + } + + err = mipi_dsi_dcs_set_column_address(right, 0, mode->hdisplay / 2 - 1); + if (err < 0) { + dev_err(&right->dev, "failed to set column address: %d\n", err); + return err; + } + + err = mipi_dsi_dcs_set_page_address(left, 0, mode->vdisplay - 1); + if (err < 0) { + dev_err(&left->dev, "failed to set page address: %d\n", err); + return err; + } + + err = mipi_dsi_dcs_set_page_address(right, 0, mode->vdisplay - 1); + if (err < 0) { + dev_err(&right->dev, "failed to set page address: %d\n", err); + return err; + } + + return 0; +} + +static int jdi_write_dcdc_registers(struct jdi_panel *jdi) +{ + /* Clear the manufacturer command access protection */ + mipi_dsi_generic_write_seq(jdi->link1, MCS_CMD_ACS_PROT, + MCS_CMD_ACS_PROT_OFF); + mipi_dsi_generic_write_seq(jdi->link2, MCS_CMD_ACS_PROT, + MCS_CMD_ACS_PROT_OFF); + /* + * Change the VGH/VGL divide rations to move the noise generated by the + * TCONN. This should hopefully avoid interaction with the backlight + * controller. + */ + mipi_dsi_generic_write_seq(jdi->link1, MCS_PWR_CTRL_FUNC, + MCS_PWR_CTRL_PARAM1_VGH_330_DIV | + MCS_PWR_CTRL_PARAM1_DEFAULT, + MCS_PWR_CTRL_PARAM2_VGL_410_DIV | + MCS_PWR_CTRL_PARAM2_DEFAULT); + + mipi_dsi_generic_write_seq(jdi->link2, MCS_PWR_CTRL_FUNC, + MCS_PWR_CTRL_PARAM1_VGH_330_DIV | + MCS_PWR_CTRL_PARAM1_DEFAULT, + MCS_PWR_CTRL_PARAM2_VGL_410_DIV | + MCS_PWR_CTRL_PARAM2_DEFAULT); + + return 0; +} + +static int jdi_panel_prepare(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_panel_jdi(panel); + int err; + + /* Disable backlight to avoid showing random pixels + * with a conservative delay for it to take effect. + */ + backlight_disable(jdi->backlight); + jdi_wait_frames(jdi, 3); + + jdi->link1->mode_flags |= MIPI_DSI_MODE_LPM; + jdi->link2->mode_flags |= MIPI_DSI_MODE_LPM; + + err = regulator_enable(jdi->supply); + if (err < 0) { + dev_err(panel->dev, "failed to enable supply: %d\n", err); + return err; + } + /* T1 = 2ms */ + usleep_range(2000, 4000); + + err = regulator_enable(jdi->ddi_supply); + if (err < 0) { + dev_err(panel->dev, "failed to enable ddi_supply: %d\n", err); + goto supply_off; + } + /* T2 = 1ms */ + usleep_range(1000, 3000); + + gpiod_set_value(jdi->enable_gpio, 1); + /* T3 = 10ms */ + usleep_range(10000, 15000); + + gpiod_set_value(jdi->reset_gpio, 0); + /* Specified by JDI @ 3ms, subject to change */ + usleep_range(3000, 5000); + + /* + * TODO: The device supports both left-right and even-odd split + * configurations, but this driver currently supports only the left- + * right split. To support a different mode a mechanism needs to be + * put in place to communicate the configuration back to the DSI host + * controller. + */ + err = jdi_setup_symmetrical_split(jdi->link1, jdi->link2, + jdi->mode); + if (err < 0) { + dev_err(panel->dev, "failed to set up symmetrical split: %d\n", + err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_tear_scanline(jdi->link1, + jdi->mode->vdisplay - 16); + if (err < 0) { + dev_err(panel->dev, "failed to set tear scanline: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_tear_scanline(jdi->link2, + jdi->mode->vdisplay - 16); + if (err < 0) { + dev_err(panel->dev, "failed to set tear scanline: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_tear_on(jdi->link1, + MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (err < 0) { + dev_err(panel->dev, "failed to set tear on: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_tear_on(jdi->link2, + MIPI_DSI_DCS_TEAR_MODE_VBLANK); + if (err < 0) { + dev_err(panel->dev, "failed to set tear on: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_pixel_format(jdi->link1, MIPI_DCS_PIXEL_FMT_24BIT); + if (err < 0) { + dev_err(panel->dev, "failed to set pixel format: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_pixel_format(jdi->link2, MIPI_DCS_PIXEL_FMT_24BIT); + if (err < 0) { + dev_err(panel->dev, "failed to set pixel format: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_exit_sleep_mode(jdi->link1); + if (err < 0) { + dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_exit_sleep_mode(jdi->link2); + if (err < 0) { + dev_err(panel->dev, "failed to exit sleep mode: %d\n", err); + goto poweroff; + } + + err = jdi_write_dcdc_registers(jdi); + if (err < 0) { + dev_err(panel->dev, "failed to write dcdc registers: %d\n", err); + goto poweroff; + } + /* + * We need to wait 150ms between mipi_dsi_dcs_exit_sleep_mode() and + * mipi_dsi_dcs_set_display_on(). + */ + msleep(150); + + err = mipi_dsi_dcs_set_display_on(jdi->link1); + if (err < 0) { + dev_err(panel->dev, "failed to set display on: %d\n", err); + goto poweroff; + } + + err = mipi_dsi_dcs_set_display_on(jdi->link2); + if (err < 0) { + dev_err(panel->dev, "failed to set display on: %d\n", err); + goto poweroff; + } + + jdi->link1->mode_flags &= ~MIPI_DSI_MODE_LPM; + jdi->link2->mode_flags &= ~MIPI_DSI_MODE_LPM; + + return 0; + +poweroff: + regulator_disable(jdi->ddi_supply); + + /* T6 = 2ms plus some time to discharge capacitors */ + usleep_range(7000, 9000); +supply_off: + regulator_disable(jdi->supply); + /* Specified by JDI @ 20ms, subject to change */ + msleep(20); + + return err; +} + +static int jdi_panel_enable(struct drm_panel *panel) +{ + struct jdi_panel *jdi = to_panel_jdi(panel); + + /* + * Ensure we send image data before turning the backlight + * on, to avoid the display showing random pixels. + */ + jdi_wait_frames(jdi, 3); + + backlight_enable(jdi->backlight); + + return 0; +} + +static const struct drm_display_mode default_mode = { + .clock = (2560 + 80 + 80 + 80) * (1800 + 4 + 4 + 4) * 60 / 1000, + .hdisplay = 2560, + .hsync_start = 2560 + 80, + .hsync_end = 2560 + 80 + 80, + .htotal = 2560 + 80 + 80 + 80, + .vdisplay = 1800, + .vsync_start = 1800 + 4, + .vsync_end = 1800 + 4 + 4, + .vtotal = 1800 + 4 + 4 + 4, + .flags = 0, +}; + +static int jdi_panel_get_modes(struct drm_panel *panel, + struct drm_connector *connector) +{ + struct drm_display_mode *mode; + struct jdi_panel *jdi = to_panel_jdi(panel); + struct device *dev = &jdi->link1->dev; + + mode = drm_mode_duplicate(connector->dev, &default_mode); + if (!mode) { + dev_err(dev, "failed to add mode %ux%ux@%u\n", + default_mode.hdisplay, default_mode.vdisplay, + drm_mode_vrefresh(&default_mode)); + return -ENOMEM; + } + + drm_mode_set_name(mode); + + drm_mode_probed_add(connector, mode); + + connector->display_info.width_mm = 211; + connector->display_info.height_mm = 148; + connector->display_info.bpc = 8; + + return 1; +} + +static const struct drm_panel_funcs jdi_panel_funcs = { + .prepare = jdi_panel_prepare, + .enable = jdi_panel_enable, + .disable = jdi_panel_disable, + .unprepare = jdi_panel_unprepare, + .get_modes = jdi_panel_get_modes, +}; + +static const struct of_device_id jdi_of_match[] = { + { .compatible = "jdi,lpm102a188a", }, + { } +}; +MODULE_DEVICE_TABLE(of, jdi_of_match); + +static int jdi_panel_add(struct jdi_panel *jdi) +{ + struct device *dev = &jdi->link1->dev; + + jdi->mode = &default_mode; + + jdi->supply = devm_regulator_get(dev, "power"); + if (IS_ERR(jdi->supply)) + return dev_err_probe(dev, PTR_ERR(jdi->supply), + "failed to get power regulator\n"); + + jdi->ddi_supply = devm_regulator_get(dev, "ddi"); + if (IS_ERR(jdi->ddi_supply)) + return dev_err_probe(dev, PTR_ERR(jdi->ddi_supply), + "failed to get ddi regulator\n"); + + jdi->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(jdi->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(jdi->reset_gpio), + "failed to get reset gpio\n"); + /* T4 = 1ms */ + usleep_range(1000, 3000); + + jdi->enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + if (IS_ERR(jdi->enable_gpio)) + return dev_err_probe(dev, PTR_ERR(jdi->enable_gpio), + "failed to get enable gpio\n"); + /* T5 = 2ms */ + usleep_range(2000, 4000); + + jdi->backlight = devm_of_find_backlight(dev); + if (IS_ERR(jdi->backlight)) + return dev_err_probe(dev, PTR_ERR(jdi->backlight), + "failed to create backlight\n"); + + drm_panel_init(&jdi->base, &jdi->link1->dev, &jdi_panel_funcs, + DRM_MODE_CONNECTOR_DSI); + + drm_panel_add(&jdi->base); + + return 0; +} + +static void jdi_panel_del(struct jdi_panel *jdi) +{ + if (jdi->base.dev) + drm_panel_remove(&jdi->base); + + if (jdi->link2) + put_device(&jdi->link2->dev); +} + +static int jdi_panel_dsi_probe(struct mipi_dsi_device *dsi) +{ + struct mipi_dsi_device *secondary = NULL; + struct jdi_panel *jdi; + struct device_node *np; + int err; + + dsi->lanes = 4; + dsi->format = MIPI_DSI_FMT_RGB888; + dsi->mode_flags = 0; + + /* Find DSI-LINK1 */ + np = of_parse_phandle(dsi->dev.of_node, "link2", 0); + if (np) { + secondary = of_find_mipi_dsi_device_by_node(np); + of_node_put(np); + + if (!secondary) + return -EPROBE_DEFER; + } + + /* register a panel for only the DSI-LINK1 interface */ + if (secondary) { + jdi = devm_kzalloc(&dsi->dev, sizeof(*jdi), GFP_KERNEL); + if (!jdi) { + put_device(&secondary->dev); + return -ENOMEM; + } + + mipi_dsi_set_drvdata(dsi, jdi); + + jdi->link1 = dsi; + jdi->link2 = secondary; + + err = jdi_panel_add(jdi); + if (err < 0) { + put_device(&secondary->dev); + return err; + } + } + + err = mipi_dsi_attach(dsi); + if (err < 0) { + if (secondary) + jdi_panel_del(jdi); + + return err; + } + + return 0; +} + +static void jdi_panel_dsi_remove(struct mipi_dsi_device *dsi) +{ + struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); + int err; + + /* only detach from host for the DSI-LINK2 interface */ + if (!jdi) + mipi_dsi_detach(dsi); + + err = jdi_panel_disable(&jdi->base); + if (err < 0) + dev_err(&dsi->dev, "failed to disable panel: %d\n", err); + + err = mipi_dsi_detach(dsi); + if (err < 0) + dev_err(&dsi->dev, "failed to detach from DSI host: %d\n", err); + + jdi_panel_del(jdi); +} + +static void jdi_panel_dsi_shutdown(struct mipi_dsi_device *dsi) +{ + struct jdi_panel *jdi = mipi_dsi_get_drvdata(dsi); + + if (!jdi) + return; + + jdi_panel_disable(&jdi->base); +} + +static struct mipi_dsi_driver jdi_panel_dsi_driver = { + .driver = { + .name = "panel-jdi-lpm102a188a", + .of_match_table = jdi_of_match, + }, + .probe = jdi_panel_dsi_probe, + .remove = jdi_panel_dsi_remove, + .shutdown = jdi_panel_dsi_shutdown, +}; +module_mipi_dsi_driver(jdi_panel_dsi_driver); + +MODULE_AUTHOR("Sean Paul <seanpaul@chromium.org>"); +MODULE_AUTHOR("Diogo Ivo <diogo.ivo@tecnico.ulisboa.pt>"); +MODULE_DESCRIPTION("DRM Driver for JDI LPM102A188A DSI panel, command mode"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c index 213008499caa..f9a69f347068 100644 --- a/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c +++ b/drivers/gpu/drm/panel/panel-jdi-lt070me05000.c @@ -5,10 +5,6 @@ * * Copyright (C) 2016 Linaro Ltd * Author: Sumit Semwal <sumit.semwal@linaro.org> - * - * From internet archives, the panel for Nexus 7 2nd Gen, 2013 model is a - * JDI model LT070ME05000, and its data sheet is at: - * http://panelone.net/en/7-0-inch/JDI_LT070ME05000_7.0_inch-datasheet */ #include <linux/backlight.h> diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c index 412ca84d0581..648ce9201426 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c @@ -59,7 +59,6 @@ struct nt35950 { int cur_mode; u8 last_page; - bool prepared; }; struct nt35950_panel_mode { @@ -431,9 +430,6 @@ static int nt35950_prepare(struct drm_panel *panel) struct device *dev = &nt->dsi[0]->dev; int ret; - if (nt->prepared) - return 0; - ret = regulator_enable(nt->vregs[0].consumer); if (ret) return ret; @@ -460,7 +456,6 @@ static int nt35950_prepare(struct drm_panel *panel) dev_err(dev, "Failed to initialize panel: %d\n", ret); goto end; } - nt->prepared = true; end: if (ret < 0) { @@ -477,9 +472,6 @@ static int nt35950_unprepare(struct drm_panel *panel) struct device *dev = &nt->dsi[0]->dev; int ret; - if (!nt->prepared) - return 0; - ret = nt35950_off(nt); if (ret < 0) dev_err(dev, "Failed to deinitialize panel: %d\n", ret); @@ -487,7 +479,6 @@ static int nt35950_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(nt->reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(nt->vregs), nt->vregs); - nt->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-novatek-nt36523.c b/drivers/gpu/drm/panel/panel-novatek-nt36523.c index 9632b9e95b71..9b9a7eb1bc60 100644 --- a/drivers/gpu/drm/panel/panel-novatek-nt36523.c +++ b/drivers/gpu/drm/panel/panel-novatek-nt36523.c @@ -38,8 +38,6 @@ struct panel_info { struct gpio_desc *reset_gpio; struct backlight_device *backlight; struct regulator *vddio; - - bool prepared; }; struct panel_desc { @@ -1046,9 +1044,6 @@ static int nt36523_prepare(struct drm_panel *panel) struct panel_info *pinfo = to_panel_info(panel); int ret; - if (pinfo->prepared) - return 0; - ret = regulator_enable(pinfo->vddio); if (ret) { dev_err(panel->dev, "failed to enable vddio regulator: %d\n", ret); @@ -1064,8 +1059,6 @@ static int nt36523_prepare(struct drm_panel *panel) return ret; } - pinfo->prepared = true; - return 0; } @@ -1095,14 +1088,9 @@ static int nt36523_unprepare(struct drm_panel *panel) { struct panel_info *pinfo = to_panel_info(panel); - if (!pinfo->prepared) - return 0; - gpiod_set_value_cansleep(pinfo->reset_gpio, 1); regulator_disable(pinfo->vddio); - pinfo->prepared = false; - return 0; } diff --git a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c index 898b892f1143..93183f30d7d6 100644 --- a/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c +++ b/drivers/gpu/drm/panel/panel-orisetech-otm8009a.c @@ -70,7 +70,6 @@ struct otm8009a { struct gpio_desc *reset_gpio; struct regulator *supply; bool prepared; - bool enabled; }; static const struct drm_display_mode modes[] = { @@ -267,9 +266,6 @@ static int otm8009a_disable(struct drm_panel *panel) struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; - if (!ctx->enabled) - return 0; /* This is not an issue so we return 0 here */ - backlight_disable(ctx->bl_dev); ret = mipi_dsi_dcs_set_display_off(dsi); @@ -282,8 +278,6 @@ static int otm8009a_disable(struct drm_panel *panel) msleep(120); - ctx->enabled = false; - return 0; } @@ -291,9 +285,6 @@ static int otm8009a_unprepare(struct drm_panel *panel) { struct otm8009a *ctx = panel_to_otm8009a(panel); - if (!ctx->prepared) - return 0; - if (ctx->reset_gpio) { gpiod_set_value_cansleep(ctx->reset_gpio, 1); msleep(20); @@ -311,9 +302,6 @@ static int otm8009a_prepare(struct drm_panel *panel) struct otm8009a *ctx = panel_to_otm8009a(panel); int ret; - if (ctx->prepared) - return 0; - ret = regulator_enable(ctx->supply); if (ret < 0) { dev_err(panel->dev, "failed to enable supply: %d\n", ret); @@ -341,13 +329,8 @@ static int otm8009a_enable(struct drm_panel *panel) { struct otm8009a *ctx = panel_to_otm8009a(panel); - if (ctx->enabled) - return 0; - backlight_enable(ctx->bl_dev); - ctx->enabled = true; - return 0; } diff --git a/drivers/gpu/drm/panel/panel-raydium-rm68200.c b/drivers/gpu/drm/panel/panel-raydium-rm68200.c index 5f9b340588fb..7b7fe987e292 100644 --- a/drivers/gpu/drm/panel/panel-raydium-rm68200.c +++ b/drivers/gpu/drm/panel/panel-raydium-rm68200.c @@ -77,8 +77,6 @@ struct rm68200 { struct drm_panel panel; struct gpio_desc *reset_gpio; struct regulator *supply; - bool prepared; - bool enabled; }; static const struct drm_display_mode default_mode = { @@ -231,27 +229,12 @@ static void rm68200_init_sequence(struct rm68200 *ctx) dcs_write_seq(ctx, MCS_CMD_MODE_SW, MCS_CMD1_UCS); } -static int rm68200_disable(struct drm_panel *panel) -{ - struct rm68200 *ctx = panel_to_rm68200(panel); - - if (!ctx->enabled) - return 0; - - ctx->enabled = false; - - return 0; -} - static int rm68200_unprepare(struct drm_panel *panel) { struct rm68200 *ctx = panel_to_rm68200(panel); struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; - if (!ctx->prepared) - return 0; - ret = mipi_dsi_dcs_set_display_off(dsi); if (ret) dev_warn(panel->dev, "failed to set display off: %d\n", ret); @@ -269,8 +252,6 @@ static int rm68200_unprepare(struct drm_panel *panel) regulator_disable(ctx->supply); - ctx->prepared = false; - return 0; } @@ -280,9 +261,6 @@ static int rm68200_prepare(struct drm_panel *panel) struct mipi_dsi_device *dsi = to_mipi_dsi_device(ctx->dev); int ret; - if (ctx->prepared) - return 0; - ret = regulator_enable(ctx->supply); if (ret < 0) { dev_err(ctx->dev, "failed to enable supply: %d\n", ret); @@ -310,20 +288,6 @@ static int rm68200_prepare(struct drm_panel *panel) msleep(20); - ctx->prepared = true; - - return 0; -} - -static int rm68200_enable(struct drm_panel *panel) -{ - struct rm68200 *ctx = panel_to_rm68200(panel); - - if (ctx->enabled) - return 0; - - ctx->enabled = true; - return 0; } @@ -352,10 +316,8 @@ static int rm68200_get_modes(struct drm_panel *panel, } static const struct drm_panel_funcs rm68200_drm_funcs = { - .disable = rm68200_disable, .unprepare = rm68200_unprepare, .prepare = rm68200_prepare, - .enable = rm68200_enable, .get_modes = rm68200_get_modes, }; diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c index b34fa4d5de07..a0e5698275a5 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e63m0.c @@ -270,9 +270,6 @@ struct s6e63m0 { struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; - bool prepared; - bool enabled; - /* * This field is tested by functions directly accessing bus before * transfer, transfer is skipped if it is set. In case of transfer @@ -502,9 +499,6 @@ static int s6e63m0_disable(struct drm_panel *panel) { struct s6e63m0 *ctx = panel_to_s6e63m0(panel); - if (!ctx->enabled) - return 0; - backlight_disable(ctx->bl_dev); s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_OFF); @@ -512,8 +506,6 @@ static int s6e63m0_disable(struct drm_panel *panel) s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_ENTER_SLEEP_MODE); msleep(120); - ctx->enabled = false; - return 0; } @@ -522,17 +514,12 @@ static int s6e63m0_unprepare(struct drm_panel *panel) struct s6e63m0 *ctx = panel_to_s6e63m0(panel); int ret; - if (!ctx->prepared) - return 0; - s6e63m0_clear_error(ctx); ret = s6e63m0_power_off(ctx); if (ret < 0) return ret; - ctx->prepared = false; - return 0; } @@ -541,9 +528,6 @@ static int s6e63m0_prepare(struct drm_panel *panel) struct s6e63m0 *ctx = panel_to_s6e63m0(panel); int ret; - if (ctx->prepared) - return 0; - ret = s6e63m0_power_on(ctx); if (ret < 0) return ret; @@ -564,8 +548,6 @@ static int s6e63m0_prepare(struct drm_panel *panel) if (ret < 0) s6e63m0_unprepare(panel); - ctx->prepared = true; - return ret; } @@ -573,9 +555,6 @@ static int s6e63m0_enable(struct drm_panel *panel) { struct s6e63m0 *ctx = panel_to_s6e63m0(panel); - if (ctx->enabled) - return 0; - s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_EXIT_SLEEP_MODE); msleep(120); s6e63m0_dcs_write_seq_static(ctx, MIPI_DCS_SET_DISPLAY_ON); @@ -588,8 +567,6 @@ static int s6e63m0_enable(struct drm_panel *panel) backlight_enable(ctx->bl_dev); - ctx->enabled = true; - return 0; } @@ -709,8 +686,6 @@ int s6e63m0_probe(struct device *dev, void *trsp, dev_set_drvdata(dev, ctx); ctx->dev = dev; - ctx->enabled = false; - ctx->prepared = false; ret = device_property_read_u32(dev, "max-brightness", &max_brightness); if (ret) diff --git a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c index 7431cae7427e..d2df227abbea 100644 --- a/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c +++ b/drivers/gpu/drm/panel/panel-samsung-s6e88a0-ams452ef01.c @@ -18,8 +18,6 @@ struct s6e88a0_ams452ef01 { struct mipi_dsi_device *dsi; struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; - - bool prepared; }; static inline struct @@ -115,9 +113,6 @@ static int s6e88a0_ams452ef01_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) { dev_err(dev, "Failed to enable regulators: %d\n", ret); @@ -135,7 +130,6 @@ static int s6e88a0_ams452ef01_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -145,9 +139,6 @@ static int s6e88a0_ams452ef01_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = s6e88a0_ams452ef01_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -155,7 +146,6 @@ static int s6e88a0_ams452ef01_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(ctx->reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-samsung-sofef00.c b/drivers/gpu/drm/panel/panel-samsung-sofef00.c index cbf9607dd576..04ce925b3d9d 100644 --- a/drivers/gpu/drm/panel/panel-samsung-sofef00.c +++ b/drivers/gpu/drm/panel/panel-samsung-sofef00.c @@ -23,7 +23,6 @@ struct sofef00_panel { struct regulator *supply; struct gpio_desc *reset_gpio; const struct drm_display_mode *mode; - bool prepared; }; static inline @@ -113,9 +112,6 @@ static int sofef00_panel_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_enable(ctx->supply); if (ret < 0) { dev_err(dev, "Failed to enable regulator: %d\n", ret); @@ -131,7 +127,6 @@ static int sofef00_panel_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -141,16 +136,12 @@ static int sofef00_panel_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = sofef00_panel_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); regulator_disable(ctx->supply); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c index 68f52eaaf4fa..74c760ee0c2d 100644 --- a/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c +++ b/drivers/gpu/drm/panel/panel-sharp-ls060t1sx01.c @@ -24,7 +24,6 @@ struct sharp_ls060 { struct regulator *avdd_supply; struct regulator *avee_supply; struct gpio_desc *reset_gpio; - bool prepared; }; static inline struct sharp_ls060 *to_sharp_ls060(struct drm_panel *panel) @@ -101,9 +100,6 @@ static int sharp_ls060_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_enable(ctx->vddi_supply); if (ret < 0) return ret; @@ -134,8 +130,6 @@ static int sharp_ls060_prepare(struct drm_panel *panel) goto err_on; } - ctx->prepared = true; - return 0; err_on: @@ -163,9 +157,6 @@ static int sharp_ls060_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = sharp_ls060_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -181,7 +172,6 @@ static int sharp_ls060_unprepare(struct drm_panel *panel) regulator_disable(ctx->vddi_supply); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 95959dcc6e0e..857bc01591db 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -2793,6 +2793,32 @@ static const struct panel_desc mitsubishi_aa070mc01 = { .bus_flags = DRM_BUS_FLAG_DE_HIGH, }; +static const struct drm_display_mode mitsubishi_aa084xe01_mode = { + .clock = 56234, + .hdisplay = 1024, + .hsync_start = 1024 + 24, + .hsync_end = 1024 + 24 + 63, + .htotal = 1024 + 24 + 63 + 1, + .vdisplay = 768, + .vsync_start = 768 + 3, + .vsync_end = 768 + 3 + 6, + .vtotal = 768 + 3 + 6 + 1, + .flags = DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC, +}; + +static const struct panel_desc mitsubishi_aa084xe01 = { + .modes = &mitsubishi_aa084xe01_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 1024, + .height = 768, + }, + .bus_format = MEDIA_BUS_FMT_RGB565_1X16, + .connector_type = DRM_MODE_CONNECTOR_DPI, + .bus_flags = DRM_BUS_FLAG_DE_HIGH | DRM_BUS_FLAG_PIXDATA_SAMPLE_NEGEDGE, +}; + static const struct display_timing multi_inno_mi0700s4t_6_timing = { .pixelclock = { 29000000, 33000000, 38000000 }, .hactive = { 800, 800, 800 }, @@ -4322,6 +4348,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "mitsubishi,aa070mc01-ca1", .data = &mitsubishi_aa070mc01, }, { + .compatible = "mitsubishi,aa084xe01", + .data = &mitsubishi_aa084xe01, + }, { .compatible = "multi-inno,mi0700s4t-6", .data = &multi_inno_mi0700s4t_6, }, { diff --git a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c index 1bde2f01786b..472195d4bbbe 100644 --- a/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c +++ b/drivers/gpu/drm/panel/panel-sony-td4353-jdi.c @@ -36,7 +36,6 @@ struct sony_td4353_jdi { struct regulator_bulk_data supplies[3]; struct gpio_desc *panel_reset_gpio; struct gpio_desc *touch_reset_gpio; - bool prepared; int type; }; @@ -150,9 +149,6 @@ static int sony_td4353_jdi_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) { dev_err(dev, "Failed to enable regulators: %d\n", ret); @@ -171,7 +167,6 @@ static int sony_td4353_jdi_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -181,9 +176,6 @@ static int sony_td4353_jdi_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = sony_td4353_jdi_off(ctx); if (ret < 0) dev_err(dev, "Failed to power off panel: %d\n", ret); @@ -191,7 +183,6 @@ static int sony_td4353_jdi_unprepare(struct drm_panel *panel) sony_td4353_assert_reset_gpios(ctx, 0); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c index ee5d20ecc577..6d44970dccd9 100644 --- a/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c +++ b/drivers/gpu/drm/panel/panel-sony-tulip-truly-nt35521.c @@ -23,8 +23,6 @@ struct truly_nt35521 { struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; struct gpio_desc *blen_gpio; - bool prepared; - bool enabled; }; static inline @@ -296,9 +294,6 @@ static int truly_nt35521_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) { dev_err(dev, "Failed to enable regulators: %d\n", ret); @@ -314,7 +309,6 @@ static int truly_nt35521_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -324,9 +318,6 @@ static int truly_nt35521_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = truly_nt35521_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -335,7 +326,6 @@ static int truly_nt35521_unprepare(struct drm_panel *panel) regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - ctx->prepared = false; return 0; } @@ -343,12 +333,8 @@ static int truly_nt35521_enable(struct drm_panel *panel) { struct truly_nt35521 *ctx = to_truly_nt35521(panel); - if (ctx->enabled) - return 0; - gpiod_set_value_cansleep(ctx->blen_gpio, 1); - ctx->enabled = true; return 0; } @@ -356,12 +342,8 @@ static int truly_nt35521_disable(struct drm_panel *panel) { struct truly_nt35521 *ctx = to_truly_nt35521(panel); - if (!ctx->enabled) - return 0; - gpiod_set_value_cansleep(ctx->blen_gpio, 0); - ctx->enabled = false; return 0; } diff --git a/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c b/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c index 6e77a2d71d81..0156689f41cd 100644 --- a/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c +++ b/drivers/gpu/drm/panel/panel-startek-kd070fhfid015.c @@ -35,7 +35,6 @@ enum { }; struct stk_panel { - bool prepared; const struct drm_display_mode *mode; struct backlight_device *backlight; struct drm_panel base; @@ -145,16 +144,11 @@ static int stk_panel_unprepare(struct drm_panel *panel) { struct stk_panel *stk = to_stk_panel(panel); - if (!stk->prepared) - return 0; - stk_panel_off(stk); regulator_bulk_disable(ARRAY_SIZE(stk->supplies), stk->supplies); gpiod_set_value(stk->reset_gpio, 0); gpiod_set_value(stk->enable_gpio, 1); - stk->prepared = false; - return 0; } @@ -164,9 +158,6 @@ static int stk_panel_prepare(struct drm_panel *panel) struct device *dev = &stk->dsi->dev; int ret; - if (stk->prepared) - return 0; - gpiod_set_value(stk->reset_gpio, 0); gpiod_set_value(stk->enable_gpio, 0); ret = regulator_enable(stk->supplies[IOVCC].consumer); @@ -195,8 +186,6 @@ static int stk_panel_prepare(struct drm_panel *panel) goto poweroff; } - stk->prepared = true; - return 0; poweroff: diff --git a/drivers/gpu/drm/panel/panel-truly-nt35597.c b/drivers/gpu/drm/panel/panel-truly-nt35597.c index 4f4009f9fe25..b73448cf349d 100644 --- a/drivers/gpu/drm/panel/panel-truly-nt35597.c +++ b/drivers/gpu/drm/panel/panel-truly-nt35597.c @@ -64,8 +64,6 @@ struct truly_nt35597 { struct mipi_dsi_device *dsi[2]; const struct nt35597_config *config; - bool prepared; - bool enabled; }; static inline struct truly_nt35597 *panel_to_ctx(struct drm_panel *panel) @@ -313,16 +311,12 @@ static int truly_nt35597_disable(struct drm_panel *panel) struct truly_nt35597 *ctx = panel_to_ctx(panel); int ret; - if (!ctx->enabled) - return 0; - if (ctx->backlight) { ret = backlight_disable(ctx->backlight); if (ret < 0) dev_err(ctx->dev, "backlight disable failed %d\n", ret); } - ctx->enabled = false; return 0; } @@ -331,9 +325,6 @@ static int truly_nt35597_unprepare(struct drm_panel *panel) struct truly_nt35597 *ctx = panel_to_ctx(panel); int ret = 0; - if (!ctx->prepared) - return 0; - ctx->dsi[0]->mode_flags = 0; ctx->dsi[1]->mode_flags = 0; @@ -354,7 +345,6 @@ static int truly_nt35597_unprepare(struct drm_panel *panel) if (ret < 0) dev_err(ctx->dev, "power_off failed ret = %d\n", ret); - ctx->prepared = false; return ret; } @@ -367,9 +357,6 @@ static int truly_nt35597_prepare(struct drm_panel *panel) const struct nt35597_config *config; u32 num_cmds; - if (ctx->prepared) - return 0; - ret = truly_35597_power_on(ctx); if (ret < 0) return ret; @@ -409,8 +396,6 @@ static int truly_nt35597_prepare(struct drm_panel *panel) /* Per DSI spec wait 120ms after sending set_display_on DCS command */ msleep(120); - ctx->prepared = true; - return 0; power_off: @@ -424,17 +409,12 @@ static int truly_nt35597_enable(struct drm_panel *panel) struct truly_nt35597 *ctx = panel_to_ctx(panel); int ret; - if (ctx->enabled) - return 0; - if (ctx->backlight) { ret = backlight_enable(ctx->backlight); if (ret < 0) dev_err(ctx->dev, "backlight enable failed %d\n", ret); } - ctx->enabled = true; - return 0; } diff --git a/drivers/gpu/drm/panel/panel-visionox-r66451.c b/drivers/gpu/drm/panel/panel-visionox-r66451.c index 00fc28ad3d07..fbb73464de33 100644 --- a/drivers/gpu/drm/panel/panel-visionox-r66451.c +++ b/drivers/gpu/drm/panel/panel-visionox-r66451.c @@ -22,7 +22,6 @@ struct visionox_r66451 { struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct regulator_bulk_data supplies[2]; - bool prepared, enabled; }; static inline struct visionox_r66451 *to_visionox_r66451(struct drm_panel *panel) @@ -124,9 +123,6 @@ static int visionox_r66451_prepare(struct drm_panel *panel) struct device *dev = &dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) @@ -144,7 +140,6 @@ static int visionox_r66451_prepare(struct drm_panel *panel) mipi_dsi_compression_mode(ctx->dsi, true); - ctx->prepared = true; return 0; } @@ -154,9 +149,6 @@ static int visionox_r66451_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = visionox_r66451_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -164,7 +156,6 @@ static int visionox_r66451_unprepare(struct drm_panel *panel) gpiod_set_value_cansleep(ctx->reset_gpio, 1); regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - ctx->prepared = false; return 0; } @@ -190,9 +181,6 @@ static int visionox_r66451_enable(struct drm_panel *panel) struct drm_dsc_picture_parameter_set pps; int ret; - if (ctx->enabled) - return 0; - if (!dsi->dsc) { dev_err(&dsi->dev, "DSC not attached to DSI\n"); return -ENODEV; @@ -219,8 +207,6 @@ static int visionox_r66451_enable(struct drm_panel *panel) } msleep(20); - ctx->enabled = true; - return 0; } @@ -231,8 +217,6 @@ static int visionox_r66451_disable(struct drm_panel *panel) struct device *dev = &dsi->dev; int ret; - ctx->enabled = false; - ret = mipi_dsi_dcs_set_display_off(dsi); if (ret < 0) { dev_err(dev, "Failed to set display off: %d\n", ret); diff --git a/drivers/gpu/drm/panel/panel-visionox-rm69299.c b/drivers/gpu/drm/panel/panel-visionox-rm69299.c index c2806e4fd553..775144695283 100644 --- a/drivers/gpu/drm/panel/panel-visionox-rm69299.c +++ b/drivers/gpu/drm/panel/panel-visionox-rm69299.c @@ -20,8 +20,6 @@ struct visionox_rm69299 { struct regulator_bulk_data supplies[2]; struct gpio_desc *reset_gpio; struct mipi_dsi_device *dsi; - bool prepared; - bool enabled; }; static inline struct visionox_rm69299 *panel_to_ctx(struct drm_panel *panel) @@ -80,7 +78,6 @@ static int visionox_rm69299_unprepare(struct drm_panel *panel) ret = visionox_rm69299_power_off(ctx); - ctx->prepared = false; return ret; } @@ -89,9 +86,6 @@ static int visionox_rm69299_prepare(struct drm_panel *panel) struct visionox_rm69299 *ctx = panel_to_ctx(panel); int ret; - if (ctx->prepared) - return 0; - ret = visionox_rm69299_power_on(ctx); if (ret < 0) return ret; @@ -140,8 +134,6 @@ static int visionox_rm69299_prepare(struct drm_panel *panel) /* Per DSI spec wait 120ms after sending set_display_on DCS command */ msleep(120); - ctx->prepared = true; - return 0; power_off: diff --git a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c index bb0dfd86ea67..a23407b9f6fb 100644 --- a/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c +++ b/drivers/gpu/drm/panel/panel-visionox-vtdr6130.c @@ -20,7 +20,6 @@ struct visionox_vtdr6130 { struct mipi_dsi_device *dsi; struct gpio_desc *reset_gpio; struct regulator_bulk_data supplies[3]; - bool prepared; }; static inline struct visionox_vtdr6130 *to_visionox_vtdr6130(struct drm_panel *panel) @@ -157,9 +156,6 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (ctx->prepared) - return 0; - ret = regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); if (ret < 0) @@ -175,7 +171,6 @@ static int visionox_vtdr6130_prepare(struct drm_panel *panel) return ret; } - ctx->prepared = true; return 0; } @@ -185,9 +180,6 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel) struct device *dev = &ctx->dsi->dev; int ret; - if (!ctx->prepared) - return 0; - ret = visionox_vtdr6130_off(ctx); if (ret < 0) dev_err(dev, "Failed to un-initialize panel: %d\n", ret); @@ -196,7 +188,6 @@ static int visionox_vtdr6130_unprepare(struct drm_panel *panel) regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); - ctx->prepared = false; return 0; } diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c index d28b99732dde..2faa344d89ee 100644 --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c @@ -390,8 +390,8 @@ int panfrost_gpu_init(struct panfrost_device *pfdev) dma_set_max_seg_size(pfdev->dev, UINT_MAX); irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu"); - if (irq <= 0) - return -ENODEV; + if (irq < 0) + return irq; err = devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler, IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev); diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c index a8b4827dc425..033f5e684707 100644 --- a/drivers/gpu/drm/panfrost/panfrost_job.c +++ b/drivers/gpu/drm/panfrost/panfrost_job.c @@ -810,8 +810,8 @@ int panfrost_job_init(struct panfrost_device *pfdev) spin_lock_init(&js->job_lock); js->irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "job"); - if (js->irq <= 0) - return -ENODEV; + if (js->irq < 0) + return js->irq; ret = devm_request_threaded_irq(pfdev->dev, js->irq, panfrost_job_irq_handler, diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c index c0123d09f699..d54d4e7b2195 100644 --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c @@ -755,8 +755,8 @@ int panfrost_mmu_init(struct panfrost_device *pfdev) int err, irq; irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu"); - if (irq <= 0) - return -ENODEV; + if (irq < 0) + return irq; err = devm_request_threaded_irq(pfdev->dev, irq, panfrost_mmu_irq_handler, diff --git a/drivers/gpu/drm/pl111/pl111_drv.c b/drivers/gpu/drm/pl111/pl111_drv.c index ba3b5b5f0cdf..02e6b74d5016 100644 --- a/drivers/gpu/drm/pl111/pl111_drv.c +++ b/drivers/gpu/drm/pl111/pl111_drv.c @@ -323,12 +323,18 @@ static void pl111_amba_remove(struct amba_device *amba_dev) struct pl111_drm_dev_private *priv = drm->dev_private; drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); if (priv->panel) drm_panel_bridge_remove(priv->bridge); drm_dev_put(drm); of_reserved_mem_device_release(dev); } +static void pl111_amba_shutdown(struct amba_device *amba_dev) +{ + drm_atomic_helper_shutdown(amba_get_drvdata(amba_dev)); +} + /* * This early variant lacks the 565 and 444 pixel formats. */ @@ -431,6 +437,7 @@ static struct amba_driver pl111_amba_driver __maybe_unused = { }, .probe = pl111_amba_probe, .remove = pl111_amba_remove, + .shutdown = pl111_amba_shutdown, .id_table = pl111_id_table, }; diff --git a/drivers/gpu/drm/qxl/qxl_drv.c b/drivers/gpu/drm/qxl/qxl_drv.c index b30ede1cf62d..a4144c62ca93 100644 --- a/drivers/gpu/drm/qxl/qxl_drv.c +++ b/drivers/gpu/drm/qxl/qxl_drv.c @@ -163,6 +163,12 @@ qxl_pci_remove(struct pci_dev *pdev) vga_put(pdev, VGA_RSRC_LEGACY_IO); } +static void +qxl_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + DEFINE_DRM_GEM_FOPS(qxl_fops); static int qxl_drm_freeze(struct drm_device *dev) @@ -269,6 +275,7 @@ static struct pci_driver qxl_pci_driver = { .id_table = pciidlist, .probe = qxl_pci_probe, .remove = qxl_pci_remove, + .shutdown = qxl_pci_shutdown, .driver.pm = &qxl_pm_ops, }; diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index c87a57c9c592..22dd8b445685 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -123,7 +123,7 @@ int radeon_sa_bo_new(struct radeon_sa_manager *sa_manager, unsigned int size, unsigned int align) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, true, align); + GFP_KERNEL, false, align); if (IS_ERR(sa)) { *sa_bo = NULL; diff --git a/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c b/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c index 30493ce87419..e5db4e0095ba 100644 --- a/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c +++ b/drivers/gpu/drm/renesas/shmobile/shmob_drm_drv.c @@ -172,7 +172,7 @@ static DEFINE_SIMPLE_DEV_PM_OPS(shmob_drm_pm_ops, * Platform driver */ -static int shmob_drm_remove(struct platform_device *pdev) +static void shmob_drm_remove(struct platform_device *pdev) { struct shmob_drm_device *sdev = platform_get_drvdata(pdev); struct drm_device *ddev = sdev->ddev; @@ -181,8 +181,6 @@ static int shmob_drm_remove(struct platform_device *pdev) drm_kms_helper_poll_fini(ddev); free_irq(sdev->irq, ddev); drm_dev_put(ddev); - - return 0; } static int shmob_drm_probe(struct platform_device *pdev) @@ -288,7 +286,7 @@ err_free_drm_dev: static struct platform_driver shmob_drm_platform_driver = { .probe = shmob_drm_probe, - .remove = shmob_drm_remove, + .remove_new = shmob_drm_remove, .driver = { .name = "shmob-drm", .pm = pm_sleep_ptr(&shmob_drm_pm_ops), diff --git a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c index 0100162a73b2..8bafb2a2747f 100644 --- a/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw-mipi-dsi-rockchip.c @@ -198,6 +198,11 @@ #define RK3568_DSI1_TURNDISABLE BIT(2) #define RK3568_DSI1_FORCERXMODE BIT(0) +#define RV1126_GRF_DSIPHY_CON 0x10220 +#define RV1126_DSI_FORCETXSTOPMODE (0xf << 4) +#define RV1126_DSI_TURNDISABLE BIT(2) +#define RV1126_DSI_FORCERXMODE BIT(0) + #define HIWORD_UPDATE(val, mask) (val | (mask) << 16) enum { @@ -1651,6 +1656,18 @@ static const struct rockchip_dw_dsi_chip_data rk3568_chip_data[] = { { /* sentinel */ } }; +static const struct rockchip_dw_dsi_chip_data rv1126_chip_data[] = { + { + .reg = 0xffb30000, + .lanecfg1_grf_reg = RV1126_GRF_DSIPHY_CON, + .lanecfg1 = HIWORD_UPDATE(0, RV1126_DSI_TURNDISABLE | + RV1126_DSI_FORCERXMODE | + RV1126_DSI_FORCETXSTOPMODE), + .max_data_lanes = 4, + }, + { /* sentinel */ } +}; + static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = { { .compatible = "rockchip,px30-mipi-dsi", @@ -1664,6 +1681,9 @@ static const struct of_device_id dw_mipi_dsi_rockchip_dt_ids[] = { }, { .compatible = "rockchip,rk3568-mipi-dsi", .data = &rk3568_chip_data, + }, { + .compatible = "rockchip,rv1126-mipi-dsi", + .data = &rv1126_chip_data, }, { /* sentinel */ } }; diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index 14320bc73e5b..e1ea50c8fd39 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -765,11 +765,6 @@ out: } } -static void vop_plane_destroy(struct drm_plane *plane) -{ - drm_plane_cleanup(plane); -} - static inline bool rockchip_afbc(u64 modifier) { return modifier == ROCKCHIP_AFBC_MOD; @@ -1131,7 +1126,7 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = { static const struct drm_plane_funcs vop_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = vop_plane_destroy, + .destroy = drm_plane_cleanup, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, @@ -1602,11 +1597,6 @@ static const struct drm_crtc_helper_funcs vop_crtc_helper_funcs = { .atomic_disable = vop_crtc_atomic_disable, }; -static void vop_crtc_destroy(struct drm_crtc *crtc) -{ - drm_crtc_cleanup(crtc); -} - static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc) { struct rockchip_crtc_state *rockchip_state; @@ -1614,7 +1604,8 @@ static struct drm_crtc_state *vop_crtc_duplicate_state(struct drm_crtc *crtc) if (WARN_ON(!crtc->state)) return NULL; - rockchip_state = kzalloc(sizeof(*rockchip_state), GFP_KERNEL); + rockchip_state = kmemdup(to_rockchip_crtc_state(crtc->state), + sizeof(*rockchip_state), GFP_KERNEL); if (!rockchip_state) return NULL; @@ -1639,7 +1630,10 @@ static void vop_crtc_reset(struct drm_crtc *crtc) if (crtc->state) vop_crtc_destroy_state(crtc, crtc->state); - __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base); + if (crtc_state) + __drm_atomic_helper_crtc_reset(crtc, &crtc_state->base); + else + __drm_atomic_helper_crtc_reset(crtc, NULL); } #ifdef CONFIG_DRM_ANALOGIX_DP @@ -1710,7 +1704,7 @@ vop_crtc_verify_crc_source(struct drm_crtc *crtc, const char *source_name, static const struct drm_crtc_funcs vop_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .destroy = vop_crtc_destroy, + .destroy = drm_crtc_cleanup, .reset = vop_crtc_reset, .atomic_duplicate_state = vop_crtc_duplicate_state, .atomic_destroy_state = vop_crtc_destroy_state, @@ -1961,7 +1955,7 @@ static void vop_destroy_crtc(struct vop *vop) */ list_for_each_entry_safe(plane, tmp, &drm_dev->mode_config.plane_list, head) - vop_plane_destroy(plane); + drm_plane_cleanup(plane); /* * Destroy CRTC after vop_plane_destroy() since vop_disable_plane() diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c index 583df4d22f7e..c306806aa3de 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop2.c @@ -2079,30 +2079,15 @@ static const struct drm_crtc_helper_funcs vop2_crtc_helper_funcs = { .atomic_disable = vop2_crtc_atomic_disable, }; -static void vop2_crtc_reset(struct drm_crtc *crtc) -{ - struct rockchip_crtc_state *vcstate = to_rockchip_crtc_state(crtc->state); - - if (crtc->state) { - __drm_atomic_helper_crtc_destroy_state(crtc->state); - kfree(vcstate); - } - - vcstate = kzalloc(sizeof(*vcstate), GFP_KERNEL); - if (!vcstate) - return; - - crtc->state = &vcstate->base; - crtc->state->crtc = crtc; -} - static struct drm_crtc_state *vop2_crtc_duplicate_state(struct drm_crtc *crtc) { - struct rockchip_crtc_state *vcstate, *old_vcstate; + struct rockchip_crtc_state *vcstate; - old_vcstate = to_rockchip_crtc_state(crtc->state); + if (WARN_ON(!crtc->state)) + return NULL; - vcstate = kmemdup(old_vcstate, sizeof(*old_vcstate), GFP_KERNEL); + vcstate = kmemdup(to_rockchip_crtc_state(crtc->state), + sizeof(*vcstate), GFP_KERNEL); if (!vcstate) return NULL; @@ -2120,6 +2105,20 @@ static void vop2_crtc_destroy_state(struct drm_crtc *crtc, kfree(vcstate); } +static void vop2_crtc_reset(struct drm_crtc *crtc) +{ + struct rockchip_crtc_state *vcstate = + kzalloc(sizeof(*vcstate), GFP_KERNEL); + + if (crtc->state) + vop2_crtc_destroy_state(crtc, crtc->state); + + if (vcstate) + __drm_atomic_helper_crtc_reset(crtc, &vcstate->base); + else + __drm_atomic_helper_crtc_reset(crtc, NULL); +} + static const struct drm_crtc_funcs vop2_crtc_funcs = { .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c index 7b2805006776..d053ef027552 100644 --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c @@ -1120,6 +1120,59 @@ static const struct vop_data rk3328_vop = { .max_output = { 4096, 2160 }, }; +static const struct vop_common rv1126_common = { + .standby = VOP_REG_SYNC(PX30_SYS_CTRL2, 0x1, 1), + .out_mode = VOP_REG(PX30_DSP_CTRL2, 0xf, 16), + .dsp_blank = VOP_REG(PX30_DSP_CTRL2, 0x1, 14), + .dither_down_en = VOP_REG(PX30_DSP_CTRL2, 0x1, 8), + .dither_down_sel = VOP_REG(PX30_DSP_CTRL2, 0x1, 7), + .dither_down_mode = VOP_REG(PX30_DSP_CTRL2, 0x1, 6), + .cfg_done = VOP_REG_SYNC(PX30_REG_CFG_DONE, 0x1, 0), + .dither_up = VOP_REG(PX30_DSP_CTRL2, 0x1, 2), + .dsp_lut_en = VOP_REG(PX30_DSP_CTRL2, 0x1, 5), + .gate_en = VOP_REG(PX30_DSP_CTRL2, 0x1, 0), +}; + +static const struct vop_modeset rv1126_modeset = { + .htotal_pw = VOP_REG(PX30_DSP_HTOTAL_HS_END, 0x0fff0fff, 0), + .hact_st_end = VOP_REG(PX30_DSP_HACT_ST_END, 0x0fff0fff, 0), + .vtotal_pw = VOP_REG(PX30_DSP_VTOTAL_VS_END, 0x0fff0fff, 0), + .vact_st_end = VOP_REG(PX30_DSP_VACT_ST_END, 0x0fff0fff, 0), +}; + +static const struct vop_output rv1126_output = { + .rgb_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 1), + .rgb_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 2), + .rgb_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 0), + .mipi_dclk_pol = VOP_REG(PX30_DSP_CTRL0, 0x1, 25), + .mipi_pin_pol = VOP_REG(PX30_DSP_CTRL0, 0x7, 26), + .mipi_en = VOP_REG(PX30_DSP_CTRL0, 0x1, 24), +}; + +static const struct vop_misc rv1126_misc = { + .global_regdone_en = VOP_REG(PX30_SYS_CTRL2, 0x1, 13), +}; + +static const struct vop_win_data rv1126_vop_win_data[] = { + { .base = 0x00, .phy = &px30_win0_data, + .type = DRM_PLANE_TYPE_OVERLAY }, + { .base = 0x00, .phy = &px30_win2_data, + .type = DRM_PLANE_TYPE_PRIMARY }, +}; + +static const struct vop_data rv1126_vop = { + .version = VOP_VERSION(2, 0xb), + .intr = &px30_intr, + .common = &rv1126_common, + .modeset = &rv1126_modeset, + .output = &rv1126_output, + .misc = &rv1126_misc, + .win = rv1126_vop_win_data, + .win_size = ARRAY_SIZE(rv1126_vop_win_data), + .max_output = { 1920, 1080 }, + .lut_size = 1024, +}; + static const struct of_device_id vop_driver_dt_match[] = { { .compatible = "rockchip,rk3036-vop", .data = &rk3036_vop }, @@ -1147,6 +1200,8 @@ static const struct of_device_id vop_driver_dt_match[] = { .data = &rk3228_vop }, { .compatible = "rockchip,rk3328-vop", .data = &rk3328_vop }, + { .compatible = "rockchip,rv1126-vop", + .data = &rv1126_vop }, {}, }; MODULE_DEVICE_TABLE(of, vop_driver_dt_match); diff --git a/drivers/gpu/drm/solomon/ssd130x.c b/drivers/gpu/drm/solomon/ssd130x.c index 5a80b228d18c..6dcf3e041113 100644 --- a/drivers/gpu/drm/solomon/ssd130x.c +++ b/drivers/gpu/drm/solomon/ssd130x.c @@ -141,14 +141,23 @@ const struct ssd130x_deviceinfo ssd130x_variants[] = { }; EXPORT_SYMBOL_NS_GPL(ssd130x_variants, DRM_SSD130X); +struct ssd130x_crtc_state { + struct drm_crtc_state base; + /* Buffer to store pixels in HW format and written to the panel */ + u8 *data_array; +}; + struct ssd130x_plane_state { struct drm_shadow_plane_state base; /* Intermediate buffer to convert pixels from XRGB8888 to HW format */ u8 *buffer; - /* Buffer to store pixels in HW format and written to the panel */ - u8 *data_array; }; +static inline struct ssd130x_crtc_state *to_ssd130x_crtc_state(struct drm_crtc_state *state) +{ + return container_of(state, struct ssd130x_crtc_state, base); +} + static inline struct ssd130x_plane_state *to_ssd130x_plane_state(struct drm_plane_state *state) { return container_of(state, struct ssd130x_plane_state, base.base); @@ -272,8 +281,8 @@ static int ssd130x_pwm_enable(struct ssd130x_device *ssd130x) /* Enable the PWM */ pwm_enable(ssd130x->pwm); - dev_dbg(dev, "Using PWM%d with a %lluns period.\n", - ssd130x->pwm->pwm, pwm_get_period(ssd130x->pwm)); + dev_dbg(dev, "Using PWM %s with a %lluns period.\n", + ssd130x->pwm->label, pwm_get_period(ssd130x->pwm)); return 0; } @@ -448,13 +457,11 @@ static int ssd130x_init(struct ssd130x_device *ssd130x) } static int ssd130x_update_rect(struct ssd130x_device *ssd130x, - struct ssd130x_plane_state *ssd130x_state, - struct drm_rect *rect) + struct drm_rect *rect, u8 *buf, + u8 *data_array) { unsigned int x = rect->x1; unsigned int y = rect->y1; - u8 *buf = ssd130x_state->buffer; - u8 *data_array = ssd130x_state->data_array; unsigned int width = drm_rect_width(rect); unsigned int height = drm_rect_height(rect); unsigned int line_length = DIV_ROUND_UP(width, 8); @@ -550,28 +557,55 @@ static int ssd130x_update_rect(struct ssd130x_device *ssd130x, return ret; } -static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, - struct ssd130x_plane_state *ssd130x_state) +static void ssd130x_clear_screen(struct ssd130x_device *ssd130x, u8 *data_array) { - struct drm_rect fullscreen = { - .x1 = 0, - .x2 = ssd130x->width, - .y1 = 0, - .y2 = ssd130x->height, - }; - - ssd130x_update_rect(ssd130x, ssd130x_state, &fullscreen); + unsigned int page_height = ssd130x->device_info->page_height; + unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); + unsigned int width = ssd130x->width; + int ret, i; + + if (!ssd130x->page_address_mode) { + memset(data_array, 0, width * pages); + + /* Set address range for horizontal addressing mode */ + ret = ssd130x_set_col_range(ssd130x, ssd130x->col_offset, width); + if (ret < 0) + return; + + ret = ssd130x_set_page_range(ssd130x, ssd130x->page_offset, pages); + if (ret < 0) + return; + + /* Write out update in one go if we aren't using page addressing mode */ + ssd130x_write_data(ssd130x, data_array, width * pages); + } else { + /* + * In page addressing mode, the start address needs to be reset, + * and each page then needs to be written out separately. + */ + memset(data_array, 0, width); + + for (i = 0; i < pages; i++) { + ret = ssd130x_set_page_pos(ssd130x, + ssd130x->page_offset + i, + ssd130x->col_offset); + if (ret < 0) + return; + + ret = ssd130x_write_data(ssd130x, data_array, width); + if (ret < 0) + return; + } + } } -static int ssd130x_fb_blit_rect(struct drm_plane_state *state, +static int ssd130x_fb_blit_rect(struct drm_framebuffer *fb, const struct iosys_map *vmap, - struct drm_rect *rect) + struct drm_rect *rect, + u8 *buf, u8 *data_array) { - struct drm_framebuffer *fb = state->fb; struct ssd130x_device *ssd130x = drm_to_ssd130x(fb->dev); unsigned int page_height = ssd130x->device_info->page_height; - struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(state); - u8 *buf = ssd130x_state->buffer; struct iosys_map dst; unsigned int dst_pitch; int ret = 0; @@ -591,24 +625,31 @@ static int ssd130x_fb_blit_rect(struct drm_plane_state *state, drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE); - ssd130x_update_rect(ssd130x, ssd130x_state, rect); + ssd130x_update_rect(ssd130x, rect, buf, data_array); return ret; } -static int ssd130x_primary_plane_helper_atomic_check(struct drm_plane *plane, - struct drm_atomic_state *state) +static int ssd130x_primary_plane_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) { struct drm_device *drm = plane->dev; struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane_state); - unsigned int page_height = ssd130x->device_info->page_height; - unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); + struct drm_crtc *crtc = plane_state->crtc; + struct drm_crtc_state *crtc_state; const struct drm_format_info *fi; unsigned int pitch; int ret; + if (!crtc) + return -EINVAL; + + crtc_state = drm_atomic_get_crtc_state(state, crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + ret = drm_plane_helper_atomic_check(plane, state); if (ret) return ret; @@ -623,23 +664,19 @@ static int ssd130x_primary_plane_helper_atomic_check(struct drm_plane *plane, if (!ssd130x_state->buffer) return -ENOMEM; - ssd130x_state->data_array = kcalloc(ssd130x->width, pages, GFP_KERNEL); - if (!ssd130x_state->data_array) { - kfree(ssd130x_state->buffer); - /* Set to prevent a double free in .atomic_destroy_state() */ - ssd130x_state->buffer = NULL; - return -ENOMEM; - } - return 0; } -static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane, - struct drm_atomic_state *state) +static void ssd130x_primary_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state, plane); struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); + struct ssd130x_crtc_state *ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state); + struct ssd130x_plane_state *ssd130x_plane_state = to_ssd130x_plane_state(plane_state); + struct drm_framebuffer *fb = plane_state->fb; struct drm_atomic_helper_damage_iter iter; struct drm_device *drm = plane->dev; struct drm_rect dst_clip; @@ -656,24 +693,34 @@ static void ssd130x_primary_plane_helper_atomic_update(struct drm_plane *plane, if (!drm_rect_intersect(&dst_clip, &damage)) continue; - ssd130x_fb_blit_rect(plane_state, &shadow_plane_state->data[0], &dst_clip); + ssd130x_fb_blit_rect(fb, &shadow_plane_state->data[0], &dst_clip, + ssd130x_plane_state->buffer, + ssd130x_crtc_state->data_array); } drm_dev_exit(idx); } -static void ssd130x_primary_plane_helper_atomic_disable(struct drm_plane *plane, - struct drm_atomic_state *state) +static void ssd130x_primary_plane_atomic_disable(struct drm_plane *plane, + struct drm_atomic_state *state) { struct drm_device *drm = plane->dev; struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); - struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(plane->state); + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + struct drm_crtc_state *crtc_state; + struct ssd130x_crtc_state *ssd130x_crtc_state; int idx; + if (!plane_state->crtc) + return; + + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); + ssd130x_crtc_state = to_ssd130x_crtc_state(crtc_state); + if (!drm_dev_enter(drm, &idx)) return; - ssd130x_clear_screen(ssd130x, ssd130x_state); + ssd130x_clear_screen(ssd130x, ssd130x_crtc_state->data_array); drm_dev_exit(idx); } @@ -706,9 +753,8 @@ static struct drm_plane_state *ssd130x_primary_plane_duplicate_state(struct drm_ if (!ssd130x_state) return NULL; - /* The buffers are not duplicated and are allocated in .atomic_check */ + /* The buffer is not duplicated and is allocated in .atomic_check */ ssd130x_state->buffer = NULL; - ssd130x_state->data_array = NULL; new_shadow_plane_state = &ssd130x_state->base; @@ -722,7 +768,6 @@ static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane, { struct ssd130x_plane_state *ssd130x_state = to_ssd130x_plane_state(state); - kfree(ssd130x_state->data_array); kfree(ssd130x_state->buffer); __drm_gem_destroy_shadow_plane_state(&ssd130x_state->base); @@ -732,9 +777,9 @@ static void ssd130x_primary_plane_destroy_state(struct drm_plane *plane, static const struct drm_plane_helper_funcs ssd130x_primary_plane_helper_funcs = { DRM_GEM_SHADOW_PLANE_HELPER_FUNCS, - .atomic_check = ssd130x_primary_plane_helper_atomic_check, - .atomic_update = ssd130x_primary_plane_helper_atomic_update, - .atomic_disable = ssd130x_primary_plane_helper_atomic_disable, + .atomic_check = ssd130x_primary_plane_atomic_check, + .atomic_update = ssd130x_primary_plane_atomic_update, + .atomic_disable = ssd130x_primary_plane_atomic_disable, }; static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { @@ -746,8 +791,8 @@ static const struct drm_plane_funcs ssd130x_primary_plane_funcs = { .destroy = drm_plane_cleanup, }; -static enum drm_mode_status ssd130x_crtc_helper_mode_valid(struct drm_crtc *crtc, - const struct drm_display_mode *mode) +static enum drm_mode_status ssd130x_crtc_mode_valid(struct drm_crtc *crtc, + const struct drm_display_mode *mode) { struct ssd130x_device *ssd130x = drm_to_ssd130x(crtc->dev); @@ -762,27 +807,96 @@ static enum drm_mode_status ssd130x_crtc_helper_mode_valid(struct drm_crtc *crtc return MODE_OK; } +static int ssd130x_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_atomic_state *state) +{ + struct drm_device *drm = crtc->dev; + struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); + struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc); + struct ssd130x_crtc_state *ssd130x_state = to_ssd130x_crtc_state(crtc_state); + unsigned int page_height = ssd130x->device_info->page_height; + unsigned int pages = DIV_ROUND_UP(ssd130x->height, page_height); + int ret; + + ret = drm_crtc_helper_atomic_check(crtc, state); + if (ret) + return ret; + + ssd130x_state->data_array = kmalloc(ssd130x->width * pages, GFP_KERNEL); + if (!ssd130x_state->data_array) + return -ENOMEM; + + return 0; +} + +/* Called during init to allocate the CRTC's atomic state. */ +static void ssd130x_crtc_reset(struct drm_crtc *crtc) +{ + struct ssd130x_crtc_state *ssd130x_state; + + WARN_ON(crtc->state); + + ssd130x_state = kzalloc(sizeof(*ssd130x_state), GFP_KERNEL); + if (!ssd130x_state) + return; + + __drm_atomic_helper_crtc_reset(crtc, &ssd130x_state->base); +} + +static struct drm_crtc_state *ssd130x_crtc_duplicate_state(struct drm_crtc *crtc) +{ + struct ssd130x_crtc_state *old_ssd130x_state; + struct ssd130x_crtc_state *ssd130x_state; + + if (WARN_ON(!crtc->state)) + return NULL; + + old_ssd130x_state = to_ssd130x_crtc_state(crtc->state); + ssd130x_state = kmemdup(old_ssd130x_state, sizeof(*ssd130x_state), GFP_KERNEL); + if (!ssd130x_state) + return NULL; + + /* The buffer is not duplicated and is allocated in .atomic_check */ + ssd130x_state->data_array = NULL; + + __drm_atomic_helper_crtc_duplicate_state(crtc, &ssd130x_state->base); + + return &ssd130x_state->base; +} + +static void ssd130x_crtc_destroy_state(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct ssd130x_crtc_state *ssd130x_state = to_ssd130x_crtc_state(state); + + kfree(ssd130x_state->data_array); + + __drm_atomic_helper_crtc_destroy_state(state); + + kfree(ssd130x_state); +} + /* * The CRTC is always enabled. Screen updates are performed by * the primary plane's atomic_update function. Disabling clears * the screen in the primary plane's atomic_disable function. */ static const struct drm_crtc_helper_funcs ssd130x_crtc_helper_funcs = { - .mode_valid = ssd130x_crtc_helper_mode_valid, - .atomic_check = drm_crtc_helper_atomic_check, + .mode_valid = ssd130x_crtc_mode_valid, + .atomic_check = ssd130x_crtc_atomic_check, }; static const struct drm_crtc_funcs ssd130x_crtc_funcs = { - .reset = drm_atomic_helper_crtc_reset, + .reset = ssd130x_crtc_reset, .destroy = drm_crtc_cleanup, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, - .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, - .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_duplicate_state = ssd130x_crtc_duplicate_state, + .atomic_destroy_state = ssd130x_crtc_destroy_state, }; -static void ssd130x_encoder_helper_atomic_enable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void ssd130x_encoder_atomic_enable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct drm_device *drm = encoder->dev; struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); @@ -807,8 +921,8 @@ power_off: return; } -static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, - struct drm_atomic_state *state) +static void ssd130x_encoder_atomic_disable(struct drm_encoder *encoder, + struct drm_atomic_state *state) { struct drm_device *drm = encoder->dev; struct ssd130x_device *ssd130x = drm_to_ssd130x(drm); @@ -821,15 +935,15 @@ static void ssd130x_encoder_helper_atomic_disable(struct drm_encoder *encoder, } static const struct drm_encoder_helper_funcs ssd130x_encoder_helper_funcs = { - .atomic_enable = ssd130x_encoder_helper_atomic_enable, - .atomic_disable = ssd130x_encoder_helper_atomic_disable, + .atomic_enable = ssd130x_encoder_atomic_enable, + .atomic_disable = ssd130x_encoder_atomic_disable, }; static const struct drm_encoder_funcs ssd130x_encoder_funcs = { .destroy = drm_encoder_cleanup, }; -static int ssd130x_connector_helper_get_modes(struct drm_connector *connector) +static int ssd130x_connector_get_modes(struct drm_connector *connector) { struct ssd130x_device *ssd130x = drm_to_ssd130x(connector->dev); struct drm_display_mode *mode; @@ -849,7 +963,7 @@ static int ssd130x_connector_helper_get_modes(struct drm_connector *connector) } static const struct drm_connector_helper_funcs ssd130x_connector_helper_funcs = { - .get_modes = ssd130x_connector_helper_get_modes, + .get_modes = ssd130x_connector_get_modes, }; static const struct drm_connector_funcs ssd130x_connector_funcs = { @@ -1131,6 +1245,7 @@ EXPORT_SYMBOL_GPL(ssd130x_probe); void ssd130x_remove(struct ssd130x_device *ssd130x) { drm_dev_unplug(&ssd130x->drm); + drm_atomic_helper_shutdown(&ssd130x->drm); } EXPORT_SYMBOL_GPL(ssd130x_remove); diff --git a/drivers/gpu/drm/solomon/ssd130x.h b/drivers/gpu/drm/solomon/ssd130x.h index 87968b3e7fb8..aa39b13615eb 100644 --- a/drivers/gpu/drm/solomon/ssd130x.h +++ b/drivers/gpu/drm/solomon/ssd130x.h @@ -40,8 +40,8 @@ struct ssd130x_deviceinfo { u32 default_width; u32 default_height; u32 page_height; - int need_pwm; - int need_chargepump; + bool need_pwm; + bool need_chargepump; bool page_mode_only; }; diff --git a/drivers/gpu/drm/sti/sti_drv.c b/drivers/gpu/drm/sti/sti_drv.c index 2390c1bb6596..4bab93c4fefd 100644 --- a/drivers/gpu/drm/sti/sti_drv.c +++ b/drivers/gpu/drm/sti/sti_drv.c @@ -174,6 +174,7 @@ static void sti_cleanup(struct drm_device *ddev) drm_atomic_helper_shutdown(ddev); drm_mode_config_cleanup(ddev); component_unbind_all(ddev->dev, ddev); + dev_set_drvdata(ddev->dev, NULL); kfree(private); ddev->dev_private = NULL; } @@ -253,6 +254,11 @@ static void sti_platform_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &sti_ops); } +static void sti_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id sti_dt_ids[] = { { .compatible = "st,sti-display-subsystem", }, { /* end node */ }, @@ -262,6 +268,7 @@ MODULE_DEVICE_TABLE(of, sti_dt_ids); static struct platform_driver sti_platform_driver = { .probe = sti_platform_probe, .remove_new = sti_platform_remove, + .shutdown = sti_platform_shutdown, .driver = { .name = DRIVER_NAME, .of_match_table = sti_dt_ids, diff --git a/drivers/gpu/drm/stm/drv.c b/drivers/gpu/drm/stm/drv.c index c68c831136c9..e8523abef27a 100644 --- a/drivers/gpu/drm/stm/drv.c +++ b/drivers/gpu/drm/stm/drv.c @@ -114,6 +114,7 @@ static void drv_unload(struct drm_device *ddev) DRM_DEBUG("%s\n", __func__); drm_kms_helper_poll_fini(ddev); + drm_atomic_helper_shutdown(ddev); ltdc_unload(ddev); } @@ -225,6 +226,11 @@ static void stm_drm_platform_remove(struct platform_device *pdev) drm_dev_put(ddev); } +static void stm_drm_platform_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id drv_dt_ids[] = { { .compatible = "st,stm32-ltdc"}, { /* end node */ }, @@ -234,6 +240,7 @@ MODULE_DEVICE_TABLE(of, drv_dt_ids); static struct platform_driver stm_drm_platform_driver = { .probe = stm_drm_platform_probe, .remove_new = stm_drm_platform_remove, + .shutdown = stm_drm_platform_shutdown, .driver = { .name = "stm32-display", .of_match_table = drv_dt_ids, diff --git a/drivers/gpu/drm/sun4i/sun4i_drv.c b/drivers/gpu/drm/sun4i/sun4i_drv.c index 6a8dfc022d3c..35d7a7ffd208 100644 --- a/drivers/gpu/drm/sun4i/sun4i_drv.c +++ b/drivers/gpu/drm/sun4i/sun4i_drv.c @@ -413,6 +413,11 @@ static void sun4i_drv_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &sun4i_drv_master_ops); } +static void sun4i_drv_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id sun4i_drv_of_table[] = { { .compatible = "allwinner,sun4i-a10-display-engine" }, { .compatible = "allwinner,sun5i-a10s-display-engine" }, @@ -437,6 +442,7 @@ MODULE_DEVICE_TABLE(of, sun4i_drv_of_table); static struct platform_driver sun4i_drv_platform_driver = { .probe = sun4i_drv_probe, .remove_new = sun4i_drv_remove, + .shutdown = sun4i_drv_shutdown, .driver = { .name = "sun4i-drm", .of_match_table = sun4i_drv_of_table, diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 13b182ab905f..be61c9d1a4f0 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -1746,8 +1746,15 @@ static void tegra_dc_early_unregister(struct drm_crtc *crtc) unsigned int count = ARRAY_SIZE(debugfs_files); struct drm_minor *minor = crtc->dev->primary; struct tegra_dc *dc = to_tegra_dc(crtc); + struct dentry *root; + +#ifdef CONFIG_DEBUG_FS + root = crtc->debugfs_entry; +#else + root = NULL; +#endif - drm_debugfs_remove_files(dc->debugfs_files, count, minor); + drm_debugfs_remove_files(dc->debugfs_files, count, root, minor); kfree(dc->debugfs_files); dc->debugfs_files = NULL; } diff --git a/drivers/gpu/drm/tegra/dsi.c b/drivers/gpu/drm/tegra/dsi.c index a9870c828374..fbfe92a816d4 100644 --- a/drivers/gpu/drm/tegra/dsi.c +++ b/drivers/gpu/drm/tegra/dsi.c @@ -256,6 +256,7 @@ static void tegra_dsi_early_unregister(struct drm_connector *connector) struct tegra_dsi *dsi = to_dsi(output); drm_debugfs_remove_files(dsi->debugfs_files, count, + connector->debugfs_entry, connector->dev->primary); kfree(dsi->debugfs_files); dsi->debugfs_files = NULL; diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 80c760986d9e..0ba3ca3ac509 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1116,7 +1116,8 @@ static void tegra_hdmi_early_unregister(struct drm_connector *connector) unsigned int count = ARRAY_SIZE(debugfs_files); struct tegra_hdmi *hdmi = to_hdmi(output); - drm_debugfs_remove_files(hdmi->debugfs_files, count, minor); + drm_debugfs_remove_files(hdmi->debugfs_files, count, + connector->debugfs_entry, minor); kfree(hdmi->debugfs_files); hdmi->debugfs_files = NULL; } diff --git a/drivers/gpu/drm/tegra/rgb.c b/drivers/gpu/drm/tegra/rgb.c index 79566c9ea8ff..fc66bbd913b2 100644 --- a/drivers/gpu/drm/tegra/rgb.c +++ b/drivers/gpu/drm/tegra/rgb.c @@ -99,6 +99,7 @@ static void tegra_rgb_encoder_disable(struct drm_encoder *encoder) static void tegra_rgb_encoder_enable(struct drm_encoder *encoder) { + struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode; struct tegra_output *output = encoder_to_output(encoder); struct tegra_rgb *rgb = to_rgb(output); u32 value; @@ -108,10 +109,19 @@ static void tegra_rgb_encoder_enable(struct drm_encoder *encoder) value = DE_SELECT_ACTIVE | DE_CONTROL_NORMAL; tegra_dc_writel(rgb->dc, value, DC_DISP_DATA_ENABLE_OPTIONS); - /* XXX: parameterize? */ + /* configure H- and V-sync signal polarities */ value = tegra_dc_readl(rgb->dc, DC_COM_PIN_OUTPUT_POLARITY(1)); - value &= ~LVS_OUTPUT_POLARITY_LOW; - value &= ~LHS_OUTPUT_POLARITY_LOW; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + value |= LHS_OUTPUT_POLARITY_LOW; + else + value &= ~LHS_OUTPUT_POLARITY_LOW; + + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + value |= LVS_OUTPUT_POLARITY_LOW; + else + value &= ~LVS_OUTPUT_POLARITY_LOW; + tegra_dc_writel(rgb->dc, value, DC_COM_PIN_OUTPUT_POLARITY(1)); /* XXX: parameterize? */ diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 61b437a84806..d5a3d3f4fece 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -1708,6 +1708,7 @@ static void tegra_sor_early_unregister(struct drm_connector *connector) struct tegra_sor *sor = to_sor(output); drm_debugfs_remove_files(sor->debugfs_files, count, + connector->debugfs_entry, connector->dev->primary); kfree(sor->debugfs_files); sor->debugfs_files = NULL; diff --git a/drivers/gpu/drm/tests/drm_format_helper_test.c b/drivers/gpu/drm/tests/drm_format_helper_test.c index 474bb7a1c4ee..1a6bd291345d 100644 --- a/drivers/gpu/drm/tests/drm_format_helper_test.c +++ b/drivers/gpu/drm/tests/drm_format_helper_test.c @@ -3,11 +3,13 @@ #include <kunit/test.h> #include <drm/drm_device.h> +#include <drm/drm_drv.h> #include <drm/drm_file.h> #include <drm/drm_format_helper.h> #include <drm/drm_fourcc.h> #include <drm/drm_framebuffer.h> #include <drm/drm_gem_framebuffer_helper.h> +#include <drm/drm_kunit_helpers.h> #include <drm/drm_mode.h> #include <drm/drm_print.h> #include <drm/drm_rect.h> @@ -16,6 +18,8 @@ #define TEST_BUF_SIZE 50 +#define TEST_USE_DEFAULT_PITCH 0 + struct convert_to_gray8_result { unsigned int dst_pitch; const u8 expected[TEST_BUF_SIZE]; @@ -72,6 +76,11 @@ struct convert_to_mono_result { const u8 expected[TEST_BUF_SIZE]; }; +struct fb_swab_result { + unsigned int dst_pitch; + const u32 expected[TEST_BUF_SIZE]; +}; + struct convert_xrgb8888_case { const char *name; unsigned int pitch; @@ -88,6 +97,7 @@ struct convert_xrgb8888_case { struct convert_to_xrgb2101010_result xrgb2101010_result; struct convert_to_argb2101010_result argb2101010_result; struct convert_to_mono_result mono_result; + struct fb_swab_result swab_result; }; static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { @@ -97,50 +107,54 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { .clip = DRM_RECT_INIT(0, 0, 1, 1), .xrgb8888 = { 0x01FF0000 }, .gray8_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x4C }, }, .rgb332_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xE0 }, }, .rgb565_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xF800 }, .expected_swab = { 0x00F8 }, }, .xrgb1555_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x7C00 }, }, .argb1555_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFC00 }, }, .rgba5551_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xF801 }, }, .rgb888_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x00, 0x00, 0xFF }, }, .argb8888_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF0000 }, }, .xrgb2101010_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x3FF00000 }, }, .argb2101010_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFF00000 }, }, .mono_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0b0 }, }, + .swab_result = { + .dst_pitch = TEST_USE_DEFAULT_PITCH, + .expected = { 0x0000FF01 }, + }, }, { .name = "single_pixel_clip_rectangle", @@ -151,50 +165,54 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { 0x00000000, 0x10FF0000, }, .gray8_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x4C }, }, .rgb332_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xE0 }, }, .rgb565_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xF800 }, .expected_swab = { 0x00F8 }, }, .xrgb1555_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x7C00 }, }, .argb1555_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFC00 }, }, .rgba5551_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xF801 }, }, .rgb888_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x00, 0x00, 0xFF }, }, .argb8888_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF0000 }, }, .xrgb2101010_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x3FF00000 }, }, .argb2101010_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFF00000 }, }, .mono_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0b0 }, }, + .swab_result = { + .dst_pitch = TEST_USE_DEFAULT_PITCH, + .expected = { 0x0000FF10 }, + }, }, { /* Well known colors: White, black, red, green, blue, magenta, @@ -212,7 +230,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { 0x00000000, 0x77FFFF00, 0x8800FFFF, 0x00000000, }, .gray8_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFF, 0x00, 0x4C, 0x99, @@ -221,7 +239,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .rgb332_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFF, 0x00, 0xE0, 0x1C, @@ -230,7 +248,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .rgb565_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF, 0x0000, 0xF800, 0x07E0, @@ -245,7 +263,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .xrgb1555_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x7FFF, 0x0000, 0x7C00, 0x03E0, @@ -254,7 +272,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .argb1555_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF, 0x8000, 0xFC00, 0x83E0, @@ -263,7 +281,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .rgba5551_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFF, 0x0001, 0xF801, 0x07C1, @@ -272,7 +290,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .rgb888_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, @@ -281,7 +299,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .argb8888_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFFFFFF, 0xFF000000, 0xFFFF0000, 0xFF00FF00, @@ -290,7 +308,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .xrgb2101010_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0x3FFFFFFF, 0x00000000, 0x3FF00000, 0x000FFC00, @@ -299,7 +317,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .argb2101010_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0xFFFFFFFF, 0xC0000000, 0xFFF00000, 0xC00FFC00, @@ -308,7 +326,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { }, }, .mono_result = { - .dst_pitch = 0, + .dst_pitch = TEST_USE_DEFAULT_PITCH, .expected = { 0b01, 0b10, @@ -316,6 +334,15 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { 0b11, }, }, + .swab_result = { + .dst_pitch = TEST_USE_DEFAULT_PITCH, + .expected = { + 0xFFFFFF11, 0x00000022, + 0x0000FF33, 0x00FF0044, + 0xFF000055, 0xFF00FF66, + 0x00FFFF77, 0xFFFF0088, + }, + }, }, { /* Randomly picked colors. Full buffer within the clip area. */ @@ -423,6 +450,14 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { 0b010, 0b000, }, }, + .swab_result = { + .dst_pitch = 20, + .expected = { + 0x9C440EA1, 0x054D11B1, 0x03F3A8C1, 0x00000000, 0x00000000, + 0x73F06CD1, 0x9C440EA2, 0x054D11B2, 0x00000000, 0x00000000, + 0x0303A8C2, 0x73F06CD2, 0x9C440EA3, 0x00000000, 0x00000000, + }, + }, }, }; @@ -437,7 +472,7 @@ static struct convert_xrgb8888_case convert_xrgb8888_cases[] = { * The size of the destination buffer or negative value on error. */ static size_t conversion_buf_size(u32 dst_format, unsigned int dst_pitch, - const struct drm_rect *clip) + const struct drm_rect *clip, int plane) { const struct drm_format_info *dst_fi = drm_format_info(dst_format); @@ -445,7 +480,7 @@ static size_t conversion_buf_size(u32 dst_format, unsigned int dst_pitch, return -EINVAL; if (!dst_pitch) - dst_pitch = drm_format_info_min_pitch(dst_fi, 0, drm_rect_width(clip)); + dst_pitch = drm_format_info_min_pitch(dst_fi, plane, drm_rect_width(clip)); return dst_pitch * drm_rect_height(clip); } @@ -519,7 +554,7 @@ static void drm_test_fb_xrgb8888_to_gray8(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_R8, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -530,7 +565,11 @@ static void drm_test_fb_xrgb8888_to_gray8(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_gray8(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_gray8(&dst, dst_pitch, &src, &fb, ¶ms->clip); + KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -549,7 +588,7 @@ static void drm_test_fb_xrgb8888_to_rgb332(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_RGB332, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -560,7 +599,10 @@ static void drm_test_fb_xrgb8888_to_rgb332(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_rgb332(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_rgb332(&dst, dst_pitch, &src, &fb, ¶ms->clip); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -579,7 +621,7 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_RGB565, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -590,7 +632,10 @@ static void drm_test_fb_xrgb8888_to_rgb565(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_rgb565(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip, false); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_rgb565(&dst, dst_pitch, &src, &fb, ¶ms->clip, false); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); @@ -615,7 +660,7 @@ static void drm_test_fb_xrgb8888_to_xrgb1555(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_XRGB1555, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -626,7 +671,10 @@ static void drm_test_fb_xrgb8888_to_xrgb1555(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_xrgb1555(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_xrgb1555(&dst, dst_pitch, &src, &fb, ¶ms->clip); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -646,7 +694,7 @@ static void drm_test_fb_xrgb8888_to_argb1555(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_ARGB1555, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -657,7 +705,10 @@ static void drm_test_fb_xrgb8888_to_argb1555(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_argb1555(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_argb1555(&dst, dst_pitch, &src, &fb, ¶ms->clip); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -677,7 +728,7 @@ static void drm_test_fb_xrgb8888_to_rgba5551(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_RGBA5551, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -688,7 +739,10 @@ static void drm_test_fb_xrgb8888_to_rgba5551(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_rgba5551(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_rgba5551(&dst, dst_pitch, &src, &fb, ¶ms->clip); buf = le16buf_to_cpu(test, (__force const __le16 *)buf, dst_size / sizeof(__le16)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -708,7 +762,7 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_RGB888, result->dst_pitch, - ¶ms->clip); + ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -723,7 +777,10 @@ static void drm_test_fb_xrgb8888_to_rgb888(struct kunit *test) * RGB888 expected results are already in little-endian * order, so there's no need to convert the test output. */ - drm_fb_xrgb8888_to_rgb888(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_rgb888(&dst, dst_pitch, &src, &fb, ¶ms->clip); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -742,7 +799,7 @@ static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_ARGB8888, - result->dst_pitch, ¶ms->clip); + result->dst_pitch, ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -753,7 +810,10 @@ static void drm_test_fb_xrgb8888_to_argb8888(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_argb8888(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_argb8888(&dst, dst_pitch, &src, &fb, ¶ms->clip); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -773,7 +833,7 @@ static void drm_test_fb_xrgb8888_to_xrgb2101010(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_XRGB2101010, - result->dst_pitch, ¶ms->clip); + result->dst_pitch, ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -784,7 +844,10 @@ static void drm_test_fb_xrgb8888_to_xrgb2101010(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_xrgb2101010(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_xrgb2101010(&dst, dst_pitch, &src, &fb, ¶ms->clip); buf = le32buf_to_cpu(test, buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -804,7 +867,7 @@ static void drm_test_fb_xrgb8888_to_argb2101010(struct kunit *test) }; dst_size = conversion_buf_size(DRM_FORMAT_ARGB2101010, - result->dst_pitch, ¶ms->clip); + result->dst_pitch, ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); @@ -815,7 +878,10 @@ static void drm_test_fb_xrgb8888_to_argb2101010(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_argb2101010(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_argb2101010(&dst, dst_pitch, &src, &fb, ¶ms->clip); buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } @@ -834,7 +900,7 @@ static void drm_test_fb_xrgb8888_to_mono(struct kunit *test) .pitches = { params->pitch, 0, 0 }, }; - dst_size = conversion_buf_size(DRM_FORMAT_C1, result->dst_pitch, ¶ms->clip); + dst_size = conversion_buf_size(DRM_FORMAT_C1, result->dst_pitch, ¶ms->clip, 0); KUNIT_ASSERT_GT(test, dst_size, 0); @@ -846,10 +912,639 @@ static void drm_test_fb_xrgb8888_to_mono(struct kunit *test) KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); iosys_map_set_vaddr(&src, xrgb8888); - drm_fb_xrgb8888_to_mono(&dst, &result->dst_pitch, &src, &fb, ¶ms->clip); + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_xrgb8888_to_mono(&dst, dst_pitch, &src, &fb, ¶ms->clip); KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); } +static void drm_test_fb_swab(struct kunit *test) +{ + const struct convert_xrgb8888_case *params = test->param_value; + const struct fb_swab_result *result = ¶ms->swab_result; + size_t dst_size; + u32 *buf = NULL; + __le32 *xrgb8888 = NULL; + struct iosys_map dst, src; + + struct drm_framebuffer fb = { + .format = drm_format_info(DRM_FORMAT_XRGB8888), + .pitches = { params->pitch, 0, 0 }, + }; + + dst_size = conversion_buf_size(DRM_FORMAT_XRGB8888, result->dst_pitch, ¶ms->clip, 0); + + KUNIT_ASSERT_GT(test, dst_size, 0); + + buf = kunit_kzalloc(test, dst_size, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + iosys_map_set_vaddr(&dst, buf); + + xrgb8888 = cpubuf_to_le32(test, params->xrgb8888, TEST_BUF_SIZE); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, xrgb8888); + iosys_map_set_vaddr(&src, xrgb8888); + + const unsigned int *dst_pitch = (result->dst_pitch == TEST_USE_DEFAULT_PITCH) ? + NULL : &result->dst_pitch; + + drm_fb_swab(&dst, dst_pitch, &src, &fb, ¶ms->clip, false); + buf = le32buf_to_cpu(test, (__force const __le32 *)buf, dst_size / sizeof(u32)); + KUNIT_EXPECT_MEMEQ(test, buf, result->expected, dst_size); +} + +struct clip_offset_case { + const char *name; + unsigned int pitch; + u32 format; + struct drm_rect clip; + unsigned int expected_offset; +}; + +static struct clip_offset_case clip_offset_cases[] = { + { + .name = "pass through", + .pitch = TEST_USE_DEFAULT_PITCH, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(0, 0, 3, 3), + .expected_offset = 0 + }, + { + .name = "horizontal offset", + .pitch = TEST_USE_DEFAULT_PITCH, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(1, 0, 3, 3), + .expected_offset = 4, + }, + { + .name = "vertical offset", + .pitch = TEST_USE_DEFAULT_PITCH, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(0, 1, 3, 3), + .expected_offset = 12, + }, + { + .name = "horizontal and vertical offset", + .pitch = TEST_USE_DEFAULT_PITCH, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(1, 1, 3, 3), + .expected_offset = 16, + }, + { + .name = "horizontal offset (custom pitch)", + .pitch = 20, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(1, 0, 3, 3), + .expected_offset = 4, + }, + { + .name = "vertical offset (custom pitch)", + .pitch = 20, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(0, 1, 3, 3), + .expected_offset = 20, + }, + { + .name = "horizontal and vertical offset (custom pitch)", + .pitch = 20, + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(1, 1, 3, 3), + .expected_offset = 24, + }, +}; + +static void clip_offset_case_desc(struct clip_offset_case *t, char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(clip_offset, clip_offset_cases, clip_offset_case_desc); + +static void drm_test_fb_clip_offset(struct kunit *test) +{ + const struct clip_offset_case *params = test->param_value; + const struct drm_format_info *format_info = drm_format_info(params->format); + + unsigned int offset; + unsigned int pitch = params->pitch; + + if (pitch == TEST_USE_DEFAULT_PITCH) + pitch = drm_format_info_min_pitch(format_info, 0, + drm_rect_width(¶ms->clip)); + + /* + * Assure that the pitch is not zero, because this will inevitable cause the + * wrong expected result + */ + KUNIT_ASSERT_NE(test, pitch, 0); + + offset = drm_fb_clip_offset(pitch, format_info, ¶ms->clip); + + KUNIT_EXPECT_EQ(test, offset, params->expected_offset); +} + +struct fb_build_fourcc_list_case { + const char *name; + u32 native_fourccs[TEST_BUF_SIZE]; + size_t native_fourccs_size; + u32 expected[TEST_BUF_SIZE]; + size_t expected_fourccs_size; +}; + +static struct fb_build_fourcc_list_case fb_build_fourcc_list_cases[] = { + { + .name = "no native formats", + .native_fourccs = { }, + .native_fourccs_size = 0, + .expected = { DRM_FORMAT_XRGB8888 }, + .expected_fourccs_size = 1, + }, + { + .name = "XRGB8888 as native format", + .native_fourccs = { DRM_FORMAT_XRGB8888 }, + .native_fourccs_size = 1, + .expected = { DRM_FORMAT_XRGB8888 }, + .expected_fourccs_size = 1, + }, + { + .name = "remove duplicates", + .native_fourccs = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565, + DRM_FORMAT_RGB565, + DRM_FORMAT_XRGB8888, + }, + .native_fourccs_size = 11, + .expected = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, + }, + .expected_fourccs_size = 3, + }, + { + .name = "convert alpha formats", + .native_fourccs = { + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + }, + .native_fourccs_size = 12, + .expected = { + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_RGBX1010102, + DRM_FORMAT_BGRX1010102, + }, + .expected_fourccs_size = 12, + }, + { + .name = "random formats", + .native_fourccs = { + DRM_FORMAT_Y212, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR16161616F, + DRM_FORMAT_C8, + DRM_FORMAT_BGR888, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGR565_A8, + DRM_FORMAT_R10, + DRM_FORMAT_XYUV8888, + }, + .native_fourccs_size = 10, + .expected = { + DRM_FORMAT_Y212, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_ABGR16161616F, + DRM_FORMAT_C8, + DRM_FORMAT_BGR888, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGR565_A8, + DRM_FORMAT_R10, + DRM_FORMAT_XYUV8888, + DRM_FORMAT_XRGB8888, + }, + .expected_fourccs_size = 10, + }, +}; + +static void fb_build_fourcc_list_case_desc(struct fb_build_fourcc_list_case *t, char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(fb_build_fourcc_list, fb_build_fourcc_list_cases, fb_build_fourcc_list_case_desc); + +static void drm_test_fb_build_fourcc_list(struct kunit *test) +{ + const struct fb_build_fourcc_list_case *params = test->param_value; + u32 fourccs_out[TEST_BUF_SIZE] = {0}; + size_t nfourccs_out; + struct drm_device *drm; + struct device *dev; + + dev = drm_kunit_helper_alloc_device(test); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dev); + + drm = __drm_kunit_helper_alloc_drm_device(test, dev, sizeof(*drm), 0, DRIVER_MODESET); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, drm); + + nfourccs_out = drm_fb_build_fourcc_list(drm, params->native_fourccs, + params->native_fourccs_size, + fourccs_out, TEST_BUF_SIZE); + + KUNIT_EXPECT_EQ(test, nfourccs_out, params->expected_fourccs_size); + KUNIT_EXPECT_MEMEQ(test, fourccs_out, params->expected, TEST_BUF_SIZE); +} + +struct fb_memcpy_case { + const char *name; + u32 format; + struct drm_rect clip; + unsigned int src_pitches[DRM_FORMAT_MAX_PLANES]; + const u32 src[DRM_FORMAT_MAX_PLANES][TEST_BUF_SIZE]; + unsigned int dst_pitches[DRM_FORMAT_MAX_PLANES]; + const u32 expected[DRM_FORMAT_MAX_PLANES][TEST_BUF_SIZE]; +}; + +/* The `src` and `expected` buffers are u32 arrays. To deal with planes that + * have a cpp != 4 the values are stored together on the same u32 number in a + * way so the order in memory is correct in a little-endian machine. + * + * Because of that, on some occasions, parts of a u32 will not be part of the + * test, to make this explicit the 0xFF byte is used on those parts. + */ + +static struct fb_memcpy_case fb_memcpy_cases[] = { + { + .name = "single_pixel_source_buffer", + .format = DRM_FORMAT_XRGB8888, + .clip = DRM_RECT_INIT(0, 0, 1, 1), + .src_pitches = { 1 * 4 }, + .src = {{ 0x01020304 }}, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = {{ 0x01020304 }}, + }, + { + .name = "single_pixel_source_buffer", + .format = DRM_FORMAT_XRGB8888_A8, + .clip = DRM_RECT_INIT(0, 0, 1, 1), + .src_pitches = { 1 * 4, 1 }, + .src = { + { 0x01020304 }, + { 0xFFFFFF01 }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { 0x01020304 }, + { 0x00000001 }, + }, + }, + { + .name = "single_pixel_source_buffer", + .format = DRM_FORMAT_YUV444, + .clip = DRM_RECT_INIT(0, 0, 1, 1), + .src_pitches = { 1, 1, 1 }, + .src = { + { 0xFFFFFF01 }, + { 0xFFFFFF01 }, + { 0xFFFFFF01 }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { 0x00000001 }, + { 0x00000001 }, + { 0x00000001 }, + }, + }, + { + .name = "single_pixel_clip_rectangle", + .format = DRM_FORMAT_XBGR8888, + .clip = DRM_RECT_INIT(1, 1, 1, 1), + .src_pitches = { 2 * 4 }, + .src = { + { + 0x00000000, 0x00000000, + 0x00000000, 0x01020304, + }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { 0x01020304 }, + }, + }, + { + .name = "single_pixel_clip_rectangle", + .format = DRM_FORMAT_XRGB8888_A8, + .clip = DRM_RECT_INIT(1, 1, 1, 1), + .src_pitches = { 2 * 4, 2 * 1 }, + .src = { + { + 0x00000000, 0x00000000, + 0x00000000, 0x01020304, + }, + { 0x01000000 }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { 0x01020304 }, + { 0x00000001 }, + }, + }, + { + .name = "single_pixel_clip_rectangle", + .format = DRM_FORMAT_YUV444, + .clip = DRM_RECT_INIT(1, 1, 1, 1), + .src_pitches = { 2 * 1, 2 * 1, 2 * 1 }, + .src = { + { 0x01000000 }, + { 0x01000000 }, + { 0x01000000 }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { 0x00000001 }, + { 0x00000001 }, + { 0x00000001 }, + }, + }, + { + .name = "well_known_colors", + .format = DRM_FORMAT_XBGR8888, + .clip = DRM_RECT_INIT(1, 1, 2, 4), + .src_pitches = { 4 * 4 }, + .src = { + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x11FFFFFF, 0x22000000, 0x00000000, + 0x00000000, 0x33FF0000, 0x4400FF00, 0x00000000, + 0x00000000, 0x550000FF, 0x66FF00FF, 0x00000000, + 0x00000000, 0x77FFFF00, 0x8800FFFF, 0x00000000, + }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { + 0x11FFFFFF, 0x22000000, + 0x33FF0000, 0x4400FF00, + 0x550000FF, 0x66FF00FF, + 0x77FFFF00, 0x8800FFFF, + }, + }, + }, + { + .name = "well_known_colors", + .format = DRM_FORMAT_XRGB8888_A8, + .clip = DRM_RECT_INIT(1, 1, 2, 4), + .src_pitches = { 4 * 4, 4 * 1 }, + .src = { + { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFF000000, 0x00000000, + 0x00000000, 0xFFFF0000, 0xFF00FF00, 0x00000000, + 0x00000000, 0xFF0000FF, 0xFFFF00FF, 0x00000000, + 0x00000000, 0xFFFFFF00, 0xFF00FFFF, 0x00000000, + }, + { + 0x00000000, + 0x00221100, + 0x00443300, + 0x00665500, + 0x00887700, + }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { + 0xFFFFFFFF, 0xFF000000, + 0xFFFF0000, 0xFF00FF00, + 0xFF0000FF, 0xFFFF00FF, + 0xFFFFFF00, 0xFF00FFFF, + }, + { + 0x44332211, + 0x88776655, + }, + }, + }, + { + .name = "well_known_colors", + .format = DRM_FORMAT_YUV444, + .clip = DRM_RECT_INIT(1, 1, 2, 4), + .src_pitches = { 4 * 1, 4 * 1, 4 * 1 }, + .src = { + { + 0x00000000, + 0x0000FF00, + 0x00954C00, + 0x00691D00, + 0x00B2E100, + }, + { + 0x00000000, + 0x00000000, + 0x00BEDE00, + 0x00436500, + 0x00229B00, + }, + { + 0x00000000, + 0x00000000, + 0x007E9C00, + 0x0083E700, + 0x00641A00, + }, + }, + .dst_pitches = { TEST_USE_DEFAULT_PITCH }, + .expected = { + { + 0x954C00FF, + 0xB2E1691D, + }, + { + 0xBEDE0000, + 0x229B4365, + }, + { + 0x7E9C0000, + 0x641A83E7, + }, + }, + }, + { + .name = "destination_pitch", + .format = DRM_FORMAT_XBGR8888, + .clip = DRM_RECT_INIT(0, 0, 3, 3), + .src_pitches = { 3 * 4 }, + .src = { + { + 0xA10E449C, 0xB1114D05, 0xC1A8F303, + 0xD16CF073, 0xA20E449C, 0xB2114D05, + 0xC2A80303, 0xD26CF073, 0xA30E449C, + }, + }, + .dst_pitches = { 5 * 4 }, + .expected = { + { + 0xA10E449C, 0xB1114D05, 0xC1A8F303, 0x00000000, 0x00000000, + 0xD16CF073, 0xA20E449C, 0xB2114D05, 0x00000000, 0x00000000, + 0xC2A80303, 0xD26CF073, 0xA30E449C, 0x00000000, 0x00000000, + }, + }, + }, + { + .name = "destination_pitch", + .format = DRM_FORMAT_XRGB8888_A8, + .clip = DRM_RECT_INIT(0, 0, 3, 3), + .src_pitches = { 3 * 4, 3 * 1 }, + .src = { + { + 0xFF0E449C, 0xFF114D05, 0xFFA8F303, + 0xFF6CF073, 0xFF0E449C, 0xFF114D05, + 0xFFA80303, 0xFF6CF073, 0xFF0E449C, + }, + { + 0xB2C1B1A1, + 0xD2A3D1A2, + 0xFFFFFFC2, + }, + }, + .dst_pitches = { 5 * 4, 5 * 1 }, + .expected = { + { + 0xFF0E449C, 0xFF114D05, 0xFFA8F303, 0x00000000, 0x00000000, + 0xFF6CF073, 0xFF0E449C, 0xFF114D05, 0x00000000, 0x00000000, + 0xFFA80303, 0xFF6CF073, 0xFF0E449C, 0x00000000, 0x00000000, + }, + { + 0x00C1B1A1, + 0xD1A2B200, + 0xD2A30000, + 0xFF0000C2, + }, + }, + }, + { + .name = "destination_pitch", + .format = DRM_FORMAT_YUV444, + .clip = DRM_RECT_INIT(0, 0, 3, 3), + .src_pitches = { 3 * 1, 3 * 1, 3 * 1 }, + .src = { + { + 0xBAC1323D, + 0xBA34323D, + 0xFFFFFF3D, + }, + { + 0xE1ABEC2A, + 0xE1EAEC2A, + 0xFFFFFF2A, + }, + { + 0xBCEBE4D7, + 0xBC65E4D7, + 0xFFFFFFD7, + }, + }, + .dst_pitches = { 5 * 1, 5 * 1, 5 * 1 }, + .expected = { + { + 0x00C1323D, + 0x323DBA00, + 0xBA340000, + 0xFF00003D, + }, + { + 0x00ABEC2A, + 0xEC2AE100, + 0xE1EA0000, + 0xFF00002A, + }, + { + 0x00EBE4D7, + 0xE4D7BC00, + 0xBC650000, + 0xFF0000D7, + }, + }, + }, +}; + +static void fb_memcpy_case_desc(struct fb_memcpy_case *t, char *desc) +{ + snprintf(desc, KUNIT_PARAM_DESC_SIZE, "%s: %p4cc", t->name, &t->format); +} + +KUNIT_ARRAY_PARAM(fb_memcpy, fb_memcpy_cases, fb_memcpy_case_desc); + +static void drm_test_fb_memcpy(struct kunit *test) +{ + const struct fb_memcpy_case *params = test->param_value; + size_t dst_size[DRM_FORMAT_MAX_PLANES] = { 0 }; + u32 *buf[DRM_FORMAT_MAX_PLANES] = { 0 }; + __le32 *src_cp[DRM_FORMAT_MAX_PLANES] = { 0 }; + __le32 *expected[DRM_FORMAT_MAX_PLANES] = { 0 }; + struct iosys_map dst[DRM_FORMAT_MAX_PLANES]; + struct iosys_map src[DRM_FORMAT_MAX_PLANES]; + + struct drm_framebuffer fb = { + .format = drm_format_info(params->format), + }; + + memcpy(fb.pitches, params->src_pitches, DRM_FORMAT_MAX_PLANES * sizeof(int)); + + for (size_t i = 0; i < fb.format->num_planes; i++) { + dst_size[i] = conversion_buf_size(params->format, params->dst_pitches[i], + ¶ms->clip, i); + KUNIT_ASSERT_GT(test, dst_size[i], 0); + + buf[i] = kunit_kzalloc(test, dst_size[i], GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf[i]); + iosys_map_set_vaddr(&dst[i], buf[i]); + + src_cp[i] = cpubuf_to_le32(test, params->src[i], TEST_BUF_SIZE); + iosys_map_set_vaddr(&src[i], src_cp[i]); + } + + const unsigned int *dst_pitches = params->dst_pitches[0] == TEST_USE_DEFAULT_PITCH ? NULL : + params->dst_pitches; + + drm_fb_memcpy(dst, dst_pitches, src, &fb, ¶ms->clip); + + for (size_t i = 0; i < fb.format->num_planes; i++) { + expected[i] = cpubuf_to_le32(test, params->expected[i], TEST_BUF_SIZE); + KUNIT_EXPECT_MEMEQ_MSG(test, buf[i], expected[i], dst_size[i], + "Failed expectation on plane %zu", i); + } +} + static struct kunit_case drm_format_helper_test_cases[] = { KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_gray8, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_rgb332, convert_xrgb8888_gen_params), @@ -862,6 +1557,10 @@ static struct kunit_case drm_format_helper_test_cases[] = { KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_xrgb2101010, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_argb2101010, convert_xrgb8888_gen_params), KUNIT_CASE_PARAM(drm_test_fb_xrgb8888_to_mono, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_swab, convert_xrgb8888_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_clip_offset, clip_offset_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_build_fourcc_list, fb_build_fourcc_list_gen_params), + KUNIT_CASE_PARAM(drm_test_fb_memcpy, fb_memcpy_gen_params), {} }; diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index fe56beea3e93..8ebd7134ee21 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c @@ -175,6 +175,7 @@ static void tilcdc_fini(struct drm_device *dev) drm_dev_unregister(dev); drm_kms_helper_poll_fini(dev); + drm_atomic_helper_shutdown(dev); tilcdc_irq_uninstall(dev); drm_mode_config_cleanup(dev); @@ -389,6 +390,7 @@ static int tilcdc_init(const struct drm_driver *ddrv, struct device *dev) init_failed: tilcdc_fini(ddev); + platform_set_drvdata(pdev, NULL); return ret; } @@ -537,7 +539,8 @@ static void tilcdc_unbind(struct device *dev) if (!ddev->dev_private) return; - tilcdc_fini(dev_get_drvdata(dev)); + tilcdc_fini(ddev); + dev_set_drvdata(dev, NULL); } static const struct component_master_ops tilcdc_comp_ops = { @@ -582,6 +585,11 @@ static int tilcdc_pdev_remove(struct platform_device *pdev) return 0; } +static void tilcdc_pdev_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id tilcdc_of_match[] = { { .compatible = "ti,am33xx-tilcdc", }, { .compatible = "ti,da850-tilcdc", }, @@ -592,6 +600,7 @@ MODULE_DEVICE_TABLE(of, tilcdc_of_match); static struct platform_driver tilcdc_platform_driver = { .probe = tilcdc_pdev_probe, .remove = tilcdc_pdev_remove, + .shutdown = tilcdc_pdev_shutdown, .driver = { .name = "tilcdc", .pm = pm_sleep_ptr(&tilcdc_pm_ops), diff --git a/drivers/gpu/drm/tiny/bochs.c b/drivers/gpu/drm/tiny/bochs.c index d254679a136e..c23c9f0cf49c 100644 --- a/drivers/gpu/drm/tiny/bochs.c +++ b/drivers/gpu/drm/tiny/bochs.c @@ -690,6 +690,11 @@ static void bochs_pci_remove(struct pci_dev *pdev) drm_dev_put(dev); } +static void bochs_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + static const struct pci_device_id bochs_pci_tbl[] = { { .vendor = 0x1234, @@ -720,6 +725,7 @@ static struct pci_driver bochs_pci_driver = { .id_table = bochs_pci_tbl, .probe = bochs_pci_probe, .remove = bochs_pci_remove, + .shutdown = bochs_pci_shutdown, .driver.pm = &bochs_pm_ops, }; diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c index 594bc472862f..c5c34cd2edc1 100644 --- a/drivers/gpu/drm/tiny/cirrus.c +++ b/drivers/gpu/drm/tiny/cirrus.c @@ -727,6 +727,11 @@ static void cirrus_pci_remove(struct pci_dev *pdev) drm_atomic_helper_shutdown(dev); } +static void cirrus_pci_shutdown(struct pci_dev *pdev) +{ + drm_atomic_helper_shutdown(pci_get_drvdata(pdev)); +} + static const struct pci_device_id pciidlist[] = { { .vendor = PCI_VENDOR_ID_CIRRUS, @@ -748,6 +753,7 @@ static struct pci_driver cirrus_pci_driver = { .id_table = pciidlist, .probe = cirrus_pci_probe, .remove = cirrus_pci_remove, + .shutdown = cirrus_pci_shutdown, }; drm_module_pci_driver(cirrus_pci_driver) diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index c5bb683e440c..0187539ff5ea 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -70,10 +70,10 @@ MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)"); #define READ_STATUS_SIZE 13 #define MISC_VALUE_SIZE 4 -#define CMD_TIMEOUT msecs_to_jiffies(200) -#define DATA_TIMEOUT msecs_to_jiffies(1000) -#define IDLE_TIMEOUT msecs_to_jiffies(2000) -#define FIRST_FRAME_TIMEOUT msecs_to_jiffies(2000) +#define CMD_TIMEOUT 200 +#define DATA_TIMEOUT 1000 +#define IDLE_TIMEOUT 2000 +#define FIRST_FRAME_TIMEOUT 2000 #define MISC_REQ_GET_SET_ECO_A 0xff #define MISC_REQ_GET_SET_ECO_B 0x35 @@ -389,7 +389,7 @@ static void gm12u320_fb_update_work(struct work_struct *work) * switches back to showing its logo. */ queue_delayed_work(system_long_wq, &gm12u320->fb_update.work, - IDLE_TIMEOUT); + msecs_to_jiffies(IDLE_TIMEOUT)); return; err: diff --git a/drivers/gpu/drm/tiny/repaper.c b/drivers/gpu/drm/tiny/repaper.c index 13ae148f59b9..73dd4f4289c2 100644 --- a/drivers/gpu/drm/tiny/repaper.c +++ b/drivers/gpu/drm/tiny/repaper.c @@ -949,7 +949,7 @@ static int repaper_probe(struct spi_device *spi) match = device_get_match_data(dev); if (match) { - model = (enum repaper_model)match; + model = (enum repaper_model)(uintptr_t)match; } else { spi_id = spi_get_device_id(spi); model = (enum repaper_model)spi_id->driver_data; diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c index ff86ba1ae1b8..9c597461d1e2 100644 --- a/drivers/gpu/drm/tiny/simpledrm.c +++ b/drivers/gpu/drm/tiny/simpledrm.c @@ -6,6 +6,7 @@ #include <linux/of_address.h> #include <linux/platform_data/simplefb.h> #include <linux/platform_device.h> +#include <linux/pm_domain.h> #include <linux/regulator/consumer.h> #include <drm/drm_aperture.h> @@ -227,6 +228,12 @@ struct simpledrm_device { unsigned int regulator_count; struct regulator **regulators; #endif + /* power-domains */ +#if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS + int pwr_dom_count; + struct device **pwr_dom_devs; + struct device_link **pwr_dom_links; +#endif /* simplefb settings */ struct drm_display_mode mode; @@ -468,6 +475,101 @@ static int simpledrm_device_init_regulators(struct simpledrm_device *sdev) } #endif +#if defined CONFIG_OF && defined CONFIG_PM_GENERIC_DOMAINS +/* + * Generic power domain handling code. + * + * Here we handle the power-domains properties of our "simple-framebuffer" + * dt node. This is only necessary if there is more than one power-domain. + * A single power-domains is handled automatically by the driver core. Multiple + * power-domains have to be handled by drivers since the driver core can't know + * the correct power sequencing. Power sequencing is not an issue for simpledrm + * since the bootloader has put the power domains already in the correct state. + * simpledrm has only to ensure they remain active for its lifetime. + * + * When the driver unloads, we detach from the power-domains. + * + * We only complain about errors here, no action is taken as the most likely + * error can only happen due to a mismatch between the bootloader which set + * up the "simple-framebuffer" dt node, and the PM domain providers in the + * device tree. Chances are that there are no adverse effects, and if there are, + * a clean teardown of the fb probe will not help us much either. So just + * complain and carry on, and hope that the user actually gets a working fb at + * the end of things. + */ +static void simpledrm_device_detach_genpd(void *res) +{ + int i; + struct simpledrm_device *sdev = res; + + if (sdev->pwr_dom_count <= 1) + return; + + for (i = sdev->pwr_dom_count - 1; i >= 0; i--) { + if (!sdev->pwr_dom_links[i]) + device_link_del(sdev->pwr_dom_links[i]); + if (!IS_ERR_OR_NULL(sdev->pwr_dom_devs[i])) + dev_pm_domain_detach(sdev->pwr_dom_devs[i], true); + } +} + +static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) +{ + struct device *dev = sdev->dev.dev; + int i; + + sdev->pwr_dom_count = of_count_phandle_with_args(dev->of_node, "power-domains", + "#power-domain-cells"); + /* + * Single power-domain devices are handled by driver core nothing to do + * here. The same for device nodes without "power-domains" property. + */ + if (sdev->pwr_dom_count <= 1) + return 0; + + sdev->pwr_dom_devs = devm_kcalloc(dev, sdev->pwr_dom_count, + sizeof(*sdev->pwr_dom_devs), + GFP_KERNEL); + if (!sdev->pwr_dom_devs) + return -ENOMEM; + + sdev->pwr_dom_links = devm_kcalloc(dev, sdev->pwr_dom_count, + sizeof(*sdev->pwr_dom_links), + GFP_KERNEL); + if (!sdev->pwr_dom_links) + return -ENOMEM; + + for (i = 0; i < sdev->pwr_dom_count; i++) { + sdev->pwr_dom_devs[i] = dev_pm_domain_attach_by_id(dev, i); + if (IS_ERR(sdev->pwr_dom_devs[i])) { + int ret = PTR_ERR(sdev->pwr_dom_devs[i]); + if (ret == -EPROBE_DEFER) { + simpledrm_device_detach_genpd(sdev); + return ret; + } + drm_warn(&sdev->dev, + "pm_domain_attach_by_id(%u) failed: %d\n", i, ret); + continue; + } + + sdev->pwr_dom_links[i] = device_link_add(dev, + sdev->pwr_dom_devs[i], + DL_FLAG_STATELESS | + DL_FLAG_PM_RUNTIME | + DL_FLAG_RPM_ACTIVE); + if (!sdev->pwr_dom_links[i]) + drm_warn(&sdev->dev, "failed to link power-domain %d\n", i); + } + + return devm_add_action_or_reset(dev, simpledrm_device_detach_genpd, sdev); +} +#else +static int simpledrm_device_attach_genpd(struct simpledrm_device *sdev) +{ + return 0; +} +#endif + /* * Modesetting */ @@ -653,6 +755,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv, ret = simpledrm_device_init_regulators(sdev); if (ret) return ERR_PTR(ret); + ret = simpledrm_device_attach_genpd(sdev); + if (ret) + return ERR_PTR(ret); if (pd) { width = simplefb_get_width_pd(dev, pd); diff --git a/drivers/gpu/drm/tve200/tve200_drv.c b/drivers/gpu/drm/tve200/tve200_drv.c index 0bb56d063536..acce210e2554 100644 --- a/drivers/gpu/drm/tve200/tve200_drv.c +++ b/drivers/gpu/drm/tve200/tve200_drv.c @@ -242,6 +242,7 @@ static void tve200_remove(struct platform_device *pdev) struct tve200_drm_dev_private *priv = drm->dev_private; drm_dev_unregister(drm); + drm_atomic_helper_shutdown(drm); if (priv->panel) drm_panel_bridge_remove(priv->bridge); drm_mode_config_cleanup(drm); @@ -249,6 +250,11 @@ static void tve200_remove(struct platform_device *pdev) drm_dev_put(drm); } +static void tve200_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id tve200_of_match[] = { { .compatible = "faraday,tve200", @@ -263,6 +269,7 @@ static struct platform_driver tve200_driver = { }, .probe = tve200_probe, .remove_new = tve200_remove, + .shutdown = tve200_shutdown, }; drm_module_platform_driver(tve200_driver); diff --git a/drivers/gpu/drm/vboxvideo/vbox_drv.c b/drivers/gpu/drm/vboxvideo/vbox_drv.c index 4fee15c97c34..047b95812334 100644 --- a/drivers/gpu/drm/vboxvideo/vbox_drv.c +++ b/drivers/gpu/drm/vboxvideo/vbox_drv.c @@ -12,6 +12,7 @@ #include <linux/vt_kern.h> #include <drm/drm_aperture.h> +#include <drm/drm_atomic_helper.h> #include <drm/drm_drv.h> #include <drm/drm_fbdev_generic.h> #include <drm/drm_file.h> @@ -97,11 +98,19 @@ static void vbox_pci_remove(struct pci_dev *pdev) struct vbox_private *vbox = pci_get_drvdata(pdev); drm_dev_unregister(&vbox->ddev); + drm_atomic_helper_shutdown(&vbox->ddev); vbox_irq_fini(vbox); vbox_mode_fini(vbox); vbox_hw_fini(vbox); } +static void vbox_pci_shutdown(struct pci_dev *pdev) +{ + struct vbox_private *vbox = pci_get_drvdata(pdev); + + drm_atomic_helper_shutdown(&vbox->ddev); +} + static int vbox_pm_suspend(struct device *dev) { struct vbox_private *vbox = dev_get_drvdata(dev); @@ -165,6 +174,7 @@ static struct pci_driver vbox_pci_driver = { .id_table = pciidlist, .probe = vbox_pci_probe, .remove = vbox_pci_remove, + .shutdown = vbox_pci_shutdown, .driver.pm = pm_sleep_ptr(&vbox_pm_ops), }; diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 1b3531374967..c133e96b8aca 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -324,21 +324,21 @@ static int vc4_drm_bind(struct device *dev) if (!is_vc5) { ret = drmm_mutex_init(drm, &vc4->bin_bo_lock); if (ret) - return ret; + goto err; ret = vc4_bo_cache_init(drm); if (ret) - return ret; + goto err; } ret = drmm_mode_config_init(drm); if (ret) - return ret; + goto err; if (!is_vc5) { ret = vc4_gem_init(drm); if (ret) - return ret; + goto err; } node = of_find_compatible_node(NULL, NULL, "raspberrypi,bcm2835-firmware"); @@ -346,13 +346,15 @@ static int vc4_drm_bind(struct device *dev) firmware = rpi_firmware_get(node); of_node_put(node); - if (!firmware) - return -EPROBE_DEFER; + if (!firmware) { + ret = -EPROBE_DEFER; + goto err; + } } ret = drm_aperture_remove_framebuffers(driver); if (ret) - return ret; + goto err; if (firmware) { ret = rpi_firmware_property(firmware, @@ -366,32 +368,33 @@ static int vc4_drm_bind(struct device *dev) ret = component_bind_all(dev, drm); if (ret) - return ret; + goto err; ret = devm_add_action_or_reset(dev, vc4_component_unbind_all, vc4); if (ret) - return ret; + goto err; ret = vc4_plane_create_additional_planes(drm); if (ret) - goto unbind_all; + goto err; ret = vc4_kms_load(drm); if (ret < 0) - goto unbind_all; + goto err; drm_for_each_crtc(crtc, drm) vc4_crtc_disable_at_boot(crtc); ret = drm_dev_register(drm, 0); if (ret < 0) - goto unbind_all; + goto err; drm_fbdev_dma_setup(drm, 16); return 0; -unbind_all: +err: + platform_set_drvdata(pdev, NULL); return ret; } @@ -401,6 +404,7 @@ static void vc4_drm_unbind(struct device *dev) drm_dev_unplug(drm); drm_atomic_helper_shutdown(drm); + dev_set_drvdata(dev, NULL); } static const struct component_master_ops vc4_drm_ops = { @@ -444,6 +448,11 @@ static void vc4_platform_drm_remove(struct platform_device *pdev) component_master_del(&pdev->dev, &vc4_drm_ops); } +static void vc4_platform_drm_shutdown(struct platform_device *pdev) +{ + drm_atomic_helper_shutdown(platform_get_drvdata(pdev)); +} + static const struct of_device_id vc4_of_match[] = { { .compatible = "brcm,bcm2711-vc5", }, { .compatible = "brcm,bcm2835-vc4", }, @@ -455,6 +464,7 @@ MODULE_DEVICE_TABLE(of, vc4_of_match); static struct platform_driver vc4_platform_driver = { .probe = vc4_platform_drm_probe, .remove_new = vc4_platform_drm_remove, + .shutdown = vc4_platform_drm_shutdown, .driver = { .name = "vc4-drm", .of_match_table = vc4_of_match, diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h index 4126c384286b..8513b671f871 100644 --- a/drivers/gpu/drm/virtio/virtgpu_drv.h +++ b/drivers/gpu/drm/virtio/virtgpu_drv.h @@ -344,8 +344,6 @@ void virtio_gpu_object_attach(struct virtio_gpu_device *vgdev, struct virtio_gpu_object *obj, struct virtio_gpu_mem_entry *ents, unsigned int nents); -int virtio_gpu_attach_status_page(struct virtio_gpu_device *vgdev); -int virtio_gpu_detach_status_page(struct virtio_gpu_device *vgdev); void virtio_gpu_cursor_ping(struct virtio_gpu_device *vgdev, struct virtio_gpu_output *output); int virtio_gpu_cmd_get_display_info(struct virtio_gpu_device *vgdev); @@ -394,11 +392,8 @@ virtio_gpu_cmd_resource_create_3d(struct virtio_gpu_device *vgdev, struct virtio_gpu_fence *fence); void virtio_gpu_ctrl_ack(struct virtqueue *vq); void virtio_gpu_cursor_ack(struct virtqueue *vq); -void virtio_gpu_fence_ack(struct virtqueue *vq); void virtio_gpu_dequeue_ctrl_func(struct work_struct *work); void virtio_gpu_dequeue_cursor_func(struct work_struct *work); -void virtio_gpu_dequeue_fence_func(struct work_struct *work); - void virtio_gpu_notify(struct virtio_gpu_device *vgdev); int @@ -465,8 +460,6 @@ struct dma_buf *virtgpu_gem_prime_export(struct drm_gem_object *obj, int flags); struct drm_gem_object *virtgpu_gem_prime_import(struct drm_device *dev, struct dma_buf *buf); -int virtgpu_gem_prime_get_uuid(struct drm_gem_object *obj, - uuid_t *uuid); struct drm_gem_object *virtgpu_gem_prime_import_sg_table( struct drm_device *dev, struct dma_buf_attachment *attach, struct sg_table *sgt); diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index d5d4f642d367..3c99fb8b54e2 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -408,15 +408,10 @@ void vkms_set_composer(struct vkms_output *out, bool enabled) if (enabled) drm_crtc_vblank_get(&out->crtc); - mutex_lock(&out->enabled_lock); + spin_lock_irq(&out->lock); old_enabled = out->composer_enabled; out->composer_enabled = enabled; - - /* the composition wasn't enabled, so unlock the lock to make sure the lock - * will be balanced even if we have a failed commit - */ - if (!out->composer_enabled) - mutex_unlock(&out->enabled_lock); + spin_unlock_irq(&out->lock); if (old_enabled) drm_crtc_vblank_put(&out->crtc); diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 3c5ebf106b66..61e500b8c9da 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -16,7 +16,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) struct drm_crtc *crtc = &output->crtc; struct vkms_crtc_state *state; u64 ret_overrun; - bool ret, fence_cookie, composer_enabled; + bool ret, fence_cookie; fence_cookie = dma_fence_begin_signalling(); @@ -25,15 +25,15 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) if (ret_overrun != 1) pr_warn("%s: vblank timer overrun\n", __func__); + spin_lock(&output->lock); ret = drm_crtc_handle_vblank(crtc); if (!ret) DRM_ERROR("vkms failure on handling vblank"); state = output->composer_state; - composer_enabled = output->composer_enabled; - mutex_unlock(&output->enabled_lock); + spin_unlock(&output->lock); - if (state && composer_enabled) { + if (state && output->composer_enabled) { u64 frame = drm_crtc_accurate_vblank_count(crtc); /* update frame_start only if a queued vkms_composer_worker() @@ -295,7 +295,6 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, spin_lock_init(&vkms_out->lock); spin_lock_init(&vkms_out->composer_lock); - mutex_init(&vkms_out->enabled_lock); vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0); if (!vkms_out->composer_workq) diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index c7ae6c2ba1df..8f5710debb1e 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -108,10 +108,8 @@ struct vkms_output { struct workqueue_struct *composer_workq; /* protects concurrent access to composer */ spinlock_t lock; - /* guarantees that if the composer is enabled, a job will be queued */ - struct mutex enabled_lock; - /* protected by @enabled_lock */ + /* protected by @lock */ bool composer_enabled; struct vkms_crtc_state *composer_state; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c index c0da89e16e6f..a07e5b7e2f2f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gem.c @@ -232,6 +232,7 @@ static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused) list_for_each_entry(file, &dev->filelist, lhead) { struct task_struct *task; struct drm_gem_object *gobj; + struct pid *pid; int id; /* @@ -241,8 +242,9 @@ static int vmw_debugfs_gem_info_show(struct seq_file *m, void *unused) * Therefore, we need to protect this ->comm access using RCU. */ rcu_read_lock(); - task = pid_task(file->pid, PIDTYPE_TGID); - seq_printf(m, "pid %8d command %s:\n", pid_nr(file->pid), + pid = rcu_dereference(file->pid); + task = pid_task(pid, PIDTYPE_TGID); + seq_printf(m, "pid %8d command %s:\n", pid_nr(pid), task ? task->comm : "<unknown>"); rcu_read_unlock(); |