| /* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ |
| /* vi: set ts=8 sw=8 sts=8: */ |
| /*************************************************************************/ /*! |
| @File |
| @Codingstyle LinuxKernel |
| @Copyright Copyright (c) Imagination Technologies Ltd. All Rights Reserved |
| @License Dual MIT/GPLv2 |
| |
| The contents of this file are subject to the MIT license as set out below. |
| |
| Permission is hereby granted, free of charge, to any person obtaining a copy |
| of this software and associated documentation files (the "Software"), to deal |
| in the Software without restriction, including without limitation the rights |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the Software is |
| furnished to do so, subject to the following conditions: |
| |
| The above copyright notice and this permission notice shall be included in |
| all copies or substantial portions of the Software. |
| |
| Alternatively, the contents of this file may be used under the terms of |
| the GNU General Public License Version 2 ("GPL") in which case the provisions |
| of GPL are applicable instead of those above. |
| |
| If you wish to allow use of your version of this file only under the terms of |
| GPL, and not to allow others to use your version of this file under the terms |
| of the MIT license, indicate your decision by deleting the provisions above |
| and replace them with the notice and other provisions required by GPL as set |
| out in the file called "GPL-COPYING" included in this distribution. If you do |
| not delete the provisions above, a recipient may use your version of this file |
| under the terms of either the MIT license or GPL. |
| |
| This License is also included in this distribution in the file called |
| "MIT-COPYING". |
| |
| EXCEPT AS OTHERWISE STATED IN A NEGOTIATED AGREEMENT: (A) THE SOFTWARE IS |
| PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING |
| BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
| PURPOSE AND NONINFRINGEMENT; AND (B) IN NO EVENT SHALL THE AUTHORS OR |
| COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| */ /**************************************************************************/ |
| |
| #include "pvr_linux_fence.h" |
| #include "drm_pdp_drv.h" |
| |
| #include <linux/reservation.h> |
| #include <linux/version.h> |
| |
| #include <drm/drmP.h> |
| #include <drm/drm_crtc.h> |
| #include <drm/drm_crtc_helper.h> |
| #include <drm/drm_plane_helper.h> |
| |
| #include "drm_pdp_gem.h" |
| |
| #include "pdp_apollo.h" |
| #include "pdp_odin.h" |
| #include "pdp_plato.h" |
| |
| #include "plato_drv.h" |
| |
| #if defined(PDP_USE_ATOMIC) |
| #include <drm/drm_atomic_helper.h> |
| #endif |
| |
| #include "kernel_compatibility.h" |
| |
| enum pdp_crtc_flip_status { |
| PDP_CRTC_FLIP_STATUS_NONE = 0, |
| PDP_CRTC_FLIP_STATUS_PENDING, |
| PDP_CRTC_FLIP_STATUS_DONE, |
| }; |
| |
| struct pdp_flip_data { |
| struct dma_fence_cb base; |
| struct drm_crtc *crtc; |
| struct dma_fence *wait_fence; |
| }; |
| |
| /* returns true for ok, false for fail */ |
| static bool pdp_clocks_set(struct drm_crtc *crtc, |
| struct drm_display_mode *adjusted_mode) |
| { |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| bool res; |
| |
| switch (dev_priv->version) { |
| case PDP_VERSION_ODIN: { |
| pdp_odin_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, false); |
| res = pdp_odin_clocks_set(crtc->dev->dev, |
| pdp_crtc->pdp_reg, pdp_crtc->pll_reg, |
| 0, /* apollo only */ |
| pdp_crtc->odn_core_reg, /* odin only */ |
| adjusted_mode->hdisplay, |
| adjusted_mode->vdisplay); |
| pdp_odin_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, true); |
| |
| break; |
| } |
| case PDP_VERSION_APOLLO: { |
| int clock_in_mhz = adjusted_mode->clock / 1000; |
| |
| pdp_apollo_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, false); |
| res = pdp_apollo_clocks_set(crtc->dev->dev, |
| pdp_crtc->pdp_reg, pdp_crtc->pll_reg, |
| clock_in_mhz, /* apollo only */ |
| NULL, /* odin only */ |
| adjusted_mode->hdisplay, |
| adjusted_mode->vdisplay); |
| pdp_apollo_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, true); |
| |
| DRM_DEBUG_DRIVER("pdp clock set to %dMhz\n", clock_in_mhz); |
| |
| break; |
| } |
| case PDP_VERSION_PLATO: |
| #if defined(SUPPORT_PLATO_DISPLAY) |
| plato_enable_pdp_clock(dev_priv->dev->dev->parent); |
| res = true; |
| #else |
| DRM_ERROR("Trying to enable plato PDP clock on non-Plato build\n"); |
| res = false; |
| #endif |
| break; |
| default: |
| BUG(); |
| } |
| |
| return res; |
| } |
| |
| void pdp_crtc_set_plane_enabled(struct drm_crtc *crtc, bool enable) |
| { |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| |
| switch (dev_priv->version) { |
| case PDP_VERSION_ODIN: |
| pdp_odin_set_plane_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| 0, enable); |
| break; |
| case PDP_VERSION_APOLLO: |
| pdp_apollo_set_plane_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| 0, enable); |
| break; |
| case PDP_VERSION_PLATO: |
| pdp_plato_set_plane_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| 0, enable); |
| break; |
| default: |
| BUG(); |
| } |
| } |
| |
| static void pdp_crtc_set_syncgen_enabled(struct drm_crtc *crtc, bool enable) |
| { |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| |
| switch (dev_priv->version) { |
| case PDP_VERSION_ODIN: |
| pdp_odin_set_syncgen_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| enable); |
| break; |
| case PDP_VERSION_APOLLO: |
| pdp_apollo_set_syncgen_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| enable); |
| break; |
| case PDP_VERSION_PLATO: |
| pdp_plato_set_syncgen_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| enable); |
| break; |
| default: |
| BUG(); |
| } |
| } |
| |
| static void pdp_crtc_set_enabled(struct drm_crtc *crtc, bool enable) |
| { |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| |
| if (enable) { |
| pdp_crtc_set_syncgen_enabled(crtc, enable); |
| pdp_crtc_set_plane_enabled(crtc, dev_priv->display_enabled); |
| drm_crtc_vblank_on(crtc); |
| } else { |
| drm_crtc_vblank_off(crtc); |
| pdp_crtc_set_plane_enabled(crtc, enable); |
| pdp_crtc_set_syncgen_enabled(crtc, enable); |
| } |
| } |
| |
| static void pdp_crtc_mode_set(struct drm_crtc *crtc, |
| struct drm_display_mode *adjusted_mode) |
| { |
| /* |
| * ht = horizontal total |
| * hbps = horizontal back porch start |
| * has = horizontal active start |
| * hlbs = horizontal left border start |
| * hfps = horizontal front porch start |
| * hrbs = horizontal right border start |
| * |
| * vt = vertical total |
| * vbps = vertical back porch start |
| * vas = vertical active start |
| * vtbs = vertical top border start |
| * vfps = vertical front porch start |
| * vbbs = vertical bottom border start |
| */ |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| uint32_t ht = adjusted_mode->htotal; |
| uint32_t hbps = adjusted_mode->hsync_end - adjusted_mode->hsync_start; |
| uint32_t has = (adjusted_mode->htotal - adjusted_mode->hsync_start); |
| uint32_t hlbs = has; |
| uint32_t hfps = (hlbs + adjusted_mode->hdisplay); |
| uint32_t hrbs = hfps; |
| uint32_t vt = adjusted_mode->vtotal; |
| uint32_t vbps = adjusted_mode->vsync_end - adjusted_mode->vsync_start; |
| uint32_t vas = (adjusted_mode->vtotal - adjusted_mode->vsync_start); |
| uint32_t vtbs = vas; |
| uint32_t vfps = (vtbs + adjusted_mode->vdisplay); |
| uint32_t vbbs = vfps; |
| bool ok; |
| |
| ok = pdp_clocks_set(crtc, adjusted_mode); |
| |
| if (!ok) { |
| dev_info(crtc->dev->dev, "%s failed\n", __func__); |
| return; |
| } |
| |
| switch (dev_priv->version) { |
| case PDP_VERSION_ODIN: |
| pdp_odin_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, false); |
| pdp_odin_reset_planes(crtc->dev->dev, |
| pdp_crtc->pdp_reg); |
| pdp_odin_mode_set(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| adjusted_mode->hdisplay, adjusted_mode->vdisplay, |
| hbps, ht, has, |
| hlbs, hfps, hrbs, |
| vbps, vt, vas, |
| vtbs, vfps, vbbs, |
| adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC, |
| adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC); |
| pdp_odin_set_powerdwn_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, false); |
| pdp_odin_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, true); |
| break; |
| case PDP_VERSION_APOLLO: |
| pdp_apollo_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, false); |
| pdp_apollo_reset_planes(crtc->dev->dev, |
| pdp_crtc->pdp_reg); |
| pdp_apollo_mode_set(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| adjusted_mode->hdisplay, adjusted_mode->vdisplay, |
| hbps, ht, has, |
| hlbs, hfps, hrbs, |
| vbps, vt, vas, |
| vtbs, vfps, vbbs, |
| adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC, |
| adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC); |
| pdp_apollo_set_powerdwn_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, false); |
| pdp_apollo_set_updates_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, true); |
| break; |
| case PDP_VERSION_PLATO: |
| pdp_plato_mode_set(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| adjusted_mode->hdisplay, |
| adjusted_mode->vdisplay, |
| hbps, ht, has, |
| hlbs, hfps, hrbs, |
| vbps, vt, vas, |
| vtbs, vfps, vbbs, |
| adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC, |
| adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC); |
| break; |
| default: |
| BUG(); |
| } |
| } |
| |
| |
| static bool pdp_crtc_helper_mode_fixup(struct drm_crtc *crtc, |
| const struct drm_display_mode *mode, |
| struct drm_display_mode *adjusted_mode) |
| { |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| |
| if (dev_priv->version == PDP_VERSION_ODIN |
| && mode->hdisplay == 1920 |
| && mode->vdisplay == 1080) { |
| |
| /* 1080p 60Hz */ |
| const int h_total = 2200; |
| const int h_active_start = 192; |
| const int h_back_porch_start = 44; |
| const int v_total = 1125; |
| const int v_active_start = 41; |
| const int v_back_porch_start = 5; |
| |
| adjusted_mode->htotal = h_total; |
| adjusted_mode->hsync_start = adjusted_mode->htotal - |
| h_active_start; |
| adjusted_mode->hsync_end = adjusted_mode->hsync_start + |
| h_back_porch_start; |
| adjusted_mode->vtotal = v_total; |
| adjusted_mode->vsync_start = adjusted_mode->vtotal - |
| v_active_start; |
| adjusted_mode->vsync_end = adjusted_mode->vsync_start + |
| v_back_porch_start; |
| } |
| return true; |
| } |
| |
| static void pdp_crtc_flip_complete(struct drm_crtc *crtc); |
| |
| #if defined(PDP_USE_ATOMIC) |
| static void pdp_crtc_helper_mode_set_nofb(struct drm_crtc *crtc) |
| { |
| pdp_crtc_mode_set(crtc, &crtc->state->adjusted_mode); |
| } |
| |
| static void pdp_crtc_helper_atomic_flush(struct drm_crtc *crtc, |
| struct drm_crtc_state *old_crtc_state) |
| { |
| struct drm_crtc_state *new_crtc_state = crtc->state; |
| |
| if (!new_crtc_state->active || !old_crtc_state->active) |
| return; |
| |
| if (crtc->state->event) { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| unsigned long flags; |
| |
| pdp_crtc->flip_async = !!(new_crtc_state->pageflip_flags |
| & DRM_MODE_PAGE_FLIP_ASYNC); |
| |
| if (pdp_crtc->flip_async) |
| WARN_ON(drm_crtc_vblank_get(crtc) != 0); |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| pdp_crtc->flip_event = crtc->state->event; |
| crtc->state->event = NULL; |
| |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_DONE); |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| |
| if (pdp_crtc->flip_async) |
| pdp_crtc_flip_complete(crtc); |
| } |
| } |
| |
| static void pdp_crtc_helper_atomic_enable(struct drm_crtc *crtc, |
| struct drm_crtc_state *old_crtc_state) |
| { |
| pdp_crtc_set_enabled(crtc, true); |
| |
| if (crtc->state->event) { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| unsigned long flags; |
| |
| WARN_ON(drm_crtc_vblank_get(crtc) != 0); |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| pdp_crtc->flip_event = crtc->state->event; |
| crtc->state->event = NULL; |
| |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_DONE); |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| } |
| } |
| |
| static void pdp_crtc_helper_atomic_disable(struct drm_crtc *crtc, |
| struct drm_crtc_state *old_crtc_state) |
| { |
| pdp_crtc_set_enabled(crtc, false); |
| |
| if (crtc->state->event) { |
| unsigned long flags; |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| drm_crtc_send_vblank_event(crtc, crtc->state->event); |
| crtc->state->event = NULL; |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| } |
| } |
| #else |
| static void pdp_crtc_helper_dpms(struct drm_crtc *crtc, int mode) |
| { |
| } |
| |
| static void pdp_crtc_helper_prepare(struct drm_crtc *crtc) |
| { |
| pdp_crtc_set_enabled(crtc, false); |
| } |
| |
| static void pdp_crtc_helper_commit(struct drm_crtc *crtc) |
| { |
| pdp_crtc_set_enabled(crtc, true); |
| } |
| |
| static int pdp_crtc_helper_mode_set_base_atomic(struct drm_crtc *crtc, |
| struct drm_framebuffer *fb, |
| int x, int y, |
| enum mode_set_atomic atomic) |
| { |
| if (x < 0 || y < 0) |
| return -EINVAL; |
| |
| pdp_plane_set_surface(crtc, crtc->primary, fb, |
| (uint32_t) x, (uint32_t) y); |
| |
| return 0; |
| } |
| |
| static int pdp_crtc_helper_mode_set_base(struct drm_crtc *crtc, |
| int x, int y, |
| struct drm_framebuffer *old_fb) |
| { |
| if (!crtc->primary->fb) { |
| DRM_ERROR("no framebuffer\n"); |
| return 0; |
| } |
| |
| return pdp_crtc_helper_mode_set_base_atomic(crtc, |
| crtc->primary->fb, |
| x, y, |
| 0); |
| } |
| |
| static int pdp_crtc_helper_mode_set(struct drm_crtc *crtc, |
| struct drm_display_mode *mode, |
| struct drm_display_mode *adjusted_mode, |
| int x, int y, |
| struct drm_framebuffer *old_fb) |
| { |
| pdp_crtc_mode_set(crtc, adjusted_mode); |
| |
| return pdp_crtc_helper_mode_set_base(crtc, x, y, old_fb); |
| } |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| static void pdp_crtc_helper_load_lut(struct drm_crtc *crtc) |
| { |
| } |
| #endif |
| |
| static void pdp_crtc_helper_disable(struct drm_crtc *crtc) |
| { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| enum pdp_crtc_flip_status status; |
| |
| pdp_crtc_set_enabled(crtc, false); |
| |
| status = atomic_read(&pdp_crtc->flip_status); |
| if (status != PDP_CRTC_FLIP_STATUS_NONE) { |
| long lerr; |
| |
| lerr = wait_event_timeout( |
| pdp_crtc->flip_pending_wait_queue, |
| atomic_read(&pdp_crtc->flip_status) |
| != PDP_CRTC_FLIP_STATUS_PENDING, |
| 30 * HZ); |
| if (!lerr) |
| DRM_ERROR("Failed to wait for pending flip\n"); |
| else if (!pdp_crtc->flip_async) |
| pdp_crtc_flip_complete(crtc); |
| } |
| } |
| #endif /* defined(PDP_USE_ATOMIC) */ |
| |
| static void pdp_crtc_destroy(struct drm_crtc *crtc) |
| { |
| struct drm_device *dev = crtc->dev; |
| struct pdp_drm_private *dev_priv = dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| |
| DRM_DEBUG_DRIVER("[CRTC:%d]\n", crtc->base.id); |
| |
| drm_crtc_cleanup(crtc); |
| |
| iounmap(pdp_crtc->pll_reg); |
| |
| iounmap(pdp_crtc->pdp_reg); |
| release_mem_region(pdp_crtc->pdp_reg_phys_base, pdp_crtc->pdp_reg_size); |
| |
| kfree(pdp_crtc); |
| dev_priv->crtc = NULL; |
| } |
| |
| static void pdp_crtc_flip_complete(struct drm_crtc *crtc) |
| { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| |
| /* The flipping process has been completed so reset the flip state */ |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_NONE); |
| pdp_crtc->flip_async = false; |
| |
| #if !defined(PDP_USE_ATOMIC) |
| if (pdp_crtc->flip_data) { |
| dma_fence_put(pdp_crtc->flip_data->wait_fence); |
| kfree(pdp_crtc->flip_data); |
| pdp_crtc->flip_data = NULL; |
| } |
| #endif |
| |
| if (pdp_crtc->flip_event) { |
| drm_crtc_send_vblank_event(crtc, pdp_crtc->flip_event); |
| pdp_crtc->flip_event = NULL; |
| } |
| |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| } |
| |
| #if !defined(PDP_USE_ATOMIC) |
| static void pdp_crtc_flip(struct drm_crtc *crtc) |
| { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| struct drm_framebuffer *old_fb; |
| |
| WARN_ON(atomic_read(&to_pdp_crtc(crtc)->flip_status) |
| != PDP_CRTC_FLIP_STATUS_PENDING); |
| |
| old_fb = pdp_crtc->old_fb; |
| pdp_crtc->old_fb = NULL; |
| |
| /* |
| * The graphics stream registers latch on vsync so we can go ahead and |
| * do the flip now. |
| */ |
| (void) pdp_crtc_helper_mode_set_base(crtc, crtc->x, crtc->y, old_fb); |
| |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_DONE); |
| wake_up(&pdp_crtc->flip_pending_wait_queue); |
| |
| if (pdp_crtc->flip_async) |
| pdp_crtc_flip_complete(crtc); |
| } |
| |
| static void pdp_crtc_flip_cb(struct dma_fence *fence, struct dma_fence_cb *cb) |
| { |
| struct pdp_flip_data *flip_data = |
| container_of(cb, struct pdp_flip_data, base); |
| |
| pdp_crtc_flip(flip_data->crtc); |
| } |
| |
| static void pdp_crtc_flip_schedule_cb(struct dma_fence *fence, |
| struct dma_fence_cb *cb) |
| { |
| struct pdp_flip_data *flip_data = |
| container_of(cb, struct pdp_flip_data, base); |
| int err = 0; |
| |
| if (flip_data->wait_fence) |
| err = dma_fence_add_callback(flip_data->wait_fence, |
| &flip_data->base, |
| pdp_crtc_flip_cb); |
| |
| if (!flip_data->wait_fence || err) { |
| if (err && err != -ENOENT) |
| DRM_ERROR("flip failed to wait on old buffer\n"); |
| pdp_crtc_flip_cb(flip_data->wait_fence, &flip_data->base); |
| } |
| } |
| |
| static int pdp_crtc_flip_schedule(struct drm_crtc *crtc, |
| struct drm_gem_object *obj, |
| struct drm_gem_object *old_obj) |
| { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| struct reservation_object *resv = pdp_gem_get_resv(obj); |
| struct reservation_object *old_resv = pdp_gem_get_resv(old_obj); |
| struct pdp_flip_data *flip_data; |
| struct dma_fence *fence; |
| int err; |
| |
| flip_data = kmalloc(sizeof(*flip_data), GFP_KERNEL); |
| if (!flip_data) |
| return -ENOMEM; |
| |
| flip_data->crtc = crtc; |
| |
| ww_mutex_lock(&old_resv->lock, NULL); |
| flip_data->wait_fence = |
| dma_fence_get(reservation_object_get_excl(old_resv)); |
| |
| if (old_resv != resv) { |
| ww_mutex_unlock(&old_resv->lock); |
| ww_mutex_lock(&resv->lock, NULL); |
| } |
| |
| fence = dma_fence_get(reservation_object_get_excl(resv)); |
| ww_mutex_unlock(&resv->lock); |
| |
| pdp_crtc->flip_data = flip_data; |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_PENDING); |
| |
| if (fence) { |
| err = dma_fence_add_callback(fence, &flip_data->base, |
| pdp_crtc_flip_schedule_cb); |
| dma_fence_put(fence); |
| if (err && err != -ENOENT) |
| goto err_set_flip_status_none; |
| } |
| |
| if (!fence || err == -ENOENT) { |
| pdp_crtc_flip_schedule_cb(fence, &flip_data->base); |
| err = 0; |
| } |
| |
| return err; |
| |
| err_set_flip_status_none: |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_NONE); |
| dma_fence_put(flip_data->wait_fence); |
| kfree(flip_data); |
| return err; |
| } |
| |
| static int pdp_crtc_page_flip(struct drm_crtc *crtc, |
| struct drm_framebuffer *fb, |
| struct drm_pending_vblank_event *event, |
| uint32_t page_flip_flags |
| #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)) |
| , struct drm_modeset_acquire_ctx *ctx |
| #endif |
| ) |
| { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| struct pdp_framebuffer *pdp_fb = to_pdp_framebuffer(fb); |
| struct pdp_framebuffer *pdp_old_fb = |
| to_pdp_framebuffer(crtc->primary->fb); |
| enum pdp_crtc_flip_status status; |
| unsigned long flags; |
| int err; |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| status = atomic_read(&pdp_crtc->flip_status); |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| |
| if (status != PDP_CRTC_FLIP_STATUS_NONE) |
| return -EBUSY; |
| |
| if (!(page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC)) { |
| err = drm_crtc_vblank_get(crtc); |
| if (err) |
| return err; |
| } |
| |
| pdp_crtc->old_fb = crtc->primary->fb; |
| pdp_crtc->flip_event = event; |
| pdp_crtc->flip_async = !!(page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC); |
| |
| /* Set the crtc primary plane to point to the new framebuffer */ |
| crtc->primary->fb = fb; |
| |
| err = pdp_crtc_flip_schedule(crtc, pdp_fb->obj[0], pdp_old_fb->obj[0]); |
| if (err) { |
| crtc->primary->fb = pdp_crtc->old_fb; |
| pdp_crtc->old_fb = NULL; |
| pdp_crtc->flip_event = NULL; |
| pdp_crtc->flip_async = false; |
| |
| DRM_ERROR("failed to schedule flip (err=%d)\n", err); |
| goto err_vblank_put; |
| } |
| |
| return 0; |
| |
| err_vblank_put: |
| if (!(page_flip_flags & DRM_MODE_PAGE_FLIP_ASYNC)) |
| drm_crtc_vblank_put(crtc); |
| return err; |
| } |
| #endif /* !defined(PDP_USE_ATOMIC) */ |
| |
| static const struct drm_crtc_helper_funcs pdp_crtc_helper_funcs = { |
| .mode_fixup = pdp_crtc_helper_mode_fixup, |
| #if defined(PDP_USE_ATOMIC) |
| .mode_set_nofb = pdp_crtc_helper_mode_set_nofb, |
| .atomic_flush = pdp_crtc_helper_atomic_flush, |
| .atomic_enable = pdp_crtc_helper_atomic_enable, |
| .atomic_disable = pdp_crtc_helper_atomic_disable, |
| #else |
| .dpms = pdp_crtc_helper_dpms, |
| .prepare = pdp_crtc_helper_prepare, |
| .commit = pdp_crtc_helper_commit, |
| .mode_set = pdp_crtc_helper_mode_set, |
| .mode_set_base = pdp_crtc_helper_mode_set_base, |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)) |
| .load_lut = pdp_crtc_helper_load_lut, |
| #endif |
| .mode_set_base_atomic = pdp_crtc_helper_mode_set_base_atomic, |
| .disable = pdp_crtc_helper_disable, |
| #endif |
| }; |
| |
| static const struct drm_crtc_funcs pdp_crtc_funcs = { |
| .destroy = pdp_crtc_destroy, |
| #if defined(PDP_USE_ATOMIC) |
| .reset = drm_atomic_helper_crtc_reset, |
| .set_config = drm_atomic_helper_set_config, |
| .page_flip = drm_atomic_helper_page_flip, |
| .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, |
| .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, |
| #else |
| .set_config = drm_crtc_helper_set_config, |
| .page_flip = pdp_crtc_page_flip, |
| #endif |
| }; |
| |
| |
| struct drm_crtc *pdp_crtc_create(struct drm_device *dev, uint32_t number, |
| struct drm_plane *primary_plane) |
| { |
| struct pdp_drm_private *dev_priv = dev->dev_private; |
| struct pdp_crtc *pdp_crtc; |
| const char *crtc_name = NULL; |
| int err; |
| |
| pdp_crtc = kzalloc(sizeof(*pdp_crtc), GFP_KERNEL); |
| if (!pdp_crtc) { |
| err = -ENOMEM; |
| goto err_exit; |
| } |
| |
| init_waitqueue_head(&pdp_crtc->flip_pending_wait_queue); |
| atomic_set(&pdp_crtc->flip_status, PDP_CRTC_FLIP_STATUS_NONE); |
| pdp_crtc->number = number; |
| |
| switch (number) { |
| case 0: |
| { |
| struct resource *regs; |
| |
| regs = platform_get_resource_byname( |
| to_platform_device(dev->dev), |
| IORESOURCE_MEM, |
| "pdp-regs"); |
| if (!regs) { |
| DRM_ERROR("missing pdp register info\n"); |
| err = -ENXIO; |
| goto err_crtc_free; |
| } |
| |
| pdp_crtc->pdp_reg_phys_base = regs->start; |
| pdp_crtc->pdp_reg_size = resource_size(regs); |
| |
| if (dev_priv->version == PDP_VERSION_ODIN || |
| dev_priv->version == PDP_VERSION_APOLLO) { |
| regs = platform_get_resource_byname( |
| to_platform_device(dev->dev), |
| IORESOURCE_MEM, |
| "pll-regs"); |
| if (!regs) { |
| DRM_ERROR("missing pll register info\n"); |
| err = -ENXIO; |
| goto err_crtc_free; |
| } |
| |
| pdp_crtc->pll_reg_phys_base = regs->start; |
| pdp_crtc->pll_reg_size = resource_size(regs); |
| |
| pdp_crtc->pll_reg = |
| ioremap_nocache(pdp_crtc->pll_reg_phys_base, |
| pdp_crtc->pll_reg_size); |
| if (!pdp_crtc->pll_reg) { |
| DRM_ERROR("failed to map pll registers\n"); |
| err = -ENOMEM; |
| goto err_crtc_free; |
| } |
| } else if (dev_priv->version == PDP_VERSION_PLATO) { |
| regs = platform_get_resource_byname( |
| to_platform_device(dev->dev), |
| IORESOURCE_MEM, |
| PLATO_PDP_RESOURCE_BIF_REGS); |
| if (!regs) { |
| DRM_ERROR("missing pdp-bif register info\n"); |
| err = -ENXIO; |
| goto err_crtc_free; |
| } |
| |
| pdp_crtc->pdp_bif_reg_phys_base = regs->start; |
| pdp_crtc->pdp_bif_reg_size = resource_size(regs); |
| |
| if (!request_mem_region(pdp_crtc->pdp_bif_reg_phys_base, |
| pdp_crtc->pdp_bif_reg_size, |
| crtc_name)) { |
| DRM_ERROR("failed to reserve pdp-bif registers\n"); |
| err = -EBUSY; |
| goto err_crtc_free; |
| } |
| |
| pdp_crtc->pdp_bif_reg = |
| ioremap_nocache(pdp_crtc->pdp_bif_reg_phys_base, |
| pdp_crtc->pdp_bif_reg_size); |
| if (!pdp_crtc->pdp_bif_reg) { |
| DRM_ERROR("failed to map pdp-bif registers\n"); |
| err = -ENOMEM; |
| goto err_iounmap_regs; |
| } |
| } |
| |
| if (dev_priv->version == PDP_VERSION_ODIN) { |
| regs = platform_get_resource_byname( |
| to_platform_device(dev->dev), |
| IORESOURCE_MEM, |
| "odn-core"); |
| if (!regs) { |
| DRM_ERROR("missing odn-core info\n"); |
| err = -ENXIO; |
| goto err_crtc_free; |
| } |
| |
| pdp_crtc->odn_core_phys_base = regs->start; |
| pdp_crtc->odn_core_size = resource_size(regs); |
| |
| pdp_crtc->odn_core_reg |
| = ioremap_nocache(pdp_crtc->odn_core_phys_base, |
| pdp_crtc->odn_core_size); |
| if (!pdp_crtc->odn_core_reg) { |
| DRM_ERROR("failed to map pdp reset register\n"); |
| err = -ENOMEM; |
| goto err_iounmap_regs; |
| } |
| } |
| |
| crtc_name = "crtc-0"; |
| break; |
| } |
| default: |
| DRM_ERROR("invalid crtc number %u\n", number); |
| err = -EINVAL; |
| goto err_crtc_free; |
| } |
| |
| if (!request_mem_region(pdp_crtc->pdp_reg_phys_base, |
| pdp_crtc->pdp_reg_size, |
| crtc_name)) { |
| DRM_ERROR("failed to reserve pdp registers\n"); |
| err = -EBUSY; |
| goto err_crtc_free; |
| } |
| |
| pdp_crtc->pdp_reg = ioremap_nocache(pdp_crtc->pdp_reg_phys_base, |
| pdp_crtc->pdp_reg_size); |
| if (!pdp_crtc->pdp_reg) { |
| DRM_ERROR("failed to map pdp registers\n"); |
| err = -ENOMEM; |
| goto err_release_mem_region; |
| } |
| |
| err = drm_crtc_init_with_planes(dev, &pdp_crtc->base, primary_plane, |
| NULL, &pdp_crtc_funcs, NULL); |
| if (err) { |
| DRM_ERROR("CRTC init with planes failed"); |
| goto err_iounmap_regs; |
| } |
| |
| drm_crtc_helper_add(&pdp_crtc->base, &pdp_crtc_helper_funcs); |
| |
| DRM_DEBUG_DRIVER("[CRTC:%d]\n", pdp_crtc->base.base.id); |
| |
| return &pdp_crtc->base; |
| |
| err_iounmap_regs: |
| iounmap(pdp_crtc->pdp_reg); |
| if (pdp_crtc->odn_core_reg) |
| iounmap(pdp_crtc->odn_core_reg); |
| if (pdp_crtc->pdp_bif_reg) |
| iounmap(pdp_crtc->pdp_bif_reg); |
| err_release_mem_region: |
| release_mem_region(pdp_crtc->pdp_reg_phys_base, pdp_crtc->pdp_reg_size); |
| err_crtc_free: |
| kfree(pdp_crtc); |
| err_exit: |
| return ERR_PTR(err); |
| } |
| |
| void pdp_crtc_set_vblank_enabled(struct drm_crtc *crtc, bool enable) |
| { |
| struct pdp_drm_private *dev_priv = crtc->dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| |
| switch (dev_priv->version) { |
| case PDP_VERSION_ODIN: |
| pdp_odin_set_vblank_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| enable); |
| break; |
| case PDP_VERSION_APOLLO: |
| pdp_apollo_set_vblank_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| enable); |
| break; |
| case PDP_VERSION_PLATO: |
| pdp_plato_set_vblank_enabled(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| enable); |
| break; |
| default: |
| BUG(); |
| } |
| } |
| |
| void pdp_crtc_irq_handler(struct drm_crtc *crtc) |
| { |
| struct drm_device *dev = crtc->dev; |
| struct pdp_drm_private *dev_priv = dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| bool handled; |
| |
| switch (dev_priv->version) { |
| case PDP_VERSION_ODIN: |
| handled = pdp_odin_check_and_clear_vblank(dev->dev, |
| pdp_crtc->pdp_reg); |
| break; |
| case PDP_VERSION_APOLLO: |
| handled = pdp_apollo_check_and_clear_vblank(dev->dev, |
| pdp_crtc->pdp_reg); |
| break; |
| case PDP_VERSION_PLATO: |
| handled = pdp_plato_check_and_clear_vblank(dev->dev, |
| pdp_crtc->pdp_reg); |
| break; |
| default: |
| handled = false; |
| break; |
| } |
| |
| if (handled) { |
| enum pdp_crtc_flip_status status; |
| |
| drm_handle_vblank(dev, pdp_crtc->number); |
| |
| status = atomic_read(&pdp_crtc->flip_status); |
| if (status == PDP_CRTC_FLIP_STATUS_DONE) { |
| if (!pdp_crtc->flip_async) { |
| pdp_crtc_flip_complete(crtc); |
| #if !defined(PDP_USE_ATOMIC) |
| drm_crtc_vblank_put(crtc); |
| #endif |
| } |
| } |
| } |
| } |
| |
| #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 6, 0)) |
| void pdp_crtc_flip_event_cancel(struct drm_crtc *crtc, struct drm_file *file) |
| { |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| unsigned long flags; |
| |
| spin_lock_irqsave(&crtc->dev->event_lock, flags); |
| |
| if (pdp_crtc->flip_event && |
| pdp_crtc->flip_event->base.file_priv == file) { |
| pdp_crtc->flip_event->base.destroy(&pdp_crtc->flip_event->base); |
| pdp_crtc->flip_event = NULL; |
| } |
| |
| spin_unlock_irqrestore(&crtc->dev->event_lock, flags); |
| } |
| #endif |