diff options
Diffstat (limited to 'drivers')
121 files changed, 6807 insertions, 633 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 95fc0410e129..ebfe8404c25f 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -240,6 +240,10 @@ source "drivers/gpu/drm/mediatek/Kconfig" source "drivers/gpu/drm/zte/Kconfig" +source "drivers/gpu/drm/mxsfb/Kconfig" + +source "drivers/gpu/drm/meson/Kconfig" + # Keep legacy drivers last menuconfig DRM_LEGACY diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index 883f3e75cfbc..b9ae4280de9d 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -81,6 +81,7 @@ obj-$(CONFIG_DRM_TEGRA) += tegra/ obj-$(CONFIG_DRM_STI) += sti/ obj-$(CONFIG_DRM_IMX) += imx/ obj-$(CONFIG_DRM_MEDIATEK) += mediatek/ +obj-$(CONFIG_DRM_MESON) += meson/ obj-y += i2c/ obj-y += panel/ obj-y += bridge/ @@ -89,3 +90,4 @@ obj-$(CONFIG_DRM_ETNAVIV) += etnaviv/ obj-$(CONFIG_DRM_ARCPGU)+= arc/ obj-y += hisilicon/ obj-$(CONFIG_DRM_ZTE) += zte/ +obj-$(CONFIG_DRM_MXSFB) += mxsfb/ diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index f53e52f4d672..54ac8a845e9f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -842,6 +842,8 @@ struct amdgpu_gfx_funcs { uint64_t (*get_gpu_clock_counter)(struct amdgpu_device *adev); void (*select_se_sh)(struct amdgpu_device *adev, u32 se_num, u32 sh_num, u32 instance); void (*read_wave_data)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields); + void (*read_wave_vgprs)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t thread, uint32_t start, uint32_t size, uint32_t *dst); + void (*read_wave_sgprs)(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t start, uint32_t size, uint32_t *dst); }; struct amdgpu_gfx { @@ -1330,6 +1332,7 @@ struct amdgpu_device { /* BIOS */ uint8_t *bios; + uint32_t bios_size; bool is_atom_bios; struct amdgpu_bo *stollen_vga_memory; uint32_t bios_scratch[AMDGPU_BIOS_NUM_SCRATCH]; @@ -1679,8 +1682,6 @@ uint32_t amdgpu_ttm_tt_pte_flags(struct amdgpu_device *adev, struct ttm_tt *ttm, void amdgpu_vram_location(struct amdgpu_device *adev, struct amdgpu_mc *mc, u64 base); void amdgpu_gtt_location(struct amdgpu_device *adev, struct amdgpu_mc *mc); void amdgpu_ttm_set_active_vram_size(struct amdgpu_device *adev, u64 size); -u64 amdgpu_ttm_get_gtt_mem_size(struct amdgpu_device *adev); -int amdgpu_ttm_global_init(struct amdgpu_device *adev); int amdgpu_ttm_init(struct amdgpu_device *adev); void amdgpu_ttm_fini(struct amdgpu_device *adev); void amdgpu_program_register_sequence(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c index b7e2762fcdd2..4f973a9c7b87 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_bios.c @@ -74,6 +74,7 @@ static bool igp_read_bios_from_vram(struct amdgpu_device *adev) iounmap(bios); return false; } + adev->bios_size = size; memcpy_fromio(adev->bios, bios, size); iounmap(bios); return true; @@ -103,6 +104,7 @@ bool amdgpu_read_bios(struct amdgpu_device *adev) pci_unmap_rom(adev->pdev, bios); return false; } + adev->bios_size = size; memcpy_fromio(adev->bios, bios, size); pci_unmap_rom(adev->pdev, bios); return true; @@ -135,6 +137,7 @@ static bool amdgpu_read_bios_from_rom(struct amdgpu_device *adev) DRM_ERROR("no memory to allocate for BIOS\n"); return false; } + adev->bios_size = len; /* read complete BIOS */ return amdgpu_asic_read_bios_from_rom(adev, adev->bios, len); @@ -159,6 +162,7 @@ static bool amdgpu_read_platform_bios(struct amdgpu_device *adev) if (adev->bios == NULL) { return false; } + adev->bios_size = size; return true; } @@ -273,6 +277,7 @@ static bool amdgpu_atrm_get_bios(struct amdgpu_device *adev) kfree(adev->bios); return false; } + adev->bios_size = size; return true; } #else @@ -334,6 +339,7 @@ static bool amdgpu_acpi_vfct_bios(struct amdgpu_device *adev) } adev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL); + adev->bios_size = vhdr->ImageLength; ret = !!adev->bios; out_unmap: diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c index 7ded61e6dd81..9ada56c16a58 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cgs.c @@ -723,7 +723,7 @@ static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device, enum cgs_ucode_id type) { CGS_FUNC_ADEV; - uint16_t fw_version; + uint16_t fw_version = 0; switch (type) { case CGS_UCODE_ID_SDMA0: @@ -753,9 +753,11 @@ static uint16_t amdgpu_get_firmware_version(struct cgs_device *cgs_device, case CGS_UCODE_ID_RLC_G: fw_version = adev->gfx.rlc_fw_version; break; + case CGS_UCODE_ID_STORAGE: + break; default: DRM_ERROR("firmware type %d do not have version\n", type); - fw_version = 0; + break; } return fw_version; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index 5a277495d6a3..29d6d84d1c28 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -451,7 +451,7 @@ static int amdgpu_cs_validate(void *param, struct amdgpu_bo *bo) return r; if (bo->shadow) - r = amdgpu_cs_bo_validate(p, bo); + r = amdgpu_cs_bo_validate(p, bo->shadow); return r; } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 5cc0ea344d1a..f6394b5793c2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1470,20 +1470,26 @@ static int amdgpu_fini(struct amdgpu_device *adev) amdgpu_wb_fini(adev); amdgpu_vram_scratch_fini(adev); } - /* ungate blocks before hw fini so that we can shutdown the blocks safely */ - r = adev->ip_blocks[i].version->funcs->set_clockgating_state((void *)adev, - AMD_CG_STATE_UNGATE); - if (r) { - DRM_ERROR("set_clockgating_state(ungate) of IP block <%s> failed %d\n", - adev->ip_blocks[i].version->funcs->name, r); - return r; + + if (adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_UVD && + adev->ip_blocks[i].version->type != AMD_IP_BLOCK_TYPE_VCE) { + /* ungate blocks before hw fini so that we can shutdown the blocks safely */ + r = adev->ip_blocks[i].version->funcs->set_clockgating_state((void *)adev, + AMD_CG_STATE_UNGATE); + if (r) { + DRM_ERROR("set_clockgating_state(ungate) of IP block <%s> failed %d\n", + adev->ip_blocks[i].version->funcs->name, r); + return r; + } } + r = adev->ip_blocks[i].version->funcs->hw_fini((void *)adev); /* XXX handle errors */ if (r) { DRM_DEBUG("hw_fini of IP block <%s> failed %d\n", adev->ip_blocks[i].version->funcs->name, r); } + adev->ip_blocks[i].status.hw = false; } @@ -2973,6 +2979,66 @@ static ssize_t amdgpu_debugfs_wave_read(struct file *f, char __user *buf, return result; } +static ssize_t amdgpu_debugfs_gpr_read(struct file *f, char __user *buf, + size_t size, loff_t *pos) +{ + struct amdgpu_device *adev = f->f_inode->i_private; + int r; + ssize_t result = 0; + uint32_t offset, se, sh, cu, wave, simd, thread, bank, *data; + + if (size & 3 || *pos & 3) + return -EINVAL; + + /* decode offset */ + offset = (*pos & 0xFFF); /* in dwords */ + se = ((*pos >> 12) & 0xFF); + sh = ((*pos >> 20) & 0xFF); + cu = ((*pos >> 28) & 0xFF); + wave = ((*pos >> 36) & 0xFF); + simd = ((*pos >> 44) & 0xFF); + thread = ((*pos >> 52) & 0xFF); + bank = ((*pos >> 60) & 1); + + data = kmalloc_array(1024, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + /* switch to the specific se/sh/cu */ + mutex_lock(&adev->grbm_idx_mutex); + amdgpu_gfx_select_se_sh(adev, se, sh, cu); + + if (bank == 0) { + if (adev->gfx.funcs->read_wave_vgprs) + adev->gfx.funcs->read_wave_vgprs(adev, simd, wave, thread, offset, size>>2, data); + } else { + if (adev->gfx.funcs->read_wave_sgprs) + adev->gfx.funcs->read_wave_sgprs(adev, simd, wave, offset, size>>2, data); + } + + amdgpu_gfx_select_se_sh(adev, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF); + mutex_unlock(&adev->grbm_idx_mutex); + + while (size) { + uint32_t value; + + value = data[offset++]; + r = put_user(value, (uint32_t *)buf); + if (r) { + result = r; + goto err; + } + + result += 4; + buf += 4; + size -= 4; + } + +err: + kfree(data); + return result; +} + static const struct file_operations amdgpu_debugfs_regs_fops = { .owner = THIS_MODULE, .read = amdgpu_debugfs_regs_read, @@ -3015,6 +3081,11 @@ static const struct file_operations amdgpu_debugfs_wave_fops = { .read = amdgpu_debugfs_wave_read, .llseek = default_llseek }; +static const struct file_operations amdgpu_debugfs_gpr_fops = { + .owner = THIS_MODULE, + .read = amdgpu_debugfs_gpr_read, + .llseek = default_llseek +}; static const struct file_operations *debugfs_regs[] = { &amdgpu_debugfs_regs_fops, @@ -3024,6 +3095,7 @@ static const struct file_operations *debugfs_regs[] = { &amdgpu_debugfs_gca_config_fops, &amdgpu_debugfs_sensors_fops, &amdgpu_debugfs_wave_fops, + &amdgpu_debugfs_gpr_fops, }; static const char *debugfs_regs_names[] = { @@ -3034,6 +3106,7 @@ static const char *debugfs_regs_names[] = { "amdgpu_gca_config", "amdgpu_sensors", "amdgpu_wave", + "amdgpu_gpr", }; static int amdgpu_debugfs_regs_init(struct amdgpu_device *adev) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c index 741144fcc7bc..581601ca6b89 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_display.c @@ -187,7 +187,7 @@ int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc, goto cleanup; } - r = amdgpu_bo_pin_restricted(new_abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, &base); + r = amdgpu_bo_pin(new_abo, AMDGPU_GEM_DOMAIN_VRAM, &base); if (unlikely(r != 0)) { r = -EINVAL; DRM_ERROR("failed to pin new abo buffer before flip\n"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c index f1c9e59a7c87..24629bec181a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fb.c @@ -171,7 +171,7 @@ static int amdgpufb_create_pinned_object(struct amdgpu_fbdev *rfbdev, } - ret = amdgpu_bo_pin_restricted(abo, AMDGPU_GEM_DOMAIN_VRAM, 0, 0, NULL); + ret = amdgpu_bo_pin(abo, AMDGPU_GEM_DOMAIN_VRAM, NULL); if (ret) { amdgpu_bo_unreserve(abo); goto out_unref; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c index 3c634f02a3d5..00f46b0e076d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gtt_mgr.c @@ -164,8 +164,10 @@ static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man, spin_unlock(&mgr->lock); node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - return -ENOMEM; + if (!node) { + r = -ENOMEM; + goto err_out; + } node->start = AMDGPU_BO_INVALID_OFFSET; node->size = mem->num_pages; @@ -176,12 +178,20 @@ static int amdgpu_gtt_mgr_new(struct ttm_mem_type_manager *man, if (unlikely(r)) { kfree(node); mem->mm_node = NULL; + r = 0; + goto err_out; } } else { mem->start = node->start; } return 0; +err_out: + spin_lock(&mgr->lock); + mgr->available += mem->num_pages; + spin_unlock(&mgr->lock); + + return r; } /** diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index d1cf9ac0dff1..9af87eaf8ee3 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -544,6 +544,32 @@ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file return copy_to_user(out, &vce_clk_table, min((size_t)size, sizeof(vce_clk_table))) ? -EFAULT : 0; } + case AMDGPU_INFO_VBIOS: { + uint32_t bios_size = adev->bios_size; + + switch (info->vbios_info.type) { + case AMDGPU_INFO_VBIOS_SIZE: + return copy_to_user(out, &bios_size, + min((size_t)size, sizeof(bios_size))) + ? -EFAULT : 0; + case AMDGPU_INFO_VBIOS_IMAGE: { + uint8_t *bios; + uint32_t bios_offset = info->vbios_info.offset; + + if (bios_offset >= bios_size) + return -EINVAL; + + bios = adev->bios + bios_offset; + return copy_to_user(out, bios, + min((size_t)size, (size_t)(bios_size - bios_offset))) + ? -EFAULT : 0; + } + default: + DRM_DEBUG_KMS("Invalid request %d\n", + info->vbios_info.type); + return -EINVAL; + } + } default: DRM_DEBUG_KMS("Invalid request %d\n", info->query); return -EINVAL; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c index 8f18b8ed2b3a..bc70f80260d8 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ttm.c @@ -34,7 +34,6 @@ #include <ttm/ttm_placement.h> #include <ttm/ttm_module.h> #include <ttm/ttm_page_alloc.h> -#include <ttm/ttm_memory.h> #include <drm/drmP.h> #include <drm/amdgpu_drm.h> #include <linux/seq_file.h> @@ -65,7 +64,7 @@ static void amdgpu_ttm_mem_global_release(struct drm_global_reference *ref) ttm_mem_global_release(ref->object); } -int amdgpu_ttm_global_init(struct amdgpu_device *adev) +static int amdgpu_ttm_global_init(struct amdgpu_device *adev) { struct drm_global_reference *global_ref; struct amdgpu_ring *ring; @@ -1151,6 +1150,10 @@ int amdgpu_ttm_init(struct amdgpu_device *adev) unsigned i, j; int r; + r = amdgpu_ttm_global_init(adev); + if (r) { + return r; + } /* No others user of address space so set it to 0 */ r = ttm_bo_device_init(&adev->mman.bdev, adev->mman.bo_global_ref.ref.object, @@ -1650,8 +1653,3 @@ static void amdgpu_ttm_debugfs_fini(struct amdgpu_device *adev) #endif } - -u64 amdgpu_ttm_get_gtt_mem_size(struct amdgpu_device *adev) -{ - return ttm_get_kernel_zone_memory_size(adev->mman.mem_global_ref.object); -} diff --git a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c index 1027f92de32b..bda9e3de191e 100644 --- a/drivers/gpu/drm/amd/amdgpu/ci_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/ci_dpm.c @@ -6083,7 +6083,7 @@ ci_dpm_debugfs_print_current_performance_level(struct amdgpu_device *adev, activity_percent = activity_percent > 100 ? 100 : activity_percent; } - seq_printf(m, "uvd %sabled\n", pi->uvd_enabled ? "en" : "dis"); + seq_printf(m, "uvd %sabled\n", pi->uvd_power_gated ? "dis" : "en"); seq_printf(m, "vce %sabled\n", rps->vce_active ? "en" : "dis"); seq_printf(m, "power level avg sclk: %u mclk: %u\n", sclk, mclk); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c index 075aa0b1b075..9999dc71b998 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v10_0.c @@ -2493,6 +2493,9 @@ static int dce_v10_0_cursor_move_locked(struct drm_crtc *crtc, struct amdgpu_device *adev = crtc->dev->dev_private; int xorigin = 0, yorigin = 0; + amdgpu_crtc->cursor_x = x; + amdgpu_crtc->cursor_y = y; + /* avivo cursor are offset into the total surface */ x += crtc->x; y += crtc->y; @@ -2509,11 +2512,6 @@ static int dce_v10_0_cursor_move_locked(struct drm_crtc *crtc, WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y); WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin); - WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, - ((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1)); - - amdgpu_crtc->cursor_x = x; - amdgpu_crtc->cursor_y = y; return 0; } @@ -2539,6 +2537,7 @@ static int dce_v10_0_crtc_cursor_set2(struct drm_crtc *crtc, int32_t hot_y) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; struct drm_gem_object *obj; struct amdgpu_bo *aobj; int ret; @@ -2577,9 +2576,6 @@ static int dce_v10_0_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - amdgpu_crtc->cursor_width = width; - amdgpu_crtc->cursor_height = height; - dce_v10_0_lock_cursor(crtc, true); if (hot_x != amdgpu_crtc->cursor_hot_x || @@ -2595,6 +2591,14 @@ static int dce_v10_0_crtc_cursor_set2(struct drm_crtc *crtc, amdgpu_crtc->cursor_hot_y = hot_y; } + if (width != amdgpu_crtc->cursor_width || + height != amdgpu_crtc->cursor_height) { + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (width - 1) << 16 | (height - 1)); + amdgpu_crtc->cursor_width = width; + amdgpu_crtc->cursor_height = height; + } + dce_v10_0_show_cursor(crtc); dce_v10_0_lock_cursor(crtc, false); @@ -2616,6 +2620,7 @@ unpin: static void dce_v10_0_cursor_reset(struct drm_crtc *crtc) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; if (amdgpu_crtc->cursor_bo) { dce_v10_0_lock_cursor(crtc, true); @@ -2623,6 +2628,10 @@ static void dce_v10_0_cursor_reset(struct drm_crtc *crtc) dce_v10_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x, amdgpu_crtc->cursor_y); + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (amdgpu_crtc->cursor_width - 1) << 16 | + (amdgpu_crtc->cursor_height - 1)); + dce_v10_0_show_cursor(crtc); dce_v10_0_lock_cursor(crtc, false); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c index a6717487ac78..b3d62b909f43 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v11_0.c @@ -2509,6 +2509,9 @@ static int dce_v11_0_cursor_move_locked(struct drm_crtc *crtc, struct amdgpu_device *adev = crtc->dev->dev_private; int xorigin = 0, yorigin = 0; + amdgpu_crtc->cursor_x = x; + amdgpu_crtc->cursor_y = y; + /* avivo cursor are offset into the total surface */ x += crtc->x; y += crtc->y; @@ -2525,11 +2528,6 @@ static int dce_v11_0_cursor_move_locked(struct drm_crtc *crtc, WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y); WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin); - WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, - ((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1)); - - amdgpu_crtc->cursor_x = x; - amdgpu_crtc->cursor_y = y; return 0; } @@ -2555,6 +2553,7 @@ static int dce_v11_0_crtc_cursor_set2(struct drm_crtc *crtc, int32_t hot_y) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; struct drm_gem_object *obj; struct amdgpu_bo *aobj; int ret; @@ -2593,9 +2592,6 @@ static int dce_v11_0_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - amdgpu_crtc->cursor_width = width; - amdgpu_crtc->cursor_height = height; - dce_v11_0_lock_cursor(crtc, true); if (hot_x != amdgpu_crtc->cursor_hot_x || @@ -2611,6 +2607,14 @@ static int dce_v11_0_crtc_cursor_set2(struct drm_crtc *crtc, amdgpu_crtc->cursor_hot_y = hot_y; } + if (width != amdgpu_crtc->cursor_width || + height != amdgpu_crtc->cursor_height) { + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (width - 1) << 16 | (height - 1)); + amdgpu_crtc->cursor_width = width; + amdgpu_crtc->cursor_height = height; + } + dce_v11_0_show_cursor(crtc); dce_v11_0_lock_cursor(crtc, false); @@ -2632,6 +2636,7 @@ unpin: static void dce_v11_0_cursor_reset(struct drm_crtc *crtc) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; if (amdgpu_crtc->cursor_bo) { dce_v11_0_lock_cursor(crtc, true); @@ -2639,6 +2644,10 @@ static void dce_v11_0_cursor_reset(struct drm_crtc *crtc) dce_v11_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x, amdgpu_crtc->cursor_y); + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (amdgpu_crtc->cursor_width - 1) << 16 | + (amdgpu_crtc->cursor_height - 1)); + dce_v11_0_show_cursor(crtc); dce_v11_0_lock_cursor(crtc, false); diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c index 15d98ef696a2..e564442b6393 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v6_0.c @@ -460,9 +460,8 @@ static void dce_v6_0_resume_mc_access(struct amdgpu_device *adev, for (i = 0; i < adev->mode_info.num_crtc; i++) { if (save->crtc_enabled[i]) { tmp = RREG32(mmMASTER_UPDATE_MODE + crtc_offsets[i]); - if ((tmp & 0x7) != 3) { + if ((tmp & 0x7) != 0) { tmp &= ~0x7; - tmp |= 0x3; WREG32(mmMASTER_UPDATE_MODE + crtc_offsets[i], tmp); } tmp = RREG32(mmGRPH_UPDATE + crtc_offsets[i]); @@ -1860,7 +1859,8 @@ static int dce_v6_0_cursor_move_locked(struct drm_crtc *crtc, struct amdgpu_device *adev = crtc->dev->dev_private; int xorigin = 0, yorigin = 0; - int w = amdgpu_crtc->cursor_width; + amdgpu_crtc->cursor_x = x; + amdgpu_crtc->cursor_y = y; /* avivo cursor are offset into the total surface */ x += crtc->x; @@ -1878,11 +1878,7 @@ static int dce_v6_0_cursor_move_locked(struct drm_crtc *crtc, WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y); WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin); - WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, - ((w - 1) << 16) | (amdgpu_crtc->cursor_height - 1)); - amdgpu_crtc->cursor_x = x; - amdgpu_crtc->cursor_y = y; return 0; } @@ -1907,6 +1903,7 @@ static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc, int32_t hot_y) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; struct drm_gem_object *obj; struct amdgpu_bo *aobj; int ret; @@ -1945,12 +1942,11 @@ static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - amdgpu_crtc->cursor_width = width; - amdgpu_crtc->cursor_height = height; - dce_v6_0_lock_cursor(crtc, true); - if (hot_x != amdgpu_crtc->cursor_hot_x || + if (width != amdgpu_crtc->cursor_width || + height != amdgpu_crtc->cursor_height || + hot_x != amdgpu_crtc->cursor_hot_x || hot_y != amdgpu_crtc->cursor_hot_y) { int x, y; @@ -1959,10 +1955,20 @@ static int dce_v6_0_crtc_cursor_set2(struct drm_crtc *crtc, dce_v6_0_cursor_move_locked(crtc, x, y); + amdgpu_crtc->cursor_width = width; + amdgpu_crtc->cursor_height = height; amdgpu_crtc->cursor_hot_x = hot_x; amdgpu_crtc->cursor_hot_y = hot_y; } + if (width != amdgpu_crtc->cursor_width || + height != amdgpu_crtc->cursor_height) { + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (width - 1) << 16 | (height - 1)); + amdgpu_crtc->cursor_width = width; + amdgpu_crtc->cursor_height = height; + } + dce_v6_0_show_cursor(crtc); dce_v6_0_lock_cursor(crtc, false); @@ -1984,6 +1990,7 @@ unpin: static void dce_v6_0_cursor_reset(struct drm_crtc *crtc) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; if (amdgpu_crtc->cursor_bo) { dce_v6_0_lock_cursor(crtc, true); @@ -1991,6 +1998,10 @@ static void dce_v6_0_cursor_reset(struct drm_crtc *crtc) dce_v6_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x, amdgpu_crtc->cursor_y); + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (amdgpu_crtc->cursor_width - 1) << 16 | + (amdgpu_crtc->cursor_height - 1)); + dce_v6_0_show_cursor(crtc); dce_v6_0_lock_cursor(crtc, false); } diff --git a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c index a699896eeabc..6ce7fb42dbef 100644 --- a/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/dce_v8_0.c @@ -2344,6 +2344,9 @@ static int dce_v8_0_cursor_move_locked(struct drm_crtc *crtc, struct amdgpu_device *adev = crtc->dev->dev_private; int xorigin = 0, yorigin = 0; + amdgpu_crtc->cursor_x = x; + amdgpu_crtc->cursor_y = y; + /* avivo cursor are offset into the total surface */ x += crtc->x; y += crtc->y; @@ -2360,11 +2363,6 @@ static int dce_v8_0_cursor_move_locked(struct drm_crtc *crtc, WREG32(mmCUR_POSITION + amdgpu_crtc->crtc_offset, (x << 16) | y); WREG32(mmCUR_HOT_SPOT + amdgpu_crtc->crtc_offset, (xorigin << 16) | yorigin); - WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, - ((amdgpu_crtc->cursor_width - 1) << 16) | (amdgpu_crtc->cursor_height - 1)); - - amdgpu_crtc->cursor_x = x; - amdgpu_crtc->cursor_y = y; return 0; } @@ -2390,6 +2388,7 @@ static int dce_v8_0_crtc_cursor_set2(struct drm_crtc *crtc, int32_t hot_y) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; struct drm_gem_object *obj; struct amdgpu_bo *aobj; int ret; @@ -2428,9 +2427,6 @@ static int dce_v8_0_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - amdgpu_crtc->cursor_width = width; - amdgpu_crtc->cursor_height = height; - dce_v8_0_lock_cursor(crtc, true); if (hot_x != amdgpu_crtc->cursor_hot_x || @@ -2442,10 +2438,20 @@ static int dce_v8_0_crtc_cursor_set2(struct drm_crtc *crtc, dce_v8_0_cursor_move_locked(crtc, x, y); + amdgpu_crtc->cursor_width = width; + amdgpu_crtc->cursor_height = height; amdgpu_crtc->cursor_hot_x = hot_x; amdgpu_crtc->cursor_hot_y = hot_y; } + if (width != amdgpu_crtc->cursor_width || + height != amdgpu_crtc->cursor_height) { + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (width - 1) << 16 | (height - 1)); + amdgpu_crtc->cursor_width = width; + amdgpu_crtc->cursor_height = height; + } + dce_v8_0_show_cursor(crtc); dce_v8_0_lock_cursor(crtc, false); @@ -2467,6 +2473,7 @@ unpin: static void dce_v8_0_cursor_reset(struct drm_crtc *crtc) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); + struct amdgpu_device *adev = crtc->dev->dev_private; if (amdgpu_crtc->cursor_bo) { dce_v8_0_lock_cursor(crtc, true); @@ -2474,6 +2481,10 @@ static void dce_v8_0_cursor_reset(struct drm_crtc *crtc) dce_v8_0_cursor_move_locked(crtc, amdgpu_crtc->cursor_x, amdgpu_crtc->cursor_y); + WREG32(mmCUR_SIZE + amdgpu_crtc->crtc_offset, + (amdgpu_crtc->cursor_width - 1) << 16 | + (amdgpu_crtc->cursor_height - 1)); + dce_v8_0_show_cursor(crtc); dce_v8_0_lock_cursor(crtc, false); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index 879a94bbfe12..558640aee15a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -2827,6 +2827,21 @@ static uint32_t wave_read_ind(struct amdgpu_device *adev, uint32_t simd, uint32_ return RREG32(mmSQ_IND_DATA); } +static void wave_read_regs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t thread, + uint32_t regno, uint32_t num, uint32_t *out) +{ + WREG32(mmSQ_IND_INDEX, + (wave << SQ_IND_INDEX__WAVE_ID__SHIFT) | + (simd << SQ_IND_INDEX__SIMD_ID__SHIFT) | + (regno << SQ_IND_INDEX__INDEX__SHIFT) | + (thread << SQ_IND_INDEX__THREAD_ID__SHIFT) | + (SQ_IND_INDEX__FORCE_READ_MASK) | + (SQ_IND_INDEX__AUTO_INCR_MASK)); + while (num--) + *(out++) = RREG32(mmSQ_IND_DATA); +} + static void gfx_v6_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields) { /* type 0 wave data */ @@ -2851,10 +2866,20 @@ static void gfx_v6_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, u dst[(*no_fields)++] = wave_read_ind(adev, simd, wave, ixSQ_WAVE_M0); } +static void gfx_v6_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t start, + uint32_t size, uint32_t *dst) +{ + wave_read_regs( + adev, simd, wave, 0, + start + SQIND_WAVE_SGPRS_OFFSET, size, dst); +} + static const struct amdgpu_gfx_funcs gfx_v6_0_gfx_funcs = { .get_gpu_clock_counter = &gfx_v6_0_get_gpu_clock_counter, .select_se_sh = &gfx_v6_0_select_se_sh, .read_wave_data = &gfx_v6_0_read_wave_data, + .read_wave_sgprs = &gfx_v6_0_read_wave_sgprs, }; static int gfx_v6_0_early_init(void *handle) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 1a745cf93f47..c4e14015ec5b 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -4380,6 +4380,21 @@ static uint32_t wave_read_ind(struct amdgpu_device *adev, uint32_t simd, uint32_ return RREG32(mmSQ_IND_DATA); } +static void wave_read_regs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t thread, + uint32_t regno, uint32_t num, uint32_t *out) +{ + WREG32(mmSQ_IND_INDEX, + (wave << SQ_IND_INDEX__WAVE_ID__SHIFT) | + (simd << SQ_IND_INDEX__SIMD_ID__SHIFT) | + (regno << SQ_IND_INDEX__INDEX__SHIFT) | + (thread << SQ_IND_INDEX__THREAD_ID__SHIFT) | + (SQ_IND_INDEX__FORCE_READ_MASK) | + (SQ_IND_INDEX__AUTO_INCR_MASK)); + while (num--) + *(out++) = RREG32(mmSQ_IND_DATA); +} + static void gfx_v7_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields) { /* type 0 wave data */ @@ -4404,10 +4419,20 @@ static void gfx_v7_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, u dst[(*no_fields)++] = wave_read_ind(adev, simd, wave, ixSQ_WAVE_M0); } +static void gfx_v7_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t start, + uint32_t size, uint32_t *dst) +{ + wave_read_regs( + adev, simd, wave, 0, + start + SQIND_WAVE_SGPRS_OFFSET, size, dst); +} + static const struct amdgpu_gfx_funcs gfx_v7_0_gfx_funcs = { .get_gpu_clock_counter = &gfx_v7_0_get_gpu_clock_counter, .select_se_sh = &gfx_v7_0_select_se_sh, .read_wave_data = &gfx_v7_0_read_wave_data, + .read_wave_sgprs = &gfx_v7_0_read_wave_sgprs, }; static const struct amdgpu_rlc_funcs gfx_v7_0_rlc_funcs = { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index a3684891c6e1..6324f67bdb1f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -25,6 +25,7 @@ #include "amdgpu.h" #include "amdgpu_gfx.h" #include "vi.h" +#include "vi_structs.h" #include "vid.h" #include "amdgpu_ucode.h" #include "amdgpu_atombios.h" @@ -167,6 +168,7 @@ static const u32 golden_settings_tonga_a11[] = mmPA_SC_ENHANCE, 0xffffffff, 0x20000001, mmPA_SC_FIFO_DEPTH_CNTL, 0x000003ff, 0x000000fc, mmPA_SC_LINE_STIPPLE_STATE, 0x0000ff0f, 0x00000000, + mmRLC_CGCG_CGLS_CTRL, 0x00000003, 0x0000003c, mmSQ_RANDOM_WAVE_PRI, 0x001fffff, 0x000006fd, mmTA_CNTL_AUX, 0x000f000f, 0x000b0000, mmTCC_CTRL, 0x00100000, 0xf31fff7f, @@ -1371,7 +1373,7 @@ static int gfx_v8_0_mec_init(struct amdgpu_device *adev) if (adev->gfx.mec.hpd_eop_obj == NULL) { r = amdgpu_bo_create(adev, - adev->gfx.mec.num_mec *adev->gfx.mec.num_pipe * MEC_HPD_SIZE * 2, + adev->gfx.mec.num_queue * MEC_HPD_SIZE, PAGE_SIZE, true, AMDGPU_GEM_DOMAIN_GTT, 0, NULL, NULL, &adev->gfx.mec.hpd_eop_obj); @@ -1400,7 +1402,7 @@ static int gfx_v8_0_mec_init(struct amdgpu_device *adev) return r; } - memset(hpd, 0, adev->gfx.mec.num_mec *adev->gfx.mec.num_pipe * MEC_HPD_SIZE * 2); + memset(hpd, 0, adev->gfx.mec.num_queue * MEC_HPD_SIZE); amdgpu_bo_kunmap(adev->gfx.mec.hpd_eop_obj); amdgpu_bo_unreserve(adev->gfx.mec.hpd_eop_obj); @@ -4469,267 +4471,6 @@ static int gfx_v8_0_cp_compute_load_microcode(struct amdgpu_device *adev) return 0; } -struct vi_mqd { - uint32_t header; /* ordinal0 */ - uint32_t compute_dispatch_initiator; /* ordinal1 */ - uint32_t compute_dim_x; /* ordinal2 */ - uint32_t compute_dim_y; /* ordinal3 */ - uint32_t compute_dim_z; /* ordinal4 */ - uint32_t compute_start_x; /* ordinal5 */ - uint32_t compute_start_y; /* ordinal6 */ - uint32_t compute_start_z; /* ordinal7 */ - uint32_t compute_num_thread_x; /* ordinal8 */ - uint32_t compute_num_thread_y; /* ordinal9 */ - uint32_t compute_num_thread_z; /* ordinal10 */ - uint32_t compute_pipelinestat_enable; /* ordinal11 */ - uint32_t compute_perfcount_enable; /* ordinal12 */ - uint32_t compute_pgm_lo; /* ordinal13 */ - uint32_t compute_pgm_hi; /* ordinal14 */ - uint32_t compute_tba_lo; /* ordinal15 */ - uint32_t compute_tba_hi; /* ordinal16 */ - uint32_t compute_tma_lo; /* ordinal17 */ - uint32_t compute_tma_hi; /* ordinal18 */ - uint32_t compute_pgm_rsrc1; /* ordinal19 */ - uint32_t compute_pgm_rsrc2; /* ordinal20 */ - uint32_t compute_vmid; /* ordinal21 */ - uint32_t compute_resource_limits; /* ordinal22 */ - uint32_t compute_static_thread_mgmt_se0; /* ordinal23 */ - uint32_t compute_static_thread_mgmt_se1; /* ordinal24 */ - uint32_t compute_tmpring_size; /* ordinal25 */ - uint32_t compute_static_thread_mgmt_se2; /* ordinal26 */ - uint32_t compute_static_thread_mgmt_se3; /* ordinal27 */ - uint32_t compute_restart_x; /* ordinal28 */ - uint32_t compute_restart_y; /* ordinal29 */ - uint32_t compute_restart_z; /* ordinal30 */ - uint32_t compute_thread_trace_enable; /* ordinal31 */ - uint32_t compute_misc_reserved; /* ordinal32 */ - uint32_t compute_dispatch_id; /* ordinal33 */ - uint32_t compute_threadgroup_id; /* ordinal34 */ - uint32_t compute_relaunch; /* ordinal35 */ - uint32_t compute_wave_restore_addr_lo; /* ordinal36 */ - uint32_t compute_wave_restore_addr_hi; /* ordinal37 */ - uint32_t compute_wave_restore_control; /* ordinal38 */ - uint32_t reserved9; /* ordinal39 */ - uint32_t reserved10; /* ordinal40 */ - uint32_t reserved11; /* ordinal41 */ - uint32_t reserved12; /* ordinal42 */ - uint32_t reserved13; /* ordinal43 */ - uint32_t reserved14; /* ordinal44 */ - uint32_t reserved15; /* ordinal45 */ - uint32_t reserved16; /* ordinal46 */ - uint32_t reserved17; /* ordinal47 */ - uint32_t reserved18; /* ordinal48 */ - uint32_t reserved19; /* ordinal49 */ - uint32_t reserved20; /* ordinal50 */ - uint32_t reserved21; /* ordinal51 */ - uint32_t reserved22; /* ordinal52 */ - uint32_t reserved23; /* ordinal53 */ - uint32_t reserved24; /* ordinal54 */ - uint32_t reserved25; /* ordinal55 */ - uint32_t reserved26; /* ordinal56 */ - uint32_t reserved27; /* ordinal57 */ - uint32_t reserved28; /* ordinal58 */ - uint32_t reserved29; /* ordinal59 */ - uint32_t reserved30; /* ordinal60 */ - uint32_t reserved31; /* ordinal61 */ - uint32_t reserved32; /* ordinal62 */ - uint32_t reserved33; /* ordinal63 */ - uint32_t reserved34; /* ordinal64 */ - uint32_t compute_user_data_0; /* ordinal65 */ - uint32_t compute_user_data_1; /* ordinal66 */ - uint32_t compute_user_data_2; /* ordinal67 */ - uint32_t compute_user_data_3; /* ordinal68 */ - uint32_t compute_user_data_4; /* ordinal69 */ - uint32_t compute_user_data_5; /* ordinal70 */ - uint32_t compute_user_data_6; /* ordinal71 */ - uint32_t compute_user_data_7; /* ordinal72 */ - uint32_t compute_user_data_8; /* ordinal73 */ - uint32_t compute_user_data_9; /* ordinal74 */ - uint32_t compute_user_data_10; /* ordinal75 */ - uint32_t compute_user_data_11; /* ordinal76 */ - uint32_t compute_user_data_12; /* ordinal77 */ - uint32_t compute_user_data_13; /* ordinal78 */ - uint32_t compute_user_data_14; /* ordinal79 */ - uint32_t compute_user_data_15; /* ordinal80 */ - uint32_t cp_compute_csinvoc_count_lo; /* ordinal81 */ - uint32_t cp_compute_csinvoc_count_hi; /* ordinal82 */ - uint32_t reserved35; /* ordinal83 */ - uint32_t reserved36; /* ordinal84 */ - uint32_t reserved37; /* ordinal85 */ - uint32_t cp_mqd_query_time_lo; /* ordinal86 */ - uint32_t cp_mqd_query_time_hi; /* ordinal87 */ - uint32_t cp_mqd_connect_start_time_lo; /* ordinal88 */ - uint32_t cp_mqd_connect_start_time_hi; /* ordinal89 */ - uint32_t cp_mqd_connect_end_time_lo; /* ordinal90 */ - uint32_t cp_mqd_connect_end_time_hi; /* ordinal91 */ - uint32_t cp_mqd_connect_end_wf_count; /* ordinal92 */ - uint32_t cp_mqd_connect_end_pq_rptr; /* ordinal93 */ - uint32_t cp_mqd_connect_end_pq_wptr; /* ordinal94 */ - uint32_t cp_mqd_connect_end_ib_rptr; /* ordinal95 */ - uint32_t reserved38; /* ordinal96 */ - uint32_t reserved39; /* ordinal97 */ - uint32_t cp_mqd_save_start_time_lo; /* ordinal98 */ - uint32_t cp_mqd_save_start_time_hi; /* ordinal99 */ - uint32_t cp_mqd_save_end_time_lo; /* ordinal100 */ - uint32_t cp_mqd_save_end_time_hi; /* ordinal101 */ - uint32_t cp_mqd_restore_start_time_lo; /* ordinal102 */ - uint32_t cp_mqd_restore_start_time_hi; /* ordinal103 */ - uint32_t cp_mqd_restore_end_time_lo; /* ordinal104 */ - uint32_t cp_mqd_restore_end_time_hi; /* ordinal105 */ - uint32_t reserved40; /* ordinal106 */ - uint32_t reserved41; /* ordinal107 */ - uint32_t gds_cs_ctxsw_cnt0; /* ordinal108 */ - uint32_t gds_cs_ctxsw_cnt1; /* ordinal109 */ - uint32_t gds_cs_ctxsw_cnt2; /* ordinal110 */ - uint32_t gds_cs_ctxsw_cnt3; /* ordinal111 */ - uint32_t reserved42; /* ordinal112 */ - uint32_t reserved43; /* ordinal113 */ - uint32_t cp_pq_exe_status_lo; /* ordinal114 */ - uint32_t cp_pq_exe_status_hi; /* ordinal115 */ - uint32_t cp_packet_id_lo; /* ordinal116 */ - uint32_t cp_packet_id_hi; /* ordinal117 */ - uint32_t cp_packet_exe_status_lo; /* ordinal118 */ - uint32_t cp_packet_exe_status_hi; /* ordinal119 */ - uint32_t gds_save_base_addr_lo; /* ordinal120 */ - uint32_t gds_save_base_addr_hi; /* ordinal121 */ - uint32_t gds_save_mask_lo; /* ordinal122 */ - uint32_t gds_save_mask_hi; /* ordinal123 */ - uint32_t ctx_save_base_addr_lo; /* ordinal124 */ - uint32_t ctx_save_base_addr_hi; /* ordinal125 */ - uint32_t reserved44; /* ordinal126 */ - uint32_t reserved45; /* ordinal127 */ - uint32_t cp_mqd_base_addr_lo; /* ordinal128 */ - uint32_t cp_mqd_base_addr_hi; /* ordinal129 */ - uint32_t cp_hqd_active; /* ordinal130 */ - uint32_t cp_hqd_vmid; /* ordinal131 */ - uint32_t cp_hqd_persistent_state; /* ordinal132 */ - uint32_t cp_hqd_pipe_priority; /* ordinal133 */ - uint32_t cp_hqd_queue_priority; /* ordinal134 */ - uint32_t cp_hqd_quantum; /* ordinal135 */ - uint32_t cp_hqd_pq_base_lo; /* ordinal136 */ - uint32_t cp_hqd_pq_base_hi; /* ordinal137 */ - uint32_t cp_hqd_pq_rptr; /* ordinal138 */ - uint32_t cp_hqd_pq_rptr_report_addr_lo; /* ordinal139 */ - uint32_t cp_hqd_pq_rptr_report_addr_hi; /* ordinal140 */ - uint32_t cp_hqd_pq_wptr_poll_addr; /* ordinal141 */ - uint32_t cp_hqd_pq_wptr_poll_addr_hi; /* ordinal142 */ - uint32_t cp_hqd_pq_doorbell_control; /* ordinal143 */ - uint32_t cp_hqd_pq_wptr; /* ordinal144 */ - uint32_t cp_hqd_pq_control; /* ordinal145 */ - uint32_t cp_hqd_ib_base_addr_lo; /* ordinal146 */ - uint32_t cp_hqd_ib_base_addr_hi; /* ordinal147 */ - uint32_t cp_hqd_ib_rptr; /* ordinal148 */ - uint32_t cp_hqd_ib_control; /* ordinal149 */ - uint32_t cp_hqd_iq_timer; /* ordinal150 */ - uint32_t cp_hqd_iq_rptr; /* ordinal151 */ - uint32_t cp_hqd_dequeue_request; /* ordinal152 */ - uint32_t cp_hqd_dma_offload; /* ordinal153 */ - uint32_t cp_hqd_sema_cmd; /* ordinal154 */ - uint32_t cp_hqd_msg_type; /* ordinal155 */ - uint32_t cp_hqd_atomic0_preop_lo; /* ordinal156 */ - uint32_t cp_hqd_atomic0_preop_hi; /* ordinal157 */ - uint32_t cp_hqd_atomic1_preop_lo; /* ordinal158 */ - uint32_t cp_hqd_atomic1_preop_hi; /* ordinal159 */ - uint32_t cp_hqd_hq_status0; /* ordinal160 */ - uint32_t cp_hqd_hq_control0; /* ordinal161 */ - uint32_t cp_mqd_control; /* ordinal162 */ - uint32_t cp_hqd_hq_status1; /* ordinal163 */ - uint32_t cp_hqd_hq_control1; /* ordinal164 */ - uint32_t cp_hqd_eop_base_addr_lo; /* ordinal165 */ - uint32_t cp_hqd_eop_base_addr_hi; /* ordinal166 */ - uint32_t cp_hqd_eop_control; /* ordinal167 */ - uint32_t cp_hqd_eop_rptr; /* ordinal168 */ - uint32_t cp_hqd_eop_wptr; /* ordinal169 */ - uint32_t cp_hqd_eop_done_events; /* ordinal170 */ - uint32_t cp_hqd_ctx_save_base_addr_lo; /* ordinal171 */ - uint32_t cp_hqd_ctx_save_base_addr_hi; /* ordinal172 */ - uint32_t cp_hqd_ctx_save_control; /* ordinal173 */ - uint32_t cp_hqd_cntl_stack_offset; /* ordinal174 */ - uint32_t cp_hqd_cntl_stack_size; /* ordinal175 */ - uint32_t cp_hqd_wg_state_offset; /* ordinal176 */ - uint32_t cp_hqd_ctx_save_size; /* ordinal177 */ - uint32_t cp_hqd_gds_resource_state; /* ordinal178 */ - uint32_t cp_hqd_error; /* ordinal179 */ - uint32_t cp_hqd_eop_wptr_mem; /* ordinal180 */ - uint32_t cp_hqd_eop_dones; /* ordinal181 */ - uint32_t reserved46; /* ordinal182 */ - uint32_t reserved47; /* ordinal183 */ - uint32_t reserved48; /* ordinal184 */ - uint32_t reserved49; /* ordinal185 */ - uint32_t reserved50; /* ordinal186 */ - uint32_t reserved51; /* ordinal187 */ - uint32_t reserved52; /* ordinal188 */ - uint32_t reserved53; /* ordinal189 */ - uint32_t reserved54; /* ordinal190 */ - uint32_t reserved55; /* ordinal191 */ - uint32_t iqtimer_pkt_header; /* ordinal192 */ - uint32_t iqtimer_pkt_dw0; /* ordinal193 */ - uint32_t iqtimer_pkt_dw1; /* ordinal194 */ - uint32_t iqtimer_pkt_dw2; /* ordinal195 */ - uint32_t iqtimer_pkt_dw3; /* ordinal196 */ - uint32_t iqtimer_pkt_dw4; /* ordinal197 */ - uint32_t iqtimer_pkt_dw5; /* ordinal198 */ - uint32_t iqtimer_pkt_dw6; /* ordinal199 */ - uint32_t iqtimer_pkt_dw7; /* ordinal200 */ - uint32_t iqtimer_pkt_dw8; /* ordinal201 */ - uint32_t iqtimer_pkt_dw9; /* ordinal202 */ - uint32_t iqtimer_pkt_dw10; /* ordinal203 */ - uint32_t iqtimer_pkt_dw11; /* ordinal204 */ - uint32_t iqtimer_pkt_dw12; /* ordinal205 */ - uint32_t iqtimer_pkt_dw13; /* ordinal206 */ - uint32_t iqtimer_pkt_dw14; /* ordinal207 */ - uint32_t iqtimer_pkt_dw15; /* ordinal208 */ - uint32_t iqtimer_pkt_dw16; /* ordinal209 */ - uint32_t iqtimer_pkt_dw17; /* ordinal210 */ - uint32_t iqtimer_pkt_dw18; /* ordinal211 */ - uint32_t iqtimer_pkt_dw19; /* ordinal212 */ - uint32_t iqtimer_pkt_dw20; /* ordinal213 */ - uint32_t iqtimer_pkt_dw21; /* ordinal214 */ - uint32_t iqtimer_pkt_dw22; /* ordinal215 */ - uint32_t iqtimer_pkt_dw23; /* ordinal216 */ - uint32_t iqtimer_pkt_dw24; /* ordinal217 */ - uint32_t iqtimer_pkt_dw25; /* ordinal218 */ - uint32_t iqtimer_pkt_dw26; /* ordinal219 */ - uint32_t iqtimer_pkt_dw27; /* ordinal220 */ - uint32_t iqtimer_pkt_dw28; /* ordinal221 */ - uint32_t iqtimer_pkt_dw29; /* ordinal222 */ - uint32_t iqtimer_pkt_dw30; /* ordinal223 */ - uint32_t iqtimer_pkt_dw31; /* ordinal224 */ - uint32_t reserved56; /* ordinal225 */ - uint32_t reserved57; /* ordinal226 */ - uint32_t reserved58; /* ordinal227 */ - uint32_t set_resources_header; /* ordinal228 */ - uint32_t set_resources_dw1; /* ordinal229 */ - uint32_t set_resources_dw2; /* ordinal230 */ - uint32_t set_resources_dw3; /* ordinal231 */ - uint32_t set_resources_dw4; /* ordinal232 */ - uint32_t set_resources_dw5; /* ordinal233 */ - uint32_t set_resources_dw6; /* ordinal234 */ - uint32_t set_resources_dw7; /* ordinal235 */ - uint32_t reserved59; /* ordinal236 */ - uint32_t reserved60; /* ordinal237 */ - uint32_t reserved61; /* ordinal238 */ - uint32_t reserved62; /* ordinal239 */ - uint32_t reserved63; /* ordinal240 */ - uint32_t reserved64; /* ordinal241 */ - uint32_t reserved65; /* ordinal242 */ - uint32_t reserved66; /* ordinal243 */ - uint32_t reserved67; /* ordinal244 */ - uint32_t reserved68; /* ordinal245 */ - uint32_t reserved69; /* ordinal246 */ - uint32_t reserved70; /* ordinal247 */ - uint32_t reserved71; /* ordinal248 */ - uint32_t reserved72; /* ordinal249 */ - uint32_t reserved73; /* ordinal250 */ - uint32_t reserved74; /* ordinal251 */ - uint32_t reserved75; /* ordinal252 */ - uint32_t reserved76; /* ordinal253 */ - uint32_t reserved77; /* ordinal254 */ - uint32_t reserved78; /* ordinal255 */ - - uint32_t reserved_t[256]; /* Reserve 256 dword buffer used by ucode */ -}; - static void gfx_v8_0_cp_compute_fini(struct amdgpu_device *adev) { int i, r; @@ -4763,34 +4504,7 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev) u32 *buf; struct vi_mqd *mqd; - /* init the pipes */ - mutex_lock(&adev->srbm_mutex); - for (i = 0; i < (adev->gfx.mec.num_pipe * adev->gfx.mec.num_mec); i++) { - int me = (i < 4) ? 1 : 2; - int pipe = (i < 4) ? i : (i - 4); - - eop_gpu_addr = adev->gfx.mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE); - eop_gpu_addr >>= 8; - - vi_srbm_select(adev, me, pipe, 0, 0); - - /* write the EOP addr */ - WREG32(mmCP_HQD_EOP_BASE_ADDR, eop_gpu_addr); - WREG32(mmCP_HQD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr)); - - /* set the VMID assigned */ - WREG32(mmCP_HQD_VMID, 0); - - /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */ - tmp = RREG32(mmCP_HQD_EOP_CONTROL); - tmp = REG_SET_FIELD(tmp, CP_HQD_EOP_CONTROL, EOP_SIZE, - (order_base_2(MEC_HPD_SIZE / 4) - 1)); - WREG32(mmCP_HQD_EOP_CONTROL, tmp); - } - vi_srbm_select(adev, 0, 0, 0, 0); - mutex_unlock(&adev->srbm_mutex); - - /* init the queues. Just two for now. */ + /* init the queues. */ for (i = 0; i < adev->gfx.num_compute_rings; i++) { struct amdgpu_ring *ring = &adev->gfx.compute_ring[i]; @@ -4842,6 +4556,22 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev) ring->pipe, ring->queue, 0); + eop_gpu_addr = adev->gfx.mec.hpd_eop_gpu_addr + (i * MEC_HPD_SIZE); + eop_gpu_addr >>= 8; + + /* write the EOP addr */ + WREG32(mmCP_HQD_EOP_BASE_ADDR, eop_gpu_addr); + WREG32(mmCP_HQD_EOP_BASE_ADDR_HI, upper_32_bits(eop_gpu_addr)); + + /* set the VMID assigned */ + WREG32(mmCP_HQD_VMID, 0); + + /* set the EOP size, register value is 2^(EOP_SIZE+1) dwords */ + tmp = RREG32(mmCP_HQD_EOP_CONTROL); + tmp = REG_SET_FIELD(tmp, CP_HQD_EOP_CONTROL, EOP_SIZE, + (order_base_2(MEC_HPD_SIZE / 4) - 1)); + WREG32(mmCP_HQD_EOP_CONTROL, tmp); + /* disable wptr polling */ tmp = RREG32(mmCP_PQ_WPTR_POLL_CNTL); tmp = REG_SET_FIELD(tmp, CP_PQ_WPTR_POLL_CNTL, EN, 0); @@ -4925,9 +4655,9 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev) /* only used if CP_PQ_WPTR_POLL_CNTL.CP_PQ_WPTR_POLL_CNTL__EN_MASK=1 */ wb_gpu_addr = adev->wb.gpu_addr + (ring->wptr_offs * 4); - mqd->cp_hqd_pq_wptr_poll_addr = wb_gpu_addr & 0xfffffffc; + mqd->cp_hqd_pq_wptr_poll_addr_lo = wb_gpu_addr & 0xfffffffc; mqd->cp_hqd_pq_wptr_poll_addr_hi = upper_32_bits(wb_gpu_addr) & 0xffff; - WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR, mqd->cp_hqd_pq_wptr_poll_addr); + WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR, mqd->cp_hqd_pq_wptr_poll_addr_lo); WREG32(mmCP_HQD_PQ_WPTR_POLL_ADDR_HI, mqd->cp_hqd_pq_wptr_poll_addr_hi); @@ -5098,6 +4828,10 @@ static int gfx_v8_0_hw_fini(void *handle) amdgpu_irq_put(adev, &adev->gfx.priv_reg_irq, 0); amdgpu_irq_put(adev, &adev->gfx.priv_inst_irq, 0); + if (amdgpu_sriov_vf(adev)) { + pr_debug("For SRIOV client, shouldn't do anything.\n"); + return 0; + } gfx_v8_0_cp_enable(adev, false); gfx_v8_0_rlc_stop(adev); gfx_v8_0_cp_compute_fini(adev); @@ -5450,6 +5184,21 @@ static uint32_t wave_read_ind(struct amdgpu_device *adev, uint32_t simd, uint32_ return RREG32(mmSQ_IND_DATA); } +static void wave_read_regs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t thread, + uint32_t regno, uint32_t num, uint32_t *out) +{ + WREG32(mmSQ_IND_INDEX, + (wave << SQ_IND_INDEX__WAVE_ID__SHIFT) | + (simd << SQ_IND_INDEX__SIMD_ID__SHIFT) | + (regno << SQ_IND_INDEX__INDEX__SHIFT) | + (thread << SQ_IND_INDEX__THREAD_ID__SHIFT) | + (SQ_IND_INDEX__FORCE_READ_MASK) | + (SQ_IND_INDEX__AUTO_INCR_MASK)); + while (num--) + *(out++) = RREG32(mmSQ_IND_DATA); +} + static void gfx_v8_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, uint32_t wave, uint32_t *dst, int *no_fields) { /* type 0 wave data */ @@ -5474,11 +5223,21 @@ static void gfx_v8_0_read_wave_data(struct amdgpu_device *adev, uint32_t simd, u dst[(*no_fields)++] = wave_read_ind(adev, simd, wave, ixSQ_WAVE_M0); } +static void gfx_v8_0_read_wave_sgprs(struct amdgpu_device *adev, uint32_t simd, + uint32_t wave, uint32_t start, + uint32_t size, uint32_t *dst) +{ + wave_read_regs( + adev, simd, wave, 0, + start + SQIND_WAVE_SGPRS_OFFSET, size, dst); +} + static const struct amdgpu_gfx_funcs gfx_v8_0_gfx_funcs = { .get_gpu_clock_counter = &gfx_v8_0_get_gpu_clock_counter, .select_se_sh = &gfx_v8_0_select_se_sh, .read_wave_data = &gfx_v8_0_read_wave_data, + .read_wave_sgprs = &gfx_v8_0_read_wave_sgprs, }; static int gfx_v8_0_early_init(void *handle) @@ -5930,29 +5689,24 @@ static void gfx_v8_0_update_coarse_grain_clock_gating(struct amdgpu_device *adev adev->gfx.rlc.funcs->enter_safe_mode(adev); if (enable && (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGCG)) { - /* 1 enable cntx_empty_int_enable/cntx_busy_int_enable/ - * Cmp_busy/GFX_Idle interrupts - */ - gfx_v8_0_enable_gui_idle_interrupt(adev, true); - temp1 = data1 = RREG32(mmRLC_CGTT_MGCG_OVERRIDE); data1 &= ~RLC_CGTT_MGCG_OVERRIDE__CGCG_MASK; if (temp1 != data1) WREG32(mmRLC_CGTT_MGCG_OVERRIDE, data1); - /* 2 wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */ + /* : wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */ gfx_v8_0_wait_for_rlc_serdes(adev); - /* 3 - clear cgcg override */ + /* 2 - clear cgcg override */ gfx_v8_0_send_serdes_cmd(adev, BPM_REG_CGCG_OVERRIDE, CLE_BPM_SERDES_CMD); /* wait for RLC_SERDES_CU_MASTER & RLC_SERDES_NONCU_MASTER idle */ gfx_v8_0_wait_for_rlc_serdes(adev); - /* 4 - write cmd to set CGLS */ + /* 3 - write cmd to set CGLS */ gfx_v8_0_send_serdes_cmd(adev, BPM_REG_CGLS_EN, SET_BPM_SERDES_CMD); - /* 5 - enable cgcg */ + /* 4 - enable cgcg */ data |= RLC_CGCG_CGLS_CTRL__CGCG_EN_MASK; if (adev->cg_flags & AMD_CG_SUPPORT_GFX_CGLS) { @@ -5970,6 +5724,11 @@ static void gfx_v8_0_update_coarse_grain_clock_gating(struct amdgpu_device *adev if (temp != data) WREG32(mmRLC_CGCG_CGLS_CTRL, data); + + /* 5 enable cntx_empty_int_enable/cntx_busy_int_enable/ + * Cmp_busy/GFX_Idle interrupts + */ + gfx_v8_0_enable_gui_idle_interrupt(adev, true); } else { /* disable cntx_empty_int_enable & GFX Idle interrupt */ gfx_v8_0_enable_gui_idle_interrupt(adev, false); diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c index 64d3c1e6014c..45a573e63d4a 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v6_0.c @@ -335,7 +335,7 @@ static int gmc_v6_0_mc_init(struct amdgpu_device *adev) * size equal to the 1024 or vram, whichever is larger. */ if (amdgpu_gart_size == -1) - adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev); + adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size); else adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20; @@ -795,11 +795,6 @@ static int gmc_v6_0_sw_init(void *handle) return r; } - r = amdgpu_ttm_global_init(adev); - if (r) { - return r; - } - r = gmc_v6_0_mc_init(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c index fbe1d9ac500a..273b16fb9459 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v7_0.c @@ -385,7 +385,7 @@ static int gmc_v7_0_mc_init(struct amdgpu_device *adev) * size equal to the 1024 or vram, whichever is larger. */ if (amdgpu_gart_size == -1) - adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev); + adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size); else adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20; @@ -945,11 +945,6 @@ static int gmc_v7_0_sw_init(void *handle) return r; } - r = amdgpu_ttm_global_init(adev); - if (r) { - return r; - } - r = gmc_v7_0_mc_init(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c index 12ea3404dd65..0daac3a5be79 100644 --- a/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gmc_v8_0.c @@ -472,7 +472,7 @@ static int gmc_v8_0_mc_init(struct amdgpu_device *adev) * size equal to the 1024 or vram, whichever is larger. */ if (amdgpu_gart_size == -1) - adev->mc.gtt_size = amdgpu_ttm_get_gtt_mem_size(adev); + adev->mc.gtt_size = max((1024ULL << 20), adev->mc.mc_vram_size); else adev->mc.gtt_size = (uint64_t)amdgpu_gart_size << 20; @@ -952,11 +952,6 @@ static int gmc_v8_0_sw_init(void *handle) return r; } - r = amdgpu_ttm_global_init(adev); - if (r) { - return r; - } - r = gmc_v8_0_mc_init(adev); if (r) return r; diff --git a/drivers/gpu/drm/amd/amdgpu/si_dpm.c b/drivers/gpu/drm/amd/amdgpu/si_dpm.c index f0f2f6c9718e..6c65a1a2de79 100644 --- a/drivers/gpu/drm/amd/amdgpu/si_dpm.c +++ b/drivers/gpu/drm/amd/amdgpu/si_dpm.c @@ -3506,6 +3506,7 @@ static void si_apply_state_adjust_rules(struct amdgpu_device *adev, (adev->pdev->revision == 0x80) || (adev->pdev->revision == 0x81) || (adev->pdev->revision == 0x83) || + (adev->pdev->revision == 0x87) || (adev->pdev->device == 0x6604) || (adev->pdev->device == 0x6605)) { max_sclk = 75000; @@ -7715,6 +7716,7 @@ static int si_dpm_init_microcode(struct amdgpu_device *adev) (adev->pdev->revision == 0x80) || (adev->pdev->revision == 0x81) || (adev->pdev->revision == 0x83) || + (adev->pdev->revision == 0x87) || (adev->pdev->device == 0x6604) || (adev->pdev->device == 0x6605)) chip_name = "oland_k"; diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c index 95cabeafc18e..a79e283590fb 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v5_0.c @@ -640,7 +640,7 @@ static void uvd_v5_0_enable_clock_gating(struct amdgpu_device *adev, bool enable UVD_SUVD_CGC_GATE__SDB_MASK; if (enable) { - data3 |= (UVD_CGC_GATE__SYS_MASK | + data3 |= (UVD_CGC_GATE__SYS_MASK | UVD_CGC_GATE__UDEC_MASK | UVD_CGC_GATE__MPEG2_MASK | UVD_CGC_GATE__RBC_MASK | @@ -656,9 +656,11 @@ static void uvd_v5_0_enable_clock_gating(struct amdgpu_device *adev, bool enable UVD_CGC_GATE__UDEC_DB_MASK | UVD_CGC_GATE__UDEC_MP_MASK | UVD_CGC_GATE__WCB_MASK | - UVD_CGC_GATE__VCPU_MASK | UVD_CGC_GATE__JPEG_MASK | UVD_CGC_GATE__SCPU_MASK); + /* only in pg enabled, we can gate clock to vcpu*/ + if (adev->pg_flags & AMD_PG_SUPPORT_UVD) + data3 |= UVD_CGC_GATE__VCPU_MASK; data3 &= ~UVD_CGC_GATE__REGS_MASK; data1 |= suvd_flags; } else { diff --git a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c index 00fad6951d82..ba0bbf7138dc 100644 --- a/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/uvd_v6_0.c @@ -42,6 +42,10 @@ static void uvd_v6_0_set_irq_funcs(struct amdgpu_device *adev); static int uvd_v6_0_start(struct amdgpu_device *adev); static void uvd_v6_0_stop(struct amdgpu_device *adev); static void uvd_v6_0_set_sw_clock_gating(struct amdgpu_device *adev); +static int uvd_v6_0_set_clockgating_state(void *handle, + enum amd_clockgating_state state); +static void uvd_v6_0_enable_mgcg(struct amdgpu_device *adev, + bool enable); /** * uvd_v6_0_ring_get_rptr - get read pointer @@ -151,8 +155,6 @@ static int uvd_v6_0_hw_init(void *handle) uint32_t tmp; int r; - amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); - r = uvd_v6_0_start(adev); if (r) goto done; @@ -395,11 +397,11 @@ static int uvd_v6_0_start(struct amdgpu_device *adev) lmi_swap_cntl = 0; mp_swap_cntl = 0; + amdgpu_asic_set_uvd_clocks(adev, 10000, 10000); + uvd_v6_0_set_clockgating_state(adev, AMD_CG_STATE_UNGATE); + uvd_v6_0_enable_mgcg(adev, true); uvd_v6_0_mc_resume(adev); - /* disable clock gating */ - WREG32_FIELD(UVD_CGC_CTRL, DYN_CLOCK_MODE, 0); - /* disable interupt */ WREG32_FIELD(UVD_MASTINT_EN, VCPU_EN, 0); @@ -838,22 +840,72 @@ static int uvd_v6_0_process_interrupt(struct amdgpu_device *adev, return 0; } +static void uvd_v6_0_enable_clock_gating(struct amdgpu_device *adev, bool enable) +{ + uint32_t data1, data3; + + data1 = RREG32(mmUVD_SUVD_CGC_GATE); + data3 = RREG32(mmUVD_CGC_GATE); + + data1 |= UVD_SUVD_CGC_GATE__SRE_MASK | + UVD_SUVD_CGC_GATE__SIT_MASK | + UVD_SUVD_CGC_GATE__SMP_MASK | + UVD_SUVD_CGC_GATE__SCM_MASK | + UVD_SUVD_CGC_GATE__SDB_MASK | + UVD_SUVD_CGC_GATE__SRE_H264_MASK | + UVD_SUVD_CGC_GATE__SRE_HEVC_MASK | + UVD_SUVD_CGC_GATE__SIT_H264_MASK | + UVD_SUVD_CGC_GATE__SIT_HEVC_MASK | + UVD_SUVD_CGC_GATE__SCM_H264_MASK | + UVD_SUVD_CGC_GATE__SCM_HEVC_MASK | + UVD_SUVD_CGC_GATE__SDB_H264_MASK | + UVD_SUVD_CGC_GATE__SDB_HEVC_MASK; + + if (enable) { + data3 |= (UVD_CGC_GATE__SYS_MASK | + UVD_CGC_GATE__UDEC_MASK | + UVD_CGC_GATE__MPEG2_MASK | + UVD_CGC_GATE__RBC_MASK | + UVD_CGC_GATE__LMI_MC_MASK | + UVD_CGC_GATE__LMI_UMC_MASK | + UVD_CGC_GATE__IDCT_MASK | + UVD_CGC_GATE__MPRD_MASK | + UVD_CGC_GATE__MPC_MASK | + UVD_CGC_GATE__LBSI_MASK | + UVD_CGC_GATE__LRBBM_MASK | + UVD_CGC_GATE__UDEC_RE_MASK | + UVD_CGC_GATE__UDEC_CM_MASK | + UVD_CGC_GATE__UDEC_IT_MASK | + UVD_CGC_GATE__UDEC_DB_MASK | + UVD_CGC_GATE__UDEC_MP_MASK | + UVD_CGC_GATE__WCB_MASK | + UVD_CGC_GATE__JPEG_MASK | + UVD_CGC_GATE__SCPU_MASK | + UVD_CGC_GATE__JPEG2_MASK); + /* only in pg enabled, we can gate clock to vcpu*/ + if (adev->pg_flags & AMD_PG_SUPPORT_UVD) + data3 |= UVD_CGC_GATE__VCPU_MASK; + + data3 &= ~UVD_CGC_GATE__REGS_MASK; + } else { + data3 = 0; + } + + WREG32(mmUVD_SUVD_CGC_GATE, data1); + WREG32(mmUVD_CGC_GATE, data3); +} + static void uvd_v6_0_set_sw_clock_gating(struct amdgpu_device *adev) { - uint32_t data, data1, data2, suvd_flags; + uint32_t data, data2; data = RREG32(mmUVD_CGC_CTRL); - data1 = RREG32(mmUVD_SUVD_CGC_GATE); data2 = RREG32(mmUVD_SUVD_CGC_CTRL); + data &= ~(UVD_CGC_CTRL__CLK_OFF_DELAY_MASK | UVD_CGC_CTRL__CLK_GATE_DLY_TIMER_MASK); - suvd_flags = UVD_SUVD_CGC_GATE__SRE_MASK | - UVD_SUVD_CGC_GATE__SIT_MASK | - UVD_SUVD_CGC_GATE__SMP_MASK | - UVD_SUVD_CGC_GATE__SCM_MASK | - UVD_SUVD_CGC_GATE__SDB_MASK; data |= UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK | (1 << REG_FIELD_SHIFT(UVD_CGC_CTRL, CLK_GATE_DLY_TIMER)) | @@ -886,11 +938,8 @@ static void uvd_v6_0_set_sw_clock_gating(struct amdgpu_device *adev) UVD_SUVD_CGC_CTRL__SMP_MODE_MASK | UVD_SUVD_CGC_CTRL__SCM_MODE_MASK | UVD_SUVD_CGC_CTRL__SDB_MODE_MASK); - data1 |= suvd_flags; WREG32(mmUVD_CGC_CTRL, data); - WREG32(mmUVD_CGC_GATE, 0); - WREG32(mmUVD_SUVD_CGC_GATE, data1); WREG32(mmUVD_SUVD_CGC_CTRL, data2); } @@ -937,6 +986,32 @@ static void uvd_v6_0_set_hw_clock_gating(struct amdgpu_device *adev) } #endif +static void uvd_v6_0_enable_mgcg(struct amdgpu_device *adev, + bool enable) +{ + u32 orig, data; + + if (enable && (adev->cg_flags & AMD_CG_SUPPORT_UVD_MGCG)) { + data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL); + data |= 0xfff; + WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data); + + orig = data = RREG32(mmUVD_CGC_CTRL); + data |= UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK; + if (orig != data) + WREG32(mmUVD_CGC_CTRL, data); + } else { + data = RREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL); + data &= ~0xfff; + WREG32_UVD_CTX(ixUVD_CGC_MEM_CTRL, data); + + orig = data = RREG32(mmUVD_CGC_CTRL); + data &= ~UVD_CGC_CTRL__DYN_CLOCK_MODE_MASK; + if (orig != data) + WREG32(mmUVD_CGC_CTRL, data); + } +} + static int uvd_v6_0_set_clockgating_state(void *handle, enum amd_clockgating_state state) { @@ -947,17 +1022,17 @@ static int uvd_v6_0_set_clockgating_state(void *handle, return 0; if (enable) { - /* disable HW gating and enable Sw gating */ - uvd_v6_0_set_sw_clock_gating(adev); - } else { /* wait for STATUS to clear */ if (uvd_v6_0_wait_for_idle(handle)) return -EBUSY; - + uvd_v6_0_enable_clock_gating(adev, true); /* enable HW gates because UVD is idle */ /* uvd_v6_0_set_hw_clock_gating(adev); */ + } else { + /* disable HW gating and enable Sw gating */ + uvd_v6_0_enable_clock_gating(adev, false); } - + uvd_v6_0_set_sw_clock_gating(adev); return 0; } diff --git a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c index 39f03f137a56..6b3293a1c7b8 100644 --- a/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vce_v3_0.c @@ -134,7 +134,7 @@ static void vce_v3_0_set_vce_sw_clock_gating(struct amdgpu_device *adev, accessible but the firmware will throttle the clocks on the fly as necessary. */ - if (gated) { + if (!gated) { data = RREG32(mmVCE_CLOCK_GATING_B); data |= 0x1ff; data &= ~0xef0000; diff --git a/drivers/gpu/drm/amd/amdgpu/vi.c b/drivers/gpu/drm/amd/amdgpu/vi.c index 243dcf7bae47..9f771f4ffcb7 100644 --- a/drivers/gpu/drm/amd/amdgpu/vi.c +++ b/drivers/gpu/drm/amd/amdgpu/vi.c @@ -937,12 +937,14 @@ static int vi_common_early_init(void *handle) adev->external_rev_id = adev->rev_id + 0x14; break; case CHIP_POLARIS11: - adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG; + adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG | + AMD_CG_SUPPORT_VCE_MGCG; adev->pg_flags = 0; adev->external_rev_id = adev->rev_id + 0x5A; break; case CHIP_POLARIS10: - adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG; + adev->cg_flags = AMD_CG_SUPPORT_UVD_MGCG | + AMD_CG_SUPPORT_VCE_MGCG; adev->pg_flags = 0; adev->external_rev_id = adev->rev_id + 0x50; break; diff --git a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c index 51a36077b993..c81cf1412728 100644 --- a/drivers/gpu/drm/amd/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/powerplay/amd_powerplay.c @@ -1004,12 +1004,12 @@ int amd_powerplay_reset(void *handle) if (ret) return ret; - hw_init_power_state_table(instance->hwmgr); - if ((amdgpu_dpm == 0) || cgs_is_virtualization_enabled(instance->smu_mgr->device)) return 0; + hw_init_power_state_table(instance->hwmgr); + if (eventmgr == NULL || eventmgr->pp_eventmgr_init == NULL) return -EINVAL; diff --git a/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c b/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c index b6f45fd01fa6..ec36c0e28388 100644 --- a/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c +++ b/drivers/gpu/drm/amd/powerplay/eventmgr/eventtasks.c @@ -154,7 +154,7 @@ int pem_task_powerdown_vce_tasks(struct pp_eventmgr *eventmgr, struct pem_event_ int pem_task_disable_clock_power_gatings_tasks(struct pp_eventmgr *eventmgr, struct pem_event_data *event_data) { - /* TODO */ + phm_disable_clock_power_gatings(eventmgr->hwmgr); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c b/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c index a6abe81bc843..71822ae73a12 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/functiontables.c @@ -35,7 +35,7 @@ static int phm_run_table(struct pp_hwmgr *hwmgr, phm_table_function *function; if (rt_table->function_list == NULL) { - printk(KERN_INFO "[ powerplay ] this function not implement!\n"); + pr_debug("[ powerplay ] this function not implement!\n"); return 0; } diff --git a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c index 0723758ed065..c355a0f51663 100644 --- a/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c +++ b/drivers/gpu/drm/amd/powerplay/hwmgr/hardwaremanager.c @@ -209,6 +209,19 @@ int phm_enable_clock_power_gatings(struct pp_hwmgr *hwmgr) return 0; } +int phm_disable_clock_power_gatings(struct pp_hwmgr *hwmgr) +{ + PHM_FUNC_CHECK(hwmgr); + + if (phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_TablelessHardwareInterface)) { + if (NULL != hwmgr->hwmgr_func->disable_clock_power_gating) + return hwmgr->hwmgr_func->disable_clock_power_gating(hwmgr); + } + return 0; +} + + int phm_display_configuration_changed(struct pp_hwmgr *hwmgr) { PHM_FUNC_CHECK(hwmgr); diff --git a/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h b/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h index d4495839c64c..26129972f686 100644 --- a/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h +++ b/drivers/gpu/drm/amd/powerplay/inc/hardwaremanager.h @@ -334,6 +334,7 @@ struct phm_clocks { uint32_t clock[MAX_NUM_CLOCKS]; }; +extern int phm_disable_clock_power_gatings(struct pp_hwmgr *hwmgr); extern int phm_enable_clock_power_gatings(struct pp_hwmgr *hwmgr); extern int phm_powergate_uvd(struct pp_hwmgr *hwmgr, bool gate); extern int phm_powergate_vce(struct pp_hwmgr *hwmgr, bool gate); diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c index 34523fe6ed6f..6aeb1d20cc3b 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/fiji_smc.c @@ -1958,6 +1958,12 @@ int fiji_thermal_setup_fan_table(struct pp_hwmgr *hwmgr) int res; uint64_t tmp64; + if (hwmgr->thermal_controller.fanInfo.bNoFan) { + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_MicrocodeFanControl); + return 0; + } + if (smu_data->smu7_data.fan_table_start == 0) { phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl); diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c index b579f0c175e6..a24971a33bfd 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/iceland_smc.c @@ -2006,6 +2006,12 @@ int iceland_thermal_setup_fan_table(struct pp_hwmgr *hwmgr) if (!phm_cap_enabled(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl)) return 0; + if (hwmgr->thermal_controller.fanInfo.bNoFan) { + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_MicrocodeFanControl); + return 0; + } + if (0 == smu7_data->fan_table_start) { phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl); return 0; diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c index 8db8e209d915..5190e821200c 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/polaris10_smc.c @@ -1885,6 +1885,12 @@ int polaris10_thermal_setup_fan_table(struct pp_hwmgr *hwmgr) int res; uint64_t tmp64; + if (hwmgr->thermal_controller.fanInfo.bNoFan) { + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_MicrocodeFanControl); + return 0; + } + if (smu_data->smu7_data.fan_table_start == 0) { phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl); diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c index 6df0d6edfdd1..f49b5487b951 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/smu7_smumgr.c @@ -366,12 +366,16 @@ static int smu7_populate_single_firmware_entry(struct pp_smumgr *smumgr, &info); if (!result) { - entry->version = info.version; + entry->version = info.fw_version; entry->id = (uint16_t)fw_type; entry->image_addr_high = smu_upper_32_bits(info.mc_addr); entry->image_addr_low = smu_lower_32_bits(info.mc_addr); entry->meta_data_addr_high = 0; entry->meta_data_addr_low = 0; + + /* digest need be excluded out */ + if (cgs_is_virtualization_enabled(smumgr->device)) + info.image_size -= 20; entry->data_size_byte = info.image_size; entry->num_register_entries = 0; } @@ -403,8 +407,14 @@ int smu7_request_smu_load_fw(struct pp_smumgr *smumgr) 0x0); if (smumgr->chip_id > CHIP_TOPAZ) { /* add support for Topaz */ - smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_HI, smu_data->smu_buffer.mc_addr_high); - smu7_send_msg_to_smc_with_parameter(smumgr, PPSMC_MSG_SMU_DRAM_ADDR_LO, smu_data->smu_buffer.mc_addr_low); + if (!cgs_is_virtualization_enabled(smumgr->device)) { + smu7_send_msg_to_smc_with_parameter(smumgr, + PPSMC_MSG_SMU_DRAM_ADDR_HI, + smu_data->smu_buffer.mc_addr_high); + smu7_send_msg_to_smc_with_parameter(smumgr, + PPSMC_MSG_SMU_DRAM_ADDR_LO, + smu_data->smu_buffer.mc_addr_low); + } fw_to_load = UCODE_ID_RLC_G_MASK + UCODE_ID_SDMA0_MASK + UCODE_ID_SDMA1_MASK @@ -539,7 +549,6 @@ int smu7_init(struct pp_smumgr *smumgr) smu_data = (struct smu7_smumgr *)(smumgr->backend); smu_data->header_buffer.data_size = ((sizeof(struct SMU_DRAMData_TOC) / 4096) + 1) * 4096; - smu_data->smu_buffer.data_size = 200*4096; /* Allocate FW image data structure and header buffer and * send the header buffer address to SMU */ @@ -562,6 +571,10 @@ int smu7_init(struct pp_smumgr *smumgr) (cgs_handle_t)smu_data->header_buffer.handle); return -EINVAL); + if (cgs_is_virtualization_enabled(smumgr->device)) + return 0; + + smu_data->smu_buffer.data_size = 200*4096; smu_allocate_memory(smumgr->device, smu_data->smu_buffer.data_size, CGS_GPU_MEM_TYPE__VISIBLE_CONTIG_FB, diff --git a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c index d08f6f19b454..2e1493ce1bb5 100644 --- a/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c +++ b/drivers/gpu/drm/amd/powerplay/smumgr/tonga_smc.c @@ -2496,6 +2496,12 @@ int tonga_thermal_setup_fan_table(struct pp_hwmgr *hwmgr) PHM_PlatformCaps_MicrocodeFanControl)) return 0; + if (hwmgr->thermal_controller.fanInfo.bNoFan) { + phm_cap_unset(hwmgr->platform_descriptor.platformCaps, + PHM_PlatformCaps_MicrocodeFanControl); + return 0; + } + if (0 == smu_data->smu7_data.fan_table_start) { phm_cap_unset(hwmgr->platform_descriptor.platformCaps, PHM_PlatformCaps_MicrocodeFanControl); diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 72fa5b20baf1..60697482b94c 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1087,12 +1087,38 @@ int drm_atomic_connector_set_property(struct drm_connector *connector, * now?) atomic writes to DPMS property: */ return -EINVAL; + } else if (property == config->tv_select_subconnector_property) { + state->tv.subconnector = val; + } else if (property == config->tv_left_margin_property) { + state->tv.margins.left = val; + } else if (property == config->tv_right_margin_property) { + state->tv.margins.right = val; + } else if (property == config->tv_top_margin_property) { + state->tv.margins.top = val; + } else if (property == config->tv_bottom_margin_property) { + state->tv.margins.bottom = val; + } else if (property == config->tv_mode_property) { + state->tv.mode = val; + } else if (property == config->tv_brightness_property) { + state->tv.brightness = val; + } else if (property == config->tv_contrast_property) { + state->tv.contrast = val; + } else if (property == config->tv_flicker_reduction_property) { + state->tv.flicker_reduction = val; + } else if (property == config->tv_overscan_property) { + state->tv.overscan = val; + } else if (property == config->tv_saturation_property) { + state->tv.saturation = val; + } else if (property == config->tv_hue_property) { + state->tv.hue = val; } else if (connector->funcs->atomic_set_property) { return connector->funcs->atomic_set_property(connector, state, property, val); } else { return -EINVAL; } + + return 0; } EXPORT_SYMBOL(drm_atomic_connector_set_property); @@ -1135,6 +1161,30 @@ drm_atomic_connector_get_property(struct drm_connector *connector, *val = (state->crtc) ? state->crtc->base.id : 0; } else if (property == config->dpms_property) { *val = connector->dpms; + } else if (property == config->tv_select_subconnector_property) { + *val = state->tv.subconnector; + } else if (property == config->tv_left_margin_property) { + *val = state->tv.margins.left; + } else if (property == config->tv_right_margin_property) { + *val = state->tv.margins.right; + } else if (property == config->tv_top_margin_property) { + *val = state->tv.margins.top; + } else if (property == config->tv_bottom_margin_property) { + *val = state->tv.margins.bottom; + } else if (property == config->tv_mode_property) { + *val = state->tv.mode; + } else if (property == config->tv_brightness_property) { + *val = state->tv.brightness; + } else if (property == config->tv_contrast_property) { + *val = state->tv.contrast; + } else if (property == config->tv_flicker_reduction_property) { + *val = state->tv.flicker_reduction; + } else if (property == config->tv_overscan_property) { + *val = state->tv.overscan; + } else if (property == config->tv_saturation_property) { + *val = state->tv.saturation; + } else if (property == config->tv_hue_property) { + *val = state->tv.hue; } else if (connector->funcs->atomic_get_property) { return connector->funcs->atomic_get_property(connector, state, property, val); diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c index 4ec61ac27477..4a7b3e98d586 100644 --- a/drivers/gpu/drm/drm_drv.c +++ b/drivers/gpu/drm/drm_drv.c @@ -650,6 +650,62 @@ void drm_dev_unref(struct drm_device *dev) } EXPORT_SYMBOL(drm_dev_unref); +static int create_compat_control_link(struct drm_device *dev) +{ + struct drm_minor *minor; + char *name; + int ret; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return 0; + + minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY); + if (!minor) + return 0; + + /* + * Some existing userspace out there uses the existing of the controlD* + * sysfs files to figure out whether it's a modeset driver. It only does + * readdir, hence a symlink is sufficient (and the least confusing + * option). Otherwise controlD* is entirely unused. + * + * Old controlD chardev have been allocated in the range + * 64-127. + */ + name = kasprintf(GFP_KERNEL, "controlD%d", minor->index + 64); + if (!name) + return -ENOMEM; + + ret = sysfs_create_link(minor->kdev->kobj.parent, + &minor->kdev->kobj, + name); + + kfree(name); + + return ret; +} + +static void remove_compat_control_link(struct drm_device *dev) +{ + struct drm_minor *minor; + char *name; + + if (!drm_core_check_feature(dev, DRIVER_MODESET)) + return; + + minor = *drm_minor_get_slot(dev, DRM_MINOR_PRIMARY); + if (!minor) + return; + + name = kasprintf(GFP_KERNEL, "controlD%d", minor->index); + if (!name) + return; + + sysfs_remove_link(minor->kdev->kobj.parent, name); + + kfree(name); +} + /** * drm_dev_register - Register DRM device * @dev: Device to register @@ -688,6 +744,10 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) if (ret) goto err_minors; + ret = create_compat_control_link(dev); + if (ret) + goto err_minors; + if (dev->driver->load) { ret = dev->driver->load(dev, flags); if (ret) @@ -701,6 +761,7 @@ int drm_dev_register(struct drm_device *dev, unsigned long flags) goto out_unlock; err_minors: + remove_compat_control_link(dev); drm_minor_unregister(dev, DRM_MINOR_PRIMARY); drm_minor_unregister(dev, DRM_MINOR_RENDER); drm_minor_unregister(dev, DRM_MINOR_CONTROL); @@ -741,6 +802,7 @@ void drm_dev_unregister(struct drm_device *dev) list_for_each_entry_safe(r_list, list_temp, &dev->maplist, head) drm_legacy_rmmap(dev, r_list->map); + remove_compat_control_link(dev); drm_minor_unregister(dev, DRM_MINOR_PRIMARY); drm_minor_unregister(dev, DRM_MINOR_RENDER); drm_minor_unregister(dev, DRM_MINOR_CONTROL); diff --git a/drivers/gpu/drm/drm_plane.c b/drivers/gpu/drm/drm_plane.c index 4bfdada23196..c2dc8e6f80ff 100644 --- a/drivers/gpu/drm/drm_plane.c +++ b/drivers/gpu/drm/drm_plane.c @@ -79,7 +79,7 @@ static unsigned int drm_num_planes(struct drm_device *dev) * Zero on success, error code on failure. */ int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, + uint32_t possible_crtcs, const struct drm_plane_funcs *funcs, const uint32_t *formats, unsigned int format_count, enum drm_plane_type type, @@ -196,7 +196,7 @@ void drm_plane_unregister_all(struct drm_device *dev) * Zero on success, error code on failure. */ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, - unsigned long possible_crtcs, + uint32_t possible_crtcs, const struct drm_plane_funcs *funcs, const uint32_t *formats, unsigned int format_count, bool is_primary) diff --git a/drivers/gpu/drm/fsl-dcu/Makefile b/drivers/gpu/drm/fsl-dcu/Makefile index b35a292287f3..aca34f656bea 100644 --- a/drivers/gpu/drm/fsl-dcu/Makefile +++ b/drivers/gpu/drm/fsl-dcu/Makefile @@ -3,6 +3,5 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \ fsl_dcu_drm_rgb.o \ fsl_dcu_drm_plane.o \ fsl_dcu_drm_crtc.o \ - fsl_dcu_drm_fbdev.o \ fsl_tcon.o obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o 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 320e4728c9b9..537ca159ffe5 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c @@ -32,6 +32,9 @@ #include "fsl_dcu_drm_drv.h" #include "fsl_tcon.h" +static int legacyfb_depth = 24; +module_param(legacyfb_depth, int, 0444); + static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg) { if (reg == DCU_INT_STATUS || reg == DCU_UPDATE_MODE) @@ -85,7 +88,18 @@ static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) goto done; dev->irq_enabled = true; - fsl_dcu_fbdev_init(dev); + if (legacyfb_depth != 16 && legacyfb_depth != 24 && + legacyfb_depth != 32) { + dev_warn(dev->dev, + "Invalid legacyfb_depth. Defaulting to 24bpp\n"); + legacyfb_depth = 24; + } + fsl_dev->fbdev = drm_fbdev_cma_init(dev, legacyfb_depth, 1, 1); + if (IS_ERR(fsl_dev->fbdev)) { + ret = PTR_ERR(fsl_dev->fbdev); + fsl_dev->fbdev = NULL; + goto done; + } return 0; done: @@ -106,6 +120,7 @@ static int fsl_dcu_unload(struct drm_device *dev) { struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; + drm_crtc_force_disable_all(dev); drm_kms_helper_poll_fini(dev); if (fsl_dev->fbdev) @@ -332,11 +347,6 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) fsl_dev->soc = id->data; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, "could not get memory IO resource\n"); - return -ENODEV; - } - base = devm_ioremap_resource(dev, res); if (IS_ERR(base)) { ret = PTR_ERR(base); @@ -346,7 +356,7 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev) fsl_dev->irq = platform_get_irq(pdev, 0); if (fsl_dev->irq < 0) { dev_err(dev, "failed to get irq\n"); - return -ENXIO; + return fsl_dev->irq; } fsl_dev->regmap = devm_regmap_init_mmio(dev, base, @@ -424,9 +434,9 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev) { struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); + drm_put_dev(fsl_dev->drm); clk_disable_unprepare(fsl_dev->clk); clk_unregister(fsl_dev->pix_clk); - drm_put_dev(fsl_dev->drm); return 0; } diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h index 3b371fe7491e..e9e9aeecf2eb 100644 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h +++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h @@ -197,7 +197,6 @@ struct fsl_dcu_drm_device { struct drm_atomic_state *state; }; -void fsl_dcu_fbdev_init(struct drm_device *dev); int fsl_dcu_drm_modeset_init(struct fsl_dcu_drm_device *fsl_dev); #endif /* __FSL_DCU_DRM_DRV_H__ */ diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c deleted file mode 100644 index 8b8b819ea704..000000000000 --- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_fbdev.c +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2015 Freescale Semiconductor, Inc. - * - * Freescale DCU drm device driver - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#include <drm/drmP.h> -#include <drm/drm_fb_cma_helper.h> - -#include "fsl_dcu_drm_drv.h" - -/* initialize fbdev helper */ -void fsl_dcu_fbdev_init(struct drm_device *dev) -{ - struct fsl_dcu_drm_device *fsl_dev = dev_get_drvdata(dev->dev); - - fsl_dev->fbdev = drm_fbdev_cma_init(dev, 24, 1, 1); -} diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 3d4223e8ebe3..b1a7c8dd4b5f 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -361,6 +361,8 @@ static inline void intel_vgpu_write_pci_bar(struct intel_vgpu *vgpu, * leave the bit 3 - bit 0 unchanged. */ *pval = (val & GENMASK(31, 4)) | (*pval & GENMASK(3, 0)); + } else { + *pval = val; } } diff --git a/drivers/gpu/drm/i915/gvt/scheduler.c b/drivers/gpu/drm/i915/gvt/scheduler.c index f898df38dd9a..4db242250235 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.c +++ b/drivers/gpu/drm/i915/gvt/scheduler.c @@ -177,8 +177,8 @@ static int dispatch_workload(struct intel_vgpu_workload *workload) rq = i915_gem_request_alloc(dev_priv->engine[ring_id], shadow_ctx); if (IS_ERR(rq)) { gvt_err("fail to allocate gem request\n"); - workload->status = PTR_ERR(rq); - return workload->status; + ret = PTR_ERR(rq); + goto out; } gvt_dbg_sched("ring id %d get i915 gem request %p\n", ring_id, rq); @@ -212,7 +212,8 @@ out: if (ret) workload->status = ret; - i915_add_request_no_flush(rq); + if (!IS_ERR_OR_NULL(rq)) + i915_add_request_no_flush(rq); mutex_unlock(&dev_priv->drm.struct_mutex); return ret; } @@ -460,7 +461,8 @@ complete: complete_current_workload(gvt, ring_id); - i915_gem_request_put(fetch_and_zero(&workload->req)); + if (workload->req) + i915_gem_request_put(fetch_and_zero(&workload->req)); if (need_force_wake) intel_uncore_forcewake_put(gvt->dev_priv, diff --git a/drivers/gpu/drm/i915/gvt/vgpu.c b/drivers/gpu/drm/i915/gvt/vgpu.c index 4f64845d8a4c..536d2b9d5777 100644 --- a/drivers/gpu/drm/i915/gvt/vgpu.c +++ b/drivers/gpu/drm/i915/gvt/vgpu.c @@ -378,6 +378,7 @@ struct intel_vgpu *intel_gvt_create_vgpu(struct intel_gvt *gvt, struct intel_vgpu *vgpu; param.handle = 0; + param.primary = 1; param.low_gm_sz = type->low_gm_size; param.high_gm_sz = type->high_gm_size; param.fence_sz = type->fence; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index b7f42c448a44..791bfc760075 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -935,27 +935,6 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data) return 0; } -static int i915_hws_info(struct seq_file *m, void *data) -{ - struct drm_info_node *node = m->private; - struct drm_i915_private *dev_priv = node_to_i915(node); - struct intel_engine_cs *engine; - const u32 *hws; - int i; - - engine = dev_priv->engine[(uintptr_t)node->info_ent->data]; - hws = engine->status_page.page_addr; - if (hws == NULL) - return 0; - - for (i = 0; i < 4096 / sizeof(u32) / 4; i += 4) { - seq_printf(m, "0x%08x: 0x%08x 0x%08x 0x%08x 0x%08x\n", - i * 4, - hws[i], hws[i + 1], hws[i + 2], hws[i + 3]); - } - return 0; -} - #if IS_ENABLED(CONFIG_DRM_I915_CAPTURE_ERROR) static ssize_t @@ -1047,7 +1026,7 @@ i915_next_seqno_get(void *data, u64 *val) { struct drm_i915_private *dev_priv = data; - *val = atomic_read(&dev_priv->gt.global_timeline.next_seqno); + *val = 1 + atomic_read(&dev_priv->gt.global_timeline.next_seqno); return 0; } @@ -5403,10 +5382,6 @@ static const struct drm_info_list i915_debugfs_list[] = { {"i915_gem_seqno", i915_gem_seqno_info, 0}, {"i915_gem_fence_regs", i915_gem_fence_regs_info, 0}, {"i915_gem_interrupt", i915_interrupt_info, 0}, - {"i915_gem_hws", i915_hws_info, 0, (void *)RCS}, - {"i915_gem_hws_blt", i915_hws_info, 0, (void *)BCS}, - {"i915_gem_hws_bsd", i915_hws_info, 0, (void *)VCS}, - {"i915_gem_hws_vebox", i915_hws_info, 0, (void *)VECS}, {"i915_gem_batch_pool", i915_gem_batch_pool_info, 0}, {"i915_guc_info", i915_guc_info, 0}, {"i915_guc_load_status", i915_guc_load_status_info, 0}, diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c index 902fa427c196..d0dcaf35b429 100644 --- a/drivers/gpu/drm/i915/i915_gem.c +++ b/drivers/gpu/drm/i915/i915_gem.c @@ -2764,6 +2764,8 @@ void i915_gem_reset(struct drm_i915_private *dev_priv) static void nop_submit_request(struct drm_i915_gem_request *request) { + i915_gem_request_submit(request); + intel_engine_init_global_seqno(request->engine, request->global_seqno); } static void i915_gem_cleanup_engine(struct intel_engine_cs *engine) diff --git a/drivers/gpu/drm/i915/i915_gem_request.c b/drivers/gpu/drm/i915/i915_gem_request.c index 27e8f257fb39..57194471f8cd 100644 --- a/drivers/gpu/drm/i915/i915_gem_request.c +++ b/drivers/gpu/drm/i915/i915_gem_request.c @@ -200,8 +200,8 @@ static void i915_gem_request_retire(struct drm_i915_gem_request *request) struct i915_gem_active *active, *next; lockdep_assert_held(&request->i915->drm.struct_mutex); - GEM_BUG_ON(!i915_sw_fence_done(&request->submit)); - GEM_BUG_ON(!i915_sw_fence_done(&request->execute)); + GEM_BUG_ON(!i915_sw_fence_signaled(&request->submit)); + GEM_BUG_ON(!i915_sw_fence_signaled(&request->execute)); GEM_BUG_ON(!i915_gem_request_completed(request)); GEM_BUG_ON(!request->i915->gt.active_requests); @@ -445,11 +445,17 @@ void i915_gem_request_submit(struct drm_i915_gem_request *request) static int __i915_sw_fence_call submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) { - if (state == FENCE_COMPLETE) { - struct drm_i915_gem_request *request = - container_of(fence, typeof(*request), submit); + struct drm_i915_gem_request *request = + container_of(fence, typeof(*request), submit); + switch (state) { + case FENCE_COMPLETE: request->engine->submit_request(request); + break; + + case FENCE_FREE: + i915_gem_request_put(request); + break; } return NOTIFY_DONE; @@ -458,6 +464,18 @@ submit_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) static int __i915_sw_fence_call execute_notify(struct i915_sw_fence *fence, enum i915_sw_fence_notify state) { + struct drm_i915_gem_request *request = + container_of(fence, typeof(*request), execute); + + switch (state) { + case FENCE_COMPLETE: + break; + + case FENCE_FREE: + i915_gem_request_put(request); + break; + } + return NOTIFY_DONE; } @@ -545,8 +563,10 @@ i915_gem_request_alloc(struct intel_engine_cs *engine, req->timeline->fence_context, __timeline_get_seqno(req->timeline->common)); - i915_sw_fence_init(&req->submit, submit_notify); - i915_sw_fence_init(&req->execute, execute_notify); + /* We bump the ref for the fence chain */ + i915_sw_fence_init(&i915_gem_request_get(req)->submit, submit_notify); + i915_sw_fence_init(&i915_gem_request_get(req)->execute, execute_notify); + /* Ensure that the execute fence completes after the submit fence - * as we complete the execute fence from within the submit fence * callback, its completion would otherwise be visible first. diff --git a/drivers/gpu/drm/i915/i915_sw_fence.h b/drivers/gpu/drm/i915/i915_sw_fence.h index 7508d23f823b..0f3185ef7f4e 100644 --- a/drivers/gpu/drm/i915/i915_sw_fence.h +++ b/drivers/gpu/drm/i915/i915_sw_fence.h @@ -75,6 +75,11 @@ int i915_sw_fence_await_reservation(struct i915_sw_fence *fence, unsigned long timeout, gfp_t gfp); +static inline bool i915_sw_fence_signaled(const struct i915_sw_fence *fence) +{ + return atomic_read(&fence->pending) <= 0; +} + static inline bool i915_sw_fence_done(const struct i915_sw_fence *fence) { return atomic_read(&fence->pending) < 0; diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c index 1c509f7410f5..49f10538d4aa 100644 --- a/drivers/gpu/drm/i915/intel_audio.c +++ b/drivers/gpu/drm/i915/intel_audio.c @@ -351,10 +351,13 @@ hsw_hdmi_audio_config_update(struct intel_crtc *intel_crtc, enum port port, I915_WRITE(HSW_AUD_CFG(pipe), tmp); + /* + * Let's disable "Enable CTS or M Prog bit" + * and let HW calculate the value + */ tmp = I915_READ(HSW_AUD_M_CTS_ENABLE(pipe)); - tmp &= ~AUD_CONFIG_M_MASK; + tmp &= ~AUD_M_CTS_M_PROG_ENABLE; tmp &= ~AUD_M_CTS_M_VALUE_INDEX; - tmp |= AUD_M_CTS_M_PROG_ENABLE; I915_WRITE(HSW_AUD_M_CTS_ENABLE(pipe), tmp); } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 962aae631f13..cf5cff7b03b8 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -12028,7 +12028,6 @@ static void intel_mmio_flip_work_func(struct work_struct *w) to_intel_framebuffer(crtc->base.primary->fb); struct drm_i915_gem_object *obj = intel_fb->obj; - i915_gem_object_wait_priority(obj, 0, I915_PRIORITY_DISPLAY); WARN_ON(i915_gem_object_wait(obj, 0, MAX_SCHEDULE_TIMEOUT, NULL) < 0); intel_pipe_update_start(crtc); @@ -12284,6 +12283,7 @@ static int intel_crtc_page_flip(struct drm_crtc *crtc, i915_add_request_no_flush(request); } + i915_gem_object_wait_priority(obj, 0, I915_PRIORITY_DISPLAY); i915_gem_track_fb(intel_fb_obj(old_fb), obj, to_intel_plane(primary)->frontbuffer_bit); mutex_unlock(&dev->struct_mutex); @@ -13995,8 +13995,9 @@ static int intel_modeset_checks(struct drm_atomic_state *state) DRM_DEBUG_KMS("New cdclk calculated to be atomic %u, actual %u\n", intel_state->cdclk, intel_state->dev_cdclk); - } else + } else { to_intel_atomic_state(state)->cdclk = dev_priv->atomic_cdclk_freq; + } intel_modeset_clear_plls(state); @@ -14097,8 +14098,9 @@ static int intel_atomic_check(struct drm_device *dev, if (ret) return ret; - } else - intel_state->cdclk = dev_priv->cdclk_freq; + } else { + intel_state->cdclk = dev_priv->atomic_cdclk_freq; + } ret = drm_atomic_helper_check_planes(dev, state); if (ret) @@ -16485,6 +16487,7 @@ int intel_modeset_init(struct drm_device *dev) intel_update_czclk(dev_priv); intel_update_cdclk(dev_priv); + dev_priv->atomic_cdclk_freq = dev_priv->cdclk_freq; intel_shared_dpll_init(dev); diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index bbb1eaf1e6db..d67974eb127a 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -3851,10 +3851,10 @@ static void skl_write_wm_level(struct drm_i915_private *dev_priv, I915_WRITE(reg, val); } -void skl_write_plane_wm(struct intel_crtc *intel_crtc, - const struct skl_plane_wm *wm, - const struct skl_ddb_allocation *ddb, - int plane) +static void skl_write_plane_wm(struct intel_crtc *intel_crtc, + const struct skl_plane_wm *wm, + const struct skl_ddb_allocation *ddb, + int plane) { struct drm_crtc *crtc = &intel_crtc->base; struct drm_device *dev = crtc->dev; @@ -3875,9 +3875,9 @@ void skl_write_plane_wm(struct intel_crtc *intel_crtc, &ddb->y_plane[pipe][plane]); } -void skl_write_cursor_wm(struct intel_crtc *intel_crtc, - const struct skl_plane_wm *wm, - const struct skl_ddb_allocation *ddb) +static void skl_write_cursor_wm(struct intel_crtc *intel_crtc, + const struct skl_plane_wm *wm, + const struct skl_ddb_allocation *ddb) { struct drm_crtc *crtc = &intel_crtc->base; struct drm_device *dev = crtc->dev; diff --git a/drivers/gpu/drm/meson/Kconfig b/drivers/gpu/drm/meson/Kconfig new file mode 100644 index 000000000000..99719afcc77f --- /dev/null +++ b/drivers/gpu/drm/meson/Kconfig @@ -0,0 +1,9 @@ +config DRM_MESON + tristate "DRM Support for Amlogic Meson Display Controller" + depends on DRM && OF && (ARM || ARM64) + depends on ARCH_MESON || COMPILE_TEST + select DRM_KMS_HELPER + select DRM_KMS_CMA_HELPER + select DRM_GEM_CMA_HELPER + select VIDEOMODE_HELPERS + select REGMAP_MMIO diff --git a/drivers/gpu/drm/meson/Makefile b/drivers/gpu/drm/meson/Makefile new file mode 100644 index 000000000000..2591978b8aad --- /dev/null +++ b/drivers/gpu/drm/meson/Makefile @@ -0,0 +1,4 @@ +meson-y := meson_drv.o meson_plane.o meson_crtc.o meson_venc_cvbs.o +meson-y += meson_viu.o meson_vpp.o meson_venc.o meson_vclk.o meson_canvas.o + +obj-$(CONFIG_DRM_MESON) += meson.o diff --git a/drivers/gpu/drm/meson/meson_canvas.c b/drivers/gpu/drm/meson/meson_canvas.c new file mode 100644 index 000000000000..4109e36c297f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_canvas.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include "meson_drv.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +/* + * CANVAS is a memory zone where physical memory frames information + * are stored for the VIU to scanout. + */ + +/* DMC Registers */ +#define DMC_CAV_LUT_DATAL 0x48 /* 0x12 offset in data sheet */ +#define CANVAS_WIDTH_LBIT 29 +#define CANVAS_WIDTH_LWID 3 +#define DMC_CAV_LUT_DATAH 0x4c /* 0x13 offset in data sheet */ +#define CANVAS_WIDTH_HBIT 0 +#define CANVAS_HEIGHT_BIT 9 +#define CANVAS_BLKMODE_BIT 24 +#define DMC_CAV_LUT_ADDR 0x50 /* 0x14 offset in data sheet */ +#define CANVAS_LUT_WR_EN (0x2 << 8) +#define CANVAS_LUT_RD_EN (0x1 << 8) + +void meson_canvas_setup(struct meson_drm *priv, + uint32_t canvas_index, uint32_t addr, + uint32_t stride, uint32_t height, + unsigned int wrap, + unsigned int blkmode) +{ + unsigned int val; + + regmap_write(priv->dmc, DMC_CAV_LUT_DATAL, + (((addr + 7) >> 3)) | + (((stride + 7) >> 3) << CANVAS_WIDTH_LBIT)); + + regmap_write(priv->dmc, DMC_CAV_LUT_DATAH, + ((((stride + 7) >> 3) >> CANVAS_WIDTH_LWID) << + CANVAS_WIDTH_HBIT) | + (height << CANVAS_HEIGHT_BIT) | + (wrap << 22) | + (blkmode << CANVAS_BLKMODE_BIT)); + + regmap_write(priv->dmc, DMC_CAV_LUT_ADDR, + CANVAS_LUT_WR_EN | canvas_index); + + /* Force a read-back to make sure everything is flushed. */ + regmap_read(priv->dmc, DMC_CAV_LUT_DATAH, &val); +} diff --git a/drivers/gpu/drm/meson/meson_canvas.h b/drivers/gpu/drm/meson/meson_canvas.h new file mode 100644 index 000000000000..af1759da4b27 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_canvas.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Canvas LUT Memory */ + +#ifndef __MESON_CANVAS_H +#define __MESON_CANVAS_H + +#define MESON_CANVAS_ID_OSD1 0x4e + +/* Canvas configuration. */ +#define MESON_CANVAS_WRAP_NONE 0x00 +#define MESON_CANVAS_WRAP_X 0x01 +#define MESON_CANVAS_WRAP_Y 0x02 + +#define MESON_CANVAS_BLKMODE_LINEAR 0x00 +#define MESON_CANVAS_BLKMODE_32x32 0x01 +#define MESON_CANVAS_BLKMODE_64x64 0x02 + +void meson_canvas_setup(struct meson_drm *priv, + uint32_t canvas_index, uint32_t addr, + uint32_t stride, uint32_t height, + unsigned int wrap, + unsigned int blkmode); + +#endif /* __MESON_CANVAS_H */ diff --git a/drivers/gpu/drm/meson/meson_crtc.c b/drivers/gpu/drm/meson/meson_crtc.c new file mode 100644 index 000000000000..749770e5c65f --- /dev/null +++ b/drivers/gpu/drm/meson/meson_crtc.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_flip_work.h> +#include <drm/drm_crtc_helper.h> + +#include "meson_crtc.h" +#include "meson_plane.h" +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_registers.h" + +/* CRTC definition */ + +struct meson_crtc { + struct drm_crtc base; + struct drm_pending_vblank_event *event; + struct meson_drm *priv; +}; +#define to_meson_crtc(x) container_of(x, struct meson_crtc, base) + +/* CRTC */ + +static const struct drm_crtc_funcs meson_crtc_funcs = { + .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, + .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, + .destroy = drm_crtc_cleanup, + .page_flip = drm_atomic_helper_page_flip, + .reset = drm_atomic_helper_crtc_reset, + .set_config = drm_atomic_helper_set_config, +}; + +static void meson_crtc_enable(struct drm_crtc *crtc) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct drm_plane *plane = meson_crtc->priv->primary_plane; + struct meson_drm *priv = meson_crtc->priv; + + /* Enable VPP Postblend */ + writel(plane->state->crtc_w, + priv->io_base + _REG(VPP_POSTBLEND_H_SIZE)); + + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, VPP_POSTBLEND_ENABLE, + priv->io_base + _REG(VPP_MISC)); + + priv->viu.osd1_enabled = true; +} + +static void meson_crtc_disable(struct drm_crtc *crtc) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + priv->viu.osd1_enabled = false; + + /* Disable VPP Postblend */ + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, + priv->io_base + _REG(VPP_MISC)); + + if (crtc->state->event && !crtc->state->active) { + spin_lock_irq(&crtc->dev->event_lock); + drm_crtc_send_vblank_event(crtc, crtc->state->event); + spin_unlock_irq(&crtc->dev->event_lock); + + crtc->state->event = NULL; + } +} + +static void meson_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + unsigned long flags; + + if (crtc->state->event) { + WARN_ON(drm_crtc_vblank_get(crtc) != 0); + + spin_lock_irqsave(&crtc->dev->event_lock, flags); + meson_crtc->event = crtc->state->event; + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + crtc->state->event = NULL; + } +} + +static void meson_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(crtc); + struct meson_drm *priv = meson_crtc->priv; + + if (priv->viu.osd1_enabled) + priv->viu.osd1_commit = true; +} + +static const struct drm_crtc_helper_funcs meson_crtc_helper_funcs = { + .enable = meson_crtc_enable, + .disable = meson_crtc_disable, + .atomic_begin = meson_crtc_atomic_begin, + .atomic_flush = meson_crtc_atomic_flush, +}; + +void meson_crtc_irq(struct meson_drm *priv) +{ + struct meson_crtc *meson_crtc = to_meson_crtc(priv->crtc); + unsigned long flags; + + /* Update the OSD registers */ + if (priv->viu.osd1_enabled && priv->viu.osd1_commit) { + writel_relaxed(priv->viu.osd1_ctrl_stat, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_relaxed(priv->viu.osd1_blk0_cfg[0], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W0)); + writel_relaxed(priv->viu.osd1_blk0_cfg[1], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W1)); + writel_relaxed(priv->viu.osd1_blk0_cfg[2], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W2)); + writel_relaxed(priv->viu.osd1_blk0_cfg[3], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W3)); + writel_relaxed(priv->viu.osd1_blk0_cfg[4], + priv->io_base + _REG(VIU_OSD1_BLK0_CFG_W4)); + + /* If output is interlace, make use of the Scaler */ + if (priv->viu.osd1_interlace) { + struct drm_plane *plane = priv->primary_plane; + struct drm_plane_state *state = plane->state; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + + meson_vpp_setup_interlace_vscaler_osd1(priv, &dest); + } else + meson_vpp_disable_interlace_vscaler_osd1(priv); + + /* Enable OSD1 */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND, VPP_OSD1_POSTBLEND, + priv->io_base + _REG(VPP_MISC)); + + priv->viu.osd1_commit = false; + } + + drm_crtc_handle_vblank(priv->crtc); + + spin_lock_irqsave(&priv->drm->event_lock, flags); + if (meson_crtc->event) { + drm_crtc_send_vblank_event(priv->crtc, meson_crtc->event); + drm_crtc_vblank_put(priv->crtc); + meson_crtc->event = NULL; + } + spin_unlock_irqrestore(&priv->drm->event_lock, flags); +} + +int meson_crtc_create(struct meson_drm *priv) +{ + struct meson_crtc *meson_crtc; + struct drm_crtc *crtc; + int ret; + + meson_crtc = devm_kzalloc(priv->drm->dev, sizeof(*meson_crtc), + GFP_KERNEL); + if (!meson_crtc) + return -ENOMEM; + + meson_crtc->priv = priv; + crtc = &meson_crtc->base; + ret = drm_crtc_init_with_planes(priv->drm, crtc, + priv->primary_plane, NULL, + &meson_crtc_funcs, "meson_crtc"); + if (ret) { + dev_err(priv->drm->dev, "Failed to init CRTC\n"); + return ret; + } + + drm_crtc_helper_add(crtc, &meson_crtc_helper_funcs); + + priv->crtc = crtc; + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_crtc.h b/drivers/gpu/drm/meson/meson_crtc.h new file mode 100644 index 000000000000..b62b9e51764d --- /dev/null +++ b/drivers/gpu/drm/meson/meson_crtc.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#ifndef __MESON_CRTC_H +#define __MESON_CRTC_H + +#include "meson_drv.h" + +int meson_crtc_create(struct meson_drm *priv); + +void meson_crtc_irq(struct meson_drm *priv); + +#endif /* __MESON_CRTC_H */ diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c new file mode 100644 index 000000000000..ff1f6019b97b --- /dev/null +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -0,0 +1,343 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/of_graph.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_flip_work.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_rect.h> +#include <drm/drm_fb_helper.h> + +#include "meson_drv.h" +#include "meson_plane.h" +#include "meson_crtc.h" +#include "meson_venc_cvbs.h" + +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_venc.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +#define DRIVER_NAME "meson" +#define DRIVER_DESC "Amlogic Meson DRM driver" + +/* + * Video Processing Unit + * + * VPU Handles the Global Video Processing, it includes management of the + * clocks gates, blocks reset lines and power domains. + * + * What is missing : + * - Full reset of entire video processing HW blocks + * - Scaling and setup of the VPU clock + * - Bus clock gates + * - Powering up video processing HW blocks + * - Powering Up HDMI controller and PHY + */ + +static void meson_fb_output_poll_changed(struct drm_device *dev) +{ + struct meson_drm *priv = dev->dev_private; + + drm_fbdev_cma_hotplug_event(priv->fbdev); +} + +static const struct drm_mode_config_funcs meson_mode_config_funcs = { + .output_poll_changed = meson_fb_output_poll_changed, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, + .fb_create = drm_fb_cma_create, +}; + +static int meson_enable_vblank(struct drm_device *dev, unsigned int crtc) +{ + struct meson_drm *priv = dev->dev_private; + + meson_venc_enable_vsync(priv); + + return 0; +} + +static void meson_disable_vblank(struct drm_device *dev, unsigned int crtc) +{ + struct meson_drm *priv = dev->dev_private; + + meson_venc_disable_vsync(priv); +} + +static irqreturn_t meson_irq(int irq, void *arg) +{ + struct drm_device *dev = arg; + struct meson_drm *priv = dev->dev_private; + + (void)readl_relaxed(priv->io_base + _REG(VENC_INTFLAG)); + + meson_crtc_irq(priv); + + return IRQ_HANDLED; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .llseek = no_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static struct drm_driver meson_driver = { + .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | + DRIVER_MODESET | DRIVER_PRIME | + DRIVER_ATOMIC, + + /* Vblank */ + .enable_vblank = meson_enable_vblank, + .disable_vblank = meson_disable_vblank, + .get_vblank_counter = drm_vblank_no_hw_counter, + + /* IRQ */ + .irq_handler = meson_irq, + + /* PRIME Ops */ + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + + /* GEM Ops */ + .dumb_create = drm_gem_cma_dumb_create, + .dumb_destroy = drm_gem_dumb_destroy, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .gem_free_object_unlocked = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + + /* Misc */ + .fops = &fops, + .name = DRIVER_NAME, + .desc = DRIVER_DESC, + .date = "20161109", + .major = 1, + .minor = 0, +}; + +static bool meson_vpu_has_available_connectors(struct device *dev) +{ + struct device_node *ep, *remote; + + /* Parses each endpoint and check if remote exists */ + for_each_endpoint_of_node(dev->of_node, ep) { + /* If the endpoint node exists, consider it enabled */ + remote = of_graph_get_remote_port(ep); + if (remote) + return true; + } + + return false; +} + +static struct regmap_config meson_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = 0x1000, +}; + +static int meson_drv_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct meson_drm *priv; + struct drm_device *drm; + struct resource *res; + void __iomem *regs; + int ret; + + /* Checks if an output connector is available */ + if (!meson_vpu_has_available_connectors(dev)) { + dev_err(dev, "No output connector available\n"); + return -ENODEV; + } + + drm = drm_dev_alloc(&meson_driver, dev); + if (IS_ERR(drm)) + return PTR_ERR(drm); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto free_drm; + } + drm->dev_private = priv; + priv->drm = drm; + priv->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpu"); + regs = devm_ioremap_resource(dev, res); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + priv->io_base = regs; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); + /* Simply ioremap since it may be a shared register zone */ + regs = devm_ioremap(dev, res->start, resource_size(res)); + if (!regs) + return -EADDRNOTAVAIL; + + priv->hhi = devm_regmap_init_mmio(dev, regs, + &meson_regmap_config); + if (IS_ERR(priv->hhi)) { + dev_err(&pdev->dev, "Couldn't create the HHI regmap\n"); + return PTR_ERR(priv->hhi); + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); + /* Simply ioremap since it may be a shared register zone */ + regs = devm_ioremap(dev, res->start, resource_size(res)); + if (!regs) + return -EADDRNOTAVAIL; + + priv->dmc = devm_regmap_init_mmio(dev, regs, + &meson_regmap_config); + if (IS_ERR(priv->dmc)) { + dev_err(&pdev->dev, "Couldn't create the DMC regmap\n"); + return PTR_ERR(priv->dmc); + } + + priv->vsync_irq = platform_get_irq(pdev, 0); + + drm_vblank_init(drm, 1); + drm_mode_config_init(drm); + + /* Encoder Initialization */ + + ret = meson_venc_cvbs_create(priv); + if (ret) + goto free_drm; + + /* Hardware Initialization */ + + meson_venc_init(priv); + meson_vpp_init(priv); + meson_viu_init(priv); + + ret = meson_plane_create(priv); + if (ret) + goto free_drm; + + ret = meson_crtc_create(priv); + if (ret) + goto free_drm; + + ret = drm_irq_install(drm, priv->vsync_irq); + if (ret) + goto free_drm; + + drm_mode_config_reset(drm); + drm->mode_config.max_width = 8192; + drm->mode_config.max_height = 8192; + drm->mode_config.funcs = &meson_mode_config_funcs; + + priv->fbdev = drm_fbdev_cma_init(drm, 32, + drm->mode_config.num_crtc, + drm->mode_config.num_connector); + if (IS_ERR(priv->fbdev)) { + ret = PTR_ERR(priv->fbdev); + goto free_drm; + } + + drm_kms_helper_poll_init(drm); + + platform_set_drvdata(pdev, priv); + + ret = drm_dev_register(drm, 0); + if (ret) + goto free_drm; + + return 0; + +free_drm: + drm_dev_unref(drm); + + return ret; +} + +static int meson_drv_remove(struct platform_device *pdev) +{ + struct drm_device *drm = dev_get_drvdata(&pdev->dev); + struct meson_drm *priv = drm->dev_private; + + drm_dev_unregister(drm); + drm_kms_helper_poll_fini(drm); + drm_fbdev_cma_fini(priv->fbdev); + drm_mode_config_cleanup(drm); + drm_vblank_cleanup(drm); + drm_dev_unref(drm); + + return 0; +} + +static const struct of_device_id dt_match[] = { + { .compatible = "amlogic,meson-gxbb-vpu" }, + { .compatible = "amlogic,meson-gxl-vpu" }, + { .compatible = "amlogic,meson-gxm-vpu" }, + {} +}; +MODULE_DEVICE_TABLE(of, dt_match); + +static struct platform_driver meson_drm_platform_driver = { + .probe = meson_drv_probe, + .remove = meson_drv_remove, + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + .of_match_table = dt_match, + }, +}; + +module_platform_driver(meson_drm_platform_driver); + +MODULE_AUTHOR("Jasper St. Pierre <jstpierre@mecheye.net>"); +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/meson/meson_drv.h b/drivers/gpu/drm/meson/meson_drv.h new file mode 100644 index 000000000000..6195327c51ca --- /dev/null +++ b/drivers/gpu/drm/meson/meson_drv.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef __MESON_DRV_H +#define __MESON_DRV_H + +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/of.h> +#include <drm/drmP.h> + +struct meson_drm { + struct device *dev; + void __iomem *io_base; + struct regmap *hhi; + struct regmap *dmc; + int vsync_irq; + + struct drm_device *drm; + struct drm_crtc *crtc; + struct drm_fbdev_cma *fbdev; + struct drm_plane *primary_plane; + + /* Components Data */ + struct { + bool osd1_enabled; + bool osd1_interlace; + bool osd1_commit; + uint32_t osd1_ctrl_stat; + uint32_t osd1_blk0_cfg[5]; + } viu; + + struct { + unsigned int current_mode; + } venc; +}; + +static inline int meson_vpu_is_compatible(struct meson_drm *priv, + const char *compat) +{ + return of_device_is_compatible(priv->dev->of_node, compat); +} + +#endif /* __MESON_DRV_H */ diff --git a/drivers/gpu/drm/meson/meson_plane.c b/drivers/gpu/drm/meson/meson_plane.c new file mode 100644 index 000000000000..4942ca090b46 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_plane.c @@ -0,0 +1,230 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_rect.h> + +#include "meson_plane.h" +#include "meson_vpp.h" +#include "meson_viu.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +struct meson_plane { + struct drm_plane base; + struct meson_drm *priv; +}; +#define to_meson_plane(x) container_of(x, struct meson_plane, base) + +static int meson_plane_atomic_check(struct drm_plane *plane, + struct drm_plane_state *state) +{ + struct drm_crtc_state *crtc_state; + struct drm_rect clip = { 0, }; + + crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + clip.x2 = crtc_state->mode.hdisplay; + clip.y2 = crtc_state->mode.vdisplay; + + return drm_plane_helper_check_state(state, &clip, + DRM_PLANE_HELPER_NO_SCALING, + DRM_PLANE_HELPER_NO_SCALING, + true, true); +} + +/* Takes a fixed 16.16 number and converts it to integer. */ +static inline int64_t fixed16_to_int(int64_t value) +{ + return value >> 16; +} + +static void meson_plane_atomic_update(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct drm_plane_state *state = plane->state; + struct drm_framebuffer *fb = state->fb; + struct meson_drm *priv = meson_plane->priv; + struct drm_gem_cma_object *gem; + struct drm_rect src = { + .x1 = (state->src_x), + .y1 = (state->src_y), + .x2 = (state->src_x + state->src_w), + .y2 = (state->src_y + state->src_h), + }; + struct drm_rect dest = { + .x1 = state->crtc_x, + .y1 = state->crtc_y, + .x2 = state->crtc_x + state->crtc_w, + .y2 = state->crtc_y + state->crtc_h, + }; + unsigned long flags; + + /* + * Update Coordinates + * Update Formats + * Update Buffer + * Enable Plane + */ + spin_lock_irqsave(&priv->drm->event_lock, flags); + + /* Enable OSD and BLK0, set max global alpha */ + priv->viu.osd1_ctrl_stat = OSD_ENABLE | + (0xFF << OSD_GLOBAL_ALPHA_SHIFT) | + OSD_BLK0_ENABLE; + + /* Set up BLK0 to point to the right canvas */ + priv->viu.osd1_blk0_cfg[0] = ((MESON_CANVAS_ID_OSD1 << OSD_CANVAS_SEL) | + OSD_ENDIANNESS_LE); + + /* On GXBB, Use the old non-HDR RGB2YUV converter */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + priv->viu.osd1_blk0_cfg[0] |= OSD_OUTPUT_COLOR_RGB; + + switch (fb->pixel_format) { + case DRM_FORMAT_XRGB8888: + /* For XRGB, replace the pixel's alpha by 0xFF */ + writel_bits_relaxed(OSD_REPLACE_EN, OSD_REPLACE_EN, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ARGB; + break; + case DRM_FORMAT_ARGB8888: + /* For ARGB, use the pixel's alpha */ + writel_bits_relaxed(OSD_REPLACE_EN, 0, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_32 | + OSD_COLOR_MATRIX_32_ARGB; + break; + case DRM_FORMAT_RGB888: + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_24 | + OSD_COLOR_MATRIX_24_RGB; + break; + case DRM_FORMAT_RGB565: + priv->viu.osd1_blk0_cfg[0] |= OSD_BLK_MODE_16 | + OSD_COLOR_MATRIX_16_RGB565; + break; + }; + + if (state->crtc->mode.flags & DRM_MODE_FLAG_INTERLACE) { + priv->viu.osd1_interlace = true; + + dest.y1 /= 2; + dest.y2 /= 2; + } else + priv->viu.osd1_interlace = false; + + /* + * The format of these registers is (x2 << 16 | x1), + * where x2 is exclusive. + * e.g. +30x1920 would be (1919 << 16) | 30 + */ + priv->viu.osd1_blk0_cfg[1] = ((fixed16_to_int(src.x2) - 1) << 16) | + fixed16_to_int(src.x1); + priv->viu.osd1_blk0_cfg[2] = ((fixed16_to_int(src.y2) - 1) << 16) | + fixed16_to_int(src.y1); + priv->viu.osd1_blk0_cfg[3] = ((dest.x2 - 1) << 16) | dest.x1; + priv->viu.osd1_blk0_cfg[4] = ((dest.y2 - 1) << 16) | dest.y1; + + /* Update Canvas with buffer address */ + gem = drm_fb_cma_get_gem_obj(fb, 0); + + meson_canvas_setup(priv, MESON_CANVAS_ID_OSD1, + gem->paddr, fb->pitches[0], + fb->height, MESON_CANVAS_WRAP_NONE, + MESON_CANVAS_BLKMODE_LINEAR); + + spin_unlock_irqrestore(&priv->drm->event_lock, flags); +} + +static void meson_plane_atomic_disable(struct drm_plane *plane, + struct drm_plane_state *old_state) +{ + struct meson_plane *meson_plane = to_meson_plane(plane); + struct meson_drm *priv = meson_plane->priv; + + /* Disable OSD1 */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + +} + +static const struct drm_plane_helper_funcs meson_plane_helper_funcs = { + .atomic_check = meson_plane_atomic_check, + .atomic_disable = meson_plane_atomic_disable, + .atomic_update = meson_plane_atomic_update, +}; + +static const struct drm_plane_funcs meson_plane_funcs = { + .update_plane = drm_atomic_helper_update_plane, + .disable_plane = drm_atomic_helper_disable_plane, + .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, +}; + +static const uint32_t supported_drm_formats[] = { + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB888, + DRM_FORMAT_RGB565, +}; + +int meson_plane_create(struct meson_drm *priv) +{ + struct meson_plane *meson_plane; + struct drm_plane *plane; + + meson_plane = devm_kzalloc(priv->drm->dev, sizeof(*meson_plane), + GFP_KERNEL); + if (!meson_plane) + return -ENOMEM; + + meson_plane->priv = priv; + plane = &meson_plane->base; + + drm_universal_plane_init(priv->drm, plane, 0xFF, + &meson_plane_funcs, + supported_drm_formats, + ARRAY_SIZE(supported_drm_formats), + DRM_PLANE_TYPE_PRIMARY, "meson_primary_plane"); + + drm_plane_helper_add(plane, &meson_plane_helper_funcs); + + priv->primary_plane = plane; + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_plane.h b/drivers/gpu/drm/meson/meson_plane.h new file mode 100644 index 000000000000..e26b8b0aa1fa --- /dev/null +++ b/drivers/gpu/drm/meson/meson_plane.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#ifndef __MESON_PLANE_H +#define __MESON_PLANE_H + +#include "meson_drv.h" + +int meson_plane_create(struct meson_drm *priv); + +#endif /* __MESON_PLANE_H */ diff --git a/drivers/gpu/drm/meson/meson_registers.h b/drivers/gpu/drm/meson/meson_registers.h new file mode 100644 index 000000000000..6adf9c13fafa --- /dev/null +++ b/drivers/gpu/drm/meson/meson_registers.h @@ -0,0 +1,1395 @@ +/* + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#ifndef __MESON_REGISTERS_H +#define __MESON_REGISTERS_H + +/* Shift all registers by 2 */ +#define _REG(reg) ((reg) << 2) + +#define writel_bits_relaxed(mask, val, addr) \ + writel_relaxed((readl_relaxed(addr) & ~(mask)) | (val), addr) + +/* vpp2 */ +#define VPP2_DUMMY_DATA 0x1900 +#define VPP2_LINE_IN_LENGTH 0x1901 +#define VPP2_PIC_IN_HEIGHT 0x1902 +#define VPP2_SCALE_COEF_IDX 0x1903 +#define VPP2_SCALE_COEF 0x1904 +#define VPP2_VSC_REGION12_STARTP 0x1905 +#define VPP2_VSC_REGION34_STARTP 0x1906 +#define VPP2_VSC_REGION4_ENDP 0x1907 +#define VPP2_VSC_START_PHASE_STEP 0x1908 +#define VPP2_VSC_REGION0_PHASE_SLOPE 0x1909 +#define VPP2_VSC_REGION1_PHASE_SLOPE 0x190a +#define VPP2_VSC_REGION3_PHASE_SLOPE 0x190b +#define VPP2_VSC_REGION4_PHASE_SLOPE 0x190c +#define VPP2_VSC_PHASE_CTRL 0x190d +#define VPP2_VSC_INI_PHASE 0x190e +#define VPP2_HSC_REGION12_STARTP 0x1910 +#define VPP2_HSC_REGION34_STARTP 0x1911 +#define VPP2_HSC_REGION4_ENDP 0x1912 +#define VPP2_HSC_START_PHASE_STEP 0x1913 +#define VPP2_HSC_REGION0_PHASE_SLOPE 0x1914 +#define VPP2_HSC_REGION1_PHASE_SLOPE 0x1915 +#define VPP2_HSC_REGION3_PHASE_SLOPE 0x1916 +#define VPP2_HSC_REGION4_PHASE_SLOPE 0x1917 +#define VPP2_HSC_PHASE_CTRL 0x1918 +#define VPP2_SC_MISC 0x1919 +#define VPP2_PREBLEND_VD1_H_START_END 0x191a +#define VPP2_PREBLEND_VD1_V_START_END 0x191b +#define VPP2_POSTBLEND_VD1_H_START_END 0x191c +#define VPP2_POSTBLEND_VD1_V_START_END 0x191d +#define VPP2_PREBLEND_H_SIZE 0x1920 +#define VPP2_POSTBLEND_H_SIZE 0x1921 +#define VPP2_HOLD_LINES 0x1922 +#define VPP2_BLEND_ONECOLOR_CTRL 0x1923 +#define VPP2_PREBLEND_CURRENT_XY 0x1924 +#define VPP2_POSTBLEND_CURRENT_XY 0x1925 +#define VPP2_MISC 0x1926 +#define VPP2_OFIFO_SIZE 0x1927 +#define VPP2_FIFO_STATUS 0x1928 +#define VPP2_SMOKE_CTRL 0x1929 +#define VPP2_SMOKE1_VAL 0x192a +#define VPP2_SMOKE2_VAL 0x192b +#define VPP2_SMOKE1_H_START_END 0x192d +#define VPP2_SMOKE1_V_START_END 0x192e +#define VPP2_SMOKE2_H_START_END 0x192f +#define VPP2_SMOKE2_V_START_END 0x1930 +#define VPP2_SCO_FIFO_CTRL 0x1933 +#define VPP2_HSC_PHASE_CTRL1 0x1934 +#define VPP2_HSC_INI_PAT_CTRL 0x1935 +#define VPP2_VADJ_CTRL 0x1940 +#define VPP2_VADJ1_Y 0x1941 +#define VPP2_VADJ1_MA_MB 0x1942 +#define VPP2_VADJ1_MC_MD 0x1943 +#define VPP2_VADJ2_Y 0x1944 +#define VPP2_VADJ2_MA_MB 0x1945 +#define VPP2_VADJ2_MC_MD 0x1946 +#define VPP2_MATRIX_PROBE_COLOR 0x195c +#define VPP2_MATRIX_HL_COLOR 0x195d +#define VPP2_MATRIX_PROBE_POS 0x195e +#define VPP2_MATRIX_CTRL 0x195f +#define VPP2_MATRIX_COEF00_01 0x1960 +#define VPP2_MATRIX_COEF02_10 0x1961 +#define VPP2_MATRIX_COEF11_12 0x1962 +#define VPP2_MATRIX_COEF20_21 0x1963 +#define VPP2_MATRIX_COEF22 0x1964 +#define VPP2_MATRIX_OFFSET0_1 0x1965 +#define VPP2_MATRIX_OFFSET2 0x1966 +#define VPP2_MATRIX_PRE_OFFSET0_1 0x1967 +#define VPP2_MATRIX_PRE_OFFSET2 0x1968 +#define VPP2_DUMMY_DATA1 0x1969 +#define VPP2_GAINOFF_CTRL0 0x196a +#define VPP2_GAINOFF_CTRL1 0x196b +#define VPP2_GAINOFF_CTRL2 0x196c +#define VPP2_GAINOFF_CTRL3 0x196d +#define VPP2_GAINOFF_CTRL4 0x196e +#define VPP2_CHROMA_ADDR_PORT 0x1970 +#define VPP2_CHROMA_DATA_PORT 0x1971 +#define VPP2_GCLK_CTRL0 0x1972 +#define VPP2_GCLK_CTRL1 0x1973 +#define VPP2_SC_GCLK_CTRL 0x1974 +#define VPP2_MISC1 0x1976 +#define VPP2_DNLP_CTRL_00 0x1981 +#define VPP2_DNLP_CTRL_01 0x1982 +#define VPP2_DNLP_CTRL_02 0x1983 +#define VPP2_DNLP_CTRL_03 0x1984 +#define VPP2_DNLP_CTRL_04 0x1985 +#define VPP2_DNLP_CTRL_05 0x1986 +#define VPP2_DNLP_CTRL_06 0x1987 +#define VPP2_DNLP_CTRL_07 0x1988 +#define VPP2_DNLP_CTRL_08 0x1989 +#define VPP2_DNLP_CTRL_09 0x198a +#define VPP2_DNLP_CTRL_10 0x198b +#define VPP2_DNLP_CTRL_11 0x198c +#define VPP2_DNLP_CTRL_12 0x198d +#define VPP2_DNLP_CTRL_13 0x198e +#define VPP2_DNLP_CTRL_14 0x198f +#define VPP2_DNLP_CTRL_15 0x1990 +#define VPP2_VE_ENABLE_CTRL 0x19a1 +#define VPP2_VE_DEMO_LEFT_TOP_SCREEN_WIDTH 0x19a2 +#define VPP2_VE_DEMO_CENTER_BAR 0x19a3 +#define VPP2_VE_H_V_SIZE 0x19a4 +#define VPP2_VDO_MEAS_CTRL 0x19a8 +#define VPP2_VDO_MEAS_VS_COUNT_HI 0x19a9 +#define VPP2_VDO_MEAS_VS_COUNT_LO 0x19aa +#define VPP2_OSD_VSC_PHASE_STEP 0x19c0 +#define VPP2_OSD_VSC_INI_PHASE 0x19c1 +#define VPP2_OSD_VSC_CTRL0 0x19c2 +#define VPP2_OSD_HSC_PHASE_STEP 0x19c3 +#define VPP2_OSD_HSC_INI_PHASE 0x19c4 +#define VPP2_OSD_HSC_CTRL0 0x19c5 +#define VPP2_OSD_HSC_INI_PAT_CTRL 0x19c6 +#define VPP2_OSD_SC_DUMMY_DATA 0x19c7 +#define VPP2_OSD_SC_CTRL0 0x19c8 +#define VPP2_OSD_SCI_WH_M1 0x19c9 +#define VPP2_OSD_SCO_H_START_END 0x19ca +#define VPP2_OSD_SCO_V_START_END 0x19cb +#define VPP2_OSD_SCALE_COEF_IDX 0x19cc +#define VPP2_OSD_SCALE_COEF 0x19cd +#define VPP2_INT_LINE_NUM 0x19ce + +/* viu */ +#define VIU_ADDR_START 0x1a00 +#define VIU_ADDR_END 0x1aff +#define VIU_SW_RESET 0x1a01 +#define VIU_MISC_CTRL0 0x1a06 +#define VIU_MISC_CTRL1 0x1a07 +#define D2D3_INTF_LENGTH 0x1a08 +#define D2D3_INTF_CTRL0 0x1a09 +#define VIU_OSD1_CTRL_STAT 0x1a10 +#define VIU_OSD1_CTRL_STAT2 0x1a2d +#define VIU_OSD1_COLOR_ADDR 0x1a11 +#define VIU_OSD1_COLOR 0x1a12 +#define VIU_OSD1_TCOLOR_AG0 0x1a17 +#define VIU_OSD1_TCOLOR_AG1 0x1a18 +#define VIU_OSD1_TCOLOR_AG2 0x1a19 +#define VIU_OSD1_TCOLOR_AG3 0x1a1a +#define VIU_OSD1_BLK0_CFG_W0 0x1a1b +#define VIU_OSD1_BLK1_CFG_W0 0x1a1f +#define VIU_OSD1_BLK2_CFG_W0 0x1a23 +#define VIU_OSD1_BLK3_CFG_W0 0x1a27 +#define VIU_OSD1_BLK0_CFG_W1 0x1a1c +#define VIU_OSD1_BLK1_CFG_W1 0x1a20 +#define VIU_OSD1_BLK2_CFG_W1 0x1a24 +#define VIU_OSD1_BLK3_CFG_W1 0x1a28 +#define VIU_OSD1_BLK0_CFG_W2 0x1a1d +#define VIU_OSD1_BLK1_CFG_W2 0x1a21 +#define VIU_OSD1_BLK2_CFG_W2 0x1a25 +#define VIU_OSD1_BLK3_CFG_W2 0x1a29 +#define VIU_OSD1_BLK0_CFG_W3 0x1a1e +#define VIU_OSD1_BLK1_CFG_W3 0x1a22 +#define VIU_OSD1_BLK2_CFG_W3 0x1a26 +#define VIU_OSD1_BLK3_CFG_W3 0x1a2a +#define VIU_OSD1_BLK0_CFG_W4 0x1a13 +#define VIU_OSD1_BLK1_CFG_W4 0x1a14 +#define VIU_OSD1_BLK2_CFG_W4 0x1a15 +#define VIU_OSD1_BLK3_CFG_W4 0x1a16 +#define VIU_OSD1_FIFO_CTRL_STAT 0x1a2b +#define VIU_OSD1_TEST_RDDATA 0x1a2c +#define VIU_OSD1_PROT_CTRL 0x1a2e +#define VIU_OSD2_CTRL_STAT 0x1a30 +#define VIU_OSD2_CTRL_STAT2 0x1a4d +#define VIU_OSD2_COLOR_ADDR 0x1a31 +#define VIU_OSD2_COLOR 0x1a32 +#define VIU_OSD2_HL1_H_START_END 0x1a33 +#define VIU_OSD2_HL1_V_START_END 0x1a34 +#define VIU_OSD2_HL2_H_START_END 0x1a35 +#define VIU_OSD2_HL2_V_START_END 0x1a36 +#define VIU_OSD2_TCOLOR_AG0 0x1a37 +#define VIU_OSD2_TCOLOR_AG1 0x1a38 +#define VIU_OSD2_TCOLOR_AG2 0x1a39 +#define VIU_OSD2_TCOLOR_AG3 0x1a3a +#define VIU_OSD2_BLK0_CFG_W0 0x1a3b +#define VIU_OSD2_BLK1_CFG_W0 0x1a3f +#define VIU_OSD2_BLK2_CFG_W0 0x1a43 +#define VIU_OSD2_BLK3_CFG_W0 0x1a47 +#define VIU_OSD2_BLK0_CFG_W1 0x1a3c +#define VIU_OSD2_BLK1_CFG_W1 0x1a40 +#define VIU_OSD2_BLK2_CFG_W1 0x1a44 +#define VIU_OSD2_BLK3_CFG_W1 0x1a48 +#define VIU_OSD2_BLK0_CFG_W2 0x1a3d +#define VIU_OSD2_BLK1_CFG_W2 0x1a41 +#define VIU_OSD2_BLK2_CFG_W2 0x1a45 +#define VIU_OSD2_BLK3_CFG_W2 0x1a49 +#define VIU_OSD2_BLK0_CFG_W3 0x1a3e +#define VIU_OSD2_BLK1_CFG_W3 0x1a42 +#define VIU_OSD2_BLK2_CFG_W3 0x1a46 +#define VIU_OSD2_BLK3_CFG_W3 0x1a4a +#define VIU_OSD2_BLK0_CFG_W4 0x1a64 +#define VIU_OSD2_BLK1_CFG_W4 0x1a65 +#define VIU_OSD2_BLK2_CFG_W4 0x1a66 +#define VIU_OSD2_BLK3_CFG_W4 0x1a67 +#define VIU_OSD2_FIFO_CTRL_STAT 0x1a4b +#define VIU_OSD2_TEST_RDDATA 0x1a4c +#define VIU_OSD2_PROT_CTRL 0x1a4e + +#define VD1_IF0_GEN_REG 0x1a50 +#define VD1_IF0_CANVAS0 0x1a51 +#define VD1_IF0_CANVAS1 0x1a52 +#define VD1_IF0_LUMA_X0 0x1a53 +#define VD1_IF0_LUMA_Y0 0x1a54 +#define VD1_IF0_CHROMA_X0 0x1a55 +#define VD1_IF0_CHROMA_Y0 0x1a56 +#define VD1_IF0_LUMA_X1 0x1a57 +#define VD1_IF0_LUMA_Y1 0x1a58 +#define VD1_IF0_CHROMA_X1 0x1a59 +#define VD1_IF0_CHROMA_Y1 0x1a5a +#define VD1_IF0_RPT_LOOP 0x1a5b +#define VD1_IF0_LUMA0_RPT_PAT 0x1a5c +#define VD1_IF0_CHROMA0_RPT_PAT 0x1a5d +#define VD1_IF0_LUMA1_RPT_PAT 0x1a5e +#define VD1_IF0_CHROMA1_RPT_PAT 0x1a5f +#define VD1_IF0_LUMA_PSEL 0x1a60 +#define VD1_IF0_CHROMA_PSEL 0x1a61 +#define VD1_IF0_DUMMY_PIXEL 0x1a62 +#define VD1_IF0_LUMA_FIFO_SIZE 0x1a63 +#define VD1_IF0_RANGE_MAP_Y 0x1a6a +#define VD1_IF0_RANGE_MAP_CB 0x1a6b +#define VD1_IF0_RANGE_MAP_CR 0x1a6c +#define VD1_IF0_GEN_REG2 0x1a6d +#define VD1_IF0_PROT_CNTL 0x1a6e +#define VIU_VD1_FMT_CTRL 0x1a68 +#define VIU_VD1_FMT_W 0x1a69 +#define VD2_IF0_GEN_REG 0x1a70 +#define VD2_IF0_CANVAS0 0x1a71 +#define VD2_IF0_CANVAS1 0x1a72 +#define VD2_IF0_LUMA_X0 0x1a73 +#define VD2_IF0_LUMA_Y0 0x1a74 +#define VD2_IF0_CHROMA_X0 0x1a75 +#define VD2_IF0_CHROMA_Y0 0x1a76 +#define VD2_IF0_LUMA_X1 0x1a77 +#define VD2_IF0_LUMA_Y1 0x1a78 +#define VD2_IF0_CHROMA_X1 0x1a79 +#define VD2_IF0_CHROMA_Y1 0x1a7a +#define VD2_IF0_RPT_LOOP 0x1a7b +#define VD2_IF0_LUMA0_RPT_PAT 0x1a7c +#define VD2_IF0_CHROMA0_RPT_PAT 0x1a7d +#define VD2_IF0_LUMA1_RPT_PAT 0x1a7e +#define VD2_IF0_CHROMA1_RPT_PAT 0x1a7f +#define VD2_IF0_LUMA_PSEL 0x1a80 +#define VD2_IF0_CHROMA_PSEL 0x1a81 +#define VD2_IF0_DUMMY_PIXEL 0x1a82 +#define VD2_IF0_LUMA_FIFO_SIZE 0x1a83 +#define VD2_IF0_RANGE_MAP_Y 0x1a8a +#define VD2_IF0_RANGE_MAP_CB 0x1a8b +#define VD2_IF0_RANGE_MAP_CR 0x1a8c +#define VD2_IF0_GEN_REG2 0x1a8d +#define VD2_IF0_PROT_CNTL 0x1a8e +#define VIU_VD2_FMT_CTRL 0x1a88 +#define VIU_VD2_FMT_W 0x1a89 + +/* VIU Matrix Registers */ +#define VIU_OSD1_MATRIX_CTRL 0x1a90 +#define VIU_OSD1_MATRIX_COEF00_01 0x1a91 +#define VIU_OSD1_MATRIX_COEF02_10 0x1a92 +#define VIU_OSD1_MATRIX_COEF11_12 0x1a93 +#define VIU_OSD1_MATRIX_COEF20_21 0x1a94 +#define VIU_OSD1_MATRIX_COLMOD_COEF42 0x1a95 +#define VIU_OSD1_MATRIX_OFFSET0_1 0x1a96 +#define VIU_OSD1_MATRIX_OFFSET2 0x1a97 +#define VIU_OSD1_MATRIX_PRE_OFFSET0_1 0x1a98 +#define VIU_OSD1_MATRIX_PRE_OFFSET2 0x1a99 +#define VIU_OSD1_MATRIX_COEF22_30 0x1a9d +#define VIU_OSD1_MATRIX_COEF31_32 0x1a9e +#define VIU_OSD1_MATRIX_COEF40_41 0x1a9f +#define VIU_OSD1_EOTF_CTL 0x1ad4 +#define VIU_OSD1_EOTF_COEF00_01 0x1ad5 +#define VIU_OSD1_EOTF_COEF02_10 0x1ad6 +#define VIU_OSD1_EOTF_COEF11_12 0x1ad7 +#define VIU_OSD1_EOTF_COEF20_21 0x1ad8 +#define VIU_OSD1_EOTF_COEF22_RS 0x1ad9 +#define VIU_OSD1_EOTF_LUT_ADDR_PORT 0x1ada +#define VIU_OSD1_EOTF_LUT_DATA_PORT 0x1adb +#define VIU_OSD1_OETF_CTL 0x1adc +#define VIU_OSD1_OETF_LUT_ADDR_PORT 0x1add +#define VIU_OSD1_OETF_LUT_DATA_PORT 0x1ade + +/* vpp */ +#define VPP_DUMMY_DATA 0x1d00 +#define VPP_LINE_IN_LENGTH 0x1d01 +#define VPP_PIC_IN_HEIGHT 0x1d02 +#define VPP_SCALE_COEF_IDX 0x1d03 +#define VPP_SCALE_COEF 0x1d04 +#define VPP_VSC_REGION12_STARTP 0x1d05 +#define VPP_VSC_REGION34_STARTP 0x1d06 +#define VPP_VSC_REGION4_ENDP 0x1d07 +#define VPP_VSC_START_PHASE_STEP 0x1d08 +#define VPP_VSC_REGION0_PHASE_SLOPE 0x1d09 +#define VPP_VSC_REGION1_PHASE_SLOPE 0x1d0a +#define VPP_VSC_REGION3_PHASE_SLOPE 0x1d0b +#define VPP_VSC_REGION4_PHASE_SLOPE 0x1d0c +#define VPP_VSC_PHASE_CTRL 0x1d0d +#define VPP_VSC_INI_PHASE 0x1d0e +#define VPP_HSC_REGION12_STARTP 0x1d10 +#define VPP_HSC_REGION34_STARTP 0x1d11 +#define VPP_HSC_REGION4_ENDP 0x1d12 +#define VPP_HSC_START_PHASE_STEP 0x1d13 +#define VPP_HSC_REGION0_PHASE_SLOPE 0x1d14 +#define VPP_HSC_REGION1_PHASE_SLOPE 0x1d15 +#define VPP_HSC_REGION3_PHASE_SLOPE 0x1d16 +#define VPP_HSC_REGION4_PHASE_SLOPE 0x1d17 +#define VPP_HSC_PHASE_CTRL 0x1d18 +#define VPP_SC_MISC 0x1d19 +#define VPP_PREBLEND_VD1_H_START_END 0x1d1a +#define VPP_PREBLEND_VD1_V_START_END 0x1d1b +#define VPP_POSTBLEND_VD1_H_START_END 0x1d1c +#define VPP_POSTBLEND_VD1_V_START_END 0x1d1d +#define VPP_BLEND_VD2_H_START_END 0x1d1e +#define VPP_BLEND_VD2_V_START_END 0x1d1f +#define VPP_PREBLEND_H_SIZE 0x1d20 +#define VPP_POSTBLEND_H_SIZE 0x1d21 +#define VPP_HOLD_LINES 0x1d22 +#define VPP_BLEND_ONECOLOR_CTRL 0x1d23 +#define VPP_PREBLEND_CURRENT_XY 0x1d24 +#define VPP_POSTBLEND_CURRENT_XY 0x1d25 +#define VPP_MISC 0x1d26 +#define VPP_PREBLEND_ENABLE BIT(6) +#define VPP_POSTBLEND_ENABLE BIT(7) +#define VPP_OSD2_ALPHA_PREMULT BIT(8) +#define VPP_OSD1_ALPHA_PREMULT BIT(9) +#define VPP_VD1_POSTBLEND BIT(10) +#define VPP_VD2_POSTBLEND BIT(11) +#define VPP_OSD1_POSTBLEND BIT(12) +#define VPP_OSD2_POSTBLEND BIT(13) +#define VPP_VD1_PREBLEND BIT(14) +#define VPP_VD2_PREBLEND BIT(15) +#define VPP_OSD1_PREBLEND BIT(16) +#define VPP_OSD2_PREBLEND BIT(17) +#define VPP_OFIFO_SIZE 0x1d27 +#define VPP_FIFO_STATUS 0x1d28 +#define VPP_SMOKE_CTRL 0x1d29 +#define VPP_SMOKE1_VAL 0x1d2a +#define VPP_SMOKE2_VAL 0x1d2b +#define VPP_SMOKE3_VAL 0x1d2c +#define VPP_SMOKE1_H_START_END 0x1d2d +#define VPP_SMOKE1_V_START_END 0x1d2e +#define VPP_SMOKE2_H_START_END 0x1d2f +#define VPP_SMOKE2_V_START_END 0x1d30 +#define VPP_SMOKE3_H_START_END 0x1d31 +#define VPP_SMOKE3_V_START_END 0x1d32 +#define VPP_SCO_FIFO_CTRL 0x1d33 +#define VPP_HSC_PHASE_CTRL1 0x1d34 +#define VPP_HSC_INI_PAT_CTRL 0x1d35 +#define VPP_VADJ_CTRL 0x1d40 +#define VPP_VADJ1_Y 0x1d41 +#define VPP_VADJ1_MA_MB 0x1d42 +#define VPP_VADJ1_MC_MD 0x1d43 +#define VPP_VADJ2_Y 0x1d44 +#define VPP_VADJ2_MA_MB 0x1d45 +#define VPP_VADJ2_MC_MD 0x1d46 +#define VPP_HSHARP_CTRL 0x1d50 +#define VPP_HSHARP_LUMA_THRESH01 0x1d51 +#define VPP_HSHARP_LUMA_THRESH23 0x1d52 +#define VPP_HSHARP_CHROMA_THRESH01 0x1d53 +#define VPP_HSHARP_CHROMA_THRESH23 0x1d54 +#define VPP_HSHARP_LUMA_GAIN 0x1d55 +#define VPP_HSHARP_CHROMA_GAIN 0x1d56 +#define VPP_MATRIX_PROBE_COLOR 0x1d5c +#define VPP_MATRIX_HL_COLOR 0x1d5d +#define VPP_MATRIX_PROBE_POS 0x1d5e +#define VPP_MATRIX_CTRL 0x1d5f +#define VPP_MATRIX_COEF00_01 0x1d60 +#define VPP_MATRIX_COEF02_10 0x1d61 +#define VPP_MATRIX_COEF11_12 0x1d62 +#define VPP_MATRIX_COEF20_21 0x1d63 +#define VPP_MATRIX_COEF22 0x1d64 +#define VPP_MATRIX_OFFSET0_1 0x1d65 +#define VPP_MATRIX_OFFSET2 0x1d66 +#define VPP_MATRIX_PRE_OFFSET0_1 0x1d67 +#define VPP_MATRIX_PRE_OFFSET2 0x1d68 +#define VPP_DUMMY_DATA1 0x1d69 +#define VPP_GAINOFF_CTRL0 0x1d6a +#define VPP_GAINOFF_CTRL1 0x1d6b +#define VPP_GAINOFF_CTRL2 0x1d6c +#define VPP_GAINOFF_CTRL3 0x1d6d +#define VPP_GAINOFF_CTRL4 0x1d6e +#define VPP_CHROMA_ADDR_PORT 0x1d70 +#define VPP_CHROMA_DATA_PORT 0x1d71 +#define VPP_GCLK_CTRL0 0x1d72 +#define VPP_GCLK_CTRL1 0x1d73 +#define VPP_SC_GCLK_CTRL 0x1d74 +#define VPP_MISC1 0x1d76 +#define VPP_BLACKEXT_CTRL 0x1d80 +#define VPP_DNLP_CTRL_00 0x1d81 +#define VPP_DNLP_CTRL_01 0x1d82 +#define VPP_DNLP_CTRL_02 0x1d83 +#define VPP_DNLP_CTRL_03 0x1d84 +#define VPP_DNLP_CTRL_04 0x1d85 +#define VPP_DNLP_CTRL_05 0x1d86 +#define VPP_DNLP_CTRL_06 0x1d87 +#define VPP_DNLP_CTRL_07 0x1d88 +#define VPP_DNLP_CTRL_08 0x1d89 +#define VPP_DNLP_CTRL_09 0x1d8a +#define VPP_DNLP_CTRL_10 0x1d8b +#define VPP_DNLP_CTRL_11 0x1d8c +#define VPP_DNLP_CTRL_12 0x1d8d +#define VPP_DNLP_CTRL_13 0x1d8e +#define VPP_DNLP_CTRL_14 0x1d8f +#define VPP_DNLP_CTRL_15 0x1d90 +#define VPP_PEAKING_HGAIN 0x1d91 +#define VPP_PEAKING_VGAIN 0x1d92 +#define VPP_PEAKING_NLP_1 0x1d93 +#define VPP_DOLBY_CTRL 0x1d93 +#define VPP_PEAKING_NLP_2 0x1d94 +#define VPP_PEAKING_NLP_3 0x1d95 +#define VPP_PEAKING_NLP_4 0x1d96 +#define VPP_PEAKING_NLP_5 0x1d97 +#define VPP_SHARP_LIMIT 0x1d98 +#define VPP_VLTI_CTRL 0x1d99 +#define VPP_HLTI_CTRL 0x1d9a +#define VPP_CTI_CTRL 0x1d9b +#define VPP_BLUE_STRETCH_1 0x1d9c +#define VPP_BLUE_STRETCH_2 0x1d9d +#define VPP_BLUE_STRETCH_3 0x1d9e +#define VPP_CCORING_CTRL 0x1da0 +#define VPP_VE_ENABLE_CTRL 0x1da1 +#define VPP_VE_DEMO_LEFT_TOP_SCREEN_WIDTH 0x1da2 +#define VPP_VE_DEMO_CENTER_BAR 0x1da3 +#define VPP_VE_H_V_SIZE 0x1da4 +#define VPP_VDO_MEAS_CTRL 0x1da8 +#define VPP_VDO_MEAS_VS_COUNT_HI 0x1da9 +#define VPP_VDO_MEAS_VS_COUNT_LO 0x1daa +#define VPP_INPUT_CTRL 0x1dab +#define VPP_CTI_CTRL2 0x1dac +#define VPP_PEAKING_SAT_THD1 0x1dad +#define VPP_PEAKING_SAT_THD2 0x1dae +#define VPP_PEAKING_SAT_THD3 0x1daf +#define VPP_PEAKING_SAT_THD4 0x1db0 +#define VPP_PEAKING_SAT_THD5 0x1db1 +#define VPP_PEAKING_SAT_THD6 0x1db2 +#define VPP_PEAKING_SAT_THD7 0x1db3 +#define VPP_PEAKING_SAT_THD8 0x1db4 +#define VPP_PEAKING_SAT_THD9 0x1db5 +#define VPP_PEAKING_GAIN_ADD1 0x1db6 +#define VPP_PEAKING_GAIN_ADD2 0x1db7 +#define VPP_PEAKING_DNLP 0x1db8 +#define VPP_SHARP_DEMO_WIN_CTRL1 0x1db9 +#define VPP_SHARP_DEMO_WIN_CTRL2 0x1dba +#define VPP_FRONT_HLTI_CTRL 0x1dbb +#define VPP_FRONT_CTI_CTRL 0x1dbc +#define VPP_FRONT_CTI_CTRL2 0x1dbd +#define VPP_OSD_VSC_PHASE_STEP 0x1dc0 +#define VPP_OSD_VSC_INI_PHASE 0x1dc1 +#define VPP_OSD_VSC_CTRL0 0x1dc2 +#define VPP_OSD_HSC_PHASE_STEP 0x1dc3 +#define VPP_OSD_HSC_INI_PHASE 0x1dc4 +#define VPP_OSD_HSC_CTRL0 0x1dc5 +#define VPP_OSD_HSC_INI_PAT_CTRL 0x1dc6 +#define VPP_OSD_SC_DUMMY_DATA 0x1dc7 +#define VPP_OSD_SC_CTRL0 0x1dc8 +#define VPP_OSD_SCI_WH_M1 0x1dc9 +#define VPP_OSD_SCO_H_START_END 0x1dca +#define VPP_OSD_SCO_V_START_END 0x1dcb +#define VPP_OSD_SCALE_COEF_IDX 0x1dcc +#define VPP_OSD_SCALE_COEF 0x1dcd +#define VPP_INT_LINE_NUM 0x1dce + +/* viu2 */ +#define VIU2_ADDR_START 0x1e00 +#define VIU2_ADDR_END 0x1eff +#define VIU2_SW_RESET 0x1e01 +#define VIU2_OSD1_CTRL_STAT 0x1e10 +#define VIU2_OSD1_CTRL_STAT2 0x1e2d +#define VIU2_OSD1_COLOR_ADDR 0x1e11 +#define VIU2_OSD1_COLOR 0x1e12 +#define VIU2_OSD1_TCOLOR_AG0 0x1e17 +#define VIU2_OSD1_TCOLOR_AG1 0x1e18 +#define VIU2_OSD1_TCOLOR_AG2 0x1e19 +#define VIU2_OSD1_TCOLOR_AG3 0x1e1a +#define VIU2_OSD1_BLK0_CFG_W0 0x1e1b +#define VIU2_OSD1_BLK1_CFG_W0 0x1e1f +#define VIU2_OSD1_BLK2_CFG_W0 0x1e23 +#define VIU2_OSD1_BLK3_CFG_W0 0x1e27 +#define VIU2_OSD1_BLK0_CFG_W1 0x1e1c +#define VIU2_OSD1_BLK1_CFG_W1 0x1e20 +#define VIU2_OSD1_BLK2_CFG_W1 0x1e24 +#define VIU2_OSD1_BLK3_CFG_W1 0x1e28 +#define VIU2_OSD1_BLK0_CFG_W2 0x1e1d +#define VIU2_OSD1_BLK1_CFG_W2 0x1e21 +#define VIU2_OSD1_BLK2_CFG_W2 0x1e25 +#define VIU2_OSD1_BLK3_CFG_W2 0x1e29 +#define VIU2_OSD1_BLK0_CFG_W3 0x1e1e +#define VIU2_OSD1_BLK1_CFG_W3 0x1e22 +#define VIU2_OSD1_BLK2_CFG_W3 0x1e26 +#define VIU2_OSD1_BLK3_CFG_W3 0x1e2a +#define VIU2_OSD1_BLK0_CFG_W4 0x1e13 +#define VIU2_OSD1_BLK1_CFG_W4 0x1e14 +#define VIU2_OSD1_BLK2_CFG_W4 0x1e15 +#define VIU2_OSD1_BLK3_CFG_W4 0x1e16 +#define VIU2_OSD1_FIFO_CTRL_STAT 0x1e2b +#define VIU2_OSD1_TEST_RDDATA 0x1e2c +#define VIU2_OSD1_PROT_CTRL 0x1e2e +#define VIU2_OSD2_CTRL_STAT 0x1e30 +#define VIU2_OSD2_CTRL_STAT2 0x1e4d +#define VIU2_OSD2_COLOR_ADDR 0x1e31 +#define VIU2_OSD2_COLOR 0x1e32 +#define VIU2_OSD2_HL1_H_START_END 0x1e33 +#define VIU2_OSD2_HL1_V_START_END 0x1e34 +#define VIU2_OSD2_HL2_H_START_END 0x1e35 +#define VIU2_OSD2_HL2_V_START_END 0x1e36 +#define VIU2_OSD2_TCOLOR_AG0 0x1e37 +#define VIU2_OSD2_TCOLOR_AG1 0x1e38 +#define VIU2_OSD2_TCOLOR_AG2 0x1e39 +#define VIU2_OSD2_TCOLOR_AG3 0x1e3a +#define VIU2_OSD2_BLK0_CFG_W0 0x1e3b +#define VIU2_OSD2_BLK1_CFG_W0 0x1e3f +#define VIU2_OSD2_BLK2_CFG_W0 0x1e43 +#define VIU2_OSD2_BLK3_CFG_W0 0x1e47 +#define VIU2_OSD2_BLK0_CFG_W1 0x1e3c +#define VIU2_OSD2_BLK1_CFG_W1 0x1e40 +#define VIU2_OSD2_BLK2_CFG_W1 0x1e44 +#define VIU2_OSD2_BLK3_CFG_W1 0x1e48 +#define VIU2_OSD2_BLK0_CFG_W2 0x1e3d +#define VIU2_OSD2_BLK1_CFG_W2 0x1e41 +#define VIU2_OSD2_BLK2_CFG_W2 0x1e45 +#define VIU2_OSD2_BLK3_CFG_W2 0x1e49 +#define VIU2_OSD2_BLK0_CFG_W3 0x1e3e +#define VIU2_OSD2_BLK1_CFG_W3 0x1e42 +#define VIU2_OSD2_BLK2_CFG_W3 0x1e46 +#define VIU2_OSD2_BLK3_CFG_W3 0x1e4a +#define VIU2_OSD2_BLK0_CFG_W4 0x1e64 +#define VIU2_OSD2_BLK1_CFG_W4 0x1e65 +#define VIU2_OSD2_BLK2_CFG_W4 0x1e66 +#define VIU2_OSD2_BLK3_CFG_W4 0x1e67 +#define VIU2_OSD2_FIFO_CTRL_STAT 0x1e4b +#define VIU2_OSD2_TEST_RDDATA 0x1e4c +#define VIU2_OSD2_PROT_CTRL 0x1e4e +#define VIU2_VD1_IF0_GEN_REG 0x1e50 +#define VIU2_VD1_IF0_CANVAS0 0x1e51 +#define VIU2_VD1_IF0_CANVAS1 0x1e52 +#define VIU2_VD1_IF0_LUMA_X0 0x1e53 +#define VIU2_VD1_IF0_LUMA_Y0 0x1e54 +#define VIU2_VD1_IF0_CHROMA_X0 0x1e55 +#define VIU2_VD1_IF0_CHROMA_Y0 0x1e56 +#define VIU2_VD1_IF0_LUMA_X1 0x1e57 +#define VIU2_VD1_IF0_LUMA_Y1 0x1e58 +#define VIU2_VD1_IF0_CHROMA_X1 0x1e59 +#define VIU2_VD1_IF0_CHROMA_Y1 0x1e5a +#define VIU2_VD1_IF0_RPT_LOOP 0x1e5b +#define VIU2_VD1_IF0_LUMA0_RPT_PAT 0x1e5c +#define VIU2_VD1_IF0_CHROMA0_RPT_PAT 0x1e5d +#define VIU2_VD1_IF0_LUMA1_RPT_PAT 0x1e5e +#define VIU2_VD1_IF0_CHROMA1_RPT_PAT 0x1e5f +#define VIU2_VD1_IF0_LUMA_PSEL 0x1e60 +#define VIU2_VD1_IF0_CHROMA_PSEL 0x1e61 +#define VIU2_VD1_IF0_DUMMY_PIXEL 0x1e62 +#define VIU2_VD1_IF0_LUMA_FIFO_SIZE 0x1e63 +#define VIU2_VD1_IF0_RANGE_MAP_Y 0x1e6a +#define VIU2_VD1_IF0_RANGE_MAP_CB 0x1e6b +#define VIU2_VD1_IF0_RANGE_MAP_CR 0x1e6c +#define VIU2_VD1_IF0_GEN_REG2 0x1e6d +#define VIU2_VD1_IF0_PROT_CNTL 0x1e6e +#define VIU2_VD1_FMT_CTRL 0x1e68 +#define VIU2_VD1_FMT_W 0x1e69 + +/* encode */ +#define ENCP_VFIFO2VD_CTL 0x1b58 +#define ENCP_VFIFO2VD_PIXEL_START 0x1b59 +#define ENCP_VFIFO2VD_PIXEL_END 0x1b5a +#define ENCP_VFIFO2VD_LINE_TOP_START 0x1b5b +#define ENCP_VFIFO2VD_LINE_TOP_END 0x1b5c +#define ENCP_VFIFO2VD_LINE_BOT_START 0x1b5d +#define ENCP_VFIFO2VD_LINE_BOT_END 0x1b5e +#define VENC_SYNC_ROUTE 0x1b60 +#define VENC_VIDEO_EXSRC 0x1b61 +#define VENC_DVI_SETTING 0x1b62 +#define VENC_C656_CTRL 0x1b63 +#define VENC_UPSAMPLE_CTRL0 0x1b64 +#define VENC_UPSAMPLE_CTRL1 0x1b65 +#define VENC_UPSAMPLE_CTRL2 0x1b66 +#define TCON_INVERT_CTL 0x1b67 +#define VENC_VIDEO_PROG_MODE 0x1b68 +#define VENC_ENCI_LINE 0x1b69 +#define VENC_ENCI_PIXEL 0x1b6a +#define VENC_ENCP_LINE 0x1b6b +#define VENC_ENCP_PIXEL 0x1b6c +#define VENC_STATA 0x1b6d +#define VENC_INTCTRL 0x1b6e +#define VENC_INTFLAG 0x1b6f +#define VENC_VIDEO_TST_EN 0x1b70 +#define VENC_VIDEO_TST_MDSEL 0x1b71 +#define VENC_VIDEO_TST_Y 0x1b72 +#define VENC_VIDEO_TST_CB 0x1b73 +#define VENC_VIDEO_TST_CR 0x1b74 +#define VENC_VIDEO_TST_CLRBAR_STRT 0x1b75 +#define VENC_VIDEO_TST_CLRBAR_WIDTH 0x1b76 +#define VENC_VIDEO_TST_VDCNT_STSET 0x1b77 +#define VENC_VDAC_DACSEL0 0x1b78 +#define VENC_VDAC_DACSEL1 0x1b79 +#define VENC_VDAC_DACSEL2 0x1b7a +#define VENC_VDAC_DACSEL3 0x1b7b +#define VENC_VDAC_DACSEL4 0x1b7c +#define VENC_VDAC_DACSEL5 0x1b7d +#define VENC_VDAC_SETTING 0x1b7e +#define VENC_VDAC_TST_VAL 0x1b7f +#define VENC_VDAC_DAC0_GAINCTRL 0x1bf0 +#define VENC_VDAC_DAC0_OFFSET 0x1bf1 +#define VENC_VDAC_DAC1_GAINCTRL 0x1bf2 +#define VENC_VDAC_DAC1_OFFSET 0x1bf3 +#define VENC_VDAC_DAC2_GAINCTRL 0x1bf4 +#define VENC_VDAC_DAC2_OFFSET 0x1bf5 +#define VENC_VDAC_DAC3_GAINCTRL 0x1bf6 +#define VENC_VDAC_DAC3_OFFSET 0x1bf7 +#define VENC_VDAC_DAC4_GAINCTRL 0x1bf8 +#define VENC_VDAC_DAC4_OFFSET 0x1bf9 +#define VENC_VDAC_DAC5_GAINCTRL 0x1bfa +#define VENC_VDAC_DAC5_OFFSET 0x1bfb +#define VENC_VDAC_FIFO_CTRL 0x1bfc +#define ENCL_TCON_INVERT_CTL 0x1bfd +#define ENCP_VIDEO_EN 0x1b80 +#define ENCP_VIDEO_SYNC_MODE 0x1b81 +#define ENCP_MACV_EN 0x1b82 +#define ENCP_VIDEO_Y_SCL 0x1b83 +#define ENCP_VIDEO_PB_SCL 0x1b84 +#define ENCP_VIDEO_PR_SCL 0x1b85 +#define ENCP_VIDEO_SYNC_SCL 0x1b86 +#define ENCP_VIDEO_MACV_SCL 0x1b87 +#define ENCP_VIDEO_Y_OFFST 0x1b88 +#define ENCP_VIDEO_PB_OFFST 0x1b89 +#define ENCP_VIDEO_PR_OFFST 0x1b8a +#define ENCP_VIDEO_SYNC_OFFST 0x1b8b +#define ENCP_VIDEO_MACV_OFFST 0x1b8c +#define ENCP_VIDEO_MODE 0x1b8d +#define ENCP_VIDEO_MODE_ADV 0x1b8e +#define ENCP_DBG_PX_RST 0x1b90 +#define ENCP_DBG_LN_RST 0x1b91 +#define ENCP_DBG_PX_INT 0x1b92 +#define ENCP_DBG_LN_INT 0x1b93 +#define ENCP_VIDEO_YFP1_HTIME 0x1b94 +#define ENCP_VIDEO_YFP2_HTIME 0x1b95 +#define ENCP_VIDEO_YC_DLY 0x1b96 +#define ENCP_VIDEO_MAX_PXCNT 0x1b97 +#define ENCP_VIDEO_HSPULS_BEGIN 0x1b98 +#define ENCP_VIDEO_HSPULS_END 0x1b99 +#define ENCP_VIDEO_HSPULS_SWITCH 0x1b9a +#define ENCP_VIDEO_VSPULS_BEGIN 0x1b9b +#define ENCP_VIDEO_VSPULS_END 0x1b9c +#define ENCP_VIDEO_VSPULS_BLINE 0x1b9d +#define ENCP_VIDEO_VSPULS_ELINE 0x1b9e +#define ENCP_VIDEO_EQPULS_BEGIN 0x1b9f +#define ENCP_VIDEO_EQPULS_END 0x1ba0 +#define ENCP_VIDEO_EQPULS_BLINE 0x1ba1 +#define ENCP_VIDEO_EQPULS_ELINE 0x1ba2 +#define ENCP_VIDEO_HAVON_END 0x1ba3 +#define ENCP_VIDEO_HAVON_BEGIN 0x1ba4 +#define ENCP_VIDEO_VAVON_ELINE 0x1baf +#define ENCP_VIDEO_VAVON_BLINE 0x1ba6 +#define ENCP_VIDEO_HSO_BEGIN 0x1ba7 +#define ENCP_VIDEO_HSO_END 0x1ba8 +#define ENCP_VIDEO_VSO_BEGIN 0x1ba9 +#define ENCP_VIDEO_VSO_END 0x1baa +#define ENCP_VIDEO_VSO_BLINE 0x1bab +#define ENCP_VIDEO_VSO_ELINE 0x1bac +#define ENCP_VIDEO_SYNC_WAVE_CURVE 0x1bad +#define ENCP_VIDEO_MAX_LNCNT 0x1bae +#define ENCP_VIDEO_SY_VAL 0x1bb0 +#define ENCP_VIDEO_SY2_VAL 0x1bb1 +#define ENCP_VIDEO_BLANKY_VAL 0x1bb2 +#define ENCP_VIDEO_BLANKPB_VAL 0x1bb3 +#define ENCP_VIDEO_BLANKPR_VAL 0x1bb4 +#define ENCP_VIDEO_HOFFST 0x1bb5 +#define ENCP_VIDEO_VOFFST 0x1bb6 +#define ENCP_VIDEO_RGB_CTRL 0x1bb7 +#define ENCP_VIDEO_FILT_CTRL 0x1bb8 +#define ENCP_VIDEO_OFLD_VPEQ_OFST 0x1bb9 +#define ENCP_VIDEO_OFLD_VOAV_OFST 0x1bba +#define ENCP_VIDEO_MATRIX_CB 0x1bbb +#define ENCP_VIDEO_MATRIX_CR 0x1bbc +#define ENCP_VIDEO_RGBIN_CTRL 0x1bbd +#define ENCP_MACV_BLANKY_VAL 0x1bc0 +#define ENCP_MACV_MAXY_VAL 0x1bc1 +#define ENCP_MACV_1ST_PSSYNC_STRT 0x1bc2 +#define ENCP_MACV_PSSYNC_STRT 0x1bc3 +#define ENCP_MACV_AGC_STRT 0x1bc4 +#define ENCP_MACV_AGC_END 0x1bc5 +#define ENCP_MACV_WAVE_END 0x1bc6 +#define ENCP_MACV_STRTLINE 0x1bc7 +#define ENCP_MACV_ENDLINE 0x1bc8 +#define ENCP_MACV_TS_CNT_MAX_L 0x1bc9 +#define ENCP_MACV_TS_CNT_MAX_H 0x1bca +#define ENCP_MACV_TIME_DOWN 0x1bcb +#define ENCP_MACV_TIME_LO 0x1bcc +#define ENCP_MACV_TIME_UP 0x1bcd +#define ENCP_MACV_TIME_RST 0x1bce +#define ENCP_VBI_CTRL 0x1bd0 +#define ENCP_VBI_SETTING 0x1bd1 +#define ENCP_VBI_BEGIN 0x1bd2 +#define ENCP_VBI_WIDTH 0x1bd3 +#define ENCP_VBI_HVAL 0x1bd4 +#define ENCP_VBI_DATA0 0x1bd5 +#define ENCP_VBI_DATA1 0x1bd6 +#define C656_HS_ST 0x1be0 +#define C656_HS_ED 0x1be1 +#define C656_VS_LNST_E 0x1be2 +#define C656_VS_LNST_O 0x1be3 +#define C656_VS_LNED_E 0x1be4 +#define C656_VS_LNED_O 0x1be5 +#define C656_FS_LNST 0x1be6 +#define C656_FS_LNED 0x1be7 +#define ENCI_VIDEO_MODE 0x1b00 +#define ENCI_VIDEO_MODE_ADV 0x1b01 +#define ENCI_VIDEO_FSC_ADJ 0x1b02 +#define ENCI_VIDEO_BRIGHT 0x1b03 +#define ENCI_VIDEO_CONT 0x1b04 +#define ENCI_VIDEO_SAT 0x1b05 +#define ENCI_VIDEO_HUE 0x1b06 +#define ENCI_VIDEO_SCH 0x1b07 +#define ENCI_SYNC_MODE 0x1b08 +#define ENCI_SYNC_CTRL 0x1b09 +#define ENCI_SYNC_HSO_BEGIN 0x1b0a +#define ENCI_SYNC_HSO_END 0x1b0b +#define ENCI_SYNC_VSO_EVN 0x1b0c +#define ENCI_SYNC_VSO_ODD 0x1b0d +#define ENCI_SYNC_VSO_EVNLN 0x1b0e +#define ENCI_SYNC_VSO_ODDLN 0x1b0f +#define ENCI_SYNC_HOFFST 0x1b10 +#define ENCI_SYNC_VOFFST 0x1b11 +#define ENCI_SYNC_ADJ 0x1b12 +#define ENCI_RGB_SETTING 0x1b13 +#define ENCI_DE_H_BEGIN 0x1b16 +#define ENCI_DE_H_END 0x1b17 +#define ENCI_DE_V_BEGIN_EVEN 0x1b18 +#define ENCI_DE_V_END_EVEN 0x1b19 +#define ENCI_DE_V_BEGIN_ODD 0x1b1a +#define ENCI_DE_V_END_ODD 0x1b1b +#define ENCI_VBI_SETTING 0x1b20 +#define ENCI_VBI_CCDT_EVN 0x1b21 +#define ENCI_VBI_CCDT_ODD 0x1b22 +#define ENCI_VBI_CC525_LN 0x1b23 +#define ENCI_VBI_CC625_LN 0x1b24 +#define ENCI_VBI_WSSDT 0x1b25 +#define ENCI_VBI_WSS_LN 0x1b26 +#define ENCI_VBI_CGMSDT_L 0x1b27 +#define ENCI_VBI_CGMSDT_H 0x1b28 +#define ENCI_VBI_CGMS_LN 0x1b29 +#define ENCI_VBI_TTX_HTIME 0x1b2a +#define ENCI_VBI_TTX_LN 0x1b2b +#define ENCI_VBI_TTXDT0 0x1b2c +#define ENCI_VBI_TTXDT1 0x1b2d +#define ENCI_VBI_TTXDT2 0x1b2e +#define ENCI_VBI_TTXDT3 0x1b2f +#define ENCI_MACV_N0 0x1b30 +#define ENCI_MACV_N1 0x1b31 +#define ENCI_MACV_N2 0x1b32 +#define ENCI_MACV_N3 0x1b33 +#define ENCI_MACV_N4 0x1b34 +#define ENCI_MACV_N5 0x1b35 +#define ENCI_MACV_N6 0x1b36 +#define ENCI_MACV_N7 0x1b37 +#define ENCI_MACV_N8 0x1b38 +#define ENCI_MACV_N9 0x1b39 +#define ENCI_MACV_N10 0x1b3a +#define ENCI_MACV_N11 0x1b3b +#define ENCI_MACV_N12 0x1b3c +#define ENCI_MACV_N13 0x1b3d +#define ENCI_MACV_N14 0x1b3e +#define ENCI_MACV_N15 0x1b3f +#define ENCI_MACV_N16 0x1b40 +#define ENCI_MACV_N17 0x1b41 +#define ENCI_MACV_N18 0x1b42 +#define ENCI_MACV_N19 0x1b43 +#define ENCI_MACV_N20 0x1b44 +#define ENCI_MACV_N21 0x1b45 +#define ENCI_MACV_N22 0x1b46 +#define ENCI_DBG_PX_RST 0x1b48 +#define ENCI_DBG_FLDLN_RST 0x1b49 +#define ENCI_DBG_PX_INT 0x1b4a +#define ENCI_DBG_FLDLN_INT 0x1b4b +#define ENCI_DBG_MAXPX 0x1b4c +#define ENCI_DBG_MAXLN 0x1b4d +#define ENCI_MACV_MAX_AMP 0x1b50 +#define ENCI_MACV_PULSE_LO 0x1b51 +#define ENCI_MACV_PULSE_HI 0x1b52 +#define ENCI_MACV_BKP_MAX 0x1b53 +#define ENCI_CFILT_CTRL 0x1b54 +#define ENCI_CFILT7 0x1b55 +#define ENCI_YC_DELAY 0x1b56 +#define ENCI_VIDEO_EN 0x1b57 +#define ENCI_DVI_HSO_BEGIN 0x1c00 +#define ENCI_DVI_HSO_END 0x1c01 +#define ENCI_DVI_VSO_BLINE_EVN 0x1c02 +#define ENCI_DVI_VSO_BLINE_ODD 0x1c03 +#define ENCI_DVI_VSO_ELINE_EVN 0x1c04 +#define ENCI_DVI_VSO_ELINE_ODD 0x1c05 +#define ENCI_DVI_VSO_BEGIN_EVN 0x1c06 +#define ENCI_DVI_VSO_BEGIN_ODD 0x1c07 +#define ENCI_DVI_VSO_END_EVN 0x1c08 +#define ENCI_DVI_VSO_END_ODD 0x1c09 +#define ENCI_CFILT_CTRL2 0x1c0a +#define ENCI_DACSEL_0 0x1c0b +#define ENCI_DACSEL_1 0x1c0c +#define ENCP_DACSEL_0 0x1c0d +#define ENCP_DACSEL_1 0x1c0e +#define ENCP_MAX_LINE_SWITCH_POINT 0x1c0f +#define ENCI_TST_EN 0x1c10 +#define ENCI_TST_MDSEL 0x1c11 +#define ENCI_TST_Y 0x1c12 +#define ENCI_TST_CB 0x1c13 +#define ENCI_TST_CR 0x1c14 +#define ENCI_TST_CLRBAR_STRT 0x1c15 +#define ENCI_TST_CLRBAR_WIDTH 0x1c16 +#define ENCI_TST_VDCNT_STSET 0x1c17 +#define ENCI_VFIFO2VD_CTL 0x1c18 +#define ENCI_VFIFO2VD_PIXEL_START 0x1c19 +#define ENCI_VFIFO2VD_PIXEL_END 0x1c1a +#define ENCI_VFIFO2VD_LINE_TOP_START 0x1c1b +#define ENCI_VFIFO2VD_LINE_TOP_END 0x1c1c +#define ENCI_VFIFO2VD_LINE_BOT_START 0x1c1d +#define ENCI_VFIFO2VD_LINE_BOT_END 0x1c1e +#define ENCI_VFIFO2VD_CTL2 0x1c1f +#define ENCT_VFIFO2VD_CTL 0x1c20 +#define ENCT_VFIFO2VD_PIXEL_START 0x1c21 +#define ENCT_VFIFO2VD_PIXEL_END 0x1c22 +#define ENCT_VFIFO2VD_LINE_TOP_START 0x1c23 +#define ENCT_VFIFO2VD_LINE_TOP_END 0x1c24 +#define ENCT_VFIFO2VD_LINE_BOT_START 0x1c25 +#define ENCT_VFIFO2VD_LINE_BOT_END 0x1c26 +#define ENCT_VFIFO2VD_CTL2 0x1c27 +#define ENCT_TST_EN 0x1c28 +#define ENCT_TST_MDSEL 0x1c29 +#define ENCT_TST_Y 0x1c2a +#define ENCT_TST_CB 0x1c2b +#define ENCT_TST_CR 0x1c2c +#define ENCT_TST_CLRBAR_STRT 0x1c2d +#define ENCT_TST_CLRBAR_WIDTH 0x1c2e +#define ENCT_TST_VDCNT_STSET 0x1c2f +#define ENCP_DVI_HSO_BEGIN 0x1c30 +#define ENCP_DVI_HSO_END 0x1c31 +#define ENCP_DVI_VSO_BLINE_EVN 0x1c32 +#define ENCP_DVI_VSO_BLINE_ODD 0x1c33 +#define ENCP_DVI_VSO_ELINE_EVN 0x1c34 +#define ENCP_DVI_VSO_ELINE_ODD 0x1c35 +#define ENCP_DVI_VSO_BEGIN_EVN 0x1c36 +#define ENCP_DVI_VSO_BEGIN_ODD 0x1c37 +#define ENCP_DVI_VSO_END_EVN 0x1c38 +#define ENCP_DVI_VSO_END_ODD 0x1c39 +#define ENCP_DE_H_BEGIN 0x1c3a +#define ENCP_DE_H_END 0x1c3b +#define ENCP_DE_V_BEGIN_EVEN 0x1c3c +#define ENCP_DE_V_END_EVEN 0x1c3d +#define ENCP_DE_V_BEGIN_ODD 0x1c3e +#define ENCP_DE_V_END_ODD 0x1c3f +#define ENCI_SYNC_LINE_LENGTH 0x1c40 +#define ENCI_SYNC_PIXEL_EN 0x1c41 +#define ENCI_SYNC_TO_LINE_EN 0x1c42 +#define ENCI_SYNC_TO_PIXEL 0x1c43 +#define ENCP_SYNC_LINE_LENGTH 0x1c44 +#define ENCP_SYNC_PIXEL_EN 0x1c45 +#define ENCP_SYNC_TO_LINE_EN 0x1c46 +#define ENCP_SYNC_TO_PIXEL 0x1c47 +#define ENCT_SYNC_LINE_LENGTH 0x1c48 +#define ENCT_SYNC_PIXEL_EN 0x1c49 +#define ENCT_SYNC_TO_LINE_EN 0x1c4a +#define ENCT_SYNC_TO_PIXEL 0x1c4b +#define ENCL_SYNC_LINE_LENGTH 0x1c4c +#define ENCL_SYNC_PIXEL_EN 0x1c4d +#define ENCL_SYNC_TO_LINE_EN 0x1c4e +#define ENCL_SYNC_TO_PIXEL 0x1c4f +#define ENCP_VFIFO2VD_CTL2 0x1c50 +#define VENC_DVI_SETTING_MORE 0x1c51 +#define VENC_VDAC_DAC4_FILT_CTRL0 0x1c54 +#define VENC_VDAC_DAC4_FILT_CTRL1 0x1c55 +#define VENC_VDAC_DAC5_FILT_CTRL0 0x1c56 +#define VENC_VDAC_DAC5_FILT_CTRL1 0x1c57 +#define VENC_VDAC_DAC0_FILT_CTRL0 0x1c58 +#define VENC_VDAC_DAC0_FILT_CTRL1 0x1c59 +#define VENC_VDAC_DAC1_FILT_CTRL0 0x1c5a +#define VENC_VDAC_DAC1_FILT_CTRL1 0x1c5b +#define VENC_VDAC_DAC2_FILT_CTRL0 0x1c5c +#define VENC_VDAC_DAC2_FILT_CTRL1 0x1c5d +#define VENC_VDAC_DAC3_FILT_CTRL0 0x1c5e +#define VENC_VDAC_DAC3_FILT_CTRL1 0x1c5f +#define ENCT_VIDEO_EN 0x1c60 +#define ENCT_VIDEO_Y_SCL 0x1c61 +#define ENCT_VIDEO_PB_SCL 0x1c62 +#define ENCT_VIDEO_PR_SCL 0x1c63 +#define ENCT_VIDEO_Y_OFFST 0x1c64 +#define ENCT_VIDEO_PB_OFFST 0x1c65 +#define ENCT_VIDEO_PR_OFFST 0x1c66 +#define ENCT_VIDEO_MODE 0x1c67 +#define ENCT_VIDEO_MODE_ADV 0x1c68 +#define ENCT_DBG_PX_RST 0x1c69 +#define ENCT_DBG_LN_RST 0x1c6a +#define ENCT_DBG_PX_INT 0x1c6b +#define ENCT_DBG_LN_INT 0x1c6c +#define ENCT_VIDEO_YFP1_HTIME 0x1c6d +#define ENCT_VIDEO_YFP2_HTIME 0x1c6e +#define ENCT_VIDEO_YC_DLY 0x1c6f +#define ENCT_VIDEO_MAX_PXCNT 0x1c70 +#define ENCT_VIDEO_HAVON_END 0x1c71 +#define ENCT_VIDEO_HAVON_BEGIN 0x1c72 +#define ENCT_VIDEO_VAVON_ELINE 0x1c73 +#define ENCT_VIDEO_VAVON_BLINE 0x1c74 +#define ENCT_VIDEO_HSO_BEGIN 0x1c75 +#define ENCT_VIDEO_HSO_END 0x1c76 +#define ENCT_VIDEO_VSO_BEGIN 0x1c77 +#define ENCT_VIDEO_VSO_END 0x1c78 +#define ENCT_VIDEO_VSO_BLINE 0x1c79 +#define ENCT_VIDEO_VSO_ELINE 0x1c7a +#define ENCT_VIDEO_MAX_LNCNT 0x1c7b +#define ENCT_VIDEO_BLANKY_VAL 0x1c7c +#define ENCT_VIDEO_BLANKPB_VAL 0x1c7d +#define ENCT_VIDEO_BLANKPR_VAL 0x1c7e +#define ENCT_VIDEO_HOFFST 0x1c7f +#define ENCT_VIDEO_VOFFST 0x1c80 +#define ENCT_VIDEO_RGB_CTRL 0x1c81 +#define ENCT_VIDEO_FILT_CTRL 0x1c82 +#define ENCT_VIDEO_OFLD_VPEQ_OFST 0x1c83 +#define ENCT_VIDEO_OFLD_VOAV_OFST 0x1c84 +#define ENCT_VIDEO_MATRIX_CB 0x1c85 +#define ENCT_VIDEO_MATRIX_CR 0x1c86 +#define ENCT_VIDEO_RGBIN_CTRL 0x1c87 +#define ENCT_MAX_LINE_SWITCH_POINT 0x1c88 +#define ENCT_DACSEL_0 0x1c89 +#define ENCT_DACSEL_1 0x1c8a +#define ENCL_VFIFO2VD_CTL 0x1c90 +#define ENCL_VFIFO2VD_PIXEL_START 0x1c91 +#define ENCL_VFIFO2VD_PIXEL_END 0x1c92 +#define ENCL_VFIFO2VD_LINE_TOP_START 0x1c93 +#define ENCL_VFIFO2VD_LINE_TOP_END 0x1c94 +#define ENCL_VFIFO2VD_LINE_BOT_START 0x1c95 +#define ENCL_VFIFO2VD_LINE_BOT_END 0x1c96 +#define ENCL_VFIFO2VD_CTL2 0x1c97 +#define ENCL_TST_EN 0x1c98 +#define ENCL_TST_MDSEL 0x1c99 +#define ENCL_TST_Y 0x1c9a +#define ENCL_TST_CB 0x1c9b +#define ENCL_TST_CR 0x1c9c +#define ENCL_TST_CLRBAR_STRT 0x1c9d +#define ENCL_TST_CLRBAR_WIDTH 0x1c9e +#define ENCL_TST_VDCNT_STSET 0x1c9f +#define ENCL_VIDEO_EN 0x1ca0 +#define ENCL_VIDEO_Y_SCL 0x1ca1 +#define ENCL_VIDEO_PB_SCL 0x1ca2 +#define ENCL_VIDEO_PR_SCL 0x1ca3 +#define ENCL_VIDEO_Y_OFFST 0x1ca4 +#define ENCL_VIDEO_PB_OFFST 0x1ca5 +#define ENCL_VIDEO_PR_OFFST 0x1ca6 +#define ENCL_VIDEO_MODE 0x1ca7 +#define ENCL_VIDEO_MODE_ADV 0x1ca8 +#define ENCL_DBG_PX_RST 0x1ca9 +#define ENCL_DBG_LN_RST 0x1caa +#define ENCL_DBG_PX_INT 0x1cab +#define ENCL_DBG_LN_INT 0x1cac +#define ENCL_VIDEO_YFP1_HTIME 0x1cad +#define ENCL_VIDEO_YFP2_HTIME 0x1cae +#define ENCL_VIDEO_YC_DLY 0x1caf +#define ENCL_VIDEO_MAX_PXCNT 0x1cb0 +#define ENCL_VIDEO_HAVON_END 0x1cb1 +#define ENCL_VIDEO_HAVON_BEGIN 0x1cb2 +#define ENCL_VIDEO_VAVON_ELINE 0x1cb3 +#define ENCL_VIDEO_VAVON_BLINE 0x1cb4 +#define ENCL_VIDEO_HSO_BEGIN 0x1cb5 +#define ENCL_VIDEO_HSO_END 0x1cb6 +#define ENCL_VIDEO_VSO_BEGIN 0x1cb7 +#define ENCL_VIDEO_VSO_END 0x1cb8 +#define ENCL_VIDEO_VSO_BLINE 0x1cb9 +#define ENCL_VIDEO_VSO_ELINE 0x1cba +#define ENCL_VIDEO_MAX_LNCNT 0x1cbb +#define ENCL_VIDEO_BLANKY_VAL 0x1cbc +#define ENCL_VIDEO_BLANKPB_VAL 0x1cbd +#define ENCL_VIDEO_BLANKPR_VAL 0x1cbe +#define ENCL_VIDEO_HOFFST 0x1cbf +#define ENCL_VIDEO_VOFFST 0x1cc0 +#define ENCL_VIDEO_RGB_CTRL 0x1cc1 +#define ENCL_VIDEO_FILT_CTRL 0x1cc2 +#define ENCL_VIDEO_OFLD_VPEQ_OFST 0x1cc3 +#define ENCL_VIDEO_OFLD_VOAV_OFST 0x1cc4 +#define ENCL_VIDEO_MATRIX_CB 0x1cc5 +#define ENCL_VIDEO_MATRIX_CR 0x1cc6 +#define ENCL_VIDEO_RGBIN_CTRL 0x1cc7 +#define ENCL_MAX_LINE_SWITCH_POINT 0x1cc8 +#define ENCL_DACSEL_0 0x1cc9 +#define ENCL_DACSEL_1 0x1cca +#define RDMA_AHB_START_ADDR_MAN 0x1100 +#define RDMA_AHB_END_ADDR_MAN 0x1101 +#define RDMA_AHB_START_ADDR_1 0x1102 +#define RDMA_AHB_END_ADDR_1 0x1103 +#define RDMA_AHB_START_ADDR_2 0x1104 +#define RDMA_AHB_END_ADDR_2 0x1105 +#define RDMA_AHB_START_ADDR_3 0x1106 +#define RDMA_AHB_END_ADDR_3 0x1107 +#define RDMA_AHB_START_ADDR_4 0x1108 +#define RDMA_AHB_END_ADDR_4 0x1109 +#define RDMA_AHB_START_ADDR_5 0x110a +#define RDMA_AHB_END_ADDR_5 0x110b +#define RDMA_AHB_START_ADDR_6 0x110c +#define RDMA_AHB_END_ADDR_6 0x110d +#define RDMA_AHB_START_ADDR_7 0x110e +#define RDMA_AHB_END_ADDR_7 0x110f +#define RDMA_ACCESS_AUTO 0x1110 +#define RDMA_ACCESS_AUTO2 0x1111 +#define RDMA_ACCESS_AUTO3 0x1112 +#define RDMA_ACCESS_MAN 0x1113 +#define RDMA_CTRL 0x1114 +#define RDMA_STATUS 0x1115 +#define RDMA_STATUS2 0x1116 +#define RDMA_STATUS3 0x1117 +#define L_GAMMA_CNTL_PORT 0x1400 +#define L_GAMMA_DATA_PORT 0x1401 +#define L_GAMMA_ADDR_PORT 0x1402 +#define L_GAMMA_VCOM_HSWITCH_ADDR 0x1403 +#define L_RGB_BASE_ADDR 0x1405 +#define L_RGB_COEFF_ADDR 0x1406 +#define L_POL_CNTL_ADDR 0x1407 +#define L_DITH_CNTL_ADDR 0x1408 +#define L_GAMMA_PROBE_CTRL 0x1409 +#define L_GAMMA_PROBE_COLOR_L 0x140a +#define L_GAMMA_PROBE_COLOR_H 0x140b +#define L_GAMMA_PROBE_HL_COLOR 0x140c +#define L_GAMMA_PROBE_POS_X 0x140d +#define L_GAMMA_PROBE_POS_Y 0x140e +#define L_STH1_HS_ADDR 0x1410 +#define L_STH1_HE_ADDR 0x1411 +#define L_STH1_VS_ADDR 0x1412 +#define L_STH1_VE_ADDR 0x1413 +#define L_STH2_HS_ADDR 0x1414 +#define L_STH2_HE_ADDR 0x1415 +#define L_STH2_VS_ADDR 0x1416 +#define L_STH2_VE_ADDR 0x1417 +#define L_OEH_HS_ADDR 0x1418 +#define L_OEH_HE_ADDR 0x1419 +#define L_OEH_VS_ADDR 0x141a +#define L_OEH_VE_ADDR 0x141b +#define L_VCOM_HSWITCH_ADDR 0x141c +#define L_VCOM_VS_ADDR 0x141d +#define L_VCOM_VE_ADDR 0x141e +#define L_CPV1_HS_ADDR 0x141f +#define L_CPV1_HE_ADDR 0x1420 +#define L_CPV1_VS_ADDR 0x1421 +#define L_CPV1_VE_ADDR 0x1422 +#define L_CPV2_HS_ADDR 0x1423 +#define L_CPV2_HE_ADDR 0x1424 +#define L_CPV2_VS_ADDR 0x1425 +#define L_CPV2_VE_ADDR 0x1426 +#define L_STV1_HS_ADDR 0x1427 +#define L_STV1_HE_ADDR 0x1428 +#define L_STV1_VS_ADDR 0x1429 +#define L_STV1_VE_ADDR 0x142a +#define L_STV2_HS_ADDR 0x142b +#define L_STV2_HE_ADDR 0x142c +#define L_STV2_VS_ADDR 0x142d +#define L_STV2_VE_ADDR 0x142e +#define L_OEV1_HS_ADDR 0x142f +#define L_OEV1_HE_ADDR 0x1430 +#define L_OEV1_VS_ADDR 0x1431 +#define L_OEV1_VE_ADDR 0x1432 +#define L_OEV2_HS_ADDR 0x1433 +#define L_OEV2_HE_ADDR 0x1434 +#define L_OEV2_VS_ADDR 0x1435 +#define L_OEV2_VE_ADDR 0x1436 +#define L_OEV3_HS_ADDR 0x1437 +#define L_OEV3_HE_ADDR 0x1438 +#define L_OEV3_VS_ADDR 0x1439 +#define L_OEV3_VE_ADDR 0x143a +#define L_LCD_PWR_ADDR 0x143b +#define L_LCD_PWM0_LO_ADDR 0x143c +#define L_LCD_PWM0_HI_ADDR 0x143d +#define L_LCD_PWM1_LO_ADDR 0x143e +#define L_LCD_PWM1_HI_ADDR 0x143f +#define L_INV_CNT_ADDR 0x1440 +#define L_TCON_MISC_SEL_ADDR 0x1441 +#define L_DUAL_PORT_CNTL_ADDR 0x1442 +#define MLVDS_CLK_CTL1_HI 0x1443 +#define MLVDS_CLK_CTL1_LO 0x1444 +#define L_TCON_DOUBLE_CTL 0x1449 +#define L_TCON_PATTERN_HI 0x144a +#define L_TCON_PATTERN_LO 0x144b +#define LDIM_BL_ADDR_PORT 0x144e +#define LDIM_BL_DATA_PORT 0x144f +#define L_DE_HS_ADDR 0x1451 +#define L_DE_HE_ADDR 0x1452 +#define L_DE_VS_ADDR 0x1453 +#define L_DE_VE_ADDR 0x1454 +#define L_HSYNC_HS_ADDR 0x1455 +#define L_HSYNC_HE_ADDR 0x1456 +#define L_HSYNC_VS_ADDR 0x1457 +#define L_HSYNC_VE_ADDR 0x1458 +#define L_VSYNC_HS_ADDR 0x1459 +#define L_VSYNC_HE_ADDR 0x145a +#define L_VSYNC_VS_ADDR 0x145b +#define L_VSYNC_VE_ADDR 0x145c +#define L_LCD_MCU_CTL 0x145d +#define DUAL_MLVDS_CTL 0x1460 +#define DUAL_MLVDS_LINE_START 0x1461 +#define DUAL_MLVDS_LINE_END 0x1462 +#define DUAL_MLVDS_PIXEL_W_START_L 0x1463 +#define DUAL_MLVDS_PIXEL_W_END_L 0x1464 +#define DUAL_MLVDS_PIXEL_W_START_R 0x1465 +#define DUAL_MLVDS_PIXEL_W_END_R 0x1466 +#define DUAL_MLVDS_PIXEL_R_START_L 0x1467 +#define DUAL_MLVDS_PIXEL_R_CNT_L 0x1468 +#define DUAL_MLVDS_PIXEL_R_START_R 0x1469 +#define DUAL_MLVDS_PIXEL_R_CNT_R 0x146a +#define V_INVERSION_PIXEL 0x1470 +#define V_INVERSION_LINE 0x1471 +#define V_INVERSION_CONTROL 0x1472 +#define MLVDS2_CONTROL 0x1474 +#define MLVDS2_CONFIG_HI 0x1475 +#define MLVDS2_CONFIG_LO 0x1476 +#define MLVDS2_DUAL_GATE_WR_START 0x1477 +#define MLVDS2_DUAL_GATE_WR_END 0x1478 +#define MLVDS2_DUAL_GATE_RD_START 0x1479 +#define MLVDS2_DUAL_GATE_RD_END 0x147a +#define MLVDS2_SECOND_RESET_CTL 0x147b +#define MLVDS2_DUAL_GATE_CTL_HI 0x147c +#define MLVDS2_DUAL_GATE_CTL_LO 0x147d +#define MLVDS2_RESET_CONFIG_HI 0x147e +#define MLVDS2_RESET_CONFIG_LO 0x147f +#define GAMMA_CNTL_PORT 0x1480 +#define GAMMA_DATA_PORT 0x1481 +#define GAMMA_ADDR_PORT 0x1482 +#define GAMMA_VCOM_HSWITCH_ADDR 0x1483 +#define RGB_BASE_ADDR 0x1485 +#define RGB_COEFF_ADDR 0x1486 +#define POL_CNTL_ADDR 0x1487 +#define DITH_CNTL_ADDR 0x1488 +#define GAMMA_PROBE_CTRL 0x1489 +#define GAMMA_PROBE_COLOR_L 0x148a +#define GAMMA_PROBE_COLOR_H 0x148b +#define GAMMA_PROBE_HL_COLOR 0x148c +#define GAMMA_PROBE_POS_X 0x148d +#define GAMMA_PROBE_POS_Y 0x148e +#define STH1_HS_ADDR 0x1490 +#define STH1_HE_ADDR 0x1491 +#define STH1_VS_ADDR 0x1492 +#define STH1_VE_ADDR 0x1493 +#define STH2_HS_ADDR 0x1494 +#define STH2_HE_ADDR 0x1495 +#define STH2_VS_ADDR 0x1496 +#define STH2_VE_ADDR 0x1497 +#define OEH_HS_ADDR 0x1498 +#define OEH_HE_ADDR 0x1499 +#define OEH_VS_ADDR 0x149a +#define OEH_VE_ADDR 0x149b +#define VCOM_HSWITCH_ADDR 0x149c +#define VCOM_VS_ADDR 0x149d +#define VCOM_VE_ADDR 0x149e +#define CPV1_HS_ADDR 0x149f +#define CPV1_HE_ADDR 0x14a0 +#define CPV1_VS_ADDR 0x14a1 +#define CPV1_VE_ADDR 0x14a2 +#define CPV2_HS_ADDR 0x14a3 +#define CPV2_HE_ADDR 0x14a4 +#define CPV2_VS_ADDR 0x14a5 +#define CPV2_VE_ADDR 0x14a6 +#define STV1_HS_ADDR 0x14a7 +#define STV1_HE_ADDR 0x14a8 +#define STV1_VS_ADDR 0x14a9 +#define STV1_VE_ADDR 0x14aa +#define STV2_HS_ADDR 0x14ab +#define STV2_HE_ADDR 0x14ac +#define STV2_VS_ADDR 0x14ad +#define STV2_VE_ADDR 0x14ae +#define OEV1_HS_ADDR 0x14af +#define OEV1_HE_ADDR 0x14b0 +#define OEV1_VS_ADDR 0x14b1 +#define OEV1_VE_ADDR 0x14b2 +#define OEV2_HS_ADDR 0x14b3 +#define OEV2_HE_ADDR 0x14b4 +#define OEV2_VS_ADDR 0x14b5 +#define OEV2_VE_ADDR 0x14b6 +#define OEV3_HS_ADDR 0x14b7 +#define OEV3_HE_ADDR 0x14b8 +#define OEV3_VS_ADDR 0x14b9 +#define OEV3_VE_ADDR 0x14ba +#define LCD_PWR_ADDR 0x14bb +#define LCD_PWM0_LO_ADDR 0x14bc +#define LCD_PWM0_HI_ADDR 0x14bd +#define LCD_PWM1_LO_ADDR 0x14be +#define LCD_PWM1_HI_ADDR 0x14bf +#define INV_CNT_ADDR 0x14c0 +#define TCON_MISC_SEL_ADDR 0x14c1 +#define DUAL_PORT_CNTL_ADDR 0x14c2 +#define MLVDS_CONTROL 0x14c3 +#define MLVDS_RESET_PATTERN_HI 0x14c4 +#define MLVDS_RESET_PATTERN_LO 0x14c5 +#define MLVDS_RESET_PATTERN_EXT 0x14c6 +#define MLVDS_CONFIG_HI 0x14c7 +#define MLVDS_CONFIG_LO 0x14c8 +#define TCON_DOUBLE_CTL 0x14c9 +#define TCON_PATTERN_HI 0x14ca +#define TCON_PATTERN_LO 0x14cb +#define TCON_CONTROL_HI 0x14cc +#define TCON_CONTROL_LO 0x14cd +#define LVDS_BLANK_DATA_HI 0x14ce +#define LVDS_BLANK_DATA_LO 0x14cf +#define LVDS_PACK_CNTL_ADDR 0x14d0 +#define DE_HS_ADDR 0x14d1 +#define DE_HE_ADDR 0x14d2 +#define DE_VS_ADDR 0x14d3 +#define DE_VE_ADDR 0x14d4 +#define HSYNC_HS_ADDR 0x14d5 +#define HSYNC_HE_ADDR 0x14d6 +#define HSYNC_VS_ADDR 0x14d7 +#define HSYNC_VE_ADDR 0x14d8 +#define VSYNC_HS_ADDR 0x14d9 +#define VSYNC_HE_ADDR 0x14da +#define VSYNC_VS_ADDR 0x14db +#define VSYNC_VE_ADDR 0x14dc +#define LCD_MCU_CTL 0x14dd +#define LCD_MCU_DATA_0 0x14de +#define LCD_MCU_DATA_1 0x14df +#define LVDS_GEN_CNTL 0x14e0 +#define LVDS_PHY_CNTL0 0x14e1 +#define LVDS_PHY_CNTL1 0x14e2 +#define LVDS_PHY_CNTL2 0x14e3 +#define LVDS_PHY_CNTL3 0x14e4 +#define LVDS_PHY_CNTL4 0x14e5 +#define LVDS_PHY_CNTL5 0x14e6 +#define LVDS_SRG_TEST 0x14e8 +#define LVDS_BIST_MUX0 0x14e9 +#define LVDS_BIST_MUX1 0x14ea +#define LVDS_BIST_FIXED0 0x14eb +#define LVDS_BIST_FIXED1 0x14ec +#define LVDS_BIST_CNTL0 0x14ed +#define LVDS_CLKB_CLKA 0x14ee +#define LVDS_PHY_CLK_CNTL 0x14ef +#define LVDS_SER_EN 0x14f0 +#define LVDS_PHY_CNTL6 0x14f1 +#define LVDS_PHY_CNTL7 0x14f2 +#define LVDS_PHY_CNTL8 0x14f3 +#define MLVDS_CLK_CTL0_HI 0x14f4 +#define MLVDS_CLK_CTL0_LO 0x14f5 +#define MLVDS_DUAL_GATE_WR_START 0x14f6 +#define MLVDS_DUAL_GATE_WR_END 0x14f7 +#define MLVDS_DUAL_GATE_RD_START 0x14f8 +#define MLVDS_DUAL_GATE_RD_END 0x14f9 +#define MLVDS_SECOND_RESET_CTL 0x14fa +#define MLVDS_DUAL_GATE_CTL_HI 0x14fb +#define MLVDS_DUAL_GATE_CTL_LO 0x14fc +#define MLVDS_RESET_CONFIG_HI 0x14fd +#define MLVDS_RESET_CONFIG_LO 0x14fe +#define VPU_OSD1_MMC_CTRL 0x2701 +#define VPU_OSD2_MMC_CTRL 0x2702 +#define VPU_VD1_MMC_CTRL 0x2703 +#define VPU_VD2_MMC_CTRL 0x2704 +#define VPU_DI_IF1_MMC_CTRL 0x2705 +#define VPU_DI_MEM_MMC_CTRL 0x2706 +#define VPU_DI_INP_MMC_CTRL 0x2707 +#define VPU_DI_MTNRD_MMC_CTRL 0x2708 +#define VPU_DI_CHAN2_MMC_CTRL 0x2709 +#define VPU_DI_MTNWR_MMC_CTRL 0x270a +#define VPU_DI_NRWR_MMC_CTRL 0x270b +#define VPU_DI_DIWR_MMC_CTRL 0x270c +#define VPU_VDIN0_MMC_CTRL 0x270d +#define VPU_VDIN1_MMC_CTRL 0x270e +#define VPU_BT656_MMC_CTRL 0x270f +#define VPU_TVD3D_MMC_CTRL 0x2710 +#define VPU_TVDVBI_MMC_CTRL 0x2711 +#define VPU_TVDVBI_VSLATCH_ADDR 0x2712 +#define VPU_TVDVBI_WRRSP_ADDR 0x2713 +#define VPU_VDIN_PRE_ARB_CTRL 0x2714 +#define VPU_VDISP_PRE_ARB_CTRL 0x2715 +#define VPU_VPUARB2_PRE_ARB_CTRL 0x2716 +#define VPU_OSD3_MMC_CTRL 0x2717 +#define VPU_OSD4_MMC_CTRL 0x2718 +#define VPU_VD3_MMC_CTRL 0x2719 +#define VPU_VIU_VENC_MUX_CTRL 0x271a +#define VIU1_SEL_VENC_MASK 0x3 +#define VIU1_SEL_VENC_ENCL 0 +#define VIU1_SEL_VENC_ENCI 1 +#define VIU1_SEL_VENC_ENCP 2 +#define VIU1_SEL_VENC_ENCT 3 +#define VIU2_SEL_VENC_MASK 0xc +#define VIU2_SEL_VENC_ENCL 0 +#define VIU2_SEL_VENC_ENCI (1 << 2) +#define VIU2_SEL_VENC_ENCP (2 << 2) +#define VIU2_SEL_VENC_ENCT (3 << 2) +#define VPU_HDMI_SETTING 0x271b +#define ENCI_INFO_READ 0x271c +#define ENCP_INFO_READ 0x271d +#define ENCT_INFO_READ 0x271e +#define ENCL_INFO_READ 0x271f +#define VPU_SW_RESET 0x2720 +#define VPU_D2D3_MMC_CTRL 0x2721 +#define VPU_CONT_MMC_CTRL 0x2722 +#define VPU_CLK_GATE 0x2723 +#define VPU_RDMA_MMC_CTRL 0x2724 +#define VPU_MEM_PD_REG0 0x2725 +#define VPU_MEM_PD_REG1 0x2726 +#define VPU_HDMI_DATA_OVR 0x2727 +#define VPU_PROT1_MMC_CTRL 0x2728 +#define VPU_PROT2_MMC_CTRL 0x2729 +#define VPU_PROT3_MMC_CTRL 0x272a +#define VPU_ARB4_V1_MMC_CTRL 0x272b +#define VPU_ARB4_V2_MMC_CTRL 0x272c +#define VPU_VPU_PWM_V0 0x2730 +#define VPU_VPU_PWM_V1 0x2731 +#define VPU_VPU_PWM_V2 0x2732 +#define VPU_VPU_PWM_V3 0x2733 +#define VPU_VPU_PWM_H0 0x2734 +#define VPU_VPU_PWM_H1 0x2735 +#define VPU_VPU_PWM_H2 0x2736 +#define VPU_VPU_PWM_H3 0x2737 +#define VPU_MISC_CTRL 0x2740 +#define VPU_ISP_GCLK_CTRL0 0x2741 +#define VPU_ISP_GCLK_CTRL1 0x2742 +#define VPU_VDIN_ASYNC_HOLD_CTRL 0x2743 +#define VPU_VDISP_ASYNC_HOLD_CTRL 0x2744 +#define VPU_VPUARB2_ASYNC_HOLD_CTRL 0x2745 + +#define VPU_PROT1_CLK_GATE 0x2750 +#define VPU_PROT1_GEN_CNTL 0x2751 +#define VPU_PROT1_X_START_END 0x2752 +#define VPU_PROT1_Y_START_END 0x2753 +#define VPU_PROT1_Y_LEN_STEP 0x2754 +#define VPU_PROT1_RPT_LOOP 0x2755 +#define VPU_PROT1_RPT_PAT 0x2756 +#define VPU_PROT1_DDR 0x2757 +#define VPU_PROT1_RBUF_ROOM 0x2758 +#define VPU_PROT1_STAT_0 0x2759 +#define VPU_PROT1_STAT_1 0x275a +#define VPU_PROT1_STAT_2 0x275b +#define VPU_PROT1_REQ_ONOFF 0x275c +#define VPU_PROT2_CLK_GATE 0x2760 +#define VPU_PROT2_GEN_CNTL 0x2761 +#define VPU_PROT2_X_START_END 0x2762 +#define VPU_PROT2_Y_START_END 0x2763 +#define VPU_PROT2_Y_LEN_STEP 0x2764 +#define VPU_PROT2_RPT_LOOP 0x2765 +#define VPU_PROT2_RPT_PAT 0x2766 +#define VPU_PROT2_DDR 0x2767 +#define VPU_PROT2_RBUF_ROOM 0x2768 +#define VPU_PROT2_STAT_0 0x2769 +#define VPU_PROT2_STAT_1 0x276a +#define VPU_PROT2_STAT_2 0x276b +#define VPU_PROT2_REQ_ONOFF 0x276c +#define VPU_PROT3_CLK_GATE 0x2770 +#define VPU_PROT3_GEN_CNTL 0x2771 +#define VPU_PROT3_X_START_END 0x2772 +#define VPU_PROT3_Y_START_END 0x2773 +#define VPU_PROT3_Y_LEN_STEP 0x2774 +#define VPU_PROT3_RPT_LOOP 0x2775 +#define VPU_PROT3_RPT_PAT 0x2776 +#define VPU_PROT3_DDR 0x2777 +#define VPU_PROT3_RBUF_ROOM 0x2778 +#define VPU_PROT3_STAT_0 0x2779 +#define VPU_PROT3_STAT_1 0x277a +#define VPU_PROT3_STAT_2 0x277b +#define VPU_PROT3_REQ_ONOFF 0x277c + +/* osd super scale */ +#define OSDSR_HV_SIZEIN 0x3130 +#define OSDSR_CTRL_MODE 0x3131 +#define OSDSR_ABIC_HCOEF 0x3132 +#define OSDSR_YBIC_HCOEF 0x3133 +#define OSDSR_CBIC_HCOEF 0x3134 +#define OSDSR_ABIC_VCOEF 0x3135 +#define OSDSR_YBIC_VCOEF 0x3136 +#define OSDSR_CBIC_VCOEF 0x3137 +#define OSDSR_VAR_PARA 0x3138 +#define OSDSR_CONST_PARA 0x3139 +#define OSDSR_RKE_EXTWIN 0x313a +#define OSDSR_UK_GRAD2DDIAG_TH_RATE 0x313b +#define OSDSR_UK_GRAD2DDIAG_LIMIT 0x313c +#define OSDSR_UK_GRAD2DADJA_TH_RATE 0x313d +#define OSDSR_UK_GRAD2DADJA_LIMIT 0x313e +#define OSDSR_UK_BST_GAIN 0x313f +#define OSDSR_HVBLEND_TH 0x3140 +#define OSDSR_DEMO_WIND_TB 0x3141 +#define OSDSR_DEMO_WIND_LR 0x3142 +#define OSDSR_INT_BLANK_NUM 0x3143 +#define OSDSR_FRM_END_STAT 0x3144 +#define OSDSR_ABIC_HCOEF0 0x3145 +#define OSDSR_YBIC_HCOEF0 0x3146 +#define OSDSR_CBIC_HCOEF0 0x3147 +#define OSDSR_ABIC_VCOEF0 0x3148 +#define OSDSR_YBIC_VCOEF0 0x3149 +#define OSDSR_CBIC_VCOEF0 0x314a + +#endif /* __MESON_REGISTERS_H */ diff --git a/drivers/gpu/drm/meson/meson_vclk.c b/drivers/gpu/drm/meson/meson_vclk.c new file mode 100644 index 000000000000..252cfd4b19b1 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vclk.c @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_vclk.h" + +/* + * VCLK is the "Pixel Clock" frequency generator from a dedicated PLL. + * We handle the following encodings : + * - CVBS 27MHz generator via the VCLK2 to the VENCI and VDAC blocks + * + * What is missing : + * - HDMI Pixel Clocks generation + */ + +/* HHI Registers */ +#define HHI_VID_PLL_CLK_DIV 0x1a0 /* 0x68 offset in data sheet */ +#define VID_PLL_EN BIT(19) +#define VID_PLL_BYPASS BIT(18) +#define VID_PLL_PRESET BIT(15) +#define HHI_VIID_CLK_DIV 0x128 /* 0x4a offset in data sheet */ +#define VCLK2_DIV_MASK 0xff +#define VCLK2_DIV_EN BIT(16) +#define VCLK2_DIV_RESET BIT(17) +#define CTS_VDAC_SEL_MASK (0xf << 28) +#define CTS_VDAC_SEL_SHIFT 28 +#define HHI_VIID_CLK_CNTL 0x12c /* 0x4b offset in data sheet */ +#define VCLK2_EN BIT(19) +#define VCLK2_SEL_MASK (0x7 << 16) +#define VCLK2_SEL_SHIFT 16 +#define VCLK2_SOFT_RESET BIT(15) +#define VCLK2_DIV1_EN BIT(0) +#define HHI_VID_CLK_DIV 0x164 /* 0x59 offset in data sheet */ +#define CTS_ENCI_SEL_MASK (0xf << 28) +#define CTS_ENCI_SEL_SHIFT 28 +#define HHI_VID_CLK_CNTL2 0x194 /* 0x65 offset in data sheet */ +#define CTS_ENCI_EN BIT(0) +#define CTS_VDAC_EN BIT(4) + +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ + +#define HHI_HDMI_PLL_CNTL 0x320 /* 0xc8 offset in data sheet */ +#define HHI_HDMI_PLL_CNTL2 0x324 /* 0xc9 offset in data sheet */ +#define HHI_HDMI_PLL_CNTL3 0x328 /* 0xca offset in data sheet */ +#define HHI_HDMI_PLL_CNTL4 0x32C /* 0xcb offset in data sheet */ +#define HHI_HDMI_PLL_CNTL5 0x330 /* 0xcc offset in data sheet */ +#define HHI_HDMI_PLL_CNTL6 0x334 /* 0xcd offset in data sheet */ + +#define HDMI_PLL_RESET BIT(28) +#define HDMI_PLL_LOCK BIT(31) + +/* + * Setup VCLK2 for 27MHz, and enable clocks for ENCI and VDAC + * + * TOFIX: Refactor into table to also handle HDMI frequency and paths + */ +static void meson_venci_cvbs_clock_config(struct meson_drm *priv) +{ + unsigned int val; + + /* Setup PLL to output 1.485GHz */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x5800023d); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x00404e00); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0x0d5c5091); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x801da72c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x71486980); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x00000e55); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4800023d); + } else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) { + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL, 0x4000027b); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL2, 0x800cb300); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL3, 0xa6212844); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL4, 0x0c4d000c); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL5, 0x001fa729); + regmap_write(priv->hhi, HHI_HDMI_PLL_CNTL6, 0x01a31500); + + /* Reset PLL */ + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, HDMI_PLL_RESET); + regmap_update_bits(priv->hhi, HHI_HDMI_PLL_CNTL, + HDMI_PLL_RESET, 0); + } + + /* Poll for lock bit */ + regmap_read_poll_timeout(priv->hhi, HHI_HDMI_PLL_CNTL, val, + (val & HDMI_PLL_LOCK), 10, 0); + + /* Disable VCLK2 */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, 0); + + /* Disable vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_EN, 0); + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, VID_PLL_PRESET, 0); + /* Enable vid_pll bypass to HDMI pll */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_BYPASS, VID_PLL_BYPASS); + /* Enable the vid_pll output clock */ + regmap_update_bits(priv->hhi, HHI_VID_PLL_CLK_DIV, + VID_PLL_EN, VID_PLL_EN); + + /* Setup the VCLK2 divider value to achieve 27MHz */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, + VCLK2_DIV_MASK, (55 - 1)); + + /* select vid_pll for vclk2 */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SEL_MASK, (4 << VCLK2_SEL_SHIFT)); + /* enable vclk2 gate */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, VCLK2_EN, VCLK2_EN); + + /* select vclk_div1 for enci */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_DIV, + CTS_ENCI_SEL_MASK, (8 << CTS_ENCI_SEL_SHIFT)); + /* select vclk_div1 for vdac */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, + CTS_VDAC_SEL_MASK, (8 << CTS_VDAC_SEL_SHIFT)); + + /* release vclk2_div_reset and enable vclk2_div */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_DIV, + VCLK2_DIV_EN | VCLK2_DIV_RESET, VCLK2_DIV_EN); + + /* enable vclk2_div1 gate */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_DIV1_EN, VCLK2_DIV1_EN); + + /* reset vclk2 */ + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SOFT_RESET, VCLK2_SOFT_RESET); + regmap_update_bits(priv->hhi, HHI_VIID_CLK_CNTL, + VCLK2_SOFT_RESET, 0); + + /* enable enci_clk */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_ENCI_EN, CTS_ENCI_EN); + /* enable vdac_clk */ + regmap_update_bits(priv->hhi, HHI_VID_CLK_CNTL2, + CTS_VDAC_EN, CTS_VDAC_EN); +} + +void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int freq) +{ + if (target == MESON_VCLK_TARGET_CVBS && freq == MESON_VCLK_CVBS) + meson_venci_cvbs_clock_config(priv); +} diff --git a/drivers/gpu/drm/meson/meson_vclk.h b/drivers/gpu/drm/meson/meson_vclk.h new file mode 100644 index 000000000000..ec62735996de --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vclk.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Video Clock */ + +#ifndef __MESON_VCLK_H +#define __MESON_VCLK_H + +enum { + MESON_VCLK_TARGET_CVBS = 0, +}; + +/* 27MHz is the CVBS Pixel Clock */ +#define MESON_VCLK_CVBS 27000 + +void meson_vclk_setup(struct meson_drm *priv, unsigned int target, + unsigned int freq); + +#endif /* __MESON_VCLK_H */ diff --git a/drivers/gpu/drm/meson/meson_venc.c b/drivers/gpu/drm/meson/meson_venc.c new file mode 100644 index 000000000000..d836b2274531 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_venc.h" +#include "meson_vpp.h" +#include "meson_vclk.h" +#include "meson_registers.h" + +/* + * VENC Handle the pixels encoding to the output formats. + * We handle the following encodings : + * - CVBS Encoding via the ENCI encoder and VDAC digital to analog converter + * + * What is missing : + * - TMDS/HDMI Encoding via ENCI_DIV and ENCP + * - Setup of more clock rates for HDMI modes + * - LCD Panel encoding via ENCL + * - TV Panel encoding via ENCT + */ + +struct meson_cvbs_enci_mode meson_cvbs_enci_pal = { + .mode_tag = MESON_VENC_MODE_CVBS_PAL, + .hso_begin = 3, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 7, + .video_prog_mode = 0xff, + .video_mode = 0x13, + .sch_adjust = 0x28, + .yc_delay = 0x343, + .pixel_start = 251, + .pixel_end = 1691, + .top_field_line_start = 22, + .top_field_line_end = 310, + .bottom_field_line_start = 23, + .bottom_field_line_end = 311, + .video_saturation = 9, + .video_contrast = 0, + .video_brightness = 0, + .video_hue = 0, + .analog_sync_adj = 0x8080, +}; + +struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc = { + .mode_tag = MESON_VENC_MODE_CVBS_NTSC, + .hso_begin = 5, + .hso_end = 129, + .vso_even = 3, + .vso_odd = 260, + .macv_max_amp = 0xb, + .video_prog_mode = 0xf0, + .video_mode = 0x8, + .sch_adjust = 0x20, + .yc_delay = 0x333, + .pixel_start = 227, + .pixel_end = 1667, + .top_field_line_start = 18, + .top_field_line_end = 258, + .bottom_field_line_start = 19, + .bottom_field_line_end = 259, + .video_saturation = 18, + .video_contrast = 3, + .video_brightness = 0, + .video_hue = 0, + .analog_sync_adj = 0x9c00, +}; + +void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode) +{ + if (mode->mode_tag == priv->venc.current_mode) + return; + + /* CVBS Filter settings */ + writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL)); + writel_relaxed(0x12, priv->io_base + _REG(ENCI_CFILT_CTRL2)); + + /* Digital Video Select : Interlace, clk27 clk, external */ + writel_relaxed(0, priv->io_base + _REG(VENC_DVI_SETTING)); + + /* Reset Video Mode */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE)); + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + /* Horizontal sync signal output */ + writel_relaxed(mode->hso_begin, + priv->io_base + _REG(ENCI_SYNC_HSO_BEGIN)); + writel_relaxed(mode->hso_end, + priv->io_base + _REG(ENCI_SYNC_HSO_END)); + + /* Vertical Sync lines */ + writel_relaxed(mode->vso_even, + priv->io_base + _REG(ENCI_SYNC_VSO_EVNLN)); + writel_relaxed(mode->vso_odd, + priv->io_base + _REG(ENCI_SYNC_VSO_ODDLN)); + + /* Macrovision max amplitude change */ + writel_relaxed(0x8100 + mode->macv_max_amp, + priv->io_base + _REG(ENCI_MACV_MAX_AMP)); + + /* Video mode */ + writel_relaxed(mode->video_prog_mode, + priv->io_base + _REG(VENC_VIDEO_PROG_MODE)); + writel_relaxed(mode->video_mode, + priv->io_base + _REG(ENCI_VIDEO_MODE)); + + /* Advanced Video Mode : + * Demux shifting 0x2 + * Blank line end at line17/22 + * High bandwidth Luma Filter + * Low bandwidth Chroma Filter + * Bypass luma low pass filter + * No macrovision on CSYNC + */ + writel_relaxed(0x26, priv->io_base + _REG(ENCI_VIDEO_MODE_ADV)); + + writel(mode->sch_adjust, priv->io_base + _REG(ENCI_VIDEO_SCH)); + + /* Sync mode : MASTER Master mode, free run, send HSO/VSO out */ + writel_relaxed(0x07, priv->io_base + _REG(ENCI_SYNC_MODE)); + + /* 0x3 Y, C, and Component Y delay */ + writel_relaxed(mode->yc_delay, priv->io_base + _REG(ENCI_YC_DELAY)); + + /* Timings */ + writel_relaxed(mode->pixel_start, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_START)); + writel_relaxed(mode->pixel_end, + priv->io_base + _REG(ENCI_VFIFO2VD_PIXEL_END)); + + writel_relaxed(mode->top_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_START)); + writel_relaxed(mode->top_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_TOP_END)); + + writel_relaxed(mode->bottom_field_line_start, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_START)); + writel_relaxed(mode->bottom_field_line_end, + priv->io_base + _REG(ENCI_VFIFO2VD_LINE_BOT_END)); + + /* Internal Venc, Internal VIU Sync, Internal Vencoder */ + writel_relaxed(0, priv->io_base + _REG(VENC_SYNC_ROUTE)); + + /* UNreset Interlaced TV Encoder */ + writel_relaxed(0, priv->io_base + _REG(ENCI_DBG_PX_RST)); + + /* Enable Vfifo2vd, Y_Cb_Y_Cr select */ + writel_relaxed(0x4e01, priv->io_base + _REG(ENCI_VFIFO2VD_CTL)); + + /* Power UP Dacs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_SETTING)); + + /* Video Upsampling */ + writel_relaxed(0x0061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL0)); + writel_relaxed(0x4061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL1)); + writel_relaxed(0x5061, priv->io_base + _REG(VENC_UPSAMPLE_CTRL2)); + + /* Select Interlace Y DACs */ + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL0)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL1)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL2)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL3)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL4)); + writel_relaxed(0, priv->io_base + _REG(VENC_VDAC_DACSEL5)); + + /* Select ENCI for VIU */ + meson_vpp_setup_mux(priv, MESON_VIU_VPP_MUX_ENCI); + + /* Enable ENCI FIFO */ + writel_relaxed(0x2000, priv->io_base + _REG(VENC_VDAC_FIFO_CTRL)); + + /* Select ENCI DACs 0, 1, 4, and 5 */ + writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_0)); + writel_relaxed(0x11, priv->io_base + _REG(ENCI_DACSEL_1)); + + /* Interlace video enable */ + writel_relaxed(1, priv->io_base + _REG(ENCI_VIDEO_EN)); + + /* Configure Video Saturation / Contrast / Brightness / Hue */ + writel_relaxed(mode->video_saturation, + priv->io_base + _REG(ENCI_VIDEO_SAT)); + writel_relaxed(mode->video_contrast, + priv->io_base + _REG(ENCI_VIDEO_CONT)); + writel_relaxed(mode->video_brightness, + priv->io_base + _REG(ENCI_VIDEO_BRIGHT)); + writel_relaxed(mode->video_hue, + priv->io_base + _REG(ENCI_VIDEO_HUE)); + + /* Enable DAC0 Filter */ + writel_relaxed(0x1, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL0)); + writel_relaxed(0xfc48, priv->io_base + _REG(VENC_VDAC_DAC0_FILT_CTRL1)); + + /* 0 in Macrovision register 0 */ + writel_relaxed(0, priv->io_base + _REG(ENCI_MACV_N0)); + + /* Analog Synchronization and color burst value adjust */ + writel_relaxed(mode->analog_sync_adj, + priv->io_base + _REG(ENCI_SYNC_ADJ)); + + /* Setup 27MHz vclk2 for ENCI and VDAC */ + meson_vclk_setup(priv, MESON_VCLK_TARGET_CVBS, MESON_VCLK_CVBS); + + priv->venc.current_mode = mode->mode_tag; +} + +/* Returns the current ENCI field polarity */ +unsigned int meson_venci_get_field(struct meson_drm *priv) +{ + return readl_relaxed(priv->io_base + _REG(ENCI_INFO_READ)) & BIT(29); +} + +void meson_venc_enable_vsync(struct meson_drm *priv) +{ + writel_relaxed(2, priv->io_base + _REG(VENC_INTCTRL)); +} + +void meson_venc_disable_vsync(struct meson_drm *priv) +{ + writel_relaxed(0, priv->io_base + _REG(VENC_INTCTRL)); +} + +void meson_venc_init(struct meson_drm *priv) +{ + /* Disable all encoders */ + writel_relaxed(0, priv->io_base + _REG(ENCI_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCP_VIDEO_EN)); + writel_relaxed(0, priv->io_base + _REG(ENCL_VIDEO_EN)); + + /* Disable VSync IRQ */ + meson_venc_disable_vsync(priv); + + priv->venc.current_mode = MESON_VENC_MODE_NONE; +} diff --git a/drivers/gpu/drm/meson/meson_venc.h b/drivers/gpu/drm/meson/meson_venc.h new file mode 100644 index 000000000000..77d4a7d82c44 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Video Encoders + * - ENCI : Interlace Video Encoder + * - ENCI_DVI : Interlace Video Encoder for DVI/HDMI + * - ENCP : Progressive Video Encoder + */ + +#ifndef __MESON_VENC_H +#define __MESON_VENC_H + +enum { + MESON_VENC_MODE_NONE = 0, + MESON_VENC_MODE_CVBS_PAL, + MESON_VENC_MODE_CVBS_NTSC, +}; + +struct meson_cvbs_enci_mode { + unsigned int mode_tag; + unsigned int hso_begin; /* HSO begin position */ + unsigned int hso_end; /* HSO end position */ + unsigned int vso_even; /* VSO even line */ + unsigned int vso_odd; /* VSO odd line */ + unsigned int macv_max_amp; /* Macrovision max amplitude */ + unsigned int video_prog_mode; + unsigned int video_mode; + unsigned int sch_adjust; + unsigned int yc_delay; + unsigned int pixel_start; + unsigned int pixel_end; + unsigned int top_field_line_start; + unsigned int top_field_line_end; + unsigned int bottom_field_line_start; + unsigned int bottom_field_line_end; + unsigned int video_saturation; + unsigned int video_contrast; + unsigned int video_brightness; + unsigned int video_hue; + unsigned int analog_sync_adj; +}; + +/* CVBS Timings and Parameters */ +extern struct meson_cvbs_enci_mode meson_cvbs_enci_pal; +extern struct meson_cvbs_enci_mode meson_cvbs_enci_ntsc; + +void meson_venci_cvbs_mode_set(struct meson_drm *priv, + struct meson_cvbs_enci_mode *mode); +unsigned int meson_venci_get_field(struct meson_drm *priv); + +void meson_venc_enable_vsync(struct meson_drm *priv); +void meson_venc_disable_vsync(struct meson_drm *priv); + +void meson_venc_init(struct meson_drm *priv); + +#endif /* __MESON_VENC_H */ diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.c b/drivers/gpu/drm/meson/meson_venc_cvbs.c new file mode 100644 index 000000000000..c809c085fd78 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.c @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_graph.h> + +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_atomic_helper.h> + +#include "meson_venc_cvbs.h" +#include "meson_venc.h" +#include "meson_registers.h" + +/* HHI VDAC Registers */ +#define HHI_VDAC_CNTL0 0x2F4 /* 0xbd offset in data sheet */ +#define HHI_VDAC_CNTL1 0x2F8 /* 0xbe offset in data sheet */ + +struct meson_venc_cvbs { + struct drm_encoder encoder; + struct drm_connector connector; + struct meson_drm *priv; +}; +#define encoder_to_meson_venc_cvbs(x) \ + container_of(x, struct meson_venc_cvbs, encoder) + +#define connector_to_meson_venc_cvbs(x) \ + container_of(x, struct meson_venc_cvbs, connector) + +/* Supported Modes */ + +struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT] = { + { /* PAL */ + .enci = &meson_cvbs_enci_pal, + .mode = { + DRM_MODE("720x576i", DRM_MODE_TYPE_DRIVER, 13500, + 720, 732, 795, 864, 0, 576, 580, 586, 625, 0, + DRM_MODE_FLAG_INTERLACE), + .vrefresh = 50, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, + }, + }, + { /* NTSC */ + .enci = &meson_cvbs_enci_ntsc, + .mode = { + DRM_MODE("720x480i", DRM_MODE_TYPE_DRIVER, 13500, + 720, 739, 801, 858, 0, 480, 488, 494, 525, 0, + DRM_MODE_FLAG_INTERLACE), + .vrefresh = 60, + .picture_aspect_ratio = HDMI_PICTURE_ASPECT_4_3, + }, + }, +}; + +/* Connector */ + +static void meson_cvbs_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static enum drm_connector_status +meson_cvbs_connector_detect(struct drm_connector *connector, bool force) +{ + /* FIXME: Add load-detect or jack-detect if possible */ + return connector_status_connected; +} + +static int meson_cvbs_connector_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct drm_display_mode *mode; + int i; + + for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { + struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + + mode = drm_mode_duplicate(dev, &meson_mode->mode); + if (!mode) { + DRM_ERROR("Failed to create a new display mode\n"); + return 0; + } + + drm_mode_probed_add(connector, mode); + } + + return i; +} + +static int meson_cvbs_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + /* Validate the modes added in get_modes */ + return MODE_OK; +} + +static const struct drm_connector_funcs meson_cvbs_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = meson_cvbs_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = meson_cvbs_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const +struct drm_connector_helper_funcs meson_cvbs_connector_helper_funcs = { + .get_modes = meson_cvbs_connector_get_modes, + .mode_valid = meson_cvbs_connector_mode_valid, +}; + +/* Encoder */ + +static void meson_venc_cvbs_encoder_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); +} + +static const struct drm_encoder_funcs meson_venc_cvbs_encoder_funcs = { + .destroy = meson_venc_cvbs_encoder_destroy, +}; + +static int meson_venc_cvbs_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + int i; + + for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { + struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + + if (drm_mode_equal(&crtc_state->mode, &meson_mode->mode)) + return 0; + } + + return -EINVAL; +} + +static void meson_venc_cvbs_encoder_disable(struct drm_encoder *encoder) +{ + struct meson_venc_cvbs *meson_venc_cvbs = + encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; + + /* Disable CVBS VDAC */ + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0); + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); +} + +static void meson_venc_cvbs_encoder_enable(struct drm_encoder *encoder) +{ + struct meson_venc_cvbs *meson_venc_cvbs = + encoder_to_meson_venc_cvbs(encoder); + struct meson_drm *priv = meson_venc_cvbs->priv; + + /* VDAC0 source is not from ATV */ + writel_bits_relaxed(BIT(5), 0, priv->io_base + _REG(VENC_VDAC_DACSEL0)); + + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxbb-vpu")) + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 1); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + regmap_write(priv->hhi, HHI_VDAC_CNTL0, 0xf0001); + + regmap_write(priv->hhi, HHI_VDAC_CNTL1, 0); +} + +static void meson_venc_cvbs_encoder_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct meson_venc_cvbs *meson_venc_cvbs = + encoder_to_meson_venc_cvbs(encoder); + int i; + + for (i = 0; i < MESON_CVBS_MODES_COUNT; ++i) { + struct meson_cvbs_mode *meson_mode = &meson_cvbs_modes[i]; + + if (drm_mode_equal(mode, &meson_mode->mode)) { + meson_venci_cvbs_mode_set(meson_venc_cvbs->priv, + meson_mode->enci); + break; + } + } +} + +static const struct drm_encoder_helper_funcs + meson_venc_cvbs_encoder_helper_funcs = { + .atomic_check = meson_venc_cvbs_encoder_atomic_check, + .disable = meson_venc_cvbs_encoder_disable, + .enable = meson_venc_cvbs_encoder_enable, + .mode_set = meson_venc_cvbs_encoder_mode_set, +}; + +static bool meson_venc_cvbs_connector_is_available(struct meson_drm *priv) +{ + struct device_node *ep, *remote; + + /* CVBS VDAC output is on the first port, first endpoint */ + ep = of_graph_get_endpoint_by_regs(priv->dev->of_node, 0, 0); + if (!ep) + return false; + + + /* If the endpoint node exists, consider it enabled */ + remote = of_graph_get_remote_port(ep); + if (remote) { + of_node_put(ep); + return true; + } + + of_node_put(ep); + of_node_put(remote); + + return false; +} + +int meson_venc_cvbs_create(struct meson_drm *priv) +{ + struct drm_device *drm = priv->drm; + struct meson_venc_cvbs *meson_venc_cvbs; + struct drm_connector *connector; + struct drm_encoder *encoder; + int ret; + + if (!meson_venc_cvbs_connector_is_available(priv)) { + dev_info(drm->dev, "CVBS Output connector not available\n"); + return -ENODEV; + } + + meson_venc_cvbs = devm_kzalloc(priv->dev, sizeof(*meson_venc_cvbs), + GFP_KERNEL); + if (!meson_venc_cvbs) + return -ENOMEM; + + meson_venc_cvbs->priv = priv; + encoder = &meson_venc_cvbs->encoder; + connector = &meson_venc_cvbs->connector; + + /* Connector */ + + drm_connector_helper_add(connector, + &meson_cvbs_connector_helper_funcs); + + ret = drm_connector_init(drm, connector, &meson_cvbs_connector_funcs, + DRM_MODE_CONNECTOR_Composite); + if (ret) { + dev_err(priv->dev, "Failed to init CVBS connector\n"); + return ret; + } + + connector->interlace_allowed = 1; + + /* Encoder */ + + drm_encoder_helper_add(encoder, &meson_venc_cvbs_encoder_helper_funcs); + + ret = drm_encoder_init(drm, encoder, &meson_venc_cvbs_encoder_funcs, + DRM_MODE_ENCODER_TVDAC, "meson_venc_cvbs"); + if (ret) { + dev_err(priv->dev, "Failed to init CVBS encoder\n"); + return ret; + } + + encoder->possible_crtcs = BIT(0); + + drm_mode_connector_attach_encoder(connector, encoder); + + return 0; +} diff --git a/drivers/gpu/drm/meson/meson_venc_cvbs.h b/drivers/gpu/drm/meson/meson_venc_cvbs.h new file mode 100644 index 000000000000..9256ccf9d931 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_venc_cvbs.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + * + * Written by: + * Jasper St. Pierre <jstpierre@mecheye.net> + */ + +#ifndef __MESON_VENC_CVBS_H +#define __MESON_VENC_CVBS_H + +#include "meson_drv.h" +#include "meson_venc.h" + +struct meson_cvbs_mode { + struct meson_cvbs_enci_mode *enci; + struct drm_display_mode mode; +}; + +#define MESON_CVBS_MODES_COUNT 2 + +/* Modes supported by the CVBS output */ +extern struct meson_cvbs_mode meson_cvbs_modes[MESON_CVBS_MODES_COUNT]; + +int meson_venc_cvbs_create(struct meson_drm *priv); + +#endif /* __MESON_VENC_CVBS_H */ diff --git a/drivers/gpu/drm/meson/meson_viu.c b/drivers/gpu/drm/meson/meson_viu.c new file mode 100644 index 000000000000..a6de8ba7af19 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_viu.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_viu.h" +#include "meson_vpp.h" +#include "meson_venc.h" +#include "meson_canvas.h" +#include "meson_registers.h" + +/* + * VIU Handles the Pixel scanout and the basic Colorspace conversions + * We handle the following features : + * - OSD1 RGB565/RGB888/xRGB8888 scanout + * - RGB conversion to x/cb/cr + * - Progressive or Interlace buffer scanout + * - OSD1 Commit on Vsync + * - HDR OSD matrix for GXL/GXM + * + * What is missing : + * - BGR888/xBGR8888/BGRx8888/BGRx8888 modes + * - YUV4:2:2 Y0CbY1Cr scanout + * - Conversion to YUV 4:4:4 from 4:2:2 input + * - Colorkey Alpha matching + * - Big endian scanout + * - X/Y reverse scanout + * - Global alpha setup + * - OSD2 support, would need interlace switching on vsync + * - OSD1 full scaling to support TV overscan + */ + +/* OSD csc defines */ + +enum viu_matrix_sel_e { + VIU_MATRIX_OSD_EOTF = 0, + VIU_MATRIX_OSD, +}; + +enum viu_lut_sel_e { + VIU_LUT_OSD_EOTF = 0, + VIU_LUT_OSD_OETF, +}; + +#define COEFF_NORM(a) ((int)((((a) * 2048.0) + 1) / 2)) +#define MATRIX_5X3_COEF_SIZE 24 + +#define EOTF_COEFF_NORM(a) ((int)((((a) * 4096.0) + 1) / 2)) +#define EOTF_COEFF_SIZE 10 +#define EOTF_COEFF_RIGHTSHIFT 1 + +static int RGB709_to_YUV709l_coeff[MATRIX_5X3_COEF_SIZE] = { + 0, 0, 0, /* pre offset */ + COEFF_NORM(0.181873), COEFF_NORM(0.611831), COEFF_NORM(0.061765), + COEFF_NORM(-0.100251), COEFF_NORM(-0.337249), COEFF_NORM(0.437500), + COEFF_NORM(0.437500), COEFF_NORM(-0.397384), COEFF_NORM(-0.040116), + 0, 0, 0, /* 10'/11'/12' */ + 0, 0, 0, /* 20'/21'/22' */ + 64, 512, 512, /* offset */ + 0, 0, 0 /* mode, right_shift, clip_en */ +}; + +/* eotf matrix: bypass */ +static int eotf_bypass_coeff[EOTF_COEFF_SIZE] = { + EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), + EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), EOTF_COEFF_NORM(0.0), + EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(0.0), EOTF_COEFF_NORM(1.0), + EOTF_COEFF_RIGHTSHIFT /* right shift */ +}; + +void meson_viu_set_osd_matrix(struct meson_drm *priv, + enum viu_matrix_sel_e m_select, + int *m, bool csc_on) +{ + if (m_select == VIU_MATRIX_OSD) { + /* osd matrix, VIU_MATRIX_0 */ + writel(((m[0] & 0xfff) << 16) | (m[1] & 0xfff), + priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET0_1)); + writel(m[2] & 0xfff, + priv->io_base + _REG(VIU_OSD1_MATRIX_PRE_OFFSET2)); + writel(((m[3] & 0x1fff) << 16) | (m[4] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF00_01)); + writel(((m[5] & 0x1fff) << 16) | (m[6] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF02_10)); + writel(((m[7] & 0x1fff) << 16) | (m[8] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF11_12)); + writel(((m[9] & 0x1fff) << 16) | (m[10] & 0x1fff), + priv->io_base + _REG(VIU_OSD1_MATRIX_COEF20_21)); + + if (m[21]) { + writel(((m[11] & 0x1fff) << 16) | (m[12] & 0x1fff), + priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF22_30)); + writel(((m[13] & 0x1fff) << 16) | (m[14] & 0x1fff), + priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF31_32)); + writel(((m[15] & 0x1fff) << 16) | (m[16] & 0x1fff), + priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF40_41)); + writel(m[17] & 0x1fff, priv->io_base + + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); + } else + writel((m[11] & 0x1fff) << 16, priv->io_base + + _REG(VIU_OSD1_MATRIX_COEF22_30)); + + writel(((m[18] & 0xfff) << 16) | (m[19] & 0xfff), + priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET0_1)); + writel(m[20] & 0xfff, + priv->io_base + _REG(VIU_OSD1_MATRIX_OFFSET2)); + + writel_bits_relaxed(3 << 30, m[21] << 30, + priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); + writel_bits_relaxed(7 << 16, m[22] << 16, + priv->io_base + _REG(VIU_OSD1_MATRIX_COLMOD_COEF42)); + + /* 23 reserved for clipping control */ + writel_bits_relaxed(BIT(0), csc_on ? BIT(0) : 0, + priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); + writel_bits_relaxed(BIT(1), 0, + priv->io_base + _REG(VIU_OSD1_MATRIX_CTRL)); + } else if (m_select == VIU_MATRIX_OSD_EOTF) { + int i; + + /* osd eotf matrix, VIU_MATRIX_OSD_EOTF */ + for (i = 0; i < 5; i++) + writel(((m[i * 2] & 0x1fff) << 16) | + (m[i * 2 + 1] & 0x1fff), priv->io_base + + _REG(VIU_OSD1_EOTF_CTL + i + 1)); + + writel_bits_relaxed(BIT(30), csc_on ? BIT(30) : 0, + priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); + writel_bits_relaxed(BIT(31), csc_on ? BIT(31) : 0, + priv->io_base + _REG(VIU_OSD1_EOTF_CTL)); + } +} + +#define OSD_EOTF_LUT_SIZE 33 +#define OSD_OETF_LUT_SIZE 41 + +void meson_viu_set_osd_lut(struct meson_drm *priv, enum viu_lut_sel_e lut_sel, + unsigned int *r_map, unsigned int *g_map, + unsigned int *b_map, + bool csc_on) +{ + unsigned int addr_port; + unsigned int data_port; + unsigned int ctrl_port; + int i; + + if (lut_sel == VIU_LUT_OSD_EOTF) { + addr_port = VIU_OSD1_EOTF_LUT_ADDR_PORT; + data_port = VIU_OSD1_EOTF_LUT_DATA_PORT; + ctrl_port = VIU_OSD1_EOTF_CTL; + } else if (lut_sel == VIU_LUT_OSD_OETF) { + addr_port = VIU_OSD1_OETF_LUT_ADDR_PORT; + data_port = VIU_OSD1_OETF_LUT_DATA_PORT; + ctrl_port = VIU_OSD1_OETF_CTL; + } else + return; + + if (lut_sel == VIU_LUT_OSD_OETF) { + writel(0, priv->io_base + _REG(addr_port)); + + for (i = 0; i < 20; i++) + writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(r_map[OSD_OETF_LUT_SIZE - 1] | (g_map[0] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < 20; i++) + writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < 20; i++) + writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(b_map[OSD_OETF_LUT_SIZE - 1], + priv->io_base + _REG(data_port)); + + if (csc_on) + writel_bits_relaxed(0x7 << 29, 7 << 29, + priv->io_base + _REG(ctrl_port)); + else + writel_bits_relaxed(0x7 << 29, 0, + priv->io_base + _REG(ctrl_port)); + } else if (lut_sel == VIU_LUT_OSD_EOTF) { + writel(0, priv->io_base + _REG(addr_port)); + + for (i = 0; i < 20; i++) + writel(r_map[i * 2] | (r_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(r_map[OSD_EOTF_LUT_SIZE - 1] | (g_map[0] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < 20; i++) + writel(g_map[i * 2 + 1] | (g_map[i * 2 + 2] << 16), + priv->io_base + _REG(data_port)); + + for (i = 0; i < 20; i++) + writel(b_map[i * 2] | (b_map[i * 2 + 1] << 16), + priv->io_base + _REG(data_port)); + + writel(b_map[OSD_EOTF_LUT_SIZE - 1], + priv->io_base + _REG(data_port)); + + if (csc_on) + writel_bits_relaxed(7 << 27, 7 << 27, + priv->io_base + _REG(ctrl_port)); + else + writel_bits_relaxed(7 << 27, 0, + priv->io_base + _REG(ctrl_port)); + + writel_bits_relaxed(BIT(31), BIT(31), + priv->io_base + _REG(ctrl_port)); + } +} + +/* eotf lut: linear */ +static unsigned int eotf_33_linear_mapping[OSD_EOTF_LUT_SIZE] = { + 0x0000, 0x0200, 0x0400, 0x0600, + 0x0800, 0x0a00, 0x0c00, 0x0e00, + 0x1000, 0x1200, 0x1400, 0x1600, + 0x1800, 0x1a00, 0x1c00, 0x1e00, + 0x2000, 0x2200, 0x2400, 0x2600, + 0x2800, 0x2a00, 0x2c00, 0x2e00, + 0x3000, 0x3200, 0x3400, 0x3600, + 0x3800, 0x3a00, 0x3c00, 0x3e00, + 0x4000 +}; + +/* osd oetf lut: linear */ +static unsigned int oetf_41_linear_mapping[OSD_OETF_LUT_SIZE] = { + 0, 0, 0, 0, + 0, 32, 64, 96, + 128, 160, 196, 224, + 256, 288, 320, 352, + 384, 416, 448, 480, + 512, 544, 576, 608, + 640, 672, 704, 736, + 768, 800, 832, 864, + 896, 928, 960, 992, + 1023, 1023, 1023, 1023, + 1023 +}; + +static void meson_viu_load_matrix(struct meson_drm *priv) +{ + /* eotf lut bypass */ + meson_viu_set_osd_lut(priv, VIU_LUT_OSD_EOTF, + eotf_33_linear_mapping, /* R */ + eotf_33_linear_mapping, /* G */ + eotf_33_linear_mapping, /* B */ + false); + + /* eotf matrix bypass */ + meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD_EOTF, + eotf_bypass_coeff, + false); + + /* oetf lut bypass */ + meson_viu_set_osd_lut(priv, VIU_LUT_OSD_OETF, + oetf_41_linear_mapping, /* R */ + oetf_41_linear_mapping, /* G */ + oetf_41_linear_mapping, /* B */ + false); + + /* osd matrix RGB709 to YUV709 limit */ + meson_viu_set_osd_matrix(priv, VIU_MATRIX_OSD, + RGB709_to_YUV709l_coeff, + true); +} + +void meson_viu_init(struct meson_drm *priv) +{ + uint32_t reg; + + /* Disable OSDs */ + writel_bits_relaxed(BIT(0) | BIT(21), 0, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT)); + writel_bits_relaxed(BIT(0) | BIT(21), 0, + priv->io_base + _REG(VIU_OSD2_CTRL_STAT)); + + /* On GXL/GXM, Use the 10bit HDR conversion matrix */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu") || + meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + meson_viu_load_matrix(priv); + + /* Initialize OSD1 fifo control register */ + reg = BIT(0) | /* Urgent DDR request priority */ + (4 << 5) | /* hold_fifo_lines */ + (3 << 10) | /* burst length 64 */ + (32 << 12) | /* fifo_depth_val: 32*8=256 */ + (2 << 22) | /* 4 words in 1 burst */ + (2 << 24); + writel_relaxed(reg, priv->io_base + _REG(VIU_OSD1_FIFO_CTRL_STAT)); + writel_relaxed(reg, priv->io_base + _REG(VIU_OSD2_FIFO_CTRL_STAT)); + + /* Set OSD alpha replace value */ + writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, + 0xff << OSD_REPLACE_SHIFT, + priv->io_base + _REG(VIU_OSD1_CTRL_STAT2)); + writel_bits_relaxed(0xff << OSD_REPLACE_SHIFT, + 0xff << OSD_REPLACE_SHIFT, + priv->io_base + _REG(VIU_OSD2_CTRL_STAT2)); + + priv->viu.osd1_enabled = false; + priv->viu.osd1_commit = false; + priv->viu.osd1_interlace = false; +} diff --git a/drivers/gpu/drm/meson/meson_viu.h b/drivers/gpu/drm/meson/meson_viu.h new file mode 100644 index 000000000000..073b1910bd1b --- /dev/null +++ b/drivers/gpu/drm/meson/meson_viu.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Video Input Unit */ + +#ifndef __MESON_VIU_H +#define __MESON_VIU_H + +/* OSDx_BLKx_CFG */ +#define OSD_CANVAS_SEL 16 + +#define OSD_ENDIANNESS_LE BIT(15) +#define OSD_ENDIANNESS_BE (0) + +#define OSD_BLK_MODE_422 (0x03 << 8) +#define OSD_BLK_MODE_16 (0x04 << 8) +#define OSD_BLK_MODE_32 (0x05 << 8) +#define OSD_BLK_MODE_24 (0x07 << 8) + +#define OSD_OUTPUT_COLOR_RGB BIT(7) +#define OSD_OUTPUT_COLOR_YUV (0) + +#define OSD_COLOR_MATRIX_32_RGBA (0x00 << 2) +#define OSD_COLOR_MATRIX_32_ARGB (0x01 << 2) +#define OSD_COLOR_MATRIX_32_ABGR (0x02 << 2) +#define OSD_COLOR_MATRIX_32_BGRA (0x03 << 2) + +#define OSD_COLOR_MATRIX_24_RGB (0x00 << 2) + +#define OSD_COLOR_MATRIX_16_RGB655 (0x00 << 2) +#define OSD_COLOR_MATRIX_16_RGB565 (0x04 << 2) + +#define OSD_INTERLACE_ENABLED BIT(1) +#define OSD_INTERLACE_ODD BIT(0) +#define OSD_INTERLACE_EVEN (0) + +/* OSDx_CTRL_STAT */ +#define OSD_ENABLE BIT(21) +#define OSD_BLK0_ENABLE BIT(0) + +#define OSD_GLOBAL_ALPHA_SHIFT 12 + +/* OSDx_CTRL_STAT2 */ +#define OSD_REPLACE_EN BIT(14) +#define OSD_REPLACE_SHIFT 6 + +void meson_viu_init(struct meson_drm *priv); + +#endif /* __MESON_VIU_H */ diff --git a/drivers/gpu/drm/meson/meson_vpp.c b/drivers/gpu/drm/meson/meson_vpp.c new file mode 100644 index 000000000000..671909d8672e --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vpp.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * Copyright (C) 2015 Amlogic, Inc. All rights reserved. + * Copyright (C) 2014 Endless Mobile + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <drm/drmP.h> +#include "meson_drv.h" +#include "meson_vpp.h" +#include "meson_registers.h" + +/* + * VPP Handles all the Post Processing after the Scanout from the VIU + * We handle the following post processings : + * - Postblend : Blends the OSD1 only + * We exclude OSD2, VS1, VS1 and Preblend output + * - Vertical OSD Scaler for OSD1 only, we disable vertical scaler and + * use it only for interlace scanout + * - Intermediate FIFO with default Amlogic values + * + * What is missing : + * - Preblend for video overlay pre-scaling + * - OSD2 support for cursor framebuffer + * - Video pre-scaling before postblend + * - Full Vertical/Horizontal OSD scaling to support TV overscan + * - HDR conversion + */ + +void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux) +{ + writel(mux, priv->io_base + _REG(VPU_VIU_VENC_MUX_CTRL)); +} + +/* + * When the output is interlaced, the OSD must switch between + * each field using the INTERLACE_SEL_ODD (0) of VIU_OSD1_BLK0_CFG_W0 + * at each vsync. + * But the vertical scaler can provide such funtionnality if + * is configured for 2:1 scaling with interlace options enabled. + */ +void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, + struct drm_rect *input) +{ + writel_relaxed(BIT(3) /* Enable scaler */ | + BIT(2), /* Select OSD1 */ + priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + + writel_relaxed(((drm_rect_width(input) - 1) << 16) | + (drm_rect_height(input) - 1), + priv->io_base + _REG(VPP_OSD_SCI_WH_M1)); + /* 2:1 scaling */ + writel_relaxed(((input->x1) << 16) | (input->x2), + priv->io_base + _REG(VPP_OSD_SCO_H_START_END)); + writel_relaxed(((input->y1 >> 1) << 16) | (input->y2 >> 1), + priv->io_base + _REG(VPP_OSD_SCO_V_START_END)); + + /* 2:1 scaling values */ + writel_relaxed(BIT(16), priv->io_base + _REG(VPP_OSD_VSC_INI_PHASE)); + writel_relaxed(BIT(25), priv->io_base + _REG(VPP_OSD_VSC_PHASE_STEP)); + + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + + writel_relaxed((4 << 0) /* osd_vsc_bank_length */ | + (4 << 3) /* osd_vsc_top_ini_rcv_num0 */ | + (1 << 8) /* osd_vsc_top_rpt_p0_num0 */ | + (6 << 11) /* osd_vsc_bot_ini_rcv_num0 */ | + (2 << 16) /* osd_vsc_bot_rpt_p0_num0 */ | + BIT(23) /* osd_prog_interlace */ | + BIT(24), /* Enable vertical scaler */ + priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); +} + +void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv) +{ + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); +} + +static unsigned int vpp_filter_coefs_4point_bspline[] = { + 0x15561500, 0x14561600, 0x13561700, 0x12561800, + 0x11551a00, 0x11541b00, 0x10541c00, 0x0f541d00, + 0x0f531e00, 0x0e531f00, 0x0d522100, 0x0c522200, + 0x0b522300, 0x0b512400, 0x0a502600, 0x0a4f2700, + 0x094e2900, 0x084e2a00, 0x084d2b00, 0x074c2c01, + 0x074b2d01, 0x064a2f01, 0x06493001, 0x05483201, + 0x05473301, 0x05463401, 0x04453601, 0x04433702, + 0x04423802, 0x03413a02, 0x03403b02, 0x033f3c02, + 0x033d3d03 +}; + +static void meson_vpp_write_scaling_filter_coefs(struct meson_drm *priv, + const unsigned int *coefs, + bool is_horizontal) +{ + int i; + + writel_relaxed(is_horizontal ? BIT(8) : 0, + priv->io_base + _REG(VPP_OSD_SCALE_COEF_IDX)); + for (i = 0; i < 33; i++) + writel_relaxed(coefs[i], + priv->io_base + _REG(VPP_OSD_SCALE_COEF)); +} + +void meson_vpp_init(struct meson_drm *priv) +{ + /* set dummy data default YUV black */ + if (meson_vpu_is_compatible(priv, "amlogic,meson-gxl-vpu")) + writel_relaxed(0x108080, priv->io_base + _REG(VPP_DUMMY_DATA1)); + else if (meson_vpu_is_compatible(priv, "amlogic,meson-gxm-vpu")) { + writel_bits_relaxed(0xff << 16, 0xff << 16, + priv->io_base + _REG(VIU_MISC_CTRL1)); + writel_relaxed(0x20000, priv->io_base + _REG(VPP_DOLBY_CTRL)); + writel_relaxed(0x1020080, + priv->io_base + _REG(VPP_DUMMY_DATA1)); + } + + /* Initialize vpu fifo control registers */ + writel_relaxed(readl_relaxed(priv->io_base + _REG(VPP_OFIFO_SIZE)) | + 0x77f, priv->io_base + _REG(VPP_OFIFO_SIZE)); + writel_relaxed(0x08080808, priv->io_base + _REG(VPP_HOLD_LINES)); + + /* Turn off preblend */ + writel_bits_relaxed(VPP_PREBLEND_ENABLE, 0, + priv->io_base + _REG(VPP_MISC)); + + /* Turn off POSTBLEND */ + writel_bits_relaxed(VPP_POSTBLEND_ENABLE, 0, + priv->io_base + _REG(VPP_MISC)); + + /* Force all planes off */ + writel_bits_relaxed(VPP_OSD1_POSTBLEND | VPP_OSD2_POSTBLEND | + VPP_VD1_POSTBLEND | VPP_VD2_POSTBLEND, 0, + priv->io_base + _REG(VPP_MISC)); + + /* Disable Scalers */ + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_SC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_VSC_CTRL0)); + writel_relaxed(0, priv->io_base + _REG(VPP_OSD_HSC_CTRL0)); + + /* Write in the proper filter coefficients. */ + meson_vpp_write_scaling_filter_coefs(priv, + vpp_filter_coefs_4point_bspline, false); + meson_vpp_write_scaling_filter_coefs(priv, + vpp_filter_coefs_4point_bspline, true); +} diff --git a/drivers/gpu/drm/meson/meson_vpp.h b/drivers/gpu/drm/meson/meson_vpp.h new file mode 100644 index 000000000000..ede3b26e0f22 --- /dev/null +++ b/drivers/gpu/drm/meson/meson_vpp.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 BayLibre, SAS + * Author: Neil Armstrong <narmstrong@baylibre.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* Video Post Process */ + +#ifndef __MESON_VPP_H +#define __MESON_VPP_H + +/* Mux VIU/VPP to ENCI */ +#define MESON_VIU_VPP_MUX_ENCI 0x5 + +void meson_vpp_setup_mux(struct meson_drm *priv, unsigned int mux); + +void meson_vpp_setup_interlace_vscaler_osd1(struct meson_drm *priv, + struct drm_rect *input); +void meson_vpp_disable_interlace_vscaler_osd1(struct meson_drm *priv); + +void meson_vpp_init(struct meson_drm *priv); + +#endif /* __MESON_VPP_H */ diff --git a/drivers/gpu/drm/mxsfb/Kconfig b/drivers/gpu/drm/mxsfb/Kconfig new file mode 100644 index 000000000000..e9a8d90e6723 --- /dev/null +++ b/drivers/gpu/drm/mxsfb/Kconfig @@ -0,0 +1,19 @@ +config DRM_MXS + bool + help + Choose this option to select drivers for MXS FB devices + +config DRM_MXSFB + tristate "i.MX23/i.MX28/i.MX6SX MXSFB LCD controller" + depends on DRM && OF + depends on COMMON_CLK + select DRM_MXS + select DRM_KMS_HELPER + select DRM_KMS_FB_HELPER + select DRM_KMS_CMA_HELPER + select DRM_PANEL + help + Choose this option if you have an i.MX23/i.MX28/i.MX6SX MXSFB + LCD controller. + + If M is selected the module will be called mxsfb. diff --git a/drivers/gpu/drm/mxsfb/Makefile b/drivers/gpu/drm/mxsfb/Makefile new file mode 100644 index 000000000000..857f3a4545ff --- /dev/null +++ b/drivers/gpu/drm/mxsfb/Makefile @@ -0,0 +1,2 @@ +mxsfb-y := mxsfb_drv.o mxsfb_crtc.o mxsfb_out.o +obj-$(CONFIG_DRM_MXSFB) += mxsfb.o diff --git a/drivers/gpu/drm/mxsfb/mxsfb_crtc.c b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c new file mode 100644 index 000000000000..081890336ce7 --- /dev/null +++ b/drivers/gpu/drm/mxsfb/mxsfb_crtc.c @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * This code is based on drivers/video/fbdev/mxsfb.c : + * Copyright (C) 2010 Juergen Beisert, Pengutronix + * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <linux/clk.h> +#include <linux/iopoll.h> +#include <linux/of_graph.h> +#include <linux/platform_data/simplefb.h> +#include <video/videomode.h> + +#include "mxsfb_drv.h" +#include "mxsfb_regs.h" + +static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) +{ + return (val & mxsfb->devdata->hs_wdth_mask) << + mxsfb->devdata->hs_wdth_shift; +} + +/* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ +static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) +{ + struct drm_crtc *crtc = &mxsfb->pipe.crtc; + struct drm_device *drm = crtc->dev; + const u32 format = crtc->primary->state->fb->pixel_format; + u32 ctrl, ctrl1; + + ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER; + + /* + * WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to + * match the selected mode here. This differs from the original + * MXSFB driver, which had the option to configure the bus width + * to arbitrary value. This limitation should not pose an issue. + */ + + /* CTRL1 contains IRQ config and status bits, preserve those. */ + ctrl1 = readl(mxsfb->base + LCDC_CTRL1); + ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ; + + switch (format) { + case DRM_FORMAT_RGB565: + dev_dbg(drm->dev, "Setting up RGB565 mode\n"); + ctrl |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT); + ctrl |= CTRL_SET_WORD_LENGTH(0); + ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf); + break; + case DRM_FORMAT_XRGB8888: + dev_dbg(drm->dev, "Setting up XRGB8888 mode\n"); + ctrl |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT); + ctrl |= CTRL_SET_WORD_LENGTH(3); + /* Do not use packed pixels = one pixel per word instead. */ + ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7); + break; + default: + dev_err(drm->dev, "Unhandled pixel format %08x\n", format); + return -EINVAL; + } + + writel(ctrl1, mxsfb->base + LCDC_CTRL1); + writel(ctrl, mxsfb->base + LCDC_CTRL); + + return 0; +} + +static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb) +{ + u32 reg; + + if (mxsfb->clk_disp_axi) + clk_prepare_enable(mxsfb->clk_disp_axi); + clk_prepare_enable(mxsfb->clk); + mxsfb_enable_axi_clk(mxsfb); + + /* If it was disabled, re-enable the mode again */ + writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET); + + /* Enable the SYNC signals first, then the DMA engine */ + reg = readl(mxsfb->base + LCDC_VDCTRL4); + reg |= VDCTRL4_SYNC_SIGNALS_ON; + writel(reg, mxsfb->base + LCDC_VDCTRL4); + + writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET); +} + +static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb) +{ + u32 reg; + + /* + * Even if we disable the controller here, it will still continue + * until its FIFOs are running out of data + */ + writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR); + + readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN), + 0, 1000); + + reg = readl(mxsfb->base + LCDC_VDCTRL4); + reg &= ~VDCTRL4_SYNC_SIGNALS_ON; + writel(reg, mxsfb->base + LCDC_VDCTRL4); + + mxsfb_disable_axi_clk(mxsfb); + + clk_disable_unprepare(mxsfb->clk); + if (mxsfb->clk_disp_axi) + clk_disable_unprepare(mxsfb->clk_disp_axi); +} + +static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) +{ + struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; + const u32 bus_flags = mxsfb->connector.display_info.bus_flags; + u32 vdctrl0, vsync_pulse_len, hsync_pulse_len; + int err; + + /* + * It seems, you can't re-program the controller if it is still + * running. This may lead to shifted pictures (FIFO issue?), so + * first stop the controller and drain its FIFOs. + */ + mxsfb_enable_axi_clk(mxsfb); + + /* Clear the FIFOs */ + writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET); + + err = mxsfb_set_pixel_fmt(mxsfb); + if (err) + return; + + clk_set_rate(mxsfb->clk, m->crtc_clock * 1000); + + writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) | + TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay), + mxsfb->base + mxsfb->devdata->transfer_count); + + vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start; + + vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */ + VDCTRL0_VSYNC_PERIOD_UNIT | + VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | + VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len); + if (m->flags & DRM_MODE_FLAG_PHSYNC) + vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH; + if (m->flags & DRM_MODE_FLAG_PVSYNC) + vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH; + if (bus_flags & DRM_BUS_FLAG_DE_HIGH) + vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH; + if (bus_flags & DRM_BUS_FLAG_PIXDATA_NEGEDGE) + vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING; + + writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0); + + /* Frame length in lines. */ + writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1); + + /* Line length in units of clocks or pixels. */ + hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start; + writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) | + VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal), + mxsfb->base + LCDC_VDCTRL2); + + writel(SET_HOR_WAIT_CNT(m->crtc_hblank_end - m->crtc_hsync_end) | + SET_VERT_WAIT_CNT(m->crtc_vblank_end - m->crtc_vsync_end), + mxsfb->base + LCDC_VDCTRL3); + + writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay), + mxsfb->base + LCDC_VDCTRL4); + + mxsfb_disable_axi_clk(mxsfb); +} + +void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) +{ + mxsfb_crtc_mode_set_nofb(mxsfb); + mxsfb_enable_controller(mxsfb); +} + +void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb) +{ + mxsfb_disable_controller(mxsfb); +} + +void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, + struct drm_plane_state *state) +{ + struct drm_simple_display_pipe *pipe = &mxsfb->pipe; + struct drm_crtc *crtc = &pipe->crtc; + struct drm_framebuffer *fb = pipe->plane.state->fb; + struct drm_pending_vblank_event *event; + struct drm_gem_cma_object *gem; + + if (!crtc) + return; + + spin_lock_irq(&crtc->dev->event_lock); + event = crtc->state->event; + if (event) { + crtc->state->event = NULL; + + if (drm_crtc_vblank_get(crtc) == 0) { + drm_crtc_arm_vblank_event(crtc, event); + } else { + drm_crtc_send_vblank_event(crtc, event); + } + } + spin_unlock_irq(&crtc->dev->event_lock); + + if (!fb) + return; + + gem = drm_fb_cma_get_gem_obj(fb, 0); + + mxsfb_enable_axi_clk(mxsfb); + writel(gem->paddr, mxsfb->base + mxsfb->devdata->next_buf); + mxsfb_disable_axi_clk(mxsfb); +} diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.c b/drivers/gpu/drm/mxsfb/mxsfb_drv.c new file mode 100644 index 000000000000..79a18bf48b54 --- /dev/null +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.c @@ -0,0 +1,444 @@ +/* + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * This code is based on drivers/video/fbdev/mxsfb.c : + * Copyright (C) 2010 Juergen Beisert, Pengutronix + * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/list.h> +#include <linux/of_device.h> +#include <linux/of_graph.h> +#include <linux/of_reserved_mem.h> +#include <linux/pm_runtime.h> +#include <linux/reservation.h> + +#include <drm/drmP.h> +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_of.h> +#include <drm/drm_panel.h> +#include <drm/drm_simple_kms_helper.h> + +#include "mxsfb_drv.h" +#include "mxsfb_regs.h" + +enum mxsfb_devtype { + MXSFB_V3, + MXSFB_V4, +}; + +static const struct mxsfb_devdata mxsfb_devdata[] = { + [MXSFB_V3] = { + .transfer_count = LCDC_V3_TRANSFER_COUNT, + .cur_buf = LCDC_V3_CUR_BUF, + .next_buf = LCDC_V3_NEXT_BUF, + .debug0 = LCDC_V3_DEBUG0, + .hs_wdth_mask = 0xff, + .hs_wdth_shift = 24, + .ipversion = 3, + }, + [MXSFB_V4] = { + .transfer_count = LCDC_V4_TRANSFER_COUNT, + .cur_buf = LCDC_V4_CUR_BUF, + .next_buf = LCDC_V4_NEXT_BUF, + .debug0 = LCDC_V4_DEBUG0, + .hs_wdth_mask = 0x3fff, + .hs_wdth_shift = 18, + .ipversion = 4, + }, +}; + +static const uint32_t mxsfb_formats[] = { + DRM_FORMAT_XRGB8888, + DRM_FORMAT_RGB565 +}; + +static struct mxsfb_drm_private * +drm_pipe_to_mxsfb_drm_private(struct drm_simple_display_pipe *pipe) +{ + return container_of(pipe, struct mxsfb_drm_private, pipe); +} + +void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb) +{ + if (mxsfb->clk_axi) + clk_prepare_enable(mxsfb->clk_axi); +} + +void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb) +{ + if (mxsfb->clk_axi) + clk_disable_unprepare(mxsfb->clk_axi); +} + +static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = { + .fb_create = drm_fb_cma_create, + .atomic_check = drm_atomic_helper_check, + .atomic_commit = drm_atomic_helper_commit, +}; + +static void mxsfb_pipe_enable(struct drm_simple_display_pipe *pipe, + struct drm_crtc_state *crtc_state) +{ + struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); + + mxsfb_crtc_enable(mxsfb); +} + +static void mxsfb_pipe_disable(struct drm_simple_display_pipe *pipe) +{ + struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); + + mxsfb_crtc_disable(mxsfb); +} + +static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + struct mxsfb_drm_private *mxsfb = drm_pipe_to_mxsfb_drm_private(pipe); + + mxsfb_plane_atomic_update(mxsfb, plane_state); +} + +static int mxsfb_pipe_prepare_fb(struct drm_simple_display_pipe *pipe, + struct drm_plane_state *plane_state) +{ + return drm_fb_cma_prepare_fb(&pipe->plane, plane_state); +} + +struct drm_simple_display_pipe_funcs mxsfb_funcs = { + .enable = mxsfb_pipe_enable, + .disable = mxsfb_pipe_disable, + .update = mxsfb_pipe_update, + .prepare_fb = mxsfb_pipe_prepare_fb, +}; + +static int mxsfb_load(struct drm_device *drm, unsigned long flags) +{ + struct platform_device *pdev = to_platform_device(drm->dev); + struct mxsfb_drm_private *mxsfb; + struct resource *res; + int ret; + + mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL); + if (!mxsfb) + return -ENOMEM; + + drm->dev_private = mxsfb; + mxsfb->devdata = &mxsfb_devdata[pdev->id_entry->driver_data]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mxsfb->base = devm_ioremap_resource(drm->dev, res); + if (IS_ERR(mxsfb->base)) + return PTR_ERR(mxsfb->base); + + mxsfb->clk = devm_clk_get(drm->dev, NULL); + if (IS_ERR(mxsfb->clk)) + return PTR_ERR(mxsfb->clk); + + mxsfb->clk_axi = devm_clk_get(drm->dev, "axi"); + if (IS_ERR(mxsfb->clk_axi)) + mxsfb->clk_axi = NULL; + + mxsfb->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi"); + if (IS_ERR(mxsfb->clk_disp_axi)) + mxsfb->clk_disp_axi = NULL; + + ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + + pm_runtime_enable(drm->dev); + + ret = drm_vblank_init(drm, drm->mode_config.num_crtc); + if (ret < 0) { + dev_err(drm->dev, "Failed to initialise vblank\n"); + goto err_vblank; + } + + /* Modeset init */ + drm_mode_config_init(drm); + + ret = mxsfb_create_output(drm); + if (ret < 0) { + dev_err(drm->dev, "Failed to create outputs\n"); + goto err_vblank; + } + + ret = drm_simple_display_pipe_init(drm, &mxsfb->pipe, &mxsfb_funcs, + mxsfb_formats, ARRAY_SIZE(mxsfb_formats), + &mxsfb->connector); + if (ret < 0) { + dev_err(drm->dev, "Cannot setup simple display pipe\n"); + goto err_vblank; + } + + ret = drm_panel_attach(mxsfb->panel, &mxsfb->connector); + if (ret) { + dev_err(drm->dev, "Cannot connect panel\n"); + goto err_vblank; + } + + drm->mode_config.min_width = MXSFB_MIN_XRES; + drm->mode_config.min_height = MXSFB_MIN_YRES; + drm->mode_config.max_width = MXSFB_MAX_XRES; + drm->mode_config.max_height = MXSFB_MAX_YRES; + drm->mode_config.funcs = &mxsfb_mode_config_funcs; + + drm_mode_config_reset(drm); + + pm_runtime_get_sync(drm->dev); + ret = drm_irq_install(drm, platform_get_irq(pdev, 0)); + pm_runtime_put_sync(drm->dev); + + if (ret < 0) { + dev_err(drm->dev, "Failed to install IRQ handler\n"); + goto err_irq; + } + + drm_kms_helper_poll_init(drm); + + mxsfb->fbdev = drm_fbdev_cma_init(drm, 32, drm->mode_config.num_crtc, + drm->mode_config.num_connector); + if (IS_ERR(mxsfb->fbdev)) { + mxsfb->fbdev = NULL; + dev_err(drm->dev, "Failed to init FB CMA area\n"); + goto err_cma; + } + + platform_set_drvdata(pdev, drm); + + drm_helper_hpd_irq_event(drm); + + return 0; + +err_cma: + drm_irq_uninstall(drm); +err_irq: + drm_panel_detach(mxsfb->panel); +err_vblank: + pm_runtime_disable(drm->dev); + + return ret; +} + +static void mxsfb_unload(struct drm_device *drm) +{ + struct mxsfb_drm_private *mxsfb = drm->dev_private; + + if (mxsfb->fbdev) + drm_fbdev_cma_fini(mxsfb->fbdev); + + drm_kms_helper_poll_fini(drm); + drm_mode_config_cleanup(drm); + drm_vblank_cleanup(drm); + + pm_runtime_get_sync(drm->dev); + drm_irq_uninstall(drm); + pm_runtime_put_sync(drm->dev); + + drm->dev_private = NULL; + + pm_runtime_disable(drm->dev); +} + +static void mxsfb_lastclose(struct drm_device *drm) +{ + struct mxsfb_drm_private *mxsfb = drm->dev_private; + + drm_fbdev_cma_restore_mode(mxsfb->fbdev); +} + +static int mxsfb_enable_vblank(struct drm_device *drm, unsigned int crtc) +{ + struct mxsfb_drm_private *mxsfb = drm->dev_private; + + /* Clear and enable VBLANK IRQ */ + mxsfb_enable_axi_clk(mxsfb); + writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); + writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET); + mxsfb_disable_axi_clk(mxsfb); + + return 0; +} + +static void mxsfb_disable_vblank(struct drm_device *drm, unsigned int crtc) +{ + struct mxsfb_drm_private *mxsfb = drm->dev_private; + + /* Disable and clear VBLANK IRQ */ + mxsfb_enable_axi_clk(mxsfb); + writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR); + writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); + mxsfb_disable_axi_clk(mxsfb); +} + +static void mxsfb_irq_preinstall(struct drm_device *drm) +{ + mxsfb_disable_vblank(drm, 0); +} + +static irqreturn_t mxsfb_irq_handler(int irq, void *data) +{ + struct drm_device *drm = data; + struct mxsfb_drm_private *mxsfb = drm->dev_private; + u32 reg; + + mxsfb_enable_axi_clk(mxsfb); + + reg = readl(mxsfb->base + LCDC_CTRL1); + + if (reg & CTRL1_CUR_FRAME_DONE_IRQ) + drm_crtc_handle_vblank(&mxsfb->pipe.crtc); + + writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR); + + mxsfb_disable_axi_clk(mxsfb); + + return IRQ_HANDLED; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .open = drm_open, + .release = drm_release, + .unlocked_ioctl = drm_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = drm_compat_ioctl, +#endif + .poll = drm_poll, + .read = drm_read, + .llseek = noop_llseek, + .mmap = drm_gem_cma_mmap, +}; + +static struct drm_driver mxsfb_driver = { + .driver_features = DRIVER_GEM | DRIVER_MODESET | + DRIVER_PRIME | DRIVER_ATOMIC | + DRIVER_HAVE_IRQ, + .lastclose = mxsfb_lastclose, + .irq_handler = mxsfb_irq_handler, + .irq_preinstall = mxsfb_irq_preinstall, + .irq_uninstall = mxsfb_irq_preinstall, + .get_vblank_counter = drm_vblank_no_hw_counter, + .enable_vblank = mxsfb_enable_vblank, + .disable_vblank = mxsfb_disable_vblank, + .gem_free_object = drm_gem_cma_free_object, + .gem_vm_ops = &drm_gem_cma_vm_ops, + .dumb_create = drm_gem_cma_dumb_create, + .dumb_map_offset = drm_gem_cma_dumb_map_offset, + .dumb_destroy = drm_gem_dumb_destroy, + .prime_handle_to_fd = drm_gem_prime_handle_to_fd, + .prime_fd_to_handle = drm_gem_prime_fd_to_handle, + .gem_prime_export = drm_gem_prime_export, + .gem_prime_import = drm_gem_prime_import, + .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, + .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, + .gem_prime_vmap = drm_gem_cma_prime_vmap, + .gem_prime_vunmap = drm_gem_cma_prime_vunmap, + .gem_prime_mmap = drm_gem_cma_prime_mmap, + .fops = &fops, + .name = "mxsfb-drm", + .desc = "MXSFB Controller DRM", + .date = "20160824", + .major = 1, + .minor = 0, +}; + +static const struct platform_device_id mxsfb_devtype[] = { + { .name = "imx23-fb", .driver_data = MXSFB_V3, }, + { .name = "imx28-fb", .driver_data = MXSFB_V4, }, + { .name = "imx6sx-fb", .driver_data = MXSFB_V4, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, mxsfb_devtype); + +static const struct of_device_id mxsfb_dt_ids[] = { + { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], }, + { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], }, + { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devtype[2], }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxsfb_dt_ids); + +static int mxsfb_probe(struct platform_device *pdev) +{ + struct drm_device *drm; + const struct of_device_id *of_id = + of_match_device(mxsfb_dt_ids, &pdev->dev); + int ret; + + if (!pdev->dev.of_node) + return -ENODEV; + + if (of_id) + pdev->id_entry = of_id->data; + + drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev); + if (!drm) + return -ENOMEM; + + ret = mxsfb_load(drm, 0); + if (ret) + goto err_free; + + ret = drm_dev_register(drm, 0); + if (ret) + goto err_unload; + + return 0; + +err_unload: + mxsfb_unload(drm); +err_free: + drm_dev_unref(drm); + + return ret; +} + +static int mxsfb_remove(struct platform_device *pdev) +{ + struct drm_device *drm = platform_get_drvdata(pdev); + + drm_dev_unregister(drm); + mxsfb_unload(drm); + drm_dev_unref(drm); + + return 0; +} + +static struct platform_driver mxsfb_platform_driver = { + .probe = mxsfb_probe, + .remove = mxsfb_remove, + .id_table = mxsfb_devtype, + .driver = { + .name = "mxsfb", + .of_match_table = mxsfb_dt_ids, + }, +}; + +module_platform_driver(mxsfb_platform_driver); + +MODULE_AUTHOR("Marek Vasut <marex@denx.de>"); +MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/mxsfb/mxsfb_drv.h b/drivers/gpu/drm/mxsfb/mxsfb_drv.h new file mode 100644 index 000000000000..5d0883fc805b --- /dev/null +++ b/drivers/gpu/drm/mxsfb/mxsfb_drv.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * i.MX23/i.MX28/i.MX6SX MXSFB LCD controller driver. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MXSFB_DRV_H__ +#define __MXSFB_DRV_H__ + +struct mxsfb_devdata { + unsigned int transfer_count; + unsigned int cur_buf; + unsigned int next_buf; + unsigned int debug0; + unsigned int hs_wdth_mask; + unsigned int hs_wdth_shift; + unsigned int ipversion; +}; + +struct mxsfb_drm_private { + const struct mxsfb_devdata *devdata; + + void __iomem *base; /* registers */ + struct clk *clk; + struct clk *clk_axi; + struct clk *clk_disp_axi; + + struct drm_simple_display_pipe pipe; + struct drm_connector connector; + struct drm_panel *panel; + struct drm_fbdev_cma *fbdev; +}; + +int mxsfb_setup_crtc(struct drm_device *dev); +int mxsfb_create_output(struct drm_device *dev); + +void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb); +void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb); + +void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb); +void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb); +void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, + struct drm_plane_state *state); + +#endif /* __MXSFB_DRV_H__ */ diff --git a/drivers/gpu/drm/mxsfb/mxsfb_out.c b/drivers/gpu/drm/mxsfb/mxsfb_out.c new file mode 100644 index 000000000000..fa8d17399407 --- /dev/null +++ b/drivers/gpu/drm/mxsfb/mxsfb_out.c @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/of_graph.h> + +#include <drm/drm_atomic.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_cma_helper.h> +#include <drm/drm_gem_cma_helper.h> +#include <drm/drm_panel.h> +#include <drm/drm_plane_helper.h> +#include <drm/drm_simple_kms_helper.h> +#include <drm/drmP.h> + +#include "mxsfb_drv.h" + +static struct mxsfb_drm_private * +drm_connector_to_mxsfb_drm_private(struct drm_connector *connector) +{ + return container_of(connector, struct mxsfb_drm_private, connector); +} + +static int mxsfb_panel_get_modes(struct drm_connector *connector) +{ + struct mxsfb_drm_private *mxsfb = + drm_connector_to_mxsfb_drm_private(connector); + + if (mxsfb->panel) + return mxsfb->panel->funcs->get_modes(mxsfb->panel); + + return 0; +} + +static const struct +drm_connector_helper_funcs mxsfb_panel_connector_helper_funcs = { + .get_modes = mxsfb_panel_get_modes, +}; + +static enum drm_connector_status +mxsfb_panel_connector_detect(struct drm_connector *connector, bool force) +{ + struct mxsfb_drm_private *mxsfb = + drm_connector_to_mxsfb_drm_private(connector); + + if (mxsfb->panel) + return connector_status_connected; + + return connector_status_disconnected; +} + +static void mxsfb_panel_connector_destroy(struct drm_connector *connector) +{ + struct mxsfb_drm_private *mxsfb = + drm_connector_to_mxsfb_drm_private(connector); + + if (mxsfb->panel) + drm_panel_detach(mxsfb->panel); + + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static const struct drm_connector_funcs mxsfb_panel_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = mxsfb_panel_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = mxsfb_panel_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int mxsfb_attach_endpoint(struct drm_device *drm, + const struct of_endpoint *ep) +{ + struct mxsfb_drm_private *mxsfb = drm->dev_private; + struct device_node *np; + struct drm_panel *panel; + int ret = -EPROBE_DEFER; + + np = of_graph_get_remote_port_parent(ep->local_node); + panel = of_drm_find_panel(np); + of_node_put(np); + + if (!panel) + return -EPROBE_DEFER; + + mxsfb->connector.dpms = DRM_MODE_DPMS_OFF; + mxsfb->connector.polled = 0; + drm_connector_helper_add(&mxsfb->connector, + &mxsfb_panel_connector_helper_funcs); + ret = drm_connector_init(drm, &mxsfb->connector, + &mxsfb_panel_connector_funcs, + DRM_MODE_CONNECTOR_Unknown); + if (!ret) + mxsfb->panel = panel; + + return ret; +} + +int mxsfb_create_output(struct drm_device *drm) +{ + struct device_node *ep_np = NULL; + struct of_endpoint ep; + int ret; + + for_each_endpoint_of_node(drm->dev->of_node, ep_np) { + ret = of_graph_parse_endpoint(ep_np, &ep); + if (!ret) + ret = mxsfb_attach_endpoint(drm, &ep); + + if (ret) { + of_node_put(ep_np); + return ret; + } + } + + return 0; +} diff --git a/drivers/gpu/drm/mxsfb/mxsfb_regs.h b/drivers/gpu/drm/mxsfb/mxsfb_regs.h new file mode 100644 index 000000000000..31d62cd0d3d7 --- /dev/null +++ b/drivers/gpu/drm/mxsfb/mxsfb_regs.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Juergen Beisert, Pengutronix + * Copyright (C) 2016 Marek Vasut <marex@denx.de> + * + * i.MX23/i.MX28/i.MX6SX MXSFB LCD controller driver. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __MXSFB_REGS_H__ +#define __MXSFB_REGS_H__ + +#define REG_SET 4 +#define REG_CLR 8 + +#define LCDC_CTRL 0x00 +#define LCDC_CTRL1 0x10 +#define LCDC_V3_TRANSFER_COUNT 0x20 +#define LCDC_V4_TRANSFER_COUNT 0x30 +#define LCDC_V4_CUR_BUF 0x40 +#define LCDC_V4_NEXT_BUF 0x50 +#define LCDC_V3_CUR_BUF 0x30 +#define LCDC_V3_NEXT_BUF 0x40 +#define LCDC_VDCTRL0 0x70 +#define LCDC_VDCTRL1 0x80 +#define LCDC_VDCTRL2 0x90 +#define LCDC_VDCTRL3 0xa0 +#define LCDC_VDCTRL4 0xb0 +#define LCDC_V4_DEBUG0 0x1d0 +#define LCDC_V3_DEBUG0 0x1f0 + +#define CTRL_SFTRST (1 << 31) +#define CTRL_CLKGATE (1 << 30) +#define CTRL_BYPASS_COUNT (1 << 19) +#define CTRL_VSYNC_MODE (1 << 18) +#define CTRL_DOTCLK_MODE (1 << 17) +#define CTRL_DATA_SELECT (1 << 16) +#define CTRL_SET_BUS_WIDTH(x) (((x) & 0x3) << 10) +#define CTRL_GET_BUS_WIDTH(x) (((x) >> 10) & 0x3) +#define CTRL_SET_WORD_LENGTH(x) (((x) & 0x3) << 8) +#define CTRL_GET_WORD_LENGTH(x) (((x) >> 8) & 0x3) +#define CTRL_MASTER (1 << 5) +#define CTRL_DF16 (1 << 3) +#define CTRL_DF18 (1 << 2) +#define CTRL_DF24 (1 << 1) +#define CTRL_RUN (1 << 0) + +#define CTRL1_FIFO_CLEAR (1 << 21) +#define CTRL1_SET_BYTE_PACKAGING(x) (((x) & 0xf) << 16) +#define CTRL1_GET_BYTE_PACKAGING(x) (((x) >> 16) & 0xf) +#define CTRL1_CUR_FRAME_DONE_IRQ_EN (1 << 13) +#define CTRL1_CUR_FRAME_DONE_IRQ (1 << 9) + +#define TRANSFER_COUNT_SET_VCOUNT(x) (((x) & 0xffff) << 16) +#define TRANSFER_COUNT_GET_VCOUNT(x) (((x) >> 16) & 0xffff) +#define TRANSFER_COUNT_SET_HCOUNT(x) ((x) & 0xffff) +#define TRANSFER_COUNT_GET_HCOUNT(x) ((x) & 0xffff) + +#define VDCTRL0_ENABLE_PRESENT (1 << 28) +#define VDCTRL0_VSYNC_ACT_HIGH (1 << 27) +#define VDCTRL0_HSYNC_ACT_HIGH (1 << 26) +#define VDCTRL0_DOTCLK_ACT_FALLING (1 << 25) +#define VDCTRL0_ENABLE_ACT_HIGH (1 << 24) +#define VDCTRL0_VSYNC_PERIOD_UNIT (1 << 21) +#define VDCTRL0_VSYNC_PULSE_WIDTH_UNIT (1 << 20) +#define VDCTRL0_HALF_LINE (1 << 19) +#define VDCTRL0_HALF_LINE_MODE (1 << 18) +#define VDCTRL0_SET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff) +#define VDCTRL0_GET_VSYNC_PULSE_WIDTH(x) ((x) & 0x3ffff) + +#define VDCTRL2_SET_HSYNC_PERIOD(x) ((x) & 0x3ffff) +#define VDCTRL2_GET_HSYNC_PERIOD(x) ((x) & 0x3ffff) + +#define VDCTRL3_MUX_SYNC_SIGNALS (1 << 29) +#define VDCTRL3_VSYNC_ONLY (1 << 28) +#define SET_HOR_WAIT_CNT(x) (((x) & 0xfff) << 16) +#define GET_HOR_WAIT_CNT(x) (((x) >> 16) & 0xfff) +#define SET_VERT_WAIT_CNT(x) ((x) & 0xffff) +#define GET_VERT_WAIT_CNT(x) ((x) & 0xffff) + +#define VDCTRL4_SET_DOTCLK_DLY(x) (((x) & 0x7) << 29) /* v4 only */ +#define VDCTRL4_GET_DOTCLK_DLY(x) (((x) >> 29) & 0x7) /* v4 only */ +#define VDCTRL4_SYNC_SIGNALS_ON (1 << 18) +#define SET_DOTCLK_H_VALID_DATA_CNT(x) ((x) & 0x3ffff) + +#define DEBUG0_HSYNC (1 < 26) +#define DEBUG0_VSYNC (1 < 25) + +#define MXSFB_MIN_XRES 120 +#define MXSFB_MIN_YRES 120 +#define MXSFB_MAX_XRES 0xffff +#define MXSFB_MAX_YRES 0xffff + +#define RED 0 +#define GREEN 1 +#define BLUE 2 +#define TRANSP 3 + +#define STMLCDIF_8BIT 1 /* pixel data bus to the display is of 8 bit width */ +#define STMLCDIF_16BIT 0 /* pixel data bus to the display is of 16 bit width */ +#define STMLCDIF_18BIT 2 /* pixel data bus to the display is of 18 bit width */ +#define STMLCDIF_24BIT 3 /* pixel data bus to the display is of 24 bit width */ + +#define MXSFB_SYNC_DATA_ENABLE_HIGH_ACT (1 << 6) +#define MXSFB_SYNC_DOTCLK_FALLING_ACT (1 << 7) /* negative edge sampling */ + +#endif /* __MXSFB_REGS_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c index 5e2c5685b4dd..8b1ca4add2ed 100644 --- a/drivers/gpu/drm/nouveau/nouveau_backlight.c +++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c @@ -30,12 +30,37 @@ * Register locations derived from NVClock by Roderick Colenbrander */ +#include <linux/apple-gmux.h> #include <linux/backlight.h> +#include <linux/idr.h> #include "nouveau_drv.h" #include "nouveau_reg.h" #include "nouveau_encoder.h" +static struct ida bl_ida; +#define BL_NAME_SIZE 15 // 12 for name + 2 for digits + 1 for '\0' + +struct backlight_connector { + struct list_head head; + int id; +}; + +static bool +nouveau_get_backlight_name(char backlight_name[BL_NAME_SIZE], struct backlight_connector + *connector) +{ + const int nb = ida_simple_get(&bl_ida, 0, 0, GFP_KERNEL); + if (nb < 0 || nb >= 100) + return false; + if (nb > 0) + snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight%d", nb); + else + snprintf(backlight_name, BL_NAME_SIZE, "nv_backlight"); + connector->id = nb; + return true; +} + static int nv40_get_intensity(struct backlight_device *bd) { @@ -74,6 +99,8 @@ nv40_backlight_init(struct drm_connector *connector) struct nvif_object *device = &drm->device.object; struct backlight_properties props; struct backlight_device *bd; + struct backlight_connector bl_connector; + char backlight_name[BL_NAME_SIZE]; if (!(nvif_rd32(device, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)) return 0; @@ -81,10 +108,19 @@ nv40_backlight_init(struct drm_connector *connector) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 31; - bd = backlight_device_register("nv_backlight", connector->kdev, drm, + if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) { + NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); + return 0; + } + bd = backlight_device_register(backlight_name , connector->kdev, drm, &nv40_bl_ops, &props); - if (IS_ERR(bd)) + + if (IS_ERR(bd)) { + if (bl_connector.id > 0) + ida_simple_remove(&bl_ida, bl_connector.id); return PTR_ERR(bd); + } + list_add(&bl_connector.head, &drm->bl_connectors); drm->backlight = bd; bd->props.brightness = nv40_get_intensity(bd); backlight_update_status(bd); @@ -182,6 +218,8 @@ nv50_backlight_init(struct drm_connector *connector) struct backlight_properties props; struct backlight_device *bd; const struct backlight_ops *ops; + struct backlight_connector bl_connector; + char backlight_name[BL_NAME_SIZE]; nv_encoder = find_encoder(connector, DCB_OUTPUT_LVDS); if (!nv_encoder) { @@ -203,11 +241,20 @@ nv50_backlight_init(struct drm_connector *connector) memset(&props, 0, sizeof(struct backlight_properties)); props.type = BACKLIGHT_RAW; props.max_brightness = 100; - bd = backlight_device_register("nv_backlight", connector->kdev, + if (!nouveau_get_backlight_name(backlight_name, &bl_connector)) { + NV_ERROR(drm, "Failed to retrieve a unique name for the backlight interface\n"); + return 0; + } + bd = backlight_device_register(backlight_name , connector->kdev, nv_encoder, ops, &props); - if (IS_ERR(bd)) + + if (IS_ERR(bd)) { + if (bl_connector.id > 0) + ida_simple_remove(&bl_ida, bl_connector.id); return PTR_ERR(bd); + } + list_add(&bl_connector.head, &drm->bl_connectors); drm->backlight = bd; bd->props.brightness = bd->ops->get_brightness(bd); backlight_update_status(bd); @@ -221,6 +268,13 @@ nouveau_backlight_init(struct drm_device *dev) struct nvif_device *device = &drm->device; struct drm_connector *connector; + if (apple_gmux_present()) { + NV_INFO(drm, "Apple GMUX detected: not registering Nouveau backlight interface\n"); + return 0; + } + + INIT_LIST_HEAD(&drm->bl_connectors); + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS && connector->connector_type != DRM_MODE_CONNECTOR_eDP) @@ -247,9 +301,27 @@ void nouveau_backlight_exit(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); + struct backlight_connector *connector; + + list_for_each_entry(connector, &drm->bl_connectors, head) { + if (connector->id >= 0) + ida_simple_remove(&bl_ida, connector->id); + } if (drm->backlight) { backlight_device_unregister(drm->backlight); drm->backlight = NULL; } } + +void +nouveau_backlight_ctor(void) +{ + ida_init(&bl_ida); +} + +void +nouveau_backlight_dtor(void) +{ + ida_destroy(&bl_ida); +} diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index e0c0007689e5..dd07ca140d12 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -1209,6 +1209,7 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem) nvbo->page_shift != vma->vm->mmu->lpg_shift)) { nvkm_vm_map(vma, new_mem->mm_node); } else { + WARN_ON(ttm_bo_wait(bo, false, false)); nvkm_vm_unmap(vma); } } diff --git a/drivers/gpu/drm/nouveau/nouveau_display.h b/drivers/gpu/drm/nouveau/nouveau_display.h index 330fe0fc5c11..4a75df06c139 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.h +++ b/drivers/gpu/drm/nouveau/nouveau_display.h @@ -91,6 +91,8 @@ int nouveau_crtc_set_config(struct drm_mode_set *set); #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT extern int nouveau_backlight_init(struct drm_device *); extern void nouveau_backlight_exit(struct drm_device *); +extern void nouveau_backlight_ctor(void); +extern void nouveau_backlight_dtor(void); #else static inline int nouveau_backlight_init(struct drm_device *dev) @@ -101,6 +103,14 @@ nouveau_backlight_init(struct drm_device *dev) static inline void nouveau_backlight_exit(struct drm_device *dev) { } + +static inline void +nouveau_backlight_ctor(void) { +} + +static inline void +nouveau_backlight_dtor(void) { +} #endif struct drm_framebuffer * diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 064a925ed69a..59348fc41c77 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -1122,6 +1122,7 @@ nouveau_drm_init(void) #endif nouveau_register_dsm_handler(); + nouveau_backlight_ctor(); return drm_pci_init(&driver_pci, &nouveau_drm_pci_driver); } @@ -1132,6 +1133,7 @@ nouveau_drm_exit(void) return; drm_pci_exit(&driver_pci, &nouveau_drm_pci_driver); + nouveau_backlight_dtor(); nouveau_unregister_dsm_handler(); #ifdef CONFIG_NOUVEAU_PLATFORM_DRIVER diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 9730c0ef6c6a..8d5ed5bfdacb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h @@ -163,6 +163,7 @@ struct nouveau_drm { struct nvbios vbios; struct nouveau_display *display; struct backlight_device *backlight; + struct list_head bl_connectors; struct work_struct hpd_work; #ifdef CONFIG_ACPI struct notifier_block acpi_nb; diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index 7a1aa9161982..2c2c64507661 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1726,6 +1726,11 @@ nv50_head_core_set(struct nv50_head *head, struct nv50_head_atom *asyh) evo_data(push, asyh->core.handle); evo_mthd(push, 0x08c0 + head->base.index * 0x400, 1); evo_data(push, (asyh->core.y << 16) | asyh->core.x); + /* EVO will complain with INVALID_STATE if we have an + * active cursor and (re)specify HeadSetContextDmaIso + * without also updating HeadSetOffsetCursor. + */ + asyh->set.curs = asyh->curs.visible; } else if (core->base.user.oclass < GF110_DISP_CORE_CHANNEL_DMA) { evo_mthd(push, 0x0860 + head->base.index * 0x400, 1); diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c index 2cbcffe78c3e..cceda959b47c 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/device/base.c @@ -2241,6 +2241,35 @@ nv134_chipset = { .fifo = gp100_fifo_new, }; +static const struct nvkm_device_chip +nv136_chipset = { + .name = "GP106", + .bar = gf100_bar_new, + .bios = nvkm_bios_new, + .bus = gf100_bus_new, + .devinit = gm200_devinit_new, + .fb = gp102_fb_new, + .fuse = gm107_fuse_new, + .gpio = gk104_gpio_new, + .i2c = gm200_i2c_new, + .ibus = gm200_ibus_new, + .imem = nv50_instmem_new, + .ltc = gp100_ltc_new, + .mc = gp100_mc_new, + .mmu = gf100_mmu_new, + .pci = gp100_pci_new, + .pmu = gp102_pmu_new, + .timer = gk20a_timer_new, + .top = gk104_top_new, + .ce[0] = gp102_ce_new, + .ce[1] = gp102_ce_new, + .ce[2] = gp102_ce_new, + .ce[3] = gp102_ce_new, + .disp = gp102_disp_new, + .dma = gf119_dma_new, + .fifo = gp100_fifo_new, +}; + static int nvkm_device_event_ctor(struct nvkm_object *object, void *data, u32 size, struct nvkm_notify *notify) @@ -2677,6 +2706,7 @@ nvkm_device_ctor(const struct nvkm_device_func *func, case 0x130: device->chip = &nv130_chipset; break; case 0x132: device->chip = &nv132_chipset; break; case 0x134: device->chip = &nv134_chipset; break; + case 0x136: device->chip = &nv136_chipset; break; default: nvdev_error(device, "unknown chipset (%08x)\n", boot0); goto done; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c index 352a0baec84d..ec68ea9747d5 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gf100.c @@ -180,7 +180,8 @@ gf100_fifo_recover(struct gf100_fifo *fifo, struct nvkm_engine *engine, list_del_init(&chan->head); chan->killed = true; - fifo->recover.mask |= 1ULL << engine->subdev.index; + if (engine != &fifo->base.engine) + fifo->recover.mask |= 1ULL << engine->subdev.index; schedule_work(&fifo->recover.work); } diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c index 103c0afaaa6d..38c0910722c0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -743,14 +743,14 @@ gk104_fifo_fault_engine[] = { { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, { 0x06, "SCHED" }, - { 0x07, "HOST0" }, - { 0x08, "HOST1" }, - { 0x09, "HOST2" }, - { 0x0a, "HOST3" }, - { 0x0b, "HOST4" }, - { 0x0c, "HOST5" }, - { 0x0d, "HOST6" }, - { 0x0e, "HOST7" }, + { 0x07, "HOST0", NULL, NVKM_ENGINE_FIFO }, + { 0x08, "HOST1", NULL, NVKM_ENGINE_FIFO }, + { 0x09, "HOST2", NULL, NVKM_ENGINE_FIFO }, + { 0x0a, "HOST3", NULL, NVKM_ENGINE_FIFO }, + { 0x0b, "HOST4", NULL, NVKM_ENGINE_FIFO }, + { 0x0c, "HOST5", NULL, NVKM_ENGINE_FIFO }, + { 0x0d, "HOST6", NULL, NVKM_ENGINE_FIFO }, + { 0x0e, "HOST7", NULL, NVKM_ENGINE_FIFO }, { 0x0f, "HOSTSR" }, { 0x10, "MSVLD", NULL, NVKM_ENGINE_MSVLD }, { 0x11, "MSPPP", NULL, NVKM_ENGINE_MSPPP }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c index bd1ff877aa06..29c080683b32 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gm107.c @@ -32,14 +32,14 @@ gm107_fifo_fault_engine[] = { { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, { 0x06, "SCHED" }, - { 0x07, "HOST0" }, - { 0x08, "HOST1" }, - { 0x09, "HOST2" }, - { 0x0a, "HOST3" }, - { 0x0b, "HOST4" }, - { 0x0c, "HOST5" }, - { 0x0d, "HOST6" }, - { 0x0e, "HOST7" }, + { 0x07, "HOST0", NULL, NVKM_ENGINE_FIFO }, + { 0x08, "HOST1", NULL, NVKM_ENGINE_FIFO }, + { 0x09, "HOST2", NULL, NVKM_ENGINE_FIFO }, + { 0x0a, "HOST3", NULL, NVKM_ENGINE_FIFO }, + { 0x0b, "HOST4", NULL, NVKM_ENGINE_FIFO }, + { 0x0c, "HOST5", NULL, NVKM_ENGINE_FIFO }, + { 0x0d, "HOST6", NULL, NVKM_ENGINE_FIFO }, + { 0x0e, "HOST7", NULL, NVKM_ENGINE_FIFO }, { 0x0f, "HOSTSR" }, { 0x13, "PERF" }, { 0x17, "PMU" }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c index eff83f7fb705..b2635aea9f6e 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gp100.c @@ -30,17 +30,17 @@ gp100_fifo_fault_engine[] = { { 0x03, "IFB", NULL, NVKM_ENGINE_IFB }, { 0x04, "BAR1", NULL, NVKM_SUBDEV_BAR }, { 0x05, "BAR2", NULL, NVKM_SUBDEV_INSTMEM }, - { 0x06, "HOST0" }, - { 0x07, "HOST1" }, - { 0x08, "HOST2" }, - { 0x09, "HOST3" }, - { 0x0a, "HOST4" }, - { 0x0b, "HOST5" }, - { 0x0c, "HOST6" }, - { 0x0d, "HOST7" }, - { 0x0e, "HOST8" }, - { 0x0f, "HOST9" }, - { 0x10, "HOST10" }, + { 0x06, "HOST0", NULL, NVKM_ENGINE_FIFO }, + { 0x07, "HOST1", NULL, NVKM_ENGINE_FIFO }, + { 0x08, "HOST2", NULL, NVKM_ENGINE_FIFO }, + { 0x09, "HOST3", NULL, NVKM_ENGINE_FIFO }, + { 0x0a, "HOST4", NULL, NVKM_ENGINE_FIFO }, + { 0x0b, "HOST5", NULL, NVKM_ENGINE_FIFO }, + { 0x0c, "HOST6", NULL, NVKM_ENGINE_FIFO }, + { 0x0d, "HOST7", NULL, NVKM_ENGINE_FIFO }, + { 0x0e, "HOST8", NULL, NVKM_ENGINE_FIFO }, + { 0x0f, "HOST9", NULL, NVKM_ENGINE_FIFO }, + { 0x10, "HOST10", NULL, NVKM_ENGINE_FIFO }, { 0x13, "PERF" }, { 0x17, "PMU" }, { 0x18, "PTP" }, diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c index 60a1b5c8214b..f65a5b0a1a4d 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/gr/gf100.c @@ -1041,6 +1041,13 @@ gf100_gr_trap_tpc(struct gf100_gr *gr, int gpc, int tpc) stat &= ~0x00000008; } + if (stat & 0x00000010) { + u32 trap = nvkm_rd32(device, TPC_UNIT(gpc, tpc, 0x0430)); + nvkm_error(subdev, "GPC%d/TPC%d/MPC: %08x\n", gpc, tpc, trap); + nvkm_wr32(device, TPC_UNIT(gpc, tpc, 0x0430), 0xc0000000); + stat &= ~0x00000010; + } + if (stat) { nvkm_error(subdev, "GPC%d/TPC%d/%08x: unknown\n", gpc, tpc, stat); } @@ -1258,7 +1265,7 @@ gf100_gr_ctxctl_isr(struct gf100_gr *gr) struct nvkm_device *device = subdev->device; u32 stat = nvkm_rd32(device, 0x409c18); - if (stat & 0x00000001) { + if (!gr->firmware && (stat & 0x00000001)) { u32 code = nvkm_rd32(device, 0x409814); if (code == E_BAD_FWMTHD) { u32 class = nvkm_rd32(device, 0x409808); @@ -1270,15 +1277,14 @@ gf100_gr_ctxctl_isr(struct gf100_gr *gr) nvkm_error(subdev, "FECS MTHD subc %d class %04x " "mthd %04x data %08x\n", subc, class, mthd, data); - - nvkm_wr32(device, 0x409c20, 0x00000001); - stat &= ~0x00000001; } else { nvkm_error(subdev, "FECS ucode error %d\n", code); } + nvkm_wr32(device, 0x409c20, 0x00000001); + stat &= ~0x00000001; } - if (stat & 0x00080000) { + if (!gr->firmware && (stat & 0x00080000)) { nvkm_error(subdev, "FECS watchdog timeout\n"); gf100_gr_ctxctl_debug(gr); nvkm_wr32(device, 0x409c20, 0x00080000); diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c index d89e78c4e689..972370ed36f0 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/dp.c @@ -207,8 +207,11 @@ nvbios_dpcfg_match(struct nvkm_bios *bios, u16 outp, u8 pc, u8 vs, u8 pe, if (*ver >= 0x30) { const u8 vsoff[] = { 0, 4, 7, 9 }; idx = (pc * 10) + vsoff[vs] + pe; - if (*ver >= 0x40 && *hdr >= 0x12) + if (*ver >= 0x40 && *ver <= 0x41 && *hdr >= 0x12) idx += nvbios_rd08(bios, outp + 0x11) * 40; + else + if (*ver >= 0x42) + idx += nvbios_rd08(bios, outp + 0x11) * 10; } else { while ((data = nvbios_dpcfg_entry(bios, outp, ++idx, ver, hdr, cnt, len))) { diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c index 39c2a38e54f7..0c7ef250dcaf 100644 --- a/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c +++ b/drivers/gpu/drm/nouveau/nvkm/subdev/ltc/base.c @@ -47,8 +47,10 @@ nvkm_ltc_tags_clear(struct nvkm_ltc *ltc, u32 first, u32 count) BUG_ON((first > limit) || (limit >= ltc->num_tags)); + mutex_lock(<c->subdev.mutex); ltc->func->cbc_clear(ltc, first, limit); ltc->func->cbc_wait(ltc); + mutex_unlock(<c->subdev.mutex); } int diff --git a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c index 6d8f79b29af6..58276a48112e 100644 --- a/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c +++ b/drivers/gpu/drm/omapdrm/displays/encoder-tpd12s015.c @@ -234,25 +234,30 @@ static int tpd_probe(struct platform_device *pdev) if (r) return r; - gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 0, GPIOD_OUT_LOW); - if (IS_ERR(gpio)) + if (IS_ERR(gpio)) { + r = PTR_ERR(gpio); goto err_gpio; + } ddata->ct_cp_hpd_gpio = gpio; gpio = devm_gpiod_get_index_optional(&pdev->dev, NULL, 1, GPIOD_OUT_LOW); - if (IS_ERR(gpio)) + if (IS_ERR(gpio)) { + r = PTR_ERR(gpio); goto err_gpio; + } ddata->ls_oe_gpio = gpio; gpio = devm_gpiod_get_index(&pdev->dev, NULL, 2, GPIOD_IN); - if (IS_ERR(gpio)) + if (IS_ERR(gpio)) { + r = PTR_ERR(gpio); goto err_gpio; + } ddata->hpd_gpio = gpio; diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 39c5312b466c..fdc83cbcde61 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -267,13 +267,15 @@ cleanup: } static int omap_modeset_create_crtc(struct drm_device *dev, int id, - enum omap_channel channel) + enum omap_channel channel, + u32 possible_crtcs) { struct omap_drm_private *priv = dev->dev_private; struct drm_plane *plane; struct drm_crtc *crtc; - plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_PRIMARY); + plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_PRIMARY, + possible_crtcs); if (IS_ERR(plane)) return PTR_ERR(plane); @@ -309,6 +311,7 @@ static int omap_modeset_init(struct drm_device *dev) int num_crtcs; int i, id = 0; int ret; + u32 possible_crtcs; drm_mode_config_init(dev); @@ -325,6 +328,7 @@ static int omap_modeset_init(struct drm_device *dev) * We use the num_crtc argument to limit the number of crtcs we create. */ num_crtcs = min3(num_crtc, num_mgrs, num_ovls); + possible_crtcs = (1 << num_crtcs) - 1; dssdev = NULL; @@ -388,7 +392,8 @@ static int omap_modeset_init(struct drm_device *dev) * allocated crtc, we create a new crtc for it */ if (!channel_used(dev, channel)) { - ret = omap_modeset_create_crtc(dev, id, channel); + ret = omap_modeset_create_crtc(dev, id, channel, + possible_crtcs); if (ret < 0) { dev_err(dev->dev, "could not create CRTC (channel %u)\n", @@ -418,7 +423,8 @@ static int omap_modeset_init(struct drm_device *dev) return -ENOMEM; } - ret = omap_modeset_create_crtc(dev, id, i); + ret = omap_modeset_create_crtc(dev, id, i, + possible_crtcs); if (ret < 0) { dev_err(dev->dev, "could not create CRTC (channel %u)\n", i); @@ -432,7 +438,8 @@ static int omap_modeset_init(struct drm_device *dev) for (; id < num_ovls; id++) { struct drm_plane *plane; - plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_OVERLAY); + plane = omap_plane_init(dev, id, DRM_PLANE_TYPE_OVERLAY, + possible_crtcs); if (IS_ERR(plane)) return PTR_ERR(plane); diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 4c51135eb9a6..7d9dd5400cef 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -157,7 +157,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev, int omap_crtc_wait_pending(struct drm_crtc *crtc); struct drm_plane *omap_plane_init(struct drm_device *dev, - int id, enum drm_plane_type type); + int id, enum drm_plane_type type, + u32 possible_crtcs); void omap_plane_install_properties(struct drm_plane *plane, struct drm_mode_object *obj); diff --git a/drivers/gpu/drm/omapdrm/omap_plane.c b/drivers/gpu/drm/omapdrm/omap_plane.c index 9c43cb481e62..82b2c23d6769 100644 --- a/drivers/gpu/drm/omapdrm/omap_plane.c +++ b/drivers/gpu/drm/omapdrm/omap_plane.c @@ -356,9 +356,9 @@ static const uint32_t error_irqs[] = { /* initialize plane */ struct drm_plane *omap_plane_init(struct drm_device *dev, - int id, enum drm_plane_type type) + int id, enum drm_plane_type type, + u32 possible_crtcs) { - struct omap_drm_private *priv = dev->dev_private; struct drm_plane *plane; struct omap_plane *omap_plane; int ret; @@ -381,7 +381,7 @@ struct drm_plane *omap_plane_init(struct drm_device *dev, omap_plane->error_irq.irq = omap_plane_error_irq; omap_irq_register(dev, &omap_plane->error_irq); - ret = drm_universal_plane_init(dev, plane, (1 << priv->num_crtcs) - 1, + ret = drm_universal_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs, omap_plane->formats, omap_plane->nformats, type, NULL); if (ret < 0) diff --git a/drivers/gpu/drm/panel/panel-simple.c b/drivers/gpu/drm/panel/panel-simple.c index 113db3c4a633..06aaf79de8c8 100644 --- a/drivers/gpu/drm/panel/panel-simple.c +++ b/drivers/gpu/drm/panel/panel-simple.c @@ -120,7 +120,7 @@ static int panel_simple_get_fixed_modes(struct panel_simple *panel) mode->type |= DRM_MODE_TYPE_DRIVER; - if (panel->desc->num_modes == 1) + if (panel->desc->num_timings == 1) mode->type |= DRM_MODE_TYPE_PREFERRED; drm_mode_probed_add(connector, mode); @@ -555,6 +555,91 @@ static const struct panel_desc auo_b133htn01 = { }, }; +static const struct display_timing auo_g133han01_timings = { + .pixelclock = { 134000000, 141200000, 149000000 }, + .hactive = { 1920, 1920, 1920 }, + .hfront_porch = { 39, 58, 77 }, + .hback_porch = { 59, 88, 117 }, + .hsync_len = { 28, 42, 56 }, + .vactive = { 1080, 1080, 1080 }, + .vfront_porch = { 3, 8, 11 }, + .vback_porch = { 5, 14, 19 }, + .vsync_len = { 4, 14, 19 }, +}; + +static const struct panel_desc auo_g133han01 = { + .timings = &auo_g133han01_timings, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 293, + .height = 165, + }, + .delay = { + .prepare = 200, + .enable = 50, + .disable = 50, + .unprepare = 1000, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA, +}; + +static const struct display_timing auo_g185han01_timings = { + .pixelclock = { 120000000, 144000000, 175000000 }, + .hactive = { 1920, 1920, 1920 }, + .hfront_porch = { 18, 60, 74 }, + .hback_porch = { 12, 44, 54 }, + .hsync_len = { 10, 24, 32 }, + .vactive = { 1080, 1080, 1080 }, + .vfront_porch = { 6, 10, 40 }, + .vback_porch = { 2, 5, 20 }, + .vsync_len = { 2, 5, 20 }, +}; + +static const struct panel_desc auo_g185han01 = { + .timings = &auo_g185han01_timings, + .num_timings = 1, + .bpc = 8, + .size = { + .width = 409, + .height = 230, + }, + .delay = { + .prepare = 50, + .enable = 200, + .disable = 110, + .unprepare = 1000, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + +static const struct drm_display_mode auo_t215hvn01_mode = { + .clock = 148800, + .hdisplay = 1920, + .hsync_start = 1920 + 88, + .hsync_end = 1920 + 88 + 44, + .htotal = 1920 + 88 + 44 + 148, + .vdisplay = 1080, + .vsync_start = 1080 + 4, + .vsync_end = 1080 + 4 + 5, + .vtotal = 1080 + 4 + 5 + 36, + .vrefresh = 60, +}; + +static const struct panel_desc auo_t215hvn01 = { + .modes = &auo_t215hvn01_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 430, + .height = 270, + }, + .delay = { + .disable = 5, + .unprepare = 1000, + } +}; + static const struct drm_display_mode avic_tm070ddh03_mode = { .clock = 51200, .hdisplay = 1024, @@ -583,6 +668,30 @@ static const struct panel_desc avic_tm070ddh03 = { }, }; +static const struct drm_display_mode chunghwa_claa070wp03xg_mode = { + .clock = 66770, + .hdisplay = 800, + .hsync_start = 800 + 49, + .hsync_end = 800 + 49 + 33, + .htotal = 800 + 49 + 33 + 17, + .vdisplay = 1280, + .vsync_start = 1280 + 1, + .vsync_end = 1280 + 1 + 7, + .vtotal = 1280 + 1 + 7 + 15, + .vrefresh = 60, + .flags = DRM_MODE_FLAG_NVSYNC | DRM_MODE_FLAG_NHSYNC, +}; + +static const struct panel_desc chunghwa_claa070wp03xg = { + .modes = &chunghwa_claa070wp03xg_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 94, + .height = 150, + }, +}; + static const struct drm_display_mode chunghwa_claa101wa01a_mode = { .clock = 72070, .hdisplay = 1366, @@ -877,27 +986,31 @@ static const struct panel_desc innolux_g101ice_l01 = { .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, }; -static const struct drm_display_mode innolux_g121i1_l01_mode = { - .clock = 71000, - .hdisplay = 1280, - .hsync_start = 1280 + 64, - .hsync_end = 1280 + 64 + 32, - .htotal = 1280 + 64 + 32 + 64, - .vdisplay = 800, - .vsync_start = 800 + 9, - .vsync_end = 800 + 9 + 6, - .vtotal = 800 + 9 + 6 + 9, - .vrefresh = 60, +static const struct display_timing innolux_g121i1_l01_timing = { + .pixelclock = { 67450000, 71000000, 74550000 }, + .hactive = { 1280, 1280, 1280 }, + .hfront_porch = { 40, 80, 160 }, + .hback_porch = { 39, 79, 159 }, + .hsync_len = { 1, 1, 1 }, + .vactive = { 800, 800, 800 }, + .vfront_porch = { 5, 11, 100 }, + .vback_porch = { 4, 11, 99 }, + .vsync_len = { 1, 1, 1 }, }; static const struct panel_desc innolux_g121i1_l01 = { - .modes = &innolux_g121i1_l01_mode, - .num_modes = 1, + .timings = &innolux_g121i1_l01_timing, + .num_timings = 1, .bpc = 6, .size = { .width = 261, .height = 163, }, + .delay = { + .enable = 200, + .disable = 20, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, }; static const struct drm_display_mode innolux_g121x1_l03_mode = { @@ -1164,6 +1277,29 @@ static const struct panel_desc nec_nl4827hc19_05b = { .bus_flags = DRM_BUS_FLAG_PIXDATA_POSEDGE, }; +static const struct drm_display_mode nvd_9128_mode = { + .clock = 29500, + .hdisplay = 800, + .hsync_start = 800 + 130, + .hsync_end = 800 + 130 + 98, + .htotal = 800 + 0 + 130 + 98, + .vdisplay = 480, + .vsync_start = 480 + 10, + .vsync_end = 480 + 10 + 50, + .vtotal = 480 + 0 + 10 + 50, +}; + +static const struct panel_desc nvd_9128 = { + .modes = &nvd_9128_mode, + .num_modes = 1, + .bpc = 8, + .size = { + .width = 156, + .height = 88, + }, + .bus_format = MEDIA_BUS_FMT_RGB888_1X7X4_SPWG, +}; + static const struct display_timing okaya_rs800480t_7x0gp_timing = { .pixelclock = { 30000000, 30000000, 40000000 }, .hactive = { 800, 800, 800 }, @@ -1409,6 +1545,7 @@ static const struct drm_display_mode sharp_lq123p1jx31_mode = { static const struct panel_desc sharp_lq123p1jx31 = { .modes = &sharp_lq123p1jx31_mode, .num_modes = 1, + .bpc = 8, .size = { .width = 259, .height = 173, @@ -1420,6 +1557,30 @@ static const struct panel_desc sharp_lq123p1jx31 = { }, }; +static const struct drm_display_mode sharp_lq150x1lg11_mode = { + .clock = 71100, + .hdisplay = 1024, + .hsync_start = 1024 + 168, + .hsync_end = 1024 + 168 + 64, + .htotal = 1024 + 168 + 64 + 88, + .vdisplay = 768, + .vsync_start = 768 + 37, + .vsync_end = 768 + 37 + 2, + .vtotal = 768 + 37 + 2 + 8, + .vrefresh = 60, +}; + +static const struct panel_desc sharp_lq150x1lg11 = { + .modes = &sharp_lq150x1lg11_mode, + .num_modes = 1, + .bpc = 6, + .size = { + .width = 304, + .height = 228, + }, + .bus_format = MEDIA_BUS_FMT_RGB565_1X16, +}; + static const struct drm_display_mode shelly_sca07010_bfn_lnn_mode = { .clock = 33300, .hdisplay = 800, @@ -1575,9 +1736,21 @@ static const struct of_device_id platform_of_match[] = { .compatible = "auo,b133xtn01", .data = &auo_b133xtn01, }, { + .compatible = "auo,g133han01", + .data = &auo_g133han01, + }, { + .compatible = "auo,g185han01", + .data = &auo_g185han01, + }, { + .compatible = "auo,t215hvn01", + .data = &auo_t215hvn01, + }, { .compatible = "avic,tm070ddh03", .data = &avic_tm070ddh03, }, { + .compatible = "chunghwa,claa070wp03xg", + .data = &chunghwa_claa070wp03xg, + }, { .compatible = "chunghwa,claa101wa01a", .data = &chunghwa_claa101wa01a }, { @@ -1653,6 +1826,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "nec,nl4827hc19-05b", .data = &nec_nl4827hc19_05b, }, { + .compatible = "nvd,9128", + .data = &nvd_9128, + }, { .compatible = "okaya,rs800480t-7x0gp", .data = &okaya_rs800480t_7x0gp, }, { @@ -1683,6 +1859,9 @@ static const struct of_device_id platform_of_match[] = { .compatible = "sharp,lq123p1jx31", .data = &sharp_lq123p1jx31, }, { + .compatible = "sharp,lq150x1lg11", + .data = &sharp_lq150x1lg11, + }, { .compatible = "shelly,sca07010-bfn-lnn", .data = &shelly_sca07010_bfn_lnn, }, { diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c index 2a10e24b34b1..fb16070b266e 100644 --- a/drivers/gpu/drm/radeon/radeon_cursor.c +++ b/drivers/gpu/drm/radeon/radeon_cursor.c @@ -90,6 +90,9 @@ static void radeon_show_cursor(struct drm_crtc *crtc) struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); struct radeon_device *rdev = crtc->dev->dev_private; + if (radeon_crtc->cursor_out_of_bounds) + return; + if (ASIC_IS_DCE4(rdev)) { WREG32(EVERGREEN_CUR_SURFACE_ADDRESS_HIGH + radeon_crtc->crtc_offset, upper_32_bits(radeon_crtc->cursor_addr)); @@ -143,21 +146,25 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) int xorigin = 0, yorigin = 0; int w = radeon_crtc->cursor_width; + radeon_crtc->cursor_x = x; + radeon_crtc->cursor_y = y; + if (ASIC_IS_AVIVO(rdev)) { /* avivo cursor are offset into the total surface */ x += crtc->x; y += crtc->y; } - DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); - if (x < 0) { + if (x < 0) xorigin = min(-x, radeon_crtc->max_cursor_width - 1); - x = 0; - } - if (y < 0) { + if (y < 0) yorigin = min(-y, radeon_crtc->max_cursor_height - 1); - y = 0; + + if (!ASIC_IS_AVIVO(rdev)) { + x += crtc->x; + y += crtc->y; } + DRM_DEBUG("x %d y %d c->x %d c->y %d\n", x, y, crtc->x, crtc->y); /* fixed on DCE6 and newer */ if (ASIC_IS_AVIVO(rdev) && !ASIC_IS_DCE6(rdev)) { @@ -180,27 +187,31 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) if (i > 1) { int cursor_end, frame_end; - cursor_end = x - xorigin + w; + cursor_end = x + w; frame_end = crtc->x + crtc->mode.crtc_hdisplay; if (cursor_end >= frame_end) { w = w - (cursor_end - frame_end); if (!(frame_end & 0x7f)) w--; - } else { - if (!(cursor_end & 0x7f)) - w--; + } else if (cursor_end <= 0) { + goto out_of_bounds; + } else if (!(cursor_end & 0x7f)) { + w--; } if (w <= 0) { - w = 1; - cursor_end = x - xorigin + w; - if (!(cursor_end & 0x7f)) { - x--; - WARN_ON_ONCE(x < 0); - } + goto out_of_bounds; } } } + if (x <= (crtc->x - w) || y <= (crtc->y - radeon_crtc->cursor_height) || + x >= (crtc->x + crtc->mode.crtc_hdisplay) || + y >= (crtc->y + crtc->mode.crtc_vdisplay)) + goto out_of_bounds; + + x += xorigin; + y += yorigin; + if (ASIC_IS_DCE4(rdev)) { WREG32(EVERGREEN_CUR_POSITION + radeon_crtc->crtc_offset, (x << 16) | y); WREG32(EVERGREEN_CUR_HOT_SPOT + radeon_crtc->crtc_offset, (xorigin << 16) | yorigin); @@ -212,6 +223,9 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) WREG32(AVIVO_D1CUR_SIZE + radeon_crtc->crtc_offset, ((w - 1) << 16) | (radeon_crtc->cursor_height - 1)); } else { + x -= crtc->x; + y -= crtc->y; + if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN) y *= 2; @@ -229,10 +243,20 @@ static int radeon_cursor_move_locked(struct drm_crtc *crtc, int x, int y) yorigin * 256); } - radeon_crtc->cursor_x = x; - radeon_crtc->cursor_y = y; + if (radeon_crtc->cursor_out_of_bounds) { + radeon_crtc->cursor_out_of_bounds = false; + if (radeon_crtc->cursor_bo) + radeon_show_cursor(crtc); + } return 0; + + out_of_bounds: + if (!radeon_crtc->cursor_out_of_bounds) { + radeon_hide_cursor(crtc); + radeon_crtc->cursor_out_of_bounds = true; + } + return 0; } int radeon_crtc_cursor_move(struct drm_crtc *crtc, @@ -297,22 +321,23 @@ int radeon_crtc_cursor_set2(struct drm_crtc *crtc, return ret; } - radeon_crtc->cursor_width = width; - radeon_crtc->cursor_height = height; - radeon_lock_cursor(crtc, true); - if (hot_x != radeon_crtc->cursor_hot_x || + if (width != radeon_crtc->cursor_width || + height != radeon_crtc->cursor_height || + hot_x != radeon_crtc->cursor_hot_x || hot_y != radeon_crtc->cursor_hot_y) { int x, y; x = radeon_crtc->cursor_x + radeon_crtc->cursor_hot_x - hot_x; y = radeon_crtc->cursor_y + radeon_crtc->cursor_hot_y - hot_y; - radeon_cursor_move_locked(crtc, x, y); - + radeon_crtc->cursor_width = width; + radeon_crtc->cursor_height = height; radeon_crtc->cursor_hot_x = hot_x; radeon_crtc->cursor_hot_y = hot_y; + + radeon_cursor_move_locked(crtc, x, y); } radeon_show_cursor(crtc); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index bb75201a24ba..f1da484864a9 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -330,6 +330,7 @@ struct radeon_crtc { u16 lut_r[256], lut_g[256], lut_b[256]; bool enabled; bool can_tile; + bool cursor_out_of_bounds; uint32_t crtc_offset; struct drm_gem_object *cursor_bo; uint64_t cursor_addr; diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 143280dc0851..ad4d7b8b8322 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -1714,6 +1714,7 @@ static int si_init_microcode(struct radeon_device *rdev) (rdev->pdev->revision == 0x80) || (rdev->pdev->revision == 0x81) || (rdev->pdev->revision == 0x83) || + (rdev->pdev->revision == 0x87) || (rdev->pdev->device == 0x6604) || (rdev->pdev->device == 0x6605)) new_smc = true; diff --git a/drivers/gpu/drm/radeon/si_dpm.c b/drivers/gpu/drm/radeon/si_dpm.c index c49934527a87..8b5e697f2549 100644 --- a/drivers/gpu/drm/radeon/si_dpm.c +++ b/drivers/gpu/drm/radeon/si_dpm.c @@ -3026,6 +3026,7 @@ static void si_apply_state_adjust_rules(struct radeon_device *rdev, (rdev->pdev->revision == 0x80) || (rdev->pdev->revision == 0x81) || (rdev->pdev->revision == 0x83) || + (rdev->pdev->revision == 0x87) || (rdev->pdev->device == 0x6604) || (rdev->pdev->device == 0x6605)) { max_sclk = 75000; diff --git a/drivers/gpu/drm/tegra/dpaux.c b/drivers/gpu/drm/tegra/dpaux.c index 059f409556d5..2fde44c3a1b3 100644 --- a/drivers/gpu/drm/tegra/dpaux.c +++ b/drivers/gpu/drm/tegra/dpaux.c @@ -539,9 +539,9 @@ static int tegra_dpaux_probe(struct platform_device *pdev) dpaux->desc.owner = THIS_MODULE; dpaux->pinctrl = devm_pinctrl_register(&pdev->dev, &dpaux->desc, dpaux); - if (!dpaux->pinctrl) { + if (IS_ERR(dpaux->pinctrl)) { dev_err(&pdev->dev, "failed to register pincontrol\n"); - return -ENODEV; + return PTR_ERR(dpaux->pinctrl); } #endif /* enable and clear all interrupts */ diff --git a/drivers/gpu/drm/tegra/gem.c b/drivers/gpu/drm/tegra/gem.c index 95e622e31931..c08e5279eeac 100644 --- a/drivers/gpu/drm/tegra/gem.c +++ b/drivers/gpu/drm/tegra/gem.c @@ -2,7 +2,7 @@ * NVIDIA Tegra DRM GEM helper functions * * Copyright (C) 2012 Sascha Hauer, Pengutronix - * Copyright (C) 2013 NVIDIA CORPORATION, All rights reserved. + * Copyright (C) 2013-2015 NVIDIA CORPORATION, All rights reserved. * * Based on the GEM/CMA helpers * @@ -36,6 +36,8 @@ static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); + *sgt = obj->sgt; + return obj->paddr; } @@ -47,23 +49,51 @@ static void *tegra_bo_mmap(struct host1x_bo *bo) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); - return obj->vaddr; + if (obj->vaddr) + return obj->vaddr; + else if (obj->gem.import_attach) + return dma_buf_vmap(obj->gem.import_attach->dmabuf); + else + return vmap(obj->pages, obj->num_pages, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); } static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) { + struct tegra_bo *obj = host1x_to_tegra_bo(bo); + + if (obj->vaddr) + return; + else if (obj->gem.import_attach) + dma_buf_vunmap(obj->gem.import_attach->dmabuf, addr); + else + vunmap(addr); } static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) { struct tegra_bo *obj = host1x_to_tegra_bo(bo); - return obj->vaddr + page * PAGE_SIZE; + if (obj->vaddr) + return obj->vaddr + page * PAGE_SIZE; + else if (obj->gem.import_attach) + return dma_buf_kmap(obj->gem.import_attach->dmabuf, page); + else + return vmap(obj->pages + page, 1, VM_MAP, + pgprot_writecombine(PAGE_KERNEL)); } static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, void *addr) { + struct tegra_bo *obj = host1x_to_tegra_bo(bo); + + if (obj->vaddr) + return; + else if (obj->gem.import_attach) + dma_buf_kunmap(obj->gem.import_attach->dmabuf, page, addr); + else + vunmap(addr); } static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) @@ -318,11 +348,6 @@ static struct tegra_bo *tegra_bo_import(struct drm_device *drm, get_dma_buf(buf); bo->sgt = dma_buf_map_attachment(attach, DMA_TO_DEVICE); - if (!bo->sgt) { - err = -ENOMEM; - goto detach; - } - if (IS_ERR(bo->sgt)) { err = PTR_ERR(bo->sgt); goto detach; diff --git a/drivers/gpu/drm/tegra/gr3d.c b/drivers/gpu/drm/tegra/gr3d.c index 0b3f2b977ba0..13f0d1b7cd98 100644 --- a/drivers/gpu/drm/tegra/gr3d.c +++ b/drivers/gpu/drm/tegra/gr3d.c @@ -268,9 +268,9 @@ static int gr3d_probe(struct platform_device *pdev) if (of_device_is_compatible(np, "nvidia,tegra30-gr3d")) { gr3d->clk_secondary = devm_clk_get(&pdev->dev, "3d2"); - if (IS_ERR(gr3d->clk)) { + if (IS_ERR(gr3d->clk_secondary)) { dev_err(&pdev->dev, "cannot get secondary clock\n"); - return PTR_ERR(gr3d->clk); + return PTR_ERR(gr3d->clk_secondary); } gr3d->rst_secondary = devm_reset_control_get(&pdev->dev, diff --git a/drivers/gpu/drm/tegra/sor.c b/drivers/gpu/drm/tegra/sor.c index 74d0540b8d4c..a8f528925009 100644 --- a/drivers/gpu/drm/tegra/sor.c +++ b/drivers/gpu/drm/tegra/sor.c @@ -349,8 +349,6 @@ static struct clk *tegra_clk_sor_brick_register(struct tegra_sor *sor, brick->hw.init = &init; clk = devm_clk_register(sor->dev, &brick->hw); - if (IS_ERR(clk)) - kfree(brick); return clk; } diff --git a/drivers/gpu/drm/vc4/Makefile b/drivers/gpu/drm/vc4/Makefile index fb77db755e0a..7757f69a8a77 100644 --- a/drivers/gpu/drm/vc4/Makefile +++ b/drivers/gpu/drm/vc4/Makefile @@ -11,6 +11,7 @@ vc4-y := \ vc4_kms.o \ vc4_gem.o \ vc4_hdmi.o \ + vc4_vec.o \ vc4_hvs.o \ vc4_irq.o \ vc4_plane.o \ diff --git a/drivers/gpu/drm/vc4/vc4_crtc.c b/drivers/gpu/drm/vc4/vc4_crtc.c index 7f08d681a74b..a0fd3e66bc4b 100644 --- a/drivers/gpu/drm/vc4/vc4_crtc.c +++ b/drivers/gpu/drm/vc4/vc4_crtc.c @@ -83,8 +83,7 @@ struct vc4_crtc_data { /* Which channel of the HVS this pixelvalve sources from. */ int hvs_channel; - enum vc4_encoder_type encoder0_type; - enum vc4_encoder_type encoder1_type; + enum vc4_encoder_type encoder_types[4]; }; #define CRTC_WRITE(offset, val) writel(val, vc4_crtc->regs + (offset)) @@ -669,6 +668,14 @@ void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id) CRTC_WRITE(PV_INTEN, 0); } +/* Must be called with the event lock held */ +bool vc4_event_pending(struct drm_crtc *crtc) +{ + struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + + return !!vc4_crtc->event; +} + static void vc4_crtc_handle_page_flip(struct vc4_crtc *vc4_crtc) { struct drm_crtc *crtc = &vc4_crtc->base; @@ -859,20 +866,26 @@ static const struct drm_crtc_helper_funcs vc4_crtc_helper_funcs = { static const struct vc4_crtc_data pv0_data = { .hvs_channel = 0, - .encoder0_type = VC4_ENCODER_TYPE_DSI0, - .encoder1_type = VC4_ENCODER_TYPE_DPI, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI0, + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_DPI, + }, }; static const struct vc4_crtc_data pv1_data = { .hvs_channel = 2, - .encoder0_type = VC4_ENCODER_TYPE_DSI1, - .encoder1_type = VC4_ENCODER_TYPE_SMI, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DSI] = VC4_ENCODER_TYPE_DSI1, + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_SMI, + }, }; static const struct vc4_crtc_data pv2_data = { .hvs_channel = 1, - .encoder0_type = VC4_ENCODER_TYPE_VEC, - .encoder1_type = VC4_ENCODER_TYPE_HDMI, + .encoder_types = { + [PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI] = VC4_ENCODER_TYPE_HDMI, + [PV_CONTROL_CLK_SELECT_VEC] = VC4_ENCODER_TYPE_VEC, + }, }; static const struct of_device_id vc4_crtc_dt_match[] = { @@ -886,17 +899,20 @@ static void vc4_set_crtc_possible_masks(struct drm_device *drm, struct drm_crtc *crtc) { struct vc4_crtc *vc4_crtc = to_vc4_crtc(crtc); + const struct vc4_crtc_data *crtc_data = vc4_crtc->data; + const enum vc4_encoder_type *encoder_types = crtc_data->encoder_types; struct drm_encoder *encoder; drm_for_each_encoder(encoder, drm) { struct vc4_encoder *vc4_encoder = to_vc4_encoder(encoder); - - if (vc4_encoder->type == vc4_crtc->data->encoder0_type) { - vc4_encoder->clock_select = 0; - encoder->possible_crtcs |= drm_crtc_mask(crtc); - } else if (vc4_encoder->type == vc4_crtc->data->encoder1_type) { - vc4_encoder->clock_select = 1; - encoder->possible_crtcs |= drm_crtc_mask(crtc); + int i; + + for (i = 0; i < ARRAY_SIZE(crtc_data->encoder_types); i++) { + if (vc4_encoder->type == encoder_types[i]) { + vc4_encoder->clock_select = i; + encoder->possible_crtcs |= drm_crtc_mask(crtc); + break; + } } } } diff --git a/drivers/gpu/drm/vc4/vc4_debugfs.c b/drivers/gpu/drm/vc4/vc4_debugfs.c index 245115d49c46..caf817bac885 100644 --- a/drivers/gpu/drm/vc4/vc4_debugfs.c +++ b/drivers/gpu/drm/vc4/vc4_debugfs.c @@ -19,6 +19,7 @@ static const struct drm_info_list vc4_debugfs_list[] = { {"bo_stats", vc4_bo_stats_debugfs, 0}, {"dpi_regs", vc4_dpi_debugfs_regs, 0}, {"hdmi_regs", vc4_hdmi_debugfs_regs, 0}, + {"vec_regs", vc4_vec_debugfs_regs, 0}, {"hvs_regs", vc4_hvs_debugfs_regs, 0}, {"crtc0_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)0}, {"crtc1_regs", vc4_crtc_debugfs_regs, 0, (void *)(uintptr_t)1}, diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c index 1dab9e5b3689..ac09ca7ff430 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.c +++ b/drivers/gpu/drm/vc4/vc4_drv.c @@ -277,12 +277,14 @@ static void vc4_drm_unbind(struct device *dev) struct drm_device *drm = platform_get_drvdata(pdev); struct vc4_dev *vc4 = to_vc4_dev(drm); + drm_dev_unregister(drm); + if (vc4->fbdev) drm_fbdev_cma_fini(vc4->fbdev); drm_mode_config_cleanup(drm); - drm_put_dev(drm); + drm_dev_unref(drm); } static const struct component_master_ops vc4_drm_ops = { @@ -292,6 +294,7 @@ static const struct component_master_ops vc4_drm_ops = { static struct platform_driver *const component_drivers[] = { &vc4_hdmi_driver, + &vc4_vec_driver, &vc4_dpi_driver, &vc4_hvs_driver, &vc4_crtc_driver, diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h index fef172804345..b5c4bb14d0d1 100644 --- a/drivers/gpu/drm/vc4/vc4_drv.h +++ b/drivers/gpu/drm/vc4/vc4_drv.h @@ -17,6 +17,7 @@ struct vc4_dev { struct vc4_crtc *crtc[3]; struct vc4_v3d *v3d; struct vc4_dpi *dpi; + struct vc4_vec *vec; struct drm_fbdev_cma *fbdev; @@ -194,6 +195,7 @@ to_vc4_plane(struct drm_plane *plane) } enum vc4_encoder_type { + VC4_ENCODER_TYPE_NONE, VC4_ENCODER_TYPE_HDMI, VC4_ENCODER_TYPE_VEC, VC4_ENCODER_TYPE_DSI0, @@ -442,6 +444,7 @@ int vc4_bo_stats_debugfs(struct seq_file *m, void *arg); extern struct platform_driver vc4_crtc_driver; int vc4_enable_vblank(struct drm_device *dev, unsigned int crtc_id); void vc4_disable_vblank(struct drm_device *dev, unsigned int crtc_id); +bool vc4_event_pending(struct drm_crtc *crtc); int vc4_crtc_debugfs_regs(struct seq_file *m, void *arg); int vc4_crtc_get_scanoutpos(struct drm_device *dev, unsigned int crtc_id, unsigned int flags, int *vpos, int *hpos, @@ -485,6 +488,10 @@ int vc4_queue_seqno_cb(struct drm_device *dev, extern struct platform_driver vc4_hdmi_driver; int vc4_hdmi_debugfs_regs(struct seq_file *m, void *unused); +/* vc4_hdmi.c */ +extern struct platform_driver vc4_vec_driver; +int vc4_vec_debugfs_regs(struct seq_file *m, void *unused); + /* vc4_irq.c */ irqreturn_t vc4_irq(int irq, void *arg); void vc4_irq_preinstall(struct drm_device *dev); diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index f31f72af8551..be8dd8262f27 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -119,17 +119,34 @@ static int vc4_atomic_commit(struct drm_device *dev, /* Make sure that any outstanding modesets have finished. */ if (nonblock) { - ret = down_trylock(&vc4->async_modeset); - if (ret) { + struct drm_crtc *crtc; + struct drm_crtc_state *crtc_state; + unsigned long flags; + bool busy = false; + + /* + * If there's an undispatched event to send then we're + * obviously still busy. If there isn't, then we can + * unconditionally wait for the semaphore because it + * shouldn't be contended (for long). + * + * This is to prevent a race where queuing a new flip + * from userspace immediately on receipt of an event + * beats our clean-up and returns EBUSY. + */ + spin_lock_irqsave(&dev->event_lock, flags); + for_each_crtc_in_state(state, crtc, crtc_state, i) + busy |= vc4_event_pending(crtc); + spin_unlock_irqrestore(&dev->event_lock, flags); + if (busy) { kfree(c); return -EBUSY; } - } else { - ret = down_interruptible(&vc4->async_modeset); - if (ret) { - kfree(c); - return ret; - } + } + ret = down_interruptible(&vc4->async_modeset); + if (ret) { + kfree(c); + return ret; } ret = drm_atomic_helper_prepare_planes(dev, state); diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h index 1aa44c2db556..39f6886b2410 100644 --- a/drivers/gpu/drm/vc4/vc4_regs.h +++ b/drivers/gpu/drm/vc4/vc4_regs.h @@ -177,8 +177,9 @@ # define PV_CONTROL_WAIT_HSTART BIT(12) # define PV_CONTROL_PIXEL_REP_MASK VC4_MASK(5, 4) # define PV_CONTROL_PIXEL_REP_SHIFT 4 -# define PV_CONTROL_CLK_SELECT_DSI_VEC 0 +# define PV_CONTROL_CLK_SELECT_DSI 0 # define PV_CONTROL_CLK_SELECT_DPI_SMI_HDMI 1 +# define PV_CONTROL_CLK_SELECT_VEC 2 # define PV_CONTROL_CLK_SELECT_MASK VC4_MASK(3, 2) # define PV_CONTROL_CLK_SELECT_SHIFT 2 # define PV_CONTROL_FIFO_CLR BIT(1) diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c new file mode 100644 index 000000000000..32bb8ef985fb --- /dev/null +++ b/drivers/gpu/drm/vc4/vc4_vec.c @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2016 Broadcom + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** + * DOC: VC4 SDTV module + */ + +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_panel.h> +#include <linux/clk.h> +#include <linux/component.h> +#include <linux/of_graph.h> +#include <linux/of_platform.h> +#include <linux/pm_runtime.h> + +#include "vc4_drv.h" +#include "vc4_regs.h" + +/* WSE Registers */ +#define VEC_WSE_RESET 0xc0 + +#define VEC_WSE_CONTROL 0xc4 +#define VEC_WSE_WSS_ENABLE BIT(7) + +#define VEC_WSE_WSS_DATA 0xc8 +#define VEC_WSE_VPS_DATA1 0xcc +#define VEC_WSE_VPS_CONTROL 0xd0 + +/* VEC Registers */ +#define VEC_REVID 0x100 + +#define VEC_CONFIG0 0x104 +#define VEC_CONFIG0_YDEL_MASK GENMASK(28, 26) +#define VEC_CONFIG0_YDEL(x) ((x) << 26) +#define VEC_CONFIG0_CDEL_MASK GENMASK(25, 24) +#define VEC_CONFIG0_CDEL(x) ((x) << 24) +#define VEC_CONFIG0_PBPR_FIL BIT(18) +#define VEC_CONFIG0_CHROMA_GAIN_MASK GENMASK(17, 16) +#define VEC_CONFIG0_CHROMA_GAIN_UNITY (0 << 16) +#define VEC_CONFIG0_CHROMA_GAIN_1_32 (1 << 16) +#define VEC_CONFIG0_CHROMA_GAIN_1_16 (2 << 16) +#define VEC_CONFIG0_CHROMA_GAIN_1_8 (3 << 16) +#define VEC_CONFIG0_CBURST_GAIN_MASK GENMASK(14, 13) +#define VEC_CONFIG0_CBURST_GAIN_UNITY (0 << 13) +#define VEC_CONFIG0_CBURST_GAIN_1_128 (1 << 13) +#define VEC_CONFIG0_CBURST_GAIN_1_64 (2 << 13) +#define VEC_CONFIG0_CBURST_GAIN_1_32 (3 << 13) +#define VEC_CONFIG0_CHRBW1 BIT(11) +#define VEC_CONFIG0_CHRBW0 BIT(10) +#define VEC_CONFIG0_SYNCDIS BIT(9) +#define VEC_CONFIG0_BURDIS BIT(8) +#define VEC_CONFIG0_CHRDIS BIT(7) +#define VEC_CONFIG0_PDEN BIT(6) +#define VEC_CONFIG0_YCDELAY BIT(4) +#define VEC_CONFIG0_RAMPEN BIT(2) +#define VEC_CONFIG0_YCDIS BIT(2) +#define VEC_CONFIG0_STD_MASK GENMASK(1, 0) +#define VEC_CONFIG0_NTSC_STD 0 +#define VEC_CONFIG0_PAL_BDGHI_STD 1 +#define VEC_CONFIG0_PAL_N_STD 3 + +#define VEC_SCHPH 0x108 +#define VEC_SOFT_RESET 0x10c +#define VEC_CLMP0_START 0x144 +#define VEC_CLMP0_END 0x148 +#define VEC_FREQ3_2 0x180 +#define VEC_FREQ1_0 0x184 + +#define VEC_CONFIG1 0x188 +#define VEC_CONFIG_VEC_RESYNC_OFF BIT(18) +#define VEC_CONFIG_RGB219 BIT(17) +#define VEC_CONFIG_CBAR_EN BIT(16) +#define VEC_CONFIG_TC_OBB BIT(15) +#define VEC_CONFIG1_OUTPUT_MODE_MASK GENMASK(12, 10) +#define VEC_CONFIG1_C_Y_CVBS (0 << 10) +#define VEC_CONFIG1_CVBS_Y_C (1 << 10) +#define VEC_CONFIG1_PR_Y_PB (2 << 10) +#define VEC_CONFIG1_RGB (4 << 10) +#define VEC_CONFIG1_Y_C_CVBS (5 << 10) +#define VEC_CONFIG1_C_CVBS_Y (6 << 10) +#define VEC_CONFIG1_C_CVBS_CVBS (7 << 10) +#define VEC_CONFIG1_DIS_CHR BIT(9) +#define VEC_CONFIG1_DIS_LUMA BIT(8) +#define VEC_CONFIG1_YCBCR_IN BIT(6) +#define VEC_CONFIG1_DITHER_TYPE_LFSR 0 +#define VEC_CONFIG1_DITHER_TYPE_COUNTER BIT(5) +#define VEC_CONFIG1_DITHER_EN BIT(4) +#define VEC_CONFIG1_CYDELAY BIT(3) +#define VEC_CONFIG1_LUMADIS BIT(2) +#define VEC_CONFIG1_COMPDIS BIT(1) +#define VEC_CONFIG1_CUSTOM_FREQ BIT(0) + +#define VEC_CONFIG2 0x18c +#define VEC_CONFIG2_PROG_SCAN BIT(15) +#define VEC_CONFIG2_SYNC_ADJ_MASK GENMASK(14, 12) +#define VEC_CONFIG2_SYNC_ADJ(x) (((x) / 2) << 12) +#define VEC_CONFIG2_PBPR_EN BIT(10) +#define VEC_CONFIG2_UV_DIG_DIS BIT(6) +#define VEC_CONFIG2_RGB_DIG_DIS BIT(5) +#define VEC_CONFIG2_TMUX_MASK GENMASK(3, 2) +#define VEC_CONFIG2_TMUX_DRIVE0 (0 << 2) +#define VEC_CONFIG2_TMUX_RG_COMP (1 << 2) +#define VEC_CONFIG2_TMUX_UV_YC (2 << 2) +#define VEC_CONFIG2_TMUX_SYNC_YC (3 << 2) + +#define VEC_INTERRUPT_CONTROL 0x190 +#define VEC_INTERRUPT_STATUS 0x194 +#define VEC_FCW_SECAM_B 0x198 +#define VEC_SECAM_GAIN_VAL 0x19c + +#define VEC_CONFIG3 0x1a0 +#define VEC_CONFIG3_HORIZ_LEN_STD (0 << 0) +#define VEC_CONFIG3_HORIZ_LEN_MPEG1_SIF (1 << 0) +#define VEC_CONFIG3_SHAPE_NON_LINEAR BIT(1) + +#define VEC_STATUS0 0x200 +#define VEC_MASK0 0x204 + +#define VEC_CFG 0x208 +#define VEC_CFG_SG_MODE_MASK GENMASK(6, 5) +#define VEC_CFG_SG_MODE(x) ((x) << 5) +#define VEC_CFG_SG_EN BIT(4) +#define VEC_CFG_VEC_EN BIT(3) +#define VEC_CFG_MB_EN BIT(2) +#define VEC_CFG_ENABLE BIT(1) +#define VEC_CFG_TB_EN BIT(0) + +#define VEC_DAC_TEST 0x20c + +#define VEC_DAC_CONFIG 0x210 +#define VEC_DAC_CONFIG_LDO_BIAS_CTRL(x) ((x) << 24) +#define VEC_DAC_CONFIG_DRIVER_CTRL(x) ((x) << 16) +#define VEC_DAC_CONFIG_DAC_CTRL(x) (x) + +#define VEC_DAC_MISC 0x214 +#define VEC_DAC_MISC_VCD_CTRL_MASK GENMASK(31, 16) +#define VEC_DAC_MISC_VCD_CTRL(x) ((x) << 16) +#define VEC_DAC_MISC_VID_ACT BIT(8) +#define VEC_DAC_MISC_VCD_PWRDN BIT(6) +#define VEC_DAC_MISC_BIAS_PWRDN BIT(5) +#define VEC_DAC_MISC_DAC_PWRDN BIT(2) +#define VEC_DAC_MISC_LDO_PWRDN BIT(1) +#define VEC_DAC_MISC_DAC_RST_N BIT(0) + + +/* General VEC hardware state. */ +struct vc4_vec { + struct platform_device *pdev; + + struct drm_encoder *encoder; + struct drm_connector *connector; + + void __iomem *regs; + + struct clk *clock; + + const struct vc4_vec_tv_mode *tv_mode; +}; + +#define VEC_READ(offset) readl(vec->regs + (offset)) +#define VEC_WRITE(offset, val) writel(val, vec->regs + (offset)) + +/* VC4 VEC encoder KMS struct */ +struct vc4_vec_encoder { + struct vc4_encoder base; + struct vc4_vec *vec; +}; + +static inline struct vc4_vec_encoder * +to_vc4_vec_encoder(struct drm_encoder *encoder) +{ + return container_of(encoder, struct vc4_vec_encoder, base.base); +} + +/* VC4 VEC connector KMS struct */ +struct vc4_vec_connector { + struct drm_connector base; + struct vc4_vec *vec; + + /* Since the connector is attached to just the one encoder, + * this is the reference to it so we can do the best_encoder() + * hook. + */ + struct drm_encoder *encoder; +}; + +static inline struct vc4_vec_connector * +to_vc4_vec_connector(struct drm_connector *connector) +{ + return container_of(connector, struct vc4_vec_connector, base); +} + +enum vc4_vec_tv_mode_id { + VC4_VEC_TV_MODE_NTSC, + VC4_VEC_TV_MODE_NTSC_J, + VC4_VEC_TV_MODE_PAL, + VC4_VEC_TV_MODE_PAL_M, +}; + +struct vc4_vec_tv_mode { + const struct drm_display_mode *mode; + void (*mode_set)(struct vc4_vec *vec); +}; + +#define VEC_REG(reg) { reg, #reg } +static const struct { + u32 reg; + const char *name; +} vec_regs[] = { + VEC_REG(VEC_WSE_CONTROL), + VEC_REG(VEC_WSE_WSS_DATA), + VEC_REG(VEC_WSE_VPS_DATA1), + VEC_REG(VEC_WSE_VPS_CONTROL), + VEC_REG(VEC_REVID), + VEC_REG(VEC_CONFIG0), + VEC_REG(VEC_SCHPH), + VEC_REG(VEC_CLMP0_START), + VEC_REG(VEC_CLMP0_END), + VEC_REG(VEC_FREQ3_2), + VEC_REG(VEC_FREQ1_0), + VEC_REG(VEC_CONFIG1), + VEC_REG(VEC_CONFIG2), + VEC_REG(VEC_INTERRUPT_CONTROL), + VEC_REG(VEC_INTERRUPT_STATUS), + VEC_REG(VEC_FCW_SECAM_B), + VEC_REG(VEC_SECAM_GAIN_VAL), + VEC_REG(VEC_CONFIG3), + VEC_REG(VEC_STATUS0), + VEC_REG(VEC_MASK0), + VEC_REG(VEC_CFG), + VEC_REG(VEC_DAC_TEST), + VEC_REG(VEC_DAC_CONFIG), + VEC_REG(VEC_DAC_MISC), +}; + +#ifdef CONFIG_DEBUG_FS +int vc4_vec_debugfs_regs(struct seq_file *m, void *unused) +{ + struct drm_info_node *node = (struct drm_info_node *)m->private; + struct drm_device *dev = node->minor->dev; + struct vc4_dev *vc4 = to_vc4_dev(dev); + struct vc4_vec *vec = vc4->vec; + int i; + + if (!vec) + return 0; + + for (i = 0; i < ARRAY_SIZE(vec_regs); i++) { + seq_printf(m, "%s (0x%04x): 0x%08x\n", + vec_regs[i].name, vec_regs[i].reg, + VEC_READ(vec_regs[i].reg)); + } + + return 0; +} +#endif + +static void vc4_vec_ntsc_mode_set(struct vc4_vec *vec) +{ + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN); + VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); +} + +static void vc4_vec_ntsc_j_mode_set(struct vc4_vec *vec) +{ + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_NTSC_STD); + VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); +} + +static const struct drm_display_mode ntsc_mode = { + DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500, + 720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0, + 480, 480 + 3, 480 + 3 + 3, 480 + 3 + 3 + 16, 0, + DRM_MODE_FLAG_INTERLACE) +}; + +static void vc4_vec_pal_mode_set(struct vc4_vec *vec) +{ + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); + VEC_WRITE(VEC_CONFIG1, VEC_CONFIG1_C_CVBS_CVBS); +} + +static void vc4_vec_pal_m_mode_set(struct vc4_vec *vec) +{ + VEC_WRITE(VEC_CONFIG0, VEC_CONFIG0_PAL_BDGHI_STD); + VEC_WRITE(VEC_CONFIG1, + VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ); + VEC_WRITE(VEC_FREQ3_2, 0x223b); + VEC_WRITE(VEC_FREQ1_0, 0x61d1); +} + +static const struct drm_display_mode pal_mode = { + DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500, + 720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0, + 576, 576 + 2, 576 + 2 + 3, 576 + 2 + 3 + 20, 0, + DRM_MODE_FLAG_INTERLACE) +}; + +static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = { + [VC4_VEC_TV_MODE_NTSC] = { + .mode = &ntsc_mode, + .mode_set = vc4_vec_ntsc_mode_set, + }, + [VC4_VEC_TV_MODE_NTSC_J] = { + .mode = &ntsc_mode, + .mode_set = vc4_vec_ntsc_j_mode_set, + }, + [VC4_VEC_TV_MODE_PAL] = { + .mode = &pal_mode, + .mode_set = vc4_vec_pal_mode_set, + }, + [VC4_VEC_TV_MODE_PAL_M] = { + .mode = &pal_mode, + .mode_set = vc4_vec_pal_m_mode_set, + }, +}; + +static enum drm_connector_status +vc4_vec_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_unknown; +} + +static void vc4_vec_connector_destroy(struct drm_connector *connector) +{ + drm_connector_unregister(connector); + drm_connector_cleanup(connector); +} + +static int vc4_vec_connector_get_modes(struct drm_connector *connector) +{ + struct drm_connector_state *state = connector->state; + struct drm_display_mode *mode; + + mode = drm_mode_duplicate(connector->dev, + vc4_vec_tv_modes[state->tv.mode].mode); + if (!mode) { + DRM_ERROR("Failed to create a new display mode\n"); + return -ENOMEM; + } + + drm_mode_probed_add(connector, mode); + + return 1; +} + +static const struct drm_connector_funcs vc4_vec_connector_funcs = { + .dpms = drm_atomic_helper_connector_dpms, + .detect = vc4_vec_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = drm_atomic_helper_connector_set_property, + .destroy = vc4_vec_connector_destroy, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs vc4_vec_connector_helper_funcs = { + .get_modes = vc4_vec_connector_get_modes, +}; + +static struct drm_connector *vc4_vec_connector_init(struct drm_device *dev, + struct vc4_vec *vec) +{ + struct drm_connector *connector = NULL; + struct vc4_vec_connector *vec_connector; + + vec_connector = devm_kzalloc(dev->dev, sizeof(*vec_connector), + GFP_KERNEL); + if (!vec_connector) + return ERR_PTR(-ENOMEM); + + connector = &vec_connector->base; + connector->interlace_allowed = true; + + vec_connector->encoder = vec->encoder; + vec_connector->vec = vec; + + drm_connector_init(dev, connector, &vc4_vec_connector_funcs, + DRM_MODE_CONNECTOR_Composite); + drm_connector_helper_add(connector, &vc4_vec_connector_helper_funcs); + + drm_object_attach_property(&connector->base, + dev->mode_config.tv_mode_property, + VC4_VEC_TV_MODE_NTSC); + vec->tv_mode = &vc4_vec_tv_modes[VC4_VEC_TV_MODE_NTSC]; + + drm_mode_connector_attach_encoder(connector, vec->encoder); + + return connector; +} + +static const struct drm_encoder_funcs vc4_vec_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static void vc4_vec_encoder_disable(struct drm_encoder *encoder) +{ + struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); + struct vc4_vec *vec = vc4_vec_encoder->vec; + int ret; + + VEC_WRITE(VEC_CFG, 0); + VEC_WRITE(VEC_DAC_MISC, + VEC_DAC_MISC_VCD_PWRDN | + VEC_DAC_MISC_BIAS_PWRDN | + VEC_DAC_MISC_DAC_PWRDN | + VEC_DAC_MISC_LDO_PWRDN); + + clk_disable_unprepare(vec->clock); + + ret = pm_runtime_put(&vec->pdev->dev); + if (ret < 0) { + DRM_ERROR("Failed to release power domain: %d\n", ret); + return; + } +} + +static void vc4_vec_encoder_enable(struct drm_encoder *encoder) +{ + struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); + struct vc4_vec *vec = vc4_vec_encoder->vec; + int ret; + + ret = pm_runtime_get_sync(&vec->pdev->dev); + if (ret < 0) { + DRM_ERROR("Failed to retain power domain: %d\n", ret); + return; + } + + /* + * We need to set the clock rate each time we enable the encoder + * because there's a chance we share the same parent with the HDMI + * clock, and both drivers are requesting different rates. + * The good news is, these 2 encoders cannot be enabled at the same + * time, thus preventing incompatible rate requests. + */ + ret = clk_set_rate(vec->clock, 108000000); + if (ret) { + DRM_ERROR("Failed to set clock rate: %d\n", ret); + return; + } + + ret = clk_prepare_enable(vec->clock); + if (ret) { + DRM_ERROR("Failed to turn on core clock: %d\n", ret); + return; + } + + /* Reset the different blocks */ + VEC_WRITE(VEC_WSE_RESET, 1); + VEC_WRITE(VEC_SOFT_RESET, 1); + + /* Disable the CGSM-A and WSE blocks */ + VEC_WRITE(VEC_WSE_CONTROL, 0); + + /* Write config common to all modes. */ + + /* + * Color subcarrier phase: phase = 360 * SCHPH / 256. + * 0x28 <=> 39.375 deg. + */ + VEC_WRITE(VEC_SCHPH, 0x28); + + /* + * Reset to default values. + */ + VEC_WRITE(VEC_CLMP0_START, 0xac); + VEC_WRITE(VEC_CLMP0_END, 0xec); + VEC_WRITE(VEC_CONFIG2, + VEC_CONFIG2_UV_DIG_DIS | VEC_CONFIG2_RGB_DIG_DIS); + VEC_WRITE(VEC_CONFIG3, VEC_CONFIG3_HORIZ_LEN_STD); + VEC_WRITE(VEC_DAC_CONFIG, + VEC_DAC_CONFIG_DAC_CTRL(0xc) | + VEC_DAC_CONFIG_DRIVER_CTRL(0xc) | + VEC_DAC_CONFIG_LDO_BIAS_CTRL(0x46)); + + /* Mask all interrupts. */ + VEC_WRITE(VEC_MASK0, 0); + + vec->tv_mode->mode_set(vec); + + VEC_WRITE(VEC_DAC_MISC, + VEC_DAC_MISC_VID_ACT | VEC_DAC_MISC_DAC_RST_N); + VEC_WRITE(VEC_CFG, VEC_CFG_VEC_EN); +} + + +static bool vc4_vec_encoder_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static void vc4_vec_encoder_atomic_mode_set(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct vc4_vec_encoder *vc4_vec_encoder = to_vc4_vec_encoder(encoder); + struct vc4_vec *vec = vc4_vec_encoder->vec; + + vec->tv_mode = &vc4_vec_tv_modes[conn_state->tv.mode]; +} + +static int vc4_vec_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + const struct vc4_vec_tv_mode *vec_mode; + + vec_mode = &vc4_vec_tv_modes[conn_state->tv.mode]; + + if (conn_state->crtc && + !drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode)) + return -EINVAL; + + return 0; +} + +static const struct drm_encoder_helper_funcs vc4_vec_encoder_helper_funcs = { + .disable = vc4_vec_encoder_disable, + .enable = vc4_vec_encoder_enable, + .mode_fixup = vc4_vec_encoder_mode_fixup, + .atomic_check = vc4_vec_encoder_atomic_check, + .atomic_mode_set = vc4_vec_encoder_atomic_mode_set, +}; + +static const struct of_device_id vc4_vec_dt_match[] = { + { .compatible = "brcm,bcm2835-vec", .data = NULL }, + { /* sentinel */ }, +}; + +static const char * const tv_mode_names[] = { + [VC4_VEC_TV_MODE_NTSC] = "NTSC", + [VC4_VEC_TV_MODE_NTSC_J] = "NTSC-J", + [VC4_VEC_TV_MODE_PAL] = "PAL", + [VC4_VEC_TV_MODE_PAL_M] = "PAL-M", +}; + +static int vc4_vec_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_vec *vec; + struct vc4_vec_encoder *vc4_vec_encoder; + int ret; + + ret = drm_mode_create_tv_properties(drm, ARRAY_SIZE(tv_mode_names), + tv_mode_names); + if (ret) + return ret; + + vec = devm_kzalloc(dev, sizeof(*vec), GFP_KERNEL); + if (!vec) + return -ENOMEM; + + vc4_vec_encoder = devm_kzalloc(dev, sizeof(*vc4_vec_encoder), + GFP_KERNEL); + if (!vc4_vec_encoder) + return -ENOMEM; + vc4_vec_encoder->base.type = VC4_ENCODER_TYPE_VEC; + vc4_vec_encoder->vec = vec; + vec->encoder = &vc4_vec_encoder->base.base; + + vec->pdev = pdev; + vec->regs = vc4_ioremap_regs(pdev, 0); + if (IS_ERR(vec->regs)) + return PTR_ERR(vec->regs); + + vec->clock = devm_clk_get(dev, NULL); + if (IS_ERR(vec->clock)) { + ret = PTR_ERR(vec->clock); + if (ret != -EPROBE_DEFER) + DRM_ERROR("Failed to get clock: %d\n", ret); + return ret; + } + + pm_runtime_enable(dev); + + drm_encoder_init(drm, vec->encoder, &vc4_vec_encoder_funcs, + DRM_MODE_ENCODER_TVDAC, NULL); + drm_encoder_helper_add(vec->encoder, &vc4_vec_encoder_helper_funcs); + + vec->connector = vc4_vec_connector_init(drm, vec); + if (IS_ERR(vec->connector)) { + ret = PTR_ERR(vec->connector); + goto err_destroy_encoder; + } + + dev_set_drvdata(dev, vec); + + vc4->vec = vec; + + return 0; + +err_destroy_encoder: + drm_encoder_cleanup(vec->encoder); + pm_runtime_disable(dev); + + return ret; +} + +static void vc4_vec_unbind(struct device *dev, struct device *master, + void *data) +{ + struct drm_device *drm = dev_get_drvdata(master); + struct vc4_dev *vc4 = to_vc4_dev(drm); + struct vc4_vec *vec = dev_get_drvdata(dev); + + vc4_vec_connector_destroy(vec->connector); + drm_encoder_cleanup(vec->encoder); + pm_runtime_disable(dev); + + vc4->vec = NULL; +} + +static const struct component_ops vc4_vec_ops = { + .bind = vc4_vec_bind, + .unbind = vc4_vec_unbind, +}; + +static int vc4_vec_dev_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &vc4_vec_ops); +} + +static int vc4_vec_dev_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &vc4_vec_ops); + return 0; +} + +struct platform_driver vc4_vec_driver = { + .probe = vc4_vec_dev_probe, + .remove = vc4_vec_dev_remove, + .driver = { + .name = "vc4_vec", + .of_match_table = vc4_vec_dt_match, + }, +}; diff --git a/drivers/gpu/host1x/dev.h b/drivers/gpu/host1x/dev.h index 5220510f39da..06dd4f85125f 100644 --- a/drivers/gpu/host1x/dev.h +++ b/drivers/gpu/host1x/dev.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012-2013, NVIDIA Corporation. + * Copyright (c) 2012-2015, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -120,6 +120,7 @@ struct host1x { struct host1x_syncpt *nop_sp; + struct mutex syncpt_mutex; struct mutex chlist_mutex; struct host1x_channel chlist; unsigned long allocated_channels; diff --git a/drivers/gpu/host1x/job.c b/drivers/gpu/host1x/job.c index a91b7c4a6110..92c3df933303 100644 --- a/drivers/gpu/host1x/job.c +++ b/drivers/gpu/host1x/job.c @@ -1,7 +1,7 @@ /* * Tegra host1x Job * - * Copyright (c) 2010-2013, NVIDIA Corporation. + * Copyright (c) 2010-2015, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -539,9 +539,12 @@ int host1x_job_pin(struct host1x_job *job, struct device *dev) g->base = job->gather_addr_phys[i]; - for (j = i + 1; j < job->num_gathers; j++) - if (job->gathers[j].bo == g->bo) + for (j = i + 1; j < job->num_gathers; j++) { + if (job->gathers[j].bo == g->bo) { job->gathers[j].handled = true; + job->gathers[j].base = g->base; + } + } err = do_relocs(job, g->bo); if (err) diff --git a/drivers/gpu/host1x/syncpt.c b/drivers/gpu/host1x/syncpt.c index 95589328ad52..25c11a85050b 100644 --- a/drivers/gpu/host1x/syncpt.c +++ b/drivers/gpu/host1x/syncpt.c @@ -1,7 +1,7 @@ /* * Tegra host1x Syncpoints * - * Copyright (c) 2010-2013, NVIDIA Corporation. + * Copyright (c) 2010-2015, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -61,22 +61,24 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, struct host1x_syncpt *sp = host->syncpt; char *name; + mutex_lock(&host->syncpt_mutex); + for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++) ; if (i >= host->info->nb_pts) - return NULL; + goto unlock; if (flags & HOST1X_SYNCPT_HAS_BASE) { sp->base = host1x_syncpt_base_request(host); if (!sp->base) - return NULL; + goto unlock; } name = kasprintf(GFP_KERNEL, "%02u-%s", sp->id, dev ? dev_name(dev) : NULL); if (!name) - return NULL; + goto free_base; sp->dev = dev; sp->name = name; @@ -86,7 +88,15 @@ static struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, else sp->client_managed = false; + mutex_unlock(&host->syncpt_mutex); return sp; + +free_base: + host1x_syncpt_base_free(sp->base); + sp->base = NULL; +unlock: + mutex_unlock(&host->syncpt_mutex); + return NULL; } u32 host1x_syncpt_id(struct host1x_syncpt *sp) @@ -378,6 +388,7 @@ int host1x_syncpt_init(struct host1x *host) for (i = 0; i < host->info->nb_bases; i++) bases[i].id = i; + mutex_init(&host->syncpt_mutex); host->syncpt = syncpt; host->bases = bases; @@ -405,12 +416,16 @@ void host1x_syncpt_free(struct host1x_syncpt *sp) if (!sp) return; + mutex_lock(&sp->host->syncpt_mutex); + host1x_syncpt_base_free(sp->base); kfree(sp->name); sp->base = NULL; sp->dev = NULL; sp->name = NULL; sp->client_managed = false; + + mutex_unlock(&sp->host->syncpt_mutex); } EXPORT_SYMBOL(host1x_syncpt_free); |