diff options
author | Gustavo Padovan <gustavo.padovan@collabora.com> | 2018-07-06 19:34:13 +0300 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.com> | 2018-07-06 19:34:13 +0300 |
commit | e22e953189f7b3a3bfc7efb511d2b1d1454adde5 (patch) | |
tree | 87e56e966e1f676f2c9389c41aa5c37969f757f1 /drivers/gpu/drm | |
parent | a012024571d98e2e4bf29a9168fb7ddc44b7ab86 (diff) | |
parent | 4da1d4c751c9b1b713c13043bad7c4d27cd1418c (diff) | |
download | linux-e22e953189f7b3a3bfc7efb511d2b1d1454adde5.tar.xz |
Merge drm-upstream/drm-next into drm-misc-next
Pull in the malidp writeback implementation for further work on writeback in drm-misc-next.
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.com>
Diffstat (limited to 'drivers/gpu/drm')
36 files changed, 1067 insertions, 278 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c index 2e9db0ebe678..7056925eb386 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_fence.c @@ -376,7 +376,7 @@ int amdgpu_fence_driver_start_ring(struct amdgpu_ring *ring, struct amdgpu_device *adev = ring->adev; uint64_t index; - if (ring != &adev->uvd.inst[ring->me].ring) { + if (ring->funcs->type != AMDGPU_RING_TYPE_UVD) { ring->fence_drv.cpu_addr = &adev->wb.wb[ring->fence_offs]; ring->fence_drv.gpu_addr = adev->wb.gpu_addr + (ring->fence_offs * 4); } else { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index 3f2a5e73e69f..a66cd521a875 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -52,7 +52,7 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev) unsigned long bo_size; const char *fw_name; const struct common_firmware_header *hdr; - unsigned version_major, version_minor, family_id; + unsigned char fw_check; int r; INIT_DELAYED_WORK(&adev->vcn.idle_work, amdgpu_vcn_idle_work_handler); @@ -83,12 +83,33 @@ int amdgpu_vcn_sw_init(struct amdgpu_device *adev) hdr = (const struct common_firmware_header *)adev->vcn.fw->data; adev->vcn.fw_version = le32_to_cpu(hdr->ucode_version); - family_id = le32_to_cpu(hdr->ucode_version) & 0xff; - version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; - version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; - DRM_INFO("Found VCN firmware Version: %hu.%hu Family ID: %hu\n", - version_major, version_minor, family_id); + /* Bit 20-23, it is encode major and non-zero for new naming convention. + * This field is part of version minor and DRM_DISABLED_FLAG in old naming + * convention. Since the l:wq!atest version minor is 0x5B and DRM_DISABLED_FLAG + * is zero in old naming convention, this field is always zero so far. + * These four bits are used to tell which naming convention is present. + */ + fw_check = (le32_to_cpu(hdr->ucode_version) >> 20) & 0xf; + if (fw_check) { + unsigned int dec_ver, enc_major, enc_minor, vep, fw_rev; + + fw_rev = le32_to_cpu(hdr->ucode_version) & 0xfff; + enc_minor = (le32_to_cpu(hdr->ucode_version) >> 12) & 0xff; + enc_major = fw_check; + dec_ver = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xf; + vep = (le32_to_cpu(hdr->ucode_version) >> 28) & 0xf; + DRM_INFO("Found VCN firmware Version ENC: %hu.%hu DEC: %hu VEP: %hu Revision: %hu\n", + enc_major, enc_minor, dec_ver, vep, fw_rev); + } else { + unsigned int version_major, version_minor, family_id; + + family_id = le32_to_cpu(hdr->ucode_version) & 0xff; + version_major = (le32_to_cpu(hdr->ucode_version) >> 24) & 0xff; + version_minor = (le32_to_cpu(hdr->ucode_version) >> 8) & 0xff; + DRM_INFO("Found VCN firmware Version: %hu.%hu Family ID: %hu\n", + version_major, version_minor, family_id); + } bo_size = AMDGPU_GPU_PAGE_ALIGN(le32_to_cpu(hdr->ucode_size_bytes) + 8) + AMDGPU_VCN_STACK_SIZE + AMDGPU_VCN_HEAP_SIZE diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 819949418495..422d1a434db4 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1577,7 +1577,9 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, uint64_t count; max_entries = min(max_entries, 16ull * 1024ull); - for (count = 1; count < max_entries; ++count) { + for (count = 1; + count < max_entries / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); + ++count) { uint64_t idx = pfn + count; if (pages_addr[idx] != @@ -1590,7 +1592,7 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, dma_addr = pages_addr; } else { addr = pages_addr[pfn]; - max_entries = count; + max_entries = count * (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); } } else if (flags & AMDGPU_PTE_VALID) { @@ -1605,7 +1607,7 @@ static int amdgpu_vm_bo_split_mapping(struct amdgpu_device *adev, if (r) return r; - pfn += last - start + 1; + pfn += (last - start + 1) / (PAGE_SIZE / AMDGPU_GPU_PAGE_SIZE); if (nodes && nodes->size == pfn) { pfn = 0; ++nodes; diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 655950102827..66bd3cc3e387 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3927,10 +3927,11 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, if (acrtc->base.state->event) prepare_flip_isr(acrtc); + spin_unlock_irqrestore(&crtc->dev->event_lock, flags); + surface_updates->surface = dc_stream_get_status(acrtc_state->stream)->plane_states[0]; surface_updates->flip_addr = &addr; - dc_commit_updates_for_stream(adev->dm.dc, surface_updates, 1, @@ -3943,9 +3944,6 @@ static void amdgpu_dm_do_flip(struct drm_crtc *crtc, __func__, addr.address.grph.addr.high_part, addr.address.grph.addr.low_part); - - - spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } /* @@ -4205,6 +4203,7 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) struct drm_connector *connector; struct drm_connector_state *old_con_state, *new_con_state; struct dm_crtc_state *dm_old_crtc_state, *dm_new_crtc_state; + int crtc_disable_count = 0; drm_atomic_helper_update_legacy_modeset_state(dev, state); @@ -4409,6 +4408,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) struct amdgpu_crtc *acrtc = to_amdgpu_crtc(crtc); bool modeset_needed; + if (old_crtc_state->active && !new_crtc_state->active) + crtc_disable_count++; + dm_new_crtc_state = to_dm_crtc_state(new_crtc_state); dm_old_crtc_state = to_dm_crtc_state(old_crtc_state); modeset_needed = modeset_required( @@ -4462,11 +4464,9 @@ static void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state *state) * so we can put the GPU into runtime suspend if we're not driving any * displays anymore */ + for (i = 0; i < crtc_disable_count; i++) + pm_runtime_put_autosuspend(dev->dev); pm_runtime_mark_last_busy(dev->dev); - for_each_oldnew_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { - if (old_crtc_state->active && !new_crtc_state->active) - pm_runtime_put_autosuspend(dev->dev); - } } diff --git a/drivers/gpu/drm/arm/Makefile b/drivers/gpu/drm/arm/Makefile index bb8b158ff90d..3bf31d1a4722 100644 --- a/drivers/gpu/drm/arm/Makefile +++ b/drivers/gpu/drm/arm/Makefile @@ -1,4 +1,5 @@ hdlcd-y := hdlcd_drv.o hdlcd_crtc.o obj-$(CONFIG_DRM_HDLCD) += hdlcd.o mali-dp-y := malidp_drv.o malidp_hw.o malidp_planes.o malidp_crtc.o +mali-dp-y += malidp_mw.o obj-$(CONFIG_DRM_MALI_DISPLAY) += mali-dp.o diff --git a/drivers/gpu/drm/arm/malidp_crtc.c b/drivers/gpu/drm/arm/malidp_crtc.c index fcc62bc60f6a..ef44202fb43f 100644 --- a/drivers/gpu/drm/arm/malidp_crtc.c +++ b/drivers/gpu/drm/arm/malidp_crtc.c @@ -411,6 +411,16 @@ static int malidp_crtc_atomic_check(struct drm_crtc *crtc, } } + /* If only the writeback routing has changed, we don't need a modeset */ + if (state->connectors_changed) { + u32 old_mask = crtc->state->connector_mask; + u32 new_mask = state->connector_mask; + + if ((old_mask ^ new_mask) == + (1 << drm_connector_index(&malidp->mw_connector.base))) + state->connectors_changed = false; + } + ret = malidp_crtc_atomic_check_gamma(crtc, state); ret = ret ? ret : malidp_crtc_atomic_check_ctm(crtc, state); ret = ret ? ret : malidp_crtc_atomic_check_scaling(crtc, state); diff --git a/drivers/gpu/drm/arm/malidp_drv.c b/drivers/gpu/drm/arm/malidp_drv.c index 8d20faa198cf..5b7260557391 100644 --- a/drivers/gpu/drm/arm/malidp_drv.c +++ b/drivers/gpu/drm/arm/malidp_drv.c @@ -17,6 +17,7 @@ #include <linux/of_graph.h> #include <linux/of_reserved_mem.h> #include <linux/pm_runtime.h> +#include <linux/debugfs.h> #include <drm/drmP.h> #include <drm/drm_atomic.h> @@ -31,6 +32,7 @@ #include <drm/drm_of.h> #include "malidp_drv.h" +#include "malidp_mw.h" #include "malidp_regs.h" #include "malidp_hw.h" @@ -170,14 +172,15 @@ static int malidp_set_and_wait_config_valid(struct drm_device *drm) struct malidp_hw_device *hwdev = malidp->dev; int ret; - atomic_set(&malidp->config_valid, 0); - hwdev->hw->set_config_valid(hwdev); + hwdev->hw->set_config_valid(hwdev, 1); /* don't wait for config_valid flag if we are in config mode */ - if (hwdev->hw->in_config_mode(hwdev)) + if (hwdev->hw->in_config_mode(hwdev)) { + atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); return 0; + } ret = wait_event_interruptible_timeout(malidp->wq, - atomic_read(&malidp->config_valid) == 1, + atomic_read(&malidp->config_valid) == MALIDP_CONFIG_VALID_DONE, msecs_to_jiffies(MALIDP_CONF_VALID_TIMEOUT)); return (ret > 0) ? 0 : -ETIMEDOUT; @@ -216,12 +219,20 @@ static void malidp_atomic_commit_hw_done(struct drm_atomic_state *state) static void malidp_atomic_commit_tail(struct drm_atomic_state *state) { struct drm_device *drm = state->dev; + struct malidp_drm *malidp = drm->dev_private; struct drm_crtc *crtc; struct drm_crtc_state *old_crtc_state; int i; pm_runtime_get_sync(drm->dev); + /* + * set config_valid to a special value to let IRQ handlers + * know that we are updating registers + */ + atomic_set(&malidp->config_valid, MALIDP_CONFIG_START); + malidp->dev->hw->set_config_valid(malidp->dev, 0); + drm_atomic_helper_commit_modeset_disables(drm, state); for_each_old_crtc_in_state(state, crtc, old_crtc_state, i) { @@ -230,7 +241,9 @@ static void malidp_atomic_commit_tail(struct drm_atomic_state *state) malidp_atomic_commit_se_config(crtc, old_crtc_state); } - drm_atomic_helper_commit_planes(drm, state, 0); + drm_atomic_helper_commit_planes(drm, state, DRM_PLANE_COMMIT_ACTIVE_ONLY); + + malidp_mw_atomic_commit(drm, state); drm_atomic_helper_commit_modeset_enables(drm, state); @@ -268,17 +281,22 @@ static int malidp_init(struct drm_device *drm) drm->mode_config.helper_private = &malidp_mode_config_helpers; ret = malidp_crtc_init(drm); - if (ret) { - drm_mode_config_cleanup(drm); - return ret; - } + if (ret) + goto crtc_fail; + + ret = malidp_mw_connector_init(drm); + if (ret) + goto crtc_fail; return 0; + +crtc_fail: + drm_mode_config_cleanup(drm); + return ret; } static void malidp_fini(struct drm_device *drm) { - drm_atomic_helper_shutdown(drm); drm_mode_config_cleanup(drm); } @@ -286,6 +304,8 @@ static int malidp_irq_init(struct platform_device *pdev) { int irq_de, irq_se, ret = 0; struct drm_device *drm = dev_get_drvdata(&pdev->dev); + struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; /* fetch the interrupts from DT */ irq_de = platform_get_irq_byname(pdev, "DE"); @@ -305,7 +325,7 @@ static int malidp_irq_init(struct platform_device *pdev) ret = malidp_se_irq_init(drm, irq_se); if (ret) { - malidp_de_irq_fini(drm); + malidp_de_irq_fini(hwdev); return ret; } @@ -327,6 +347,106 @@ static int malidp_dumb_create(struct drm_file *file_priv, return drm_gem_cma_dumb_create_internal(file_priv, drm, args); } +#ifdef CONFIG_DEBUG_FS + +static void malidp_error_stats_init(struct malidp_error_stats *error_stats) +{ + error_stats->num_errors = 0; + error_stats->last_error_status = 0; + error_stats->last_error_vblank = -1; +} + +void malidp_error(struct malidp_drm *malidp, + struct malidp_error_stats *error_stats, u32 status, + u64 vblank) +{ + unsigned long irqflags; + + spin_lock_irqsave(&malidp->errors_lock, irqflags); + error_stats->last_error_status = status; + error_stats->last_error_vblank = vblank; + error_stats->num_errors++; + spin_unlock_irqrestore(&malidp->errors_lock, irqflags); +} + +void malidp_error_stats_dump(const char *prefix, + struct malidp_error_stats error_stats, + struct seq_file *m) +{ + seq_printf(m, "[%s] num_errors : %d\n", prefix, + error_stats.num_errors); + seq_printf(m, "[%s] last_error_status : 0x%08x\n", prefix, + error_stats.last_error_status); + seq_printf(m, "[%s] last_error_vblank : %lld\n", prefix, + error_stats.last_error_vblank); +} + +static int malidp_show_stats(struct seq_file *m, void *arg) +{ + struct drm_device *drm = m->private; + struct malidp_drm *malidp = drm->dev_private; + unsigned long irqflags; + struct malidp_error_stats de_errors, se_errors; + + spin_lock_irqsave(&malidp->errors_lock, irqflags); + de_errors = malidp->de_errors; + se_errors = malidp->se_errors; + spin_unlock_irqrestore(&malidp->errors_lock, irqflags); + malidp_error_stats_dump("DE", de_errors, m); + malidp_error_stats_dump("SE", se_errors, m); + return 0; +} + +static int malidp_debugfs_open(struct inode *inode, struct file *file) +{ + return single_open(file, malidp_show_stats, inode->i_private); +} + +static ssize_t malidp_debugfs_write(struct file *file, const char __user *ubuf, + size_t len, loff_t *offp) +{ + struct seq_file *m = file->private_data; + struct drm_device *drm = m->private; + struct malidp_drm *malidp = drm->dev_private; + unsigned long irqflags; + + spin_lock_irqsave(&malidp->errors_lock, irqflags); + malidp_error_stats_init(&malidp->de_errors); + malidp_error_stats_init(&malidp->se_errors); + spin_unlock_irqrestore(&malidp->errors_lock, irqflags); + return len; +} + +static const struct file_operations malidp_debugfs_fops = { + .owner = THIS_MODULE, + .open = malidp_debugfs_open, + .read = seq_read, + .write = malidp_debugfs_write, + .llseek = seq_lseek, + .release = single_release, +}; + +static int malidp_debugfs_init(struct drm_minor *minor) +{ + struct malidp_drm *malidp = minor->dev->dev_private; + struct dentry *dentry = NULL; + + malidp_error_stats_init(&malidp->de_errors); + malidp_error_stats_init(&malidp->se_errors); + spin_lock_init(&malidp->errors_lock); + dentry = debugfs_create_file("debug", + S_IRUGO | S_IWUSR, + minor->debugfs_root, minor->dev, + &malidp_debugfs_fops); + if (!dentry) { + DRM_ERROR("Cannot create debug file\n"); + return -ENOMEM; + } + return 0; +} + +#endif //CONFIG_DEBUG_FS + static struct drm_driver malidp_driver = { .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_PRIME, @@ -343,6 +463,9 @@ static struct drm_driver malidp_driver = { .gem_prime_vmap = drm_gem_cma_prime_vmap, .gem_prime_vunmap = drm_gem_cma_prime_vunmap, .gem_prime_mmap = drm_gem_cma_prime_mmap, +#ifdef CONFIG_DEBUG_FS + .debugfs_init = malidp_debugfs_init, +#endif .fops = &fops, .name = "mali-dp", .desc = "ARM Mali Display Processor driver", @@ -459,6 +582,8 @@ static int malidp_runtime_pm_suspend(struct device *dev) /* we can only suspend if the hardware is in config mode */ WARN_ON(!hwdev->hw->in_config_mode(hwdev)); + malidp_se_irq_fini(hwdev); + malidp_de_irq_fini(hwdev); hwdev->pm_suspended = true; clk_disable_unprepare(hwdev->mclk); clk_disable_unprepare(hwdev->aclk); @@ -477,6 +602,8 @@ static int malidp_runtime_pm_resume(struct device *dev) clk_prepare_enable(hwdev->aclk); clk_prepare_enable(hwdev->mclk); hwdev->pm_suspended = false; + malidp_de_irq_hw_init(hwdev); + malidp_se_irq_hw_init(hwdev); return 0; } @@ -588,8 +715,9 @@ static int malidp_bind(struct device *dev) for (i = 0; i < MAX_OUTPUT_CHANNELS; i++) out_depth = (out_depth << 8) | (output_width[i] & 0xf); malidp_hw_write(hwdev, out_depth, hwdev->hw->map.out_depth_base); + hwdev->output_color_depth = out_depth; - atomic_set(&malidp->config_valid, 0); + atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_INIT); init_waitqueue_head(&malidp->wq); ret = malidp_init(drm); @@ -642,10 +770,11 @@ register_fail: fbdev_fail: pm_runtime_get_sync(dev); vblank_fail: - malidp_se_irq_fini(drm); - malidp_de_irq_fini(drm); + malidp_se_irq_fini(hwdev); + malidp_de_irq_fini(hwdev); drm->irq_enabled = false; irq_init_fail: + drm_atomic_helper_shutdown(drm); component_unbind_all(dev, drm); bind_fail: of_node_put(malidp->crtc.port); @@ -672,15 +801,17 @@ static void malidp_unbind(struct device *dev) { struct drm_device *drm = dev_get_drvdata(dev); struct malidp_drm *malidp = drm->dev_private; + struct malidp_hw_device *hwdev = malidp->dev; drm_dev_unregister(drm); drm_fb_cma_fbdev_fini(drm); drm_kms_helper_poll_fini(drm); pm_runtime_get_sync(dev); drm_crtc_vblank_off(&malidp->crtc); - malidp_se_irq_fini(drm); - malidp_de_irq_fini(drm); + malidp_se_irq_fini(hwdev); + malidp_de_irq_fini(hwdev); drm->irq_enabled = false; + drm_atomic_helper_shutdown(drm); component_unbind_all(dev, drm); of_node_put(malidp->crtc.port); malidp->crtc.port = NULL; @@ -751,8 +882,25 @@ static int __maybe_unused malidp_pm_resume(struct device *dev) return 0; } +static int __maybe_unused malidp_pm_suspend_late(struct device *dev) +{ + if (!pm_runtime_status_suspended(dev)) { + malidp_runtime_pm_suspend(dev); + pm_runtime_set_suspended(dev); + } + return 0; +} + +static int __maybe_unused malidp_pm_resume_early(struct device *dev) +{ + malidp_runtime_pm_resume(dev); + pm_runtime_set_active(dev); + return 0; +} + static const struct dev_pm_ops malidp_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend, malidp_pm_resume) \ + SET_LATE_SYSTEM_SLEEP_PM_OPS(malidp_pm_suspend_late, malidp_pm_resume_early) \ SET_RUNTIME_PM_OPS(malidp_runtime_pm_suspend, malidp_runtime_pm_resume, NULL) }; diff --git a/drivers/gpu/drm/arm/malidp_drv.h b/drivers/gpu/drm/arm/malidp_drv.h index c70989b93387..e3eb0cb1f385 100644 --- a/drivers/gpu/drm/arm/malidp_drv.h +++ b/drivers/gpu/drm/arm/malidp_drv.h @@ -13,18 +13,38 @@ #ifndef __MALIDP_DRV_H__ #define __MALIDP_DRV_H__ +#include <drm/drm_writeback.h> +#include <drm/drm_encoder.h> #include <linux/mutex.h> #include <linux/wait.h> +#include <linux/spinlock.h> #include <drm/drmP.h> #include "malidp_hw.h" +#define MALIDP_CONFIG_VALID_INIT 0 +#define MALIDP_CONFIG_VALID_DONE 1 +#define MALIDP_CONFIG_START 0xd0 + +struct malidp_error_stats { + s32 num_errors; + u32 last_error_status; + s64 last_error_vblank; +}; + struct malidp_drm { struct malidp_hw_device *dev; struct drm_crtc crtc; + struct drm_writeback_connector mw_connector; wait_queue_head_t wq; struct drm_pending_vblank_event *event; atomic_t config_valid; u32 core_id; +#ifdef CONFIG_DEBUG_FS + struct malidp_error_stats de_errors; + struct malidp_error_stats se_errors; + /* Protects errors stats */ + spinlock_t errors_lock; +#endif }; #define crtc_to_malidp_device(x) container_of(x, struct malidp_drm, crtc) @@ -62,6 +82,12 @@ struct malidp_crtc_state { int malidp_de_planes_init(struct drm_device *drm); int malidp_crtc_init(struct drm_device *drm); +#ifdef CONFIG_DEBUG_FS +void malidp_error(struct malidp_drm *malidp, + struct malidp_error_stats *error_stats, u32 status, + u64 vblank); +#endif + /* often used combination of rotational bits */ #define MALIDP_ROTATED_MASK (DRM_MODE_ROTATE_90 | DRM_MODE_ROTATE_270) diff --git a/drivers/gpu/drm/arm/malidp_hw.c b/drivers/gpu/drm/arm/malidp_hw.c index d789b46dc817..c94a4422e0e9 100644 --- a/drivers/gpu/drm/arm/malidp_hw.c +++ b/drivers/gpu/drm/arm/malidp_hw.c @@ -21,15 +21,24 @@ #include "malidp_drv.h" #include "malidp_hw.h" +#include "malidp_mw.h" + +enum { + MW_NOT_ENABLED = 0, /* SE writeback not enabled */ + MW_ONESHOT, /* SE in one-shot mode for writeback */ + MW_START, /* SE started writeback */ + MW_RESTART, /* SE will start another writeback after this one */ + MW_STOP, /* SE needs to stop after this writeback */ +}; static const struct malidp_format_id malidp500_de_formats[] = { /* fourcc, layers supporting the format, internal id */ - { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 0 }, - { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 1 }, + { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 0 }, + { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 1 }, { DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 2 }, { DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 3 }, - { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 4 }, - { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 5 }, + { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 4 }, + { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2 | SE_MEMWRITE, 5 }, { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 6 }, { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 7 }, { DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 8 }, @@ -38,7 +47,7 @@ static const struct malidp_format_id malidp500_de_formats[] = { { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_GRAPHICS2, 11 }, { DRM_FORMAT_UYVY, DE_VIDEO1, 12 }, { DRM_FORMAT_YUYV, DE_VIDEO1, 13 }, - { DRM_FORMAT_NV12, DE_VIDEO1, 14 }, + { DRM_FORMAT_NV12, DE_VIDEO1 | SE_MEMWRITE, 14 }, { DRM_FORMAT_YUV420, DE_VIDEO1, 15 }, }; @@ -47,27 +56,27 @@ static const struct malidp_format_id malidp500_de_formats[] = { #define MALIDP_COMMON_FORMATS \ /* fourcc, layers supporting the format, internal id */ \ - { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 0) }, \ - { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 1) }, \ - { DRM_FORMAT_RGBA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 2) }, \ - { DRM_FORMAT_BGRA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(0, 3) }, \ + { DRM_FORMAT_ARGB2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 0) }, \ + { DRM_FORMAT_ABGR2101010, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 1) }, \ + { DRM_FORMAT_RGBA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 2) }, \ + { DRM_FORMAT_BGRA1010102, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(0, 3) }, \ { DRM_FORMAT_ARGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 0) }, \ { DRM_FORMAT_ABGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 1) }, \ { DRM_FORMAT_RGBA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 2) }, \ { DRM_FORMAT_BGRA8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(1, 3) }, \ - { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 0) }, \ - { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 1) }, \ - { DRM_FORMAT_RGBX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 2) }, \ - { DRM_FORMAT_BGRX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART, MALIDP_ID(2, 3) }, \ - { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 0) }, \ - { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(3, 1) }, \ + { DRM_FORMAT_XRGB8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 0) }, \ + { DRM_FORMAT_XBGR8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 1) }, \ + { DRM_FORMAT_RGBX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 2) }, \ + { DRM_FORMAT_BGRX8888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | DE_SMART | SE_MEMWRITE, MALIDP_ID(2, 3) }, \ + { DRM_FORMAT_RGB888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(3, 0) }, \ + { DRM_FORMAT_BGR888, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(3, 1) }, \ { DRM_FORMAT_RGBA5551, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 0) }, \ { DRM_FORMAT_ABGR1555, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 1) }, \ { DRM_FORMAT_RGB565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 2) }, \ { DRM_FORMAT_BGR565, DE_VIDEO1 | DE_GRAPHICS1 | DE_VIDEO2, MALIDP_ID(4, 3) }, \ { DRM_FORMAT_YUYV, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 2) }, \ { DRM_FORMAT_UYVY, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 3) }, \ - { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 6) }, \ + { DRM_FORMAT_NV12, DE_VIDEO1 | DE_VIDEO2 | SE_MEMWRITE, MALIDP_ID(5, 6) }, \ { DRM_FORMAT_YUV420, DE_VIDEO1 | DE_VIDEO2, MALIDP_ID(5, 7) } static const struct malidp_format_id malidp550_de_formats[] = { @@ -223,15 +232,20 @@ static bool malidp500_in_config_mode(struct malidp_hw_device *hwdev) return false; } -static void malidp500_set_config_valid(struct malidp_hw_device *hwdev) +static void malidp500_set_config_valid(struct malidp_hw_device *hwdev, u8 value) { - malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP500_CONFIG_VALID); + if (value) + malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP500_CONFIG_VALID); + else + malidp_hw_clearbits(hwdev, MALIDP_CFG_VALID, MALIDP500_CONFIG_VALID); } static void malidp500_modeset(struct malidp_hw_device *hwdev, struct videomode *mode) { u32 val = 0; + malidp_hw_write(hwdev, hwdev->output_color_depth, + hwdev->hw->map.out_depth_base); malidp_hw_clearbits(hwdev, MALIDP500_DC_CLEAR_MASK, MALIDP500_DC_CONTROL); if (mode->flags & DISPLAY_FLAGS_HSYNC_HIGH) val |= MALIDP500_HSYNCPOL; @@ -368,6 +382,55 @@ static long malidp500_se_calc_mclk(struct malidp_hw_device *hwdev, return ret; } +static int malidp500_enable_memwrite(struct malidp_hw_device *hwdev, + dma_addr_t *addrs, s32 *pitches, + int num_planes, u16 w, u16 h, u32 fmt_id) +{ + u32 base = MALIDP500_SE_MEMWRITE_BASE; + u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK); + + /* enable the scaling engine block */ + malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC); + + /* restart the writeback if already enabled */ + if (hwdev->mw_state != MW_NOT_ENABLED) + hwdev->mw_state = MW_RESTART; + else + hwdev->mw_state = MW_START; + + malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT); + switch (num_planes) { + case 2: + malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW); + malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH); + malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE); + /* fall through */ + case 1: + malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW); + malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH); + malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE); + break; + default: + WARN(1, "Invalid number of planes"); + } + + malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h), + MALIDP500_SE_MEMWRITE_OUT_SIZE); + malidp_hw_setbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL); + + return 0; +} + +static void malidp500_disable_memwrite(struct malidp_hw_device *hwdev) +{ + u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK); + + if (hwdev->mw_state == MW_START || hwdev->mw_state == MW_RESTART) + hwdev->mw_state = MW_STOP; + malidp_hw_clearbits(hwdev, MALIDP_SE_MEMWRITE_EN, MALIDP500_SE_CONTROL); + malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC); +} + static int malidp550_query_hw(struct malidp_hw_device *hwdev) { u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID); @@ -447,15 +510,20 @@ static bool malidp550_in_config_mode(struct malidp_hw_device *hwdev) return false; } -static void malidp550_set_config_valid(struct malidp_hw_device *hwdev) +static void malidp550_set_config_valid(struct malidp_hw_device *hwdev, u8 value) { - malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP550_CONFIG_VALID); + if (value) + malidp_hw_setbits(hwdev, MALIDP_CFG_VALID, MALIDP550_CONFIG_VALID); + else + malidp_hw_clearbits(hwdev, MALIDP_CFG_VALID, MALIDP550_CONFIG_VALID); } static void malidp550_modeset(struct malidp_hw_device *hwdev, struct videomode *mode) { u32 val = MALIDP_DE_DEFAULT_PREFETCH_START; + malidp_hw_write(hwdev, hwdev->output_color_depth, + hwdev->hw->map.out_depth_base); malidp_hw_write(hwdev, val, MALIDP550_DE_CONTROL); /* * Mali-DP550 and Mali-DP650 encode the background color like this: @@ -588,6 +656,51 @@ static long malidp550_se_calc_mclk(struct malidp_hw_device *hwdev, return ret; } +static int malidp550_enable_memwrite(struct malidp_hw_device *hwdev, + dma_addr_t *addrs, s32 *pitches, + int num_planes, u16 w, u16 h, u32 fmt_id) +{ + u32 base = MALIDP550_SE_MEMWRITE_BASE; + u32 de_base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK); + + /* enable the scaling engine block */ + malidp_hw_setbits(hwdev, MALIDP_SCALE_ENGINE_EN, de_base + MALIDP_DE_DISPLAY_FUNC); + + hwdev->mw_state = MW_ONESHOT; + + malidp_hw_write(hwdev, fmt_id, base + MALIDP_MW_FORMAT); + switch (num_planes) { + case 2: + malidp_hw_write(hwdev, lower_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_LOW); + malidp_hw_write(hwdev, upper_32_bits(addrs[1]), base + MALIDP_MW_P2_PTR_HIGH); + malidp_hw_write(hwdev, pitches[1], base + MALIDP_MW_P2_STRIDE); + /* fall through */ + case 1: + malidp_hw_write(hwdev, lower_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_LOW); + malidp_hw_write(hwdev, upper_32_bits(addrs[0]), base + MALIDP_MW_P1_PTR_HIGH); + malidp_hw_write(hwdev, pitches[0], base + MALIDP_MW_P1_STRIDE); + break; + default: + WARN(1, "Invalid number of planes"); + } + + malidp_hw_write(hwdev, MALIDP_DE_H_ACTIVE(w) | MALIDP_DE_V_ACTIVE(h), + MALIDP550_SE_MEMWRITE_OUT_SIZE); + malidp_hw_setbits(hwdev, MALIDP550_SE_MEMWRITE_ONESHOT | MALIDP_SE_MEMWRITE_EN, + MALIDP550_SE_CONTROL); + + return 0; +} + +static void malidp550_disable_memwrite(struct malidp_hw_device *hwdev) +{ + u32 base = malidp_get_block_base(hwdev, MALIDP_DE_BLOCK); + + malidp_hw_clearbits(hwdev, MALIDP550_SE_MEMWRITE_ONESHOT | MALIDP_SE_MEMWRITE_EN, + MALIDP550_SE_CONTROL); + malidp_hw_clearbits(hwdev, MALIDP_SCALE_ENGINE_EN, base + MALIDP_DE_DISPLAY_FUNC); +} + static int malidp650_query_hw(struct malidp_hw_device *hwdev) { u32 conf = malidp_hw_read(hwdev, MALIDP550_CONFIG_ID); @@ -632,10 +745,18 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { MALIDP500_DE_IRQ_VSYNC | MALIDP500_DE_IRQ_GLOBAL, .vsync_irq = MALIDP500_DE_IRQ_VSYNC, + .err_mask = MALIDP_DE_IRQ_UNDERRUN | + MALIDP500_DE_IRQ_AXI_ERR | + MALIDP500_DE_IRQ_SATURATION, }, .se_irq_map = { - .irq_mask = MALIDP500_SE_IRQ_CONF_MODE, - .vsync_irq = 0, + .irq_mask = MALIDP500_SE_IRQ_CONF_MODE | + MALIDP500_SE_IRQ_CONF_VALID | + MALIDP500_SE_IRQ_GLOBAL, + .vsync_irq = MALIDP500_SE_IRQ_CONF_VALID, + .err_mask = MALIDP500_SE_IRQ_INIT_BUSY | + MALIDP500_SE_IRQ_AXI_ERROR | + MALIDP500_SE_IRQ_OVERRUN, }, .dc_irq_map = { .irq_mask = MALIDP500_DE_IRQ_CONF_VALID, @@ -654,6 +775,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .rotmem_required = malidp500_rotmem_required, .se_set_scaling_coeffs = malidp500_se_set_scaling_coeffs, .se_calc_mclk = malidp500_se_calc_mclk, + .enable_memwrite = malidp500_enable_memwrite, + .disable_memwrite = malidp500_disable_memwrite, .features = MALIDP_DEVICE_LV_HAS_3_STRIDES, }, [MALIDP_550] = { @@ -669,13 +792,20 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .irq_mask = MALIDP_DE_IRQ_UNDERRUN | MALIDP550_DE_IRQ_VSYNC, .vsync_irq = MALIDP550_DE_IRQ_VSYNC, + .err_mask = MALIDP_DE_IRQ_UNDERRUN | + MALIDP550_DE_IRQ_SATURATION | + MALIDP550_DE_IRQ_AXI_ERR, }, .se_irq_map = { - .irq_mask = MALIDP550_SE_IRQ_EOW | - MALIDP550_SE_IRQ_AXI_ERR, + .irq_mask = MALIDP550_SE_IRQ_EOW, + .vsync_irq = MALIDP550_SE_IRQ_EOW, + .err_mask = MALIDP550_SE_IRQ_AXI_ERR | + MALIDP550_SE_IRQ_OVR | + MALIDP550_SE_IRQ_IBSY, }, .dc_irq_map = { - .irq_mask = MALIDP550_DC_IRQ_CONF_VALID, + .irq_mask = MALIDP550_DC_IRQ_CONF_VALID | + MALIDP550_DC_IRQ_SE, .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, }, .pixel_formats = malidp550_de_formats, @@ -691,6 +821,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .rotmem_required = malidp550_rotmem_required, .se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs, .se_calc_mclk = malidp550_se_calc_mclk, + .enable_memwrite = malidp550_enable_memwrite, + .disable_memwrite = malidp550_disable_memwrite, .features = 0, }, [MALIDP_650] = { @@ -707,13 +839,25 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { MALIDP650_DE_IRQ_DRIFT | MALIDP550_DE_IRQ_VSYNC, .vsync_irq = MALIDP550_DE_IRQ_VSYNC, + .err_mask = MALIDP_DE_IRQ_UNDERRUN | + MALIDP650_DE_IRQ_DRIFT | + MALIDP550_DE_IRQ_SATURATION | + MALIDP550_DE_IRQ_AXI_ERR | + MALIDP650_DE_IRQ_ACEV1 | + MALIDP650_DE_IRQ_ACEV2 | + MALIDP650_DE_IRQ_ACEG | + MALIDP650_DE_IRQ_AXIEP, }, .se_irq_map = { - .irq_mask = MALIDP550_SE_IRQ_EOW | - MALIDP550_SE_IRQ_AXI_ERR, + .irq_mask = MALIDP550_SE_IRQ_EOW, + .vsync_irq = MALIDP550_SE_IRQ_EOW, + .err_mask = MALIDP550_SE_IRQ_AXI_ERR | + MALIDP550_SE_IRQ_OVR | + MALIDP550_SE_IRQ_IBSY, }, .dc_irq_map = { - .irq_mask = MALIDP550_DC_IRQ_CONF_VALID, + .irq_mask = MALIDP550_DC_IRQ_CONF_VALID | + MALIDP550_DC_IRQ_SE, .vsync_irq = MALIDP550_DC_IRQ_CONF_VALID, }, .pixel_formats = malidp550_de_formats, @@ -729,6 +873,8 @@ const struct malidp_hw malidp_device[MALIDP_MAX_DEVICES] = { .rotmem_required = malidp550_rotmem_required, .se_set_scaling_coeffs = malidp550_se_set_scaling_coeffs, .se_calc_mclk = malidp550_se_calc_mclk, + .enable_memwrite = malidp550_enable_memwrite, + .disable_memwrite = malidp550_disable_memwrite, .features = 0, }, }; @@ -790,7 +936,7 @@ static irqreturn_t malidp_de_irq(int irq, void *arg) malidp->event = NULL; spin_unlock(&drm->event_lock); } - atomic_set(&malidp->config_valid, 1); + atomic_set(&malidp->config_valid, MALIDP_CONFIG_VALID_DONE); ret = IRQ_WAKE_THREAD; } @@ -799,10 +945,17 @@ static irqreturn_t malidp_de_irq(int irq, void *arg) return ret; mask = malidp_hw_read(hwdev, MALIDP_REG_MASKIRQ); - status &= mask; + /* keep the status of the enabled interrupts, plus the error bits */ + status &= (mask | de->err_mask); if ((status & de->vsync_irq) && malidp->crtc.enabled) drm_crtc_handle_vblank(&malidp->crtc); +#ifdef CONFIG_DEBUG_FS + if (status & de->err_mask) { + malidp_error(malidp, &malidp->de_errors, status, + drm_crtc_vblank_count(&malidp->crtc)); + } +#endif malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, status); return (ret == IRQ_NONE) ? IRQ_HANDLED : ret; @@ -818,6 +971,23 @@ static irqreturn_t malidp_de_irq_thread_handler(int irq, void *arg) return IRQ_HANDLED; } +void malidp_de_irq_hw_init(struct malidp_hw_device *hwdev) +{ + /* ensure interrupts are disabled */ + malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff); + malidp_hw_clear_irq(hwdev, MALIDP_DE_BLOCK, 0xffffffff); + malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff); + malidp_hw_clear_irq(hwdev, MALIDP_DC_BLOCK, 0xffffffff); + + /* first enable the DC block IRQs */ + malidp_hw_enable_irq(hwdev, MALIDP_DC_BLOCK, + hwdev->hw->map.dc_irq_map.irq_mask); + + /* now enable the DE block IRQs */ + malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK, + hwdev->hw->map.de_irq_map.irq_mask); +} + int malidp_de_irq_init(struct drm_device *drm, int irq) { struct malidp_drm *malidp = drm->dev_private; @@ -838,22 +1008,13 @@ int malidp_de_irq_init(struct drm_device *drm, int irq) return ret; } - /* first enable the DC block IRQs */ - malidp_hw_enable_irq(hwdev, MALIDP_DC_BLOCK, - hwdev->hw->map.dc_irq_map.irq_mask); - - /* now enable the DE block IRQs */ - malidp_hw_enable_irq(hwdev, MALIDP_DE_BLOCK, - hwdev->hw->map.de_irq_map.irq_mask); + malidp_de_irq_hw_init(hwdev); return 0; } -void malidp_de_irq_fini(struct drm_device *drm) +void malidp_de_irq_fini(struct malidp_hw_device *hwdev) { - struct malidp_drm *malidp = drm->dev_private; - struct malidp_hw_device *hwdev = malidp->dev; - malidp_hw_disable_irq(hwdev, MALIDP_DE_BLOCK, hwdev->hw->map.de_irq_map.irq_mask); malidp_hw_disable_irq(hwdev, MALIDP_DC_BLOCK, @@ -878,19 +1039,61 @@ static irqreturn_t malidp_se_irq(int irq, void *arg) return IRQ_NONE; status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS); - if (!(status & se->irq_mask)) + if (!(status & (se->irq_mask | se->err_mask))) return IRQ_NONE; +#ifdef CONFIG_DEBUG_FS + if (status & se->err_mask) + malidp_error(malidp, &malidp->se_errors, status, + drm_crtc_vblank_count(&malidp->crtc)); +#endif mask = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_MASKIRQ); - status = malidp_hw_read(hwdev, hw->map.se_base + MALIDP_REG_STATUS); status &= mask; - /* ToDo: status decoding and firing up of VSYNC and page flip events */ + + if (status & se->vsync_irq) { + switch (hwdev->mw_state) { + case MW_ONESHOT: + drm_writeback_signal_completion(&malidp->mw_connector, 0); + break; + case MW_STOP: + drm_writeback_signal_completion(&malidp->mw_connector, 0); + /* disable writeback after stop */ + hwdev->mw_state = MW_NOT_ENABLED; + break; + case MW_RESTART: + drm_writeback_signal_completion(&malidp->mw_connector, 0); + /* fall through to a new start */ + case MW_START: + /* writeback started, need to emulate one-shot mode */ + hw->disable_memwrite(hwdev); + /* + * only set config_valid HW bit if there is no other update + * in progress or if we raced ahead of the DE IRQ handler + * and config_valid flag will not be update until later + */ + status = malidp_hw_read(hwdev, hw->map.dc_base + MALIDP_REG_STATUS); + if ((atomic_read(&malidp->config_valid) != MALIDP_CONFIG_START) || + (status & hw->map.dc_irq_map.vsync_irq)) + hw->set_config_valid(hwdev, 1); + break; + } + } malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, status); return IRQ_HANDLED; } +void malidp_se_irq_hw_init(struct malidp_hw_device *hwdev) +{ + /* ensure interrupts are disabled */ + malidp_hw_disable_irq(hwdev, MALIDP_SE_BLOCK, 0xffffffff); + malidp_hw_clear_irq(hwdev, MALIDP_SE_BLOCK, 0xffffffff); + + malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK, + hwdev->hw->map.se_irq_map.irq_mask); +} + static irqreturn_t malidp_se_irq_thread_handler(int irq, void *arg) { return IRQ_HANDLED; @@ -914,17 +1117,14 @@ int malidp_se_irq_init(struct drm_device *drm, int irq) return ret; } - malidp_hw_enable_irq(hwdev, MALIDP_SE_BLOCK, - hwdev->hw->map.se_irq_map.irq_mask); + hwdev->mw_state = MW_NOT_ENABLED; + malidp_se_irq_hw_init(hwdev); return 0; } -void malidp_se_irq_fini(struct drm_device *drm) +void malidp_se_irq_fini(struct malidp_hw_device *hwdev) { - struct malidp_drm *malidp = drm->dev_private; - struct malidp_hw_device *hwdev = malidp->dev; - malidp_hw_disable_irq(hwdev, MALIDP_SE_BLOCK, hwdev->hw->map.se_irq_map.irq_mask); } diff --git a/drivers/gpu/drm/arm/malidp_hw.h b/drivers/gpu/drm/arm/malidp_hw.h index b5dd6c73ec9f..ad2e96915d44 100644 --- a/drivers/gpu/drm/arm/malidp_hw.h +++ b/drivers/gpu/drm/arm/malidp_hw.h @@ -33,6 +33,7 @@ enum { DE_GRAPHICS2 = BIT(2), /* used only in DP500 */ DE_VIDEO2 = BIT(3), DE_SMART = BIT(4), + SE_MEMWRITE = BIT(5), }; struct malidp_format_id { @@ -52,6 +53,7 @@ struct malidp_format_id { struct malidp_irq_map { u32 irq_mask; /* mask of IRQs that can be enabled in the block */ u32 vsync_irq; /* IRQ bit used for signaling during VSYNC */ + u32 err_mask; /* mask of bits that represent errors */ }; struct malidp_layer { @@ -151,12 +153,13 @@ struct malidp_hw { bool (*in_config_mode)(struct malidp_hw_device *hwdev); /* - * Set configuration valid flag for hardware parameters that can - * be changed outside the configuration mode. Hardware will use - * the new settings when config valid is set after the end of the - * current buffer scanout + * Set/clear configuration valid flag for hardware parameters that can + * be changed outside the configuration mode to the given value. + * Hardware will use the new settings when config valid is set, + * after the end of the current buffer scanout, and will ignore + * any new values for those parameters if config valid flag is cleared */ - void (*set_config_valid)(struct malidp_hw_device *hwdev); + void (*set_config_valid)(struct malidp_hw_device *hwdev, u8 value); /* * Set a new mode in hardware. Requires the hardware to be in @@ -177,6 +180,23 @@ struct malidp_hw { long (*se_calc_mclk)(struct malidp_hw_device *hwdev, struct malidp_se_config *se_config, struct videomode *vm); + /* + * Enable writing to memory the content of the next frame + * @param hwdev - malidp_hw_device structure containing the HW description + * @param addrs - array of addresses for each plane + * @param pitches - array of pitches for each plane + * @param num_planes - number of planes to be written + * @param w - width of the output frame + * @param h - height of the output frame + * @param fmt_id - internal format ID of output buffer + */ + int (*enable_memwrite)(struct malidp_hw_device *hwdev, dma_addr_t *addrs, + s32 *pitches, int num_planes, u16 w, u16 h, u32 fmt_id); + + /* + * Disable the writing to memory of the next frame's content. + */ + void (*disable_memwrite)(struct malidp_hw_device *hwdev); u8 features; }; @@ -210,10 +230,14 @@ struct malidp_hw_device { u8 min_line_size; u16 max_line_size; + u32 output_color_depth; /* track the device PM state */ bool pm_suspended; + /* track the SE memory writeback state */ + u8 mw_state; + /* size of memory used for rotating layers, up to two banks available */ u32 rotation_memory[2]; }; @@ -279,9 +303,11 @@ static inline void malidp_hw_enable_irq(struct malidp_hw_device *hwdev, } int malidp_de_irq_init(struct drm_device *drm, int irq); -void malidp_de_irq_fini(struct drm_device *drm); +void malidp_se_irq_hw_init(struct malidp_hw_device *hwdev); +void malidp_de_irq_hw_init(struct malidp_hw_device *hwdev); +void malidp_de_irq_fini(struct malidp_hw_device *hwdev); int malidp_se_irq_init(struct drm_device *drm, int irq); -void malidp_se_irq_fini(struct drm_device *drm); +void malidp_se_irq_fini(struct malidp_hw_device *hwdev); u8 malidp_hw_get_format_id(const struct malidp_hw_regmap *map, u8 layer_id, u32 format); diff --git a/drivers/gpu/drm/arm/malidp_mw.c b/drivers/gpu/drm/arm/malidp_mw.c new file mode 100644 index 000000000000..cfd718e7e97c --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_mw.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Brian Starkey <brian.starkey@arm.com> + * + * ARM Mali DP Writeback connector implementation + */ +#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/drmP.h> +#include <drm/drm_writeback.h> + +#include "malidp_drv.h" +#include "malidp_hw.h" +#include "malidp_mw.h" + +#define to_mw_state(_state) (struct malidp_mw_connector_state *)(_state) + +struct malidp_mw_connector_state { + struct drm_connector_state base; + dma_addr_t addrs[2]; + s32 pitches[2]; + u8 format; + u8 n_planes; +}; + +static int malidp_mw_connector_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + + return drm_add_modes_noedid(connector, dev->mode_config.max_width, + dev->mode_config.max_height); +} + +static enum drm_mode_status +malidp_mw_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + struct drm_mode_config *mode_config = &dev->mode_config; + int w = mode->hdisplay, h = mode->vdisplay; + + if ((w < mode_config->min_width) || (w > mode_config->max_width)) + return MODE_BAD_HVALUE; + + if ((h < mode_config->min_height) || (h > mode_config->max_height)) + return MODE_BAD_VVALUE; + + return MODE_OK; +} + +const struct drm_connector_helper_funcs malidp_mw_connector_helper_funcs = { + .get_modes = malidp_mw_connector_get_modes, + .mode_valid = malidp_mw_connector_mode_valid, +}; + +static void malidp_mw_connector_reset(struct drm_connector *connector) +{ + struct malidp_mw_connector_state *mw_state = + kzalloc(sizeof(*mw_state), GFP_KERNEL); + + if (connector->state) + __drm_atomic_helper_connector_destroy_state(connector->state); + + kfree(connector->state); + __drm_atomic_helper_connector_reset(connector, &mw_state->base); +} + +static enum drm_connector_status +malidp_mw_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_disconnected; +} + +static void malidp_mw_connector_destroy(struct drm_connector *connector) +{ + drm_connector_cleanup(connector); +} + +static struct drm_connector_state * +malidp_mw_connector_duplicate_state(struct drm_connector *connector) +{ + struct malidp_mw_connector_state *mw_state; + + if (WARN_ON(!connector->state)) + return NULL; + + mw_state = kzalloc(sizeof(*mw_state), GFP_KERNEL); + if (!mw_state) + return NULL; + + /* No need to preserve any of our driver-local data */ + __drm_atomic_helper_connector_duplicate_state(connector, &mw_state->base); + + return &mw_state->base; +} + +static const struct drm_connector_funcs malidp_mw_connector_funcs = { + .reset = malidp_mw_connector_reset, + .detect = malidp_mw_connector_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = malidp_mw_connector_destroy, + .atomic_duplicate_state = malidp_mw_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static int +malidp_mw_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct malidp_mw_connector_state *mw_state = to_mw_state(conn_state); + struct malidp_drm *malidp = encoder->dev->dev_private; + struct drm_framebuffer *fb; + int i, n_planes; + + if (!conn_state->writeback_job || !conn_state->writeback_job->fb) + return 0; + + fb = conn_state->writeback_job->fb; + if ((fb->width != crtc_state->mode.hdisplay) || + (fb->height != crtc_state->mode.vdisplay)) { + DRM_DEBUG_KMS("Invalid framebuffer size %ux%u\n", + fb->width, fb->height); + return -EINVAL; + } + + mw_state->format = + malidp_hw_get_format_id(&malidp->dev->hw->map, SE_MEMWRITE, + fb->format->format); + if (mw_state->format == MALIDP_INVALID_FORMAT_ID) { + struct drm_format_name_buf format_name; + + DRM_DEBUG_KMS("Invalid pixel format %s\n", + drm_get_format_name(fb->format->format, + &format_name)); + return -EINVAL; + } + + n_planes = drm_format_num_planes(fb->format->format); + for (i = 0; i < n_planes; i++) { + struct drm_gem_cma_object *obj = drm_fb_cma_get_gem_obj(fb, i); + /* memory write buffers are never rotated */ + u8 alignment = malidp_hw_get_pitch_align(malidp->dev, 0); + + if (fb->pitches[i] & (alignment - 1)) { + DRM_DEBUG_KMS("Invalid pitch %u for plane %d\n", + fb->pitches[i], i); + return -EINVAL; + } + mw_state->pitches[i] = fb->pitches[i]; + mw_state->addrs[i] = obj->paddr + fb->offsets[i]; + } + mw_state->n_planes = n_planes; + + return 0; +} + +static const struct drm_encoder_helper_funcs malidp_mw_encoder_helper_funcs = { + .atomic_check = malidp_mw_encoder_atomic_check, +}; + +static u32 *get_writeback_formats(struct malidp_drm *malidp, int *n_formats) +{ + const struct malidp_hw_regmap *map = &malidp->dev->hw->map; + u32 *formats; + int n, i; + + formats = kcalloc(map->n_pixel_formats, sizeof(*formats), + GFP_KERNEL); + if (!formats) + return NULL; + + for (n = 0, i = 0; i < map->n_pixel_formats; i++) { + if (map->pixel_formats[i].layer & SE_MEMWRITE) + formats[n++] = map->pixel_formats[i].format; + } + + *n_formats = n; + + return formats; +} + +int malidp_mw_connector_init(struct drm_device *drm) +{ + struct malidp_drm *malidp = drm->dev_private; + u32 *formats; + int ret, n_formats; + + if (!malidp->dev->hw->enable_memwrite) + return 0; + + malidp->mw_connector.encoder.possible_crtcs = 1 << drm_crtc_index(&malidp->crtc); + drm_connector_helper_add(&malidp->mw_connector.base, + &malidp_mw_connector_helper_funcs); + + formats = get_writeback_formats(malidp, &n_formats); + if (!formats) + return -ENOMEM; + + ret = drm_writeback_connector_init(drm, &malidp->mw_connector, + &malidp_mw_connector_funcs, + &malidp_mw_encoder_helper_funcs, + formats, n_formats); + kfree(formats); + if (ret) + return ret; + + return 0; +} + +void malidp_mw_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *old_state) +{ + struct malidp_drm *malidp = drm->dev_private; + struct drm_writeback_connector *mw_conn = &malidp->mw_connector; + struct drm_connector_state *conn_state = mw_conn->base.state; + struct malidp_hw_device *hwdev = malidp->dev; + struct malidp_mw_connector_state *mw_state; + + if (!conn_state) + return; + + mw_state = to_mw_state(conn_state); + + if (conn_state->writeback_job && conn_state->writeback_job->fb) { + struct drm_framebuffer *fb = conn_state->writeback_job->fb; + + DRM_DEV_DEBUG_DRIVER(drm->dev, + "Enable memwrite %ux%u:%d %pad fmt: %u\n", + fb->width, fb->height, + mw_state->pitches[0], + &mw_state->addrs[0], + mw_state->format); + + drm_writeback_queue_job(mw_conn, conn_state->writeback_job); + conn_state->writeback_job = NULL; + + hwdev->hw->enable_memwrite(hwdev, mw_state->addrs, + mw_state->pitches, mw_state->n_planes, + fb->width, fb->height, mw_state->format); + } else { + DRM_DEV_DEBUG_DRIVER(drm->dev, "Disable memwrite\n"); + hwdev->hw->disable_memwrite(hwdev); + } +} diff --git a/drivers/gpu/drm/arm/malidp_mw.h b/drivers/gpu/drm/arm/malidp_mw.h new file mode 100644 index 000000000000..19a007676a1d --- /dev/null +++ b/drivers/gpu/drm/arm/malidp_mw.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * (C) COPYRIGHT 2016 ARM Limited. All rights reserved. + * Author: Brian Starkey <brian.starkey@arm.com> + * + */ + +#ifndef __MALIDP_MW_H__ +#define __MALIDP_MW_H__ + +int malidp_mw_connector_init(struct drm_device *drm); +void malidp_mw_atomic_commit(struct drm_device *drm, + struct drm_atomic_state *old_state); +#endif diff --git a/drivers/gpu/drm/arm/malidp_planes.c b/drivers/gpu/drm/arm/malidp_planes.c index 7a44897c50fe..29409a65d864 100644 --- a/drivers/gpu/drm/arm/malidp_planes.c +++ b/drivers/gpu/drm/arm/malidp_planes.c @@ -23,6 +23,7 @@ /* Layer specific register offsets */ #define MALIDP_LAYER_FORMAT 0x000 +#define LAYER_FORMAT_MASK 0x3f #define MALIDP_LAYER_CONTROL 0x004 #define LAYER_ENABLE (1 << 0) #define LAYER_FLOWCFG_MASK 7 @@ -235,8 +236,8 @@ static int malidp_de_plane_check(struct drm_plane *plane, if (state->rotation & MALIDP_ROTATED_MASK) { int val; - val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_h, - state->crtc_w, + val = mp->hwdev->hw->rotmem_required(mp->hwdev, state->crtc_w, + state->crtc_h, fb->format->format); if (val < 0) return val; @@ -337,7 +338,9 @@ static void malidp_de_plane_update(struct drm_plane *plane, dest_w = plane->state->crtc_w; dest_h = plane->state->crtc_h; - malidp_hw_write(mp->hwdev, ms->format, mp->layer->base); + val = malidp_hw_read(mp->hwdev, mp->layer->base); + val = (val & ~LAYER_FORMAT_MASK) | ms->format; + malidp_hw_write(mp->hwdev, val, mp->layer->base); for (i = 0; i < ms->n_planes; i++) { /* calculate the offset for the layer's plane registers */ diff --git a/drivers/gpu/drm/arm/malidp_regs.h b/drivers/gpu/drm/arm/malidp_regs.h index 149024fb4432..3579d36b2a71 100644 --- a/drivers/gpu/drm/arm/malidp_regs.h +++ b/drivers/gpu/drm/arm/malidp_regs.h @@ -53,6 +53,8 @@ #define MALIDP550_DE_IRQ_AXI_ERR (1 << 16) #define MALIDP550_SE_IRQ_EOW (1 << 0) #define MALIDP550_SE_IRQ_AXI_ERR (1 << 16) +#define MALIDP550_SE_IRQ_OVR (1 << 17) +#define MALIDP550_SE_IRQ_IBSY (1 << 18) #define MALIDP550_DC_IRQ_CONF_VALID (1 << 0) #define MALIDP550_DC_IRQ_CONF_MODE (1 << 4) #define MALIDP550_DC_IRQ_CONF_ACTIVE (1 << 16) @@ -60,12 +62,18 @@ #define MALIDP550_DC_IRQ_SE (1 << 24) #define MALIDP650_DE_IRQ_DRIFT (1 << 4) +#define MALIDP650_DE_IRQ_ACEV1 (1 << 17) +#define MALIDP650_DE_IRQ_ACEV2 (1 << 18) +#define MALIDP650_DE_IRQ_ACEG (1 << 19) +#define MALIDP650_DE_IRQ_AXIEP (1 << 28) /* bit masks that are common between products */ #define MALIDP_CFG_VALID (1 << 0) #define MALIDP_DISP_FUNC_GAMMA (1 << 0) #define MALIDP_DISP_FUNC_CADJ (1 << 4) #define MALIDP_DISP_FUNC_ILACED (1 << 8) +#define MALIDP_SCALE_ENGINE_EN (1 << 16) +#define MALIDP_SE_MEMWRITE_EN (2 << 5) /* register offsets for IRQ management */ #define MALIDP_REG_STATUS 0x00000 @@ -153,6 +161,16 @@ (((x) & MALIDP_SE_ENH_LIMIT_MASK) << 16) #define MALIDP_SE_ENH_COEFF0 0x04 + +/* register offsets relative to MALIDP5x0_SE_MEMWRITE_BASE */ +#define MALIDP_MW_FORMAT 0x00000 +#define MALIDP_MW_P1_STRIDE 0x00004 +#define MALIDP_MW_P2_STRIDE 0x00008 +#define MALIDP_MW_P1_PTR_LOW 0x0000c +#define MALIDP_MW_P1_PTR_HIGH 0x00010 +#define MALIDP_MW_P2_PTR_LOW 0x0002c +#define MALIDP_MW_P2_PTR_HIGH 0x00030 + /* register offsets and bits specific to DP500 */ #define MALIDP500_ADDR_SPACE_SIZE 0x01000 #define MALIDP500_DC_BASE 0x00000 @@ -186,7 +204,8 @@ #define MALIDP500_DE_LG2_PTR_BASE 0x0031c #define MALIDP500_SE_BASE 0x00c00 #define MALIDP500_SE_CONTROL 0x00c0c -#define MALIDP500_SE_PTR_BASE 0x00e0c +#define MALIDP500_SE_MEMWRITE_OUT_SIZE 0x00c2c +#define MALIDP500_SE_MEMWRITE_BASE 0x00e00 #define MALIDP500_DC_IRQ_BASE 0x00f00 #define MALIDP500_CONFIG_VALID 0x00f00 #define MALIDP500_CONFIG_ID 0x00fd4 @@ -217,6 +236,9 @@ #define MALIDP550_DE_PERF_BASE 0x00500 #define MALIDP550_SE_BASE 0x08000 #define MALIDP550_SE_CONTROL 0x08010 +#define MALIDP550_SE_MEMWRITE_ONESHOT (1 << 7) +#define MALIDP550_SE_MEMWRITE_OUT_SIZE 0x08030 +#define MALIDP550_SE_MEMWRITE_BASE 0x08100 #define MALIDP550_DC_BASE 0x0c000 #define MALIDP550_DC_CONTROL 0x0c010 #define MALIDP550_DC_CONFIG_REQ (1 << 16) diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index 7c55991170cc..e6062c779aaf 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -1437,6 +1437,10 @@ static void drm_atomic_connector_print_state(struct drm_printer *p, drm_printf(p, "connector[%u]: %s\n", connector->base.id, connector->name); drm_printf(p, "\tcrtc=%s\n", state->crtc ? state->crtc->name : "(null)"); + if (connector->connector_type == DRM_MODE_CONNECTOR_WRITEBACK) + if (state->writeback_job && state->writeback_job->fb) + drm_printf(p, "\tfb=%d\n", state->writeback_job->fb->base.id); + if (connector->funcs->atomic_print_state) connector->funcs->atomic_print_state(p, state); } diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 735f695cb889..f4751b383858 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2261,9 +2261,6 @@ static inline struct scatterlist *____sg_next(struct scatterlist *sg) **/ static inline struct scatterlist *__sg_next(struct scatterlist *sg) { -#ifdef CONFIG_DEBUG_SG - BUG_ON(sg->sg_magic != SG_MAGIC); -#endif return sg_is_last(sg) ? NULL : ____sg_next(sg); } diff --git a/drivers/gpu/drm/meson/meson_drv.c b/drivers/gpu/drm/meson/meson_drv.c index 32b1a6cdecfc..d3443125e661 100644 --- a/drivers/gpu/drm/meson/meson_drv.c +++ b/drivers/gpu/drm/meson/meson_drv.c @@ -197,8 +197,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) priv->io_base = regs; res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hhi"); - if (!res) - return -EINVAL; + if (!res) { + ret = -EINVAL; + goto free_drm; + } /* Simply ioremap since it may be a shared register zone */ regs = devm_ioremap(dev, res->start, resource_size(res)); if (!regs) { @@ -215,8 +217,10 @@ static int meson_drv_bind_master(struct device *dev, bool has_components) } res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmc"); - if (!res) - return -EINVAL; + if (!res) { + ret = -EINVAL; + goto free_drm; + } /* Simply ioremap since it may be a shared register zone */ regs = devm_ioremap(dev, res->start, resource_size(res)); if (!regs) { diff --git a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c index 92fe125ce22e..f34c06bb5bd7 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-sony-acx565akm.c @@ -4,7 +4,7 @@ * Copyright (C) 2010 Nokia Corporation * * Original Driver Author: Imre Deak <imre.deak@nokia.com> - * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@ti.com> * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> * * This program is free software; you can redistribute it and/or modify it diff --git a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c index b5d8a00df811..a1f1dc18407a 100644 --- a/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c +++ b/drivers/gpu/drm/omapdrm/displays/panel-tpo-td028ttec1.c @@ -2,7 +2,7 @@ * Toppoly TD028TTEC1 panel support * * Copyright (C) 2008 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Neo 1973 code (jbt6k74.c): * Copyright (C) 2006-2007 by OpenMoko, Inc. diff --git a/drivers/gpu/drm/omapdrm/dss/core.c b/drivers/gpu/drm/omapdrm/dss/core.c index acef7ece5783..07d00a186f15 100644 --- a/drivers/gpu/drm/omapdrm/dss/core.c +++ b/drivers/gpu/drm/omapdrm/dss/core.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. @@ -82,7 +82,7 @@ static void __exit omap_dss_exit(void) module_init(omap_dss_init); module_exit(omap_dss_exit); -MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/omapdrm/dss/dispc.c b/drivers/gpu/drm/omapdrm/dss/dispc.c index 7f3ac6b13b56..84f274c4a4cb 100644 --- a/drivers/gpu/drm/omapdrm/dss/dispc.c +++ b/drivers/gpu/drm/omapdrm/dss/dispc.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. diff --git a/drivers/gpu/drm/omapdrm/dss/display.c b/drivers/gpu/drm/omapdrm/dss/display.c index 424143128cd4..9e7fcbd57e52 100644 --- a/drivers/gpu/drm/omapdrm/dss/display.c +++ b/drivers/gpu/drm/omapdrm/dss/display.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. diff --git a/drivers/gpu/drm/omapdrm/dss/dpi.c b/drivers/gpu/drm/omapdrm/dss/dpi.c index 3d662e6805eb..9fcc50217133 100644 --- a/drivers/gpu/drm/omapdrm/dss/dpi.c +++ b/drivers/gpu/drm/omapdrm/dss/dpi.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. diff --git a/drivers/gpu/drm/omapdrm/dss/dsi.c b/drivers/gpu/drm/omapdrm/dss/dsi.c index d4a680629825..74467b308721 100644 --- a/drivers/gpu/drm/omapdrm/dss/dsi.c +++ b/drivers/gpu/drm/omapdrm/dss/dsi.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * 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 diff --git a/drivers/gpu/drm/omapdrm/dss/dss.c b/drivers/gpu/drm/omapdrm/dss/dss.c index 0b908e9de792..cb80ddaa19d2 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.c +++ b/drivers/gpu/drm/omapdrm/dss/dss.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. diff --git a/drivers/gpu/drm/omapdrm/dss/dss.h b/drivers/gpu/drm/omapdrm/dss/dss.h index 847c78ade024..38302631b64b 100644 --- a/drivers/gpu/drm/omapdrm/dss/dss.h +++ b/drivers/gpu/drm/omapdrm/dss/dss.h @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * Some code and ideas taken from drivers/video/omap/ driver * by Imre Deak. @@ -180,6 +180,9 @@ struct dss_pll_hw { /* DRA7 errata i886: use high N & M to avoid jitter */ bool errata_i886; + + /* DRA7 errata i932: retry pll lock on failure */ + bool errata_i932; }; struct dss_pll { diff --git a/drivers/gpu/drm/omapdrm/dss/pll.c b/drivers/gpu/drm/omapdrm/dss/pll.c index 078b0e8216c3..ff362b38bf0d 100644 --- a/drivers/gpu/drm/omapdrm/dss/pll.c +++ b/drivers/gpu/drm/omapdrm/dss/pll.c @@ -16,6 +16,7 @@ #define DSS_SUBSYS_NAME "PLL" +#include <linux/delay.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/kernel.h> @@ -381,6 +382,22 @@ static int dss_wait_hsdiv_ack(struct dss_pll *pll, u32 hsdiv_ack_mask) return -ETIMEDOUT; } +static bool pll_is_locked(u32 stat) +{ + /* + * Required value for each bitfield listed below + * + * PLL_STATUS[6] = 0 PLL_BYPASS + * PLL_STATUS[5] = 0 PLL_HIGHJITTER + * + * PLL_STATUS[3] = 0 PLL_LOSSREF + * PLL_STATUS[2] = 0 PLL_RECAL + * PLL_STATUS[1] = 1 PLL_LOCK + * PLL_STATUS[0] = 1 PLL_CTRL_RESET_DONE + */ + return ((stat & 0x6f) == 0x3); +} + int dss_pll_write_config_type_a(struct dss_pll *pll, const struct dss_pll_clock_info *cinfo) { @@ -436,18 +453,54 @@ int dss_pll_write_config_type_a(struct dss_pll *pll, l = FLD_MOD(l, 0, 25, 25); /* M7_CLOCK_EN */ writel_relaxed(l, base + PLL_CONFIGURATION2); - writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + if (hw->errata_i932) { + int cnt = 0; + u32 sleep_time; + const u32 max_lock_retries = 20; - if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { - DSSERR("DSS DPLL GO bit not going down.\n"); - r = -EIO; - goto err; - } + /* + * Calculate wait time for PLL LOCK + * 1000 REFCLK cycles in us. + */ + sleep_time = DIV_ROUND_UP(1000*1000*1000, cinfo->fint); - if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { - DSSERR("cannot lock DSS DPLL\n"); - r = -EIO; - goto err; + for (cnt = 0; cnt < max_lock_retries; cnt++) { + writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + + /** + * read the register back to ensure the write is + * flushed + */ + readl_relaxed(base + PLL_GO); + + usleep_range(sleep_time, sleep_time + 5); + l = readl_relaxed(base + PLL_STATUS); + + if (pll_is_locked(l) && + !(readl_relaxed(base + PLL_GO) & 0x1)) + break; + + } + + if (cnt == max_lock_retries) { + DSSERR("cannot lock PLL\n"); + r = -EIO; + goto err; + } + } else { + writel_relaxed(1, base + PLL_GO); /* PLL_GO */ + + if (wait_for_bit_change(base + PLL_GO, 0, 0) != 0) { + DSSERR("DSS DPLL GO bit not going down.\n"); + r = -EIO; + goto err; + } + + if (wait_for_bit_change(base + PLL_STATUS, 1, 1) != 1) { + DSSERR("cannot lock DSS DPLL\n"); + r = -EIO; + goto err; + } } l = readl_relaxed(base + PLL_CONFIGURATION2); diff --git a/drivers/gpu/drm/omapdrm/dss/sdi.c b/drivers/gpu/drm/omapdrm/dss/sdi.c index 1e2c931f6acf..69c3b7a3d5c7 100644 --- a/drivers/gpu/drm/omapdrm/dss/sdi.c +++ b/drivers/gpu/drm/omapdrm/dss/sdi.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * 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 diff --git a/drivers/gpu/drm/omapdrm/dss/venc.c b/drivers/gpu/drm/omapdrm/dss/venc.c index 24d1ced210bd..ac01907dcc34 100644 --- a/drivers/gpu/drm/omapdrm/dss/venc.c +++ b/drivers/gpu/drm/omapdrm/dss/venc.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2009 Nokia Corporation - * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> + * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> * * VENC settings from TI's DSS driver * diff --git a/drivers/gpu/drm/omapdrm/dss/video-pll.c b/drivers/gpu/drm/omapdrm/dss/video-pll.c index 585ed94ccf17..cb46311f92c9 100644 --- a/drivers/gpu/drm/omapdrm/dss/video-pll.c +++ b/drivers/gpu/drm/omapdrm/dss/video-pll.c @@ -134,6 +134,7 @@ static const struct dss_pll_hw dss_dra7_video_pll_hw = { .has_refsel = true, .errata_i886 = true, + .errata_i932 = true, }; struct dss_pll *dss_video_pll_init(struct dss_device *dss, diff --git a/drivers/gpu/drm/omapdrm/omap_debugfs.c b/drivers/gpu/drm/omapdrm/omap_debugfs.c index b42e286616b0..91cf043f2b6b 100644 --- a/drivers/gpu/drm/omapdrm/omap_debugfs.c +++ b/drivers/gpu/drm/omapdrm/omap_debugfs.c @@ -30,16 +30,11 @@ static int gem_show(struct seq_file *m, void *arg) struct drm_info_node *node = (struct drm_info_node *) m->private; struct drm_device *dev = node->minor->dev; struct omap_drm_private *priv = dev->dev_private; - int ret; - - ret = mutex_lock_interruptible(&dev->struct_mutex); - if (ret) - return ret; seq_printf(m, "All Objects:\n"); + mutex_lock(&priv->list_lock); omap_gem_describe_objects(&priv->obj_list, m); - - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&priv->list_lock); return 0; } diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index ef3b0e3571ec..5005ecc284d2 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c @@ -493,7 +493,7 @@ static struct drm_driver omap_drm_driver = { .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_export = omap_gem_prime_export, .gem_prime_import = omap_gem_prime_import, - .gem_free_object = omap_gem_free_object, + .gem_free_object_unlocked = omap_gem_free_object, .gem_vm_ops = &omap_gem_vm_ops, .dumb_create = omap_gem_dumb_create, .dumb_map_offset = omap_gem_dumb_map_offset, @@ -540,7 +540,7 @@ static int omapdrm_init(struct omap_drm_private *priv, struct device *dev) priv->omaprev = soc ? (unsigned int)soc->data : 0; priv->wq = alloc_ordered_workqueue("omapdrm", 0); - spin_lock_init(&priv->list_lock); + mutex_init(&priv->list_lock); INIT_LIST_HEAD(&priv->obj_list); /* Allocate and initialize the DRM device. */ diff --git a/drivers/gpu/drm/omapdrm/omap_drv.h b/drivers/gpu/drm/omapdrm/omap_drv.h index 6eaee4df4559..f27c8e216adf 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.h +++ b/drivers/gpu/drm/omapdrm/omap_drv.h @@ -71,7 +71,7 @@ struct omap_drm_private { struct workqueue_struct *wq; /* lock for obj_list below */ - spinlock_t list_lock; + struct mutex list_lock; /* list of GEM objects: */ struct list_head obj_list; diff --git a/drivers/gpu/drm/omapdrm/omap_fbdev.c b/drivers/gpu/drm/omapdrm/omap_fbdev.c index 0f66c74a54b0..d958cc813a94 100644 --- a/drivers/gpu/drm/omapdrm/omap_fbdev.c +++ b/drivers/gpu/drm/omapdrm/omap_fbdev.c @@ -170,13 +170,11 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, goto fail; } - mutex_lock(&dev->struct_mutex); - fbi = drm_fb_helper_alloc_fbi(helper); if (IS_ERR(fbi)) { dev_err(dev->dev, "failed to allocate fb info\n"); ret = PTR_ERR(fbi); - goto fail_unlock; + goto fail; } DBG("fbi=%p, dev=%p", fbi, dev); @@ -212,12 +210,8 @@ static int omap_fbdev_create(struct drm_fb_helper *helper, DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height); - mutex_unlock(&dev->struct_mutex); - return 0; -fail_unlock: - mutex_unlock(&dev->struct_mutex); fail: if (ret) { diff --git a/drivers/gpu/drm/omapdrm/omap_gem.c b/drivers/gpu/drm/omapdrm/omap_gem.c index 17a53d207978..4ba5d035c590 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.c +++ b/drivers/gpu/drm/omapdrm/omap_gem.c @@ -47,6 +47,9 @@ struct omap_gem_object { /** roll applied when mapping to DMM */ u32 roll; + /** protects dma_addr_cnt, block, pages, dma_addrs and vaddr */ + struct mutex lock; + /** * dma_addr contains the buffer DMA address. It is valid for * @@ -137,14 +140,12 @@ struct omap_drm_usergart { */ /** get mmap offset */ -static u64 mmap_offset(struct drm_gem_object *obj) +u64 omap_gem_mmap_offset(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; int ret; size_t size; - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - /* Make it mmapable */ size = omap_gem_mmap_size(obj); ret = drm_gem_create_mmap_offset_size(obj, size); @@ -156,7 +157,7 @@ static u64 mmap_offset(struct drm_gem_object *obj) return drm_vma_node_offset_addr(&obj->vma_node); } -static bool is_contiguous(struct omap_gem_object *omap_obj) +static bool omap_gem_is_contiguous(struct omap_gem_object *omap_obj) { if (omap_obj->flags & OMAP_BO_MEM_DMA_API) return true; @@ -171,14 +172,14 @@ static bool is_contiguous(struct omap_gem_object *omap_obj) * Eviction */ -static void evict_entry(struct drm_gem_object *obj, +static void omap_gem_evict_entry(struct drm_gem_object *obj, enum tiler_fmt fmt, struct omap_drm_usergart_entry *entry) { struct omap_gem_object *omap_obj = to_omap_bo(obj); struct omap_drm_private *priv = obj->dev->dev_private; int n = priv->usergart[fmt].height; size_t size = PAGE_SIZE * n; - loff_t off = mmap_offset(obj) + + loff_t off = omap_gem_mmap_offset(obj) + (entry->obj_pgoff << PAGE_SHIFT); const int m = DIV_ROUND_UP(omap_obj->width << fmt, PAGE_SIZE); @@ -199,7 +200,7 @@ static void evict_entry(struct drm_gem_object *obj, } /* Evict a buffer from usergart, if it is mapped there */ -static void evict(struct drm_gem_object *obj) +static void omap_gem_evict(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); struct omap_drm_private *priv = obj->dev->dev_private; @@ -213,7 +214,7 @@ static void evict(struct drm_gem_object *obj) &priv->usergart[fmt].entry[i]; if (entry->obj == obj) - evict_entry(obj, fmt, entry); + omap_gem_evict_entry(obj, fmt, entry); } } } @@ -222,7 +223,10 @@ static void evict(struct drm_gem_object *obj) * Page Management */ -/** ensure backing pages are allocated */ +/* + * Ensure backing pages are allocated. Must be called with the omap_obj.lock + * held. + */ static int omap_gem_attach_pages(struct drm_gem_object *obj) { struct drm_device *dev = obj->dev; @@ -232,7 +236,14 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj) int i, ret; dma_addr_t *addrs; - WARN_ON(omap_obj->pages); + lockdep_assert_held(&omap_obj->lock); + + /* + * If not using shmem (in which case backing pages don't need to be + * allocated) or if pages are already allocated we're done. + */ + if (!(omap_obj->flags & OMAP_BO_MEM_SHMEM) || omap_obj->pages) + return 0; pages = drm_gem_get_pages(obj); if (IS_ERR(pages)) { @@ -288,35 +299,15 @@ free_pages: return ret; } -/* acquire pages when needed (for example, for DMA where physically - * contiguous buffer is not required - */ -static int get_pages(struct drm_gem_object *obj, struct page ***pages) -{ - struct omap_gem_object *omap_obj = to_omap_bo(obj); - int ret = 0; - - if ((omap_obj->flags & OMAP_BO_MEM_SHMEM) && !omap_obj->pages) { - ret = omap_gem_attach_pages(obj); - if (ret) { - dev_err(obj->dev->dev, "could not attach pages\n"); - return ret; - } - } - - /* TODO: even phys-contig.. we should have a list of pages? */ - *pages = omap_obj->pages; - - return 0; -} - -/** release backing pages */ +/* Release backing pages. Must be called with the omap_obj.lock held. */ static void omap_gem_detach_pages(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); unsigned int npages = obj->size >> PAGE_SHIFT; unsigned int i; + lockdep_assert_held(&omap_obj->lock); + for (i = 0; i < npages; i++) { if (omap_obj->dma_addrs[i]) dma_unmap_page(obj->dev->dev, omap_obj->dma_addrs[i], @@ -336,16 +327,6 @@ u32 omap_gem_flags(struct drm_gem_object *obj) return to_omap_bo(obj)->flags; } -u64 omap_gem_mmap_offset(struct drm_gem_object *obj) -{ - u64 offset; - - mutex_lock(&obj->dev->struct_mutex); - offset = mmap_offset(obj); - mutex_unlock(&obj->dev->struct_mutex); - return offset; -} - /** get mmap size */ size_t omap_gem_mmap_size(struct drm_gem_object *obj) { @@ -371,7 +352,7 @@ size_t omap_gem_mmap_size(struct drm_gem_object *obj) */ /* Normal handling for the case of faulting in non-tiled buffers */ -static int fault_1d(struct drm_gem_object *obj, +static vm_fault_t omap_gem_fault_1d(struct drm_gem_object *obj, struct vm_area_struct *vma, struct vm_fault *vmf) { struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -385,18 +366,19 @@ static int fault_1d(struct drm_gem_object *obj, omap_gem_cpu_sync_page(obj, pgoff); pfn = page_to_pfn(omap_obj->pages[pgoff]); } else { - BUG_ON(!is_contiguous(omap_obj)); + BUG_ON(!omap_gem_is_contiguous(omap_obj)); pfn = (omap_obj->dma_addr >> PAGE_SHIFT) + pgoff; } VERB("Inserting %p pfn %lx, pa %lx", (void *)vmf->address, pfn, pfn << PAGE_SHIFT); - return vm_insert_mixed(vma, vmf->address, __pfn_to_pfn_t(pfn, PFN_DEV)); + return vmf_insert_mixed(vma, vmf->address, + __pfn_to_pfn_t(pfn, PFN_DEV)); } /* Special handling for the case of faulting in 2d tiled buffers */ -static int fault_2d(struct drm_gem_object *obj, +static vm_fault_t omap_gem_fault_2d(struct drm_gem_object *obj, struct vm_area_struct *vma, struct vm_fault *vmf) { struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -407,7 +389,8 @@ static int fault_2d(struct drm_gem_object *obj, unsigned long pfn; pgoff_t pgoff, base_pgoff; unsigned long vaddr; - int i, ret, slots; + int i, err, slots; + vm_fault_t ret = VM_FAULT_NOPAGE; /* * Note the height of the slot is also equal to the number of pages @@ -443,7 +426,7 @@ static int fault_2d(struct drm_gem_object *obj, /* evict previous buffer using this usergart entry, if any: */ if (entry->obj) - evict_entry(entry->obj, fmt, entry); + omap_gem_evict_entry(entry->obj, fmt, entry); entry->obj = obj; entry->obj_pgoff = base_pgoff; @@ -473,9 +456,10 @@ static int fault_2d(struct drm_gem_object *obj, memset(pages + slots, 0, sizeof(struct page *) * (n - slots)); - ret = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true); - if (ret) { - dev_err(obj->dev->dev, "failed to pin: %d\n", ret); + err = tiler_pin(entry->block, pages, ARRAY_SIZE(pages), 0, true); + if (err) { + ret = vmf_error(err); + dev_err(obj->dev->dev, "failed to pin: %d\n", err); return ret; } @@ -485,7 +469,10 @@ static int fault_2d(struct drm_gem_object *obj, pfn, pfn << PAGE_SHIFT); for (i = n; i > 0; i--) { - vm_insert_mixed(vma, vaddr, __pfn_to_pfn_t(pfn, PFN_DEV)); + ret = vmf_insert_mixed(vma, + vaddr, __pfn_to_pfn_t(pfn, PFN_DEV)); + if (ret & VM_FAULT_ERROR) + break; pfn += priv->usergart[fmt].stride_pfn; vaddr += PAGE_SIZE * m; } @@ -494,7 +481,7 @@ static int fault_2d(struct drm_gem_object *obj, priv->usergart[fmt].last = (priv->usergart[fmt].last + 1) % NUM_USERGART_ENTRIES; - return 0; + return ret; } /** @@ -509,24 +496,25 @@ static int fault_2d(struct drm_gem_object *obj, * vma->vm_private_data points to the GEM object that is backing this * mapping. */ -int omap_gem_fault(struct vm_fault *vmf) +vm_fault_t omap_gem_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; struct drm_gem_object *obj = vma->vm_private_data; struct omap_gem_object *omap_obj = to_omap_bo(obj); - struct drm_device *dev = obj->dev; - struct page **pages; - int ret; + int err; + vm_fault_t ret; /* Make sure we don't parallel update on a fault, nor move or remove * something from beneath our feet */ - mutex_lock(&dev->struct_mutex); + mutex_lock(&omap_obj->lock); /* if a shmem backed object, make sure we have pages attached now */ - ret = get_pages(obj, &pages); - if (ret) + err = omap_gem_attach_pages(obj); + if (err) { + ret = vmf_error(err); goto fail; + } /* where should we do corresponding put_pages().. we are mapping * the original page, rather than thru a GART, so we can't rely @@ -535,28 +523,14 @@ int omap_gem_fault(struct vm_fault *vmf) */ if (omap_obj->flags & OMAP_BO_TILED) - ret = fault_2d(obj, vma, vmf); + ret = omap_gem_fault_2d(obj, vma, vmf); else - ret = fault_1d(obj, vma, vmf); + ret = omap_gem_fault_1d(obj, vma, vmf); fail: - mutex_unlock(&dev->struct_mutex); - switch (ret) { - case 0: - case -ERESTARTSYS: - case -EINTR: - case -EBUSY: - /* - * EBUSY is ok: this just means that another thread - * already did the job. - */ - return VM_FAULT_NOPAGE; - case -ENOMEM: - return VM_FAULT_OOM; - default: - return VM_FAULT_SIGBUS; - } + mutex_unlock(&omap_obj->lock); + return ret; } /** We override mainly to fix up some of the vm mapping flags.. */ @@ -689,21 +663,22 @@ int omap_gem_roll(struct drm_gem_object *obj, u32 roll) omap_obj->roll = roll; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&omap_obj->lock); /* if we aren't mapped yet, we don't need to do anything */ if (omap_obj->block) { - struct page **pages; - ret = get_pages(obj, &pages); + ret = omap_gem_attach_pages(obj); if (ret) goto fail; - ret = tiler_pin(omap_obj->block, pages, npages, roll, true); + + ret = tiler_pin(omap_obj->block, omap_obj->pages, npages, + roll, true); if (ret) dev_err(obj->dev->dev, "could not repin: %d\n", ret); } fail: - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&omap_obj->lock); return ret; } @@ -722,7 +697,7 @@ fail: * the omap_obj->dma_addrs[i] is set to the DMA address, and the page is * unmapped from the CPU. */ -static inline bool is_cached_coherent(struct drm_gem_object *obj) +static inline bool omap_gem_is_cached_coherent(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); @@ -738,7 +713,7 @@ void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff) struct drm_device *dev = obj->dev; struct omap_gem_object *omap_obj = to_omap_bo(obj); - if (is_cached_coherent(obj)) + if (omap_gem_is_cached_coherent(obj)) return; if (omap_obj->dma_addrs[pgoff]) { @@ -758,7 +733,7 @@ void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, struct page **pages = omap_obj->pages; bool dirty = false; - if (is_cached_coherent(obj)) + if (omap_gem_is_cached_coherent(obj)) return; for (i = 0; i < npages; i++) { @@ -804,18 +779,17 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = 0; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&omap_obj->lock); - if (!is_contiguous(omap_obj) && priv->has_dmm) { + if (!omap_gem_is_contiguous(omap_obj) && priv->has_dmm) { if (omap_obj->dma_addr_cnt == 0) { - struct page **pages; u32 npages = obj->size >> PAGE_SHIFT; enum tiler_fmt fmt = gem2fmt(omap_obj->flags); struct tiler_block *block; BUG_ON(omap_obj->block); - ret = get_pages(obj, &pages); + ret = omap_gem_attach_pages(obj); if (ret) goto fail; @@ -835,7 +809,7 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) } /* TODO: enable async refill.. */ - ret = tiler_pin(block, pages, npages, + ret = tiler_pin(block, omap_obj->pages, npages, omap_obj->roll, true); if (ret) { tiler_release(block); @@ -853,7 +827,7 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) omap_obj->dma_addr_cnt++; *dma_addr = omap_obj->dma_addr; - } else if (is_contiguous(omap_obj)) { + } else if (omap_gem_is_contiguous(omap_obj)) { *dma_addr = omap_obj->dma_addr; } else { ret = -EINVAL; @@ -861,7 +835,7 @@ int omap_gem_pin(struct drm_gem_object *obj, dma_addr_t *dma_addr) } fail: - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&omap_obj->lock); return ret; } @@ -879,7 +853,8 @@ void omap_gem_unpin(struct drm_gem_object *obj) struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&omap_obj->lock); + if (omap_obj->dma_addr_cnt > 0) { omap_obj->dma_addr_cnt--; if (omap_obj->dma_addr_cnt == 0) { @@ -898,7 +873,7 @@ void omap_gem_unpin(struct drm_gem_object *obj) } } - mutex_unlock(&obj->dev->struct_mutex); + mutex_unlock(&omap_obj->lock); } /* Get rotated scanout address (only valid if already pinned), at the @@ -911,13 +886,16 @@ int omap_gem_rotated_dma_addr(struct drm_gem_object *obj, u32 orient, struct omap_gem_object *omap_obj = to_omap_bo(obj); int ret = -EINVAL; - mutex_lock(&obj->dev->struct_mutex); + mutex_lock(&omap_obj->lock); + if ((omap_obj->dma_addr_cnt > 0) && omap_obj->block && (omap_obj->flags & OMAP_BO_TILED)) { *dma_addr = tiler_tsptr(omap_obj->block, orient, x, y); ret = 0; } - mutex_unlock(&obj->dev->struct_mutex); + + mutex_unlock(&omap_obj->lock); + return ret; } @@ -944,17 +922,27 @@ int omap_gem_tiled_stride(struct drm_gem_object *obj, u32 orient) int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages, bool remap) { - int ret; - if (!remap) { - struct omap_gem_object *omap_obj = to_omap_bo(obj); - if (!omap_obj->pages) - return -ENOMEM; - *pages = omap_obj->pages; - return 0; + struct omap_gem_object *omap_obj = to_omap_bo(obj); + int ret = 0; + + mutex_lock(&omap_obj->lock); + + if (remap) { + ret = omap_gem_attach_pages(obj); + if (ret) + goto unlock; } - mutex_lock(&obj->dev->struct_mutex); - ret = get_pages(obj, pages); - mutex_unlock(&obj->dev->struct_mutex); + + if (!omap_obj->pages) { + ret = -ENOMEM; + goto unlock; + } + + *pages = omap_obj->pages; + +unlock: + mutex_unlock(&omap_obj->lock); + return ret; } @@ -969,23 +957,34 @@ int omap_gem_put_pages(struct drm_gem_object *obj) } #ifdef CONFIG_DRM_FBDEV_EMULATION -/* Get kernel virtual address for CPU access.. this more or less only - * exists for omap_fbdev. This should be called with struct_mutex - * held. +/* + * Get kernel virtual address for CPU access.. this more or less only + * exists for omap_fbdev. */ void *omap_gem_vaddr(struct drm_gem_object *obj) { struct omap_gem_object *omap_obj = to_omap_bo(obj); - WARN_ON(!mutex_is_locked(&obj->dev->struct_mutex)); + void *vaddr; + int ret; + + mutex_lock(&omap_obj->lock); + if (!omap_obj->vaddr) { - struct page **pages; - int ret = get_pages(obj, &pages); - if (ret) - return ERR_PTR(ret); - omap_obj->vaddr = vmap(pages, obj->size >> PAGE_SHIFT, + ret = omap_gem_attach_pages(obj); + if (ret) { + vaddr = ERR_PTR(ret); + goto unlock; + } + + omap_obj->vaddr = vmap(omap_obj->pages, obj->size >> PAGE_SHIFT, VM_MAP, pgprot_writecombine(PAGE_KERNEL)); } - return omap_obj->vaddr; + + vaddr = omap_obj->vaddr; + +unlock: + mutex_unlock(&omap_obj->lock); + return vaddr; } #endif @@ -1001,6 +1000,7 @@ int omap_gem_resume(struct drm_device *dev) struct omap_gem_object *omap_obj; int ret = 0; + mutex_lock(&priv->list_lock); list_for_each_entry(omap_obj, &priv->obj_list, mm_list) { if (omap_obj->block) { struct drm_gem_object *obj = &omap_obj->base; @@ -1012,12 +1012,14 @@ int omap_gem_resume(struct drm_device *dev) omap_obj->roll, true); if (ret) { dev_err(dev->dev, "could not repin: %d\n", ret); - return ret; + goto done; } } } - return 0; +done: + mutex_unlock(&priv->list_lock); + return ret; } #endif @@ -1033,6 +1035,8 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) off = drm_vma_node_start(&obj->vma_node); + mutex_lock(&omap_obj->lock); + seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d", omap_obj->flags, obj->name, kref_read(&obj->refcount), off, &omap_obj->dma_addr, omap_obj->dma_addr_cnt, @@ -1050,6 +1054,8 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m) seq_printf(m, " %zu", obj->size); } + mutex_unlock(&omap_obj->lock); + seq_printf(m, "\n"); } @@ -1081,17 +1087,21 @@ void omap_gem_free_object(struct drm_gem_object *obj) struct omap_drm_private *priv = dev->dev_private; struct omap_gem_object *omap_obj = to_omap_bo(obj); - evict(obj); - - WARN_ON(!mutex_is_locked(&dev->struct_mutex)); + omap_gem_evict(obj); - spin_lock(&priv->list_lock); + mutex_lock(&priv->list_lock); list_del(&omap_obj->mm_list); - spin_unlock(&priv->list_lock); + mutex_unlock(&priv->list_lock); - /* this means the object is still pinned.. which really should - * not happen. I think.. + /* + * We own the sole reference to the object at this point, but to keep + * lockdep happy, we must still take the omap_obj_lock to call + * omap_gem_detach_pages(). This should hardly make any difference as + * there can't be any lock contention. */ + mutex_lock(&omap_obj->lock); + + /* The object should not be pinned. */ WARN_ON(omap_obj->dma_addr_cnt > 0); if (omap_obj->pages) { @@ -1110,8 +1120,12 @@ void omap_gem_free_object(struct drm_gem_object *obj) drm_prime_gem_destroy(obj, omap_obj->sgt); } + mutex_unlock(&omap_obj->lock); + drm_gem_object_release(obj); + mutex_destroy(&omap_obj->lock); + kfree(omap_obj); } @@ -1167,6 +1181,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, obj = &omap_obj->base; omap_obj->flags = flags; + mutex_init(&omap_obj->lock); if (flags & OMAP_BO_TILED) { /* @@ -1206,9 +1221,9 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev, goto err_release; } - spin_lock(&priv->list_lock); + mutex_lock(&priv->list_lock); list_add(&omap_obj->mm_list, &priv->obj_list); - spin_unlock(&priv->list_lock); + mutex_unlock(&priv->list_lock); return obj; @@ -1231,16 +1246,15 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, if (sgt->orig_nents != 1 && !priv->has_dmm) return ERR_PTR(-EINVAL); - mutex_lock(&dev->struct_mutex); - gsize.bytes = PAGE_ALIGN(size); obj = omap_gem_new(dev, gsize, OMAP_BO_MEM_DMABUF | OMAP_BO_WC); - if (!obj) { - obj = ERR_PTR(-ENOMEM); - goto done; - } + if (!obj) + return ERR_PTR(-ENOMEM); omap_obj = to_omap_bo(obj); + + mutex_lock(&omap_obj->lock); + omap_obj->sgt = sgt; if (sgt->orig_nents == 1) { @@ -1276,7 +1290,7 @@ struct drm_gem_object *omap_gem_new_dmabuf(struct drm_device *dev, size_t size, } done: - mutex_unlock(&dev->struct_mutex); + mutex_unlock(&omap_obj->lock); return obj; } diff --git a/drivers/gpu/drm/omapdrm/omap_gem.h b/drivers/gpu/drm/omapdrm/omap_gem.h index a78bde05193a..c1c45fbde155 100644 --- a/drivers/gpu/drm/omapdrm/omap_gem.h +++ b/drivers/gpu/drm/omapdrm/omap_gem.h @@ -21,6 +21,7 @@ #define __OMAPDRM_GEM_H__ #include <linux/types.h> +#include <linux/mm_types.h> enum dma_data_direction; @@ -80,7 +81,7 @@ struct dma_buf *omap_gem_prime_export(struct drm_device *dev, struct drm_gem_object *omap_gem_prime_import(struct drm_device *dev, struct dma_buf *buffer); -int omap_gem_fault(struct vm_fault *vmf); +vm_fault_t omap_gem_fault(struct vm_fault *vmf); int omap_gem_roll(struct drm_gem_object *obj, u32 roll); void omap_gem_cpu_sync_page(struct drm_gem_object *obj, int pgoff); void omap_gem_dma_sync_buffer(struct drm_gem_object *obj, |