| /* -*- 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 "drm_pdp_drv.h" |
| |
| #include <drm/drmP.h> |
| #include <drm/drm_plane_helper.h> |
| |
| #if defined(PDP_USE_ATOMIC) |
| #include <drm/drm_atomic.h> |
| #include <drm/drm_atomic_helper.h> |
| #include <drm/drm_gem_framebuffer_helper.h> |
| #endif |
| |
| #include "drm_pdp_gem.h" |
| #include "pdp_apollo.h" |
| #include "pdp_odin.h" |
| #include "pdp_plato.h" |
| |
| #include "kernel_compatibility.h" |
| |
| |
| #if defined(PDP_USE_ATOMIC) |
| static int pdp_plane_helper_atomic_check(struct drm_plane *plane, |
| struct drm_plane_state *state) |
| { |
| struct drm_crtc_state *crtc_new_state; |
| |
| if (!state->crtc) |
| return 0; |
| |
| crtc_new_state = drm_atomic_get_new_crtc_state(state->state, |
| state->crtc); |
| |
| return drm_atomic_helper_check_plane_state(state, crtc_new_state, |
| DRM_PLANE_HELPER_NO_SCALING, |
| DRM_PLANE_HELPER_NO_SCALING, |
| false, true); |
| } |
| |
| static void pdp_plane_helper_atomic_update(struct drm_plane *plane, |
| struct drm_plane_state *old_state) |
| { |
| struct drm_plane_state *plane_state = plane->state; |
| struct drm_framebuffer *fb = plane_state->fb; |
| |
| if (fb) { |
| pdp_plane_set_surface(plane_state->crtc, plane, fb, |
| plane_state->src_x, plane_state->src_y); |
| } |
| } |
| |
| static const struct drm_plane_helper_funcs pdp_plane_helper_funcs = { |
| .prepare_fb = drm_gem_fb_prepare_fb, |
| .atomic_check = pdp_plane_helper_atomic_check, |
| .atomic_update = pdp_plane_helper_atomic_update, |
| }; |
| |
| static const struct drm_plane_funcs pdp_plane_funcs = { |
| .update_plane = drm_atomic_helper_update_plane, |
| .disable_plane = drm_atomic_helper_disable_plane, |
| .destroy = drm_primary_helper_destroy, |
| .reset = drm_atomic_helper_plane_reset, |
| .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, |
| .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, |
| }; |
| #else |
| #define pdp_plane_funcs drm_primary_helper_funcs |
| #endif |
| |
| struct drm_plane *pdp_plane_create(struct drm_device *dev, |
| enum drm_plane_type type) |
| { |
| struct drm_plane *plane; |
| const uint32_t supported_formats[] = { |
| DRM_FORMAT_XRGB8888, |
| DRM_FORMAT_ARGB8888, |
| }; |
| int err; |
| |
| plane = kzalloc(sizeof(*plane), GFP_KERNEL); |
| if (!plane) { |
| err = -ENOMEM; |
| goto err_exit; |
| } |
| |
| err = drm_universal_plane_init(dev, plane, 0, &pdp_plane_funcs, |
| supported_formats, |
| ARRAY_SIZE(supported_formats), |
| NULL, type, NULL); |
| if (err) |
| goto err_plane_free; |
| |
| #if defined(PDP_USE_ATOMIC) |
| drm_plane_helper_add(plane, &pdp_plane_helper_funcs); |
| #endif |
| |
| DRM_DEBUG_DRIVER("[PLANE:%d]\n", plane->base.id); |
| |
| return plane; |
| |
| err_plane_free: |
| kfree(plane); |
| err_exit: |
| return ERR_PTR(err); |
| } |
| |
| void pdp_plane_set_surface(struct drm_crtc *crtc, struct drm_plane *plane, |
| struct drm_framebuffer *fb, |
| const uint32_t src_x, const uint32_t src_y) |
| { |
| struct pdp_drm_private *dev_priv = plane->dev->dev_private; |
| struct pdp_crtc *pdp_crtc = to_pdp_crtc(crtc); |
| struct pdp_framebuffer *pdp_fb = to_pdp_framebuffer(fb); |
| unsigned int pitch = fb->pitches[0]; |
| uint64_t address = pdp_gem_get_dev_addr(pdp_fb->obj[0]); |
| |
| /* |
| * User space specifies 'x' and 'y' and this is used to tell the display |
| * to scan out from part way through a buffer. |
| */ |
| address += ((src_y * pitch) + (src_x * (pdp_drm_fb_cpp(fb)))); |
| |
| /* |
| * NOTE: If the buffer dimensions are less than the current mode then |
| * the output will appear in the top left of the screen. This can be |
| * centered by adjusting horizontal active start, right border start, |
| * vertical active start and bottom border start. At this point it's |
| * not entirely clear where this should be done. On the one hand it's |
| * related to pdp_crtc_helper_mode_set but on the other hand there |
| * might not always be a call to pdp_crtc_helper_mode_set. This needs |
| * to be investigated. |
| */ |
| switch (dev_priv->version) { |
| case PDP_VERSION_APOLLO: |
| switch (pdp_drm_fb_format(fb)) { |
| case DRM_FORMAT_ARGB8888: |
| case DRM_FORMAT_XRGB8888: |
| break; |
| default: |
| DRM_ERROR("unsupported pixel format (format = %d)\n", |
| pdp_drm_fb_format(fb)); |
| return; |
| } |
| |
| pdp_apollo_set_surface(plane->dev->dev, |
| pdp_crtc->pdp_reg, |
| 0, |
| address, |
| 0, 0, |
| fb->width, fb->height, pitch, |
| 0xE, |
| 255, |
| false); |
| break; |
| case PDP_VERSION_ODIN: |
| switch (pdp_drm_fb_format(fb)) { |
| case DRM_FORMAT_ARGB8888: |
| case DRM_FORMAT_XRGB8888: |
| break; |
| default: |
| DRM_ERROR("unsupported pixel format (format = %d)\n", |
| pdp_drm_fb_format(fb)); |
| return; |
| } |
| |
| pdp_odin_set_surface(plane->dev->dev, |
| pdp_crtc->pdp_reg, |
| 0, |
| address, |
| 0, 0, |
| fb->width, fb->height, pitch, |
| ODN_PDP_SURF_PIXFMT_ARGB8888, |
| 255, |
| false); |
| break; |
| case PDP_VERSION_PLATO: |
| switch (pdp_drm_fb_format(fb)) { |
| case DRM_FORMAT_ARGB8888: |
| case DRM_FORMAT_XRGB8888: |
| break; |
| default: |
| DRM_ERROR("unsupported pixel format (format = %d)\n", |
| pdp_drm_fb_format(fb)); |
| return; |
| } |
| |
| pdp_plato_set_surface(crtc->dev->dev, |
| pdp_crtc->pdp_reg, |
| pdp_crtc->pdp_bif_reg, |
| 0, |
| address, |
| 0, 0, |
| fb->width, fb->height, pitch, |
| PLATO_PDP_PIXEL_FORMAT_ARGB8, |
| 255, |
| false); |
| break; |
| default: |
| BUG(); |
| } |
| } |